Merge "Remove enableSystemApp() and enableSystemAppWithInten()."
diff --git a/Android.mk b/Android.mk
index 8603d99..642d8b9 100644
--- a/Android.mk
+++ b/Android.mk
@@ -118,6 +118,7 @@
 	core/java/android/content/IIntentReceiver.aidl \
 	core/java/android/content/IIntentSender.aidl \
 	core/java/android/content/IOnPrimaryClipChangedListener.aidl \
+	core/java/android/content/IRestrictionsManager.aidl \
 	core/java/android/content/ISyncAdapter.aidl \
 	core/java/android/content/ISyncContext.aidl \
 	core/java/android/content/ISyncServiceAdapter.aidl \
@@ -216,14 +217,6 @@
 	core/java/android/service/wallpaper/IWallpaperConnection.aidl \
 	core/java/android/service/wallpaper/IWallpaperEngine.aidl \
 	core/java/android/service/wallpaper/IWallpaperService.aidl \
-	core/java/android/tv/ITvInputClient.aidl \
-	core/java/android/tv/ITvInputHardware.aidl \
-	core/java/android/tv/ITvInputHardwareCallback.aidl \
-	core/java/android/tv/ITvInputManager.aidl \
-	core/java/android/tv/ITvInputService.aidl \
-	core/java/android/tv/ITvInputServiceCallback.aidl \
-	core/java/android/tv/ITvInputSession.aidl \
-	core/java/android/tv/ITvInputSessionCallback.aidl \
 	core/java/android/view/accessibility/IAccessibilityInteractionConnection.aidl\
 	core/java/android/view/accessibility/IAccessibilityInteractionConnectionCallback.aidl\
 	core/java/android/view/accessibility/IAccessibilityManager.aidl \
@@ -321,6 +314,14 @@
         media/java/android/media/session/ISession.aidl \
         media/java/android/media/session/ISessionCallback.aidl \
         media/java/android/media/session/ISessionManager.aidl \
+	media/java/android/media/tv/ITvInputClient.aidl \
+	media/java/android/media/tv/ITvInputHardware.aidl \
+	media/java/android/media/tv/ITvInputHardwareCallback.aidl \
+	media/java/android/media/tv/ITvInputManager.aidl \
+	media/java/android/media/tv/ITvInputService.aidl \
+	media/java/android/media/tv/ITvInputServiceCallback.aidl \
+	media/java/android/media/tv/ITvInputSession.aidl \
+	media/java/android/media/tv/ITvInputSessionCallback.aidl \
 	telecomm/java/com/android/internal/telecomm/ICallService.aidl \
 	telecomm/java/com/android/internal/telecomm/ICallServiceAdapter.aidl \
 	telecomm/java/com/android/internal/telecomm/ICallServiceLookupResponse.aidl \
@@ -381,55 +382,14 @@
 LOCAL_STATIC_JAVA_LIBRARIES := framework-base
 LOCAL_DX_FLAGS := --core-library
 
-# Packages to include, use \* wildcard to include descendants.
+# List of packages to include along with their descendants.
 LOCAL_JAR_PACKAGES := \
-	android \
-	android.accessibilityservice\* \
-	android.accounts\* \
-	android.alsa\* \
-	android.animation\* \
-	android.annotation\* \
-	android.app\* \
-	android.appwidget\* \
-	android.bluetooth\* \
-	android.content\* \
-	android.content\* \
-	android.database\* \
-	android.ddm\* \
-	android.drm\* \
-	android.emoji\* \
-	android.filterfw\* \
-	android.filterpacks\* \
-	android.gesture\* \
-	android.graphics\* \
-	android.inputmethodservice\* \
-	android.location\* \
-	android.media\* \
-	android.mtp\* \
-	android.net\* \
-	android.nfc\* \
-	android.opengl\* \
-	android.os\* \
-	android.preference\* \
-	android.print\* \
-	android.printservice\* \
-	android.provider\* \
-	android.renderscript\* \
-	android.sax\* \
-	android.security\* \
-	android.service\* \
-	android.speech\* \
-	android.system\* \
-	android.telecomm\* \
-	android.telephony\* \
-	android.test\* \
-	android.text\* \
-	android.transition\* \
-	android.tv\* \
-	android.util\* \
-	android.view\* \
-	android.webkit\* \
-	android.widget\*
+    android
+
+# List of packages to exclude along with their descendants.
+# Overrides inclusion.
+LOCAL_JAR_EXCLUDE_PACKAGES := \
+    android.hardware
 
 # List of classes and interfaces which should be loaded by the Zygote.
 LOCAL_JAVA_RESOURCE_FILES += $(LOCAL_PATH)/preloaded-classes
@@ -447,16 +407,11 @@
 LOCAL_STATIC_JAVA_LIBRARIES := framework-base
 LOCAL_DX_FLAGS := --core-library
 
-# Packages to include, use \* wildcard to include descendants.
-# 'android' is required to be able to include 'android.hardware',
-# however, it causes classes in 'android' to be part of framework
-# and framework2.
-# TODO: Finer grained matching.
+# List of packages to include along with their descendants.
 LOCAL_JAR_PACKAGES := \
-	android \
-	android.hardware\* \
-	com\* \
-	javax\*
+    android.hardware \
+    com \
+    javax
 
 include $(BUILD_JAVA_LIBRARY)
 framework2_module := $(LOCAL_INSTALLED_MODULE)
diff --git a/CleanSpec.mk b/CleanSpec.mk
index 48a20a4..f3bb9b6 100644
--- a/CleanSpec.mk
+++ b/CleanSpec.mk
@@ -192,6 +192,7 @@
 $(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/framework-base_intermediates/src/media/java/android/media/)
 $(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/framework-base_intermediates/src/core/java/android/app)
 $(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*)
 
 # ************************************************
 # NEWER CLEAN STEPS MUST BE AT THE END OF THE LIST
diff --git a/api/current.txt b/api/current.txt
index 73ec234..8a5f1df 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -708,7 +708,6 @@
     field public static final int l_resource_pad24 = 16843769; // 0x10103f9
     field public static final int l_resource_pad25 = 16843768; // 0x10103f8
     field public static final int l_resource_pad26 = 16843767; // 0x10103f7
-    field public static final int l_resource_pad27 = 16843766; // 0x10103f6
     field public static final int l_resource_pad3 = 16843790; // 0x101040e
     field public static final int l_resource_pad4 = 16843789; // 0x101040d
     field public static final int l_resource_pad5 = 16843788; // 0x101040c
@@ -1017,10 +1016,12 @@
     field public static final int selectAllOnFocus = 16843102; // 0x101015e
     field public static final int selectable = 16843238; // 0x10101e6
     field public static final int selectableItemBackground = 16843534; // 0x101030e
+    field public static final int selectableItemBackgroundBorderless = 16843871; // 0x101045f
     field public static final int selectedDateVerticalBar = 16843591; // 0x1010347
     field public static final int selectedWeekBackgroundColor = 16843586; // 0x1010342
     field public static final int sessionService = 16843841; // 0x1010441
     field public static final int settingsActivity = 16843301; // 0x1010225
+    field public static final int setupActivity = 16843766; // 0x10103f6
     field public static final int shadowColor = 16843105; // 0x1010161
     field public static final int shadowDx = 16843106; // 0x1010162
     field public static final int shadowDy = 16843107; // 0x1010163
@@ -1243,6 +1244,8 @@
     field public static final int transition = 16843743; // 0x10103df
     field public static final int transitionGroup = 16843803; // 0x101041b
     field public static final int transitionOrdering = 16843744; // 0x10103e0
+    field public static final int translateX = 16843869; // 0x101045d
+    field public static final int translateY = 16843870; // 0x101045e
     field public static final int translationX = 16843554; // 0x1010322
     field public static final int translationY = 16843555; // 0x1010323
     field public static final int translationZ = 16843796; // 0x1010414
@@ -1865,28 +1868,28 @@
     field public static final int TextAppearance_Medium = 16973892; // 0x1030044
     field public static final int TextAppearance_Medium_Inverse = 16973893; // 0x1030045
     field public static final int TextAppearance_Quantum = 16974348; // 0x103020c
-    field public static final int TextAppearance_Quantum_Body1 = 16974543; // 0x10302cf
-    field public static final int TextAppearance_Quantum_Body2 = 16974542; // 0x10302ce
-    field public static final int TextAppearance_Quantum_Button = 16974546; // 0x10302d2
-    field public static final int TextAppearance_Quantum_Caption = 16974544; // 0x10302d0
+    field public static final int TextAppearance_Quantum_Body1 = 16974545; // 0x10302d1
+    field public static final int TextAppearance_Quantum_Body2 = 16974544; // 0x10302d0
+    field public static final int TextAppearance_Quantum_Button = 16974548; // 0x10302d4
+    field public static final int TextAppearance_Quantum_Caption = 16974546; // 0x10302d2
     field public static final int TextAppearance_Quantum_DialogWindowTitle = 16974349; // 0x103020d
-    field public static final int TextAppearance_Quantum_Display1 = 16974538; // 0x10302ca
-    field public static final int TextAppearance_Quantum_Display2 = 16974537; // 0x10302c9
-    field public static final int TextAppearance_Quantum_Display3 = 16974536; // 0x10302c8
-    field public static final int TextAppearance_Quantum_Display4 = 16974535; // 0x10302c7
-    field public static final int TextAppearance_Quantum_Headline = 16974539; // 0x10302cb
+    field public static final int TextAppearance_Quantum_Display1 = 16974540; // 0x10302cc
+    field public static final int TextAppearance_Quantum_Display2 = 16974539; // 0x10302cb
+    field public static final int TextAppearance_Quantum_Display3 = 16974538; // 0x10302ca
+    field public static final int TextAppearance_Quantum_Display4 = 16974537; // 0x10302c9
+    field public static final int TextAppearance_Quantum_Headline = 16974541; // 0x10302cd
     field public static final int TextAppearance_Quantum_Inverse = 16974350; // 0x103020e
     field public static final int TextAppearance_Quantum_Large = 16974351; // 0x103020f
     field public static final int TextAppearance_Quantum_Large_Inverse = 16974352; // 0x1030210
     field public static final int TextAppearance_Quantum_Medium = 16974353; // 0x1030211
     field public static final int TextAppearance_Quantum_Medium_Inverse = 16974354; // 0x1030212
-    field public static final int TextAppearance_Quantum_Menu = 16974545; // 0x10302d1
+    field public static final int TextAppearance_Quantum_Menu = 16974547; // 0x10302d3
     field public static final int TextAppearance_Quantum_SearchResult_Subtitle = 16974355; // 0x1030213
     field public static final int TextAppearance_Quantum_SearchResult_Title = 16974356; // 0x1030214
     field public static final int TextAppearance_Quantum_Small = 16974357; // 0x1030215
     field public static final int TextAppearance_Quantum_Small_Inverse = 16974358; // 0x1030216
-    field public static final int TextAppearance_Quantum_Subhead = 16974541; // 0x10302cd
-    field public static final int TextAppearance_Quantum_Title = 16974540; // 0x10302cc
+    field public static final int TextAppearance_Quantum_Subhead = 16974543; // 0x10302cf
+    field public static final int TextAppearance_Quantum_Title = 16974542; // 0x10302ce
     field public static final int TextAppearance_Quantum_Widget = 16974360; // 0x1030218
     field public static final int TextAppearance_Quantum_Widget_ActionBar_Menu = 16974361; // 0x1030219
     field public static final int TextAppearance_Quantum_Widget_ActionBar_Subtitle = 16974362; // 0x103021a
@@ -1933,11 +1936,11 @@
     field public static final int TextAppearance_Widget_TextView_SpinnerItem = 16973906; // 0x1030052
     field public static final int TextAppearance_WindowTitle = 16973907; // 0x1030053
     field public static final int Theme = 16973829; // 0x1030005
-    field public static final int ThemeOverlay = 16974410; // 0x103024a
-    field public static final int ThemeOverlay_Quantum = 16974411; // 0x103024b
-    field public static final int ThemeOverlay_Quantum_ActionBarWidget = 16974414; // 0x103024e
-    field public static final int ThemeOverlay_Quantum_Dark = 16974413; // 0x103024d
-    field public static final int ThemeOverlay_Quantum_Light = 16974412; // 0x103024c
+    field public static final int ThemeOverlay = 16974412; // 0x103024c
+    field public static final int ThemeOverlay_Quantum = 16974413; // 0x103024d
+    field public static final int ThemeOverlay_Quantum_ActionBarWidget = 16974416; // 0x1030250
+    field public static final int ThemeOverlay_Quantum_Dark = 16974415; // 0x103024f
+    field public static final int ThemeOverlay_Quantum_Light = 16974414; // 0x103024e
     field public static final int Theme_Black = 16973832; // 0x1030008
     field public static final int Theme_Black_NoTitleBar = 16973833; // 0x1030009
     field public static final int Theme_Black_NoTitleBar_Fullscreen = 16973834; // 0x103000a
@@ -2017,26 +2020,28 @@
     field public static final int Theme_Quantum_Dialog_NoActionBar = 16974385; // 0x1030231
     field public static final int Theme_Quantum_Dialog_NoActionBar_MinWidth = 16974386; // 0x1030232
     field public static final int Theme_Quantum_InputMethod = 16974389; // 0x1030235
-    field public static final int Theme_Quantum_Light = 16974397; // 0x103023d
-    field public static final int Theme_Quantum_Light_DarkActionBar = 16974398; // 0x103023e
-    field public static final int Theme_Quantum_Light_Dialog = 16974399; // 0x103023f
-    field public static final int Theme_Quantum_Light_DialogWhenLarge = 16974403; // 0x1030243
-    field public static final int Theme_Quantum_Light_DialogWhenLarge_NoActionBar = 16974404; // 0x1030244
-    field public static final int Theme_Quantum_Light_Dialog_MinWidth = 16974400; // 0x1030240
-    field public static final int Theme_Quantum_Light_Dialog_NoActionBar = 16974401; // 0x1030241
-    field public static final int Theme_Quantum_Light_Dialog_NoActionBar_MinWidth = 16974402; // 0x1030242
-    field public static final int Theme_Quantum_Light_NoActionBar = 16974405; // 0x1030245
-    field public static final int Theme_Quantum_Light_NoActionBar_Fullscreen = 16974406; // 0x1030246
-    field public static final int Theme_Quantum_Light_NoActionBar_Overscan = 16974407; // 0x1030247
-    field public static final int Theme_Quantum_Light_NoActionBar_TranslucentDecor = 16974408; // 0x1030248
-    field public static final int Theme_Quantum_Light_Panel = 16974409; // 0x1030249
+    field public static final int Theme_Quantum_Light = 16974398; // 0x103023e
+    field public static final int Theme_Quantum_Light_DarkActionBar = 16974399; // 0x103023f
+    field public static final int Theme_Quantum_Light_Dialog = 16974400; // 0x1030240
+    field public static final int Theme_Quantum_Light_DialogWhenLarge = 16974404; // 0x1030244
+    field public static final int Theme_Quantum_Light_DialogWhenLarge_NoActionBar = 16974405; // 0x1030245
+    field public static final int Theme_Quantum_Light_Dialog_MinWidth = 16974401; // 0x1030241
+    field public static final int Theme_Quantum_Light_Dialog_NoActionBar = 16974402; // 0x1030242
+    field public static final int Theme_Quantum_Light_Dialog_NoActionBar_MinWidth = 16974403; // 0x1030243
+    field public static final int Theme_Quantum_Light_NoActionBar = 16974406; // 0x1030246
+    field public static final int Theme_Quantum_Light_NoActionBar_Fullscreen = 16974407; // 0x1030247
+    field public static final int Theme_Quantum_Light_NoActionBar_Overscan = 16974408; // 0x1030248
+    field public static final int Theme_Quantum_Light_NoActionBar_TranslucentDecor = 16974409; // 0x1030249
+    field public static final int Theme_Quantum_Light_Panel = 16974410; // 0x103024a
+    field public static final int Theme_Quantum_Light_Voice = 16974411; // 0x103024b
     field public static final int Theme_Quantum_NoActionBar = 16974390; // 0x1030236
     field public static final int Theme_Quantum_NoActionBar_Fullscreen = 16974391; // 0x1030237
     field public static final int Theme_Quantum_NoActionBar_Overscan = 16974392; // 0x1030238
     field public static final int Theme_Quantum_NoActionBar_TranslucentDecor = 16974393; // 0x1030239
     field public static final int Theme_Quantum_Panel = 16974394; // 0x103023a
-    field public static final int Theme_Quantum_Wallpaper = 16974395; // 0x103023b
-    field public static final int Theme_Quantum_Wallpaper_NoTitleBar = 16974396; // 0x103023c
+    field public static final int Theme_Quantum_Voice = 16974395; // 0x103023b
+    field public static final int Theme_Quantum_Wallpaper = 16974396; // 0x103023c
+    field public static final int Theme_Quantum_Wallpaper_NoTitleBar = 16974397; // 0x103023d
     field public static final int Theme_Translucent = 16973839; // 0x103000f
     field public static final int Theme_Translucent_NoTitleBar = 16973840; // 0x1030010
     field public static final int Theme_Translucent_NoTitleBar_Fullscreen = 16973841; // 0x1030011
@@ -2325,126 +2330,126 @@
     field public static final int Widget_ProgressBar_Large_Inverse = 16973916; // 0x103005c
     field public static final int Widget_ProgressBar_Small = 16973854; // 0x103001e
     field public static final int Widget_ProgressBar_Small_Inverse = 16973917; // 0x103005d
-    field public static final int Widget_Quantum = 16974415; // 0x103024f
-    field public static final int Widget_Quantum_ActionBar = 16974416; // 0x1030250
-    field public static final int Widget_Quantum_ActionBar_Solid = 16974417; // 0x1030251
-    field public static final int Widget_Quantum_ActionBar_TabBar = 16974418; // 0x1030252
-    field public static final int Widget_Quantum_ActionBar_TabText = 16974419; // 0x1030253
-    field public static final int Widget_Quantum_ActionBar_TabView = 16974420; // 0x1030254
-    field public static final int Widget_Quantum_ActionButton = 16974421; // 0x1030255
-    field public static final int Widget_Quantum_ActionButton_CloseMode = 16974422; // 0x1030256
-    field public static final int Widget_Quantum_ActionButton_Overflow = 16974423; // 0x1030257
-    field public static final int Widget_Quantum_ActionMode = 16974424; // 0x1030258
-    field public static final int Widget_Quantum_AutoCompleteTextView = 16974425; // 0x1030259
-    field public static final int Widget_Quantum_Button = 16974426; // 0x103025a
-    field public static final int Widget_Quantum_ButtonBar = 16974432; // 0x1030260
-    field public static final int Widget_Quantum_ButtonBar_AlertDialog = 16974433; // 0x1030261
-    field public static final int Widget_Quantum_Button_Borderless = 16974427; // 0x103025b
-    field public static final int Widget_Quantum_Button_Borderless_Small = 16974428; // 0x103025c
-    field public static final int Widget_Quantum_Button_Inset = 16974429; // 0x103025d
-    field public static final int Widget_Quantum_Button_Small = 16974430; // 0x103025e
-    field public static final int Widget_Quantum_Button_Toggle = 16974431; // 0x103025f
-    field public static final int Widget_Quantum_CalendarView = 16974434; // 0x1030262
-    field public static final int Widget_Quantum_CheckedTextView = 16974435; // 0x1030263
-    field public static final int Widget_Quantum_CompoundButton_CheckBox = 16974436; // 0x1030264
-    field public static final int Widget_Quantum_CompoundButton_RadioButton = 16974437; // 0x1030265
-    field public static final int Widget_Quantum_CompoundButton_Star = 16974438; // 0x1030266
-    field public static final int Widget_Quantum_DatePicker = 16974439; // 0x1030267
-    field public static final int Widget_Quantum_DropDownItem = 16974440; // 0x1030268
-    field public static final int Widget_Quantum_DropDownItem_Spinner = 16974441; // 0x1030269
-    field public static final int Widget_Quantum_EditText = 16974442; // 0x103026a
-    field public static final int Widget_Quantum_ExpandableListView = 16974443; // 0x103026b
-    field public static final int Widget_Quantum_FastScroll = 16974444; // 0x103026c
-    field public static final int Widget_Quantum_GridView = 16974445; // 0x103026d
-    field public static final int Widget_Quantum_HorizontalScrollView = 16974446; // 0x103026e
-    field public static final int Widget_Quantum_ImageButton = 16974447; // 0x103026f
-    field public static final int Widget_Quantum_Light = 16974474; // 0x103028a
-    field public static final int Widget_Quantum_Light_ActionBar = 16974475; // 0x103028b
-    field public static final int Widget_Quantum_Light_ActionBar_Solid = 16974476; // 0x103028c
-    field public static final int Widget_Quantum_Light_ActionBar_TabBar = 16974477; // 0x103028d
-    field public static final int Widget_Quantum_Light_ActionBar_TabText = 16974478; // 0x103028e
-    field public static final int Widget_Quantum_Light_ActionBar_TabView = 16974479; // 0x103028f
-    field public static final int Widget_Quantum_Light_ActionButton = 16974480; // 0x1030290
-    field public static final int Widget_Quantum_Light_ActionButton_CloseMode = 16974481; // 0x1030291
-    field public static final int Widget_Quantum_Light_ActionButton_Overflow = 16974482; // 0x1030292
-    field public static final int Widget_Quantum_Light_ActionMode = 16974483; // 0x1030293
-    field public static final int Widget_Quantum_Light_AutoCompleteTextView = 16974484; // 0x1030294
-    field public static final int Widget_Quantum_Light_Button = 16974485; // 0x1030295
-    field public static final int Widget_Quantum_Light_ButtonBar = 16974491; // 0x103029b
-    field public static final int Widget_Quantum_Light_ButtonBar_AlertDialog = 16974492; // 0x103029c
-    field public static final int Widget_Quantum_Light_Button_Borderless = 16974486; // 0x1030296
-    field public static final int Widget_Quantum_Light_Button_Borderless_Small = 16974487; // 0x1030297
-    field public static final int Widget_Quantum_Light_Button_Inset = 16974488; // 0x1030298
-    field public static final int Widget_Quantum_Light_Button_Small = 16974489; // 0x1030299
-    field public static final int Widget_Quantum_Light_Button_Toggle = 16974490; // 0x103029a
-    field public static final int Widget_Quantum_Light_CalendarView = 16974493; // 0x103029d
-    field public static final int Widget_Quantum_Light_CheckedTextView = 16974494; // 0x103029e
-    field public static final int Widget_Quantum_Light_CompoundButton_CheckBox = 16974495; // 0x103029f
-    field public static final int Widget_Quantum_Light_CompoundButton_RadioButton = 16974496; // 0x10302a0
-    field public static final int Widget_Quantum_Light_CompoundButton_Star = 16974497; // 0x10302a1
-    field public static final int Widget_Quantum_Light_DropDownItem = 16974498; // 0x10302a2
-    field public static final int Widget_Quantum_Light_DropDownItem_Spinner = 16974499; // 0x10302a3
-    field public static final int Widget_Quantum_Light_EditText = 16974500; // 0x10302a4
-    field public static final int Widget_Quantum_Light_ExpandableListView = 16974501; // 0x10302a5
-    field public static final int Widget_Quantum_Light_FastScroll = 16974502; // 0x10302a6
-    field public static final int Widget_Quantum_Light_GridView = 16974503; // 0x10302a7
-    field public static final int Widget_Quantum_Light_HorizontalScrollView = 16974504; // 0x10302a8
-    field public static final int Widget_Quantum_Light_ImageButton = 16974505; // 0x10302a9
-    field public static final int Widget_Quantum_Light_ListPopupWindow = 16974506; // 0x10302aa
-    field public static final int Widget_Quantum_Light_ListView = 16974507; // 0x10302ab
-    field public static final int Widget_Quantum_Light_ListView_DropDown = 16974508; // 0x10302ac
-    field public static final int Widget_Quantum_Light_MediaRouteButton = 16974509; // 0x10302ad
-    field public static final int Widget_Quantum_Light_PopupMenu = 16974510; // 0x10302ae
-    field public static final int Widget_Quantum_Light_PopupMenu_Overflow = 16974511; // 0x10302af
-    field public static final int Widget_Quantum_Light_PopupWindow = 16974512; // 0x10302b0
-    field public static final int Widget_Quantum_Light_ProgressBar = 16974513; // 0x10302b1
-    field public static final int Widget_Quantum_Light_ProgressBar_Horizontal = 16974514; // 0x10302b2
-    field public static final int Widget_Quantum_Light_ProgressBar_Inverse = 16974515; // 0x10302b3
-    field public static final int Widget_Quantum_Light_ProgressBar_Large = 16974516; // 0x10302b4
-    field public static final int Widget_Quantum_Light_ProgressBar_Large_Inverse = 16974517; // 0x10302b5
-    field public static final int Widget_Quantum_Light_ProgressBar_Small = 16974518; // 0x10302b6
-    field public static final int Widget_Quantum_Light_ProgressBar_Small_Inverse = 16974519; // 0x10302b7
-    field public static final int Widget_Quantum_Light_ProgressBar_Small_Title = 16974520; // 0x10302b8
-    field public static final int Widget_Quantum_Light_RatingBar = 16974521; // 0x10302b9
-    field public static final int Widget_Quantum_Light_RatingBar_Indicator = 16974522; // 0x10302ba
-    field public static final int Widget_Quantum_Light_RatingBar_Small = 16974523; // 0x10302bb
-    field public static final int Widget_Quantum_Light_ScrollView = 16974524; // 0x10302bc
-    field public static final int Widget_Quantum_Light_SeekBar = 16974525; // 0x10302bd
-    field public static final int Widget_Quantum_Light_SegmentedButton = 16974526; // 0x10302be
-    field public static final int Widget_Quantum_Light_Spinner = 16974528; // 0x10302c0
-    field public static final int Widget_Quantum_Light_StackView = 16974527; // 0x10302bf
-    field public static final int Widget_Quantum_Light_Tab = 16974529; // 0x10302c1
-    field public static final int Widget_Quantum_Light_TabWidget = 16974530; // 0x10302c2
-    field public static final int Widget_Quantum_Light_TextView = 16974531; // 0x10302c3
-    field public static final int Widget_Quantum_Light_TextView_SpinnerItem = 16974532; // 0x10302c4
-    field public static final int Widget_Quantum_Light_WebTextView = 16974533; // 0x10302c5
-    field public static final int Widget_Quantum_Light_WebView = 16974534; // 0x10302c6
-    field public static final int Widget_Quantum_ListPopupWindow = 16974448; // 0x1030270
-    field public static final int Widget_Quantum_ListView = 16974449; // 0x1030271
-    field public static final int Widget_Quantum_ListView_DropDown = 16974450; // 0x1030272
-    field public static final int Widget_Quantum_MediaRouteButton = 16974451; // 0x1030273
-    field public static final int Widget_Quantum_PopupMenu = 16974452; // 0x1030274
-    field public static final int Widget_Quantum_PopupMenu_Overflow = 16974453; // 0x1030275
-    field public static final int Widget_Quantum_PopupWindow = 16974454; // 0x1030276
-    field public static final int Widget_Quantum_ProgressBar = 16974455; // 0x1030277
-    field public static final int Widget_Quantum_ProgressBar_Horizontal = 16974456; // 0x1030278
-    field public static final int Widget_Quantum_ProgressBar_Large = 16974457; // 0x1030279
-    field public static final int Widget_Quantum_ProgressBar_Small = 16974458; // 0x103027a
-    field public static final int Widget_Quantum_ProgressBar_Small_Title = 16974459; // 0x103027b
-    field public static final int Widget_Quantum_RatingBar = 16974460; // 0x103027c
-    field public static final int Widget_Quantum_RatingBar_Indicator = 16974461; // 0x103027d
-    field public static final int Widget_Quantum_RatingBar_Small = 16974462; // 0x103027e
-    field public static final int Widget_Quantum_ScrollView = 16974463; // 0x103027f
-    field public static final int Widget_Quantum_SeekBar = 16974464; // 0x1030280
-    field public static final int Widget_Quantum_SegmentedButton = 16974465; // 0x1030281
-    field public static final int Widget_Quantum_Spinner = 16974467; // 0x1030283
-    field public static final int Widget_Quantum_StackView = 16974466; // 0x1030282
-    field public static final int Widget_Quantum_Tab = 16974468; // 0x1030284
-    field public static final int Widget_Quantum_TabWidget = 16974469; // 0x1030285
-    field public static final int Widget_Quantum_TextView = 16974470; // 0x1030286
-    field public static final int Widget_Quantum_TextView_SpinnerItem = 16974471; // 0x1030287
-    field public static final int Widget_Quantum_WebTextView = 16974472; // 0x1030288
-    field public static final int Widget_Quantum_WebView = 16974473; // 0x1030289
+    field public static final int Widget_Quantum = 16974417; // 0x1030251
+    field public static final int Widget_Quantum_ActionBar = 16974418; // 0x1030252
+    field public static final int Widget_Quantum_ActionBar_Solid = 16974419; // 0x1030253
+    field public static final int Widget_Quantum_ActionBar_TabBar = 16974420; // 0x1030254
+    field public static final int Widget_Quantum_ActionBar_TabText = 16974421; // 0x1030255
+    field public static final int Widget_Quantum_ActionBar_TabView = 16974422; // 0x1030256
+    field public static final int Widget_Quantum_ActionButton = 16974423; // 0x1030257
+    field public static final int Widget_Quantum_ActionButton_CloseMode = 16974424; // 0x1030258
+    field public static final int Widget_Quantum_ActionButton_Overflow = 16974425; // 0x1030259
+    field public static final int Widget_Quantum_ActionMode = 16974426; // 0x103025a
+    field public static final int Widget_Quantum_AutoCompleteTextView = 16974427; // 0x103025b
+    field public static final int Widget_Quantum_Button = 16974428; // 0x103025c
+    field public static final int Widget_Quantum_ButtonBar = 16974434; // 0x1030262
+    field public static final int Widget_Quantum_ButtonBar_AlertDialog = 16974435; // 0x1030263
+    field public static final int Widget_Quantum_Button_Borderless = 16974429; // 0x103025d
+    field public static final int Widget_Quantum_Button_Borderless_Small = 16974430; // 0x103025e
+    field public static final int Widget_Quantum_Button_Inset = 16974431; // 0x103025f
+    field public static final int Widget_Quantum_Button_Small = 16974432; // 0x1030260
+    field public static final int Widget_Quantum_Button_Toggle = 16974433; // 0x1030261
+    field public static final int Widget_Quantum_CalendarView = 16974436; // 0x1030264
+    field public static final int Widget_Quantum_CheckedTextView = 16974437; // 0x1030265
+    field public static final int Widget_Quantum_CompoundButton_CheckBox = 16974438; // 0x1030266
+    field public static final int Widget_Quantum_CompoundButton_RadioButton = 16974439; // 0x1030267
+    field public static final int Widget_Quantum_CompoundButton_Star = 16974440; // 0x1030268
+    field public static final int Widget_Quantum_DatePicker = 16974441; // 0x1030269
+    field public static final int Widget_Quantum_DropDownItem = 16974442; // 0x103026a
+    field public static final int Widget_Quantum_DropDownItem_Spinner = 16974443; // 0x103026b
+    field public static final int Widget_Quantum_EditText = 16974444; // 0x103026c
+    field public static final int Widget_Quantum_ExpandableListView = 16974445; // 0x103026d
+    field public static final int Widget_Quantum_FastScroll = 16974446; // 0x103026e
+    field public static final int Widget_Quantum_GridView = 16974447; // 0x103026f
+    field public static final int Widget_Quantum_HorizontalScrollView = 16974448; // 0x1030270
+    field public static final int Widget_Quantum_ImageButton = 16974449; // 0x1030271
+    field public static final int Widget_Quantum_Light = 16974476; // 0x103028c
+    field public static final int Widget_Quantum_Light_ActionBar = 16974477; // 0x103028d
+    field public static final int Widget_Quantum_Light_ActionBar_Solid = 16974478; // 0x103028e
+    field public static final int Widget_Quantum_Light_ActionBar_TabBar = 16974479; // 0x103028f
+    field public static final int Widget_Quantum_Light_ActionBar_TabText = 16974480; // 0x1030290
+    field public static final int Widget_Quantum_Light_ActionBar_TabView = 16974481; // 0x1030291
+    field public static final int Widget_Quantum_Light_ActionButton = 16974482; // 0x1030292
+    field public static final int Widget_Quantum_Light_ActionButton_CloseMode = 16974483; // 0x1030293
+    field public static final int Widget_Quantum_Light_ActionButton_Overflow = 16974484; // 0x1030294
+    field public static final int Widget_Quantum_Light_ActionMode = 16974485; // 0x1030295
+    field public static final int Widget_Quantum_Light_AutoCompleteTextView = 16974486; // 0x1030296
+    field public static final int Widget_Quantum_Light_Button = 16974487; // 0x1030297
+    field public static final int Widget_Quantum_Light_ButtonBar = 16974493; // 0x103029d
+    field public static final int Widget_Quantum_Light_ButtonBar_AlertDialog = 16974494; // 0x103029e
+    field public static final int Widget_Quantum_Light_Button_Borderless = 16974488; // 0x1030298
+    field public static final int Widget_Quantum_Light_Button_Borderless_Small = 16974489; // 0x1030299
+    field public static final int Widget_Quantum_Light_Button_Inset = 16974490; // 0x103029a
+    field public static final int Widget_Quantum_Light_Button_Small = 16974491; // 0x103029b
+    field public static final int Widget_Quantum_Light_Button_Toggle = 16974492; // 0x103029c
+    field public static final int Widget_Quantum_Light_CalendarView = 16974495; // 0x103029f
+    field public static final int Widget_Quantum_Light_CheckedTextView = 16974496; // 0x10302a0
+    field public static final int Widget_Quantum_Light_CompoundButton_CheckBox = 16974497; // 0x10302a1
+    field public static final int Widget_Quantum_Light_CompoundButton_RadioButton = 16974498; // 0x10302a2
+    field public static final int Widget_Quantum_Light_CompoundButton_Star = 16974499; // 0x10302a3
+    field public static final int Widget_Quantum_Light_DropDownItem = 16974500; // 0x10302a4
+    field public static final int Widget_Quantum_Light_DropDownItem_Spinner = 16974501; // 0x10302a5
+    field public static final int Widget_Quantum_Light_EditText = 16974502; // 0x10302a6
+    field public static final int Widget_Quantum_Light_ExpandableListView = 16974503; // 0x10302a7
+    field public static final int Widget_Quantum_Light_FastScroll = 16974504; // 0x10302a8
+    field public static final int Widget_Quantum_Light_GridView = 16974505; // 0x10302a9
+    field public static final int Widget_Quantum_Light_HorizontalScrollView = 16974506; // 0x10302aa
+    field public static final int Widget_Quantum_Light_ImageButton = 16974507; // 0x10302ab
+    field public static final int Widget_Quantum_Light_ListPopupWindow = 16974508; // 0x10302ac
+    field public static final int Widget_Quantum_Light_ListView = 16974509; // 0x10302ad
+    field public static final int Widget_Quantum_Light_ListView_DropDown = 16974510; // 0x10302ae
+    field public static final int Widget_Quantum_Light_MediaRouteButton = 16974511; // 0x10302af
+    field public static final int Widget_Quantum_Light_PopupMenu = 16974512; // 0x10302b0
+    field public static final int Widget_Quantum_Light_PopupMenu_Overflow = 16974513; // 0x10302b1
+    field public static final int Widget_Quantum_Light_PopupWindow = 16974514; // 0x10302b2
+    field public static final int Widget_Quantum_Light_ProgressBar = 16974515; // 0x10302b3
+    field public static final int Widget_Quantum_Light_ProgressBar_Horizontal = 16974516; // 0x10302b4
+    field public static final int Widget_Quantum_Light_ProgressBar_Inverse = 16974517; // 0x10302b5
+    field public static final int Widget_Quantum_Light_ProgressBar_Large = 16974518; // 0x10302b6
+    field public static final int Widget_Quantum_Light_ProgressBar_Large_Inverse = 16974519; // 0x10302b7
+    field public static final int Widget_Quantum_Light_ProgressBar_Small = 16974520; // 0x10302b8
+    field public static final int Widget_Quantum_Light_ProgressBar_Small_Inverse = 16974521; // 0x10302b9
+    field public static final int Widget_Quantum_Light_ProgressBar_Small_Title = 16974522; // 0x10302ba
+    field public static final int Widget_Quantum_Light_RatingBar = 16974523; // 0x10302bb
+    field public static final int Widget_Quantum_Light_RatingBar_Indicator = 16974524; // 0x10302bc
+    field public static final int Widget_Quantum_Light_RatingBar_Small = 16974525; // 0x10302bd
+    field public static final int Widget_Quantum_Light_ScrollView = 16974526; // 0x10302be
+    field public static final int Widget_Quantum_Light_SeekBar = 16974527; // 0x10302bf
+    field public static final int Widget_Quantum_Light_SegmentedButton = 16974528; // 0x10302c0
+    field public static final int Widget_Quantum_Light_Spinner = 16974530; // 0x10302c2
+    field public static final int Widget_Quantum_Light_StackView = 16974529; // 0x10302c1
+    field public static final int Widget_Quantum_Light_Tab = 16974531; // 0x10302c3
+    field public static final int Widget_Quantum_Light_TabWidget = 16974532; // 0x10302c4
+    field public static final int Widget_Quantum_Light_TextView = 16974533; // 0x10302c5
+    field public static final int Widget_Quantum_Light_TextView_SpinnerItem = 16974534; // 0x10302c6
+    field public static final int Widget_Quantum_Light_WebTextView = 16974535; // 0x10302c7
+    field public static final int Widget_Quantum_Light_WebView = 16974536; // 0x10302c8
+    field public static final int Widget_Quantum_ListPopupWindow = 16974450; // 0x1030272
+    field public static final int Widget_Quantum_ListView = 16974451; // 0x1030273
+    field public static final int Widget_Quantum_ListView_DropDown = 16974452; // 0x1030274
+    field public static final int Widget_Quantum_MediaRouteButton = 16974453; // 0x1030275
+    field public static final int Widget_Quantum_PopupMenu = 16974454; // 0x1030276
+    field public static final int Widget_Quantum_PopupMenu_Overflow = 16974455; // 0x1030277
+    field public static final int Widget_Quantum_PopupWindow = 16974456; // 0x1030278
+    field public static final int Widget_Quantum_ProgressBar = 16974457; // 0x1030279
+    field public static final int Widget_Quantum_ProgressBar_Horizontal = 16974458; // 0x103027a
+    field public static final int Widget_Quantum_ProgressBar_Large = 16974459; // 0x103027b
+    field public static final int Widget_Quantum_ProgressBar_Small = 16974460; // 0x103027c
+    field public static final int Widget_Quantum_ProgressBar_Small_Title = 16974461; // 0x103027d
+    field public static final int Widget_Quantum_RatingBar = 16974462; // 0x103027e
+    field public static final int Widget_Quantum_RatingBar_Indicator = 16974463; // 0x103027f
+    field public static final int Widget_Quantum_RatingBar_Small = 16974464; // 0x1030280
+    field public static final int Widget_Quantum_ScrollView = 16974465; // 0x1030281
+    field public static final int Widget_Quantum_SeekBar = 16974466; // 0x1030282
+    field public static final int Widget_Quantum_SegmentedButton = 16974467; // 0x1030283
+    field public static final int Widget_Quantum_Spinner = 16974469; // 0x1030285
+    field public static final int Widget_Quantum_StackView = 16974468; // 0x1030284
+    field public static final int Widget_Quantum_Tab = 16974470; // 0x1030286
+    field public static final int Widget_Quantum_TabWidget = 16974471; // 0x1030287
+    field public static final int Widget_Quantum_TextView = 16974472; // 0x1030288
+    field public static final int Widget_Quantum_TextView_SpinnerItem = 16974473; // 0x1030289
+    field public static final int Widget_Quantum_WebTextView = 16974474; // 0x103028a
+    field public static final int Widget_Quantum_WebView = 16974475; // 0x103028b
     field public static final int Widget_RatingBar = 16973857; // 0x1030021
     field public static final int Widget_ScrollView = 16973869; // 0x103002d
     field public static final int Widget_SeekBar = 16973856; // 0x1030020
@@ -3425,11 +3430,11 @@
     method public static void getMyMemoryState(android.app.ActivityManager.RunningAppProcessInfo);
     method public android.os.Debug.MemoryInfo[] getProcessMemoryInfo(int[]);
     method public java.util.List<android.app.ActivityManager.ProcessErrorStateInfo> getProcessesInErrorState();
-    method public java.util.List<android.app.ActivityManager.RecentTaskInfo> getRecentTasks(int, int) throws java.lang.SecurityException;
+    method public deprecated java.util.List<android.app.ActivityManager.RecentTaskInfo> getRecentTasks(int, int) throws java.lang.SecurityException;
     method public java.util.List<android.app.ActivityManager.RunningAppProcessInfo> getRunningAppProcesses();
     method public android.app.PendingIntent getRunningServiceControlPanel(android.content.ComponentName) throws java.lang.SecurityException;
     method public java.util.List<android.app.ActivityManager.RunningServiceInfo> getRunningServices(int) throws java.lang.SecurityException;
-    method public java.util.List<android.app.ActivityManager.RunningTaskInfo> getRunningTasks(int) throws java.lang.SecurityException;
+    method public deprecated java.util.List<android.app.ActivityManager.RunningTaskInfo> getRunningTasks(int) throws java.lang.SecurityException;
     method public boolean isLowRamDevice();
     method public static boolean isRunningInTestHarness();
     method public static boolean isUserAMonkey();
@@ -4691,6 +4696,7 @@
     method public android.app.Notification.WearableExtender setHintShowBackgroundOnly(boolean);
     method public android.app.Notification.WearableExtender setStartScrollBottom(boolean);
     field public static final int SIZE_DEFAULT = 0; // 0x0
+    field public static final int SIZE_FULL_SCREEN = 5; // 0x5
     field public static final int SIZE_LARGE = 4; // 0x4
     field public static final int SIZE_MEDIUM = 3; // 0x3
     field public static final int SIZE_SMALL = 2; // 0x2
@@ -5028,6 +5034,11 @@
     method public boolean[] supportsCommands(java.lang.String[]);
   }
 
+  public static class VoiceInteractor.AbortVoiceRequest extends android.app.VoiceInteractor.Request {
+    ctor public VoiceInteractor.AbortVoiceRequest(java.lang.CharSequence, android.os.Bundle);
+    method public void onAbortResult(android.os.Bundle);
+  }
+
   public static class VoiceInteractor.CommandRequest extends android.app.VoiceInteractor.Request {
     ctor public VoiceInteractor.CommandRequest(java.lang.String, android.os.Bundle);
     method public void onCommandResult(android.os.Bundle);
@@ -5043,7 +5054,9 @@
     method public void cancel();
     method public android.app.Activity getActivity();
     method public android.content.Context getContext();
+    method public void onAttached(android.app.Activity);
     method public void onCancel();
+    method public void onDetached();
   }
 
   public final class WallpaperInfo implements android.os.Parcelable {
@@ -5215,6 +5228,7 @@
     method public void setPasswordMinimumUpperCase(android.content.ComponentName, int);
     method public void setPasswordQuality(android.content.ComponentName, int);
     method public void setProfileEnabled(android.content.ComponentName);
+    method public void setRestrictionsProvider(android.content.ComponentName, android.content.ComponentName);
     method public void setSecureSetting(android.content.ComponentName, java.lang.String, java.lang.String);
     method public int setStorageEncryption(android.content.ComponentName, boolean);
     method public void wipeData(int);
@@ -5230,6 +5244,7 @@
     field public static final java.lang.String EXTRA_DEVICE_ADMIN = "android.app.extra.DEVICE_ADMIN";
     field public static final java.lang.String EXTRA_PROVISIONING_DEFAULT_MANAGED_PROFILE_NAME = "defaultManagedProfileName";
     field public static final java.lang.String EXTRA_PROVISIONING_DEVICE_ADMIN_PACKAGE_NAME = "deviceAdminPackageName";
+    field public static final java.lang.String EXTRA_PROVISIONING_TOKEN = "android.app.extra.token";
     field public static int FLAG_MANAGED_CAN_ACCESS_PARENT;
     field public static int FLAG_PARENT_CAN_ACCESS_MANAGED;
     field public static final int KEYGUARD_DISABLE_FEATURES_ALL = 2147483647; // 0x7fffffff
@@ -5354,13 +5369,13 @@
     method public int describeContents();
     method public int getBackoffPolicy();
     method public android.os.Bundle getExtras();
+    method public int getId();
     method public long getInitialBackoffMillis();
     method public long getIntervalMillis();
     method public long getMaxExecutionDelayMillis();
     method public long getMinLatencyMillis();
     method public int getNetworkCapabilities();
     method public android.content.ComponentName getService();
-    method public int getTaskId();
     method public boolean isPeriodic();
     method public boolean isRequireCharging();
     method public boolean isRequireDeviceIdle();
@@ -5373,7 +5388,7 @@
     field public static final int LINEAR = 0; // 0x0
   }
 
-  public final class Task.Builder {
+  public static final class Task.Builder {
     ctor public Task.Builder(int, android.content.ComponentName);
     method public android.app.task.Task build();
     method public android.app.task.Task.Builder setBackoffCriteria(long, int);
@@ -5387,8 +5402,9 @@
   }
 
   public static abstract interface Task.NetworkType {
-    field public static final int ANY = 0; // 0x0
-    field public static final int UNMETERED = 1; // 0x1
+    field public static final int ANY = 1; // 0x1
+    field public static final int NONE = 0; // 0x0
+    field public static final int UNMETERED = 2; // 0x2
   }
 
   public abstract class TaskManager {
@@ -5397,8 +5413,8 @@
     method public abstract void cancelAll();
     method public abstract java.util.List<android.app.task.Task> getAllPendingTasks();
     method public abstract int schedule(android.app.task.Task);
-    field public static final int RESULT_INVALID_PARAMETERS = -1; // 0xffffffff
-    field public static final int RESULT_OVER_QUOTA = -2; // 0xfffffffe
+    field public static final int RESULT_FAILURE = 0; // 0x0
+    field public static final int RESULT_SUCCESS = 1; // 0x1
   }
 
   public class TaskParams implements android.os.Parcelable {
@@ -5560,8 +5576,8 @@
     method public boolean disable();
     method public boolean enable();
     method public java.lang.String getAddress();
-    method public android.bluetooth.BluetoothLeAdvertiser getBluetoothLeAdvertiser();
-    method public android.bluetooth.BluetoothLeScanner getBluetoothLeScanner();
+    method public android.bluetooth.le.BluetoothLeAdvertiser getBluetoothLeAdvertiser();
+    method public android.bluetooth.le.BluetoothLeScanner getBluetoothLeScanner();
     method public java.util.Set<android.bluetooth.BluetoothDevice> getBondedDevices();
     method public static synchronized android.bluetooth.BluetoothAdapter getDefaultAdapter();
     method public java.lang.String getName();
@@ -6204,180 +6220,6 @@
     method public void onHealthChannelStateChange(android.bluetooth.BluetoothHealthAppConfiguration, android.bluetooth.BluetoothDevice, int, int, android.os.ParcelFileDescriptor, int);
   }
 
-  public final class BluetoothLeAdvertiseScanData {
-    ctor public BluetoothLeAdvertiseScanData();
-    field public static final int ADVERTISING_DATA = 0; // 0x0
-    field public static final int PARSED_SCAN_RECORD = 2; // 0x2
-    field public static final int SCAN_RESPONSE_DATA = 1; // 0x1
-  }
-
-  public static abstract class BluetoothLeAdvertiseScanData.AdvertiseBaseData {
-    method public int getDataType();
-    method public int getManufacturerId();
-    method public byte[] getManufacturerSpecificData();
-    method public byte[] getServiceData();
-    method public android.os.ParcelUuid getServiceDataUuid();
-    method public java.util.List<android.os.ParcelUuid> getServiceUuids();
-  }
-
-  public static final class BluetoothLeAdvertiseScanData.AdvertisementData extends android.bluetooth.BluetoothLeAdvertiseScanData.AdvertiseBaseData implements android.os.Parcelable {
-    method public int describeContents();
-    method public boolean getIncludeTxPowerLevel();
-    method public static android.bluetooth.BluetoothLeAdvertiseScanData.AdvertisementData.Builder newBuilder();
-    method public void writeToParcel(android.os.Parcel, int);
-    field public static final android.os.Parcelable.Creator CREATOR;
-  }
-
-  public static final class BluetoothLeAdvertiseScanData.AdvertisementData.Builder {
-    ctor public BluetoothLeAdvertiseScanData.AdvertisementData.Builder();
-    method public android.bluetooth.BluetoothLeAdvertiseScanData.AdvertisementData build();
-    method public android.bluetooth.BluetoothLeAdvertiseScanData.AdvertisementData.Builder dataType(int);
-    method public android.bluetooth.BluetoothLeAdvertiseScanData.AdvertisementData.Builder includeTxPowerLevel(boolean);
-    method public android.bluetooth.BluetoothLeAdvertiseScanData.AdvertisementData.Builder manufacturerData(int, byte[]);
-    method public android.bluetooth.BluetoothLeAdvertiseScanData.AdvertisementData.Builder serviceData(android.os.ParcelUuid, byte[]);
-    method public android.bluetooth.BluetoothLeAdvertiseScanData.AdvertisementData.Builder serviceUuids(java.util.List<android.os.ParcelUuid>);
-  }
-
-  public static final class BluetoothLeAdvertiseScanData.ScanRecord extends android.bluetooth.BluetoothLeAdvertiseScanData.AdvertiseBaseData {
-    method public int getAdvertiseFlags();
-    method public java.lang.String getLocalName();
-    method public static android.bluetooth.BluetoothLeAdvertiseScanData.ScanRecord.Parser getParser();
-    method public int getTxPowerLevel();
-  }
-
-  public static final class BluetoothLeAdvertiseScanData.ScanRecord.Parser {
-    ctor public BluetoothLeAdvertiseScanData.ScanRecord.Parser();
-    method public android.bluetooth.BluetoothLeAdvertiseScanData.ScanRecord parseFromScanRecord(byte[]);
-  }
-
-  public class BluetoothLeAdvertiser {
-    method public void startAdvertising(android.bluetooth.BluetoothLeAdvertiser.Settings, android.bluetooth.BluetoothLeAdvertiseScanData.AdvertisementData, android.bluetooth.BluetoothLeAdvertiser.AdvertiseCallback);
-    method public void startAdvertising(android.bluetooth.BluetoothLeAdvertiser.Settings, android.bluetooth.BluetoothLeAdvertiseScanData.AdvertisementData, android.bluetooth.BluetoothLeAdvertiseScanData.AdvertisementData, android.bluetooth.BluetoothLeAdvertiser.AdvertiseCallback);
-    method public void stopAdvertising(android.bluetooth.BluetoothLeAdvertiser.Settings, android.bluetooth.BluetoothLeAdvertiser.AdvertiseCallback);
-  }
-
-  public static abstract interface BluetoothLeAdvertiser.AdvertiseCallback {
-    method public abstract void onFailure(int);
-    method public abstract void onSuccess(android.bluetooth.BluetoothLeAdvertiser.Settings);
-    field public static final int ADVERISING_NOT_STARTED = 4; // 0x4
-    field public static final int ADVERTISING_ALREADY_STARTED = 3; // 0x3
-    field public static final int ADVERTISING_SERVICE_UNKNOWN = 1; // 0x1
-    field public static final int CONTROLLER_FAILURE = 5; // 0x5
-    field public static final int TOO_MANY_ADVERTISERS = 2; // 0x2
-  }
-
-  public static final class BluetoothLeAdvertiser.Settings implements android.os.Parcelable {
-    method public int describeContents();
-    method public int getMode();
-    method public int getTxPowerLevel();
-    method public int getType();
-    method public static android.bluetooth.BluetoothLeAdvertiser.Settings.Builder newBuilder();
-    method public void writeToParcel(android.os.Parcel, int);
-    field public static final int ADVERTISE_MODE_BALANCED = 1; // 0x1
-    field public static final int ADVERTISE_MODE_LOW_LATENCY = 2; // 0x2
-    field public static final int ADVERTISE_MODE_LOW_POWER = 0; // 0x0
-    field public static final int ADVERTISE_TX_POWER_HIGH = 3; // 0x3
-    field public static final int ADVERTISE_TX_POWER_LOW = 1; // 0x1
-    field public static final int ADVERTISE_TX_POWER_MEDIUM = 2; // 0x2
-    field public static final int ADVERTISE_TX_POWER_ULTRA_LOW = 0; // 0x0
-    field public static final int ADVERTISE_TYPE_CONNECTABLE = 2; // 0x2
-    field public static final int ADVERTISE_TYPE_NON_CONNECTABLE = 0; // 0x0
-    field public static final int ADVERTISE_TYPE_SCANNABLE = 1; // 0x1
-    field public static final android.os.Parcelable.Creator CREATOR;
-  }
-
-  public static final class BluetoothLeAdvertiser.Settings.Builder {
-    method public android.bluetooth.BluetoothLeAdvertiser.Settings.Builder advertiseMode(int);
-    method public android.bluetooth.BluetoothLeAdvertiser.Settings build();
-    method public android.bluetooth.BluetoothLeAdvertiser.Settings.Builder txPowerLevel(int);
-    method public android.bluetooth.BluetoothLeAdvertiser.Settings.Builder type(int);
-  }
-
-  public final class BluetoothLeScanFilter implements android.os.Parcelable {
-    method public int describeContents();
-    method public java.lang.String getDeviceAddress();
-    method public java.lang.String getLocalName();
-    method public byte[] getManufacturerData();
-    method public byte[] getManufacturerDataMask();
-    method public int getManufacturerId();
-    method public int getMaxRssi();
-    method public int getMinRssi();
-    method public byte[] getServiceData();
-    method public byte[] getServiceDataMask();
-    method public android.os.ParcelUuid getServiceUuid();
-    method public android.os.ParcelUuid getServiceUuidMask();
-    method public boolean matches(android.bluetooth.BluetoothLeScanner.ScanResult);
-    method public static android.bluetooth.BluetoothLeScanFilter.Builder newBuilder();
-    method public void writeToParcel(android.os.Parcel, int);
-    field public static final android.os.Parcelable.Creator CREATOR;
-  }
-
-  public static class BluetoothLeScanFilter.Builder {
-    method public android.bluetooth.BluetoothLeScanFilter build();
-    method public android.bluetooth.BluetoothLeScanFilter.Builder macAddress(java.lang.String);
-    method public android.bluetooth.BluetoothLeScanFilter.Builder manufacturerData(int, byte[]);
-    method public android.bluetooth.BluetoothLeScanFilter.Builder manufacturerDataMask(byte[]);
-    method public android.bluetooth.BluetoothLeScanFilter.Builder name(java.lang.String);
-    method public android.bluetooth.BluetoothLeScanFilter.Builder rssiRange(int, int);
-    method public android.bluetooth.BluetoothLeScanFilter.Builder serviceData(byte[]);
-    method public android.bluetooth.BluetoothLeScanFilter.Builder serviceDataMask(byte[]);
-    method public android.bluetooth.BluetoothLeScanFilter.Builder serviceUuid(android.os.ParcelUuid);
-    method public android.bluetooth.BluetoothLeScanFilter.Builder serviceUuidMask(android.os.ParcelUuid);
-  }
-
-  public class BluetoothLeScanner {
-    method public void startScan(java.util.List<android.bluetooth.BluetoothLeScanFilter>, android.bluetooth.BluetoothLeScanner.Settings, android.bluetooth.BluetoothLeScanner.ScanCallback);
-    method public void stopScan(android.bluetooth.BluetoothLeScanner.Settings);
-  }
-
-  public static abstract interface BluetoothLeScanner.ScanCallback {
-    method public abstract void onBatchScanResults(java.util.List<android.bluetooth.BluetoothLeScanner.ScanResult>);
-    method public abstract void onDeviceFound(android.bluetooth.BluetoothLeScanner.ScanResult);
-    method public abstract void onDeviceLost(android.bluetooth.BluetoothDevice);
-    method public abstract void onDeviceUpdate(android.bluetooth.BluetoothLeScanner.ScanResult);
-    method public abstract void onScanFailed(int);
-    field public static final int APPLICATION_REGISTRATION_FAILED = 2; // 0x2
-    field public static final int CONTROLLER_FAILURE = 4; // 0x4
-    field public static final int GATT_SERVICE_FAILURE = 3; // 0x3
-    field public static final int SCAN_ALREADY_STARTED = 1; // 0x1
-  }
-
-  public static final class BluetoothLeScanner.ScanResult implements android.os.Parcelable {
-    ctor public BluetoothLeScanner.ScanResult(android.bluetooth.BluetoothDevice, byte[], int, long);
-    method public int describeContents();
-    method public android.bluetooth.BluetoothDevice getDevice();
-    method public int getRssi();
-    method public byte[] getScanRecord();
-    method public long getTimestampMicros();
-    method public void writeToParcel(android.os.Parcel, int);
-    field public static final android.os.Parcelable.Creator CREATOR;
-  }
-
-  public static final class BluetoothLeScanner.Settings implements android.os.Parcelable {
-    method public int describeContents();
-    method public int getCallbackType();
-    method public long getReportDelayMicros();
-    method public int getScanMode();
-    method public int getScanResultType();
-    method public static android.bluetooth.BluetoothLeScanner.Settings.Builder newBuilder();
-    method public void writeToParcel(android.os.Parcel, int);
-    field public static final int CALLBACK_TYPE_ON_FOUND = 1; // 0x1
-    field public static final int CALLBACK_TYPE_ON_LOST = 2; // 0x2
-    field public static final int CALLBACK_TYPE_ON_UPDATE = 0; // 0x0
-    field public static final android.os.Parcelable.Creator CREATOR;
-    field public static final int SCAN_MODE_BALANCED = 1; // 0x1
-    field public static final int SCAN_MODE_LOW_LATENCY = 2; // 0x2
-    field public static final int SCAN_MODE_LOW_POWER = 0; // 0x0
-    field public static final int SCAN_RESULT_TYPE_FULL = 0; // 0x0
-  }
-
-  public static class BluetoothLeScanner.Settings.Builder {
-    method public android.bluetooth.BluetoothLeScanner.Settings build();
-    method public android.bluetooth.BluetoothLeScanner.Settings.Builder callbackType(int);
-    method public android.bluetooth.BluetoothLeScanner.Settings.Builder reportDelayMicros(long);
-    method public android.bluetooth.BluetoothLeScanner.Settings.Builder scanMode(int);
-  }
-
   public final class BluetoothManager {
     method public android.bluetooth.BluetoothAdapter getAdapter();
     method public java.util.List<android.bluetooth.BluetoothDevice> getConnectedDevices(int);
@@ -6425,6 +6267,167 @@
 
 }
 
+package android.bluetooth.le {
+
+  public abstract class AdvertiseCallback {
+    ctor public AdvertiseCallback();
+    method public abstract void onFailure(int);
+    method public abstract void onSuccess(android.bluetooth.le.AdvertiseSettings);
+    field public static final int ADVERTISE_FAILED_ALREADY_STARTED = 3; // 0x3
+    field public static final int ADVERTISE_FAILED_CONTROLLER_FAILURE = 5; // 0x5
+    field public static final int ADVERTISE_FAILED_NOT_STARTED = 4; // 0x4
+    field public static final int ADVERTISE_FAILED_SERVICE_UNKNOWN = 1; // 0x1
+    field public static final int ADVERTISE_FAILED_TOO_MANY_ADVERTISERS = 2; // 0x2
+  }
+
+  public final class AdvertiseSettings implements android.os.Parcelable {
+    method public int describeContents();
+    method public int getMode();
+    method public int getTxPowerLevel();
+    method public int getType();
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final int ADVERTISE_MODE_BALANCED = 1; // 0x1
+    field public static final int ADVERTISE_MODE_LOW_LATENCY = 2; // 0x2
+    field public static final int ADVERTISE_MODE_LOW_POWER = 0; // 0x0
+    field public static final int ADVERTISE_TX_POWER_HIGH = 3; // 0x3
+    field public static final int ADVERTISE_TX_POWER_LOW = 1; // 0x1
+    field public static final int ADVERTISE_TX_POWER_MEDIUM = 2; // 0x2
+    field public static final int ADVERTISE_TX_POWER_ULTRA_LOW = 0; // 0x0
+    field public static final int ADVERTISE_TYPE_CONNECTABLE = 2; // 0x2
+    field public static final int ADVERTISE_TYPE_NON_CONNECTABLE = 0; // 0x0
+    field public static final int ADVERTISE_TYPE_SCANNABLE = 1; // 0x1
+    field public static final android.os.Parcelable.Creator CREATOR;
+  }
+
+  public static final class AdvertiseSettings.Builder {
+    ctor public AdvertiseSettings.Builder();
+    method public android.bluetooth.le.AdvertiseSettings build();
+    method public android.bluetooth.le.AdvertiseSettings.Builder setAdvertiseMode(int);
+    method public android.bluetooth.le.AdvertiseSettings.Builder setTxPowerLevel(int);
+    method public android.bluetooth.le.AdvertiseSettings.Builder setType(int);
+  }
+
+  public final class AdvertisementData implements android.os.Parcelable {
+    method public int describeContents();
+    method public boolean getIncludeTxPowerLevel();
+    method public int getManufacturerId();
+    method public byte[] getManufacturerSpecificData();
+    method public byte[] getServiceData();
+    method public android.os.ParcelUuid getServiceDataUuid();
+    method public java.util.List<android.os.ParcelUuid> getServiceUuids();
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final android.os.Parcelable.Creator CREATOR;
+  }
+
+  public static final class AdvertisementData.Builder {
+    ctor public AdvertisementData.Builder();
+    method public android.bluetooth.le.AdvertisementData build();
+    method public android.bluetooth.le.AdvertisementData.Builder setIncludeTxPowerLevel(boolean);
+    method public android.bluetooth.le.AdvertisementData.Builder setManufacturerData(int, byte[]);
+    method public android.bluetooth.le.AdvertisementData.Builder setServiceData(android.os.ParcelUuid, byte[]);
+    method public android.bluetooth.le.AdvertisementData.Builder setServiceUuids(java.util.List<android.os.ParcelUuid>);
+  }
+
+  public final class BluetoothLeAdvertiser {
+    method public void startAdvertising(android.bluetooth.le.AdvertiseSettings, android.bluetooth.le.AdvertisementData, android.bluetooth.le.AdvertiseCallback);
+    method public void startAdvertising(android.bluetooth.le.AdvertiseSettings, android.bluetooth.le.AdvertisementData, android.bluetooth.le.AdvertisementData, android.bluetooth.le.AdvertiseCallback);
+    method public void stopAdvertising(android.bluetooth.le.AdvertiseCallback);
+  }
+
+  public final class BluetoothLeScanner {
+    method public void startScan(java.util.List<android.bluetooth.le.ScanFilter>, android.bluetooth.le.ScanSettings, android.bluetooth.le.ScanCallback);
+    method public void stopScan(android.bluetooth.le.ScanCallback);
+  }
+
+  public abstract class ScanCallback {
+    ctor public ScanCallback();
+    method public abstract void onAdvertisementUpdate(android.bluetooth.le.ScanResult);
+    method public abstract void onScanFailed(int);
+    field public static final int SCAN_FAILED_ALREADY_STARTED = 1; // 0x1
+    field public static final int SCAN_FAILED_APPLICATION_REGISTRATION_FAILED = 2; // 0x2
+    field public static final int SCAN_FAILED_CONTROLLER_FAILURE = 4; // 0x4
+    field public static final int SCAN_FAILED_GATT_SERVICE_FAILURE = 3; // 0x3
+  }
+
+  public final class ScanFilter implements android.os.Parcelable {
+    method public int describeContents();
+    method public java.lang.String getDeviceAddress();
+    method public java.lang.String getLocalName();
+    method public byte[] getManufacturerData();
+    method public byte[] getManufacturerDataMask();
+    method public int getManufacturerId();
+    method public int getMaxRssi();
+    method public int getMinRssi();
+    method public byte[] getServiceData();
+    method public byte[] getServiceDataMask();
+    method public android.os.ParcelUuid getServiceUuid();
+    method public android.os.ParcelUuid getServiceUuidMask();
+    method public boolean matches(android.bluetooth.le.ScanResult);
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final android.os.Parcelable.Creator CREATOR;
+  }
+
+  public static final class ScanFilter.Builder {
+    ctor public ScanFilter.Builder();
+    method public android.bluetooth.le.ScanFilter build();
+    method public android.bluetooth.le.ScanFilter.Builder setMacAddress(java.lang.String);
+    method public android.bluetooth.le.ScanFilter.Builder setManufacturerData(int, byte[]);
+    method public android.bluetooth.le.ScanFilter.Builder setManufacturerData(int, byte[], byte[]);
+    method public android.bluetooth.le.ScanFilter.Builder setName(java.lang.String);
+    method public android.bluetooth.le.ScanFilter.Builder setRssiRange(int, int);
+    method public android.bluetooth.le.ScanFilter.Builder setServiceData(byte[]);
+    method public android.bluetooth.le.ScanFilter.Builder setServiceData(byte[], byte[]);
+    method public android.bluetooth.le.ScanFilter.Builder setServiceUuid(android.os.ParcelUuid);
+    method public android.bluetooth.le.ScanFilter.Builder setServiceUuid(android.os.ParcelUuid, android.os.ParcelUuid);
+  }
+
+  public final class ScanRecord {
+    method public int getAdvertiseFlags();
+    method public java.lang.String getLocalName();
+    method public int getManufacturerId();
+    method public byte[] getManufacturerSpecificData();
+    method public byte[] getServiceData();
+    method public android.os.ParcelUuid getServiceDataUuid();
+    method public java.util.List<android.os.ParcelUuid> getServiceUuids();
+    method public int getTxPowerLevel();
+    method public static android.bluetooth.le.ScanRecord parseFromBytes(byte[]);
+  }
+
+  public final class ScanResult implements android.os.Parcelable {
+    method public int describeContents();
+    method public android.bluetooth.BluetoothDevice getDevice();
+    method public int getRssi();
+    method public byte[] getScanRecord();
+    method public long getTimestampNanos();
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final android.os.Parcelable.Creator CREATOR;
+  }
+
+  public final class ScanSettings implements android.os.Parcelable {
+    method public int describeContents();
+    method public int getCallbackType();
+    method public long getReportDelayNanos();
+    method public int getScanMode();
+    method public int getScanResultType();
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final int CALLBACK_TYPE_ON_UPDATE = 0; // 0x0
+    field public static final android.os.Parcelable.Creator CREATOR;
+    field public static final int SCAN_MODE_BALANCED = 1; // 0x1
+    field public static final int SCAN_MODE_LOW_LATENCY = 2; // 0x2
+    field public static final int SCAN_MODE_LOW_POWER = 0; // 0x0
+    field public static final int SCAN_RESULT_TYPE_FULL = 0; // 0x0
+  }
+
+  public static final class ScanSettings.Builder {
+    ctor public ScanSettings.Builder();
+    method public android.bluetooth.le.ScanSettings build();
+    method public android.bluetooth.le.ScanSettings.Builder setCallbackType(int);
+    method public android.bluetooth.le.ScanSettings.Builder setReportDelayNanos(long);
+    method public android.bluetooth.le.ScanSettings.Builder setScanMode(int);
+  }
+
+}
+
 package android.content {
 
   public abstract class AbstractThreadedSyncAdapter {
@@ -6909,6 +6912,7 @@
     method public abstract java.io.File[] getExternalCacheDirs();
     method public abstract java.io.File getExternalFilesDir(java.lang.String);
     method public abstract java.io.File[] getExternalFilesDirs(java.lang.String);
+    method public abstract java.io.File[] getExternalMediaDirs();
     method public abstract java.io.File getFileStreamPath(java.lang.String);
     method public abstract java.io.File getFilesDir();
     method public abstract android.os.Looper getMainLooper();
@@ -7021,6 +7025,7 @@
     field public static final java.lang.String NSD_SERVICE = "servicediscovery";
     field public static final java.lang.String POWER_SERVICE = "power";
     field public static final java.lang.String PRINT_SERVICE = "print";
+    field public static final java.lang.String RESTRICTIONS_SERVICE = "restrictions";
     field public static final java.lang.String SEARCH_SERVICE = "search";
     field public static final java.lang.String SENSOR_SERVICE = "sensor";
     field public static final java.lang.String STORAGE_SERVICE = "storage";
@@ -7034,7 +7039,6 @@
     field public static final java.lang.String VIBRATOR_SERVICE = "vibrator";
     field public static final java.lang.String WALLPAPER_SERVICE = "wallpaper";
     field public static final java.lang.String WIFI_P2P_SERVICE = "wifip2p";
-    field public static final java.lang.String WIFI_PASSPOINT_SERVICE = "wifipasspoint";
     field public static final java.lang.String WIFI_SERVICE = "wifi";
     field public static final java.lang.String WINDOW_SERVICE = "window";
   }
@@ -7078,6 +7082,7 @@
     method public java.io.File[] getExternalCacheDirs();
     method public java.io.File getExternalFilesDir(java.lang.String);
     method public java.io.File[] getExternalFilesDirs(java.lang.String);
+    method public java.io.File[] getExternalMediaDirs();
     method public java.io.File getFileStreamPath(java.lang.String);
     method public java.io.File getFilesDir();
     method public android.os.Looper getMainLooper();
@@ -7777,12 +7782,14 @@
     ctor public RestrictionEntry(java.lang.String, java.lang.String);
     ctor public RestrictionEntry(java.lang.String, boolean);
     ctor public RestrictionEntry(java.lang.String, java.lang.String[]);
+    ctor public RestrictionEntry(java.lang.String, int);
     ctor public RestrictionEntry(android.os.Parcel);
     method public int describeContents();
     method public java.lang.String[] getAllSelectedStrings();
     method public java.lang.String[] getChoiceEntries();
     method public java.lang.String[] getChoiceValues();
     method public java.lang.String getDescription();
+    method public int getIntValue();
     method public java.lang.String getKey();
     method public boolean getSelectedState();
     method public java.lang.String getSelectedString();
@@ -7794,6 +7801,7 @@
     method public void setChoiceValues(java.lang.String[]);
     method public void setChoiceValues(android.content.Context, int);
     method public void setDescription(java.lang.String);
+    method public void setIntValue(int);
     method public void setSelectedState(boolean);
     method public void setSelectedString(java.lang.String);
     method public void setTitle(java.lang.String);
@@ -7802,10 +7810,36 @@
     field public static final android.os.Parcelable.Creator CREATOR;
     field public static final int TYPE_BOOLEAN = 1; // 0x1
     field public static final int TYPE_CHOICE = 2; // 0x2
+    field public static final int TYPE_INTEGER = 5; // 0x5
     field public static final int TYPE_MULTI_SELECT = 4; // 0x4
     field public static final int TYPE_NULL = 0; // 0x0
   }
 
+  public class RestrictionsManager {
+    method public android.os.Bundle getApplicationRestrictions();
+    method public java.util.List<android.content.RestrictionEntry> getManifestRestrictions(java.lang.String);
+    method public boolean hasRestrictionsProvider();
+    method public void notifyPermissionResponse(java.lang.String, android.os.Bundle);
+    method public void requestPermission(java.lang.String, android.os.Bundle);
+    field public static final java.lang.String ACTION_PERMISSION_RESPONSE_RECEIVED = "android.intent.action.PERMISSION_RESPONSE_RECEIVED";
+    field public static final java.lang.String ACTION_REQUEST_PERMISSION = "android.intent.action.REQUEST_PERMISSION";
+    field public static final java.lang.String EXTRA_PACKAGE_NAME = "package_name";
+    field public static final java.lang.String EXTRA_REQUEST_BUNDLE = "request_bundle";
+    field public static final java.lang.String EXTRA_RESPONSE_BUNDLE = "response_bundle";
+    field public static final java.lang.String EXTRA_TEMPLATE_ID = "template_id";
+    field public static final java.lang.String REQUEST_KEY_APPROVE_LABEL = "android.req_template.accept";
+    field public static final java.lang.String REQUEST_KEY_DATA = "android.req_template.data";
+    field public static final java.lang.String REQUEST_KEY_DENY_LABEL = "android.req_template.reject";
+    field public static final java.lang.String REQUEST_KEY_DEVICE_NAME = "android.req_template.device";
+    field public static final java.lang.String REQUEST_KEY_ICON = "android.req_template.icon";
+    field public static final java.lang.String REQUEST_KEY_ID = "android.req_template.req_id";
+    field public static final java.lang.String REQUEST_KEY_MESSAGE = "android.req_template.mesg";
+    field public static final java.lang.String REQUEST_KEY_REQUESTOR_NAME = "android.req_template.requestor";
+    field public static final java.lang.String REQUEST_KEY_TITLE = "android.req_template.title";
+    field public static final java.lang.String REQUEST_TEMPLATE_QUESTION = "android.req_template.type.simple";
+    field public static final java.lang.String RESPONSE_KEY_BOOLEAN = "android.req_template.response";
+  }
+
   public class SearchRecentSuggestionsProvider extends android.content.ContentProvider {
     ctor public SearchRecentSuggestionsProvider();
     method public int delete(android.net.Uri, java.lang.String, java.lang.String[]);
@@ -8194,7 +8228,7 @@
   }
 
   public class LauncherActivityInfo {
-    method public int getApplicationFlags();
+    method public android.content.pm.ApplicationInfo getApplicationInfo();
     method public android.graphics.drawable.Drawable getBadgedIcon(int);
     method public android.content.ComponentName getComponentName();
     method public long getFirstInstallTime();
@@ -8205,21 +8239,21 @@
   }
 
   public class LauncherApps {
-    method public synchronized void addOnAppsChangedListener(android.content.pm.LauncherApps.OnAppsChangedListener);
+    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 synchronized void removeOnAppsChangedListener(android.content.pm.LauncherApps.OnAppsChangedListener);
+    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.graphics.Rect, android.os.Bundle, android.os.UserHandle);
+    method public void startActivityForProfile(android.content.ComponentName, android.os.UserHandle, android.graphics.Rect, android.os.Bundle);
   }
 
   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);
-    method public abstract void onPackageRemoved(android.os.UserHandle, java.lang.String);
-    method public abstract void onPackagesAvailable(android.os.UserHandle, java.lang.String[], boolean);
-    method public abstract void onPackagesUnavailable(android.os.UserHandle, java.lang.String[], boolean);
+    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 class PackageInfo implements android.os.Parcelable {
@@ -10170,8 +10204,8 @@
     method public boolean clipRect(float, float, float, float, android.graphics.Region.Op);
     method public boolean clipRect(float, float, float, float);
     method public boolean clipRect(int, int, int, int);
-    method public boolean clipRegion(android.graphics.Region, android.graphics.Region.Op);
-    method public boolean clipRegion(android.graphics.Region);
+    method public deprecated boolean clipRegion(android.graphics.Region, android.graphics.Region.Op);
+    method public deprecated boolean clipRegion(android.graphics.Region);
     method public void concat(android.graphics.Matrix);
     method public void drawARGB(int, int, int, int);
     method public void drawArc(android.graphics.RectF, float, float, boolean, android.graphics.Paint);
@@ -11236,14 +11270,11 @@
     method public static android.graphics.drawable.Drawable createFromPath(java.lang.String);
     method public static android.graphics.drawable.Drawable createFromResourceStream(android.content.res.Resources, android.util.TypedValue, java.io.InputStream, java.lang.String);
     method public static android.graphics.drawable.Drawable createFromResourceStream(android.content.res.Resources, android.util.TypedValue, java.io.InputStream, java.lang.String, android.graphics.BitmapFactory.Options);
-    method public static android.graphics.drawable.Drawable createFromResourceStreamThemed(android.content.res.Resources, android.util.TypedValue, java.io.InputStream, java.lang.String, android.content.res.Resources.Theme);
-    method public static android.graphics.drawable.Drawable createFromResourceStreamThemed(android.content.res.Resources, android.util.TypedValue, java.io.InputStream, java.lang.String, android.graphics.BitmapFactory.Options, android.content.res.Resources.Theme);
     method public static android.graphics.drawable.Drawable createFromStream(java.io.InputStream, java.lang.String);
-    method public static android.graphics.drawable.Drawable createFromStreamThemed(java.io.InputStream, java.lang.String, android.content.res.Resources.Theme);
     method public static android.graphics.drawable.Drawable createFromXml(android.content.res.Resources, org.xmlpull.v1.XmlPullParser) throws java.io.IOException, org.xmlpull.v1.XmlPullParserException;
+    method public static android.graphics.drawable.Drawable createFromXml(android.content.res.Resources, org.xmlpull.v1.XmlPullParser, android.content.res.Resources.Theme) throws java.io.IOException, org.xmlpull.v1.XmlPullParserException;
     method public static android.graphics.drawable.Drawable createFromXmlInner(android.content.res.Resources, org.xmlpull.v1.XmlPullParser, android.util.AttributeSet) throws java.io.IOException, org.xmlpull.v1.XmlPullParserException;
-    method public static android.graphics.drawable.Drawable createFromXmlInnerThemed(android.content.res.Resources, org.xmlpull.v1.XmlPullParser, android.util.AttributeSet, android.content.res.Resources.Theme) throws java.io.IOException, org.xmlpull.v1.XmlPullParserException;
-    method public static android.graphics.drawable.Drawable createFromXmlThemed(android.content.res.Resources, org.xmlpull.v1.XmlPullParser, android.content.res.Resources.Theme) throws java.io.IOException, org.xmlpull.v1.XmlPullParserException;
+    method public static android.graphics.drawable.Drawable createFromXmlInner(android.content.res.Resources, org.xmlpull.v1.XmlPullParser, android.util.AttributeSet, android.content.res.Resources.Theme) throws java.io.IOException, org.xmlpull.v1.XmlPullParserException;
     method public abstract void draw(android.graphics.Canvas);
     method public int getAlpha();
     method public final android.graphics.Rect getBounds();
@@ -11471,6 +11502,7 @@
   }
 
   public class RippleDrawable extends android.graphics.drawable.LayerDrawable {
+    ctor public RippleDrawable(android.graphics.drawable.Drawable, android.graphics.drawable.Drawable);
   }
 
   public class RotateDrawable extends android.graphics.drawable.Drawable implements android.graphics.drawable.Drawable.Callback {
@@ -11518,7 +11550,6 @@
     method public android.graphics.Paint getPaint();
     method public android.graphics.drawable.ShapeDrawable.ShaderFactory getShaderFactory();
     method public android.graphics.drawable.shapes.Shape getShape();
-    method public android.content.res.ColorStateList getTint();
     method protected boolean inflateTag(java.lang.String, android.content.res.Resources, org.xmlpull.v1.XmlPullParser, android.util.AttributeSet);
     method protected void onDraw(android.graphics.drawable.shapes.Shape, android.graphics.Canvas, android.graphics.Paint);
     method public void setAlpha(int);
@@ -11529,7 +11560,6 @@
     method public void setPadding(android.graphics.Rect);
     method public void setShaderFactory(android.graphics.drawable.ShapeDrawable.ShaderFactory);
     method public void setShape(android.graphics.drawable.shapes.Shape);
-    method public void setTint(android.content.res.ColorStateList);
   }
 
   public static abstract class ShapeDrawable.ShaderFactory {
@@ -11931,6 +11961,7 @@
   public final class Sensor {
     method public int getFifoMaxEventCount();
     method public int getFifoReservedEventCount();
+    method public int getMaxDelay();
     method public float getMaximumRange();
     method public int getMinDelay();
     method public java.lang.String getName();
@@ -11940,6 +11971,9 @@
     method public int getType();
     method public java.lang.String getVendor();
     method public int getVersion();
+    method public boolean isWakeUpSensor();
+    field public static final java.lang.String SENSOR_STRING_TYPE_NON_WAKE_UP_PROXIMITY_SENSOR = "android.sensor.non_wake_up_proximity_sensor";
+    field public static final java.lang.String SENSOR_STRING_TYPE_WAKE_UP_TILT_DETECTOR = "android.sensor.wake_up_tilt_detector";
     field public static final java.lang.String STRING_TYPE_ACCELEROMETER = "android.sensor.accelerometer";
     field public static final java.lang.String STRING_TYPE_AMBIENT_TEMPERATURE = "android.sensor.ambient_temperature";
     field public static final java.lang.String STRING_TYPE_GAME_ROTATION_VECTOR = "android.sensor.game_rotation_vector";
@@ -11961,6 +11995,24 @@
     field public static final java.lang.String STRING_TYPE_STEP_COUNTER = "android.sensor.step_counter";
     field public static final java.lang.String STRING_TYPE_STEP_DETECTOR = "android.sensor.step_detector";
     field public static final deprecated java.lang.String STRING_TYPE_TEMPERATURE = "android.sensor.temperature";
+    field public static final java.lang.String STRING_TYPE_WAKE_UP_ACCELEROMETER = "android.sensor.wake_up_accelerometer";
+    field public static final java.lang.String STRING_TYPE_WAKE_UP_AMBIENT_TEMPERATURE = "android.sensor.wake_up_ambient_temperature";
+    field public static final java.lang.String STRING_TYPE_WAKE_UP_GAME_ROTATION_VECTOR = "android.sensor.wake_up_game_rotation_vector";
+    field public static final java.lang.String STRING_TYPE_WAKE_UP_GEOMAGNETIC_ROTATION_VECTOR = "android.sensor.wake_up_geomagnetic_rotation_vector";
+    field public static final java.lang.String STRING_TYPE_WAKE_UP_GRAVITY = "android.sensor.wake_up_gravity";
+    field public static final java.lang.String STRING_TYPE_WAKE_UP_GYROSCOPE = "android.sensor.wake_up_gyroscope";
+    field public static final java.lang.String STRING_TYPE_WAKE_UP_GYROSCOPE_UNCALIBRATED = "android.sensor.wake_up_gyroscope_uncalibrated";
+    field public static final java.lang.String STRING_TYPE_WAKE_UP_HEART_RATE = "android.sensor.wake_up_heart_rate";
+    field public static final java.lang.String STRING_TYPE_WAKE_UP_LIGHT = "android.sensor.wake_up_light";
+    field public static final java.lang.String STRING_TYPE_WAKE_UP_LINEAR_ACCELERATION = "android.sensor.wake_up_linear_acceleration";
+    field public static final java.lang.String STRING_TYPE_WAKE_UP_MAGNETIC_FIELD = "android.sensor.wake_up_magnetic_field";
+    field public static final java.lang.String STRING_TYPE_WAKE_UP_MAGNETIC_FIELD_UNCALIBRATED = "android.sensor.wake_up_magnetic_field_uncalibrated";
+    field public static final java.lang.String STRING_TYPE_WAKE_UP_ORIENTATION = "android.sensor.wake_up_orientation";
+    field public static final java.lang.String STRING_TYPE_WAKE_UP_PRESSURE = "android.sensor.wake_up_pressure";
+    field public static final java.lang.String STRING_TYPE_WAKE_UP_RELATIVE_HUMIDITY = "android.sensor.wake_up_relative_humidity";
+    field public static final java.lang.String STRING_TYPE_WAKE_UP_ROTATION_VECTOR = "android.sensor.wake_up_rotation_vector";
+    field public static final java.lang.String STRING_TYPE_WAKE_UP_STEP_COUNTER = "android.sensor.wake_up_step_counter";
+    field public static final java.lang.String STRING_TYPE_WAKE_UP_STEP_DETECTOR = "android.sensor.wake_up_step_detector";
     field public static final int TYPE_ACCELEROMETER = 1; // 0x1
     field public static final int TYPE_ALL = -1; // 0xffffffff
     field public static final int TYPE_AMBIENT_TEMPERATURE = 13; // 0xd
@@ -11974,6 +12026,7 @@
     field public static final int TYPE_LINEAR_ACCELERATION = 10; // 0xa
     field public static final int TYPE_MAGNETIC_FIELD = 2; // 0x2
     field public static final int TYPE_MAGNETIC_FIELD_UNCALIBRATED = 14; // 0xe
+    field public static final int TYPE_NON_WAKE_UP_PROXIMITY_SENSOR = 22; // 0x16
     field public static final deprecated int TYPE_ORIENTATION = 3; // 0x3
     field public static final int TYPE_PRESSURE = 6; // 0x6
     field public static final int TYPE_PROXIMITY = 8; // 0x8
@@ -11983,6 +12036,25 @@
     field public static final int TYPE_STEP_COUNTER = 19; // 0x13
     field public static final int TYPE_STEP_DETECTOR = 18; // 0x12
     field public static final deprecated int TYPE_TEMPERATURE = 7; // 0x7
+    field public static final int TYPE_WAKE_UP_ACCELEROMETER = 23; // 0x17
+    field public static final int TYPE_WAKE_UP_AMBIENT_TEMPERATURE = 33; // 0x21
+    field public static final int TYPE_WAKE_UP_GAME_ROTATION_VECTOR = 35; // 0x23
+    field public static final int TYPE_WAKE_UP_GEOMAGNETIC_ROTATION_VECTOR = 39; // 0x27
+    field public static final int TYPE_WAKE_UP_GRAVITY = 29; // 0x1d
+    field public static final int TYPE_WAKE_UP_GYROSCOPE = 26; // 0x1a
+    field public static final int TYPE_WAKE_UP_GYROSCOPE_UNCALIBRATED = 36; // 0x24
+    field public static final int TYPE_WAKE_UP_HEART_RATE = 40; // 0x28
+    field public static final int TYPE_WAKE_UP_LIGHT = 27; // 0x1b
+    field public static final int TYPE_WAKE_UP_LINEAR_ACCELERATION = 30; // 0x1e
+    field public static final int TYPE_WAKE_UP_MAGNETIC_FIELD = 24; // 0x18
+    field public static final int TYPE_WAKE_UP_MAGNETIC_FIELD_UNCALIBRATED = 34; // 0x22
+    field public static final int TYPE_WAKE_UP_ORIENTATION = 25; // 0x19
+    field public static final int TYPE_WAKE_UP_PRESSURE = 28; // 0x1c
+    field public static final int TYPE_WAKE_UP_RELATIVE_HUMIDITY = 32; // 0x20
+    field public static final int TYPE_WAKE_UP_ROTATION_VECTOR = 31; // 0x1f
+    field public static final int TYPE_WAKE_UP_STEP_COUNTER = 38; // 0x26
+    field public static final int TYPE_WAKE_UP_STEP_DETECTOR = 37; // 0x25
+    field public static final int TYPE_WAKE_UP_TILT_DETECTOR = 41; // 0x29
   }
 
   public class SensorEvent {
@@ -12084,6 +12156,7 @@
     field public static final int SENSOR_STATUS_ACCURACY_HIGH = 3; // 0x3
     field public static final int SENSOR_STATUS_ACCURACY_LOW = 1; // 0x1
     field public static final int SENSOR_STATUS_ACCURACY_MEDIUM = 2; // 0x2
+    field public static final int SENSOR_STATUS_NO_CONTACT = -1; // 0xffffffff
     field public static final int SENSOR_STATUS_UNRELIABLE = 0; // 0x0
     field public static final deprecated int SENSOR_TEMPERATURE = 4; // 0x4
     field public static final deprecated int SENSOR_TRICORDER = 64; // 0x40
@@ -12130,9 +12203,11 @@
 
   public static abstract class CameraCaptureSession.CaptureListener {
     ctor public CameraCaptureSession.CaptureListener();
-    method public void onCaptureCompleted(android.hardware.camera2.CameraDevice, android.hardware.camera2.CaptureRequest, android.hardware.camera2.CaptureResult);
+    method public void onCaptureCompleted(android.hardware.camera2.CameraDevice, android.hardware.camera2.CaptureRequest, android.hardware.camera2.TotalCaptureResult);
     method public void onCaptureFailed(android.hardware.camera2.CameraDevice, android.hardware.camera2.CaptureRequest, android.hardware.camera2.CaptureFailure);
-    method public void onCaptureSequenceCompleted(android.hardware.camera2.CameraDevice, int, int);
+    method public void onCaptureProgressed(android.hardware.camera2.CameraDevice, android.hardware.camera2.CaptureRequest, android.hardware.camera2.CaptureResult);
+    method public void onCaptureSequenceAborted(android.hardware.camera2.CameraDevice, int);
+    method public void onCaptureSequenceCompleted(android.hardware.camera2.CameraDevice, int, long);
     method public void onCaptureStarted(android.hardware.camera2.CameraDevice, android.hardware.camera2.CaptureRequest, long);
   }
 
@@ -12159,7 +12234,9 @@
     field public static final android.hardware.camera2.CameraCharacteristics.Key CONTROL_AVAILABLE_SCENE_MODES;
     field public static final android.hardware.camera2.CameraCharacteristics.Key CONTROL_AVAILABLE_VIDEO_STABILIZATION_MODES;
     field public static final android.hardware.camera2.CameraCharacteristics.Key CONTROL_AWB_AVAILABLE_MODES;
-    field public static final android.hardware.camera2.CameraCharacteristics.Key CONTROL_MAX_REGIONS;
+    field public static final android.hardware.camera2.CameraCharacteristics.Key CONTROL_MAX_REGIONS_AE;
+    field public static final android.hardware.camera2.CameraCharacteristics.Key CONTROL_MAX_REGIONS_AF;
+    field public static final android.hardware.camera2.CameraCharacteristics.Key CONTROL_MAX_REGIONS_AWB;
     field public static final android.hardware.camera2.CameraCharacteristics.Key EDGE_AVAILABLE_EDGE_MODES;
     field public static final android.hardware.camera2.CameraCharacteristics.Key FLASH_INFO_AVAILABLE;
     field public static final android.hardware.camera2.CameraCharacteristics.Key HOT_PIXEL_AVAILABLE_HOT_PIXEL_MODES;
@@ -12173,11 +12250,12 @@
     field public static final android.hardware.camera2.CameraCharacteristics.Key LENS_INFO_FOCUS_DISTANCE_CALIBRATION;
     field public static final android.hardware.camera2.CameraCharacteristics.Key LENS_INFO_HYPERFOCAL_DISTANCE;
     field public static final android.hardware.camera2.CameraCharacteristics.Key LENS_INFO_MINIMUM_FOCUS_DISTANCE;
-    field public static final android.hardware.camera2.CameraCharacteristics.Key LENS_INFO_SHADING_MAP_SIZE;
     field public static final android.hardware.camera2.CameraCharacteristics.Key NOISE_REDUCTION_AVAILABLE_NOISE_REDUCTION_MODES;
     field public static final android.hardware.camera2.CameraCharacteristics.Key REQUEST_AVAILABLE_CAPABILITIES;
     field public static final android.hardware.camera2.CameraCharacteristics.Key REQUEST_MAX_NUM_INPUT_STREAMS;
-    field public static final android.hardware.camera2.CameraCharacteristics.Key REQUEST_MAX_NUM_OUTPUT_STREAMS;
+    field public static final android.hardware.camera2.CameraCharacteristics.Key REQUEST_MAX_NUM_OUTPUT_PROC;
+    field public static final android.hardware.camera2.CameraCharacteristics.Key REQUEST_MAX_NUM_OUTPUT_PROC_STALLING;
+    field public static final android.hardware.camera2.CameraCharacteristics.Key REQUEST_MAX_NUM_OUTPUT_RAW;
     field public static final android.hardware.camera2.CameraCharacteristics.Key REQUEST_PARTIAL_RESULT_COUNT;
     field public static final android.hardware.camera2.CameraCharacteristics.Key REQUEST_PIPELINE_MAX_DEPTH;
     field public static final android.hardware.camera2.CameraCharacteristics.Key SCALER_AVAILABLE_MAX_DIGITAL_ZOOM;
@@ -12239,9 +12317,11 @@
 
   public static abstract deprecated class CameraDevice.CaptureListener {
     ctor public CameraDevice.CaptureListener();
-    method public void onCaptureCompleted(android.hardware.camera2.CameraDevice, android.hardware.camera2.CaptureRequest, android.hardware.camera2.CaptureResult);
+    method public void onCaptureCompleted(android.hardware.camera2.CameraDevice, android.hardware.camera2.CaptureRequest, android.hardware.camera2.TotalCaptureResult);
     method public void onCaptureFailed(android.hardware.camera2.CameraDevice, android.hardware.camera2.CaptureRequest, android.hardware.camera2.CaptureFailure);
-    method public void onCaptureSequenceCompleted(android.hardware.camera2.CameraDevice, int, int);
+    method public void onCaptureProgressed(android.hardware.camera2.CameraDevice, android.hardware.camera2.CaptureRequest, android.hardware.camera2.CaptureResult);
+    method public void onCaptureSequenceAborted(android.hardware.camera2.CameraDevice, int);
+    method public void onCaptureSequenceCompleted(android.hardware.camera2.CameraDevice, int, long);
     method public void onCaptureStarted(android.hardware.camera2.CameraDevice, android.hardware.camera2.CaptureRequest, long);
   }
 
@@ -12395,6 +12475,7 @@
     field public static final int NOISE_REDUCTION_MODE_HIGH_QUALITY = 2; // 0x2
     field public static final int NOISE_REDUCTION_MODE_OFF = 0; // 0x0
     field public static final int REQUEST_AVAILABLE_CAPABILITIES_DNG = 5; // 0x5
+    field public static final int REQUEST_AVAILABLE_CAPABILITIES_MANUAL_POST_PROCESSING = 3; // 0x3
     field public static final int REQUEST_AVAILABLE_CAPABILITIES_MANUAL_SENSOR = 2; // 0x2
     field public static final int REQUEST_AVAILABLE_CAPABILITIES_ZSL = 4; // 0x4
     field public static final int SCALER_CROPPING_TYPE_CENTER_ONLY = 0; // 0x0
@@ -12488,9 +12569,7 @@
     field public static final android.hardware.camera2.CaptureRequest.Key EDGE_MODE;
     field public static final android.hardware.camera2.CaptureRequest.Key FLASH_MODE;
     field public static final android.hardware.camera2.CaptureRequest.Key HOT_PIXEL_MODE;
-    field public static final android.hardware.camera2.CaptureRequest.Key JPEG_GPS_COORDINATES;
-    field public static final android.hardware.camera2.CaptureRequest.Key JPEG_GPS_PROCESSING_METHOD;
-    field public static final android.hardware.camera2.CaptureRequest.Key JPEG_GPS_TIMESTAMP;
+    field public static final android.hardware.camera2.CaptureRequest.Key JPEG_GPS_LOCATION;
     field public static final android.hardware.camera2.CaptureRequest.Key JPEG_ORIENTATION;
     field public static final android.hardware.camera2.CaptureRequest.Key JPEG_QUALITY;
     field public static final android.hardware.camera2.CaptureRequest.Key JPEG_THUMBNAIL_QUALITY;
@@ -12511,9 +12590,7 @@
     field public static final android.hardware.camera2.CaptureRequest.Key STATISTICS_FACE_DETECT_MODE;
     field public static final android.hardware.camera2.CaptureRequest.Key STATISTICS_HOT_PIXEL_MAP_MODE;
     field public static final android.hardware.camera2.CaptureRequest.Key STATISTICS_LENS_SHADING_MAP_MODE;
-    field public static final android.hardware.camera2.CaptureRequest.Key TONEMAP_CURVE_BLUE;
-    field public static final android.hardware.camera2.CaptureRequest.Key TONEMAP_CURVE_GREEN;
-    field public static final android.hardware.camera2.CaptureRequest.Key TONEMAP_CURVE_RED;
+    field public static final android.hardware.camera2.CaptureRequest.Key TONEMAP_CURVE;
     field public static final android.hardware.camera2.CaptureRequest.Key TONEMAP_MODE;
   }
 
@@ -12532,7 +12609,7 @@
     method public final int hashCode();
   }
 
-  public final class CaptureResult extends android.hardware.camera2.CameraMetadata {
+  public class CaptureResult extends android.hardware.camera2.CameraMetadata {
     method public T get(android.hardware.camera2.CaptureResult.Key<T>);
     method public int getFrameNumber();
     method public android.hardware.camera2.CaptureRequest getRequest();
@@ -12566,9 +12643,7 @@
     field public static final android.hardware.camera2.CaptureResult.Key FLASH_MODE;
     field public static final android.hardware.camera2.CaptureResult.Key FLASH_STATE;
     field public static final android.hardware.camera2.CaptureResult.Key HOT_PIXEL_MODE;
-    field public static final android.hardware.camera2.CaptureResult.Key JPEG_GPS_COORDINATES;
-    field public static final android.hardware.camera2.CaptureResult.Key JPEG_GPS_PROCESSING_METHOD;
-    field public static final android.hardware.camera2.CaptureResult.Key JPEG_GPS_TIMESTAMP;
+    field public static final android.hardware.camera2.CaptureResult.Key JPEG_GPS_LOCATION;
     field public static final android.hardware.camera2.CaptureResult.Key JPEG_ORIENTATION;
     field public static final android.hardware.camera2.CaptureResult.Key JPEG_QUALITY;
     field public static final android.hardware.camera2.CaptureResult.Key JPEG_THUMBNAIL_QUALITY;
@@ -12597,12 +12672,10 @@
     field public static final android.hardware.camera2.CaptureResult.Key STATISTICS_FACE_DETECT_MODE;
     field public static final android.hardware.camera2.CaptureResult.Key STATISTICS_HOT_PIXEL_MAP;
     field public static final android.hardware.camera2.CaptureResult.Key STATISTICS_HOT_PIXEL_MAP_MODE;
-    field public static final android.hardware.camera2.CaptureResult.Key STATISTICS_LENS_SHADING_MAP;
+    field public static final android.hardware.camera2.CaptureResult.Key STATISTICS_LENS_SHADING_CORRECTION_MAP;
     field public static final android.hardware.camera2.CaptureResult.Key STATISTICS_LENS_SHADING_MAP_MODE;
     field public static final android.hardware.camera2.CaptureResult.Key STATISTICS_SCENE_FLICKER;
-    field public static final android.hardware.camera2.CaptureResult.Key TONEMAP_CURVE_BLUE;
-    field public static final android.hardware.camera2.CaptureResult.Key TONEMAP_CURVE_GREEN;
-    field public static final android.hardware.camera2.CaptureResult.Key TONEMAP_CURVE_RED;
+    field public static final android.hardware.camera2.CaptureResult.Key TONEMAP_CURVE;
     field public static final android.hardware.camera2.CaptureResult.Key TONEMAP_MODE;
   }
 
@@ -12625,6 +12698,10 @@
     method public void writeInputStream(java.io.OutputStream, android.util.Size, java.io.InputStream, long) throws java.io.IOException;
   }
 
+  public final class TotalCaptureResult extends android.hardware.camera2.CaptureResult {
+    method public java.util.List<android.hardware.camera2.CaptureResult> getPartialResults();
+  }
+
 }
 
 package android.hardware.camera2.params {
@@ -12672,6 +12749,9 @@
     method public int getWidth();
     method public int getX();
     method public int getY();
+    field public static final int METERING_WEIGHT_DONT_CARE = 0; // 0x0
+    field public static final int METERING_WEIGHT_MAX = 1000; // 0x3e8
+    field public static final int METERING_WEIGHT_MIN = 0; // 0x0
   }
 
   public final class RggbChannelVector {
@@ -14243,7 +14323,6 @@
     method public final void release();
     method public final void releaseOutputBuffer(int, boolean);
     method public final void releaseOutputBuffer(int, long);
-    method public void setNotificationCallback(android.media.MediaCodec.NotificationCallback);
     method public final void setParameters(android.os.Bundle);
     method public final void setVideoScalingMode(int);
     method public final void signalEndOfInputStream();
@@ -14293,10 +14372,6 @@
     field public int numSubSamples;
   }
 
-  public static abstract interface MediaCodec.NotificationCallback {
-    method public abstract void onCodecNotify(android.media.MediaCodec);
-  }
-
   public final class MediaCodecInfo {
     method public final android.media.MediaCodecInfo.CodecCapabilities getCapabilitiesForType(java.lang.String);
     method public final java.lang.String getName();
@@ -14625,6 +14700,7 @@
     method public long getLong(java.lang.String);
     method public android.media.Rating getRating(java.lang.String);
     method public java.lang.String getString(java.lang.String);
+    method public java.util.Set<java.lang.String> keySet();
     method public int size();
     method public void writeToParcel(android.os.Parcel, int);
     field public static final android.os.Parcelable.Creator CREATOR;
@@ -15762,56 +15838,81 @@
   public final class MediaController {
     method public void addCallback(android.media.session.MediaController.Callback);
     method public void addCallback(android.media.session.MediaController.Callback, android.os.Handler);
+    method public boolean dispatchMediaButtonEvent(android.view.KeyEvent);
     method public static android.media.session.MediaController fromToken(android.media.session.MediaSessionToken);
-    method public android.media.session.TransportController getTransportController();
+    method public android.media.MediaMetadata getMetadata();
+    method public android.media.session.PlaybackState getPlaybackState();
+    method public int getRatingType();
+    method public android.media.session.MediaController.TransportControls getTransportControls();
     method public void removeCallback(android.media.session.MediaController.Callback);
-    method public void sendCommand(java.lang.String, android.os.Bundle, android.os.ResultReceiver);
-    method public void sendMediaButton(int);
+    method public void sendControlCommand(java.lang.String, android.os.Bundle, android.os.ResultReceiver);
   }
 
   public static abstract class MediaController.Callback {
     ctor public MediaController.Callback();
-    method public void onEvent(java.lang.String, android.os.Bundle);
+    method public void onMetadataChanged(android.media.MediaMetadata);
+    method public void onPlaybackStateChanged(android.media.session.PlaybackState);
+    method public void onSessionEvent(java.lang.String, android.os.Bundle);
+  }
+
+  public final class MediaController.TransportControls {
+    method public void fastForward();
+    method public void pause();
+    method public void play();
+    method public void rewind();
+    method public void seekTo(long);
+    method public void setRating(android.media.Rating);
+    method public void skipToNext();
+    method public void skipToPrevious();
+    method public void stop();
   }
 
   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);
+    method public void addTransportControlsCallback(android.media.session.MediaSession.TransportControlsCallback);
+    method public void addTransportControlsCallback(android.media.session.MediaSession.TransportControlsCallback, android.os.Handler);
     method public android.media.session.MediaSessionToken getSessionToken();
-    method public android.media.session.TransportPerformer getTransportPerformer();
     method public boolean isActive();
     method public void release();
     method public void removeCallback(android.media.session.MediaSession.Callback);
-    method public void sendEvent(java.lang.String, android.os.Bundle);
+    method public void removeTransportControlsCallback(android.media.session.MediaSession.TransportControlsCallback);
+    method public void sendSessionEvent(java.lang.String, android.os.Bundle);
     method public void setActive(boolean);
     method public void setFlags(int);
     method public void setLaunchPendingIntent(android.app.PendingIntent);
-    method public void useLocalPlayback(int);
-    method public void useRemotePlayback(android.media.session.RemoteVolumeProvider);
+    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);
     field public static final int FLAG_HANDLES_MEDIA_BUTTONS = 1; // 0x1
     field public static final int FLAG_HANDLES_TRANSPORT_CONTROLS = 2; // 0x2
   }
 
   public static abstract class MediaSession.Callback {
     ctor public MediaSession.Callback();
-    method public void onCommand(java.lang.String, android.os.Bundle, android.os.ResultReceiver);
-    method public void onMediaButton(android.content.Intent);
+    method public void onControlCommand(java.lang.String, android.os.Bundle, android.os.ResultReceiver);
+    method public void onMediaButtonEvent(android.content.Intent);
   }
 
-  public final class MediaSessionInfo implements android.os.Parcelable {
-    method public int describeContents();
-    method public java.lang.String getId();
-    method public java.lang.String getPackageName();
-    method public void writeToParcel(android.os.Parcel, int);
-    field public static final android.os.Parcelable.Creator CREATOR;
+  public static abstract class MediaSession.TransportControlsCallback {
+    ctor public MediaSession.TransportControlsCallback();
+    method public void onFastForward();
+    method public void onPause();
+    method public void onPlay();
+    method public void onRewind();
+    method public void onSeekTo(long);
+    method public void onSetRating(android.media.Rating);
+    method public void onSkipToNext();
+    method public void onSkipToPrevious();
+    method public void onStop();
   }
 
   public final class MediaSessionManager {
     method public android.media.session.MediaSession createSession(java.lang.String);
-    method public java.util.List<android.media.session.MediaController> getActiveSessions(android.content.ComponentName);
   }
 
-  public class MediaSessionToken implements android.os.Parcelable {
+  public final class MediaSessionToken implements android.os.Parcelable {
     method public int describeContents();
     method public void writeToParcel(android.os.Parcel, int);
     field public static final android.os.Parcelable.Creator CREATOR;
@@ -15823,95 +15924,224 @@
     method public int describeContents();
     method public long getActions();
     method public long getBufferPosition();
-    method public java.lang.String getErrorMessage();
+    method public java.lang.CharSequence getErrorMessage();
+    method public float getPlaybackRate();
     method public long getPosition();
-    method public float getRate();
     method public int getState();
     method public void setActions(long);
     method public void setBufferPosition(long);
-    method public void setErrorMessage(java.lang.String);
+    method public void setErrorMessage(java.lang.CharSequence);
     method public void setState(int, long, float);
     method public void writeToParcel(android.os.Parcel, int);
-    field public static final long ACTION_FASTFORWARD = 64L; // 0x40L
-    field public static final long ACTION_NEXT_ITEM = 32L; // 0x20L
+    field public static final long ACTION_FAST_FORWARD = 64L; // 0x40L
     field public static final long ACTION_PAUSE = 2L; // 0x2L
     field public static final long ACTION_PLAY = 4L; // 0x4L
     field public static final long ACTION_PLAY_PAUSE = 512L; // 0x200L
-    field public static final long ACTION_PREVIOUS_ITEM = 16L; // 0x10L
-    field public static final long ACTION_RATING = 128L; // 0x80L
     field public static final long ACTION_REWIND = 8L; // 0x8L
     field public static final long ACTION_SEEK_TO = 256L; // 0x100L
+    field public static final long ACTION_SET_RATING = 128L; // 0x80L
+    field public static final long ACTION_SKIP_TO_NEXT = 32L; // 0x20L
+    field public static final long ACTION_SKIP_TO_PREVIOUS = 16L; // 0x10L
     field public static final long ACTION_STOP = 1L; // 0x1L
     field public static final android.os.Parcelable.Creator CREATOR;
     field public static final long PLAYBACK_POSITION_UNKNOWN = -1L; // 0xffffffffffffffffL
-    field public static final int PLAYSTATE_BUFFERING = 6; // 0x6
-    field public static final int PLAYSTATE_ERROR = 7; // 0x7
-    field public static final int PLAYSTATE_FAST_FORWARDING = 4; // 0x4
-    field public static final int PLAYSTATE_NONE = 0; // 0x0
-    field public static final int PLAYSTATE_PAUSED = 2; // 0x2
-    field public static final int PLAYSTATE_PLAYING = 3; // 0x3
-    field public static final int PLAYSTATE_REWINDING = 5; // 0x5
-    field public static final int PLAYSTATE_SKIPPING_BACKWARDS = 9; // 0x9
-    field public static final int PLAYSTATE_SKIPPING_FORWARDS = 10; // 0xa
-    field public static final int PLAYSTATE_STOPPED = 1; // 0x1
+    field public static final int STATE_BUFFERING = 6; // 0x6
+    field public static final int STATE_ERROR = 7; // 0x7
+    field public static final int STATE_FAST_FORWARDING = 4; // 0x4
+    field public static final int STATE_NONE = 0; // 0x0
+    field public static final int STATE_PAUSED = 2; // 0x2
+    field public static final int STATE_PLAYING = 3; // 0x3
+    field public static final int STATE_REWINDING = 5; // 0x5
+    field public static final int STATE_SKIPPING_TO_NEXT = 10; // 0xa
+    field public static final int STATE_SKIPPING_TO_PREVIOUS = 9; // 0x9
+    field public static final int STATE_STOPPED = 1; // 0x1
   }
 
   public abstract class RemoteVolumeProvider {
     ctor public RemoteVolumeProvider(int, int);
-    method public abstract int getCurrentVolume();
-    method public final int getFlags();
     method public final int getMaxVolume();
+    method public final int getVolumeControl();
     method public final void notifyVolumeChanged();
-    method public void onAdjustVolume(int);
-    method public void onSetVolume(int);
-    field public static final int FLAG_VOLUME_ABSOLUTE = 2; // 0x2
-    field public static final int FLAG_VOLUME_RELATIVE = 1; // 0x1
+    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
   }
 
-  public final class TransportController {
-    method public void addStateListener(android.media.session.TransportController.TransportStateListener);
-    method public void addStateListener(android.media.session.TransportController.TransportStateListener, android.os.Handler);
-    method public void fastForward();
-    method public android.media.MediaMetadata getMetadata();
-    method public android.media.session.PlaybackState getPlaybackState();
-    method public int getRatingType();
-    method public void next();
-    method public void pause();
-    method public void play();
-    method public void previous();
-    method public void rate(android.media.Rating);
-    method public void removeStateListener(android.media.session.TransportController.TransportStateListener);
-    method public void rewind();
-    method public void seekTo(long);
-    method public void stop();
+}
+
+package android.media.tv {
+
+  public final class TvContract {
+    method public static final android.net.Uri buildChannelUri(long);
+    method public static final android.net.Uri buildChannelsUriForInput(android.content.ComponentName);
+    method public static final android.net.Uri buildChannelsUriForInput(android.content.ComponentName, boolean);
+    method public static final android.net.Uri buildProgramUri(long);
+    method public static final android.net.Uri buildProgramsUriForChannel(android.net.Uri);
+    method public static final android.net.Uri buildProgramsUriForChannel(android.net.Uri, long, long);
+    field public static final java.lang.String AUTHORITY = "android.media.tv";
   }
 
-  public static abstract class TransportController.TransportStateListener {
-    ctor public TransportController.TransportStateListener();
-    method public void onMetadataChanged(android.media.MediaMetadata);
-    method public void onPlaybackStateChanged(android.media.session.PlaybackState);
+  public static abstract interface TvContract.BaseTvColumns implements android.provider.BaseColumns {
+    field public static final java.lang.String COLUMN_PACKAGE_NAME = "package_name";
   }
 
-  public final class TransportPerformer {
-    method public void addListener(android.media.session.TransportPerformer.Listener);
-    method public void addListener(android.media.session.TransportPerformer.Listener, android.os.Handler);
-    method public void removeListener(android.media.session.TransportPerformer.Listener);
-    method public final void setMetadata(android.media.MediaMetadata);
-    method public final void setPlaybackState(android.media.session.PlaybackState);
+  public static final class TvContract.Channels implements android.media.tv.TvContract.BaseTvColumns {
+    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";
+    field public static final java.lang.String COLUMN_DISPLAY_NUMBER = "display_number";
+    field public static final java.lang.String COLUMN_INTERNAL_PROVIDER_DATA = "internal_provider_data";
+    field public static final java.lang.String COLUMN_ORIGINAL_NETWORK_ID = "original_network_id";
+    field public static final java.lang.String COLUMN_SEARCHABLE = "searchable";
+    field public static final java.lang.String COLUMN_SERVICE_ID = "service_id";
+    field public static final java.lang.String COLUMN_SERVICE_NAME = "service_name";
+    field public static final java.lang.String COLUMN_SERVICE_TYPE = "service_type";
+    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 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;
+    field public static final int SERVICE_TYPE_AUDIO = 2; // 0x2
+    field public static final int SERVICE_TYPE_AUDIO_VIDEO = 1; // 0x1
+    field public static final int SERVICE_TYPE_OTHER = 0; // 0x0
+    field public static final int TYPE_1SEG = 263168; // 0x40400
+    field public static final int TYPE_ATSC_C = 197120; // 0x30200
+    field public static final int TYPE_ATSC_M_H = 197120; // 0x30200
+    field public static final int TYPE_ATSC_T = 196608; // 0x30000
+    field public static final int TYPE_CMMB = 327936; // 0x50100
+    field public static final int TYPE_DTMB = 327680; // 0x50000
+    field public static final int TYPE_DVB_C = 131584; // 0x20200
+    field public static final int TYPE_DVB_C2 = 131585; // 0x20201
+    field public static final int TYPE_DVB_H = 131840; // 0x20300
+    field public static final int TYPE_DVB_S = 131328; // 0x20100
+    field public static final int TYPE_DVB_S2 = 131329; // 0x20101
+    field public static final int TYPE_DVB_SH = 132096; // 0x20400
+    field public static final int TYPE_DVB_T = 131072; // 0x20000
+    field public static final int TYPE_DVB_T2 = 131073; // 0x20001
+    field public static final int TYPE_ISDB_C = 262912; // 0x40300
+    field public static final int TYPE_ISDB_S = 262656; // 0x40200
+    field public static final int TYPE_ISDB_T = 262144; // 0x40000
+    field public static final int TYPE_ISDB_TB = 262400; // 0x40100
+    field public static final int TYPE_OTHER = 0; // 0x0
+    field public static final int TYPE_PASSTHROUGH = 65536; // 0x10000
+    field public static final int TYPE_S_DMB = 393472; // 0x60100
+    field public static final int TYPE_T_DMB = 393216; // 0x60000
   }
 
-  public static abstract class TransportPerformer.Listener {
-    ctor public TransportPerformer.Listener();
-    method public void onFastForward();
-    method public void onNext();
-    method public void onPause();
-    method public void onPlay();
-    method public void onPrevious();
-    method public void onRate(android.media.Rating);
-    method public void onRewind();
-    method public void onRouteFocusChange(int);
-    method public void onSeekTo(long);
-    method public void onStop();
+  public static final class TvContract.Programs implements android.media.tv.TvContract.BaseTvColumns {
+    field public static final java.lang.String COLUMN_AUDIO_LANGUAGE = "audio_language";
+    field public static final java.lang.String COLUMN_BROADCAST_GENRE = "broadcast_genre";
+    field public static final java.lang.String COLUMN_CANONICAL_GENRE = "canonical_genre";
+    field public static final java.lang.String COLUMN_CHANNEL_ID = "channel_id";
+    field public static final java.lang.String COLUMN_END_TIME_UTC_MILLIS = "end_time_utc_millis";
+    field public static final java.lang.String COLUMN_INTERNAL_PROVIDER_DATA = "internal_provider_data";
+    field public static final java.lang.String COLUMN_LONG_DESCRIPTION = "long_description";
+    field public static final java.lang.String COLUMN_SHORT_DESCRIPTION = "short_description";
+    field public static final java.lang.String COLUMN_START_TIME_UTC_MILLIS = "start_time_utc_millis";
+    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 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;
+  }
+
+  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";
+  }
+
+  public final class TvInputInfo implements android.os.Parcelable {
+    method public int describeContents();
+    method public android.content.ComponentName getComponent();
+    method public java.lang.String getId();
+    method public android.content.Intent getIntentForSettingsActivity();
+    method public android.content.Intent getIntentForSetupActivity();
+    method public java.lang.String getPackageName();
+    method public java.lang.String getServiceName();
+    method public java.lang.CharSequence loadLabel(android.content.pm.PackageManager);
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final java.lang.String EXTRA_SERVICE_NAME = "serviceName";
+  }
+
+  public final class TvInputManager {
+    method public void createSession(java.lang.String, android.media.tv.TvInputManager.SessionCallback, android.os.Handler);
+    method public boolean getAvailability(java.lang.String);
+    method public java.util.List<android.media.tv.TvInputInfo> getTvInputList();
+    method public void registerListener(java.lang.String, android.media.tv.TvInputManager.TvInputListener, android.os.Handler);
+    method public void unregisterListener(java.lang.String, android.media.tv.TvInputManager.TvInputListener);
+  }
+
+  public static final class TvInputManager.Session {
+    method public void release();
+    method public void setVolume(float);
+    method public void tune(android.net.Uri);
+  }
+
+  public static abstract class TvInputManager.SessionCallback {
+    ctor public TvInputManager.SessionCallback();
+    method public void onSessionCreated(android.media.tv.TvInputManager.Session);
+    method public void onSessionReleased(android.media.tv.TvInputManager.Session);
+  }
+
+  public static abstract class TvInputManager.TvInputListener {
+    ctor public TvInputManager.TvInputListener();
+    method public void onAvailabilityChanged(java.lang.String, boolean);
+  }
+
+  public abstract class TvInputService extends android.app.Service {
+    ctor public TvInputService();
+    method public final android.os.IBinder onBind(android.content.Intent);
+    method public abstract android.media.tv.TvInputService.TvInputSessionImpl onCreateSession();
+    method public final void setAvailable(boolean);
+    field public static final java.lang.String SERVICE_INTERFACE = "android.media.tv.TvInputService";
+    field public static final java.lang.String SERVICE_META_DATA = "android.media.tv.input";
+  }
+
+  public abstract class TvInputService.TvInputSessionImpl implements android.view.KeyEvent.Callback {
+    ctor public TvInputService.TvInputSessionImpl();
+    method public android.view.View onCreateOverlayView();
+    method public boolean onGenericMotionEvent(android.view.MotionEvent);
+    method public boolean onKeyDown(int, android.view.KeyEvent);
+    method public boolean onKeyLongPress(int, android.view.KeyEvent);
+    method public boolean onKeyMultiple(int, int, android.view.KeyEvent);
+    method public boolean onKeyUp(int, android.view.KeyEvent);
+    method public abstract void onRelease();
+    method public abstract boolean onSetSurface(android.view.Surface);
+    method public abstract void onSetVolume(float);
+    method public boolean onTouchEvent(android.view.MotionEvent);
+    method public boolean onTrackballEvent(android.view.MotionEvent);
+    method public abstract boolean onTune(android.net.Uri);
+    method public void setOverlayViewEnabled(boolean);
+  }
+
+  public class TvView extends android.view.SurfaceView {
+    ctor public TvView(android.content.Context);
+    ctor public TvView(android.content.Context, android.util.AttributeSet);
+    ctor public TvView(android.content.Context, android.util.AttributeSet, int);
+    method public void bindTvInput(java.lang.String, android.media.tv.TvInputManager.SessionCallback);
+    method public boolean dispatchUnhandledInputEvent(android.view.InputEvent);
+    method public boolean onUnhandledInputEvent(android.view.InputEvent);
+    method public void setOnUnhandledInputEventListener(android.media.tv.TvView.OnUnhandledInputEventListener);
+    method public void unbindTvInput();
+  }
+
+  public static abstract interface TvView.OnUnhandledInputEventListener {
+    method public abstract boolean onUnhandledInputEvent(android.view.InputEvent);
   }
 
 }
@@ -17237,86 +17467,6 @@
     method public abstract void onStartSuccess(java.lang.String);
   }
 
-  public class WifiScanner {
-    method public void configureWifiChange(int, int, int, int, int, android.net.wifi.WifiScanner.HotspotInfo[]);
-    method public void resetHotlist(android.net.wifi.WifiScanner.HotlistListener);
-    method public void retrieveScanResults(boolean, android.net.wifi.WifiScanner.ScanListener);
-    method public void setHotlist(android.net.wifi.WifiScanner.HotspotInfo[], int, android.net.wifi.WifiScanner.HotlistListener);
-    method public void startBackgroundScan(android.net.wifi.WifiScanner.ScanSettings, android.net.wifi.WifiScanner.ScanListener);
-    method public void startTrackingWifiChange(android.net.wifi.WifiScanner.WifiChangeListener);
-    method public void stopBackgroundScan(android.net.wifi.WifiScanner.ScanListener);
-    method public void stopTrackingWifiChange(android.net.wifi.WifiScanner.WifiChangeListener);
-    field public static final int MAX_SCAN_PERIOD_MS = 1024000; // 0xfa000
-    field public static final int MIN_SCAN_PERIOD_MS = 2000; // 0x7d0
-    field public static final int REASON_CONFLICTING_REQUEST = -4; // 0xfffffffc
-    field public static final int REASON_INVALID_LISTENER = -2; // 0xfffffffe
-    field public static final int REASON_INVALID_REQUEST = -3; // 0xfffffffd
-    field public static final int REASON_SUCCEEDED = 0; // 0x0
-    field public static final int REASON_UNSPECIFIED = -1; // 0xffffffff
-    field public static final int REPORT_EVENT_AFTER_BUFFER_FULL = 0; // 0x0
-    field public static final int REPORT_EVENT_AFTER_EACH_SCAN = 1; // 0x1
-    field public static final int REPORT_EVENT_FULL_SCAN_RESULT = 2; // 0x2
-    field public static final int WIFI_BAND_24_GHZ = 1; // 0x1
-    field public static final int WIFI_BAND_5_GHZ = 2; // 0x2
-    field public static final int WIFI_BAND_5_GHZ_DFS_ONLY = 4; // 0x4
-    field public static final int WIFI_BAND_5_GHZ_WITH_DFS = 6; // 0x6
-    field public static final int WIFI_BAND_BOTH = 3; // 0x3
-    field public static final int WIFI_BAND_BOTH_WITH_DFS = 7; // 0x7
-    field public static final int WIFI_BAND_UNSPECIFIED = 0; // 0x0
-  }
-
-  public static class WifiScanner.ChannelSpec {
-    ctor public WifiScanner.ChannelSpec(int);
-    field public int frequency;
-  }
-
-  public static class WifiScanner.FullScanResult implements android.os.Parcelable {
-    ctor public WifiScanner.FullScanResult();
-    method public int describeContents();
-    method public void writeToParcel(android.os.Parcel, int);
-    field public android.net.wifi.WifiScanner.InformationElement[] informationElements;
-    field public android.net.wifi.ScanResult result;
-  }
-
-  public static abstract interface WifiScanner.HotlistListener {
-    method public abstract void onFound(android.net.wifi.ScanResult[]);
-  }
-
-  public static class WifiScanner.HotspotInfo {
-    ctor public WifiScanner.HotspotInfo();
-    field public java.lang.String bssid;
-    field public int frequencyHint;
-    field public int high;
-    field public int low;
-  }
-
-  public static class WifiScanner.InformationElement {
-    ctor public WifiScanner.InformationElement();
-    field public byte[] bytes;
-    field public int id;
-  }
-
-  public static abstract interface WifiScanner.ScanListener {
-    method public abstract void onFullResult(android.net.wifi.WifiScanner.FullScanResult);
-    method public abstract void onPeriodChanged(int);
-    method public abstract void onResults(android.net.wifi.ScanResult[]);
-  }
-
-  public static class WifiScanner.ScanSettings implements android.os.Parcelable {
-    ctor public WifiScanner.ScanSettings();
-    method public int describeContents();
-    method public void writeToParcel(android.os.Parcel, int);
-    field public int band;
-    field public android.net.wifi.WifiScanner.ChannelSpec[] channels;
-    field public int periodInMs;
-    field public int reportEvents;
-  }
-
-  public static abstract interface WifiScanner.WifiChangeListener {
-    method public abstract void onChanging(android.net.wifi.ScanResult[]);
-    method public abstract void onQuiescence(android.net.wifi.ScanResult[]);
-  }
-
   public class WpsInfo implements android.os.Parcelable {
     ctor public WpsInfo();
     ctor public WpsInfo(android.net.wifi.WpsInfo);
@@ -17530,29 +17680,6 @@
 
 }
 
-package android.net.wifi.passpoint {
-
-  public class WifiPasspointCredential implements android.os.Parcelable {
-    ctor public WifiPasspointCredential(java.lang.String, java.lang.String, android.net.wifi.WifiEnterpriseConfig);
-    method public int describeContents();
-    method public android.net.wifi.WifiEnterpriseConfig getEnterpriseConfig();
-    method public java.lang.String getFqdn();
-    method public java.lang.String getRealm();
-    method public void setEnterpriseConfig(android.net.wifi.WifiEnterpriseConfig);
-    method public void setFqdn(java.lang.String);
-    method public void setRealm(java.lang.String);
-    method public void writeToParcel(android.os.Parcel, int);
-  }
-
-  public class WifiPasspointManager {
-    method public boolean addCredential(android.net.wifi.passpoint.WifiPasspointCredential);
-    method public java.util.List<android.net.wifi.passpoint.WifiPasspointCredential> getSavedCredentials();
-    method public boolean removeCredential(android.net.wifi.passpoint.WifiPasspointCredential);
-    method public boolean updateCredential(android.net.wifi.passpoint.WifiPasspointCredential);
-  }
-
-}
-
 package android.nfc {
 
   public class FormatException extends java.lang.Exception {
@@ -17687,25 +17814,15 @@
 
 package android.nfc.cardemulation {
 
-  public final class AidGroup implements android.os.Parcelable {
-    ctor public AidGroup(java.util.ArrayList<java.lang.String>, java.lang.String);
-    method public int describeContents();
-    method public java.util.ArrayList<java.lang.String> getAids();
-    method public java.lang.String getCategory();
-    method public void writeToParcel(android.os.Parcel, int);
-    field public static final android.os.Parcelable.Creator CREATOR;
-    field public static final int MAX_NUM_AIDS = 256; // 0x100
-  }
-
   public final class CardEmulation {
     method public boolean categoryAllowsForegroundPreference(java.lang.String);
-    method public android.nfc.cardemulation.AidGroup getAidGroupForService(android.content.ComponentName, java.lang.String);
+    method public java.util.List<java.lang.String> getAidsForService(android.content.ComponentName, java.lang.String);
     method public static synchronized android.nfc.cardemulation.CardEmulation getInstance(android.nfc.NfcAdapter);
     method public int getSelectionModeForCategory(java.lang.String);
     method public boolean isDefaultServiceForAid(android.content.ComponentName, java.lang.String);
     method public boolean isDefaultServiceForCategory(android.content.ComponentName, java.lang.String);
-    method public boolean registerAidGroupForService(android.content.ComponentName, android.nfc.cardemulation.AidGroup);
-    method public boolean removeAidGroupForService(android.content.ComponentName, java.lang.String);
+    method public boolean registerAidsForService(android.content.ComponentName, java.lang.String, java.util.List<java.lang.String>);
+    method public boolean removeAidsForService(android.content.ComponentName, java.lang.String);
     method public boolean setPreferredService(android.app.Activity, android.content.ComponentName);
     method public boolean unsetPreferredService(android.app.Activity);
     field public static final java.lang.String ACTION_CHANGE_DEFAULT = "android.nfc.cardemulation.action.ACTION_CHANGE_DEFAULT";
@@ -20546,6 +20663,37 @@
     ctor public BadParcelableException(java.lang.Exception);
   }
 
+  public class BaseBundle {
+    method public void clear();
+    method public boolean containsKey(java.lang.String);
+    method public java.lang.Object get(java.lang.String);
+    method public double getDouble(java.lang.String);
+    method public double getDouble(java.lang.String, double);
+    method public double[] getDoubleArray(java.lang.String);
+    method public int getInt(java.lang.String);
+    method public int getInt(java.lang.String, int);
+    method public int[] getIntArray(java.lang.String);
+    method public long getLong(java.lang.String);
+    method public long getLong(java.lang.String, long);
+    method public long[] getLongArray(java.lang.String);
+    method public java.lang.String getString(java.lang.String);
+    method public java.lang.String getString(java.lang.String, java.lang.String);
+    method public java.lang.String[] getStringArray(java.lang.String);
+    method public boolean isEmpty();
+    method public java.util.Set<java.lang.String> keySet();
+    method public void putAll(android.os.PersistableBundle);
+    method public void putDouble(java.lang.String, double);
+    method public void putDoubleArray(java.lang.String, double[]);
+    method public void putInt(java.lang.String, int);
+    method public void putIntArray(java.lang.String, int[]);
+    method public void putLong(java.lang.String, long);
+    method public void putLongArray(java.lang.String, long[]);
+    method public void putString(java.lang.String, java.lang.String);
+    method public void putStringArray(java.lang.String, java.lang.String[]);
+    method public void remove(java.lang.String);
+    method public int size();
+  }
+
   public class BatteryManager {
     ctor public BatteryManager();
     method public android.os.BatteryProperty getProperty(int) throws android.os.RemoteException;
@@ -20674,17 +20822,14 @@
     field public static final int L = 10000; // 0x2710
   }
 
-  public final class Bundle extends android.os.CommonBundle {
+  public final class Bundle extends android.os.BaseBundle implements java.lang.Cloneable android.os.Parcelable {
     ctor public Bundle();
     ctor public Bundle(java.lang.ClassLoader);
     ctor public Bundle(int);
     ctor public Bundle(android.os.Bundle);
     ctor public Bundle(android.os.PersistableBundle);
-    method public void clear();
     method public java.lang.Object clone();
-    method public boolean containsKey(java.lang.String);
     method public int describeContents();
-    method public java.lang.Object get(java.lang.String);
     method public android.os.IBinder getBinder(java.lang.String);
     method public boolean getBoolean(java.lang.String);
     method public boolean getBoolean(java.lang.String, boolean);
@@ -20701,37 +20846,21 @@
     method public java.lang.CharSequence[] getCharSequenceArray(java.lang.String);
     method public java.util.ArrayList<java.lang.CharSequence> getCharSequenceArrayList(java.lang.String);
     method public java.lang.ClassLoader getClassLoader();
-    method public double getDouble(java.lang.String);
-    method public double getDouble(java.lang.String, double);
-    method public double[] getDoubleArray(java.lang.String);
     method public float getFloat(java.lang.String);
     method public float getFloat(java.lang.String, float);
     method public float[] getFloatArray(java.lang.String);
-    method public int getInt(java.lang.String);
-    method public int getInt(java.lang.String, int);
-    method public int[] getIntArray(java.lang.String);
     method public java.util.ArrayList<java.lang.Integer> getIntegerArrayList(java.lang.String);
-    method public long getLong(java.lang.String);
-    method public long getLong(java.lang.String, long);
-    method public long[] getLongArray(java.lang.String);
     method public T getParcelable(java.lang.String);
     method public android.os.Parcelable[] getParcelableArray(java.lang.String);
     method public java.util.ArrayList<T> getParcelableArrayList(java.lang.String);
-    method public android.os.PersistableBundle getPersistableBundle(java.lang.String);
     method public java.io.Serializable getSerializable(java.lang.String);
     method public short getShort(java.lang.String);
     method public short getShort(java.lang.String, short);
     method public short[] getShortArray(java.lang.String);
     method public android.util.SparseArray<T> getSparseParcelableArray(java.lang.String);
-    method public java.lang.String getString(java.lang.String);
-    method public java.lang.String getString(java.lang.String, java.lang.String);
-    method public java.lang.String[] getStringArray(java.lang.String);
     method public java.util.ArrayList<java.lang.String> getStringArrayList(java.lang.String);
     method public boolean hasFileDescriptors();
-    method public boolean isEmpty();
-    method public java.util.Set<java.lang.String> keySet();
     method public void putAll(android.os.Bundle);
-    method public void putAll(android.os.PersistableBundle);
     method public void putBinder(java.lang.String, android.os.IBinder);
     method public void putBoolean(java.lang.String, boolean);
     method public void putBooleanArray(java.lang.String, boolean[]);
@@ -20743,30 +20872,19 @@
     method public void putCharSequence(java.lang.String, java.lang.CharSequence);
     method public void putCharSequenceArray(java.lang.String, java.lang.CharSequence[]);
     method public void putCharSequenceArrayList(java.lang.String, java.util.ArrayList<java.lang.CharSequence>);
-    method public void putDouble(java.lang.String, double);
-    method public void putDoubleArray(java.lang.String, double[]);
     method public void putFloat(java.lang.String, float);
     method public void putFloatArray(java.lang.String, float[]);
-    method public void putInt(java.lang.String, int);
-    method public void putIntArray(java.lang.String, int[]);
     method public void putIntegerArrayList(java.lang.String, java.util.ArrayList<java.lang.Integer>);
-    method public void putLong(java.lang.String, long);
-    method public void putLongArray(java.lang.String, long[]);
     method public void putParcelable(java.lang.String, android.os.Parcelable);
     method public void putParcelableArray(java.lang.String, android.os.Parcelable[]);
     method public void putParcelableArrayList(java.lang.String, java.util.ArrayList<? extends android.os.Parcelable>);
-    method public void putPersistableBundle(java.lang.String, android.os.PersistableBundle);
     method public void putSerializable(java.lang.String, java.io.Serializable);
     method public void putShort(java.lang.String, short);
     method public void putShortArray(java.lang.String, short[]);
     method public void putSparseParcelableArray(java.lang.String, android.util.SparseArray<? extends android.os.Parcelable>);
-    method public void putString(java.lang.String, java.lang.String);
-    method public void putStringArray(java.lang.String, java.lang.String[]);
     method public void putStringArrayList(java.lang.String, java.util.ArrayList<java.lang.String>);
     method public void readFromParcel(android.os.Parcel);
-    method public void remove(java.lang.String);
     method public void setClassLoader(java.lang.ClassLoader);
-    method public int size();
     method public void writeToParcel(android.os.Parcel, int);
     field public static final android.os.Parcelable.Creator CREATOR;
     field public static final android.os.Bundle EMPTY;
@@ -20784,9 +20902,6 @@
     method public abstract void onCancel();
   }
 
-   abstract class CommonBundle implements java.lang.Cloneable android.os.Parcelable {
-  }
-
   public class ConditionVariable {
     ctor public ConditionVariable();
     ctor public ConditionVariable(boolean);
@@ -21366,46 +21481,14 @@
     field public static final int PATTERN_SIMPLE_GLOB = 2; // 0x2
   }
 
-  public final class PersistableBundle extends android.os.CommonBundle {
+  public final class PersistableBundle extends android.os.BaseBundle implements java.lang.Cloneable android.os.Parcelable {
     ctor public PersistableBundle();
-    ctor public PersistableBundle(java.lang.ClassLoader);
     ctor public PersistableBundle(int);
     ctor public PersistableBundle(android.os.PersistableBundle);
-    method public void clear();
     method public java.lang.Object clone();
-    method public boolean containsKey(java.lang.String);
     method public int describeContents();
-    method public java.lang.Object get(java.lang.String);
-    method public java.lang.ClassLoader getClassLoader();
-    method public double getDouble(java.lang.String);
-    method public double getDouble(java.lang.String, double);
-    method public double[] getDoubleArray(java.lang.String);
-    method public int getInt(java.lang.String);
-    method public int getInt(java.lang.String, int);
-    method public int[] getIntArray(java.lang.String);
-    method public long getLong(java.lang.String);
-    method public long getLong(java.lang.String, long);
-    method public long[] getLongArray(java.lang.String);
     method public android.os.PersistableBundle getPersistableBundle(java.lang.String);
-    method public java.lang.String getString(java.lang.String);
-    method public java.lang.String getString(java.lang.String, java.lang.String);
-    method public java.lang.String[] getStringArray(java.lang.String);
-    method public boolean isEmpty();
-    method public java.util.Set<java.lang.String> keySet();
-    method public void putAll(android.os.PersistableBundle);
-    method public void putDouble(java.lang.String, double);
-    method public void putDoubleArray(java.lang.String, double[]);
-    method public void putInt(java.lang.String, int);
-    method public void putIntArray(java.lang.String, int[]);
-    method public void putLong(java.lang.String, long);
-    method public void putLongArray(java.lang.String, long[]);
     method public void putPersistableBundle(java.lang.String, android.os.PersistableBundle);
-    method public void putString(java.lang.String, java.lang.String);
-    method public void putStringArray(java.lang.String, java.lang.String[]);
-    method public void readFromParcel(android.os.Parcel);
-    method public void remove(java.lang.String);
-    method public void setClassLoader(java.lang.ClassLoader);
-    method public int size();
     method public void writeToParcel(android.os.Parcel, int);
     field public static final android.os.Parcelable.Creator CREATOR;
     field public static final android.os.PersistableBundle EMPTY;
@@ -21710,10 +21793,6 @@
     method public boolean isObbMounted(java.lang.String);
     method public boolean mountObb(java.lang.String, java.lang.String, android.os.storage.OnObbStateChangeListener);
     method public boolean unmountObb(java.lang.String, boolean, android.os.storage.OnObbStateChangeListener);
-    field public static final int CRYPT_TYPE_DEFAULT = 1; // 0x1
-    field public static final int CRYPT_TYPE_PASSWORD = 0; // 0x0
-    field public static final int CRYPT_TYPE_PATTERN = 2; // 0x2
-    field public static final int CRYPT_TYPE_PIN = 3; // 0x3
   }
 
 }
@@ -23460,6 +23539,13 @@
     field public static final java.lang.String URL = "data1";
   }
 
+  public static final class ContactsContract.ContactCounts {
+    ctor public ContactsContract.ContactCounts();
+    field public static final java.lang.String ADDRESS_BOOK_INDEX_EXTRAS = "address_book_index_extras";
+    field public static final java.lang.String EXTRA_ADDRESS_BOOK_INDEX_COUNTS = "address_book_index_counts";
+    field public static final java.lang.String EXTRA_ADDRESS_BOOK_INDEX_TITLES = "address_book_index_titles";
+  }
+
   protected static abstract interface ContactsContract.ContactNameColumns {
     field public static final java.lang.String DISPLAY_NAME_ALTERNATIVE = "display_name_alt";
     field public static final java.lang.String DISPLAY_NAME_PRIMARY = "display_name";
@@ -25027,81 +25113,6 @@
     field public static final java.lang.String TYPE = "type";
   }
 
-  public final class TvContract {
-    method public static final android.net.Uri buildChannelUri(long);
-    method public static final android.net.Uri buildChannelsUriForInput(android.content.ComponentName);
-    method public static final android.net.Uri buildChannelsUriForInput(android.content.ComponentName, boolean);
-    method public static final android.net.Uri buildProgramUri(long);
-    method public static final android.net.Uri buildProgramsUriForChannel(android.net.Uri);
-    method public static final android.net.Uri buildProgramsUriForChannel(android.net.Uri, long, long);
-    field public static final java.lang.String AUTHORITY = "com.android.tv";
-  }
-
-  public static abstract interface TvContract.BaseTvColumns implements android.provider.BaseColumns {
-    field public static final java.lang.String COLUMN_PACKAGE_NAME = "package_name";
-  }
-
-  public static final class TvContract.Channels implements android.provider.TvContract.BaseTvColumns {
-    field public static final java.lang.String COLUMN_BROWSABLE = "browsable";
-    field public static final java.lang.String COLUMN_DATA = "data";
-    field public static final java.lang.String COLUMN_DESCRIPTION = "description";
-    field public static final java.lang.String COLUMN_DISPLAY_NAME = "display_name";
-    field public static final java.lang.String COLUMN_DISPLAY_NUMBER = "display_number";
-    field public static final java.lang.String COLUMN_LOCKED = "locked";
-    field public static final java.lang.String COLUMN_ORIGINAL_NETWORK_ID = "original_network_id";
-    field public static final java.lang.String COLUMN_SEARCHABLE = "searchable";
-    field public static final java.lang.String COLUMN_SERVICE_ID = "service_id";
-    field public static final java.lang.String COLUMN_SERVICE_NAME = "service_name";
-    field public static final java.lang.String COLUMN_SERVICE_TYPE = "service_type";
-    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 CONTENT_ITEM_TYPE = "vnd.android.cursor.item/vnd.com.android.tv.channels";
-    field public static final java.lang.String CONTENT_TYPE = "vnd.android.cursor.dir/vnd.com.android.tv.channels";
-    field public static final android.net.Uri CONTENT_URI;
-    field public static final int SERVICE_TYPE_OTHER = 0; // 0x0
-    field public static final int SERVICE_TYPE_RADIO = 2; // 0x2
-    field public static final int SERVICE_TYPE_TV = 1; // 0x1
-    field public static final int TYPE_1SEG = 263168; // 0x40400
-    field public static final int TYPE_ATSC_C = 197120; // 0x30200
-    field public static final int TYPE_ATSC_M_H = 197120; // 0x30200
-    field public static final int TYPE_ATSC_T = 196608; // 0x30000
-    field public static final int TYPE_CMMB = 327936; // 0x50100
-    field public static final int TYPE_DTMB = 327680; // 0x50000
-    field public static final int TYPE_DVB_C = 131584; // 0x20200
-    field public static final int TYPE_DVB_C2 = 131585; // 0x20201
-    field public static final int TYPE_DVB_H = 131840; // 0x20300
-    field public static final int TYPE_DVB_S = 131328; // 0x20100
-    field public static final int TYPE_DVB_S2 = 131329; // 0x20101
-    field public static final int TYPE_DVB_SH = 132096; // 0x20400
-    field public static final int TYPE_DVB_T = 131072; // 0x20000
-    field public static final int TYPE_DVB_T2 = 131073; // 0x20001
-    field public static final int TYPE_ISDB_C = 262912; // 0x40300
-    field public static final int TYPE_ISDB_S = 262656; // 0x40200
-    field public static final int TYPE_ISDB_T = 262144; // 0x40000
-    field public static final int TYPE_ISDB_TB = 262400; // 0x40100
-    field public static final int TYPE_OTHER = 0; // 0x0
-    field public static final int TYPE_PASSTHROUGH = 65536; // 0x10000
-    field public static final int TYPE_S_DMB = 393472; // 0x60100
-    field public static final int TYPE_T_DMB = 393216; // 0x60000
-  }
-
-  public static final class TvContract.Programs implements android.provider.TvContract.BaseTvColumns {
-    field public static final java.lang.String COLUMN_AUDIO_LANGUAGE = "audio_language";
-    field public static final java.lang.String COLUMN_CHANNEL_ID = "channel_id";
-    field public static final java.lang.String COLUMN_DATA = "data";
-    field public static final java.lang.String COLUMN_DESCRIPTION = "description";
-    field public static final java.lang.String COLUMN_END_TIME_UTC_MILLIS = "end_time_utc_millis";
-    field public static final java.lang.String COLUMN_GENRE = "genre";
-    field public static final java.lang.String COLUMN_LONG_DESCRIPTION = "long_description";
-    field public static final java.lang.String COLUMN_START_TIME_UTC_MILLIS = "start_time_utc_millis";
-    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 CONTENT_ITEM_TYPE = "vnd.android.cursor.item/vnd.com.android.tv.programs";
-    field public static final java.lang.String CONTENT_TYPE = "vnd.android.cursor.dir/vnd.com.android.tv.programs";
-    field public static final android.net.Uri CONTENT_URI;
-  }
-
   public class UserDictionary {
     ctor public UserDictionary();
     field public static final java.lang.String AUTHORITY = "user_dictionary";
@@ -26359,16 +26370,17 @@
     method public android.view.LayoutInflater getLayoutInflater();
     method public android.app.Dialog getWindow();
     method public void hideWindow();
+    method public void onAbortVoice(android.service.voice.VoiceInteractionSession.Caller, android.service.voice.VoiceInteractionSession.Request, java.lang.CharSequence, android.os.Bundle);
     method public void onBackPressed();
     method public abstract void onCancel(android.service.voice.VoiceInteractionSession.Request);
     method public void onCloseSystemDialogs();
     method public abstract void onCommand(android.service.voice.VoiceInteractionSession.Caller, android.service.voice.VoiceInteractionSession.Request, java.lang.String, android.os.Bundle);
     method public void onComputeInsets(android.service.voice.VoiceInteractionSession.Insets);
-    method public abstract void onConfirm(android.service.voice.VoiceInteractionSession.Caller, android.service.voice.VoiceInteractionSession.Request, java.lang.String, android.os.Bundle);
+    method public abstract void onConfirm(android.service.voice.VoiceInteractionSession.Caller, android.service.voice.VoiceInteractionSession.Request, java.lang.CharSequence, android.os.Bundle);
     method public void onCreate(android.os.Bundle);
     method public android.view.View onCreateContentView();
     method public void onDestroy();
-    method public abstract boolean[] onGetSupportedCommands(android.service.voice.VoiceInteractionSession.Caller, java.lang.String[]);
+    method public boolean[] onGetSupportedCommands(android.service.voice.VoiceInteractionSession.Caller, java.lang.String[]);
     method public boolean onKeyDown(int, android.view.KeyEvent);
     method public boolean onKeyLongPress(int, android.view.KeyEvent);
     method public boolean onKeyMultiple(int, int, android.view.KeyEvent);
@@ -26389,12 +26401,13 @@
     field public static final int TOUCHABLE_INSETS_CONTENT = 1; // 0x1
     field public static final int TOUCHABLE_INSETS_FRAME = 0; // 0x0
     field public static final int TOUCHABLE_INSETS_REGION = 3; // 0x3
-    field public int contentTopInsets;
+    field public final android.graphics.Rect contentInsets;
     field public int touchableInsets;
     field public final android.graphics.Region touchableRegion;
   }
 
   public static class VoiceInteractionSession.Request {
+    method public void sendAbortVoiceResult(android.os.Bundle);
     method public void sendCancelResult();
     method public void sendCommandResult(boolean, android.os.Bundle);
     method public void sendConfirmResult(boolean, android.os.Bundle);
@@ -26549,6 +26562,28 @@
 
 package android.speech.tts {
 
+  public final class Markup implements android.os.Parcelable {
+    ctor public Markup();
+    ctor public Markup(java.lang.String);
+    ctor public Markup(android.speech.tts.Markup);
+    method public android.speech.tts.Markup addNestedMarkup(android.speech.tts.Markup);
+    method public int describeContents();
+    method public android.speech.tts.Markup getNestedMarkup(int);
+    method public java.util.List<android.speech.tts.Markup> getNestedMarkups();
+    method public java.lang.String getParameter(java.lang.String);
+    method public java.lang.String getPlainText();
+    method public java.lang.String getType();
+    method public static android.speech.tts.Markup markupFromString(java.lang.String) throws java.lang.IllegalArgumentException;
+    method public int nestedMarkupSize();
+    method public int parametersSize();
+    method public boolean removeNestedMarkup(android.speech.tts.Markup);
+    method public void removeParameter(java.lang.String);
+    method public android.speech.tts.Markup setParameter(java.lang.String, java.lang.String);
+    method public void setPlainText(java.lang.String);
+    method public void setType(java.lang.String);
+    method public void writeToParcel(android.os.Parcel, int);
+  }
+
   public final class RequestConfig {
     method public android.os.Bundle getAudioParams();
     method public android.speech.tts.VoiceInfo getVoice();
@@ -26611,9 +26646,10 @@
   }
 
   public final class SynthesisRequestV2 implements android.os.Parcelable {
-    ctor public SynthesisRequestV2(java.lang.String, java.lang.String, java.lang.String, android.os.Bundle, android.os.Bundle);
+    ctor public SynthesisRequestV2(android.speech.tts.Markup, java.lang.String, java.lang.String, android.os.Bundle, android.os.Bundle);
     method public int describeContents();
     method public android.os.Bundle getAudioParams();
+    method public android.speech.tts.Markup getMarkup();
     method public java.lang.String getText();
     method public java.lang.String getUtteranceId();
     method public java.lang.String getVoiceName();
@@ -26713,10 +26749,13 @@
     method public void disconnect();
     method public android.speech.tts.TextToSpeechClient.EngineStatus getEngineStatus();
     method public boolean isConnected();
+    method public boolean isSpeaking();
     method public void queueAudio(android.net.Uri, android.speech.tts.TextToSpeechClient.UtteranceId, android.speech.tts.RequestConfig, android.speech.tts.TextToSpeechClient.RequestCallbacks);
     method public void queueSilence(long, android.speech.tts.TextToSpeechClient.UtteranceId, android.speech.tts.TextToSpeechClient.RequestCallbacks);
     method public void queueSpeak(java.lang.String, android.speech.tts.TextToSpeechClient.UtteranceId, android.speech.tts.RequestConfig, android.speech.tts.TextToSpeechClient.RequestCallbacks);
+    method public void queueSpeak(android.speech.tts.Markup, android.speech.tts.TextToSpeechClient.UtteranceId, android.speech.tts.RequestConfig, android.speech.tts.TextToSpeechClient.RequestCallbacks);
     method public void queueSynthesizeToFile(java.lang.String, android.speech.tts.TextToSpeechClient.UtteranceId, java.io.File, android.speech.tts.RequestConfig, android.speech.tts.TextToSpeechClient.RequestCallbacks);
+    method public void queueSynthesizeToFile(android.speech.tts.Markup, android.speech.tts.TextToSpeechClient.UtteranceId, java.io.File, android.speech.tts.RequestConfig, android.speech.tts.TextToSpeechClient.RequestCallbacks);
     method public void stop();
   }
 
@@ -26790,6 +26829,85 @@
     method protected void onVoicesInfoChange();
   }
 
+  public class Utterance {
+    ctor public Utterance();
+    method public android.speech.tts.Utterance append(android.speech.tts.Utterance.AbstractTts<? extends android.speech.tts.Utterance.AbstractTts<?>>);
+    method public android.speech.tts.Utterance append(java.lang.String);
+    method public android.speech.tts.Utterance append(int);
+    method public android.speech.tts.Markup createMarkup();
+    method public android.speech.tts.Utterance.AbstractTts<? extends android.speech.tts.Utterance.AbstractTts<?>> get(int);
+    method public android.speech.tts.Utterance setNoWarningOnFallback(boolean);
+    method public int size();
+    method public static android.speech.tts.Utterance utteranceFromString(java.lang.String) throws java.lang.IllegalArgumentException;
+    field public static final int ANIMACY_ANIMATE = 1; // 0x1
+    field public static final int ANIMACY_INANIMATE = 2; // 0x2
+    field public static final int ANIMACY_UNKNOWN = 0; // 0x0
+    field public static final int CASE_ABLATIVE = 4; // 0x4
+    field public static final int CASE_ACCUSATIVE = 2; // 0x2
+    field public static final int CASE_DATIVE = 3; // 0x3
+    field public static final int CASE_GENITIVE = 5; // 0x5
+    field public static final int CASE_INSTRUMENTAL = 8; // 0x8
+    field public static final int CASE_LOCATIVE = 7; // 0x7
+    field public static final int CASE_NOMINATIVE = 1; // 0x1
+    field public static final int CASE_UNKNOWN = 0; // 0x0
+    field public static final int CASE_VOCATIVE = 6; // 0x6
+    field public static final int GENDER_FEMALE = 3; // 0x3
+    field public static final int GENDER_MALE = 2; // 0x2
+    field public static final int GENDER_NEUTRAL = 1; // 0x1
+    field public static final int GENDER_UNKNOWN = 0; // 0x0
+    field public static final java.lang.String KEY_NO_WARNING_ON_FALLBACK = "no_warning_on_fallback";
+    field public static final int MULTIPLICITY_DUAL = 2; // 0x2
+    field public static final int MULTIPLICITY_PLURAL = 3; // 0x3
+    field public static final int MULTIPLICITY_SINGLE = 1; // 0x1
+    field public static final int MULTIPLICITY_UNKNOWN = 0; // 0x0
+    field public static final java.lang.String TYPE_UTTERANCE = "utterance";
+  }
+
+  public static abstract class Utterance.AbstractTts {
+    ctor protected Utterance.AbstractTts();
+    ctor protected Utterance.AbstractTts(android.speech.tts.Markup);
+    method public java.lang.String generatePlainText();
+    method public android.speech.tts.Markup getMarkup();
+    method protected java.lang.String getParameter(java.lang.String);
+    method public java.lang.String getPlainText();
+    method public java.lang.String getType();
+    method protected C removeParameter(java.lang.String);
+    method protected C setParameter(java.lang.String, java.lang.String);
+    method public C setPlainText(java.lang.String);
+    field protected android.speech.tts.Markup mMarkup;
+  }
+
+  public static abstract class Utterance.AbstractTtsSemioticClass extends android.speech.tts.Utterance.AbstractTts {
+    ctor protected Utterance.AbstractTtsSemioticClass();
+    ctor protected Utterance.AbstractTtsSemioticClass(android.speech.tts.Markup);
+    method public int getAnimacy();
+    method public int getCase();
+    method public int getGender();
+    method public int getMultiplicity();
+    method public C setAnimacy(int);
+    method public C setCase(int);
+    method public C setGender(int);
+    method public C setMultiplicity(int);
+  }
+
+  public static class Utterance.TtsCardinal extends android.speech.tts.Utterance.AbstractTtsSemioticClass {
+    ctor public Utterance.TtsCardinal();
+    ctor public Utterance.TtsCardinal(int);
+    ctor public Utterance.TtsCardinal(java.lang.String);
+    method public java.lang.String getInteger();
+    method public android.speech.tts.Utterance.TtsCardinal setInteger(int);
+    method public android.speech.tts.Utterance.TtsCardinal setInteger(java.lang.String);
+    field protected static final java.lang.String TYPE_CARDINAL = "cardinal";
+  }
+
+  public static class Utterance.TtsText extends android.speech.tts.Utterance.AbstractTtsSemioticClass {
+    ctor public Utterance.TtsText();
+    ctor public Utterance.TtsText(java.lang.String);
+    method public java.lang.String getText();
+    method public android.speech.tts.Utterance.TtsText setText(java.lang.String);
+    field protected static final java.lang.String TYPE_TEXT = "text";
+  }
+
   public abstract class UtteranceProgressListener {
     ctor public UtteranceProgressListener();
     method public abstract void onDone(java.lang.String);
@@ -27551,6 +27669,7 @@
     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);
   }
 
@@ -27616,6 +27735,7 @@
     ctor protected Connection();
     method public final android.telecomm.CallAudioState getCallAudioState();
     method public final android.net.Uri getHandle();
+    method public boolean isRequestingRingback();
     method protected void onAbort();
     method protected void onAnswer();
     method protected void onDisconnect();
@@ -27624,6 +27744,7 @@
     method protected void onReject();
     method protected void onSetAudioState(android.telecomm.CallAudioState);
     method protected void onSetSignal(android.os.Bundle);
+    method protected void onSetState(int);
     method protected void onStopDtmfTone();
     method protected void onUnhold();
     method protected void setActive();
@@ -27632,6 +27753,7 @@
     method protected void setDisconnected(int, java.lang.String);
     method protected void setHandle(android.net.Uri);
     method protected void setOnHold();
+    method protected void setRequestingRingback(boolean);
     method protected void setRinging();
     method public static java.lang.String stateToString(int);
   }
@@ -27641,6 +27763,7 @@
     method public abstract void onDestroyed(android.telecomm.Connection);
     method public abstract void onDisconnected(android.telecomm.Connection, int, java.lang.String);
     method public abstract void onHandleChanged(android.telecomm.Connection, android.net.Uri);
+    method public abstract void onRequestingRingback(android.telecomm.Connection, boolean);
     method public abstract void onSignalChanged(android.telecomm.Connection, android.os.Bundle);
     method public abstract void onStateChanged(android.telecomm.Connection, int);
   }
@@ -27651,6 +27774,7 @@
     method public void onDestroyed(android.telecomm.Connection);
     method public void onDisconnected(android.telecomm.Connection, int, java.lang.String);
     method public void onHandleChanged(android.telecomm.Connection, android.net.Uri);
+    method public void onRequestingRingback(android.telecomm.Connection, boolean);
     method public void onSignalChanged(android.telecomm.Connection, android.os.Bundle);
     method public void onStateChanged(android.telecomm.Connection, int);
   }
@@ -27731,6 +27855,7 @@
   public abstract class InCallService extends android.app.Service {
     ctor protected InCallService();
     method protected abstract void addCall(android.telecomm.InCallCall);
+    method protected abstract void bringToForeground(boolean);
     method protected final android.telecomm.InCallAdapter getAdapter();
     method protected void onAdapterAttached(android.telecomm.InCallAdapter);
     method protected abstract void onAudioStateChanged(android.telecomm.CallAudioState);
@@ -28203,12 +28328,8 @@
     method public java.lang.String getVoiceMailAlphaTag();
     method public java.lang.String getVoiceMailNumber();
     method public boolean hasIccCard();
-    method public boolean iccCloseLogicalChannel(int);
-    method public int iccOpenLogicalChannel(java.lang.String);
-    method public java.lang.String iccTransmitApduLogicalChannel(int, int, int, int, int, int, java.lang.String);
     method public boolean isNetworkRoaming();
     method public void listen(android.telephony.PhoneStateListener, int);
-    method public java.lang.String sendEnvelopeWithStatus(java.lang.String);
     field public static final java.lang.String ACTION_PHONE_STATE_CHANGED = "android.intent.action.PHONE_STATE";
     field public static final java.lang.String ACTION_RESPOND_VIA_MESSAGE = "android.intent.action.RESPOND_VIA_MESSAGE";
     field public static final int CALL_STATE_IDLE = 0; // 0x0
@@ -28734,6 +28855,7 @@
     method public java.io.File[] getExternalCacheDirs();
     method public java.io.File getExternalFilesDir(java.lang.String);
     method public java.io.File[] getExternalFilesDirs(java.lang.String);
+    method public java.io.File[] getExternalMediaDirs();
     method public java.io.File getFileStreamPath(java.lang.String);
     method public java.io.File getFilesDir();
     method public android.os.Looper getMainLooper();
@@ -30565,85 +30687,6 @@
 
 }
 
-package android.tv {
-
-  public final class TvInputInfo implements android.os.Parcelable {
-    method public int describeContents();
-    method public android.content.ComponentName getComponent();
-    method public java.lang.String getId();
-    method public java.lang.String getPackageName();
-    method public java.lang.String getServiceName();
-    method public java.lang.CharSequence loadLabel(android.content.pm.PackageManager);
-    method public void writeToParcel(android.os.Parcel, int);
-  }
-
-  public final class TvInputManager {
-    method public void createSession(java.lang.String, android.tv.TvInputManager.SessionCallback, android.os.Handler);
-    method public boolean getAvailability(java.lang.String);
-    method public java.util.List<android.tv.TvInputInfo> getTvInputList();
-    method public void registerListener(java.lang.String, android.tv.TvInputManager.TvInputListener, android.os.Handler);
-    method public void unregisterListener(java.lang.String, android.tv.TvInputManager.TvInputListener);
-  }
-
-  public static final class TvInputManager.Session {
-    method public void release();
-    method public void setVolume(float);
-    method public void tune(android.net.Uri);
-  }
-
-  public static abstract class TvInputManager.SessionCallback {
-    ctor public TvInputManager.SessionCallback();
-    method public void onSessionCreated(android.tv.TvInputManager.Session);
-    method public void onSessionReleased(android.tv.TvInputManager.Session);
-  }
-
-  public static abstract class TvInputManager.TvInputListener {
-    ctor public TvInputManager.TvInputListener();
-    method public void onAvailabilityChanged(java.lang.String, boolean);
-  }
-
-  public abstract class TvInputService extends android.app.Service {
-    ctor public TvInputService();
-    method public final android.os.IBinder onBind(android.content.Intent);
-    method public abstract android.tv.TvInputService.TvInputSessionImpl onCreateSession();
-    method public final void setAvailable(boolean);
-    field public static final java.lang.String SERVICE_INTERFACE = "android.tv.TvInputService";
-  }
-
-  public abstract class TvInputService.TvInputSessionImpl implements android.view.KeyEvent.Callback {
-    ctor public TvInputService.TvInputSessionImpl();
-    method public android.view.View onCreateOverlayView();
-    method public boolean onGenericMotionEvent(android.view.MotionEvent);
-    method public boolean onKeyDown(int, android.view.KeyEvent);
-    method public boolean onKeyLongPress(int, android.view.KeyEvent);
-    method public boolean onKeyMultiple(int, int, android.view.KeyEvent);
-    method public boolean onKeyUp(int, android.view.KeyEvent);
-    method public abstract void onRelease();
-    method public abstract boolean onSetSurface(android.view.Surface);
-    method public abstract void onSetVolume(float);
-    method public boolean onTouchEvent(android.view.MotionEvent);
-    method public boolean onTrackballEvent(android.view.MotionEvent);
-    method public abstract boolean onTune(android.net.Uri);
-    method public void setOverlayViewEnabled(boolean);
-  }
-
-  public class TvView extends android.view.SurfaceView {
-    ctor public TvView(android.content.Context);
-    ctor public TvView(android.content.Context, android.util.AttributeSet);
-    ctor public TvView(android.content.Context, android.util.AttributeSet, int);
-    method public void bindTvInput(java.lang.String, android.tv.TvInputManager.SessionCallback);
-    method public boolean dispatchUnhandledInputEvent(android.view.InputEvent);
-    method public boolean onUnhandledInputEvent(android.view.InputEvent);
-    method public void setOnUnhandledInputEventListener(android.tv.TvView.OnUnhandledInputEventListener);
-    method public void unbindTvInput();
-  }
-
-  public static abstract interface TvView.OnUnhandledInputEventListener {
-    method public abstract boolean onUnhandledInputEvent(android.view.InputEvent);
-  }
-
-}
-
 package android.util {
 
   public class AndroidException extends java.lang.Exception {
@@ -35891,10 +35934,14 @@
   public class ActionMenuView extends android.widget.LinearLayout {
     ctor public ActionMenuView(android.content.Context);
     ctor public ActionMenuView(android.content.Context, android.util.AttributeSet);
+    method public void dismissPopupMenus();
     method public android.view.Menu getMenu();
+    method public boolean hideOverflowMenu();
+    method public boolean isOverflowMenuShowing();
     method public void onConfigurationChanged(android.content.res.Configuration);
     method public void onDetachedFromWindow();
     method public void setOnMenuItemClickListener(android.widget.ActionMenuView.OnMenuItemClickListener);
+    method public boolean showOverflowMenu();
   }
 
   public static class ActionMenuView.LayoutParams extends android.widget.LinearLayout.LayoutParams {
@@ -38076,6 +38123,8 @@
     ctor public Toolbar(android.content.Context, android.util.AttributeSet);
     ctor public Toolbar(android.content.Context, android.util.AttributeSet, int);
     ctor public Toolbar(android.content.Context, android.util.AttributeSet, int, int);
+    method public void collapseActionView();
+    method public void dismissPopupMenus();
     method public int getContentInsetEnd();
     method public int getContentInsetLeft();
     method public int getContentInsetRight();
@@ -38086,7 +38135,10 @@
     method public android.graphics.drawable.Drawable getNavigationIcon();
     method public java.lang.CharSequence getSubtitle();
     method public java.lang.CharSequence getTitle();
+    method public boolean hasExpandedActionView();
+    method public boolean hideOverflowMenu();
     method public void inflateMenu(int);
+    method public boolean isOverflowMenuShowing();
     method protected void onLayout(boolean, int, int, int, int);
     method public void setContentInsetsAbsolute(int, int);
     method public void setContentInsetsRelative(int, int);
@@ -38094,6 +38146,8 @@
     method public void setLogo(android.graphics.drawable.Drawable);
     method public void setLogoDescription(int);
     method public void setLogoDescription(java.lang.CharSequence);
+    method public void setNavigationContentDescription(java.lang.CharSequence);
+    method public void setNavigationContentDescription(int);
     method public void setNavigationDescription(int);
     method public void setNavigationDescription(java.lang.CharSequence);
     method public void setNavigationIcon(int);
@@ -38102,19 +38156,22 @@
     method public void setOnMenuItemClickListener(android.widget.Toolbar.OnMenuItemClickListener);
     method public void setSubtitle(int);
     method public void setSubtitle(java.lang.CharSequence);
+    method public void setSubtitleTextAppearance(android.content.Context, int);
     method public void setTitle(int);
     method public void setTitle(java.lang.CharSequence);
+    method public void setTitleTextAppearance(android.content.Context, int);
+    method public boolean showOverflowMenu();
   }
 
-  public static class Toolbar.LayoutParams extends android.view.ViewGroup.MarginLayoutParams {
+  public static class Toolbar.LayoutParams extends android.app.ActionBar.LayoutParams {
     ctor public Toolbar.LayoutParams(android.content.Context, android.util.AttributeSet);
     ctor public Toolbar.LayoutParams(int, int);
     ctor public Toolbar.LayoutParams(int, int, int);
     ctor public Toolbar.LayoutParams(int);
     ctor public Toolbar.LayoutParams(android.widget.Toolbar.LayoutParams);
+    ctor public Toolbar.LayoutParams(android.app.ActionBar.LayoutParams);
     ctor public Toolbar.LayoutParams(android.view.ViewGroup.MarginLayoutParams);
     ctor public Toolbar.LayoutParams(android.view.ViewGroup.LayoutParams);
-    field public int gravity;
   }
 
   public static abstract interface Toolbar.OnMenuItemClickListener {
diff --git a/cmds/am/src/com/android/commands/am/Am.java b/cmds/am/src/com/android/commands/am/Am.java
index 6b55b7b..8945526 100644
--- a/cmds/am/src/com/android/commands/am/Am.java
+++ b/cmds/am/src/com/android/commands/am/Am.java
@@ -35,6 +35,7 @@
 import android.graphics.Rect;
 import android.net.Uri;
 import android.os.Binder;
+import android.os.Build;
 import android.os.Bundle;
 import android.os.IBinder;
 import android.os.ParcelFileDescriptor;
@@ -46,6 +47,8 @@
 import android.view.IWindowManager;
 import com.android.internal.os.BaseCommand;
 
+import dalvik.system.VMRuntime;
+
 import java.io.BufferedReader;
 import java.io.File;
 import java.io.FileNotFoundException;
@@ -94,7 +97,11 @@
                 "       am broadcast [--user <USER_ID> | all | current] <INTENT>\n" +
                 "       am instrument [-r] [-e <NAME> <VALUE>] [-p <FILE>] [-w]\n" +
                 "               [--user <USER_ID> | current]\n" +
-                "               [--no-window-animation] <COMPONENT>\n" +
+                "               [--no-window-animation]\n" +
+                "               [--abi <ABI>]\n : Launch the instrumented process with the "  +
+                "                   selected ABI. This assumes that the process supports the" +
+                "                   selected ABI." +
+                "               <COMPONENT>\n" +
                 "       am profile start [--user <USER_ID> current] <PROCESS> <FILE>\n" +
                 "       am profile stop [--user <USER_ID> current] [<PROCESS>]\n" +
                 "       am dumpheap [--user <USER_ID> current] [-n] <PROCESS> <FILE>\n" +
@@ -835,6 +842,7 @@
         Bundle args = new Bundle();
         String argKey = null, argValue = null;
         IWindowManager wm = IWindowManager.Stub.asInterface(ServiceManager.getService("window"));
+        String abi = null;
 
         String opt;
         while ((opt=nextOption()) != null) {
@@ -853,6 +861,8 @@
                 no_window_animation = true;
             } else if (opt.equals("--user")) {
                 userId = parseUserArg(nextArgRequired());
+            } else if (opt.equals("--abi")) {
+                abi = nextArgRequired();
             } else {
                 System.err.println("Error: Unknown option: " + opt);
                 return;
@@ -883,7 +893,24 @@
             wm.setAnimationScale(1, 0.0f);
         }
 
-        if (!mAm.startInstrumentation(cn, profileFile, 0, args, watcher, connection, userId)) {
+        if (abi != null) {
+            final String[] supportedAbis = Build.SUPPORTED_ABIS;
+            boolean matched = false;
+            for (String supportedAbi : supportedAbis) {
+                if (supportedAbi.equals(abi)) {
+                    matched = true;
+                    break;
+                }
+            }
+
+            if (!matched) {
+                throw new AndroidException(
+                        "INSTRUMENTATION_FAILED: Unsupported instruction set " + abi);
+            }
+        }
+
+        if (!mAm.startInstrumentation(cn, profileFile, 0, args, watcher, connection, userId,
+                abi)) {
             throw new AndroidException("INSTRUMENTATION_FAILED: " + cn.flattenToString());
         }
 
diff --git a/cmds/bu/src/com/android/commands/bu/Backup.java b/cmds/bu/src/com/android/commands/bu/Backup.java
index 2673031..4503726 100644
--- a/cmds/bu/src/com/android/commands/bu/Backup.java
+++ b/cmds/bu/src/com/android/commands/bu/Backup.java
@@ -20,6 +20,7 @@
 import android.os.ParcelFileDescriptor;
 import android.os.RemoteException;
 import android.os.ServiceManager;
+import android.system.OsConstants;
 import android.util.Log;
 
 import java.io.IOException;
@@ -50,13 +51,11 @@
             return;
         }
 
-        int socketFd = Integer.parseInt(nextArg());
-
         String arg = nextArg();
         if (arg.equals("backup")) {
-            doFullBackup(socketFd);
+            doFullBackup(OsConstants.STDOUT_FILENO);
         } else if (arg.equals("restore")) {
-            doFullRestore(socketFd);
+            doFullRestore(OsConstants.STDIN_FILENO);
         } else {
             Log.e(TAG, "Invalid operation '" + arg + "'");
         }
diff --git a/cmds/pm/src/com/android/commands/pm/Pm.java b/cmds/pm/src/com/android/commands/pm/Pm.java
index b7c2c22..47047b8 100644
--- a/cmds/pm/src/com/android/commands/pm/Pm.java
+++ b/cmds/pm/src/com/android/commands/pm/Pm.java
@@ -39,6 +39,7 @@
 import android.content.res.AssetManager;
 import android.content.res.Resources;
 import android.net.Uri;
+import android.os.Build;
 import android.os.Bundle;
 import android.os.IUserManager;
 import android.os.Process;
@@ -57,7 +58,6 @@
 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;
@@ -823,6 +823,7 @@
         byte[] tag = null;
         String originatingUriString = null;
         String referrer = null;
+        String abi = null;
 
         while ((opt=nextOption()) != null) {
             if (opt.equals("-l")) {
@@ -893,12 +894,34 @@
                     System.err.println("Error: must supply argument for --referrer");
                     return;
                 }
+            } else if (opt.equals("--abi")) {
+                abi = nextOptionData();
+                if (abi == null) {
+                    System.err.println("Error: must supply argument for --abi");
+                    return;
+                }
             } else {
                 System.err.println("Error: Unknown option: " + opt);
                 return;
             }
         }
 
+        if (abi != null) {
+            final String[] supportedAbis = Build.SUPPORTED_ABIS;
+            boolean matched = false;
+            for (String supportedAbi : supportedAbis) {
+                if (supportedAbi.equals(abi)) {
+                    matched = true;
+                    break;
+                }
+            }
+
+            if (!matched) {
+                System.err.println("Error: abi " + abi + " not supported on this device.");
+                return;
+            }
+        }
+
         final ContainerEncryptionParams encryptionParams;
         if (algo != null || iv != null || key != null || macAlgo != null || macKey != null
                 || tag != null) {
@@ -976,8 +999,9 @@
             VerificationParams verificationParams = new VerificationParams(verificationURI,
                     originatingURI, referrerURI, VerificationParams.NO_UID, null);
 
-            mPm.installPackageWithVerificationAndEncryptionEtc(apkURI, null, obs, installFlags,
-                    installerPackageName, verificationParams, encryptionParams);
+            mPm.installPackageWithVerificationEncryptionAndAbiOverrideEtc(apkURI, null,
+                    obs, installFlags, installerPackageName, verificationParams,
+                    encryptionParams, abi);
 
             synchronized (obs) {
                 while (!obs.finished) {
diff --git a/cmds/screencap/screencap.cpp b/cmds/screencap/screencap.cpp
index 2efe4d3..6296dd1 100644
--- a/cmds/screencap/screencap.cpp
+++ b/cmds/screencap/screencap.cpp
@@ -141,7 +141,7 @@
 
     ScreenshotClient screenshot;
     sp<IBinder> display = SurfaceComposerClient::getBuiltInDisplay(displayId);
-    if (display != NULL && screenshot.update(display, false) == NO_ERROR) {
+    if (display != NULL && screenshot.update(display, Rect(), false) == NO_ERROR) {
         base = screenshot.getPixels();
         w = screenshot.getWidth();
         h = screenshot.getHeight();
diff --git a/core/java/android/app/ActionBar.java b/core/java/android/app/ActionBar.java
index 3c3df01..d4c4318 100644
--- a/core/java/android/app/ActionBar.java
+++ b/core/java/android/app/ActionBar.java
@@ -26,6 +26,7 @@
 import android.util.AttributeSet;
 import android.view.ActionMode;
 import android.view.Gravity;
+import android.view.KeyEvent;
 import android.view.View;
 import android.view.ViewDebug;
 import android.view.ViewGroup;
@@ -1013,6 +1014,26 @@
         return null;
     }
 
+    /** @hide */
+    public boolean openOptionsMenu() {
+        return false;
+    }
+
+    /** @hide */
+    public boolean invalidateOptionsMenu() {
+        return false;
+    }
+
+    /** @hide */
+    public boolean onMenuKeyEvent(KeyEvent event) {
+        return false;
+    }
+
+    /** @hide */
+    public boolean collapseActionView() {
+        return false;
+    }
+
     /**
      * Listener interface for ActionBar navigation events.
      *
@@ -1291,6 +1312,7 @@
 
         public LayoutParams(int width, int height) {
             super(width, height);
+            this.gravity = Gravity.CENTER_VERTICAL | Gravity.START;
         }
 
         public LayoutParams(int width, int height, int gravity) {
@@ -1305,6 +1327,7 @@
 
         public LayoutParams(LayoutParams source) {
             super(source);
+            this.gravity = source.gravity;
         }
 
         public LayoutParams(ViewGroup.LayoutParams source) {
diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java
index 07de85c..23b5f29 100644
--- a/core/java/android/app/Activity.java
+++ b/core/java/android/app/Activity.java
@@ -716,6 +716,7 @@
         HashMap<String, Object> children;
         ArrayList<Fragment> fragments;
         ArrayMap<String, LoaderManagerImpl> loaders;
+        VoiceInteractor voiceInteractor;
     }
     /* package */ NonConfigurationInstances mLastNonConfigurationInstances;
     
@@ -920,6 +921,9 @@
         }
         mFragments.dispatchCreate();
         getApplication().dispatchActivityCreated(this, savedInstanceState);
+        if (mVoiceInteractor != null) {
+            mVoiceInteractor.attachActivity(this);
+        }
         mCalled = true;
     }
 
@@ -1830,7 +1834,8 @@
                 }
             }
         }
-        if (activity == null && children == null && fragments == null && !retainLoaders) {
+        if (activity == null && children == null && fragments == null && !retainLoaders
+                && mVoiceInteractor == null) {
             return null;
         }
         
@@ -1839,6 +1844,7 @@
         nci.children = children;
         nci.fragments = fragments;
         nci.loaders = mAllLoaderManagers;
+        nci.voiceInteractor = mVoiceInteractor;
         return nci;
     }
 
@@ -2069,15 +2075,16 @@
      * <p>In order to use a Toolbar within the Activity's window content the application
      * must not request the window feature {@link Window#FEATURE_ACTION_BAR FEATURE_ACTION_BAR}.</p>
      *
-     * @param actionBar Toolbar to set as the Activity's action bar
+     * @param toolbar Toolbar to set as the Activity's action bar
      */
-    public void setActionBar(@Nullable Toolbar actionBar) {
+    public void setActionBar(@Nullable Toolbar toolbar) {
         if (getActionBar() instanceof WindowDecorActionBar) {
             throw new IllegalStateException("This Activity already has an action bar supplied " +
                     "by the window decor. Do not request Window.FEATURE_ACTION_BAR and set " +
                     "android:windowActionBar to false in your theme to use a Toolbar instead.");
         }
-        mActionBar = new ToolbarActionBar(actionBar);
+        mActionBar = new ToolbarActionBar(toolbar, getTitle(), this);
+        mActionBar.invalidateOptionsMenu();
     }
     
     /**
@@ -2443,6 +2450,10 @@
      * but you can override this to do whatever you want.
      */
     public void onBackPressed() {
+        if (mActionBar != null && mActionBar.collapseActionView()) {
+            return;
+        }
+
         if (!mFragments.popBackStackImmediate()) {
             finishAfterTransition();
         }
@@ -2654,6 +2665,14 @@
      */
     public boolean dispatchKeyEvent(KeyEvent event) {
         onUserInteraction();
+
+        // Let action bars open menus in response to the menu key prioritized over
+        // the window handling it
+        if (event.getKeyCode() == KeyEvent.KEYCODE_MENU &&
+                mActionBar != null && mActionBar.onMenuKeyEvent(event)) {
+            return true;
+        }
+
         Window win = getWindow();
         if (win.superDispatchKeyEvent(event)) {
             return true;
@@ -2901,7 +2920,9 @@
      * time it needs to be displayed.
      */
     public void invalidateOptionsMenu() {
-        mWindow.invalidatePanelMenu(Window.FEATURE_OPTIONS_PANEL);
+        if (mActionBar == null || !mActionBar.invalidateOptionsMenu()) {
+            mWindow.invalidatePanelMenu(Window.FEATURE_OPTIONS_PANEL);
+        }
     }
     
     /**
@@ -3111,7 +3132,9 @@
      * open, this method does nothing.
      */
     public void openOptionsMenu() {
-        mWindow.openPanel(Window.FEATURE_OPTIONS_PANEL, null);
+        if (mActionBar == null || !mActionBar.openOptionsMenu()) {
+            mWindow.openPanel(Window.FEATURE_OPTIONS_PANEL, null);
+        }
     }
     
     /**
@@ -4194,7 +4217,11 @@
      */
     public void startActivityFromFragment(@NonNull Fragment fragment, Intent intent,
             int requestCode) {
-        startActivityFromFragment(fragment, intent, requestCode, null);
+        Bundle options = null;
+        if (mWindow.hasFeature(Window.FEATURE_CONTENT_TRANSITIONS)) {
+            options = ActivityOptions.makeSceneTransitionAnimation(this).toBundle();
+        }
+        startActivityFromFragment(fragment, intent, requestCode, options);
     }
 
     /**
@@ -4219,6 +4246,9 @@
      */
     public void startActivityFromFragment(@NonNull Fragment fragment, Intent intent,
             int requestCode, @Nullable Bundle options) {
+        if (options != null) {
+            mActivityTransitionState.startExitOutTransition(this, options);
+        }
         Instrumentation.ActivityResult ar =
             mInstrumentation.execStartActivity(
                 this, mMainThread.getApplicationThread(), mToken, fragment,
@@ -5625,8 +5655,14 @@
         mParent = parent;
         mEmbeddedID = id;
         mLastNonConfigurationInstances = lastNonConfigurationInstances;
-        mVoiceInteractor = voiceInteractor != null
-                ? new VoiceInteractor(this, this, voiceInteractor, Looper.myLooper()) : null;
+        if (voiceInteractor != null) {
+            if (lastNonConfigurationInstances != null) {
+                mVoiceInteractor = lastNonConfigurationInstances.voiceInteractor;
+            } else {
+                mVoiceInteractor = new VoiceInteractor(voiceInteractor, this, this,
+                        Looper.myLooper());
+            }
+        }
 
         mWindow.setWindowManager(
                 (WindowManager)context.getSystemService(Context.WINDOW_SERVICE),
@@ -5835,6 +5871,9 @@
         if (mLoaderManager != null) {
             mLoaderManager.doDestroy();
         }
+        if (mVoiceInteractor != null) {
+            mVoiceInteractor.detachActivity();
+        }
     }
 
     /**
diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java
index abcb0d0..788ac56 100644
--- a/core/java/android/app/ActivityManager.java
+++ b/core/java/android/app/ActivityManager.java
@@ -738,7 +738,7 @@
     public static final int RECENT_INCLUDE_PROFILES = 0x0004;
 
     /**
-     * Return a list of the tasks that the user has recently launched, with
+     * <p></p>Return a list of the tasks that the user has recently launched, with
      * the most recent being first and older ones after in order.
      *
      * <p><b>Note: this method is only intended for debugging and presenting
@@ -750,6 +750,15 @@
      * same time, assumptions made about the meaning of the data here for
      * purposes of control flow will be incorrect.</p>
      *
+     * @deprecated As of {@link android.os.Build.VERSION_CODES#L}, this method is
+     * no longer available to third party applications: as the introduction of
+     * document-centric recents means
+     * it can leak personal information to the caller.  For backwards compatibility,
+     * it will still return a small subset of its data: at least the caller's
+     * own tasks (though see {@link #getAppTasks()} for the correct supported
+     * way to retrieve that information), and possibly some other tasks
+     * such as home that are known to not be sensitive.
+     *
      * @param maxNum The maximum number of entries to return in the list.  The
      * actual number returned may be smaller, depending on how many tasks the
      * user has started and the maximum number the system can remember.
@@ -762,6 +771,7 @@
      * @throws SecurityException Throws SecurityException if the caller does
      * not hold the {@link android.Manifest.permission#GET_TASKS} permission.
      */
+    @Deprecated
     public List<RecentTaskInfo> getRecentTasks(int maxNum, int flags)
             throws SecurityException {
         try {
@@ -946,6 +956,14 @@
      * same time, assumptions made about the meaning of the data here for
      * purposes of control flow will be incorrect.</p>
      *
+     * @deprecated As of {@link android.os.Build.VERSION_CODES#L}, this method
+     * is no longer available to third party
+     * applications: the introduction of document-centric recents means
+     * it can leak person information to the caller.  For backwards compatibility,
+     * it will still retu rn a small subset of its data: at least the caller's
+     * own tasks, and possibly some other tasks
+     * such as home that are known to not be sensitive.
+     *
      * @param maxNum The maximum number of entries to return in the list.  The
      * actual number returned may be smaller, depending on how many tasks the
      * user has started.
@@ -956,6 +974,7 @@
      * @throws SecurityException Throws SecurityException if the caller does
      * not hold the {@link android.Manifest.permission#GET_TASKS} permission.
      */
+    @Deprecated
     public List<RunningTaskInfo> getRunningTasks(int maxNum)
             throws SecurityException {
         try {
diff --git a/core/java/android/app/ActivityManagerNative.java b/core/java/android/app/ActivityManagerNative.java
index 0f65454..56462ae 100644
--- a/core/java/android/app/ActivityManagerNative.java
+++ b/core/java/android/app/ActivityManagerNative.java
@@ -943,7 +943,9 @@
             b = data.readStrongBinder();
             IUiAutomationConnection c = IUiAutomationConnection.Stub.asInterface(b);
             int userId = data.readInt();
-            boolean res = startInstrumentation(className, profileFile, fl, arguments, w, c, userId);
+            String abiOverride = data.readString();
+            boolean res = startInstrumentation(className, profileFile, fl, arguments, w, c, userId,
+                    abiOverride);
             reply.writeNoException();
             reply.writeInt(res ? 1 : 0);
             return true;
@@ -3339,7 +3341,8 @@
 
     public boolean startInstrumentation(ComponentName className, String profileFile,
             int flags, Bundle arguments, IInstrumentationWatcher watcher,
-            IUiAutomationConnection connection, int userId) throws RemoteException {
+            IUiAutomationConnection connection, int userId, String instructionSet)
+            throws RemoteException {
         Parcel data = Parcel.obtain();
         Parcel reply = Parcel.obtain();
         data.writeInterfaceToken(IActivityManager.descriptor);
@@ -3350,6 +3353,7 @@
         data.writeStrongBinder(watcher != null ? watcher.asBinder() : null);
         data.writeStrongBinder(connection != null ? connection.asBinder() : null);
         data.writeInt(userId);
+        data.writeString(instructionSet);
         mRemote.transact(START_INSTRUMENTATION_TRANSACTION, data, reply, 0);
         reply.readException();
         boolean res = reply.readInt() != 0;
diff --git a/core/java/android/app/ActivityTransitionCoordinator.java b/core/java/android/app/ActivityTransitionCoordinator.java
index d08978b..b739387 100644
--- a/core/java/android/app/ActivityTransitionCoordinator.java
+++ b/core/java/android/app/ActivityTransitionCoordinator.java
@@ -15,14 +15,23 @@
  */
 package android.app;
 
+import android.content.Context;
+import android.content.res.Resources;
+import android.graphics.Bitmap;
+import android.graphics.Canvas;
+import android.graphics.Matrix;
 import android.graphics.Rect;
+import android.graphics.drawable.BitmapDrawable;
+import android.os.Bundle;
 import android.os.Handler;
 import android.os.ResultReceiver;
 import android.transition.Transition;
 import android.transition.TransitionSet;
 import android.util.ArrayMap;
+import android.util.Pair;
 import android.view.View;
 import android.view.ViewGroup;
+import android.view.ViewTreeObserver;
 import android.view.Window;
 import android.widget.ImageView;
 
@@ -120,8 +129,8 @@
     protected static final String KEY_SCALE_TYPE = "shared_element:scaleType";
     protected static final String KEY_IMAGE_MATRIX = "shared_element:imageMatrix";
 
-    // The background fade in/out duration. 150ms is pretty quick, but not abrupt.
-    public static final int FADE_BACKGROUND_DURATION_MS = 150;
+    // The background fade in/out duration. TODO: Enable tuning this.
+    public static final int FADE_BACKGROUND_DURATION_MS = 300;
 
     protected static final ImageView.ScaleType[] SCALE_TYPE_VALUES = ImageView.ScaleType.values();
 
@@ -181,6 +190,16 @@
      */
     public static final int MSG_CANCEL = 106;
 
+    /**
+     * When returning, this is the destination location for the shared element.
+     */
+    public static final int MSG_SHARED_ELEMENT_DESTINATION = 107;
+
+    /**
+     * Send the shared element positions.
+     */
+    public static final int MSG_SEND_SHARED_ELEMENT_DESTINATION = 108;
+
     final private Window mWindow;
     final protected ArrayList<String> mAllSharedElementNames;
     final protected ArrayList<View> mSharedElements = new ArrayList<View>();
@@ -334,6 +353,210 @@
 
     protected abstract Transition getViewsTransition();
 
+    private static void setSharedElementState(View view, String name, Bundle transitionArgs,
+            int[] parentLoc) {
+        Bundle sharedElementBundle = transitionArgs.getBundle(name);
+        if (sharedElementBundle == null) {
+            return;
+        }
+
+        if (view instanceof ImageView) {
+            int scaleTypeInt = sharedElementBundle.getInt(KEY_SCALE_TYPE, -1);
+            if (scaleTypeInt >= 0) {
+                ImageView imageView = (ImageView) view;
+                ImageView.ScaleType scaleType = SCALE_TYPE_VALUES[scaleTypeInt];
+                imageView.setScaleType(scaleType);
+                if (scaleType == ImageView.ScaleType.MATRIX) {
+                    float[] matrixValues = sharedElementBundle.getFloatArray(KEY_IMAGE_MATRIX);
+                    Matrix matrix = new Matrix();
+                    matrix.setValues(matrixValues);
+                    imageView.setImageMatrix(matrix);
+                }
+            }
+        }
+
+        float z = sharedElementBundle.getFloat(KEY_TRANSLATION_Z);
+        view.setTranslationZ(z);
+
+        int x = sharedElementBundle.getInt(KEY_SCREEN_X);
+        int y = sharedElementBundle.getInt(KEY_SCREEN_Y);
+        int width = sharedElementBundle.getInt(KEY_WIDTH);
+        int height = sharedElementBundle.getInt(KEY_HEIGHT);
+
+        int widthSpec = View.MeasureSpec.makeMeasureSpec(width, View.MeasureSpec.EXACTLY);
+        int heightSpec = View.MeasureSpec.makeMeasureSpec(height, View.MeasureSpec.EXACTLY);
+        view.measure(widthSpec, heightSpec);
+
+        int left = x - parentLoc[0];
+        int top = y - parentLoc[1];
+        int right = left + width;
+        int bottom = top + height;
+        view.layout(left, top, right, bottom);
+    }
+
+    protected ArrayMap<ImageView, Pair<ImageView.ScaleType, Matrix>> setSharedElementState(
+            Bundle sharedElementState, final ArrayList<View> snapshots) {
+        ArrayMap<ImageView, Pair<ImageView.ScaleType, Matrix>> originalImageState =
+                new ArrayMap<ImageView, Pair<ImageView.ScaleType, Matrix>>();
+        if (sharedElementState != null) {
+            int[] tempLoc = new int[2];
+            for (int i = 0; i < mSharedElementNames.size(); i++) {
+                View sharedElement = mSharedElements.get(i);
+                String name = mSharedElementNames.get(i);
+                Pair<ImageView.ScaleType, Matrix> originalState = getOldImageState(sharedElement,
+                        name, sharedElementState);
+                if (originalState != null) {
+                    originalImageState.put((ImageView) sharedElement, originalState);
+                }
+                View parent = (View) sharedElement.getParent();
+                parent.getLocationOnScreen(tempLoc);
+                setSharedElementState(sharedElement, name, sharedElementState, tempLoc);
+            }
+        }
+        mListener.setSharedElementStart(mSharedElementNames, mSharedElements, snapshots);
+
+        getDecor().getViewTreeObserver().addOnPreDrawListener(
+                new ViewTreeObserver.OnPreDrawListener() {
+                    @Override
+                    public boolean onPreDraw() {
+                        getDecor().getViewTreeObserver().removeOnPreDrawListener(this);
+                        mListener.setSharedElementEnd(mSharedElementNames, mSharedElements,
+                                snapshots);
+                        return true;
+                    }
+                }
+        );
+        return originalImageState;
+    }
+
+    private static Pair<ImageView.ScaleType, Matrix> getOldImageState(View view, String name,
+            Bundle transitionArgs) {
+        if (!(view instanceof ImageView)) {
+            return null;
+        }
+        Bundle bundle = transitionArgs.getBundle(name);
+        if (bundle == null) {
+            return null;
+        }
+        int scaleTypeInt = bundle.getInt(KEY_SCALE_TYPE, -1);
+        if (scaleTypeInt < 0) {
+            return null;
+        }
+
+        ImageView imageView = (ImageView) view;
+        ImageView.ScaleType originalScaleType = imageView.getScaleType();
+
+        Matrix originalMatrix = null;
+        if (originalScaleType == ImageView.ScaleType.MATRIX) {
+            originalMatrix = new Matrix(imageView.getImageMatrix());
+        }
+
+        return Pair.create(originalScaleType, originalMatrix);
+    }
+
+    protected ArrayList<View> createSnapshots(Bundle state, Collection<String> names) {
+        int numSharedElements = names.size();
+        if (numSharedElements == 0) {
+            return null;
+        }
+        ArrayList<View> snapshots = new ArrayList<View>(numSharedElements);
+        Context context = getWindow().getContext();
+        int[] parentLoc = new int[2];
+        getDecor().getLocationOnScreen(parentLoc);
+        for (String name: names) {
+            Bundle sharedElementBundle = state.getBundle(name);
+            if (sharedElementBundle != null) {
+                Bitmap bitmap = sharedElementBundle.getParcelable(KEY_BITMAP);
+                View snapshot = new View(context);
+                Resources resources = getWindow().getContext().getResources();
+                if (bitmap != null) {
+                    snapshot.setBackground(new BitmapDrawable(resources, bitmap));
+                }
+                snapshot.setViewName(name);
+                setSharedElementState(snapshot, name, state, parentLoc);
+                snapshots.add(snapshot);
+            }
+        }
+        return snapshots;
+    }
+
+    protected static void setOriginalImageViewState(
+            ArrayMap<ImageView, Pair<ImageView.ScaleType, Matrix>> originalState) {
+        for (int i = 0; i < originalState.size(); i++) {
+            ImageView imageView = originalState.keyAt(i);
+            Pair<ImageView.ScaleType, Matrix> state = originalState.valueAt(i);
+            imageView.setScaleType(state.first);
+            imageView.setImageMatrix(state.second);
+        }
+    }
+
+    protected Bundle captureSharedElementState() {
+        Bundle bundle = new Bundle();
+        int[] tempLoc = new int[2];
+        for (int i = 0; i < mSharedElementNames.size(); i++) {
+            View sharedElement = mSharedElements.get(i);
+            String name = mSharedElementNames.get(i);
+            captureSharedElementState(sharedElement, name, bundle, tempLoc);
+        }
+        return bundle;
+    }
+
+    /**
+     * Captures placement information for Views with a shared element name for
+     * Activity Transitions.
+     *
+     * @param view           The View to capture the placement information for.
+     * @param name           The shared element name in the target Activity to apply the placement
+     *                       information for.
+     * @param transitionArgs Bundle to store shared element placement information.
+     * @param tempLoc        A temporary int[2] for capturing the current location of views.
+     */
+    private static void captureSharedElementState(View view, String name, Bundle transitionArgs,
+            int[] tempLoc) {
+        Bundle sharedElementBundle = new Bundle();
+        view.getLocationOnScreen(tempLoc);
+        float scaleX = view.getScaleX();
+        sharedElementBundle.putInt(KEY_SCREEN_X, tempLoc[0]);
+        int width = Math.round(view.getWidth() * scaleX);
+        sharedElementBundle.putInt(KEY_WIDTH, width);
+
+        float scaleY = view.getScaleY();
+        sharedElementBundle.putInt(KEY_SCREEN_Y, tempLoc[1]);
+        int height = Math.round(view.getHeight() * scaleY);
+        sharedElementBundle.putInt(KEY_HEIGHT, height);
+
+        sharedElementBundle.putFloat(KEY_TRANSLATION_Z, view.getTranslationZ());
+
+        if (width > 0 && height > 0) {
+            Bitmap bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
+            Canvas canvas = new Canvas(bitmap);
+            view.draw(canvas);
+            sharedElementBundle.putParcelable(KEY_BITMAP, bitmap);
+        }
+
+        if (view instanceof ImageView) {
+            ImageView imageView = (ImageView) view;
+            int scaleTypeInt = scaleTypeToInt(imageView.getScaleType());
+            sharedElementBundle.putInt(KEY_SCALE_TYPE, scaleTypeInt);
+            if (imageView.getScaleType() == ImageView.ScaleType.MATRIX) {
+                float[] matrix = new float[9];
+                imageView.getImageMatrix().getValues(matrix);
+                sharedElementBundle.putFloatArray(KEY_IMAGE_MATRIX, matrix);
+            }
+        }
+
+        transitionArgs.putBundle(name, sharedElementBundle);
+    }
+
+    private static int scaleTypeToInt(ImageView.ScaleType scaleType) {
+        for (int i = 0; i < SCALE_TYPE_VALUES.length; i++) {
+            if (scaleType == SCALE_TYPE_VALUES[i]) {
+                return i;
+            }
+        }
+        return -1;
+    }
+
     private static class FixedEpicenterCallback extends Transition.EpicenterCallback {
         private Rect mEpicenter;
 
diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java
index ff8688d..e03224c 100644
--- a/core/java/android/app/ContextImpl.java
+++ b/core/java/android/app/ContextImpl.java
@@ -35,7 +35,9 @@
 import android.content.IntentFilter;
 import android.content.IIntentReceiver;
 import android.content.IntentSender;
+import android.content.IRestrictionsManager;
 import android.content.ReceiverCallNotAllowedException;
+import android.content.RestrictionsManager;
 import android.content.ServiceConnection;
 import android.content.SharedPreferences;
 import android.content.pm.ApplicationInfo;
@@ -58,7 +60,9 @@
 import android.hardware.SerialManager;
 import android.hardware.SystemSensorManager;
 import android.hardware.hdmi.HdmiCecManager;
+import android.hardware.hdmi.HdmiControlManager;
 import android.hardware.hdmi.IHdmiCecService;
+import android.hardware.hdmi.IHdmiControlService;
 import android.hardware.camera2.CameraManager;
 import android.hardware.display.DisplayManager;
 import android.hardware.input.InputManager;
@@ -71,6 +75,8 @@
 import android.media.AudioManager;
 import android.media.MediaRouter;
 import android.media.session.MediaSessionManager;
+import android.media.tv.ITvInputManager;
+import android.media.tv.TvInputManager;
 import android.net.ConnectivityManager;
 import android.net.IConnectivityManager;
 import android.net.EthernetManager;
@@ -115,8 +121,6 @@
 import android.service.fingerprint.FingerprintManagerReceiver;
 import android.service.fingerprint.FingerprintService;
 import android.telephony.TelephonyManager;
-import android.tv.ITvInputManager;
-import android.tv.TvInputManager;
 import android.content.ClipboardManager;
 import android.util.AndroidRuntimeException;
 import android.util.ArrayMap;
@@ -249,6 +253,8 @@
     private File[] mExternalFilesDirs;
     @GuardedBy("mSync")
     private File[] mExternalCacheDirs;
+    @GuardedBy("mSync")
+    private File[] mExternalMediaDirs;
 
     private static final String[] EMPTY_FILE_LIST = {};
 
@@ -386,6 +392,11 @@
                     return new HdmiCecManager(IHdmiCecService.Stub.asInterface(b));
                 }});
 
+        registerService(HDMI_CONTROL_SERVICE, new StaticServiceFetcher() {
+                public Object createStaticService() {
+                    IBinder b = ServiceManager.getService(HDMI_CONTROL_SERVICE);
+                    return new HdmiControlManager(IHdmiControlService.Stub.asInterface(b));
+                }});
 
         registerService(CLIPBOARD_SERVICE, new ServiceFetcher() {
                 public Object createService(ContextImpl ctx) {
@@ -653,6 +664,13 @@
             }
         });
 
+        registerService(RESTRICTIONS_SERVICE, new ServiceFetcher() {
+            public Object createService(ContextImpl ctx) {
+                IBinder b = ServiceManager.getService(RESTRICTIONS_SERVICE);
+                IRestrictionsManager service = IRestrictionsManager.Stub.asInterface(b);
+                return new RestrictionsManager(ctx, service);
+            }
+        });
         registerService(PRINT_SERVICE, new ServiceFetcher() {
             public Object createService(ContextImpl ctx) {
                 IBinder iBinder = ServiceManager.getService(Context.PRINT_SERVICE);
@@ -1032,6 +1050,18 @@
     }
 
     @Override
+    public File[] getExternalMediaDirs() {
+        synchronized (mSync) {
+            if (mExternalMediaDirs == null) {
+                mExternalMediaDirs = Environment.buildExternalStorageAppMediaDirs(getPackageName());
+            }
+
+            // Create dirs if needed
+            return ensureDirsExistOrFilter(mExternalMediaDirs);
+        }
+    }
+
+    @Override
     public File getFileStreamPath(String name) {
         return makeFilename(getFilesDir(), name);
     }
@@ -1734,7 +1764,8 @@
                 arguments.setAllowFds(false);
             }
             return ActivityManagerNative.getDefault().startInstrumentation(
-                    className, profileFile, 0, arguments, null, null, getUserId());
+                    className, profileFile, 0, arguments, null, null, getUserId(),
+                    null /* ABI override */);
         } catch (RemoteException e) {
             // System has crashed, nothing we can do.
         }
diff --git a/core/java/android/app/EnterTransitionCoordinator.java b/core/java/android/app/EnterTransitionCoordinator.java
index bc97852..4b052e7 100644
--- a/core/java/android/app/EnterTransitionCoordinator.java
+++ b/core/java/android/app/EnterTransitionCoordinator.java
@@ -18,11 +18,7 @@
 import android.animation.Animator;
 import android.animation.AnimatorListenerAdapter;
 import android.animation.ObjectAnimator;
-import android.content.Context;
-import android.content.res.Resources;
-import android.graphics.Bitmap;
 import android.graphics.Matrix;
-import android.graphics.drawable.BitmapDrawable;
 import android.graphics.drawable.Drawable;
 import android.os.Bundle;
 import android.os.Handler;
@@ -33,12 +29,12 @@
 import android.util.ArrayMap;
 import android.util.Pair;
 import android.view.View;
+import android.view.ViewGroup;
 import android.view.ViewGroupOverlay;
 import android.view.ViewTreeObserver;
 import android.widget.ImageView;
 
 import java.util.ArrayList;
-import java.util.Collection;
 
 /**
  * This ActivityTransitionCoordinator is created by the Activity to manage
@@ -56,6 +52,7 @@
     private Handler mHandler;
     private boolean mIsCanceled;
     private ObjectAnimator mBackgroundAnimator;
+    private boolean mIsExitTransitionComplete;
 
     public EnterTransitionCoordinator(Activity activity, ResultReceiver resultReceiver,
             ArrayList<String> sharedElementNames,
@@ -76,6 +73,24 @@
                 }
             };
             mHandler.sendEmptyMessageDelayed(MSG_CANCEL, MAX_WAIT_MS);
+            send(MSG_SEND_SHARED_ELEMENT_DESTINATION, null);
+        }
+    }
+
+    private void sendSharedElementDestination() {
+        ViewGroup decor = getDecor();
+        if (!decor.isLayoutRequested()) {
+            Bundle state = captureSharedElementState();
+            mResultReceiver.send(MSG_SHARED_ELEMENT_DESTINATION, state);
+        } else {
+            getDecor().getViewTreeObserver()
+                    .addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() {
+                        @Override
+                        public boolean onPreDraw() {
+                            getDecor().getViewTreeObserver().removeOnPreDrawListener(this);
+                            return true;
+                        }
+                    });
         }
     }
 
@@ -98,9 +113,8 @@
                 break;
             case MSG_EXIT_TRANSITION_COMPLETE:
                 if (!mIsCanceled) {
-                    if (!mSharedElementTransitionStarted) {
-                        send(resultCode, resultData);
-                    } else {
+                    mIsExitTransitionComplete = true;
+                    if (mSharedElementTransitionStarted) {
                         onRemoteExitTransitionComplete();
                     }
                 }
@@ -108,6 +122,9 @@
             case MSG_CANCEL:
                 cancel();
                 break;
+            case MSG_SEND_SHARED_ELEMENT_DESTINATION:
+                sendSharedElementDestination();
+                break;
         }
     }
 
@@ -183,6 +200,7 @@
         setViewVisibility(mSharedElements, View.VISIBLE);
         ArrayMap<ImageView, Pair<ImageView.ScaleType, Matrix>> originalImageViewState =
                 setSharedElementState(sharedElementState, sharedElementSnapshots);
+        requestLayoutForSharedElements();
 
         boolean startEnterTransition = allowOverlappingTransitions();
         boolean startSharedElementTransition = true;
@@ -200,6 +218,13 @@
         mResultReceiver = null; // all done sending messages.
     }
 
+    private void requestLayoutForSharedElements() {
+        int numSharedElements = mSharedElements.size();
+        for (int i = 0; i < numSharedElements; i++) {
+            mSharedElements.get(i).requestLayout();
+        }
+    }
+
     private Transition beginTransition(boolean startEnterTransition,
             boolean startSharedElementTransition) {
         Transition sharedElementTransition = null;
@@ -213,6 +238,19 @@
         }
 
         Transition transition = mergeTransitions(sharedElementTransition, viewsTransition);
+        if (startSharedElementTransition) {
+            if (transition == null) {
+                sharedElementTransitionStarted();
+            } else {
+                transition.addListener(new Transition.TransitionListenerAdapter() {
+                    @Override
+                    public void onTransitionStart(Transition transition) {
+                        transition.removeListener(this);
+                        sharedElementTransitionStarted();
+                    }
+                });
+            }
+        }
         if (transition != null) {
             TransitionManager.beginDelayedTransition(getDecor(), transition);
             if (startSharedElementTransition && !mSharedElementNames.isEmpty()) {
@@ -224,6 +262,13 @@
         return transition;
     }
 
+    private void sharedElementTransitionStarted() {
+        mSharedElementTransitionStarted = true;
+        if (mIsExitTransitionComplete) {
+            send(MSG_EXIT_TRANSITION_COMPLETE, null);
+        }
+    }
+
     private void startEnterTransition(Transition transition) {
         setViewVisibility(mTransitioningViews, View.VISIBLE);
         if (!mIsReturning) {
@@ -310,142 +355,4 @@
             startEnterTransition(transition);
         }
     }
-
-    private ArrayList<View> createSnapshots(Bundle state, Collection<String> names) {
-        int numSharedElements = names.size();
-        if (numSharedElements == 0) {
-            return null;
-        }
-        ArrayList<View> snapshots = new ArrayList<View>(numSharedElements);
-        Context context = getWindow().getContext();
-        int[] parentLoc = new int[2];
-        getDecor().getLocationOnScreen(parentLoc);
-        for (String name: names) {
-            Bundle sharedElementBundle = state.getBundle(name);
-            if (sharedElementBundle != null) {
-                Bitmap bitmap = sharedElementBundle.getParcelable(KEY_BITMAP);
-                View snapshot = new View(context);
-                Resources resources = getWindow().getContext().getResources();
-                snapshot.setBackground(new BitmapDrawable(resources, bitmap));
-                snapshot.setViewName(name);
-                setSharedElementState(snapshot, name, state, parentLoc);
-                snapshots.add(snapshot);
-            }
-        }
-        return snapshots;
-    }
-
-    private static void setSharedElementState(View view, String name, Bundle transitionArgs,
-            int[] parentLoc) {
-        Bundle sharedElementBundle = transitionArgs.getBundle(name);
-        if (sharedElementBundle == null) {
-            return;
-        }
-
-        if (view instanceof ImageView) {
-            int scaleTypeInt = sharedElementBundle.getInt(KEY_SCALE_TYPE, -1);
-            if (scaleTypeInt >= 0) {
-                ImageView imageView = (ImageView) view;
-                ImageView.ScaleType scaleType = SCALE_TYPE_VALUES[scaleTypeInt];
-                imageView.setScaleType(scaleType);
-                if (scaleType == ImageView.ScaleType.MATRIX) {
-                    float[] matrixValues = sharedElementBundle.getFloatArray(KEY_IMAGE_MATRIX);
-                    Matrix matrix = new Matrix();
-                    matrix.setValues(matrixValues);
-                    imageView.setImageMatrix(matrix);
-                }
-            }
-        }
-
-        float z = sharedElementBundle.getFloat(KEY_TRANSLATION_Z);
-        view.setTranslationZ(z);
-
-        int x = sharedElementBundle.getInt(KEY_SCREEN_X);
-        int y = sharedElementBundle.getInt(KEY_SCREEN_Y);
-        int width = sharedElementBundle.getInt(KEY_WIDTH);
-        int height = sharedElementBundle.getInt(KEY_HEIGHT);
-
-        int widthSpec = View.MeasureSpec.makeMeasureSpec(width, View.MeasureSpec.EXACTLY);
-        int heightSpec = View.MeasureSpec.makeMeasureSpec(height, View.MeasureSpec.EXACTLY);
-        view.measure(widthSpec, heightSpec);
-
-        int left = x - parentLoc[0];
-        int top = y - parentLoc[1];
-        int right = left + width;
-        int bottom = top + height;
-        view.layout(left, top, right, bottom);
-    }
-
-    private ArrayMap<ImageView, Pair<ImageView.ScaleType, Matrix>> setSharedElementState(
-            Bundle sharedElementState, final ArrayList<View> snapshots) {
-        ArrayMap<ImageView, Pair<ImageView.ScaleType, Matrix>> originalImageState =
-                new ArrayMap<ImageView, Pair<ImageView.ScaleType, Matrix>>();
-        if (sharedElementState != null) {
-            int[] tempLoc = new int[2];
-            for (int i = 0; i < mSharedElementNames.size(); i++) {
-                View sharedElement = mSharedElements.get(i);
-                String name = mSharedElementNames.get(i);
-                Pair<ImageView.ScaleType, Matrix> originalState = getOldImageState(sharedElement,
-                        name, sharedElementState);
-                if (originalState != null) {
-                    originalImageState.put((ImageView) sharedElement, originalState);
-                }
-                View parent = (View) sharedElement.getParent();
-                parent.getLocationOnScreen(tempLoc);
-                setSharedElementState(sharedElement, name, sharedElementState, tempLoc);
-                sharedElement.requestLayout();
-            }
-        }
-        mListener.setSharedElementStart(mSharedElementNames, mSharedElements, snapshots);
-
-        getDecor().getViewTreeObserver().addOnPreDrawListener(
-                new ViewTreeObserver.OnPreDrawListener() {
-                    @Override
-                    public boolean onPreDraw() {
-                        getDecor().getViewTreeObserver().removeOnPreDrawListener(this);
-                        mListener.setSharedElementEnd(mSharedElementNames, mSharedElements,
-                                snapshots);
-                        mSharedElementTransitionStarted = true;
-                        return true;
-                    }
-                }
-        );
-        return originalImageState;
-    }
-
-    private static Pair<ImageView.ScaleType, Matrix> getOldImageState(View view, String name,
-            Bundle transitionArgs) {
-        if (!(view instanceof ImageView)) {
-            return null;
-        }
-        Bundle bundle = transitionArgs.getBundle(name);
-        if (bundle == null) {
-            return null;
-        }
-        int scaleTypeInt = bundle.getInt(KEY_SCALE_TYPE, -1);
-        if (scaleTypeInt < 0) {
-            return null;
-        }
-
-        ImageView imageView = (ImageView) view;
-        ImageView.ScaleType originalScaleType = imageView.getScaleType();
-
-        Matrix originalMatrix = null;
-        if (originalScaleType == ImageView.ScaleType.MATRIX) {
-            originalMatrix = new Matrix(imageView.getImageMatrix());
-        }
-
-        return Pair.create(originalScaleType, originalMatrix);
-    }
-
-    private static void setOriginalImageViewState(
-            ArrayMap<ImageView, Pair<ImageView.ScaleType, Matrix>> originalState) {
-        for (int i = 0; i < originalState.size(); i++) {
-            ImageView imageView = originalState.keyAt(i);
-            Pair<ImageView.ScaleType, Matrix> state = originalState.valueAt(i);
-            imageView.setScaleType(state.first);
-            imageView.setImageMatrix(state.second);
-        }
-    }
-
 }
diff --git a/core/java/android/app/ExitTransitionCoordinator.java b/core/java/android/app/ExitTransitionCoordinator.java
index 93eb53e..ba1638f 100644
--- a/core/java/android/app/ExitTransitionCoordinator.java
+++ b/core/java/android/app/ExitTransitionCoordinator.java
@@ -19,8 +19,6 @@
 import android.animation.AnimatorListenerAdapter;
 import android.animation.ObjectAnimator;
 import android.content.Intent;
-import android.graphics.Bitmap;
-import android.graphics.Canvas;
 import android.graphics.drawable.ColorDrawable;
 import android.graphics.drawable.Drawable;
 import android.os.Bundle;
@@ -29,7 +27,7 @@
 import android.transition.Transition;
 import android.transition.TransitionManager;
 import android.view.View;
-import android.widget.ImageView;
+import android.view.ViewTreeObserver;
 
 import java.util.ArrayList;
 
@@ -62,6 +60,10 @@
 
     private boolean mIsHidden;
 
+    private boolean mExitTransitionStarted;
+
+    private Bundle mExitSharedElementBundle;
+
     public ExitTransitionCoordinator(Activity activity, ArrayList<String> names,
             ArrayList<String> accepted, ArrayList<String> mapped, boolean isReturning) {
         super(activity.getWindow(), names, accepted, mapped, getListener(activity, isReturning),
@@ -102,15 +104,32 @@
                 setViewVisibility(mSharedElements, View.VISIBLE);
                 mIsHidden = true;
                 break;
+            case MSG_SHARED_ELEMENT_DESTINATION:
+                mExitSharedElementBundle = resultData;
+                if (mExitTransitionStarted) {
+                    startSharedElementExit();
+                }
+                break;
+        }
+    }
+
+    private void startSharedElementExit() {
+        if (!mSharedElements.isEmpty() && getSharedElementTransition() != null) {
+            Transition transition = getSharedElementExitTransition();
+            TransitionManager.beginDelayedTransition(getDecor(), transition);
+            ArrayList<View> sharedElementSnapshots = createSnapshots(mExitSharedElementBundle,
+                    mSharedElementNames);
+            setSharedElementState(mExitSharedElementBundle, sharedElementSnapshots);
         }
     }
 
     private void hideSharedElements() {
         setViewVisibility(mSharedElements, View.INVISIBLE);
+        finishIfNecessary();
     }
 
     public void startExit() {
-        beginTransition();
+        beginTransitions();
         setViewVisibility(mTransitioningViews, View.INVISIBLE);
     }
 
@@ -140,7 +159,30 @@
                 }
             }
         }, options);
-        startExit();
+        Transition sharedElementTransition = mSharedElements.isEmpty()
+                ? null : getSharedElementTransition();
+        if (sharedElementTransition == null) {
+            sharedElementTransitionComplete();
+        }
+        Transition transition = mergeTransitions(sharedElementTransition, getExitTransition());
+        if (transition == null) {
+            mExitTransitionStarted = true;
+        } else {
+            TransitionManager.beginDelayedTransition(getDecor(), transition);
+            setViewVisibility(mTransitioningViews, View.INVISIBLE);
+            getDecor().getViewTreeObserver().addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() {
+                @Override
+                public boolean onPreDraw() {
+                    getDecor().getViewTreeObserver().removeOnPreDrawListener(this);
+                    mExitTransitionStarted = true;
+                    if (mExitSharedElementBundle != null) {
+                        startSharedElementExit();
+                    }
+                    notifyComplete();
+                    return true;
+                }
+            });
+        }
     }
 
     private void fadeOutBackground() {
@@ -162,24 +204,13 @@
         }
     }
 
-    private void beginTransition() {
-        Transition sharedElementTransition = configureTransition(getSharedElementTransition());
-        Transition viewsTransition = configureTransition(getViewsTransition());
-        viewsTransition = addTargets(viewsTransition, mTransitioningViews);
-        if (sharedElementTransition == null || mSharedElements.isEmpty()) {
-            sharedElementTransitionComplete();
-            sharedElementTransition = null;
-        } else {
-            sharedElementTransition.addListener(new Transition.TransitionListenerAdapter() {
-                @Override
-                public void onTransitionEnd(Transition transition) {
-                    sharedElementTransitionComplete();
-                }
-            });
+    private Transition getExitTransition() {
+        Transition viewsTransition = null;
+        if (!mTransitioningViews.isEmpty()) {
+            viewsTransition = configureTransition(getViewsTransition());
         }
-        if (viewsTransition == null || mTransitioningViews.isEmpty()) {
+        if (viewsTransition == null) {
             exitTransitionComplete();
-            viewsTransition = null;
         } else {
             viewsTransition.addListener(new Transition.TransitionListenerAdapter() {
                 @Override
@@ -189,13 +220,46 @@
                         setViewVisibility(mTransitioningViews, View.VISIBLE);
                     }
                 }
+
+                @Override
+                public void onTransitionCancel(Transition transition) {
+                    super.onTransitionCancel(transition);
+                }
             });
         }
+        return viewsTransition;
+    }
+
+    private Transition getSharedElementExitTransition() {
+        Transition sharedElementTransition = null;
+        if (!mSharedElements.isEmpty()) {
+            sharedElementTransition = configureTransition(getSharedElementTransition());
+        }
+        if (sharedElementTransition == null) {
+            sharedElementTransitionComplete();
+        } else {
+            sharedElementTransition.addListener(new Transition.TransitionListenerAdapter() {
+                @Override
+                public void onTransitionEnd(Transition transition) {
+                    sharedElementTransitionComplete();
+                    if (mIsHidden) {
+                        setViewVisibility(mSharedElements, View.VISIBLE);
+                    }
+                }
+            });
+            mSharedElements.get(0).invalidate();
+        }
+        return sharedElementTransition;
+    }
+
+    private void beginTransitions() {
+        Transition sharedElementTransition = getSharedElementExitTransition();
+        Transition viewsTransition = getExitTransition();
 
         Transition transition = mergeTransitions(sharedElementTransition, viewsTransition);
-        TransitionManager.beginDelayedTransition(getDecor(), transition);
-        if (viewsTransition == null && sharedElementTransition != null) {
-            mSharedElements.get(0).requestLayout();
+        mExitTransitionStarted = true;
+        if (transition != null) {
+            TransitionManager.beginDelayedTransition(getDecor(), transition);
         }
     }
 
@@ -205,18 +269,12 @@
     }
 
     protected boolean isReadyToNotify() {
-        return mSharedElementBundle != null && mResultReceiver != null && mIsBackgroundReady;
+        return mSharedElementBundle != null && mResultReceiver != null && mIsBackgroundReady
+                && mExitTransitionStarted;
     }
 
     private void sharedElementTransitionComplete() {
-        Bundle bundle = new Bundle();
-        int[] tempLoc = new int[2];
-        for (int i = 0; i < mSharedElementNames.size(); i++) {
-            View sharedElement = mSharedElements.get(i);
-            String name = mSharedElementNames.get(i);
-            captureSharedElementState(sharedElement, name, bundle, tempLoc);
-        }
-        mSharedElementBundle = bundle;
+        mSharedElementBundle = captureSharedElementState();
         notifyComplete();
     }
 
@@ -230,15 +288,23 @@
                 mExitNotified = true;
                 mResultReceiver.send(MSG_EXIT_TRANSITION_COMPLETE, null);
                 mResultReceiver = null; // done talking
-                if (mIsReturning) {
-                    mActivity.finish();
-                    mActivity.overridePendingTransition(0, 0);
-                }
-                mActivity = null;
+                finishIfNecessary();
             }
         }
     }
 
+    private void finishIfNecessary() {
+        if (mIsReturning && mExitNotified && mActivity != null && (mSharedElements.isEmpty()
+                || mSharedElements.get(0).getVisibility() == View.INVISIBLE)) {
+            mActivity.finish();
+            mActivity.overridePendingTransition(0, 0);
+            mActivity = null;
+        }
+        if (!mIsReturning && mExitNotified) {
+            mActivity = null; // don't need it anymore
+        }
+    }
+
     @Override
     protected Transition getViewsTransition() {
         if (mIsReturning) {
@@ -255,58 +321,4 @@
             return getWindow().getSharedElementExitTransition();
         }
     }
-
-    /**
-     * Captures placement information for Views with a shared element name for
-     * Activity Transitions.
-     *
-     * @param view           The View to capture the placement information for.
-     * @param name           The shared element name in the target Activity to apply the placement
-     *                       information for.
-     * @param transitionArgs Bundle to store shared element placement information.
-     * @param tempLoc        A temporary int[2] for capturing the current location of views.
-     */
-    private static void captureSharedElementState(View view, String name, Bundle transitionArgs,
-            int[] tempLoc) {
-        Bundle sharedElementBundle = new Bundle();
-        view.getLocationOnScreen(tempLoc);
-        float scaleX = view.getScaleX();
-        sharedElementBundle.putInt(KEY_SCREEN_X, tempLoc[0]);
-        int width = Math.round(view.getWidth() * scaleX);
-        sharedElementBundle.putInt(KEY_WIDTH, width);
-
-        float scaleY = view.getScaleY();
-        sharedElementBundle.putInt(KEY_SCREEN_Y, tempLoc[1]);
-        int height = Math.round(view.getHeight() * scaleY);
-        sharedElementBundle.putInt(KEY_HEIGHT, height);
-
-        sharedElementBundle.putFloat(KEY_TRANSLATION_Z, view.getTranslationZ());
-
-        Bitmap bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
-        Canvas canvas = new Canvas(bitmap);
-        view.draw(canvas);
-        sharedElementBundle.putParcelable(KEY_BITMAP, bitmap);
-
-        if (view instanceof ImageView) {
-            ImageView imageView = (ImageView) view;
-            int scaleTypeInt = scaleTypeToInt(imageView.getScaleType());
-            sharedElementBundle.putInt(KEY_SCALE_TYPE, scaleTypeInt);
-            if (imageView.getScaleType() == ImageView.ScaleType.MATRIX) {
-                float[] matrix = new float[9];
-                imageView.getImageMatrix().getValues(matrix);
-                sharedElementBundle.putFloatArray(KEY_IMAGE_MATRIX, matrix);
-            }
-        }
-
-        transitionArgs.putBundle(name, sharedElementBundle);
-    }
-
-    private static int scaleTypeToInt(ImageView.ScaleType scaleType) {
-        for (int i = 0; i < SCALE_TYPE_VALUES.length; i++) {
-            if (scaleType == SCALE_TYPE_VALUES[i]) {
-                return i;
-            }
-        }
-        return -1;
-    }
 }
diff --git a/core/java/android/app/IActivityManager.java b/core/java/android/app/IActivityManager.java
index 8434c2a..bf2d7e5 100644
--- a/core/java/android/app/IActivityManager.java
+++ b/core/java/android/app/IActivityManager.java
@@ -176,7 +176,8 @@
 
     public boolean startInstrumentation(ComponentName className, String profileFile,
             int flags, Bundle arguments, IInstrumentationWatcher watcher,
-            IUiAutomationConnection connection, int userId) throws RemoteException;
+            IUiAutomationConnection connection, int userId,
+            String abiOverride) throws RemoteException;
     public void finishInstrumentation(IApplicationThread target,
             int resultCode, Bundle results) throws RemoteException;
 
diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java
index 6e23b11..8dba1dc 100644
--- a/core/java/android/app/Notification.java
+++ b/core/java/android/app/Notification.java
@@ -989,10 +989,9 @@
          * <pre class="prettyprint">
          * Notification.Action action = new Notification.Action.Builder(
          *         R.drawable.archive_all, "Archive all", actionIntent)
-         *         .apply(new Notification.Action.WearableExtender()
+         *         .extend(new Notification.Action.WearableExtender()
          *                 .setAvailableOffline(false))
-         *         .build();
-         * </pre>
+         *         .build();</pre>
          */
         public static final class WearableExtender implements Extender {
             /** Notification action extra which contains wearable extensions */
@@ -1672,7 +1671,6 @@
         private Notification mPublicVersion = null;
         private final NotificationColorUtil mColorUtil;
         private ArrayList<String> mPeople;
-        private boolean mPreQuantum;
         private int mColor = COLOR_DEFAULT;
 
         /**
@@ -1695,6 +1693,15 @@
          *            object.
          */
         public Builder(Context context) {
+            /*
+             * Important compatibility note!
+             * Some apps out in the wild create a Notification.Builder in their Activity subclass
+             * constructor for later use. At this point Activities - themselves subclasses of
+             * ContextWrapper - do not have their inner Context populated yet. This means that
+             * any calls to Context methods from within this constructor can cause NPEs in existing
+             * apps. Any data populated from mContext should therefore be populated lazily to
+             * preserve compatibility.
+             */
             mContext = context;
 
             // Set defaults to match the defaults of a Notification
@@ -1703,7 +1710,6 @@
             mPriority = PRIORITY_DEFAULT;
             mPeople = new ArrayList<String>();
 
-            mPreQuantum = context.getApplicationInfo().targetSdkVersion < Build.VERSION_CODES.L;
             mColorUtil = NotificationColorUtil.getInstance();
         }
 
@@ -3307,8 +3313,7 @@
      * <pre class="prettyprint">
      * Notification.WearableExtender wearableExtender = new Notification.WearableExtender(
      *         notification);
-     * List&lt;Notification&gt; pages = wearableExtender.getPages();
-     * </pre>
+     * List&lt;Notification&gt; pages = wearableExtender.getPages();</pre>
      */
     public static final class WearableExtender implements Extender {
         /**
@@ -3357,6 +3362,14 @@
          */
         public static final int SIZE_LARGE = 4;
 
+        /**
+         * Size value for use with {@link #setCustomSizePreset} to show this notification
+         * full screen.
+         * <p>This value is only applicable for custom display notifications created using
+         * {@link #setDisplayIntent}.
+         */
+        public static final int SIZE_FULL_SCREEN = 5;
+
         /** Notification extra which contains wearable extensions */
         private static final String EXTRA_WEARABLE_EXTENSIONS = "android.wearable.EXTENSIONS";
 
@@ -3553,7 +3566,27 @@
 
         /**
          * Set an intent to launch inside of an activity view when displaying
-         * this notification. This {@link PendingIntent} should be for an activity.
+         * this notification. The {@link PendingIntent} provided should be for an activity.
+         *
+         * <pre class="prettyprint">
+         * Intent displayIntent = new Intent(context, MyDisplayActivity.class);
+         * PendingIntent displayPendingIntent = PendingIntent.getActivity(context,
+         *         0, displayIntent, PendingIntent.FLAG_UPDATE_CURRENT);
+         * Notification notif = new Notification.Builder(context)
+         *         .extend(new Notification.WearableExtender()
+         *                 .setDisplayIntent(displayPendingIntent)
+         *                 .setCustomSizePreset(Notification.WearableExtender.SIZE_MEDIUM))
+         *         .build();</pre>
+         *
+         * <p>The activity to launch needs to allow embedding, must be exported, and
+         * should have an empty task affinity.
+         *
+         * <p>Example AndroidManifest.xml entry:
+         * <pre class="prettyprint">
+         * &lt;activity android:name=&quot;com.example.MyDisplayActivity&quot;
+         *     android:exported=&quot;true&quot;
+         *     android:allowEmbedded=&quot;true&quot;
+         *     android:taskAffinity=&quot;&quot; /&gt;</pre>
          *
          * @param intent the {@link PendingIntent} for an activity
          * @return this object for method chaining
@@ -3687,12 +3720,17 @@
 
         /**
          * Set an action from this notification's actions to be clickable with the content of
-         * this notification page. This action will no longer display separately from the
-         * notification content. This action's icon will display with optional subtext provided
-         * by the action's title.
-         * @param actionIndex The index of the action to hoist on the current notification page.
-         *                    If wearable actions are present, this index will apply to that list,
-         *                    otherwise it will apply to the main notification's actions list.
+         * this notification. This action will no longer display separately from the
+         * notification's content.
+         *
+         * <p>For notifications with multiple pages, child pages can also have content actions
+         * set, although the list of available actions comes from the main notification and not
+         * from the child page's notification.
+         *
+         * @param actionIndex The index of the action to hoist onto the current notification page.
+         *                    If wearable actions were added to the main notification, this index
+         *                    will apply to that list, otherwise it will apply to the regular
+         *                    actions list.
          */
         public WearableExtender setContentAction(int actionIndex) {
             mContentActionIndex = actionIndex;
@@ -3700,14 +3738,18 @@
         }
 
         /**
-         * Get the action index of an action from this notification to show as clickable with
-         * the content of this notification page. When the user clicks this notification page,
-         * this action will trigger. This action will no longer display separately from the
-         * notification content. The action's icon will display with optional subtext provided
-         * by the action's title.
+         * Get the index of the notification action, if any, that was specified as being clickable
+         * with the content of this notification. This action will no longer display separately
+         * from the notification's content.
          *
-         * <p>If wearable specific actions are present, this index will apply to that list,
-         * otherwise it will apply to the main notification's actions list.
+         * <p>For notifications with multiple pages, child pages can also have content actions
+         * set, although the list of available actions comes from the main notification and not
+         * from the child page's notification.
+         *
+         * <p>If wearable specific actions were added to the main notification, this index will
+         * apply to that list, otherwise it will apply to the regular actions list.
+         *
+         * @return the action index or {@link #UNSET_ACTION_INDEX} if no action was selected.
          */
         public int getContentAction() {
             return mContentActionIndex;
diff --git a/core/java/android/app/VoiceInteractor.java b/core/java/android/app/VoiceInteractor.java
index 6dc48b0..85e970c 100644
--- a/core/java/android/app/VoiceInteractor.java
+++ b/core/java/android/app/VoiceInteractor.java
@@ -30,18 +30,39 @@
 import com.android.internal.os.HandlerCaller;
 import com.android.internal.os.SomeArgs;
 
-import java.util.WeakHashMap;
+import java.util.ArrayList;
 
 /**
- * Interface for an {@link Activity} to interact with the user through voice.
+ * Interface for an {@link Activity} to interact with the user through voice.  Use
+ * {@link android.app.Activity#getVoiceInteractor() Activity.getVoiceInteractor}
+ * to retrieve the interface, if the activity is currently involved in a voice interaction.
+ *
+ * <p>The voice interactor revolves around submitting voice interaction requests to the
+ * back-end voice interaction service that is working with the user.  These requests are
+ * submitted with {@link #submitRequest}, providing a new instance of a
+ * {@link Request} subclass describing the type of operation to perform -- currently the
+ * possible requests are {@link ConfirmationRequest} and {@link CommandRequest}.
+ *
+ * <p>Once a request is submitted, the voice system will process it and eventually deliver
+ * the result to the request object.  The application can cancel a pending request at any
+ * time.
+ *
+ * <p>The VoiceInteractor is integrated with Activity's state saving mechanism, so that
+ * if an activity is being restarted with retained state, it will retain the current
+ * VoiceInteractor and any outstanding requests.  Because of this, you should always use
+ * {@link Request#getActivity() Request.getActivity} to get back to the activity of a
+ * request, rather than holding on to the activity instance yourself, either explicitly
+ * or implicitly through a non-static inner class.
  */
 public class VoiceInteractor {
     static final String TAG = "VoiceInteractor";
     static final boolean DEBUG = true;
 
-    final Context mContext;
-    final Activity mActivity;
     final IVoiceInteractor mInteractor;
+
+    Context mContext;
+    Activity mActivity;
+
     final HandlerCaller mHandlerCaller;
     final HandlerCaller.Callback mHandlerCallerCallback = new HandlerCaller.Callback() {
         @Override
@@ -60,6 +81,16 @@
                         request.clear();
                     }
                     break;
+                case MSG_ABORT_VOICE_RESULT:
+                    request = pullRequest((IVoiceInteractorRequest)args.arg1, true);
+                    if (DEBUG) Log.d(TAG, "onAbortVoice: req="
+                            + ((IVoiceInteractorRequest)args.arg1).asBinder() + "/" + request
+                            + " result=" + args.arg1);
+                    if (request != null) {
+                        ((AbortVoiceRequest)request).onAbortResult((Bundle) args.arg2);
+                        request.clear();
+                    }
+                    break;
                 case MSG_COMMAND_RESULT:
                     request = pullRequest((IVoiceInteractorRequest)args.arg1, msg.arg1 != 0);
                     if (DEBUG) Log.d(TAG, "onCommandResult: req="
@@ -94,6 +125,12 @@
         }
 
         @Override
+        public void deliverAbortVoiceResult(IVoiceInteractorRequest request, Bundle result) {
+            mHandlerCaller.sendMessage(mHandlerCaller.obtainMessageOO(
+                    MSG_ABORT_VOICE_RESULT, request, result));
+        }
+
+        @Override
         public void deliverCommandResult(IVoiceInteractorRequest request, boolean complete,
                 Bundle result) {
             mHandlerCaller.sendMessage(mHandlerCaller.obtainMessageIOO(
@@ -110,8 +147,9 @@
     final ArrayMap<IBinder, Request> mActiveRequests = new ArrayMap<IBinder, Request>();
 
     static final int MSG_CONFIRMATION_RESULT = 1;
-    static final int MSG_COMMAND_RESULT = 2;
-    static final int MSG_CANCEL_RESULT = 3;
+    static final int MSG_ABORT_VOICE_RESULT = 2;
+    static final int MSG_COMMAND_RESULT = 3;
+    static final int MSG_CANCEL_RESULT = 4;
 
     public static abstract class Request {
         IVoiceInteractorRequest mRequestInterface;
@@ -140,6 +178,12 @@
         public void onCancel() {
         }
 
+        public void onAttached(Activity activity) {
+        }
+
+        public void onDetached() {
+        }
+
         void clear() {
             mRequestInterface = null;
             mContext = null;
@@ -180,9 +224,42 @@
 
         IVoiceInteractorRequest submit(IVoiceInteractor interactor, String packageName,
                 IVoiceInteractorCallback callback) throws RemoteException {
-            return interactor.startConfirmation(packageName, callback, mPrompt.toString(), mExtras);
+            return interactor.startConfirmation(packageName, callback, mPrompt, mExtras);
         }
-   }
+    }
+
+    public static class AbortVoiceRequest extends Request {
+        final CharSequence mMessage;
+        final Bundle mExtras;
+
+        /**
+         * Reports that the current interaction can not be complete with voice, so the
+         * application will need to switch to a traditional input UI.  Applications should
+         * only use this when they need to completely bail out of the voice interaction
+         * and switch to a traditional UI.  When the response comes back, the voice
+         * system has handled the request and is ready to switch; at that point the application
+         * can start a new non-voice activity.  Be sure when starting the new activity
+         * to use {@link android.content.Intent#FLAG_ACTIVITY_NEW_TASK
+         * Intent.FLAG_ACTIVITY_NEW_TASK} to keep the new activity out of the current voice
+         * interaction task.
+         *
+         * @param message Optional message to tell user about not being able to complete
+         * the interaction with voice.
+         * @param extras Additional optional information.
+         */
+        public AbortVoiceRequest(CharSequence message, Bundle extras) {
+            mMessage = message;
+            mExtras = extras;
+        }
+
+        public void onAbortResult(Bundle result) {
+        }
+
+        IVoiceInteractorRequest submit(IVoiceInteractor interactor, String packageName,
+                IVoiceInteractorCallback callback) throws RemoteException {
+            return interactor.startAbortVoice(packageName, callback, mMessage, mExtras);
+        }
+    }
 
     public static class CommandRequest extends Request {
         final String mCommand;
@@ -220,11 +297,11 @@
         }
    }
 
-    VoiceInteractor(Context context, Activity activity, IVoiceInteractor interactor,
+    VoiceInteractor(IVoiceInteractor interactor, Context context, Activity activity,
             Looper looper) {
+        mInteractor = interactor;
         mContext = context;
         mActivity = activity;
-        mInteractor = interactor;
         mHandlerCaller = new HandlerCaller(context, looper, mHandlerCallerCallback, true);
     }
 
@@ -238,6 +315,49 @@
         }
     }
 
+    private ArrayList<Request> makeRequestList() {
+        final int N = mActiveRequests.size();
+        if (N < 1) {
+            return null;
+        }
+        ArrayList<Request> list = new ArrayList<Request>(N);
+        for (int i=0; i<N; i++) {
+            list.add(mActiveRequests.valueAt(i));
+        }
+        return list;
+    }
+
+    void attachActivity(Activity activity) {
+        if (mActivity == activity) {
+            return;
+        }
+        mContext = activity;
+        mActivity = activity;
+        ArrayList<Request> reqs = makeRequestList();
+        if (reqs != null) {
+            for (int i=0; i<reqs.size(); i++) {
+                Request req = reqs.get(i);
+                req.mContext = activity;
+                req.mActivity = activity;
+                req.onAttached(activity);
+            }
+        }
+    }
+
+    void detachActivity() {
+        ArrayList<Request> reqs = makeRequestList();
+        if (reqs != null) {
+            for (int i=0; i<reqs.size(); i++) {
+                Request req = reqs.get(i);
+                req.onDetached();
+                req.mActivity = null;
+                req.mContext = null;
+            }
+        }
+        mContext = null;
+        mActivity = null;
+    }
+
     public boolean submitRequest(Request request) {
         try {
             IVoiceInteractorRequest ireq = request.submit(mInteractor,
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index 3ef16a9..24a354f 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -25,6 +25,7 @@
 import android.content.pm.ActivityInfo;
 import android.content.pm.PackageManager;
 import android.content.pm.ResolveInfo;
+import android.content.RestrictionsManager;
 import android.os.Bundle;
 import android.os.Handler;
 import android.os.Process;
@@ -103,6 +104,17 @@
         = "android.app.action.ACTION_PROVISION_MANAGED_PROFILE";
 
     /**
+     * A broadcast intent with this action can be sent to ManagedProvisionning to specify that the
+     * user has already consented to the creation of the managed profile.
+     * The intent must contain the extras
+     * {@link #EXTRA_PROVISIONING_DEVICE_ADMIN_PACKAGE_NAME} and
+     * {@link #EXTRA_PROVISIONING_TOKEN}
+     * @hide
+     */
+    public static final String ACTION_PROVISIONING_USER_HAS_CONSENTED
+        = "android.app.action.USER_HAS_CONSENTED";
+
+    /**
      * A String extra holding the name of the package of the mobile device management application
      * that starts the managed provisioning flow. This package will be set as the profile owner.
      * <p>Use with {@link #ACTION_PROVISION_MANAGED_PROFILE}.
@@ -111,6 +123,13 @@
         = "deviceAdminPackageName";
 
     /**
+     * An int extra used to identify the consent of the user to create the managed profile.
+     * <p>Use with {@link #ACTION_PROVISION_MANAGED_PROFILE}
+     */
+    public static final String EXTRA_PROVISIONING_TOKEN
+        = "android.app.extra.token";
+
+    /**
      * A String extra holding the default name of the profile that is created during managed profile
      * provisioning.
      * <p>Use with {@link #ACTION_PROVISION_MANAGED_PROFILE}
@@ -2281,4 +2300,23 @@
         }
     }
 
+    /**
+     * Designates a specific broadcast receiver component as the provider for
+     * making permission requests of a local or remote administrator of the user.
+     * <p/>
+     * Only a profile owner can designate the restrictions provider.
+     * @param admin Which {@link DeviceAdminReceiver} this request is associated with.
+     * @param receiver The component name of a BroadcastReceiver that handles the
+     * {@link RestrictionsManager#ACTION_REQUEST_PERMISSION} intent. If this param is null,
+     * it removes the restrictions provider previously assigned.
+     */
+    public void setRestrictionsProvider(ComponentName admin, ComponentName receiver) {
+        if (mService != null) {
+            try {
+                mService.setRestrictionsProvider(admin, receiver);
+            } catch (RemoteException re) {
+                Log.w(TAG, "Failed to set permission provider on device policy service");
+            }
+        }
+    }
 }
diff --git a/core/java/android/app/admin/IDevicePolicyManager.aidl b/core/java/android/app/admin/IDevicePolicyManager.aidl
index 4639195..7d7a312 100644
--- a/core/java/android/app/admin/IDevicePolicyManager.aidl
+++ b/core/java/android/app/admin/IDevicePolicyManager.aidl
@@ -121,6 +121,9 @@
     void setApplicationRestrictions(in ComponentName who, in String packageName, in Bundle settings);
     Bundle getApplicationRestrictions(in ComponentName who, in String packageName);
 
+    void setRestrictionsProvider(in ComponentName who, in ComponentName provider);
+    ComponentName getRestrictionsProvider(int userHandle);
+
     void setUserRestriction(in ComponentName who, in String key, boolean enable);
     void addCrossProfileIntentFilter(in ComponentName admin, in IntentFilter filter, int flags);
     void clearCrossProfileIntentFilters(in ComponentName admin);
diff --git a/core/java/android/app/backup/BackupTransport.java b/core/java/android/app/backup/BackupTransport.java
new file mode 100644
index 0000000..46f082e
--- /dev/null
+++ b/core/java/android/app/backup/BackupTransport.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 android.app.backup;
+
+import android.content.Intent;
+import android.content.pm.PackageInfo;
+import android.os.IBinder;
+import android.os.ParcelFileDescriptor;
+import android.os.RemoteException;
+
+import com.android.internal.backup.IBackupTransport;
+
+/**
+ * Concrete class that provides a stable-API bridge between IBackupTransport
+ * and its implementations.
+ *
+ * @hide
+ */
+public class BackupTransport {
+    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;
+
+    IBackupTransport mBinderImpl = new TransportImpl();
+    /** @hide */
+    public IBinder getBinder() {
+        return mBinderImpl.asBinder();
+    }
+
+    // ------------------------------------------------------------------------------------
+    // Transport self-description and general configuration interfaces
+    //
+
+    /**
+     * Ask the transport for the name under which it should be registered.  This will
+     * typically be its host service's component name, but need not be.
+     */
+    public String name() {
+        throw new UnsupportedOperationException("Transport name() not implemented");
+    }
+
+    /**
+     * Ask the transport for an Intent that can be used to launch any internal
+     * configuration Activity that it wishes to present.  For example, the transport
+     * may offer a UI for allowing the user to supply login credentials for the
+     * transport's off-device backend.
+     *
+     * If the transport does not supply any user-facing configuration UI, it should
+     * return null from this method.
+     *
+     * @return An Intent that can be passed to Context.startActivity() in order to
+     *         launch the transport's configuration UI.  This method will return null
+     *         if the transport does not offer any user-facing configuration UI.
+     */
+    public Intent configurationIntent() {
+        return null;
+    }
+
+    /**
+     * On demand, supply a one-line string that can be shown to the user that
+     * describes the current backend destination.  For example, a transport that
+     * can potentially associate backup data with arbitrary user accounts should
+     * include the name of the currently-active account here.
+     *
+     * @return A string describing the destination to which the transport is currently
+     *         sending data.  This method should not return null.
+     */
+    public String currentDestinationString() {
+        throw new UnsupportedOperationException(
+                "Transport currentDestinationString() not implemented");
+    }
+
+    /**
+     * Ask the transport where, on local device storage, to keep backup state blobs.
+     * This is per-transport so that mock transports used for testing can coexist with
+     * "live" backup services without interfering with the live bookkeeping.  The
+     * returned string should be a name that is expected to be unambiguous among all
+     * available backup transports; the name of the class implementing the transport
+     * is a good choice.
+     *
+     * @return A unique name, suitable for use as a file or directory name, that the
+     *         Backup Manager could use to disambiguate state files associated with
+     *         different backup transports.
+     */
+    public String transportDirName() {
+        throw new UnsupportedOperationException(
+                "Transport transportDirName() not implemented");
+    }
+
+    // ------------------------------------------------------------------------------------
+    // Device-level operations common to both key/value and full-data storage
+
+    /**
+     * Initialize the server side storage for this device, erasing all stored data.
+     * The transport may send the request immediately, or may buffer it.  After
+     * this is called, {@link #finishBackup} will be called to ensure the request
+     * is sent and received successfully.
+     *
+     * @return One of {@link BackupTransport#TRANSPORT_OK} (OK so far) or
+     *   {@link BackupTransport#TRANSPORT_ERROR} (on network error or other failure).
+     */
+    public int initializeDevice() {
+        return BackupTransport.TRANSPORT_ERROR;
+    }
+
+    /**
+     * Erase the given application's data from the backup destination.  This clears
+     * out the given package's data from the current backup set, making it as though
+     * the app had never yet been backed up.  After this is called, {@link finishBackup}
+     * must be called to ensure that the operation is recorded successfully.
+     *
+     * @return the same error codes as {@link #performBackup}.
+     */
+    public int clearBackupData(PackageInfo packageInfo) {
+        return BackupTransport.TRANSPORT_ERROR;
+    }
+
+    /**
+     * Finish sending application data to the backup destination.  This must be
+     * called after {@link #performBackup}, {@link #performFullBackup}, or {@link clearBackupData}
+     * to ensure that all data is sent and the operation properly finalized.  Only when this
+     * method returns true can a backup be assumed to have succeeded.
+     *
+     * @return the same error codes as {@link #performBackup} or {@link #performFullBackup}.
+     */
+    public int finishBackup() {
+        return BackupTransport.TRANSPORT_ERROR;
+    }
+
+    // ------------------------------------------------------------------------------------
+    // Key/value incremental backup support interfaces
+
+    /**
+     * Verify that this is a suitable time for a key/value backup pass.  This should return zero
+     * if a backup is reasonable right now, some positive value otherwise.  This method
+     * will be called outside of the {@link #performBackup}/{@link #finishBackup} pair.
+     *
+     * <p>If this is not a suitable time for a backup, the transport should return a
+     * backoff delay, in milliseconds, after which the Backup Manager should try again.
+     *
+     * @return Zero if this is a suitable time for a backup pass, or a positive time delay
+     *   in milliseconds to suggest deferring the backup pass for a while.
+     */
+    public long requestBackupTime() {
+        return 0;
+    }
+
+    /**
+     * Send one application's key/value data update to the backup destination.  The
+     * transport may send the data immediately, or may buffer it.  After this is called,
+     * {@link #finishBackup} will be called to ensure the data is sent and recorded successfully.
+     *
+     * @param packageInfo The identity of the application whose data is being backed up.
+     *   This specifically includes the signature list for the package.
+     * @param data The data stream that resulted from invoking the application's
+     *   BackupService.doBackup() method.  This may be a pipe rather than a file on
+     *   persistent media, so it may not be seekable.
+     * @param wipeAllFirst When true, <i>all</i> backed-up data for the current device/account
+     *   must be erased prior to the storage of the data provided here.  The purpose of this
+     *   is to provide a guarantee that no stale data exists in the restore set when the
+     *   device begins providing incremental backups.
+     * @return one of {@link BackupTransport#TRANSPORT_OK} (OK so far),
+     *  {@link BackupTransport#TRANSPORT_ERROR} (on network error or other failure), or
+     *  {@link BackupTransport#TRANSPORT_NOT_INITIALIZED} (if the backend dataset has
+     *  become lost due to inactivity purge or some other reason and needs re-initializing)
+     */
+    public int performBackup(PackageInfo packageInfo, ParcelFileDescriptor inFd) {
+        return BackupTransport.TRANSPORT_ERROR;
+    }
+
+    // ------------------------------------------------------------------------------------
+    // Key/value dataset restore interfaces
+
+    /**
+     * Get the set of all backups currently available over this transport.
+     *
+     * @return Descriptions of the set of restore images available for this device,
+     *   or null if an error occurred (the attempt should be rescheduled).
+     **/
+    public RestoreSet[] getAvailableRestoreSets() {
+        return null;
+    }
+
+    /**
+     * Get the identifying token of the backup set currently being stored from
+     * this device.  This is used in the case of applications wishing to restore
+     * their last-known-good data.
+     *
+     * @return A token that can be passed to {@link #startRestore}, or 0 if there
+     *   is no backup set available corresponding to the current device state.
+     */
+    public long getCurrentRestoreSet() {
+        return 0;
+    }
+
+    /**
+     * Start restoring application data from backup.  After calling this function,
+     * alternate calls to {@link #nextRestorePackage} and {@link #nextRestoreData}
+     * to walk through the actual application data.
+     *
+     * @param token A backup token as returned by {@link #getAvailableRestoreSets}
+     *   or {@link #getCurrentRestoreSet}.
+     * @param packages List of applications to restore (if data is available).
+     *   Application data will be restored in the order given.
+     * @return One of {@link BackupTransport#TRANSPORT_OK} (OK so far, call
+     *   {@link #nextRestorePackage}) or {@link BackupTransport#TRANSPORT_ERROR}
+     *   (an error occurred, the restore should be aborted and rescheduled).
+     */
+    public int startRestore(long token, PackageInfo[] packages) {
+        return BackupTransport.TRANSPORT_ERROR;
+    }
+
+    /**
+     * 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).
+     */
+    public String 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.
+     * @return the same error codes as {@link #startRestore}.
+     */
+    public int getRestoreData(ParcelFileDescriptor outFd) {
+        return BackupTransport.TRANSPORT_ERROR;
+    }
+
+    /**
+     * End a restore session (aborting any in-process data transfer as necessary),
+     * freeing any resources and connections used during the restore process.
+     */
+    public void finishRestore() {
+        throw new UnsupportedOperationException(
+                "Transport finishRestore() not implemented");
+    }
+
+    // ------------------------------------------------------------------------------------
+    // Full backup interfaces
+
+    /**
+     * Verify that this is a suitable time for a full-data backup pass.  This should return zero
+     * if a backup is reasonable right now, some positive value otherwise.  This method
+     * will be called outside of the {@link #performFullBackup}/{@link #finishBackup} pair.
+     *
+     * <p>If this is not a suitable time for a backup, the transport should return a
+     * backoff delay, in milliseconds, after which the Backup Manager should try again.
+     *
+     * @return Zero if this is a suitable time for a backup pass, or a positive time delay
+     *   in milliseconds to suggest deferring the backup pass for a while.
+     *
+     * @see #requestBackupTime()
+     */
+    public long requestFullBackupTime() {
+        return 0;
+    }
+
+    /**
+     * Begin the process of sending an application's full-data archive to the backend.
+     * The description of the package whose data will be delivered is provided, as well as
+     * the socket file descriptor on which the transport will receive the data itself.
+     *
+     * <p>If the package is not eligible for backup, the transport should return
+     * {@link BackupTransport#TRANSPORT_PACKAGE_REJECTED}.  In this case the system will
+     * simply proceed with the next candidate if any, or finish the full backup operation
+     * if all apps have been processed.
+     *
+     * <p>After the transport returns {@link BackupTransport#TRANSPORT_OK} from this
+     * method, the OS will proceed to call {@link #sendBackupData()} one or more times
+     * to deliver the application's data as a streamed tarball.  The transport should not
+     * read() from the socket except as instructed to via the {@link #sendBackupData(int)}
+     * method.
+     *
+     * <p>After all data has been delivered to the transport, the system will call
+     * {@link #finishBackup()}.  At this point the transport should commit the data to
+     * its datastore, if appropriate, and close the socket that had been provided in
+     * {@link #performFullBackup(PackageInfo, ParcelFileDescriptor)}.
+     *
+     * @param targetPackage The package whose data is to follow.
+     * @param socket The socket file descriptor through which the data will be provided.
+     *    If the transport returns {@link #TRANSPORT_PACKAGE_REJECTED} here, it must still
+     *    close this file descriptor now; otherwise it should be cached for use during
+     *    succeeding calls to {@link #sendBackupData(int)}, and closed in response to
+     *    {@link #finishBackup()}.
+     * @return TRANSPORT_PACKAGE_REJECTED to indicate that the stated application is not
+     *    to be backed up; TRANSPORT_OK to indicate that the OS may proceed with delivering
+     *    backup data; TRANSPORT_ERROR to indicate a fatal error condition that precludes
+     *    performing a backup at this time.
+     */
+    public int performFullBackup(PackageInfo targetPackage, ParcelFileDescriptor socket) {
+        return BackupTransport.TRANSPORT_PACKAGE_REJECTED;
+    }
+
+    /**
+     * Tells the transport to read {@code numBytes} bytes of data from the socket file
+     * descriptor provided in the {@link #performFullBackup(PackageInfo, ParcelFileDescriptor)}
+     * call, and deliver those bytes to the datastore.
+     *
+     * @param numBytes The number of bytes of tarball data available to be read from the
+     *    socket.
+     * @return TRANSPORT_OK on successful processing of the data; TRANSPORT_ERROR to
+     *    indicate a fatal error situation.  If an error is returned, the system will
+     *    call finishBackup() and stop attempting backups until after a backoff and retry
+     *    interval.
+     */
+    public int sendBackupData(int numBytes) {
+        return BackupTransport.TRANSPORT_ERROR;
+    }
+
+    /**
+     * 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
+     * (if appropriate) decouple those framework-side changes from the BackupTransport
+     * implementations.
+     */
+    class TransportImpl extends IBackupTransport.Stub {
+
+        @Override
+        public String name() throws RemoteException {
+            return BackupTransport.this.name();
+        }
+
+        @Override
+        public Intent configurationIntent() throws RemoteException {
+            return BackupTransport.this.configurationIntent();
+        }
+
+        @Override
+        public String currentDestinationString() throws RemoteException {
+            return BackupTransport.this.currentDestinationString();
+        }
+
+        @Override
+        public String transportDirName() throws RemoteException {
+            return BackupTransport.this.transportDirName();
+        }
+
+        @Override
+        public long requestBackupTime() throws RemoteException {
+            return BackupTransport.this.requestBackupTime();
+        }
+
+        @Override
+        public int initializeDevice() throws RemoteException {
+            return BackupTransport.this.initializeDevice();
+        }
+
+        @Override
+        public int performBackup(PackageInfo packageInfo, ParcelFileDescriptor inFd)
+                throws RemoteException {
+            return BackupTransport.this.performBackup(packageInfo, inFd);
+        }
+
+        @Override
+        public int clearBackupData(PackageInfo packageInfo) throws RemoteException {
+            return BackupTransport.this.clearBackupData(packageInfo);
+        }
+
+        @Override
+        public int finishBackup() throws RemoteException {
+            return BackupTransport.this.finishBackup();
+        }
+
+        @Override
+        public RestoreSet[] getAvailableRestoreSets() throws RemoteException {
+            return BackupTransport.this.getAvailableRestoreSets();
+        }
+
+        @Override
+        public long getCurrentRestoreSet() throws RemoteException {
+            return BackupTransport.this.getCurrentRestoreSet();
+        }
+
+        @Override
+        public int startRestore(long token, PackageInfo[] packages) throws RemoteException {
+            return BackupTransport.this.startRestore(token, packages);
+        }
+
+        @Override
+        public String nextRestorePackage() throws RemoteException {
+            return BackupTransport.this.nextRestorePackage();
+        }
+
+        @Override
+        public int getRestoreData(ParcelFileDescriptor outFd) throws RemoteException {
+            return BackupTransport.this.getRestoreData(outFd);
+        }
+
+        @Override
+        public void finishRestore() throws RemoteException {
+            BackupTransport.this.finishRestore();
+        }
+    }
+}
diff --git a/core/java/android/app/task/Task.java b/core/java/android/app/task/Task.java
index dd184a5..ca4aeb2 100644
--- a/core/java/android/app/task/Task.java
+++ b/core/java/android/app/task/Task.java
@@ -27,10 +27,13 @@
  * using the {@link Task.Builder}.
  */
 public class Task implements Parcelable {
-
     public interface NetworkType {
-        public final int ANY = 0;
-        public final int UNMETERED = 1;
+        /** Default. */
+        public final int NONE = 0;
+        /** This task requires network connectivity. */
+        public final int ANY = 1;
+        /** This task requires network connectivity that is unmetered. */
+        public final int UNMETERED = 2;
     }
 
     /**
@@ -48,6 +51,8 @@
     private final ComponentName service;
     private final boolean requireCharging;
     private final boolean requireDeviceIdle;
+    private final boolean hasEarlyConstraint;
+    private final boolean hasLateConstraint;
     private final int networkCapabilities;
     private final long minLatencyMillis;
     private final long maxExecutionDelayMillis;
@@ -59,7 +64,7 @@
     /**
      * Unique task id associated with this class. This is assigned to your task by the scheduler.
      */
-    public int getTaskId() {
+    public int getId() {
         return taskId;
     }
 
@@ -146,6 +151,24 @@
         return backoffPolicy;
     }
 
+    /**
+     * User can specify an early constraint of 0L, which is valid, so we keep track of whether the
+     * function was called at all.
+     * @hide
+     */
+    public boolean hasEarlyConstraint() {
+        return hasEarlyConstraint;
+    }
+
+    /**
+     * User can specify a late constraint of 0L, which is valid, so we keep track of whether the
+     * function was called at all.
+     * @hide
+     */
+    public boolean hasLateConstraint() {
+        return hasLateConstraint;
+    }
+
     private Task(Parcel in) {
         taskId = in.readInt();
         extras = in.readBundle();
@@ -159,6 +182,8 @@
         intervalMillis = in.readLong();
         initialBackoffMillis = in.readLong();
         backoffPolicy = in.readInt();
+        hasEarlyConstraint = in.readInt() == 1;
+        hasLateConstraint = in.readInt() == 1;
     }
 
     private Task(Task.Builder b) {
@@ -174,6 +199,8 @@
         intervalMillis = b.mIntervalMillis;
         initialBackoffMillis = b.mInitialBackoffMillis;
         backoffPolicy = b.mBackoffPolicy;
+        hasEarlyConstraint = b.mHasEarlyConstraint;
+        hasLateConstraint = b.mHasLateConstraint;
     }
 
     @Override
@@ -195,6 +222,8 @@
         out.writeLong(intervalMillis);
         out.writeLong(initialBackoffMillis);
         out.writeInt(backoffPolicy);
+        out.writeInt(hasEarlyConstraint ? 1 : 0);
+        out.writeInt(hasLateConstraint ? 1 : 0);
     }
 
     public static final Creator<Task> CREATOR = new Creator<Task>() {
@@ -212,7 +241,7 @@
     /**
      * Builder class for constructing {@link Task} objects.
      */
-    public final class Builder {
+    public static final class Builder {
         private int mTaskId;
         private Bundle mExtras;
         private ComponentName mTaskService;
@@ -225,6 +254,8 @@
         private long mMaxExecutionDelayMillis;
         // Periodic parameters.
         private boolean mIsPeriodic;
+        private boolean mHasEarlyConstraint;
+        private boolean mHasLateConstraint;
         private long mIntervalMillis;
         // Back-off parameters.
         private long mInitialBackoffMillis = 5000L;
@@ -307,6 +338,7 @@
         public Builder setPeriodic(long intervalMillis) {
             mIsPeriodic = true;
             mIntervalMillis = intervalMillis;
+            mHasEarlyConstraint = mHasLateConstraint = true;
             return this;
         }
 
@@ -320,6 +352,7 @@
          */
         public Builder setMinimumLatency(long minLatencyMillis) {
             mMinLatencyMillis = minLatencyMillis;
+            mHasEarlyConstraint = true;
             return this;
         }
 
@@ -332,6 +365,7 @@
          */
         public Builder setOverrideDeadline(long maxExecutionDelayMillis) {
             mMaxExecutionDelayMillis = maxExecutionDelayMillis;
+            mHasLateConstraint = true;
             return this;
         }
 
@@ -360,31 +394,18 @@
          * @return The task object to hand to the TaskManager. This object is immutable.
          */
         public Task build() {
-            // Check that extras bundle only contains primitive types.
-            try {
-                for (String key : extras.keySet()) {
-                    Object value = extras.get(key);
-                    if (value == null) continue;
-                    if (value instanceof Long) continue;
-                    if (value instanceof Integer) continue;
-                    if (value instanceof Boolean) continue;
-                    if (value instanceof Float) continue;
-                    if (value instanceof Double) continue;
-                    if (value instanceof String) continue;
-                    throw new IllegalArgumentException("Unexpected value type: "
-                            + value.getClass().getName());
-                }
-            } catch (IllegalArgumentException e) {
-                throw e;
-            } catch (RuntimeException exc) {
-                throw new IllegalArgumentException("error unparcelling Bundle", exc);
+            if (mExtras == null) {
+                mExtras = Bundle.EMPTY;
+            }
+            if (mTaskId < 0) {
+                throw new IllegalArgumentException("Task id must be greater than 0.");
             }
             // Check that a deadline was not set on a periodic task.
-            if (mIsPeriodic && (mMaxExecutionDelayMillis != 0L)) {
+            if (mIsPeriodic && mHasLateConstraint) {
                 throw new IllegalArgumentException("Can't call setOverrideDeadline() on a " +
                         "periodic task.");
             }
-            if (mIsPeriodic && (mMinLatencyMillis != 0L)) {
+            if (mIsPeriodic && mHasEarlyConstraint) {
                 throw new IllegalArgumentException("Can't call setMinimumLatency() on a " +
                         "periodic task");
             }
diff --git a/core/java/android/app/task/TaskManager.java b/core/java/android/app/task/TaskManager.java
index 0fbe37d..00f57da 100644
--- a/core/java/android/app/task/TaskManager.java
+++ b/core/java/android/app/task/TaskManager.java
@@ -34,14 +34,13 @@
      * if the run-time for your task is too short, or perhaps the system can't resolve the
      * requisite {@link TaskService} in your package.
      */
-    public static final int RESULT_INVALID_PARAMETERS = -1;
-
+    public static final int RESULT_FAILURE = 0;
     /**
      * Returned from {@link #schedule(Task)} if this application has made too many requests for
      * work over too short a time.
      */
     // TODO: Determine if this is necessary.
-    public static final int RESULT_OVER_QUOTA = -2;
+    public static final int RESULT_SUCCESS = 1;
 
     /**
      * @param task The task you wish scheduled. See
diff --git a/core/java/android/appwidget/AppWidgetManager.java b/core/java/android/appwidget/AppWidgetManager.java
index d3e9089..e5bf7d0 100644
--- a/core/java/android/appwidget/AppWidgetManager.java
+++ b/core/java/android/appwidget/AppWidgetManager.java
@@ -317,9 +317,9 @@
     public static final String ACTION_APPWIDGET_ENABLED = "android.appwidget.action.APPWIDGET_ENABLED";
 
     /**
-     * Sent to providers after AppWidget state related to the provider has been restored from
-     * backup. The intent contains information about how to translate AppWidget ids from the
-     * restored data to their new equivalents.
+     * Sent to an {@link AppWidgetProvider} after AppWidget state related to that provider has
+     * been restored from backup. The intent contains information about how to translate AppWidget
+     * ids from the restored data to their new equivalents.
      *
      * <p>The intent will contain the following extras:
      *
@@ -343,7 +343,7 @@
      * <p class="note">This is a protected intent that can only be sent
      * by the system.
      *
-     * @see {@link #ACTION_APPWIDGET_HOST_RESTORED} for the corresponding host broadcast
+     * @see #ACTION_APPWIDGET_HOST_RESTORED
      */
     public static final String ACTION_APPWIDGET_RESTORED
             = "android.appwidget.action.APPWIDGET_RESTORED";
@@ -352,7 +352,7 @@
      * Sent to widget hosts after AppWidget state related to the host has been restored from
      * backup. The intent contains information about how to translate AppWidget ids from the
      * restored data to their new equivalents.  If an application maintains multiple separate
-     * widget hosts instances, it will receive this broadcast separately for each one.
+     * widget host instances, it will receive this broadcast separately for each one.
      *
      * <p>The intent will contain the following extras:
      *
@@ -380,7 +380,7 @@
      * <p class="note">This is a protected intent that can only be sent
      * by the system.
      *
-     * @see {@link #ACTION_APPWIDGET_RESTORED} for the corresponding provider broadcast
+     * @see #ACTION_APPWIDGET_RESTORED
      */
     public static final String ACTION_APPWIDGET_HOST_RESTORED
             = "android.appwidget.action.APPWIDGET_HOST_RESTORED";
diff --git a/core/java/android/bluetooth/BluetoothAdapter.java b/core/java/android/bluetooth/BluetoothAdapter.java
index 9e1c995..42c2aeb 100644
--- a/core/java/android/bluetooth/BluetoothAdapter.java
+++ b/core/java/android/bluetooth/BluetoothAdapter.java
@@ -18,6 +18,8 @@
 
 import android.annotation.SdkConstant;
 import android.annotation.SdkConstant.SdkConstantType;
+import android.bluetooth.le.BluetoothLeAdvertiser;
+import android.bluetooth.le.BluetoothLeScanner;
 import android.content.Context;
 import android.os.Handler;
 import android.os.IBinder;
diff --git a/core/java/android/bluetooth/BluetoothLeAdvertiseScanData.aidl b/core/java/android/bluetooth/BluetoothLeAdvertiseScanData.aidl
deleted file mode 100644
index 4aa8881..0000000
--- a/core/java/android/bluetooth/BluetoothLeAdvertiseScanData.aidl
+++ /dev/null
@@ -1,19 +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.bluetooth;
-
-parcelable BluetoothLeAdvertiseScanData.AdvertisementData;
\ No newline at end of file
diff --git a/core/java/android/bluetooth/BluetoothLeAdvertiseScanData.java b/core/java/android/bluetooth/BluetoothLeAdvertiseScanData.java
deleted file mode 100644
index 2fa5e49..0000000
--- a/core/java/android/bluetooth/BluetoothLeAdvertiseScanData.java
+++ /dev/null
@@ -1,645 +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.bluetooth;
-
-import android.annotation.Nullable;
-import android.bluetooth.BluetoothLeAdvertiseScanData.AdvertisementData;
-import android.os.Parcel;
-import android.os.ParcelUuid;
-import android.os.Parcelable;
-import android.util.Log;
-
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.List;
-
-/**
- * Represents Bluetooth LE advertise and scan response data. This could be either the advertisement
- * data to be advertised, or the scan record obtained from BLE scans.
- * <p>
- * The exact bluetooth advertising and scan response data fields and types are defined in Bluetooth
- * 4.0 specification, Volume 3, Part C, Section 11 and 18, as well as Supplement to the Bluetooth
- * Core Specification Version 4. Currently the following fields are allowed to be set:
- * <li>Service UUIDs which identify the bluetooth gatt services running on the device.
- * <li>Tx power level which is the transmission power level.
- * <li>Service data which is the data associated with a service.
- * <li>Manufacturer specific data which is the data associated with a particular manufacturer.
- *
- * @see BluetoothLeAdvertiser
- */
-public final class BluetoothLeAdvertiseScanData {
-    private static final String TAG = "BluetoothLeAdvertiseScanData";
-
-    /**
-     * Bluetooth LE Advertising Data type, the data will be placed in AdvData field of advertising
-     * packet.
-     */
-    public static final int ADVERTISING_DATA = 0;
-    /**
-     * Bluetooth LE scan response data, the data will be placed in ScanRspData field of advertising
-     * packet.
-     * <p>
-     */
-    public static final int SCAN_RESPONSE_DATA = 1;
-    /**
-     * Scan record parsed from Bluetooth LE scans. The content can contain a concatenation of
-     * advertising data and scan response data.
-     */
-    public static final int PARSED_SCAN_RECORD = 2;
-
-    /**
-     * Base data type which contains the common fields for {@link AdvertisementData} and
-     * {@link ScanRecord}.
-     */
-    public abstract static class AdvertiseBaseData {
-
-        private final int mDataType;
-
-        @Nullable
-        private final List<ParcelUuid> mServiceUuids;
-
-        private final int mManufacturerId;
-        @Nullable
-        private final byte[] mManufacturerSpecificData;
-
-        @Nullable
-        private final ParcelUuid mServiceDataUuid;
-        @Nullable
-        private final byte[] mServiceData;
-
-        private AdvertiseBaseData(int dataType,
-                List<ParcelUuid> serviceUuids,
-                ParcelUuid serviceDataUuid, byte[] serviceData,
-                int manufacturerId,
-                byte[] manufacturerSpecificData) {
-            mDataType = dataType;
-            mServiceUuids = serviceUuids;
-            mManufacturerId = manufacturerId;
-            mManufacturerSpecificData = manufacturerSpecificData;
-            mServiceDataUuid = serviceDataUuid;
-            mServiceData = serviceData;
-        }
-
-        /**
-         * Returns the type of data, indicating whether the data is advertising data, scan response
-         * data or scan record.
-         */
-        public int getDataType() {
-            return mDataType;
-        }
-
-        /**
-         * Returns a list of service uuids within the advertisement that are used to identify the
-         * bluetooth gatt services.
-         */
-        public List<ParcelUuid> getServiceUuids() {
-            return mServiceUuids;
-        }
-
-        /**
-         * Returns the manufacturer identifier, which is a non-negative number assigned by Bluetooth
-         * SIG.
-         */
-        public int getManufacturerId() {
-            return mManufacturerId;
-        }
-
-        /**
-         * Returns the manufacturer specific data which is the content of manufacturer specific data
-         * field. The first 2 bytes of the data contain the company id.
-         */
-        public byte[] getManufacturerSpecificData() {
-            return mManufacturerSpecificData;
-        }
-
-        /**
-         * Returns a 16 bit uuid of the service that the service data is associated with.
-         */
-        public ParcelUuid getServiceDataUuid() {
-            return mServiceDataUuid;
-        }
-
-        /**
-         * Returns service data. The first two bytes should be a 16 bit service uuid associated with
-         * the service data.
-         */
-        public byte[] getServiceData() {
-            return mServiceData;
-        }
-
-        @Override
-        public String toString() {
-            return "AdvertiseBaseData [mDataType=" + mDataType + ", mServiceUuids=" + mServiceUuids
-                    + ", mManufacturerId=" + mManufacturerId + ", mManufacturerSpecificData="
-                    + Arrays.toString(mManufacturerSpecificData) + ", mServiceDataUuid="
-                    + mServiceDataUuid + ", mServiceData=" + Arrays.toString(mServiceData) + "]";
-        }
-    }
-
-    /**
-     * Advertisement data packet for Bluetooth LE advertising. This represents the data to be
-     * broadcasted in Bluetooth LE advertising.
-     * <p>
-     * Use {@link AdvertisementData.Builder} to create an instance of {@link AdvertisementData} to
-     * be advertised.
-     *
-     * @see BluetoothLeAdvertiser
-     */
-    public static final class AdvertisementData extends AdvertiseBaseData implements Parcelable {
-
-        private boolean mIncludeTxPowerLevel;
-
-        /**
-         * Whether the transmission power level will be included in the advertisement packet.
-         */
-        public boolean getIncludeTxPowerLevel() {
-            return mIncludeTxPowerLevel;
-        }
-
-        /**
-         * Returns a {@link Builder} to build {@link AdvertisementData}.
-         */
-        public static Builder newBuilder() {
-            return new Builder();
-        }
-
-        @Override
-        public int describeContents() {
-            return 0;
-        }
-
-        @Override
-        public void writeToParcel(Parcel dest, int flags) {
-            dest.writeInt(getDataType());
-            List<ParcelUuid> uuids = getServiceUuids();
-            if (uuids == null) {
-                dest.writeInt(0);
-            } else {
-                dest.writeInt(uuids.size());
-                dest.writeList(uuids);
-            }
-
-            dest.writeInt(getManufacturerId());
-            byte[] manufacturerData = getManufacturerSpecificData();
-            if (manufacturerData == null) {
-                dest.writeInt(0);
-            } else {
-                dest.writeInt(manufacturerData.length);
-                dest.writeByteArray(manufacturerData);
-            }
-
-            ParcelUuid serviceDataUuid = getServiceDataUuid();
-            if (serviceDataUuid == null) {
-                dest.writeInt(0);
-            } else {
-                dest.writeInt(1);
-                dest.writeParcelable(serviceDataUuid, flags);
-                byte[] serviceData = getServiceData();
-                if (serviceData == null) {
-                    dest.writeInt(0);
-                } else {
-                    dest.writeInt(serviceData.length);
-                    dest.writeByteArray(serviceData);
-                }
-            }
-            dest.writeByte((byte) (getIncludeTxPowerLevel() ? 1 : 0));
-        }
-
-        private AdvertisementData(int dataType,
-                List<ParcelUuid> serviceUuids,
-                ParcelUuid serviceDataUuid, byte[] serviceData,
-                int manufacturerId,
-                byte[] manufacturerSpecificData, boolean mIncludeTxPowerLevel) {
-            super(dataType, serviceUuids, serviceDataUuid, serviceData, manufacturerId,
-                    manufacturerSpecificData);
-            this.mIncludeTxPowerLevel = mIncludeTxPowerLevel;
-        }
-
-        public static final Parcelable.Creator<AdvertisementData> CREATOR =
-                new Creator<AdvertisementData>() {
-                @Override
-                    public AdvertisementData[] newArray(int size) {
-                        return new AdvertisementData[size];
-                    }
-
-                @Override
-                    public AdvertisementData createFromParcel(Parcel in) {
-                        Builder builder = newBuilder();
-                        int dataType = in.readInt();
-                        builder.dataType(dataType);
-                        if (in.readInt() > 0) {
-                            List<ParcelUuid> uuids = new ArrayList<ParcelUuid>();
-                            in.readList(uuids, ParcelUuid.class.getClassLoader());
-                            builder.serviceUuids(uuids);
-                        }
-                        int manufacturerId = in.readInt();
-                        int manufacturerDataLength = in.readInt();
-                        if (manufacturerDataLength > 0) {
-                            byte[] manufacturerData = new byte[manufacturerDataLength];
-                            in.readByteArray(manufacturerData);
-                            builder.manufacturerData(manufacturerId, manufacturerData);
-                        }
-                        if (in.readInt() == 1) {
-                            ParcelUuid serviceDataUuid = in.readParcelable(
-                                    ParcelUuid.class.getClassLoader());
-                            int serviceDataLength = in.readInt();
-                            if (serviceDataLength > 0) {
-                                byte[] serviceData = new byte[serviceDataLength];
-                                in.readByteArray(serviceData);
-                                builder.serviceData(serviceDataUuid, serviceData);
-                            }
-                        }
-                        builder.includeTxPowerLevel(in.readByte() == 1);
-                        return builder.build();
-                    }
-                };
-
-        /**
-         * Builder for {@link BluetoothLeAdvertiseScanData.AdvertisementData}. Use
-         * {@link AdvertisementData#newBuilder()} to get an instance of the Builder.
-         */
-        public static final class Builder {
-            private static final int MAX_ADVERTISING_DATA_BYTES = 31;
-            // Each fields need one byte for field length and another byte for field type.
-            private static final int OVERHEAD_BYTES_PER_FIELD = 2;
-            // Flags field will be set by system.
-            private static final int FLAGS_FIELD_BYTES = 3;
-
-            private int mDataType;
-            @Nullable
-            private List<ParcelUuid> mServiceUuids;
-            private boolean mIncludeTxPowerLevel;
-            private int mManufacturerId;
-            @Nullable
-            private byte[] mManufacturerSpecificData;
-            @Nullable
-            private ParcelUuid mServiceDataUuid;
-            @Nullable
-            private byte[] mServiceData;
-
-            /**
-             * Set data type.
-             *
-             * @param dataType Data type, could only be
-             *            {@link BluetoothLeAdvertiseScanData#ADVERTISING_DATA}
-             * @throws IllegalArgumentException If the {@code dataType} is invalid.
-             */
-            public Builder dataType(int dataType) {
-                if (mDataType != ADVERTISING_DATA && mDataType != SCAN_RESPONSE_DATA) {
-                    throw new IllegalArgumentException("invalid data type - " + dataType);
-                }
-                mDataType = dataType;
-                return this;
-            }
-
-            /**
-             * Set the service uuids. Note the corresponding bluetooth Gatt services need to be
-             * already added on the device before start BLE advertising.
-             *
-             * @param serviceUuids Service uuids to be advertised, could be 16-bit, 32-bit or
-             *            128-bit uuids.
-             * @throws IllegalArgumentException If the {@code serviceUuids} are null.
-             */
-            public Builder serviceUuids(List<ParcelUuid> serviceUuids) {
-                if (serviceUuids == null) {
-                    throw new IllegalArgumentException("serivceUuids are null");
-                }
-                mServiceUuids = serviceUuids;
-                return this;
-            }
-
-            /**
-             * Add service data to advertisement.
-             *
-             * @param serviceDataUuid A 16 bit uuid of the service data
-             * @param serviceData Service data - the first two bytes of the service data are the
-             *            service data uuid.
-             * @throws IllegalArgumentException If the {@code serviceDataUuid} or
-             *             {@code serviceData} is empty.
-             */
-            public Builder serviceData(ParcelUuid serviceDataUuid, byte[] serviceData) {
-                if (serviceDataUuid == null || serviceData == null) {
-                    throw new IllegalArgumentException(
-                            "serviceDataUuid or serviceDataUuid is null");
-                }
-                mServiceDataUuid = serviceDataUuid;
-                mServiceData = serviceData;
-                return this;
-            }
-
-            /**
-             * Set manufacturer id and data. See <a
-             * href="https://www.bluetooth.org/en-us/specification/assigned-numbers/company-identifiers">assigned
-             * manufacturer identifies</a> for the existing company identifiers.
-             *
-             * @param manufacturerId Manufacturer id assigned by Bluetooth SIG.
-             * @param manufacturerSpecificData Manufacturer specific data - the first two bytes of
-             *            the manufacturer specific data are the manufacturer id.
-             * @throws IllegalArgumentException If the {@code manufacturerId} is negative or
-             *             {@code manufacturerSpecificData} is null.
-             */
-            public Builder manufacturerData(int manufacturerId, byte[] manufacturerSpecificData) {
-                if (manufacturerId < 0) {
-                    throw new IllegalArgumentException(
-                            "invalid manufacturerId - " + manufacturerId);
-                }
-                if (manufacturerSpecificData == null) {
-                    throw new IllegalArgumentException("manufacturerSpecificData is null");
-                }
-                mManufacturerId = manufacturerId;
-                mManufacturerSpecificData = manufacturerSpecificData;
-                return this;
-            }
-
-            /**
-             * Whether the transmission power level should be included in the advertising packet.
-             */
-            public Builder includeTxPowerLevel(boolean includeTxPowerLevel) {
-                mIncludeTxPowerLevel = includeTxPowerLevel;
-                return this;
-            }
-
-            /**
-             * Build the {@link BluetoothLeAdvertiseScanData}.
-             *
-             * @throws IllegalArgumentException If the data size is larger than 31 bytes.
-             */
-            public AdvertisementData build() {
-                if (totalBytes() > MAX_ADVERTISING_DATA_BYTES) {
-                    throw new IllegalArgumentException(
-                            "advertisement data size is larger than 31 bytes");
-                }
-                return new AdvertisementData(mDataType,
-                        mServiceUuids,
-                        mServiceDataUuid,
-                        mServiceData, mManufacturerId, mManufacturerSpecificData,
-                        mIncludeTxPowerLevel);
-            }
-
-            // Compute the size of the advertisement data.
-            private int totalBytes() {
-                int size = FLAGS_FIELD_BYTES; // flags field is always set.
-                if (mServiceUuids != null) {
-                    int num16BitUuids = 0;
-                    int num32BitUuids = 0;
-                    int num128BitUuids = 0;
-                    for (ParcelUuid uuid : mServiceUuids) {
-                        if (BluetoothUuid.is16BitUuid(uuid)) {
-                            ++num16BitUuids;
-                        } else if (BluetoothUuid.is32BitUuid(uuid)) {
-                            ++num32BitUuids;
-                        } else {
-                            ++num128BitUuids;
-                        }
-                    }
-                    // 16 bit service uuids are grouped into one field when doing advertising.
-                    if (num16BitUuids != 0) {
-                        size += OVERHEAD_BYTES_PER_FIELD +
-                                num16BitUuids * BluetoothUuid.UUID_BYTES_16_BIT;
-                    }
-                    // 32 bit service uuids are grouped into one field when doing advertising.
-                    if (num32BitUuids != 0) {
-                        size += OVERHEAD_BYTES_PER_FIELD +
-                                num32BitUuids * BluetoothUuid.UUID_BYTES_32_BIT;
-                    }
-                    // 128 bit service uuids are grouped into one field when doing advertising.
-                    if (num128BitUuids != 0) {
-                        size += OVERHEAD_BYTES_PER_FIELD +
-                                num128BitUuids * BluetoothUuid.UUID_BYTES_128_BIT;
-                    }
-                }
-                if (mServiceData != null) {
-                    size += OVERHEAD_BYTES_PER_FIELD + mServiceData.length;
-                }
-                if (mManufacturerSpecificData != null) {
-                    size += OVERHEAD_BYTES_PER_FIELD + mManufacturerSpecificData.length;
-                }
-                if (mIncludeTxPowerLevel) {
-                    size += OVERHEAD_BYTES_PER_FIELD + 1; // tx power level value is one byte.
-                }
-                return size;
-            }
-        }
-
-    }
-
-    /**
-     * Represents a scan record from Bluetooth LE scan.
-     */
-    public static final class ScanRecord extends AdvertiseBaseData {
-        // Flags of the advertising data.
-        private final int mAdvertiseFlags;
-
-        // Transmission power level(in dB).
-        private final int mTxPowerLevel;
-
-        // Local name of the Bluetooth LE device.
-        private final String mLocalName;
-
-        /**
-         * Returns the advertising flags indicating the discoverable mode and capability of the
-         * device. Returns -1 if the flag field is not set.
-         */
-        public int getAdvertiseFlags() {
-            return mAdvertiseFlags;
-        }
-
-        /**
-         * Returns the transmission power level of the packet in dBm. Returns
-         * {@link Integer#MIN_VALUE} if the field is not set. This value can be used to calculate
-         * the path loss of a received packet using the following equation:
-         * <p>
-         * <code>pathloss = txPowerLevel - rssi</code>
-         */
-        public int getTxPowerLevel() {
-            return mTxPowerLevel;
-        }
-
-        /**
-         * Returns the local name of the BLE device. The is a UTF-8 encoded string.
-         */
-        @Nullable
-        public String getLocalName() {
-            return mLocalName;
-        }
-
-        ScanRecord(int dataType,
-                List<ParcelUuid> serviceUuids,
-                ParcelUuid serviceDataUuid, byte[] serviceData,
-                int manufacturerId,
-                byte[] manufacturerSpecificData, int advertiseFlags, int txPowerLevel,
-                String localName) {
-            super(dataType, serviceUuids, serviceDataUuid, serviceData, manufacturerId,
-                    manufacturerSpecificData);
-            mLocalName = localName;
-            mAdvertiseFlags = advertiseFlags;
-            mTxPowerLevel = txPowerLevel;
-        }
-
-        /**
-         * Get a {@link Parser} to parse the scan record byte array into {@link ScanRecord}.
-         */
-        public static Parser getParser() {
-            return new Parser();
-        }
-
-        /**
-         * A parser class used to parse a Bluetooth LE scan record to
-         * {@link BluetoothLeAdvertiseScanData}. Note not all field types would be parsed.
-         */
-        public static final class Parser {
-            private static final String PARSER_TAG = "BluetoothLeAdvertiseDataParser";
-
-            // The following data type values are assigned by Bluetooth SIG.
-            // For more details refer to Bluetooth 4.0 specification, Volume 3, Part C, Section 18.
-            private static final int DATA_TYPE_FLAGS = 0x01;
-            private static final int DATA_TYPE_SERVICE_UUIDS_16_BIT_PARTIAL = 0x02;
-            private static final int DATA_TYPE_SERVICE_UUIDS_16_BIT_COMPLETE = 0x03;
-            private static final int DATA_TYPE_SERVICE_UUIDS_32_BIT_PARTIAL = 0x04;
-            private static final int DATA_TYPE_SERVICE_UUIDS_32_BIT_COMPLETE = 0x05;
-            private static final int DATA_TYPE_SERVICE_UUIDS_128_BIT_PARTIAL = 0x06;
-            private static final int DATA_TYPE_SERVICE_UUIDS_128_BIT_COMPLETE = 0x07;
-            private static final int DATA_TYPE_LOCAL_NAME_SHORT = 0x08;
-            private static final int DATA_TYPE_LOCAL_NAME_COMPLETE = 0x09;
-            private static final int DATA_TYPE_TX_POWER_LEVEL = 0x0A;
-            private static final int DATA_TYPE_SERVICE_DATA = 0x16;
-            private static final int DATA_TYPE_MANUFACTURER_SPECIFIC_DATA = 0xFF;
-
-            // Helper method to extract bytes from byte array.
-            private static byte[] extractBytes(byte[] scanRecord, int start, int length) {
-                byte[] bytes = new byte[length];
-                System.arraycopy(scanRecord, start, bytes, 0, length);
-                return bytes;
-            }
-
-            /**
-             * Parse scan record to {@link BluetoothLeAdvertiseScanData.ScanRecord}.
-             * <p>
-             * The format is defined in Bluetooth 4.0 specification, Volume 3, Part C, Section 11
-             * and 18.
-             * <p>
-             * All numerical multi-byte entities and values shall use little-endian
-             * <strong>byte</strong> order.
-             *
-             * @param scanRecord The scan record of Bluetooth LE advertisement and/or scan response.
-             */
-            public ScanRecord parseFromScanRecord(byte[] scanRecord) {
-                if (scanRecord == null) {
-                    return null;
-                }
-
-                int currentPos = 0;
-                int advertiseFlag = -1;
-                List<ParcelUuid> serviceUuids = new ArrayList<ParcelUuid>();
-                String localName = null;
-                int txPowerLevel = Integer.MIN_VALUE;
-                ParcelUuid serviceDataUuid = null;
-                byte[] serviceData = null;
-                int manufacturerId = -1;
-                byte[] manufacturerSpecificData = null;
-
-                try {
-                    while (currentPos < scanRecord.length) {
-                        // length is unsigned int.
-                        int length = scanRecord[currentPos++] & 0xFF;
-                        if (length == 0) {
-                            break;
-                        }
-                        // Note the length includes the length of the field type itself.
-                        int dataLength = length - 1;
-                        // fieldType is unsigned int.
-                        int fieldType = scanRecord[currentPos++] & 0xFF;
-                        switch (fieldType) {
-                            case DATA_TYPE_FLAGS:
-                                advertiseFlag = scanRecord[currentPos] & 0xFF;
-                                break;
-                            case DATA_TYPE_SERVICE_UUIDS_16_BIT_PARTIAL:
-                            case DATA_TYPE_SERVICE_UUIDS_16_BIT_COMPLETE:
-                                parseServiceUuid(scanRecord, currentPos,
-                                        dataLength, BluetoothUuid.UUID_BYTES_16_BIT, serviceUuids);
-                                break;
-                            case DATA_TYPE_SERVICE_UUIDS_32_BIT_PARTIAL:
-                            case DATA_TYPE_SERVICE_UUIDS_32_BIT_COMPLETE:
-                                parseServiceUuid(scanRecord, currentPos, dataLength,
-                                        BluetoothUuid.UUID_BYTES_32_BIT, serviceUuids);
-                                break;
-                            case DATA_TYPE_SERVICE_UUIDS_128_BIT_PARTIAL:
-                            case DATA_TYPE_SERVICE_UUIDS_128_BIT_COMPLETE:
-                                parseServiceUuid(scanRecord, currentPos, dataLength,
-                                        BluetoothUuid.UUID_BYTES_128_BIT, serviceUuids);
-                                break;
-                            case DATA_TYPE_LOCAL_NAME_SHORT:
-                            case DATA_TYPE_LOCAL_NAME_COMPLETE:
-                                localName = new String(
-                                        extractBytes(scanRecord, currentPos, dataLength));
-                                break;
-                            case DATA_TYPE_TX_POWER_LEVEL:
-                                txPowerLevel = scanRecord[currentPos];
-                                break;
-                            case DATA_TYPE_SERVICE_DATA:
-                                serviceData = extractBytes(scanRecord, currentPos, dataLength);
-                                // The first two bytes of the service data are service data uuid.
-                                int serviceUuidLength = BluetoothUuid.UUID_BYTES_16_BIT;
-                                byte[] serviceDataUuidBytes = extractBytes(scanRecord, currentPos,
-                                        serviceUuidLength);
-                                serviceDataUuid = BluetoothUuid.parseUuidFrom(serviceDataUuidBytes);
-                                break;
-                            case DATA_TYPE_MANUFACTURER_SPECIFIC_DATA:
-                                manufacturerSpecificData = extractBytes(scanRecord, currentPos,
-                                        dataLength);
-                                // The first two bytes of the manufacturer specific data are
-                                // manufacturer ids in little endian.
-                                manufacturerId = ((manufacturerSpecificData[1] & 0xFF) << 8) +
-                                        (manufacturerSpecificData[0] & 0xFF);
-                                break;
-                            default:
-                                // Just ignore, we don't handle such data type.
-                                break;
-                        }
-                        currentPos += dataLength;
-                    }
-
-                    if (serviceUuids.isEmpty()) {
-                        serviceUuids = null;
-                    }
-                    return new ScanRecord(PARSED_SCAN_RECORD,
-                            serviceUuids, serviceDataUuid, serviceData,
-                            manufacturerId, manufacturerSpecificData, advertiseFlag, txPowerLevel,
-                            localName);
-                } catch (IndexOutOfBoundsException e) {
-                    Log.e(PARSER_TAG,
-                            "unable to parse scan record: " + Arrays.toString(scanRecord));
-                    return null;
-                }
-            }
-
-            // Parse service uuids.
-            private int parseServiceUuid(byte[] scanRecord, int currentPos, int dataLength,
-                    int uuidLength, List<ParcelUuid> serviceUuids) {
-                while (dataLength > 0) {
-                    byte[] uuidBytes = extractBytes(scanRecord, currentPos,
-                            uuidLength);
-                    serviceUuids.add(BluetoothUuid.parseUuidFrom(uuidBytes));
-                    dataLength -= uuidLength;
-                    currentPos += uuidLength;
-                }
-                return currentPos;
-            }
-        }
-    }
-
-}
diff --git a/core/java/android/bluetooth/BluetoothLeAdvertiser.aidl b/core/java/android/bluetooth/BluetoothLeAdvertiser.aidl
deleted file mode 100644
index 3108610..0000000
--- a/core/java/android/bluetooth/BluetoothLeAdvertiser.aidl
+++ /dev/null
@@ -1,19 +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.bluetooth;
-
-parcelable BluetoothLeAdvertiser.Settings;
\ No newline at end of file
diff --git a/core/java/android/bluetooth/BluetoothLeAdvertiser.java b/core/java/android/bluetooth/BluetoothLeAdvertiser.java
deleted file mode 100644
index 30c90c4..0000000
--- a/core/java/android/bluetooth/BluetoothLeAdvertiser.java
+++ /dev/null
@@ -1,615 +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.bluetooth;
-
-import android.bluetooth.BluetoothLeAdvertiseScanData.AdvertisementData;
-import android.os.Handler;
-import android.os.Looper;
-import android.os.Parcel;
-import android.os.ParcelUuid;
-import android.os.Parcelable;
-import android.os.RemoteException;
-import android.util.Log;
-
-import java.util.HashMap;
-import java.util.Map;
-import java.util.UUID;
-
-/**
- * This class provides a way to perform Bluetooth LE advertise operations, such as start and stop
- * advertising. An advertiser can broadcast up to 31 bytes of advertisement data represented by
- * {@link BluetoothLeAdvertiseScanData.AdvertisementData}.
- * <p>
- * To get an instance of {@link BluetoothLeAdvertiser}, call the
- * {@link BluetoothAdapter#getBluetoothLeAdvertiser()} method.
- * <p>
- * Note most of the methods here require {@link android.Manifest.permission#BLUETOOTH_ADMIN}
- * permission.
- *
- * @see BluetoothLeAdvertiseScanData.AdvertisementData
- */
-public class BluetoothLeAdvertiser {
-
-    private static final String TAG = "BluetoothLeAdvertiser";
-
-    /**
-     * The {@link Settings} provide a way to adjust advertising preferences for each individual
-     * advertisement. Use {@link Settings.Builder} to create a {@link Settings} instance.
-     */
-    public static final class Settings implements Parcelable {
-        /**
-         * Perform Bluetooth LE advertising in low power mode. This is the default and preferred
-         * advertising mode as it consumes the least power.
-         */
-        public static final int ADVERTISE_MODE_LOW_POWER = 0;
-        /**
-         * Perform Bluetooth LE advertising in balanced power mode. This is balanced between
-         * advertising frequency and power consumption.
-         */
-        public static final int ADVERTISE_MODE_BALANCED = 1;
-        /**
-         * Perform Bluetooth LE advertising in low latency, high power mode. This has the highest
-         * power consumption and should not be used for background continuous advertising.
-         */
-        public static final int ADVERTISE_MODE_LOW_LATENCY = 2;
-
-        /**
-         * Advertise using the lowest transmission(tx) power level. An app can use low transmission
-         * power to restrict the visibility range of its advertising packet.
-         */
-        public static final int ADVERTISE_TX_POWER_ULTRA_LOW = 0;
-        /**
-         * Advertise using low tx power level.
-         */
-        public static final int ADVERTISE_TX_POWER_LOW = 1;
-        /**
-         * Advertise using medium tx power level.
-         */
-        public static final int ADVERTISE_TX_POWER_MEDIUM = 2;
-        /**
-         * Advertise using high tx power level. This is corresponding to largest visibility range of
-         * the advertising packet.
-         */
-        public static final int ADVERTISE_TX_POWER_HIGH = 3;
-
-        /**
-         * Non-connectable undirected advertising event, as defined in Bluetooth Specification V4.0
-         * vol6, part B, section 4.4.2 - Advertising state.
-         */
-        public static final int ADVERTISE_TYPE_NON_CONNECTABLE = 0;
-        /**
-         * Scannable undirected advertise type, as defined in same spec mentioned above. This event
-         * type allows a scanner to send a scan request asking additional information about the
-         * advertiser.
-         */
-        public static final int ADVERTISE_TYPE_SCANNABLE = 1;
-        /**
-         * Connectable undirected advertising type, as defined in same spec mentioned above. This
-         * event type allows a scanner to send scan request asking additional information about the
-         * advertiser. It also allows an initiator to send a connect request for connection.
-         */
-        public static final int ADVERTISE_TYPE_CONNECTABLE = 2;
-
-        private final int mAdvertiseMode;
-        private final int mAdvertiseTxPowerLevel;
-        private final int mAdvertiseEventType;
-
-        private Settings(int advertiseMode, int advertiseTxPowerLevel,
-                int advertiseEventType) {
-            mAdvertiseMode = advertiseMode;
-            mAdvertiseTxPowerLevel = advertiseTxPowerLevel;
-            mAdvertiseEventType = advertiseEventType;
-        }
-
-        private Settings(Parcel in) {
-            mAdvertiseMode = in.readInt();
-            mAdvertiseTxPowerLevel = in.readInt();
-            mAdvertiseEventType = in.readInt();
-        }
-
-        /**
-         * Creates a {@link Builder} to construct a {@link Settings} object.
-         */
-        public static Builder newBuilder() {
-            return new Builder();
-        }
-
-        /**
-         * Returns the advertise mode.
-         */
-        public int getMode() {
-            return mAdvertiseMode;
-        }
-
-        /**
-         * Returns the tx power level for advertising.
-         */
-        public int getTxPowerLevel() {
-            return mAdvertiseTxPowerLevel;
-        }
-
-        /**
-         * Returns the advertise event type.
-         */
-        public int getType() {
-            return mAdvertiseEventType;
-        }
-
-        @Override
-        public String toString() {
-            return "Settings [mAdvertiseMode=" + mAdvertiseMode + ", mAdvertiseTxPowerLevel="
-                    + mAdvertiseTxPowerLevel + ", mAdvertiseEventType=" + mAdvertiseEventType + "]";
-        }
-
-        @Override
-        public int describeContents() {
-            return 0;
-        }
-
-        @Override
-        public void writeToParcel(Parcel dest, int flags) {
-            dest.writeInt(mAdvertiseMode);
-            dest.writeInt(mAdvertiseTxPowerLevel);
-            dest.writeInt(mAdvertiseEventType);
-        }
-
-        public static final Parcelable.Creator<Settings> CREATOR =
-                new Creator<BluetoothLeAdvertiser.Settings>() {
-                @Override
-                    public Settings[] newArray(int size) {
-                        return new Settings[size];
-                    }
-
-                @Override
-                    public Settings createFromParcel(Parcel in) {
-                        return new Settings(in);
-                    }
-                };
-
-        /**
-         * Builder class for {@link BluetoothLeAdvertiser.Settings}. Caller should use
-         * {@link Settings#newBuilder()} to get an instance of the builder.
-         */
-        public static final class Builder {
-            private int mMode = ADVERTISE_MODE_LOW_POWER;
-            private int mTxPowerLevel = ADVERTISE_TX_POWER_MEDIUM;
-            private int mType = ADVERTISE_TYPE_NON_CONNECTABLE;
-
-            // Private constructor, use Settings.newBuilder() get an instance of BUILDER.
-            private Builder() {
-            }
-
-            /**
-             * Set advertise mode to control the advertising power and latency.
-             *
-             * @param advertiseMode Bluetooth LE Advertising mode, can only be one of
-             *            {@link Settings#ADVERTISE_MODE_LOW_POWER},
-             *            {@link Settings#ADVERTISE_MODE_BALANCED}, or
-             *            {@link Settings#ADVERTISE_MODE_LOW_LATENCY}.
-             * @throws IllegalArgumentException If the advertiseMode is invalid.
-             */
-            public Builder advertiseMode(int advertiseMode) {
-                if (advertiseMode < ADVERTISE_MODE_LOW_POWER
-                        || advertiseMode > ADVERTISE_MODE_LOW_LATENCY) {
-                    throw new IllegalArgumentException("unknown mode " + advertiseMode);
-                }
-                mMode = advertiseMode;
-                return this;
-            }
-
-            /**
-             * Set advertise tx power level to control the transmission power level for the
-             * advertising.
-             *
-             * @param txPowerLevel Transmission power of Bluetooth LE Advertising, can only be one
-             *            of {@link Settings#ADVERTISE_TX_POWER_ULTRA_LOW},
-             *            {@link Settings#ADVERTISE_TX_POWER_LOW},
-             *            {@link Settings#ADVERTISE_TX_POWER_MEDIUM} or
-             *            {@link Settings#ADVERTISE_TX_POWER_HIGH}.
-             * @throws IllegalArgumentException If the {@code txPowerLevel} is invalid.
-             */
-            public Builder txPowerLevel(int txPowerLevel) {
-                if (txPowerLevel < ADVERTISE_TX_POWER_ULTRA_LOW
-                        || txPowerLevel > ADVERTISE_TX_POWER_HIGH) {
-                    throw new IllegalArgumentException("unknown tx power level " + txPowerLevel);
-                }
-                mTxPowerLevel = txPowerLevel;
-                return this;
-            }
-
-            /**
-             * Set advertise type to control the event type of advertising.
-             *
-             * @param type Bluetooth LE Advertising type, can be either
-             *            {@link Settings#ADVERTISE_TYPE_NON_CONNECTABLE},
-             *            {@link Settings#ADVERTISE_TYPE_SCANNABLE} or
-             *            {@link Settings#ADVERTISE_TYPE_CONNECTABLE}.
-             * @throws IllegalArgumentException If the {@code type} is invalid.
-             */
-            public Builder type(int type) {
-                if (type < ADVERTISE_TYPE_NON_CONNECTABLE
-                        || type > ADVERTISE_TYPE_CONNECTABLE) {
-                    throw new IllegalArgumentException("unknown advertise type " + type);
-                }
-                mType = type;
-                return this;
-            }
-
-            /**
-             * Build the {@link Settings} object.
-             */
-            public Settings build() {
-                return new Settings(mMode, mTxPowerLevel, mType);
-            }
-        }
-    }
-
-    /**
-     * Callback of Bluetooth LE advertising, which is used to deliver operation status for start and
-     * stop advertising.
-     */
-    public interface AdvertiseCallback {
-
-        /**
-         * The operation is success.
-         *
-         * @hide
-         */
-        public static final int SUCCESS = 0;
-        /**
-         * Fails to start advertising as the advertisement data contains services that are not added
-         * to the local bluetooth Gatt server.
-         */
-        public static final int ADVERTISING_SERVICE_UNKNOWN = 1;
-        /**
-         * Fails to start advertising as system runs out of quota for advertisers.
-         */
-        public static final int TOO_MANY_ADVERTISERS = 2;
-
-        /**
-         * Fails to start advertising as the advertising is already started.
-         */
-        public static final int ADVERTISING_ALREADY_STARTED = 3;
-        /**
-         * Fails to stop advertising as the advertising is not started.
-         */
-        public static final int ADVERISING_NOT_STARTED = 4;
-
-        /**
-         * Operation fails due to bluetooth controller failure.
-         */
-        public static final int CONTROLLER_FAILURE = 5;
-
-        /**
-         * Callback when advertising operation succeeds.
-         *
-         * @param settingsInEffect The actual settings used for advertising, which may be different
-         *            from what the app asks.
-         */
-        public void onSuccess(Settings settingsInEffect);
-
-        /**
-         * Callback when advertising operation fails.
-         *
-         * @param errorCode Error code for failures.
-         */
-        public void onFailure(int errorCode);
-    }
-
-    private final IBluetoothGatt mBluetoothGatt;
-    private final Handler mHandler;
-    private final Map<Settings, AdvertiseCallbackWrapper>
-            mLeAdvertisers = new HashMap<Settings, AdvertiseCallbackWrapper>();
-
-    // Package private constructor, use BluetoothAdapter.getLeAdvertiser() instead.
-    BluetoothLeAdvertiser(IBluetoothGatt bluetoothGatt) {
-        mBluetoothGatt = bluetoothGatt;
-        mHandler = new Handler(Looper.getMainLooper());
-    }
-
-    /**
-     * Bluetooth GATT interface callbacks for advertising.
-     */
-    private static class AdvertiseCallbackWrapper extends IBluetoothGattCallback.Stub {
-        private static final int LE_CALLBACK_TIMEOUT_MILLIS = 2000;
-        private final AdvertiseCallback mAdvertiseCallback;
-        private final AdvertisementData mAdvertisement;
-        private final AdvertisementData mScanResponse;
-        private final Settings mSettings;
-        private final IBluetoothGatt mBluetoothGatt;
-
-        // mLeHandle 0: not registered
-        // -1: scan stopped
-        // >0: registered and scan started
-        private int mLeHandle;
-        private boolean isAdvertising = false;
-
-        public AdvertiseCallbackWrapper(AdvertiseCallback advertiseCallback,
-                AdvertisementData advertiseData, AdvertisementData scanResponse, Settings settings,
-                IBluetoothGatt bluetoothGatt) {
-            mAdvertiseCallback = advertiseCallback;
-            mAdvertisement = advertiseData;
-            mScanResponse = scanResponse;
-            mSettings = settings;
-            mBluetoothGatt = bluetoothGatt;
-            mLeHandle = 0;
-        }
-
-        public boolean advertiseStarted() {
-            boolean started = false;
-            synchronized (this) {
-                if (mLeHandle == -1) {
-                    return false;
-                }
-                try {
-                    wait(LE_CALLBACK_TIMEOUT_MILLIS);
-                } catch (InterruptedException e) {
-                    Log.e(TAG, "Callback reg wait interrupted: " + e);
-                }
-                started = (mLeHandle > 0 && isAdvertising);
-            }
-            return started;
-        }
-
-        public boolean advertiseStopped() {
-            synchronized (this) {
-                try {
-                    wait(LE_CALLBACK_TIMEOUT_MILLIS);
-                } catch (InterruptedException e) {
-                    Log.e(TAG, "Callback reg wait interrupted: " + e);
-                }
-                return !isAdvertising;
-            }
-        }
-
-        /**
-         * Application interface registered - app is ready to go
-         */
-        @Override
-        public void onClientRegistered(int status, int clientIf) {
-            Log.d(TAG, "onClientRegistered() - status=" + status + " clientIf=" + clientIf);
-            synchronized (this) {
-                if (status == BluetoothGatt.GATT_SUCCESS) {
-                    mLeHandle = clientIf;
-                    try {
-                        mBluetoothGatt.startMultiAdvertising(mLeHandle, mAdvertisement,
-                                mScanResponse, mSettings);
-                    } catch (RemoteException e) {
-                        Log.e(TAG, "fail to start le advertise: " + e);
-                        mLeHandle = -1;
-                        notifyAll();
-                    } catch (Exception e) {
-                        Log.e(TAG, "fail to start advertise: " + e.getStackTrace());
-                    }
-                } else {
-                    // registration failed
-                    mLeHandle = -1;
-                    notifyAll();
-                }
-            }
-        }
-
-        @Override
-        public void onClientConnectionState(int status, int clientIf,
-                boolean connected, String address) {
-            // no op
-        }
-
-        @Override
-        public void onScanResult(String address, int rssi, byte[] advData) {
-            // no op
-        }
-
-        @Override
-        public void onGetService(String address, int srvcType,
-                int srvcInstId, ParcelUuid srvcUuid) {
-            // no op
-        }
-
-        @Override
-        public void onGetIncludedService(String address, int srvcType,
-                int srvcInstId, ParcelUuid srvcUuid,
-                int inclSrvcType, int inclSrvcInstId,
-                ParcelUuid inclSrvcUuid) {
-            // no op
-        }
-
-        @Override
-        public void onGetCharacteristic(String address, int srvcType,
-                int srvcInstId, ParcelUuid srvcUuid,
-                int charInstId, ParcelUuid charUuid,
-                int charProps) {
-            // no op
-        }
-
-        @Override
-        public void onGetDescriptor(String address, int srvcType,
-                int srvcInstId, ParcelUuid srvcUuid,
-                int charInstId, ParcelUuid charUuid,
-                int descInstId, ParcelUuid descUuid) {
-            // no op
-        }
-
-        @Override
-        public void onSearchComplete(String address, int status) {
-            // no op
-        }
-
-        @Override
-        public void onCharacteristicRead(String address, int status, int srvcType,
-                int srvcInstId, ParcelUuid srvcUuid,
-                int charInstId, ParcelUuid charUuid, byte[] value) {
-            // no op
-        }
-
-        @Override
-        public void onCharacteristicWrite(String address, int status, int srvcType,
-                int srvcInstId, ParcelUuid srvcUuid,
-                int charInstId, ParcelUuid charUuid) {
-            // no op
-        }
-
-        @Override
-        public void onNotify(String address, int srvcType,
-                int srvcInstId, ParcelUuid srvcUuid,
-                int charInstId, ParcelUuid charUuid,
-                byte[] value) {
-            // no op
-        }
-
-        @Override
-        public void onDescriptorRead(String address, int status, int srvcType,
-                int srvcInstId, ParcelUuid srvcUuid,
-                int charInstId, ParcelUuid charUuid,
-                int descInstId, ParcelUuid descrUuid, byte[] value) {
-            // no op
-        }
-
-        @Override
-        public void onDescriptorWrite(String address, int status, int srvcType,
-                int srvcInstId, ParcelUuid srvcUuid,
-                int charInstId, ParcelUuid charUuid,
-                int descInstId, ParcelUuid descrUuid) {
-            // no op
-        }
-
-        @Override
-        public void onExecuteWrite(String address, int status) {
-            // no op
-        }
-
-        @Override
-        public void onReadRemoteRssi(String address, int rssi, int status) {
-            // no op
-        }
-
-        @Override
-        public void onAdvertiseStateChange(int advertiseState, int status) {
-            // no op
-        }
-
-        @Override
-        public void onMultiAdvertiseCallback(int status) {
-            synchronized (this) {
-                if (status == 0) {
-                    isAdvertising = !isAdvertising;
-                    if (!isAdvertising) {
-                        try {
-                            mBluetoothGatt.unregisterClient(mLeHandle);
-                            mLeHandle = -1;
-                        } catch (RemoteException e) {
-                            Log.e(TAG, "remote exception when unregistering", e);
-                        }
-                    }
-                    mAdvertiseCallback.onSuccess(null);
-                } else {
-                    mAdvertiseCallback.onFailure(status);
-                }
-                notifyAll();
-            }
-
-        }
-
-        /**
-         * Callback reporting LE ATT MTU.
-         *
-         * @hide
-         */
-        public void onConfigureMTU(String address, int mtu, int status) {
-            // no op
-        }
-    }
-
-    /**
-     * Start Bluetooth LE Advertising.
-     *
-     * @param settings {@link Settings} for Bluetooth LE advertising.
-     * @param advertiseData {@link AdvertisementData} to be advertised.
-     * @param callback {@link AdvertiseCallback} for advertising status.
-     */
-    public void startAdvertising(Settings settings,
-            AdvertisementData advertiseData, final AdvertiseCallback callback) {
-        startAdvertising(settings, advertiseData, null, callback);
-    }
-
-    /**
-     * Start Bluetooth LE Advertising.
-     * @param settings {@link Settings} for Bluetooth LE advertising.
-     * @param advertiseData {@link AdvertisementData} to be advertised in advertisement packet.
-     * @param scanResponse {@link AdvertisementData} for scan response.
-     * @param callback {@link AdvertiseCallback} for advertising status.
-     */
-    public void startAdvertising(Settings settings,
-            AdvertisementData advertiseData, AdvertisementData scanResponse,
-            final AdvertiseCallback callback) {
-        if (callback == null) {
-            throw new IllegalArgumentException("callback cannot be null");
-        }
-        if (mLeAdvertisers.containsKey(settings)) {
-            postCallbackFailure(callback, AdvertiseCallback.ADVERTISING_ALREADY_STARTED);
-            return;
-        }
-        AdvertiseCallbackWrapper wrapper = new AdvertiseCallbackWrapper(callback, advertiseData,
-                scanResponse, settings, mBluetoothGatt);
-        UUID uuid = UUID.randomUUID();
-        try {
-            mBluetoothGatt.registerClient(new ParcelUuid(uuid), wrapper);
-            if (wrapper.advertiseStarted()) {
-                mLeAdvertisers.put(settings, wrapper);
-            }
-        } catch (RemoteException e) {
-            Log.e(TAG, "failed to stop advertising", e);
-        }
-    }
-
-    /**
-     * Stop Bluetooth LE advertising. Returns immediately, the operation status will be delivered
-     * through the {@code callback}.
-     * <p>
-     * Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN}
-     *
-     * @param settings {@link Settings} used to start Bluetooth LE advertising.
-     * @param callback {@link AdvertiseCallback} for delivering stopping advertising status.
-     */
-    public void stopAdvertising(final Settings settings, final AdvertiseCallback callback) {
-        if (callback == null) {
-            throw new IllegalArgumentException("callback cannot be null");
-        }
-        AdvertiseCallbackWrapper wrapper = mLeAdvertisers.get(settings);
-        if (wrapper == null) {
-            postCallbackFailure(callback, AdvertiseCallback.ADVERISING_NOT_STARTED);
-            return;
-        }
-        try {
-            mBluetoothGatt.stopMultiAdvertising(wrapper.mLeHandle);
-            if (wrapper.advertiseStopped()) {
-                mLeAdvertisers.remove(settings);
-            }
-        } catch (RemoteException e) {
-            Log.e(TAG, "failed to stop advertising", e);
-        }
-    }
-
-    private void postCallbackFailure(final AdvertiseCallback callback, final int error) {
-        mHandler.post(new Runnable() {
-                @Override
-            public void run() {
-                callback.onFailure(error);
-            }
-        });
-    }
-}
diff --git a/core/java/android/bluetooth/BluetoothLeScanner.aidl b/core/java/android/bluetooth/BluetoothLeScanner.aidl
deleted file mode 100644
index 8cecdd7..0000000
--- a/core/java/android/bluetooth/BluetoothLeScanner.aidl
+++ /dev/null
@@ -1,20 +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.bluetooth;
-
-parcelable BluetoothLeScanner.ScanResult;
-parcelable BluetoothLeScanner.Settings;
diff --git a/core/java/android/bluetooth/BluetoothLeScanner.java b/core/java/android/bluetooth/BluetoothLeScanner.java
deleted file mode 100644
index ed3188b..0000000
--- a/core/java/android/bluetooth/BluetoothLeScanner.java
+++ /dev/null
@@ -1,759 +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.bluetooth;
-
-import android.annotation.Nullable;
-import android.os.Handler;
-import android.os.Looper;
-import android.os.Parcel;
-import android.os.ParcelUuid;
-import android.os.Parcelable;
-import android.os.RemoteException;
-import android.os.SystemClock;
-import android.util.Log;
-
-import java.util.Arrays;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.Objects;
-import java.util.UUID;
-import java.util.concurrent.TimeUnit;
-
-/**
- * This class provides methods to perform scan related operations for Bluetooth LE devices. An
- * application can scan for a particular type of BLE devices using {@link BluetoothLeScanFilter}. It
- * can also request different types of callbacks for delivering the result.
- * <p>
- * Use {@link BluetoothAdapter#getBluetoothLeScanner()} to get an instance of
- * {@link BluetoothLeScanner}.
- * <p>
- * Note most of the scan methods here require {@link android.Manifest.permission#BLUETOOTH_ADMIN}
- * permission.
- *
- * @see BluetoothLeScanFilter
- */
-public class BluetoothLeScanner {
-
-    private static final String TAG = "BluetoothLeScanner";
-    private static final boolean DBG = true;
-
-    /**
-     * Settings for Bluetooth LE scan.
-     */
-    public static final class Settings implements Parcelable {
-        /**
-         * Perform Bluetooth LE scan in low power mode. This is the default scan mode as it consumes
-         * the least power.
-         */
-        public static final int SCAN_MODE_LOW_POWER = 0;
-        /**
-         * Perform Bluetooth LE scan in balanced power mode.
-         */
-        public static final int SCAN_MODE_BALANCED = 1;
-        /**
-         * Scan using highest duty cycle. It's recommended only using this mode when the application
-         * is running in foreground.
-         */
-        public static final int SCAN_MODE_LOW_LATENCY = 2;
-
-        /**
-         * Callback each time when a bluetooth advertisement is found.
-         */
-        public static final int CALLBACK_TYPE_ON_UPDATE = 0;
-        /**
-         * Callback when a bluetooth advertisement is found for the first time.
-         */
-        public static final int CALLBACK_TYPE_ON_FOUND = 1;
-        /**
-         * Callback when a bluetooth advertisement is found for the first time, then lost.
-         */
-        public static final int CALLBACK_TYPE_ON_LOST = 2;
-
-        /**
-         * Full scan result which contains device mac address, rssi, advertising and scan response
-         * and scan timestamp.
-         */
-        public static final int SCAN_RESULT_TYPE_FULL = 0;
-        /**
-         * Truncated scan result which contains device mac address, rssi and scan timestamp. Note
-         * it's possible for an app to get more scan results that it asks if there are multiple apps
-         * using this type. TODO: decide whether we could unhide this setting.
-         *
-         * @hide
-         */
-        public static final int SCAN_RESULT_TYPE_TRUNCATED = 1;
-
-        // Bluetooth LE scan mode.
-        private int mScanMode;
-
-        // Bluetooth LE scan callback type
-        private int mCallbackType;
-
-        // Bluetooth LE scan result type
-        private int mScanResultType;
-
-        // Time of delay for reporting the scan result
-        private long mReportDelayMicros;
-
-        public int getScanMode() {
-            return mScanMode;
-        }
-
-        public int getCallbackType() {
-            return mCallbackType;
-        }
-
-        public int getScanResultType() {
-            return mScanResultType;
-        }
-
-        /**
-         * Returns report delay timestamp based on the device clock.
-         */
-        public long getReportDelayMicros() {
-            return mReportDelayMicros;
-        }
-
-        /**
-         * Creates a new {@link Builder} to build {@link Settings} object.
-         */
-        public static Builder newBuilder() {
-            return new Builder();
-        }
-
-        private Settings(int scanMode, int callbackType, int scanResultType,
-                long reportDelayMicros) {
-            mScanMode = scanMode;
-            mCallbackType = callbackType;
-            mScanResultType = scanResultType;
-            mReportDelayMicros = reportDelayMicros;
-        }
-
-        private Settings(Parcel in) {
-            mScanMode = in.readInt();
-            mCallbackType = in.readInt();
-            mScanResultType = in.readInt();
-            mReportDelayMicros = in.readLong();
-        }
-
-        @Override
-        public void writeToParcel(Parcel dest, int flags) {
-            dest.writeInt(mScanMode);
-            dest.writeInt(mCallbackType);
-            dest.writeInt(mScanResultType);
-            dest.writeLong(mReportDelayMicros);
-        }
-
-        @Override
-        public int describeContents() {
-            return 0;
-        }
-
-        public static final Parcelable.Creator<Settings> CREATOR = new Creator<Settings>() {
-                @Override
-            public Settings[] newArray(int size) {
-                return new Settings[size];
-            }
-
-                @Override
-            public Settings createFromParcel(Parcel in) {
-                return new Settings(in);
-            }
-        };
-
-        /**
-         * Builder for {@link BluetoothLeScanner.Settings}.
-         */
-        public static class Builder {
-            private int mScanMode = SCAN_MODE_LOW_POWER;
-            private int mCallbackType = CALLBACK_TYPE_ON_UPDATE;
-            private int mScanResultType = SCAN_RESULT_TYPE_FULL;
-            private long mReportDelayMicros = 0;
-
-            // Hidden constructor.
-            private Builder() {
-            }
-
-            /**
-             * Set scan mode for Bluetooth LE scan.
-             *
-             * @param scanMode The scan mode can be one of {@link Settings#SCAN_MODE_LOW_POWER},
-             *            {@link Settings#SCAN_MODE_BALANCED} or
-             *            {@link Settings#SCAN_MODE_LOW_LATENCY}.
-             * @throws IllegalArgumentException If the {@code scanMode} is invalid.
-             */
-            public Builder scanMode(int scanMode) {
-                if (scanMode < SCAN_MODE_LOW_POWER || scanMode > SCAN_MODE_LOW_LATENCY) {
-                    throw new IllegalArgumentException("invalid scan mode " + scanMode);
-                }
-                mScanMode = scanMode;
-                return this;
-            }
-
-            /**
-             * Set callback type for Bluetooth LE scan.
-             *
-             * @param callbackType The callback type for the scan. Can be either one of
-             *            {@link Settings#CALLBACK_TYPE_ON_UPDATE},
-             *            {@link Settings#CALLBACK_TYPE_ON_FOUND} or
-             *            {@link Settings#CALLBACK_TYPE_ON_LOST}.
-             * @throws IllegalArgumentException If the {@code callbackType} is invalid.
-             */
-            public Builder callbackType(int callbackType) {
-                if (callbackType < CALLBACK_TYPE_ON_UPDATE
-                        || callbackType > CALLBACK_TYPE_ON_LOST) {
-                    throw new IllegalArgumentException("invalid callback type - " + callbackType);
-                }
-                mCallbackType = callbackType;
-                return this;
-            }
-
-            /**
-             * Set scan result type for Bluetooth LE scan.
-             *
-             * @param scanResultType Type for scan result, could be either
-             *            {@link Settings#SCAN_RESULT_TYPE_FULL} or
-             *            {@link Settings#SCAN_RESULT_TYPE_TRUNCATED}.
-             * @throws IllegalArgumentException If the {@code scanResultType} is invalid.
-             * @hide
-             */
-            public Builder scanResultType(int scanResultType) {
-                if (scanResultType < SCAN_RESULT_TYPE_FULL
-                        || scanResultType > SCAN_RESULT_TYPE_TRUNCATED) {
-                    throw new IllegalArgumentException(
-                            "invalid scanResultType - " + scanResultType);
-                }
-                mScanResultType = scanResultType;
-                return this;
-            }
-
-            /**
-             * Set report delay timestamp for Bluetooth LE scan.
-             */
-            public Builder reportDelayMicros(long reportDelayMicros) {
-                mReportDelayMicros = reportDelayMicros;
-                return this;
-            }
-
-            /**
-             * Build {@link Settings}.
-             */
-            public Settings build() {
-                return new Settings(mScanMode, mCallbackType, mScanResultType, mReportDelayMicros);
-            }
-        }
-    }
-
-    /**
-     * ScanResult for Bluetooth LE scan.
-     */
-    public static final class ScanResult implements Parcelable {
-        // Remote bluetooth device.
-        private BluetoothDevice mDevice;
-
-        // Scan record, including advertising data and scan response data.
-        private byte[] mScanRecord;
-
-        // Received signal strength.
-        private int mRssi;
-
-        // Device timestamp when the result was last seen.
-        private long mTimestampMicros;
-
-        // Constructor of scan result.
-        public ScanResult(BluetoothDevice device, byte[] scanRecord, int rssi, long timestampMicros) {
-            mDevice = device;
-            mScanRecord = scanRecord;
-            mRssi = rssi;
-            mTimestampMicros = timestampMicros;
-        }
-
-        private ScanResult(Parcel in) {
-            readFromParcel(in);
-        }
-
-        @Override
-        public void writeToParcel(Parcel dest, int flags) {
-            if (mDevice != null) {
-                dest.writeInt(1);
-                mDevice.writeToParcel(dest, flags);
-            } else {
-                dest.writeInt(0);
-            }
-            if (mScanRecord != null) {
-                dest.writeInt(1);
-                dest.writeByteArray(mScanRecord);
-            } else {
-                dest.writeInt(0);
-            }
-            dest.writeInt(mRssi);
-            dest.writeLong(mTimestampMicros);
-        }
-
-        private void readFromParcel(Parcel in) {
-            if (in.readInt() == 1) {
-                mDevice = BluetoothDevice.CREATOR.createFromParcel(in);
-            }
-            if (in.readInt() == 1) {
-                mScanRecord = in.createByteArray();
-            }
-            mRssi = in.readInt();
-            mTimestampMicros = in.readLong();
-        }
-
-        @Override
-        public int describeContents() {
-            return 0;
-        }
-
-        /**
-         * Returns the remote bluetooth device identified by the bluetooth device address.
-         */
-        @Nullable
-        public BluetoothDevice getDevice() {
-            return mDevice;
-        }
-
-        @Nullable /**
-                   * Returns the scan record, which can be a combination of advertisement and scan response.
-                   */
-        public byte[] getScanRecord() {
-            return mScanRecord;
-        }
-
-        /**
-         * Returns the received signal strength in dBm. The valid range is [-127, 127].
-         */
-        public int getRssi() {
-            return mRssi;
-        }
-
-        /**
-         * Returns timestamp since boot when the scan record was observed.
-         */
-        public long getTimestampMicros() {
-            return mTimestampMicros;
-        }
-
-        @Override
-        public int hashCode() {
-            return Objects.hash(mDevice, mRssi, mScanRecord, mTimestampMicros);
-        }
-
-        @Override
-        public boolean equals(Object obj) {
-            if (this == obj) {
-                return true;
-            }
-            if (obj == null || getClass() != obj.getClass()) {
-                return false;
-            }
-            ScanResult other = (ScanResult) obj;
-            return Objects.equals(mDevice, other.mDevice) && (mRssi == other.mRssi) &&
-                    Objects.deepEquals(mScanRecord, other.mScanRecord)
-                    && (mTimestampMicros == other.mTimestampMicros);
-        }
-
-        @Override
-        public String toString() {
-            return "ScanResult{" + "mDevice=" + mDevice + ", mScanRecord="
-                    + Arrays.toString(mScanRecord) + ", mRssi=" + mRssi + ", mTimestampMicros="
-                    + mTimestampMicros + '}';
-        }
-
-        public static final Parcelable.Creator<ScanResult> CREATOR = new Creator<ScanResult>() {
-                @Override
-            public ScanResult createFromParcel(Parcel source) {
-                return new ScanResult(source);
-            }
-
-                @Override
-            public ScanResult[] newArray(int size) {
-                return new ScanResult[size];
-            }
-        };
-
-    }
-
-    /**
-     * Callback of Bluetooth LE scans. The results of the scans will be delivered through the
-     * callbacks.
-     */
-    public interface ScanCallback {
-        /**
-         * Callback when any BLE beacon is found.
-         *
-         * @param result A Bluetooth LE scan result.
-         */
-        public void onDeviceUpdate(ScanResult result);
-
-        /**
-         * Callback when the BLE beacon is found for the first time.
-         *
-         * @param result The Bluetooth LE scan result when the onFound event is triggered.
-         */
-        public void onDeviceFound(ScanResult result);
-
-        /**
-         * Callback when the BLE device was lost. Note a device has to be "found" before it's lost.
-         *
-         * @param device The Bluetooth device that is lost.
-         */
-        public void onDeviceLost(BluetoothDevice device);
-
-        /**
-         * Callback when batch results are delivered.
-         *
-         * @param results List of scan results that are previously scanned.
-         */
-        public void onBatchScanResults(List<ScanResult> results);
-
-        /**
-         * Fails to start scan as BLE scan with the same settings is already started by the app.
-         */
-        public static final int SCAN_ALREADY_STARTED = 1;
-        /**
-         * Fails to start scan as app cannot be registered.
-         */
-        public static final int APPLICATION_REGISTRATION_FAILED = 2;
-        /**
-         * Fails to start scan due to gatt service failure.
-         */
-        public static final int GATT_SERVICE_FAILURE = 3;
-        /**
-         * Fails to start scan due to controller failure.
-         */
-        public static final int CONTROLLER_FAILURE = 4;
-
-        /**
-         * Callback when scan failed.
-         */
-        public void onScanFailed(int errorCode);
-    }
-
-    private final IBluetoothGatt mBluetoothGatt;
-    private final Handler mHandler;
-    private final Map<Settings, BleScanCallbackWrapper> mLeScanClients;
-
-    BluetoothLeScanner(IBluetoothGatt bluetoothGatt) {
-        mBluetoothGatt = bluetoothGatt;
-        mHandler = new Handler(Looper.getMainLooper());
-        mLeScanClients = new HashMap<Settings, BleScanCallbackWrapper>();
-    }
-
-    /**
-     * Bluetooth GATT interface callbacks
-     */
-    private static class BleScanCallbackWrapper extends IBluetoothGattCallback.Stub {
-        private static final int REGISTRATION_CALLBACK_TIMEOUT_SECONDS = 5;
-
-        private final ScanCallback mScanCallback;
-        private final List<BluetoothLeScanFilter> mFilters;
-        private Settings mSettings;
-        private IBluetoothGatt mBluetoothGatt;
-
-        // mLeHandle 0: not registered
-        // -1: scan stopped
-        // > 0: registered and scan started
-        private int mLeHandle;
-
-        public BleScanCallbackWrapper(IBluetoothGatt bluetoothGatt,
-                List<BluetoothLeScanFilter> filters, Settings settings, ScanCallback scanCallback) {
-            mBluetoothGatt = bluetoothGatt;
-            mFilters = filters;
-            mSettings = settings;
-            mScanCallback = scanCallback;
-            mLeHandle = 0;
-        }
-
-        public boolean scanStarted() {
-            synchronized (this) {
-                if (mLeHandle == -1) {
-                    return false;
-                }
-                try {
-                    wait(REGISTRATION_CALLBACK_TIMEOUT_SECONDS);
-                } catch (InterruptedException e) {
-                    Log.e(TAG, "Callback reg wait interrupted: " + e);
-                }
-            }
-            return mLeHandle > 0;
-        }
-
-        public void stopLeScan() {
-            synchronized (this) {
-                if (mLeHandle <= 0) {
-                    Log.e(TAG, "Error state, mLeHandle: " + mLeHandle);
-                    return;
-                }
-                try {
-                    mBluetoothGatt.stopScan(mLeHandle, false);
-                    mBluetoothGatt.unregisterClient(mLeHandle);
-                } catch (RemoteException e) {
-                    Log.e(TAG, "Failed to stop scan and unregister" + e);
-                }
-                mLeHandle = -1;
-                notifyAll();
-            }
-        }
-
-        /**
-         * Application interface registered - app is ready to go
-         */
-        @Override
-        public void onClientRegistered(int status, int clientIf) {
-            Log.d(TAG, "onClientRegistered() - status=" + status +
-                    " clientIf=" + clientIf);
-
-            synchronized (this) {
-                if (mLeHandle == -1) {
-                    if (DBG)
-                        Log.d(TAG, "onClientRegistered LE scan canceled");
-                }
-
-                if (status == BluetoothGatt.GATT_SUCCESS) {
-                    mLeHandle = clientIf;
-                    try {
-                        mBluetoothGatt.startScanWithFilters(mLeHandle, false, mSettings, mFilters);
-                    } catch (RemoteException e) {
-                        Log.e(TAG, "fail to start le scan: " + e);
-                        mLeHandle = -1;
-                    }
-                } else {
-                    // registration failed
-                    mLeHandle = -1;
-                }
-                notifyAll();
-            }
-        }
-
-        @Override
-        public void onClientConnectionState(int status, int clientIf,
-                boolean connected, String address) {
-            // no op
-        }
-
-        /**
-         * Callback reporting an LE scan result.
-         *
-         * @hide
-         */
-        @Override
-        public void onScanResult(String address, int rssi, byte[] advData) {
-            if (DBG)
-                Log.d(TAG, "onScanResult() - Device=" + address + " RSSI=" + rssi);
-
-            // Check null in case the scan has been stopped
-            synchronized (this) {
-                if (mLeHandle <= 0)
-                    return;
-            }
-            BluetoothDevice device = BluetoothAdapter.getDefaultAdapter().getRemoteDevice(
-                    address);
-            long scanMicros = TimeUnit.NANOSECONDS.toMicros(SystemClock.elapsedRealtimeNanos());
-            ScanResult result = new ScanResult(device, advData, rssi,
-                    scanMicros);
-            mScanCallback.onDeviceUpdate(result);
-        }
-
-        @Override
-        public void onGetService(String address, int srvcType,
-                int srvcInstId, ParcelUuid srvcUuid) {
-            // no op
-        }
-
-        @Override
-        public void onGetIncludedService(String address, int srvcType,
-                int srvcInstId, ParcelUuid srvcUuid,
-                int inclSrvcType, int inclSrvcInstId,
-                ParcelUuid inclSrvcUuid) {
-            // no op
-        }
-
-        @Override
-        public void onGetCharacteristic(String address, int srvcType,
-                int srvcInstId, ParcelUuid srvcUuid,
-                int charInstId, ParcelUuid charUuid,
-                int charProps) {
-            // no op
-        }
-
-        @Override
-        public void onGetDescriptor(String address, int srvcType,
-                int srvcInstId, ParcelUuid srvcUuid,
-                int charInstId, ParcelUuid charUuid,
-                int descInstId, ParcelUuid descUuid) {
-            // no op
-        }
-
-        @Override
-        public void onSearchComplete(String address, int status) {
-            // no op
-        }
-
-        @Override
-        public void onCharacteristicRead(String address, int status, int srvcType,
-                int srvcInstId, ParcelUuid srvcUuid,
-                int charInstId, ParcelUuid charUuid, byte[] value) {
-            // no op
-        }
-
-        @Override
-        public void onCharacteristicWrite(String address, int status, int srvcType,
-                int srvcInstId, ParcelUuid srvcUuid,
-                int charInstId, ParcelUuid charUuid) {
-            // no op
-        }
-
-        @Override
-        public void onNotify(String address, int srvcType,
-                int srvcInstId, ParcelUuid srvcUuid,
-                int charInstId, ParcelUuid charUuid,
-                byte[] value) {
-            // no op
-        }
-
-        @Override
-        public void onDescriptorRead(String address, int status, int srvcType,
-                int srvcInstId, ParcelUuid srvcUuid,
-                int charInstId, ParcelUuid charUuid,
-                int descInstId, ParcelUuid descrUuid, byte[] value) {
-            // no op
-        }
-
-        @Override
-        public void onDescriptorWrite(String address, int status, int srvcType,
-                int srvcInstId, ParcelUuid srvcUuid,
-                int charInstId, ParcelUuid charUuid,
-                int descInstId, ParcelUuid descrUuid) {
-            // no op
-        }
-
-        @Override
-        public void onExecuteWrite(String address, int status) {
-            // no op
-        }
-
-        @Override
-        public void onReadRemoteRssi(String address, int rssi, int status) {
-            // no op
-        }
-
-        @Override
-        public void onAdvertiseStateChange(int advertiseState, int status) {
-            // no op
-        }
-
-        @Override
-        public void onMultiAdvertiseCallback(int status) {
-            // no op
-        }
-
-        @Override
-        public void onConfigureMTU(String address, int mtu, int status) {
-            // no op
-        }
-    }
-
-    /**
-     * Scan Bluetooth LE scan. The scan results will be delivered through {@code callback}.
-     *
-     * @param filters {@link BluetoothLeScanFilter}s for finding exact BLE devices.
-     * @param settings Settings for ble scan.
-     * @param callback Callback when scan results are delivered.
-     * @throws IllegalArgumentException If {@code settings} or {@code callback} is null.
-     */
-    public void startScan(List<BluetoothLeScanFilter> filters, Settings settings,
-            final ScanCallback callback) {
-        if (settings == null || callback == null) {
-            throw new IllegalArgumentException("settings or callback is null");
-        }
-        synchronized (mLeScanClients) {
-            if (mLeScanClients.get(settings) != null) {
-                postCallbackError(callback, ScanCallback.SCAN_ALREADY_STARTED);
-                return;
-            }
-            BleScanCallbackWrapper wrapper = new BleScanCallbackWrapper(mBluetoothGatt, filters,
-                    settings, callback);
-            try {
-                UUID uuid = UUID.randomUUID();
-                mBluetoothGatt.registerClient(new ParcelUuid(uuid), wrapper);
-                if (wrapper.scanStarted()) {
-                    mLeScanClients.put(settings, wrapper);
-                } else {
-                    postCallbackError(callback, ScanCallback.APPLICATION_REGISTRATION_FAILED);
-                    return;
-                }
-            } catch (RemoteException e) {
-                Log.e(TAG, "GATT service exception when starting scan", e);
-                postCallbackError(callback, ScanCallback.GATT_SERVICE_FAILURE);
-            }
-        }
-    }
-
-    private void postCallbackError(final ScanCallback callback, final int errorCode) {
-        mHandler.post(new Runnable() {
-                @Override
-            public void run() {
-                callback.onScanFailed(errorCode);
-            }
-        });
-    }
-
-    /**
-     * Stop Bluetooth LE scan.
-     *
-     * @param settings The same settings as used in {@link #startScan}, which is used to identify
-     *            the BLE scan.
-     */
-    public void stopScan(Settings settings) {
-        synchronized (mLeScanClients) {
-            BleScanCallbackWrapper wrapper = mLeScanClients.remove(settings);
-            if (wrapper == null) {
-                return;
-            }
-            wrapper.stopLeScan();
-        }
-    }
-
-    /**
-     * Returns available storage size for batch scan results. It's recommended not to use batch scan
-     * if available storage size is small (less than 1k bytes, for instance).
-     *
-     * @hide TODO: unhide when batching is supported in stack.
-     */
-    public int getAvailableBatchStorageSizeBytes() {
-        throw new UnsupportedOperationException("not impelemented");
-    }
-
-    /**
-     * Poll scan results from bluetooth controller. This will return Bluetooth LE scan results
-     * batched on bluetooth controller.
-     *
-     * @param callback Callback of the Bluetooth LE Scan, it has to be the same instance as the one
-     *            used to start scan.
-     * @param flush Whether to flush the batch scan buffer. Note the other batch scan clients will
-     *            get batch scan callback if the batch scan buffer is flushed.
-     * @return Batch Scan results.
-     * @hide TODO: unhide when batching is supported in stack.
-     */
-    public List<ScanResult> getBatchScanResults(ScanCallback callback, boolean flush) {
-        throw new UnsupportedOperationException("not impelemented");
-    }
-
-}
diff --git a/core/java/android/bluetooth/BluetoothProfile.java b/core/java/android/bluetooth/BluetoothProfile.java
index 1574090..d898060 100644
--- a/core/java/android/bluetooth/BluetoothProfile.java
+++ b/core/java/android/bluetooth/BluetoothProfile.java
@@ -104,6 +104,12 @@
     public static final int MAP = 9;
 
     /**
+     * A2DP Sink Profile
+     * @hide
+     */
+    public static final int A2DP_SINK = 10;
+
+    /**
      * Default priority for devices that we try to auto-connect to and
      * and allow incoming connections for the profile
      * @hide
diff --git a/core/java/android/bluetooth/IBluetoothGatt.aidl b/core/java/android/bluetooth/IBluetoothGatt.aidl
index ceed52b..00a0750 100644
--- a/core/java/android/bluetooth/IBluetoothGatt.aidl
+++ b/core/java/android/bluetooth/IBluetoothGatt.aidl
@@ -17,10 +17,10 @@
 package android.bluetooth;
 
 import android.bluetooth.BluetoothDevice;
-import android.bluetooth.BluetoothLeAdvertiseScanData;
-import android.bluetooth.BluetoothLeAdvertiser;
-import android.bluetooth.BluetoothLeScanFilter;
-import android.bluetooth.BluetoothLeScanner;
+import android.bluetooth.le.AdvertiseSettings;
+import android.bluetooth.le.AdvertisementData;
+import android.bluetooth.le.ScanFilter;
+import android.bluetooth.le.ScanSettings;
 import android.os.ParcelUuid;
 
 import android.bluetooth.IBluetoothGattCallback;
@@ -38,13 +38,12 @@
     void startScanWithUuidsScanParam(in int appIf, in boolean isServer,
                     in ParcelUuid[] ids, int scanWindow, int scanInterval);
     void startScanWithFilters(in int appIf, in boolean isServer,
-                              in BluetoothLeScanner.Settings settings,
-                              in List<BluetoothLeScanFilter> filters);
+                              in ScanSettings settings, in List<ScanFilter> filters);
     void stopScan(in int appIf, in boolean isServer);
     void startMultiAdvertising(in int appIf,
-                               in BluetoothLeAdvertiseScanData.AdvertisementData advertiseData,
-                               in BluetoothLeAdvertiseScanData.AdvertisementData scanResponse,
-                               in BluetoothLeAdvertiser.Settings settings);
+                               in AdvertisementData advertiseData,
+                               in AdvertisementData scanResponse,
+                               in AdvertiseSettings settings);
     void stopMultiAdvertising(in int appIf);
     void registerClient(in ParcelUuid appId, in IBluetoothGattCallback callback);
     void unregisterClient(in int clientIf);
diff --git a/core/java/android/bluetooth/le/AdvertiseCallback.java b/core/java/android/bluetooth/le/AdvertiseCallback.java
new file mode 100644
index 0000000..f1334c2
--- /dev/null
+++ b/core/java/android/bluetooth/le/AdvertiseCallback.java
@@ -0,0 +1,68 @@
+/*
+ * 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.bluetooth.le;
+
+/**
+ * Callback of Bluetooth LE advertising, which is used to deliver advertising operation status.
+ */
+public abstract class AdvertiseCallback {
+
+    /**
+     * The operation is success.
+     *
+     * @hide
+     */
+    public static final int SUCCESS = 0;
+    /**
+     * Fails to start advertising as the advertisement data contains services that are not added to
+     * the local bluetooth GATT server.
+     */
+    public static final int ADVERTISE_FAILED_SERVICE_UNKNOWN = 1;
+    /**
+     * Fails to start advertising as system runs out of quota for advertisers.
+     */
+    public static final int ADVERTISE_FAILED_TOO_MANY_ADVERTISERS = 2;
+
+    /**
+     * Fails to start advertising as the advertising is already started.
+     */
+    public static final int ADVERTISE_FAILED_ALREADY_STARTED = 3;
+    /**
+     * Fails to stop advertising as the advertising is not started.
+     */
+    public static final int ADVERTISE_FAILED_NOT_STARTED = 4;
+
+    /**
+     * Operation fails due to bluetooth controller failure.
+     */
+    public static final int ADVERTISE_FAILED_CONTROLLER_FAILURE = 5;
+
+    /**
+     * Callback when advertising operation succeeds.
+     *
+     * @param settingsInEffect The actual settings used for advertising, which may be different from
+     *            what the app asks.
+     */
+    public abstract void onSuccess(AdvertiseSettings settingsInEffect);
+
+    /**
+     * Callback when advertising operation fails.
+     *
+     * @param errorCode Error code for failures.
+     */
+    public abstract void onFailure(int errorCode);
+}
diff --git a/core/java/android/bluetooth/BluetoothLeScanFilter.aidl b/core/java/android/bluetooth/le/AdvertiseSettings.aidl
similarity index 90%
rename from core/java/android/bluetooth/BluetoothLeScanFilter.aidl
rename to core/java/android/bluetooth/le/AdvertiseSettings.aidl
index 86ee06d..9f47d74 100644
--- a/core/java/android/bluetooth/BluetoothLeScanFilter.aidl
+++ b/core/java/android/bluetooth/le/AdvertiseSettings.aidl
@@ -14,6 +14,6 @@
  * limitations under the License.
  */
 
-package android.bluetooth;
+package android.bluetooth.le;
 
-parcelable BluetoothLeScanFilter;
+parcelable AdvertiseSettings;
\ No newline at end of file
diff --git a/core/java/android/bluetooth/le/AdvertiseSettings.java b/core/java/android/bluetooth/le/AdvertiseSettings.java
new file mode 100644
index 0000000..87d0346
--- /dev/null
+++ b/core/java/android/bluetooth/le/AdvertiseSettings.java
@@ -0,0 +1,218 @@
+/*
+ * 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.bluetooth.le;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/**
+ * The {@link AdvertiseSettings} provide a way to adjust advertising preferences for each
+ * individual advertisement. Use {@link AdvertiseSettings.Builder} to create an instance.
+ */
+public final class AdvertiseSettings implements Parcelable {
+    /**
+     * Perform Bluetooth LE advertising in low power mode. This is the default and preferred
+     * advertising mode as it consumes the least power.
+     */
+    public static final int ADVERTISE_MODE_LOW_POWER = 0;
+    /**
+     * Perform Bluetooth LE advertising in balanced power mode. This is balanced between advertising
+     * frequency and power consumption.
+     */
+    public static final int ADVERTISE_MODE_BALANCED = 1;
+    /**
+     * Perform Bluetooth LE advertising in low latency, high power mode. This has the highest power
+     * consumption and should not be used for background continuous advertising.
+     */
+    public static final int ADVERTISE_MODE_LOW_LATENCY = 2;
+
+    /**
+     * Advertise using the lowest transmission(tx) power level. An app can use low transmission
+     * power to restrict the visibility range of its advertising packet.
+     */
+    public static final int ADVERTISE_TX_POWER_ULTRA_LOW = 0;
+    /**
+     * Advertise using low tx power level.
+     */
+    public static final int ADVERTISE_TX_POWER_LOW = 1;
+    /**
+     * Advertise using medium tx power level.
+     */
+    public static final int ADVERTISE_TX_POWER_MEDIUM = 2;
+    /**
+     * Advertise using high tx power level. This is corresponding to largest visibility range of the
+     * advertising packet.
+     */
+    public static final int ADVERTISE_TX_POWER_HIGH = 3;
+
+    /**
+     * Non-connectable undirected advertising event, as defined in Bluetooth Specification V4.1
+     * vol6, part B, section 4.4.2 - Advertising state.
+     */
+    public static final int ADVERTISE_TYPE_NON_CONNECTABLE = 0;
+    /**
+     * Scannable undirected advertise type, as defined in same spec mentioned above. This event type
+     * allows a scanner to send a scan request asking additional information about the advertiser.
+     */
+    public static final int ADVERTISE_TYPE_SCANNABLE = 1;
+    /**
+     * Connectable undirected advertising type, as defined in same spec mentioned above. This event
+     * type allows a scanner to send scan request asking additional information about the
+     * advertiser. It also allows an initiator to send a connect request for connection.
+     */
+    public static final int ADVERTISE_TYPE_CONNECTABLE = 2;
+
+    private final int mAdvertiseMode;
+    private final int mAdvertiseTxPowerLevel;
+    private final int mAdvertiseEventType;
+
+    private AdvertiseSettings(int advertiseMode, int advertiseTxPowerLevel,
+            int advertiseEventType) {
+        mAdvertiseMode = advertiseMode;
+        mAdvertiseTxPowerLevel = advertiseTxPowerLevel;
+        mAdvertiseEventType = advertiseEventType;
+    }
+
+    private AdvertiseSettings(Parcel in) {
+        mAdvertiseMode = in.readInt();
+        mAdvertiseTxPowerLevel = in.readInt();
+        mAdvertiseEventType = in.readInt();
+    }
+
+    /**
+     * Returns the advertise mode.
+     */
+    public int getMode() {
+        return mAdvertiseMode;
+    }
+
+    /**
+     * Returns the tx power level for advertising.
+     */
+    public int getTxPowerLevel() {
+        return mAdvertiseTxPowerLevel;
+    }
+
+    /**
+     * Returns the advertise event type.
+     */
+    public int getType() {
+        return mAdvertiseEventType;
+    }
+
+    @Override
+    public String toString() {
+        return "Settings [mAdvertiseMode=" + mAdvertiseMode + ", mAdvertiseTxPowerLevel="
+                + mAdvertiseTxPowerLevel + ", mAdvertiseEventType=" + mAdvertiseEventType + "]";
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        dest.writeInt(mAdvertiseMode);
+        dest.writeInt(mAdvertiseTxPowerLevel);
+        dest.writeInt(mAdvertiseEventType);
+    }
+
+    public static final Parcelable.Creator<AdvertiseSettings> CREATOR =
+            new Creator<AdvertiseSettings>() {
+            @Override
+                public AdvertiseSettings[] newArray(int size) {
+                    return new AdvertiseSettings[size];
+                }
+
+            @Override
+                public AdvertiseSettings createFromParcel(Parcel in) {
+                    return new AdvertiseSettings(in);
+                }
+            };
+
+    /**
+     * Builder class for {@link AdvertiseSettings}.
+     */
+    public static final class Builder {
+        private int mMode = ADVERTISE_MODE_LOW_POWER;
+        private int mTxPowerLevel = ADVERTISE_TX_POWER_MEDIUM;
+        private int mType = ADVERTISE_TYPE_NON_CONNECTABLE;
+
+        /**
+         * Set advertise mode to control the advertising power and latency.
+         *
+         * @param advertiseMode Bluetooth LE Advertising mode, can only be one of
+         *            {@link AdvertiseSettings#ADVERTISE_MODE_LOW_POWER},
+         *            {@link AdvertiseSettings#ADVERTISE_MODE_BALANCED}, or
+         *            {@link AdvertiseSettings#ADVERTISE_MODE_LOW_LATENCY}.
+         * @throws IllegalArgumentException If the advertiseMode is invalid.
+         */
+        public Builder setAdvertiseMode(int advertiseMode) {
+            if (advertiseMode < ADVERTISE_MODE_LOW_POWER
+                    || advertiseMode > ADVERTISE_MODE_LOW_LATENCY) {
+                throw new IllegalArgumentException("unknown mode " + advertiseMode);
+            }
+            mMode = advertiseMode;
+            return this;
+        }
+
+        /**
+         * Set advertise tx power level to control the transmission power level for the advertising.
+         *
+         * @param txPowerLevel Transmission power of Bluetooth LE Advertising, can only be one of
+         *            {@link AdvertiseSettings#ADVERTISE_TX_POWER_ULTRA_LOW},
+         *            {@link AdvertiseSettings#ADVERTISE_TX_POWER_LOW},
+         *            {@link AdvertiseSettings#ADVERTISE_TX_POWER_MEDIUM} or
+         *            {@link AdvertiseSettings#ADVERTISE_TX_POWER_HIGH}.
+         * @throws IllegalArgumentException If the {@code txPowerLevel} is invalid.
+         */
+        public Builder setTxPowerLevel(int txPowerLevel) {
+            if (txPowerLevel < ADVERTISE_TX_POWER_ULTRA_LOW
+                    || txPowerLevel > ADVERTISE_TX_POWER_HIGH) {
+                throw new IllegalArgumentException("unknown tx power level " + txPowerLevel);
+            }
+            mTxPowerLevel = txPowerLevel;
+            return this;
+        }
+
+        /**
+         * Set advertise type to control the event type of advertising.
+         *
+         * @param type Bluetooth LE Advertising type, can be either
+         *            {@link AdvertiseSettings#ADVERTISE_TYPE_NON_CONNECTABLE},
+         *            {@link AdvertiseSettings#ADVERTISE_TYPE_SCANNABLE} or
+         *            {@link AdvertiseSettings#ADVERTISE_TYPE_CONNECTABLE}.
+         * @throws IllegalArgumentException If the {@code type} is invalid.
+         */
+        public Builder setType(int type) {
+            if (type < ADVERTISE_TYPE_NON_CONNECTABLE
+                    || type > ADVERTISE_TYPE_CONNECTABLE) {
+                throw new IllegalArgumentException("unknown advertise type " + type);
+            }
+            mType = type;
+            return this;
+        }
+
+        /**
+         * Build the {@link AdvertiseSettings} object.
+         */
+        public AdvertiseSettings build() {
+            return new AdvertiseSettings(mMode, mTxPowerLevel, mType);
+        }
+    }
+}
diff --git a/core/java/android/bluetooth/BluetoothLeScanFilter.aidl b/core/java/android/bluetooth/le/AdvertisementData.aidl
similarity index 90%
copy from core/java/android/bluetooth/BluetoothLeScanFilter.aidl
copy to core/java/android/bluetooth/le/AdvertisementData.aidl
index 86ee06d..3da1321 100644
--- a/core/java/android/bluetooth/BluetoothLeScanFilter.aidl
+++ b/core/java/android/bluetooth/le/AdvertisementData.aidl
@@ -14,6 +14,6 @@
  * limitations under the License.
  */
 
-package android.bluetooth;
+package android.bluetooth.le;
 
-parcelable BluetoothLeScanFilter;
+parcelable AdvertisementData;
\ No newline at end of file
diff --git a/core/java/android/bluetooth/le/AdvertisementData.java b/core/java/android/bluetooth/le/AdvertisementData.java
new file mode 100644
index 0000000..c587204
--- /dev/null
+++ b/core/java/android/bluetooth/le/AdvertisementData.java
@@ -0,0 +1,344 @@
+/*
+ * 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.bluetooth.le;
+
+import android.annotation.Nullable;
+import android.bluetooth.BluetoothUuid;
+import android.os.Parcel;
+import android.os.ParcelUuid;
+import android.os.Parcelable;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+/**
+ * Advertisement data packet for Bluetooth LE advertising. This represents the data to be
+ * broadcasted in Bluetooth LE advertising as well as the scan response for active scan.
+ * <p>
+ * Use {@link AdvertisementData.Builder} to create an instance of {@link AdvertisementData} to be
+ * advertised.
+ *
+ * @see BluetoothLeAdvertiser
+ * @see ScanRecord
+ */
+public final class AdvertisementData implements Parcelable {
+
+    @Nullable
+    private final List<ParcelUuid> mServiceUuids;
+
+    private final int mManufacturerId;
+    @Nullable
+    private final byte[] mManufacturerSpecificData;
+
+    @Nullable
+    private final ParcelUuid mServiceDataUuid;
+    @Nullable
+    private final byte[] mServiceData;
+
+    private boolean mIncludeTxPowerLevel;
+
+    private AdvertisementData(List<ParcelUuid> serviceUuids,
+            ParcelUuid serviceDataUuid, byte[] serviceData,
+            int manufacturerId,
+            byte[] manufacturerSpecificData, boolean includeTxPowerLevel) {
+        mServiceUuids = serviceUuids;
+        mManufacturerId = manufacturerId;
+        mManufacturerSpecificData = manufacturerSpecificData;
+        mServiceDataUuid = serviceDataUuid;
+        mServiceData = serviceData;
+        mIncludeTxPowerLevel = includeTxPowerLevel;
+    }
+
+    /**
+     * Returns a list of service uuids within the advertisement that are used to identify the
+     * bluetooth GATT services.
+     */
+    public List<ParcelUuid> getServiceUuids() {
+        return mServiceUuids;
+    }
+
+    /**
+     * Returns the manufacturer identifier, which is a non-negative number assigned by Bluetooth
+     * SIG.
+     */
+    public int getManufacturerId() {
+        return mManufacturerId;
+    }
+
+    /**
+     * Returns the manufacturer specific data which is the content of manufacturer specific data
+     * field. The first 2 bytes of the data contain the company id.
+     */
+    public byte[] getManufacturerSpecificData() {
+        return mManufacturerSpecificData;
+    }
+
+    /**
+     * Returns a 16 bit uuid of the service that the service data is associated with.
+     */
+    public ParcelUuid getServiceDataUuid() {
+        return mServiceDataUuid;
+    }
+
+    /**
+     * Returns service data. The first two bytes should be a 16 bit service uuid associated with the
+     * service data.
+     */
+    public byte[] getServiceData() {
+        return mServiceData;
+    }
+
+    /**
+     * Whether the transmission power level will be included in the advertisement packet.
+     */
+    public boolean getIncludeTxPowerLevel() {
+        return mIncludeTxPowerLevel;
+    }
+
+    @Override
+    public String toString() {
+        return "AdvertisementData [mServiceUuids=" + mServiceUuids + ", mManufacturerId="
+                + mManufacturerId + ", mManufacturerSpecificData="
+                + Arrays.toString(mManufacturerSpecificData) + ", mServiceDataUuid="
+                + mServiceDataUuid + ", mServiceData=" + Arrays.toString(mServiceData)
+                + ", mIncludeTxPowerLevel=" + mIncludeTxPowerLevel + "]";
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        if (mServiceUuids == null) {
+            dest.writeInt(0);
+        } else {
+            dest.writeInt(mServiceUuids.size());
+            dest.writeList(mServiceUuids);
+        }
+
+        dest.writeInt(mManufacturerId);
+        if (mManufacturerSpecificData == null) {
+            dest.writeInt(0);
+        } else {
+            dest.writeInt(mManufacturerSpecificData.length);
+            dest.writeByteArray(mManufacturerSpecificData);
+        }
+
+        if (mServiceDataUuid == null) {
+            dest.writeInt(0);
+        } else {
+            dest.writeInt(1);
+            dest.writeParcelable(mServiceDataUuid, flags);
+            if (mServiceData == null) {
+                dest.writeInt(0);
+            } else {
+                dest.writeInt(mServiceData.length);
+                dest.writeByteArray(mServiceData);
+            }
+        }
+        dest.writeByte((byte) (getIncludeTxPowerLevel() ? 1 : 0));
+    }
+
+    public static final Parcelable.Creator<AdvertisementData> CREATOR =
+            new Creator<AdvertisementData>() {
+            @Override
+                public AdvertisementData[] newArray(int size) {
+                    return new AdvertisementData[size];
+                }
+
+            @Override
+                public AdvertisementData createFromParcel(Parcel in) {
+                    Builder builder = new Builder();
+                    if (in.readInt() > 0) {
+                        List<ParcelUuid> uuids = new ArrayList<ParcelUuid>();
+                        in.readList(uuids, ParcelUuid.class.getClassLoader());
+                        builder.setServiceUuids(uuids);
+                    }
+                    int manufacturerId = in.readInt();
+                    int manufacturerDataLength = in.readInt();
+                    if (manufacturerDataLength > 0) {
+                        byte[] manufacturerData = new byte[manufacturerDataLength];
+                        in.readByteArray(manufacturerData);
+                        builder.setManufacturerData(manufacturerId, manufacturerData);
+                    }
+                    if (in.readInt() == 1) {
+                        ParcelUuid serviceDataUuid = in.readParcelable(
+                                ParcelUuid.class.getClassLoader());
+                        int serviceDataLength = in.readInt();
+                        if (serviceDataLength > 0) {
+                            byte[] serviceData = new byte[serviceDataLength];
+                            in.readByteArray(serviceData);
+                            builder.setServiceData(serviceDataUuid, serviceData);
+                        }
+                    }
+                    builder.setIncludeTxPowerLevel(in.readByte() == 1);
+                    return builder.build();
+                }
+            };
+
+    /**
+     * Builder for {@link AdvertisementData}.
+     */
+    public static final class Builder {
+        private static final int MAX_ADVERTISING_DATA_BYTES = 31;
+        // Each fields need one byte for field length and another byte for field type.
+        private static final int OVERHEAD_BYTES_PER_FIELD = 2;
+        // Flags field will be set by system.
+        private static final int FLAGS_FIELD_BYTES = 3;
+
+        @Nullable
+        private List<ParcelUuid> mServiceUuids;
+        private boolean mIncludeTxPowerLevel;
+        private int mManufacturerId;
+        @Nullable
+        private byte[] mManufacturerSpecificData;
+        @Nullable
+        private ParcelUuid mServiceDataUuid;
+        @Nullable
+        private byte[] mServiceData;
+
+        /**
+         * Set the service uuids. Note the corresponding bluetooth Gatt services need to be already
+         * added on the device before start BLE advertising.
+         *
+         * @param serviceUuids Service uuids to be advertised, could be 16-bit, 32-bit or 128-bit
+         *            uuids.
+         * @throws IllegalArgumentException If the {@code serviceUuids} are null.
+         */
+        public Builder setServiceUuids(List<ParcelUuid> serviceUuids) {
+            if (serviceUuids == null) {
+                throw new IllegalArgumentException("serivceUuids are null");
+            }
+            mServiceUuids = serviceUuids;
+            return this;
+        }
+
+        /**
+         * Add service data to advertisement.
+         *
+         * @param serviceDataUuid A 16 bit uuid of the service data
+         * @param serviceData Service data - the first two bytes of the service data are the service
+         *            data uuid.
+         * @throws IllegalArgumentException If the {@code serviceDataUuid} or {@code serviceData} is
+         *             empty.
+         */
+        public Builder setServiceData(ParcelUuid serviceDataUuid, byte[] serviceData) {
+            if (serviceDataUuid == null || serviceData == null) {
+                throw new IllegalArgumentException(
+                        "serviceDataUuid or serviceDataUuid is null");
+            }
+            mServiceDataUuid = serviceDataUuid;
+            mServiceData = serviceData;
+            return this;
+        }
+
+        /**
+         * Set manufacturer id and data. See <a
+         * href="https://www.bluetooth.org/en-us/specification/assigned-numbers/company-identifiers">assigned
+         * manufacturer identifies</a> for the existing company identifiers.
+         *
+         * @param manufacturerId Manufacturer id assigned by Bluetooth SIG.
+         * @param manufacturerSpecificData Manufacturer specific data - the first two bytes of the
+         *            manufacturer specific data are the manufacturer id.
+         * @throws IllegalArgumentException If the {@code manufacturerId} is negative or
+         *             {@code manufacturerSpecificData} is null.
+         */
+        public Builder setManufacturerData(int manufacturerId, byte[] manufacturerSpecificData) {
+            if (manufacturerId < 0) {
+                throw new IllegalArgumentException(
+                        "invalid manufacturerId - " + manufacturerId);
+            }
+            if (manufacturerSpecificData == null) {
+                throw new IllegalArgumentException("manufacturerSpecificData is null");
+            }
+            mManufacturerId = manufacturerId;
+            mManufacturerSpecificData = manufacturerSpecificData;
+            return this;
+        }
+
+        /**
+         * Whether the transmission power level should be included in the advertising packet.
+         */
+        public Builder setIncludeTxPowerLevel(boolean includeTxPowerLevel) {
+            mIncludeTxPowerLevel = includeTxPowerLevel;
+            return this;
+        }
+
+        /**
+         * Build the {@link AdvertisementData}.
+         *
+         * @throws IllegalArgumentException If the data size is larger than 31 bytes.
+         */
+        public AdvertisementData build() {
+            if (totalBytes() > MAX_ADVERTISING_DATA_BYTES) {
+                throw new IllegalArgumentException(
+                        "advertisement data size is larger than 31 bytes");
+            }
+            return new AdvertisementData(mServiceUuids,
+                    mServiceDataUuid,
+                    mServiceData, mManufacturerId, mManufacturerSpecificData,
+                    mIncludeTxPowerLevel);
+        }
+
+        // Compute the size of the advertisement data.
+        private int totalBytes() {
+            int size = FLAGS_FIELD_BYTES; // flags field is always set.
+            if (mServiceUuids != null) {
+                int num16BitUuids = 0;
+                int num32BitUuids = 0;
+                int num128BitUuids = 0;
+                for (ParcelUuid uuid : mServiceUuids) {
+                    if (BluetoothUuid.is16BitUuid(uuid)) {
+                        ++num16BitUuids;
+                    } else if (BluetoothUuid.is32BitUuid(uuid)) {
+                        ++num32BitUuids;
+                    } else {
+                        ++num128BitUuids;
+                    }
+                }
+                // 16 bit service uuids are grouped into one field when doing advertising.
+                if (num16BitUuids != 0) {
+                    size += OVERHEAD_BYTES_PER_FIELD +
+                            num16BitUuids * BluetoothUuid.UUID_BYTES_16_BIT;
+                }
+                // 32 bit service uuids are grouped into one field when doing advertising.
+                if (num32BitUuids != 0) {
+                    size += OVERHEAD_BYTES_PER_FIELD +
+                            num32BitUuids * BluetoothUuid.UUID_BYTES_32_BIT;
+                }
+                // 128 bit service uuids are grouped into one field when doing advertising.
+                if (num128BitUuids != 0) {
+                    size += OVERHEAD_BYTES_PER_FIELD +
+                            num128BitUuids * BluetoothUuid.UUID_BYTES_128_BIT;
+                }
+            }
+            if (mServiceData != null) {
+                size += OVERHEAD_BYTES_PER_FIELD + mServiceData.length;
+            }
+            if (mManufacturerSpecificData != null) {
+                size += OVERHEAD_BYTES_PER_FIELD + mManufacturerSpecificData.length;
+            }
+            if (mIncludeTxPowerLevel) {
+                size += OVERHEAD_BYTES_PER_FIELD + 1; // tx power level value is one byte.
+            }
+            return size;
+        }
+    }
+}
diff --git a/core/java/android/bluetooth/le/BluetoothLeAdvertiser.java b/core/java/android/bluetooth/le/BluetoothLeAdvertiser.java
new file mode 100644
index 0000000..ed43407
--- /dev/null
+++ b/core/java/android/bluetooth/le/BluetoothLeAdvertiser.java
@@ -0,0 +1,368 @@
+/*
+ * 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.bluetooth.le;
+
+import android.bluetooth.BluetoothAdapter;
+import android.bluetooth.BluetoothGatt;
+import android.bluetooth.IBluetoothGatt;
+import android.bluetooth.IBluetoothGattCallback;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.ParcelUuid;
+import android.os.RemoteException;
+import android.util.Log;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.UUID;
+
+/**
+ * This class provides a way to perform Bluetooth LE advertise operations, such as start and stop
+ * advertising. An advertiser can broadcast up to 31 bytes of advertisement data represented by
+ * {@link AdvertisementData}.
+ * <p>
+ * To get an instance of {@link BluetoothLeAdvertiser}, call the
+ * {@link BluetoothAdapter#getBluetoothLeAdvertiser()} method.
+ * <p>
+ * Note most of the methods here require {@link android.Manifest.permission#BLUETOOTH_ADMIN}
+ * permission.
+ *
+ * @see AdvertisementData
+ */
+public final class BluetoothLeAdvertiser {
+
+    private static final String TAG = "BluetoothLeAdvertiser";
+
+    private final IBluetoothGatt mBluetoothGatt;
+    private final Handler mHandler;
+    private final Map<AdvertiseCallback, AdvertiseCallbackWrapper>
+            mLeAdvertisers = new HashMap<AdvertiseCallback, AdvertiseCallbackWrapper>();
+
+    /**
+     * Use BluetoothAdapter.getLeAdvertiser() instead.
+     *
+     * @param bluetoothGatt
+     * @hide
+     */
+    public BluetoothLeAdvertiser(IBluetoothGatt bluetoothGatt) {
+        mBluetoothGatt = bluetoothGatt;
+        mHandler = new Handler(Looper.getMainLooper());
+    }
+
+    /**
+     * Start Bluetooth LE Advertising. The {@code advertiseData} would be broadcasted after the
+     * operation succeeds. Returns immediately, the operation status are delivered through
+     * {@code callback}.
+     * <p>
+     * Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN} permission.
+     *
+     * @param settings Settings for Bluetooth LE advertising.
+     * @param advertiseData Advertisement data to be broadcasted.
+     * @param callback Callback for advertising status.
+     */
+    public void startAdvertising(AdvertiseSettings settings,
+            AdvertisementData advertiseData, final AdvertiseCallback callback) {
+        startAdvertising(settings, advertiseData, null, callback);
+    }
+
+    /**
+     * Start Bluetooth LE Advertising. The {@code advertiseData} would be broadcasted after the
+     * operation succeeds. The {@code scanResponse} would be returned when the scanning device sends
+     * active scan request. Method returns immediately, the operation status are delivered through
+     * {@code callback}.
+     * <p>
+     * Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN}
+     *
+     * @param settings Settings for Bluetooth LE advertising.
+     * @param advertiseData Advertisement data to be advertised in advertisement packet.
+     * @param scanResponse Scan response associated with the advertisement data.
+     * @param callback Callback for advertising status.
+     */
+    public void startAdvertising(AdvertiseSettings settings,
+            AdvertisementData advertiseData, AdvertisementData scanResponse,
+            final AdvertiseCallback callback) {
+        if (callback == null) {
+            throw new IllegalArgumentException("callback cannot be null");
+        }
+        if (mLeAdvertisers.containsKey(callback)) {
+            postCallbackFailure(callback, AdvertiseCallback.ADVERTISE_FAILED_ALREADY_STARTED);
+            return;
+        }
+        AdvertiseCallbackWrapper wrapper = new AdvertiseCallbackWrapper(callback, advertiseData,
+                scanResponse, settings, mBluetoothGatt);
+        UUID uuid = UUID.randomUUID();
+        try {
+            mBluetoothGatt.registerClient(new ParcelUuid(uuid), wrapper);
+            if (wrapper.advertiseStarted()) {
+                mLeAdvertisers.put(callback, wrapper);
+            }
+        } catch (RemoteException e) {
+            Log.e(TAG, "failed to stop advertising", e);
+        }
+    }
+
+    /**
+     * Stop Bluetooth LE advertising. The {@code callback} must be the same one use in
+     * {@link BluetoothLeAdvertiser#startAdvertising}.
+     * <p>
+     * Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN} permission.
+     *
+     * @param callback {@link AdvertiseCallback} for delivering stopping advertising status.
+     */
+    public void stopAdvertising(final AdvertiseCallback callback) {
+        if (callback == null) {
+            throw new IllegalArgumentException("callback cannot be null");
+        }
+        AdvertiseCallbackWrapper wrapper = mLeAdvertisers.get(callback);
+        if (wrapper == null) {
+            postCallbackFailure(callback, AdvertiseCallback.ADVERTISE_FAILED_NOT_STARTED);
+            return;
+        }
+        try {
+            mBluetoothGatt.stopMultiAdvertising(wrapper.mLeHandle);
+            if (wrapper.advertiseStopped()) {
+                mLeAdvertisers.remove(callback);
+            }
+        } catch (RemoteException e) {
+            Log.e(TAG, "failed to stop advertising", e);
+        }
+    }
+
+    /**
+     * Bluetooth GATT interface callbacks for advertising.
+     */
+    private static class AdvertiseCallbackWrapper extends IBluetoothGattCallback.Stub {
+        private static final int LE_CALLBACK_TIMEOUT_MILLIS = 2000;
+        private final AdvertiseCallback mAdvertiseCallback;
+        private final AdvertisementData mAdvertisement;
+        private final AdvertisementData mScanResponse;
+        private final AdvertiseSettings mSettings;
+        private final IBluetoothGatt mBluetoothGatt;
+
+        // mLeHandle 0: not registered
+        // -1: scan stopped
+        // >0: registered and scan started
+        private int mLeHandle;
+        private boolean isAdvertising = false;
+
+        public AdvertiseCallbackWrapper(AdvertiseCallback advertiseCallback,
+                AdvertisementData advertiseData, AdvertisementData scanResponse,
+                AdvertiseSettings settings,
+                IBluetoothGatt bluetoothGatt) {
+            mAdvertiseCallback = advertiseCallback;
+            mAdvertisement = advertiseData;
+            mScanResponse = scanResponse;
+            mSettings = settings;
+            mBluetoothGatt = bluetoothGatt;
+            mLeHandle = 0;
+        }
+
+        public boolean advertiseStarted() {
+            boolean started = false;
+            synchronized (this) {
+                if (mLeHandle == -1) {
+                    return false;
+                }
+                try {
+                    wait(LE_CALLBACK_TIMEOUT_MILLIS);
+                } catch (InterruptedException e) {
+                    Log.e(TAG, "Callback reg wait interrupted: ", e);
+                }
+                started = (mLeHandle > 0 && isAdvertising);
+            }
+            return started;
+        }
+
+        public boolean advertiseStopped() {
+            synchronized (this) {
+                try {
+                    wait(LE_CALLBACK_TIMEOUT_MILLIS);
+                } catch (InterruptedException e) {
+                    Log.e(TAG, "Callback reg wait interrupted: " + e);
+                }
+                return !isAdvertising;
+            }
+        }
+
+        /**
+         * Application interface registered - app is ready to go
+         */
+        @Override
+        public void onClientRegistered(int status, int clientIf) {
+            Log.d(TAG, "onClientRegistered() - status=" + status + " clientIf=" + clientIf);
+            synchronized (this) {
+                if (status == BluetoothGatt.GATT_SUCCESS) {
+                    mLeHandle = clientIf;
+                    try {
+                        mBluetoothGatt.startMultiAdvertising(mLeHandle, mAdvertisement,
+                                mScanResponse, mSettings);
+                    } catch (RemoteException e) {
+                        Log.e(TAG, "fail to start le advertise: " + e);
+                        mLeHandle = -1;
+                        notifyAll();
+                    } catch (Exception e) {
+                        Log.e(TAG, "fail to start advertise: " + e.getStackTrace());
+                    }
+                } else {
+                    // registration failed
+                    mLeHandle = -1;
+                    notifyAll();
+                }
+            }
+        }
+
+        @Override
+        public void onClientConnectionState(int status, int clientIf,
+                boolean connected, String address) {
+            // no op
+        }
+
+        @Override
+        public void onScanResult(String address, int rssi, byte[] advData) {
+            // no op
+        }
+
+        @Override
+        public void onGetService(String address, int srvcType,
+                int srvcInstId, ParcelUuid srvcUuid) {
+            // no op
+        }
+
+        @Override
+        public void onGetIncludedService(String address, int srvcType,
+                int srvcInstId, ParcelUuid srvcUuid,
+                int inclSrvcType, int inclSrvcInstId,
+                ParcelUuid inclSrvcUuid) {
+            // no op
+        }
+
+        @Override
+        public void onGetCharacteristic(String address, int srvcType,
+                int srvcInstId, ParcelUuid srvcUuid,
+                int charInstId, ParcelUuid charUuid,
+                int charProps) {
+            // no op
+        }
+
+        @Override
+        public void onGetDescriptor(String address, int srvcType,
+                int srvcInstId, ParcelUuid srvcUuid,
+                int charInstId, ParcelUuid charUuid,
+                int descInstId, ParcelUuid descUuid) {
+            // no op
+        }
+
+        @Override
+        public void onSearchComplete(String address, int status) {
+            // no op
+        }
+
+        @Override
+        public void onCharacteristicRead(String address, int status, int srvcType,
+                int srvcInstId, ParcelUuid srvcUuid,
+                int charInstId, ParcelUuid charUuid, byte[] value) {
+            // no op
+        }
+
+        @Override
+        public void onCharacteristicWrite(String address, int status, int srvcType,
+                int srvcInstId, ParcelUuid srvcUuid,
+                int charInstId, ParcelUuid charUuid) {
+            // no op
+        }
+
+        @Override
+        public void onNotify(String address, int srvcType,
+                int srvcInstId, ParcelUuid srvcUuid,
+                int charInstId, ParcelUuid charUuid,
+                byte[] value) {
+            // no op
+        }
+
+        @Override
+        public void onDescriptorRead(String address, int status, int srvcType,
+                int srvcInstId, ParcelUuid srvcUuid,
+                int charInstId, ParcelUuid charUuid,
+                int descInstId, ParcelUuid descrUuid, byte[] value) {
+            // no op
+        }
+
+        @Override
+        public void onDescriptorWrite(String address, int status, int srvcType,
+                int srvcInstId, ParcelUuid srvcUuid,
+                int charInstId, ParcelUuid charUuid,
+                int descInstId, ParcelUuid descrUuid) {
+            // no op
+        }
+
+        @Override
+        public void onExecuteWrite(String address, int status) {
+            // no op
+        }
+
+        @Override
+        public void onReadRemoteRssi(String address, int rssi, int status) {
+            // no op
+        }
+
+        @Override
+        public void onAdvertiseStateChange(int advertiseState, int status) {
+            // no op
+        }
+
+        @Override
+        public void onMultiAdvertiseCallback(int status) {
+            synchronized (this) {
+                if (status == 0) {
+                    isAdvertising = !isAdvertising;
+                    if (!isAdvertising) {
+                        try {
+                            mBluetoothGatt.unregisterClient(mLeHandle);
+                            mLeHandle = -1;
+                        } catch (RemoteException e) {
+                            Log.e(TAG, "remote exception when unregistering", e);
+                        }
+                    }
+                    mAdvertiseCallback.onSuccess(null);
+                } else {
+                    mAdvertiseCallback.onFailure(status);
+                }
+                notifyAll();
+            }
+
+        }
+
+        /**
+         * Callback reporting LE ATT MTU.
+         *
+         * @hide
+         */
+        @Override
+        public void onConfigureMTU(String address, int mtu, int status) {
+            // no op
+        }
+    }
+
+    private void postCallbackFailure(final AdvertiseCallback callback, final int error) {
+        mHandler.post(new Runnable() {
+                @Override
+            public void run() {
+                callback.onFailure(error);
+            }
+        });
+    }
+}
diff --git a/core/java/android/bluetooth/le/BluetoothLeScanner.java b/core/java/android/bluetooth/le/BluetoothLeScanner.java
new file mode 100644
index 0000000..4c6346c
--- /dev/null
+++ b/core/java/android/bluetooth/le/BluetoothLeScanner.java
@@ -0,0 +1,371 @@
+/*
+ * 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.bluetooth.le;
+
+import android.bluetooth.BluetoothAdapter;
+import android.bluetooth.BluetoothDevice;
+import android.bluetooth.BluetoothGatt;
+import android.bluetooth.IBluetoothGatt;
+import android.bluetooth.IBluetoothGattCallback;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.ParcelUuid;
+import android.os.RemoteException;
+import android.os.SystemClock;
+import android.util.Log;
+
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.UUID;
+
+/**
+ * This class provides methods to perform scan related operations for Bluetooth LE devices. An
+ * application can scan for a particular type of BLE devices using {@link ScanFilter}. It can also
+ * request different types of callbacks for delivering the result.
+ * <p>
+ * Use {@link BluetoothAdapter#getBluetoothLeScanner()} to get an instance of
+ * {@link BluetoothLeScanner}.
+ * <p>
+ * Note most of the scan methods here require {@link android.Manifest.permission#BLUETOOTH_ADMIN}
+ * permission.
+ *
+ * @see ScanFilter
+ */
+public final class BluetoothLeScanner {
+
+    private static final String TAG = "BluetoothLeScanner";
+    private static final boolean DBG = true;
+
+    private final IBluetoothGatt mBluetoothGatt;
+    private final Handler mHandler;
+    private final Map<ScanCallback, BleScanCallbackWrapper> mLeScanClients;
+
+    /**
+     * @hide
+     */
+    public BluetoothLeScanner(IBluetoothGatt bluetoothGatt) {
+        mBluetoothGatt = bluetoothGatt;
+        mHandler = new Handler(Looper.getMainLooper());
+        mLeScanClients = new HashMap<ScanCallback, BleScanCallbackWrapper>();
+    }
+
+    /**
+     * Start Bluetooth LE scan. The scan results will be delivered through {@code callback}.
+     * <p>
+     * Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN} permission.
+     *
+     * @param filters {@link ScanFilter}s for finding exact BLE devices.
+     * @param settings Settings for ble scan.
+     * @param callback Callback when scan results are delivered.
+     * @throws IllegalArgumentException If {@code settings} or {@code callback} is null.
+     */
+    public void startScan(List<ScanFilter> filters, ScanSettings settings,
+            final ScanCallback callback) {
+        if (settings == null || callback == null) {
+            throw new IllegalArgumentException("settings or callback is null");
+        }
+        synchronized (mLeScanClients) {
+            if (mLeScanClients.containsKey(callback)) {
+                postCallbackError(callback, ScanCallback.SCAN_FAILED_ALREADY_STARTED);
+                return;
+            }
+            BleScanCallbackWrapper wrapper = new BleScanCallbackWrapper(mBluetoothGatt, filters,
+                    settings, callback);
+            try {
+                UUID uuid = UUID.randomUUID();
+                mBluetoothGatt.registerClient(new ParcelUuid(uuid), wrapper);
+                if (wrapper.scanStarted()) {
+                    mLeScanClients.put(callback, wrapper);
+                } else {
+                    postCallbackError(callback,
+                            ScanCallback.SCAN_FAILED_APPLICATION_REGISTRATION_FAILED);
+                    return;
+                }
+            } catch (RemoteException e) {
+                Log.e(TAG, "GATT service exception when starting scan", e);
+                postCallbackError(callback, ScanCallback.SCAN_FAILED_GATT_SERVICE_FAILURE);
+            }
+        }
+    }
+
+    /**
+     * Stops an ongoing Bluetooth LE scan.
+     * <p>
+     * Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN} permission.
+     *
+     * @param callback
+     */
+    public void stopScan(ScanCallback callback) {
+        synchronized (mLeScanClients) {
+            BleScanCallbackWrapper wrapper = mLeScanClients.remove(callback);
+            if (wrapper == null) {
+                return;
+            }
+            wrapper.stopLeScan();
+        }
+    }
+
+    /**
+     * Returns available storage size for batch scan results. It's recommended not to use batch scan
+     * if available storage size is small (less than 1k bytes, for instance).
+     *
+     * @hide TODO: unhide when batching is supported in stack.
+     */
+    public int getAvailableBatchStorageSizeBytes() {
+        throw new UnsupportedOperationException("not impelemented");
+    }
+
+    /**
+     * Poll scan results from bluetooth controller. This will return Bluetooth LE scan results
+     * batched on bluetooth controller.
+     *
+     * @param callback Callback of the Bluetooth LE Scan, it has to be the same instance as the one
+     *            used to start scan.
+     * @param flush Whether to flush the batch scan buffer. Note the other batch scan clients will
+     *            get batch scan callback if the batch scan buffer is flushed.
+     * @return Batch Scan results.
+     * @hide TODO: unhide when batching is supported in stack.
+     */
+    public List<ScanResult> getBatchScanResults(ScanCallback callback, boolean flush) {
+        throw new UnsupportedOperationException("not impelemented");
+    }
+
+    /**
+     * Bluetooth GATT interface callbacks
+     */
+    private static class BleScanCallbackWrapper extends IBluetoothGattCallback.Stub {
+        private static final int REGISTRATION_CALLBACK_TIMEOUT_SECONDS = 5;
+
+        private final ScanCallback mScanCallback;
+        private final List<ScanFilter> mFilters;
+        private ScanSettings mSettings;
+        private IBluetoothGatt mBluetoothGatt;
+
+        // mLeHandle 0: not registered
+        // -1: scan stopped
+        // > 0: registered and scan started
+        private int mLeHandle;
+
+        public BleScanCallbackWrapper(IBluetoothGatt bluetoothGatt,
+                List<ScanFilter> filters, ScanSettings settings,
+                ScanCallback scanCallback) {
+            mBluetoothGatt = bluetoothGatt;
+            mFilters = filters;
+            mSettings = settings;
+            mScanCallback = scanCallback;
+            mLeHandle = 0;
+        }
+
+        public boolean scanStarted() {
+            synchronized (this) {
+                if (mLeHandle == -1) {
+                    return false;
+                }
+                try {
+                    wait(REGISTRATION_CALLBACK_TIMEOUT_SECONDS);
+                } catch (InterruptedException e) {
+                    Log.e(TAG, "Callback reg wait interrupted: " + e);
+                }
+            }
+            return mLeHandle > 0;
+        }
+
+        public void stopLeScan() {
+            synchronized (this) {
+                if (mLeHandle <= 0) {
+                    Log.e(TAG, "Error state, mLeHandle: " + mLeHandle);
+                    return;
+                }
+                try {
+                    mBluetoothGatt.stopScan(mLeHandle, false);
+                    mBluetoothGatt.unregisterClient(mLeHandle);
+                } catch (RemoteException e) {
+                    Log.e(TAG, "Failed to stop scan and unregister" + e);
+                }
+                mLeHandle = -1;
+                notifyAll();
+            }
+        }
+
+        /**
+         * Application interface registered - app is ready to go
+         */
+        @Override
+        public void onClientRegistered(int status, int clientIf) {
+            Log.d(TAG, "onClientRegistered() - status=" + status +
+                    " clientIf=" + clientIf);
+
+            synchronized (this) {
+                if (mLeHandle == -1) {
+                    if (DBG)
+                        Log.d(TAG, "onClientRegistered LE scan canceled");
+                }
+
+                if (status == BluetoothGatt.GATT_SUCCESS) {
+                    mLeHandle = clientIf;
+                    try {
+                        mBluetoothGatt.startScanWithFilters(mLeHandle, false, mSettings, mFilters);
+                    } catch (RemoteException e) {
+                        Log.e(TAG, "fail to start le scan: " + e);
+                        mLeHandle = -1;
+                    }
+                } else {
+                    // registration failed
+                    mLeHandle = -1;
+                }
+                notifyAll();
+            }
+        }
+
+        @Override
+        public void onClientConnectionState(int status, int clientIf,
+                boolean connected, String address) {
+            // no op
+        }
+
+        /**
+         * Callback reporting an LE scan result.
+         *
+         * @hide
+         */
+        @Override
+        public void onScanResult(String address, int rssi, byte[] advData) {
+            if (DBG)
+                Log.d(TAG, "onScanResult() - Device=" + address + " RSSI=" + rssi);
+
+            // Check null in case the scan has been stopped
+            synchronized (this) {
+                if (mLeHandle <= 0)
+                    return;
+            }
+            BluetoothDevice device = BluetoothAdapter.getDefaultAdapter().getRemoteDevice(
+                    address);
+            long scanNanos = SystemClock.elapsedRealtimeNanos();
+            ScanResult result = new ScanResult(device, advData, rssi,
+                    scanNanos);
+            mScanCallback.onAdvertisementUpdate(result);
+        }
+
+        @Override
+        public void onGetService(String address, int srvcType,
+                int srvcInstId, ParcelUuid srvcUuid) {
+            // no op
+        }
+
+        @Override
+        public void onGetIncludedService(String address, int srvcType,
+                int srvcInstId, ParcelUuid srvcUuid,
+                int inclSrvcType, int inclSrvcInstId,
+                ParcelUuid inclSrvcUuid) {
+            // no op
+        }
+
+        @Override
+        public void onGetCharacteristic(String address, int srvcType,
+                int srvcInstId, ParcelUuid srvcUuid,
+                int charInstId, ParcelUuid charUuid,
+                int charProps) {
+            // no op
+        }
+
+        @Override
+        public void onGetDescriptor(String address, int srvcType,
+                int srvcInstId, ParcelUuid srvcUuid,
+                int charInstId, ParcelUuid charUuid,
+                int descInstId, ParcelUuid descUuid) {
+            // no op
+        }
+
+        @Override
+        public void onSearchComplete(String address, int status) {
+            // no op
+        }
+
+        @Override
+        public void onCharacteristicRead(String address, int status, int srvcType,
+                int srvcInstId, ParcelUuid srvcUuid,
+                int charInstId, ParcelUuid charUuid, byte[] value) {
+            // no op
+        }
+
+        @Override
+        public void onCharacteristicWrite(String address, int status, int srvcType,
+                int srvcInstId, ParcelUuid srvcUuid,
+                int charInstId, ParcelUuid charUuid) {
+            // no op
+        }
+
+        @Override
+        public void onNotify(String address, int srvcType,
+                int srvcInstId, ParcelUuid srvcUuid,
+                int charInstId, ParcelUuid charUuid,
+                byte[] value) {
+            // no op
+        }
+
+        @Override
+        public void onDescriptorRead(String address, int status, int srvcType,
+                int srvcInstId, ParcelUuid srvcUuid,
+                int charInstId, ParcelUuid charUuid,
+                int descInstId, ParcelUuid descrUuid, byte[] value) {
+            // no op
+        }
+
+        @Override
+        public void onDescriptorWrite(String address, int status, int srvcType,
+                int srvcInstId, ParcelUuid srvcUuid,
+                int charInstId, ParcelUuid charUuid,
+                int descInstId, ParcelUuid descrUuid) {
+            // no op
+        }
+
+        @Override
+        public void onExecuteWrite(String address, int status) {
+            // no op
+        }
+
+        @Override
+        public void onReadRemoteRssi(String address, int rssi, int status) {
+            // no op
+        }
+
+        @Override
+        public void onAdvertiseStateChange(int advertiseState, int status) {
+            // no op
+        }
+
+        @Override
+        public void onMultiAdvertiseCallback(int status) {
+            // no op
+        }
+
+        @Override
+        public void onConfigureMTU(String address, int mtu, int status) {
+            // no op
+        }
+    }
+
+    private void postCallbackError(final ScanCallback callback, final int errorCode) {
+        mHandler.post(new Runnable() {
+                @Override
+            public void run() {
+                callback.onScanFailed(errorCode);
+            }
+        });
+    }
+}
diff --git a/core/java/android/bluetooth/le/ScanCallback.java b/core/java/android/bluetooth/le/ScanCallback.java
new file mode 100644
index 0000000..50ebf50
--- /dev/null
+++ b/core/java/android/bluetooth/le/ScanCallback.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 android.bluetooth.le;
+
+import java.util.List;
+
+/**
+ * Callback of Bluetooth LE scans. The results of the scans will be delivered through the callbacks.
+ */
+public abstract class ScanCallback {
+
+    /**
+     * Fails to start scan as BLE scan with the same settings is already started by the app.
+     */
+    public static final int SCAN_FAILED_ALREADY_STARTED = 1;
+    /**
+     * Fails to start scan as app cannot be registered.
+     */
+    public static final int SCAN_FAILED_APPLICATION_REGISTRATION_FAILED = 2;
+    /**
+     * Fails to start scan due to gatt service failure.
+     */
+    public static final int SCAN_FAILED_GATT_SERVICE_FAILURE = 3;
+    /**
+     * Fails to start scan due to controller failure.
+     */
+    public static final int SCAN_FAILED_CONTROLLER_FAILURE = 4;
+
+    /**
+     * Callback when a BLE advertisement is found.
+     *
+     * @param result A Bluetooth LE scan result.
+     */
+    public abstract void onAdvertisementUpdate(ScanResult result);
+
+    /**
+     * Callback when the BLE advertisement is found for the first time.
+     *
+     * @param result The Bluetooth LE scan result when the onFound event is triggered.
+     * @hide
+     */
+    public abstract void onAdvertisementFound(ScanResult result);
+
+    /**
+     * Callback when the BLE advertisement was lost. Note a device has to be "found" before it's
+     * lost.
+     *
+     * @param result The Bluetooth scan result that was last found.
+     * @hide
+     */
+    public abstract void onAdvertisementLost(ScanResult result);
+
+    /**
+     * Callback when batch results are delivered.
+     *
+     * @param results List of scan results that are previously scanned.
+     * @hide
+     */
+    public abstract void onBatchScanResults(List<ScanResult> results);
+
+    /**
+     * Callback when scan failed.
+     */
+    public abstract void onScanFailed(int errorCode);
+}
diff --git a/core/java/android/tv/TvInputInfo.aidl b/core/java/android/bluetooth/le/ScanFilter.aidl
similarity index 91%
copy from core/java/android/tv/TvInputInfo.aidl
copy to core/java/android/bluetooth/le/ScanFilter.aidl
index abc4b47..4cecfe6 100644
--- a/core/java/android/tv/TvInputInfo.aidl
+++ b/core/java/android/bluetooth/le/ScanFilter.aidl
@@ -14,6 +14,6 @@
  * limitations under the License.
  */
 
-package android.tv;
+package android.bluetooth.le;
 
-parcelable TvInputInfo;
+parcelable ScanFilter;
diff --git a/core/java/android/bluetooth/BluetoothLeScanFilter.java b/core/java/android/bluetooth/le/ScanFilter.java
similarity index 71%
rename from core/java/android/bluetooth/BluetoothLeScanFilter.java
rename to core/java/android/bluetooth/le/ScanFilter.java
index 2ed85ba..c2e316b 100644
--- a/core/java/android/bluetooth/BluetoothLeScanFilter.java
+++ b/core/java/android/bluetooth/le/ScanFilter.java
@@ -14,11 +14,11 @@
  * limitations under the License.
  */
 
-package android.bluetooth;
+package android.bluetooth.le;
 
 import android.annotation.Nullable;
-import android.bluetooth.BluetoothLeAdvertiseScanData.ScanRecord;
-import android.bluetooth.BluetoothLeScanner.ScanResult;
+import android.bluetooth.BluetoothAdapter;
+import android.bluetooth.BluetoothDevice;
 import android.os.Parcel;
 import android.os.ParcelUuid;
 import android.os.Parcelable;
@@ -29,8 +29,7 @@
 import java.util.UUID;
 
 /**
- * {@link BluetoothLeScanFilter} abstracts different scan filters across Bluetooth Advertisement
- * packet fields.
+ * {@link ScanFilter} abstracts different scan filters across Bluetooth Advertisement packet fields.
  * <p>
  * Current filtering on the following fields are supported:
  * <li>Service UUIDs which identify the bluetooth gatt services running on the device.
@@ -40,10 +39,10 @@
  * <li>Service data which is the data associated with a service.
  * <li>Manufacturer specific data which is the data associated with a particular manufacturer.
  *
- * @see BluetoothLeAdvertiseScanData.ScanRecord
+ * @see ScanRecord
  * @see BluetoothLeScanner
  */
-public final class BluetoothLeScanFilter implements Parcelable {
+public final class ScanFilter implements Parcelable {
 
     @Nullable
     private final String mLocalName;
@@ -70,7 +69,7 @@
     private final int mMinRssi;
     private final int mMaxRssi;
 
-    private BluetoothLeScanFilter(String name, String macAddress, ParcelUuid uuid,
+    private ScanFilter(String name, String macAddress, ParcelUuid uuid,
             ParcelUuid uuidMask, byte[] serviceData, byte[] serviceDataMask,
             int manufacturerId, byte[] manufacturerData, byte[] manufacturerDataMask,
             int minRssi, int maxRssi) {
@@ -105,88 +104,93 @@
         dest.writeInt(mServiceUuid == null ? 0 : 1);
         if (mServiceUuid != null) {
             dest.writeParcelable(mServiceUuid, flags);
-        }
-        dest.writeInt(mServiceUuidMask == null ? 0 : 1);
-        if (mServiceUuidMask != null) {
-            dest.writeParcelable(mServiceUuidMask, flags);
+            dest.writeInt(mServiceUuidMask == null ? 0 : 1);
+            if (mServiceUuidMask != null) {
+                dest.writeParcelable(mServiceUuidMask, flags);
+            }
         }
         dest.writeInt(mServiceData == null ? 0 : mServiceData.length);
         if (mServiceData != null) {
             dest.writeByteArray(mServiceData);
-        }
-        dest.writeInt(mServiceDataMask == null ? 0 : mServiceDataMask.length);
-        if (mServiceDataMask != null) {
-            dest.writeByteArray(mServiceDataMask);
+            dest.writeInt(mServiceDataMask == null ? 0 : mServiceDataMask.length);
+            if (mServiceDataMask != null) {
+                dest.writeByteArray(mServiceDataMask);
+            }
         }
         dest.writeInt(mManufacturerId);
         dest.writeInt(mManufacturerData == null ? 0 : mManufacturerData.length);
         if (mManufacturerData != null) {
             dest.writeByteArray(mManufacturerData);
-        }
-        dest.writeInt(mManufacturerDataMask == null ? 0 : mManufacturerDataMask.length);
-        if (mManufacturerDataMask != null) {
-            dest.writeByteArray(mManufacturerDataMask);
+            dest.writeInt(mManufacturerDataMask == null ? 0 : mManufacturerDataMask.length);
+            if (mManufacturerDataMask != null) {
+                dest.writeByteArray(mManufacturerDataMask);
+            }
         }
         dest.writeInt(mMinRssi);
         dest.writeInt(mMaxRssi);
     }
 
     /**
-     * A {@link android.os.Parcelable.Creator} to create {@link BluetoothLeScanFilter} form parcel.
+     * A {@link android.os.Parcelable.Creator} to create {@link ScanFilter} form parcel.
      */
-    public static final Creator<BluetoothLeScanFilter>
-            CREATOR = new Creator<BluetoothLeScanFilter>() {
+    public static final Creator<ScanFilter>
+            CREATOR = new Creator<ScanFilter>() {
 
                     @Override
-                public BluetoothLeScanFilter[] newArray(int size) {
-                    return new BluetoothLeScanFilter[size];
+                public ScanFilter[] newArray(int size) {
+                    return new ScanFilter[size];
                 }
 
                     @Override
-                public BluetoothLeScanFilter createFromParcel(Parcel in) {
-                    Builder builder = newBuilder();
+                public ScanFilter createFromParcel(Parcel in) {
+                    Builder builder = new Builder();
                     if (in.readInt() == 1) {
-                        builder.name(in.readString());
+                        builder.setName(in.readString());
                     }
                     if (in.readInt() == 1) {
-                        builder.macAddress(in.readString());
+                        builder.setMacAddress(in.readString());
                     }
                     if (in.readInt() == 1) {
                         ParcelUuid uuid = in.readParcelable(ParcelUuid.class.getClassLoader());
-                        builder.serviceUuid(uuid);
+                        builder.setServiceUuid(uuid);
+                        if (in.readInt() == 1) {
+                            ParcelUuid uuidMask = in.readParcelable(
+                                    ParcelUuid.class.getClassLoader());
+                            builder.setServiceUuid(uuid, uuidMask);
+                        }
                     }
-                    if (in.readInt() == 1) {
-                        ParcelUuid uuidMask = in.readParcelable(ParcelUuid.class.getClassLoader());
-                        builder.serviceUuidMask(uuidMask);
-                    }
+
                     int serviceDataLength = in.readInt();
                     if (serviceDataLength > 0) {
                         byte[] serviceData = new byte[serviceDataLength];
                         in.readByteArray(serviceData);
-                        builder.serviceData(serviceData);
+                        builder.setServiceData(serviceData);
+                        int serviceDataMaskLength = in.readInt();
+                        if (serviceDataMaskLength > 0) {
+                            byte[] serviceDataMask = new byte[serviceDataMaskLength];
+                            in.readByteArray(serviceDataMask);
+                            builder.setServiceData(serviceData, serviceDataMask);
+                        }
                     }
-                    int serviceDataMaskLength = in.readInt();
-                    if (serviceDataMaskLength > 0) {
-                        byte[] serviceDataMask = new byte[serviceDataMaskLength];
-                        in.readByteArray(serviceDataMask);
-                        builder.serviceDataMask(serviceDataMask);
-                    }
+
                     int manufacturerId = in.readInt();
                     int manufacturerDataLength = in.readInt();
                     if (manufacturerDataLength > 0) {
                         byte[] manufacturerData = new byte[manufacturerDataLength];
                         in.readByteArray(manufacturerData);
-                        builder.manufacturerData(manufacturerId, manufacturerData);
+                        builder.setManufacturerData(manufacturerId, manufacturerData);
+                        int manufacturerDataMaskLength = in.readInt();
+                        if (manufacturerDataMaskLength > 0) {
+                            byte[] manufacturerDataMask = new byte[manufacturerDataMaskLength];
+                            in.readByteArray(manufacturerDataMask);
+                            builder.setManufacturerData(manufacturerId, manufacturerData,
+                                    manufacturerDataMask);
+                        }
                     }
-                    int manufacturerDataMaskLength = in.readInt();
-                    if (manufacturerDataMaskLength > 0) {
-                        byte[] manufacturerDataMask = new byte[manufacturerDataMaskLength];
-                        in.readByteArray(manufacturerDataMask);
-                        builder.manufacturerDataMask(manufacturerDataMask);
-                    }
+
                     int minRssi = in.readInt();
                     int maxRssi = in.readInt();
-                    builder.rssiRange(minRssi, maxRssi);
+                    builder.setRssiRange(minRssi, maxRssi);
                     return builder.build();
                 }
             };
@@ -199,9 +203,10 @@
         return mLocalName;
     }
 
-    @Nullable /**
-               * Returns the filter set on the service uuid.
-               */
+    /**
+     * Returns the filter set on the service uuid.
+     */
+    @Nullable
     public ParcelUuid getServiceUuid() {
         return mServiceUuid;
     }
@@ -277,7 +282,7 @@
         }
 
         byte[] scanRecordBytes = scanResult.getScanRecord();
-        ScanRecord scanRecord = ScanRecord.getParser().parseFromScanRecord(scanRecordBytes);
+        ScanRecord scanRecord = ScanRecord.parseFromBytes(scanRecordBytes);
 
         // Scan record is null but there exist filters on it.
         if (scanRecord == null
@@ -386,13 +391,13 @@
         if (obj == null || getClass() != obj.getClass()) {
             return false;
         }
-        BluetoothLeScanFilter other = (BluetoothLeScanFilter) obj;
+        ScanFilter other = (ScanFilter) obj;
         return Objects.equals(mLocalName, other.mLocalName) &&
                 Objects.equals(mMacAddress, other.mMacAddress) &&
-                mManufacturerId == other.mManufacturerId &&
+                        mManufacturerId == other.mManufacturerId &&
                 Objects.deepEquals(mManufacturerData, other.mManufacturerData) &&
                 Objects.deepEquals(mManufacturerDataMask, other.mManufacturerDataMask) &&
-                mMinRssi == other.mMinRssi && mMaxRssi == other.mMaxRssi &&
+                        mMinRssi == other.mMinRssi && mMaxRssi == other.mMaxRssi &&
                 Objects.deepEquals(mServiceData, other.mServiceData) &&
                 Objects.deepEquals(mServiceDataMask, other.mServiceDataMask) &&
                 Objects.equals(mServiceUuid, other.mServiceUuid) &&
@@ -400,17 +405,9 @@
     }
 
     /**
-     * Returns the {@link Builder} for {@link BluetoothLeScanFilter}.
+     * Builder class for {@link ScanFilter}.
      */
-    public static Builder newBuilder() {
-        return new Builder();
-    }
-
-    /**
-     * Builder class for {@link BluetoothLeScanFilter}. Use
-     * {@link BluetoothLeScanFilter#newBuilder()} to get an instance of the {@link Builder}.
-     */
-    public static class Builder {
+    public static final class Builder {
 
         private String mLocalName;
         private String mMacAddress;
@@ -428,27 +425,23 @@
         private int mMinRssi = Integer.MIN_VALUE;
         private int mMaxRssi = Integer.MAX_VALUE;
 
-        // Private constructor, use BluetoothLeScanFilter.newBuilder instead.
-        private Builder() {
-        }
-
         /**
-         * Set filtering on local name.
+         * Set filter on local name.
          */
-        public Builder name(String localName) {
+        public Builder setName(String localName) {
             mLocalName = localName;
             return this;
         }
 
         /**
-         * Set filtering on device mac address.
+         * Set filter on device mac address.
          *
          * @param macAddress The device mac address for the filter. It needs to be in the format of
          *            "01:02:03:AB:CD:EF". The mac address can be validated using
          *            {@link BluetoothAdapter#checkBluetoothAddress}.
          * @throws IllegalArgumentException If the {@code macAddress} is invalid.
          */
-        public Builder macAddress(String macAddress) {
+        public Builder setMacAddress(String macAddress) {
             if (macAddress != null && !BluetoothAdapter.checkBluetoothAddress(macAddress)) {
                 throw new IllegalArgumentException("invalid mac address " + macAddress);
             }
@@ -457,92 +450,51 @@
         }
 
         /**
-         * Set filtering on service uuid.
+         * Set filter on service uuid.
          */
-        public Builder serviceUuid(ParcelUuid serviceUuid) {
+        public Builder setServiceUuid(ParcelUuid serviceUuid) {
             mServiceUuid = serviceUuid;
+            mUuidMask = null; // clear uuid mask
             return this;
         }
 
         /**
-         * Set partial uuid filter. The {@code uuidMask} is the bit mask for the {@code uuid} set
-         * through {@link #serviceUuid(ParcelUuid)} method. Set any bit in the mask to 1 to indicate
-         * a match is needed for the bit in {@code serviceUuid}, and 0 to ignore that bit.
-         * <p>
-         * The length of {@code uuidMask} must be the same as {@code serviceUuid}.
+         * Set filter on partial service uuid. The {@code uuidMask} is the bit mask for the
+         * {@code serviceUuid}. Set any bit in the mask to 1 to indicate a match is needed for the
+         * bit in {@code serviceUuid}, and 0 to ignore that bit.
+         *
+         * @throws IllegalArgumentException If {@code serviceUuid} is {@code null} but
+         *             {@code uuidMask} is not {@code null}.
          */
-        public Builder serviceUuidMask(ParcelUuid uuidMask) {
+        public Builder setServiceUuid(ParcelUuid serviceUuid, ParcelUuid uuidMask) {
+            if (mUuidMask != null && mServiceUuid == null) {
+                throw new IllegalArgumentException("uuid is null while uuidMask is not null!");
+            }
+            mServiceUuid = serviceUuid;
             mUuidMask = uuidMask;
             return this;
         }
 
         /**
-         * Set service data filter.
+         * Set filtering on service data.
          */
-        public Builder serviceData(byte[] serviceData) {
+        public Builder setServiceData(byte[] serviceData) {
             mServiceData = serviceData;
+            mServiceDataMask = null; // clear service data mask
             return this;
         }
 
         /**
-         * Set partial service data filter bit mask. For any bit in the mask, set it to 1 if it
-         * needs to match the one in service data, otherwise set it to 0 to ignore that bit.
+         * Set partial filter on service data. For any bit in the mask, set it to 1 if it needs to
+         * match the one in service data, otherwise set it to 0 to ignore that bit.
          * <p>
-         * The {@code serviceDataMask} must have the same length of the {@code serviceData} set
-         * through {@link #serviceData(byte[])}.
-         */
-        public Builder serviceDataMask(byte[] serviceDataMask) {
-            mServiceDataMask = serviceDataMask;
-            return this;
-        }
-
-        /**
-         * Set manufacturerId and manufacturerData. A negative manufacturerId is considered as
-         * invalid id.
-         * <p>
-         * Note the first two bytes of the {@code manufacturerData} is the manufacturerId.
-         */
-        public Builder manufacturerData(int manufacturerId, byte[] manufacturerData) {
-            if (manufacturerData != null && manufacturerId < 0) {
-                throw new IllegalArgumentException("invalid manufacture id");
-            }
-            mManufacturerId = manufacturerId;
-            mManufacturerData = manufacturerData;
-            return this;
-        }
-
-        /**
-         * Set partial manufacture data filter bit mask. For any bit in the mask, set it the 1 if it
-         * needs to match the one in manufacturer data, otherwise set it to 0.
-         * <p>
-         * The {@code manufacturerDataMask} must have the same length of {@code manufacturerData}
-         * set through {@link #manufacturerData(int, byte[])}.
-         */
-        public Builder manufacturerDataMask(byte[] manufacturerDataMask) {
-            mManufacturerDataMask = manufacturerDataMask;
-            return this;
-        }
-
-        /**
-         * Set the desired rssi range for the filter. A scan result with rssi in the range of
-         * [minRssi, maxRssi] will be consider as a match.
-         */
-        public Builder rssiRange(int minRssi, int maxRssi) {
-            mMinRssi = minRssi;
-            mMaxRssi = maxRssi;
-            return this;
-        }
-
-        /**
-         * Build {@link BluetoothLeScanFilter}.
+         * The {@code serviceDataMask} must have the same length of the {@code serviceData}.
          *
-         * @throws IllegalArgumentException If the filter cannot be built.
+         * @throws IllegalArgumentException If {@code serviceDataMask} is {@code null} while
+         *             {@code serviceData} is not or {@code serviceDataMask} and {@code serviceData}
+         *             has different length.
          */
-        public BluetoothLeScanFilter build() {
-            if (mUuidMask != null && mServiceUuid == null) {
-                throw new IllegalArgumentException("uuid is null while uuidMask is not null!");
-            }
-
+        public Builder setServiceData(byte[] serviceData, byte[] serviceDataMask) {
             if (mServiceDataMask != null) {
                 if (mServiceData == null) {
                     throw new IllegalArgumentException(
@@ -555,7 +507,44 @@
                             "size mismatch for service data and service data mask");
                 }
             }
+            mServiceData = serviceData;
+            mServiceDataMask = serviceDataMask;
+            return this;
+        }
 
+        /**
+         * Set filter on on manufacturerData. A negative manufacturerId is considered as invalid id.
+         * <p>
+         * Note the first two bytes of the {@code manufacturerData} is the manufacturerId.
+         *
+         * @throws IllegalArgumentException If the {@code manufacturerId} is invalid.
+         */
+        public Builder setManufacturerData(int manufacturerId, byte[] manufacturerData) {
+            if (manufacturerData != null && manufacturerId < 0) {
+                throw new IllegalArgumentException("invalid manufacture id");
+            }
+            mManufacturerId = manufacturerId;
+            mManufacturerData = manufacturerData;
+            mManufacturerDataMask = null; // clear manufacturer data mask
+            return this;
+        }
+
+        /**
+         * Set filter on partial manufacture data. For any bit in the mask, set it the 1 if it
+         * needs to match the one in manufacturer data, otherwise set it to 0.
+         * <p>
+         * The {@code manufacturerDataMask} must have the same length of {@code manufacturerData}.
+         *
+         * @throws IllegalArgumentException If the {@code manufacturerId} is invalid, or
+         *             {@code manufacturerData} is null while {@code manufacturerDataMask} is not,
+         *             or {@code manufacturerData} and {@code manufacturerDataMask} have different
+         *             length.
+         */
+        public Builder setManufacturerData(int manufacturerId, byte[] manufacturerData,
+                byte[] manufacturerDataMask) {
+            if (manufacturerData != null && manufacturerId < 0) {
+                throw new IllegalArgumentException("invalid manufacture id");
+            }
             if (mManufacturerDataMask != null) {
                 if (mManufacturerData == null) {
                     throw new IllegalArgumentException(
@@ -568,7 +557,29 @@
                             "size mismatch for manufacturerData and manufacturerDataMask");
                 }
             }
-            return new BluetoothLeScanFilter(mLocalName, mMacAddress,
+            mManufacturerId = manufacturerId;
+            mManufacturerData = manufacturerData;
+            mManufacturerDataMask = manufacturerDataMask;
+            return this;
+        }
+
+        /**
+         * Set the desired rssi range for the filter. A scan result with rssi in the range of
+         * [minRssi, maxRssi] will be consider as a match.
+         */
+        public Builder setRssiRange(int minRssi, int maxRssi) {
+            mMinRssi = minRssi;
+            mMaxRssi = maxRssi;
+            return this;
+        }
+
+        /**
+         * Build {@link ScanFilter}.
+         *
+         * @throws IllegalArgumentException If the filter cannot be built.
+         */
+        public ScanFilter build() {
+            return new ScanFilter(mLocalName, mMacAddress,
                     mServiceUuid, mUuidMask,
                     mServiceData, mServiceDataMask,
                     mManufacturerId, mManufacturerData, mManufacturerDataMask, mMinRssi, mMaxRssi);
diff --git a/core/java/android/bluetooth/le/ScanRecord.java b/core/java/android/bluetooth/le/ScanRecord.java
new file mode 100644
index 0000000..bd7304b
--- /dev/null
+++ b/core/java/android/bluetooth/le/ScanRecord.java
@@ -0,0 +1,278 @@
+/*
+ * 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.bluetooth.le;
+
+import android.annotation.Nullable;
+import android.bluetooth.BluetoothUuid;
+import android.os.ParcelUuid;
+import android.util.Log;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+/**
+ * Represents a scan record from Bluetooth LE scan.
+ */
+public final class ScanRecord {
+
+    private static final String TAG = "ScanRecord";
+
+    // The following data type values are assigned by Bluetooth SIG.
+    // For more details refer to Bluetooth 4.1 specification, Volume 3, Part C, Section 18.
+    private static final int DATA_TYPE_FLAGS = 0x01;
+    private static final int DATA_TYPE_SERVICE_UUIDS_16_BIT_PARTIAL = 0x02;
+    private static final int DATA_TYPE_SERVICE_UUIDS_16_BIT_COMPLETE = 0x03;
+    private static final int DATA_TYPE_SERVICE_UUIDS_32_BIT_PARTIAL = 0x04;
+    private static final int DATA_TYPE_SERVICE_UUIDS_32_BIT_COMPLETE = 0x05;
+    private static final int DATA_TYPE_SERVICE_UUIDS_128_BIT_PARTIAL = 0x06;
+    private static final int DATA_TYPE_SERVICE_UUIDS_128_BIT_COMPLETE = 0x07;
+    private static final int DATA_TYPE_LOCAL_NAME_SHORT = 0x08;
+    private static final int DATA_TYPE_LOCAL_NAME_COMPLETE = 0x09;
+    private static final int DATA_TYPE_TX_POWER_LEVEL = 0x0A;
+    private static final int DATA_TYPE_SERVICE_DATA = 0x16;
+    private static final int DATA_TYPE_MANUFACTURER_SPECIFIC_DATA = 0xFF;
+
+    // Flags of the advertising data.
+    private final int mAdvertiseFlags;
+
+    @Nullable
+    private final List<ParcelUuid> mServiceUuids;
+
+    private final int mManufacturerId;
+    @Nullable
+    private final byte[] mManufacturerSpecificData;
+
+    @Nullable
+    private final ParcelUuid mServiceDataUuid;
+    @Nullable
+    private final byte[] mServiceData;
+
+    // Transmission power level(in dB).
+    private final int mTxPowerLevel;
+
+    // Local name of the Bluetooth LE device.
+    private final String mLocalName;
+
+    /**
+     * Returns the advertising flags indicating the discoverable mode and capability of the device.
+     * Returns -1 if the flag field is not set.
+     */
+    public int getAdvertiseFlags() {
+        return mAdvertiseFlags;
+    }
+
+    /**
+     * Returns a list of service uuids within the advertisement that are used to identify the
+     * bluetooth gatt services.
+     */
+    public List<ParcelUuid> getServiceUuids() {
+        return mServiceUuids;
+    }
+
+    /**
+     * Returns the manufacturer identifier, which is a non-negative number assigned by Bluetooth
+     * SIG.
+     */
+    public int getManufacturerId() {
+        return mManufacturerId;
+    }
+
+    /**
+     * Returns the manufacturer specific data which is the content of manufacturer specific data
+     * field. The first 2 bytes of the data contain the company id.
+     */
+    public byte[] getManufacturerSpecificData() {
+        return mManufacturerSpecificData;
+    }
+
+    /**
+     * Returns a 16 bit uuid of the service that the service data is associated with.
+     */
+    public ParcelUuid getServiceDataUuid() {
+        return mServiceDataUuid;
+    }
+
+    /**
+     * Returns service data. The first two bytes should be a 16 bit service uuid associated with the
+     * service data.
+     */
+    public byte[] getServiceData() {
+        return mServiceData;
+    }
+
+    /**
+     * Returns the transmission power level of the packet in dBm. Returns {@link Integer#MIN_VALUE}
+     * if the field is not set. This value can be used to calculate the path loss of a received
+     * packet using the following equation:
+     * <p>
+     * <code>pathloss = txPowerLevel - rssi</code>
+     */
+    public int getTxPowerLevel() {
+        return mTxPowerLevel;
+    }
+
+    /**
+     * Returns the local name of the BLE device. The is a UTF-8 encoded string.
+     */
+    @Nullable
+    public String getLocalName() {
+        return mLocalName;
+    }
+
+    private ScanRecord(List<ParcelUuid> serviceUuids,
+            ParcelUuid serviceDataUuid, byte[] serviceData,
+            int manufacturerId,
+            byte[] manufacturerSpecificData, int advertiseFlags, int txPowerLevel,
+            String localName) {
+        mServiceUuids = serviceUuids;
+        mManufacturerId = manufacturerId;
+        mManufacturerSpecificData = manufacturerSpecificData;
+        mServiceDataUuid = serviceDataUuid;
+        mServiceData = serviceData;
+        mLocalName = localName;
+        mAdvertiseFlags = advertiseFlags;
+        mTxPowerLevel = txPowerLevel;
+    }
+
+    @Override
+    public String toString() {
+        return "ScanRecord [mAdvertiseFlags=" + mAdvertiseFlags + ", mServiceUuids=" + mServiceUuids
+                + ", mManufacturerId=" + mManufacturerId + ", mManufacturerSpecificData="
+                + Arrays.toString(mManufacturerSpecificData) + ", mServiceDataUuid="
+                + mServiceDataUuid + ", mServiceData=" + Arrays.toString(mServiceData)
+                + ", mTxPowerLevel=" + mTxPowerLevel + ", mLocalName=" + mLocalName + "]";
+    }
+
+    /**
+     * Parse scan record bytes to {@link ScanRecord}.
+     * <p>
+     * The format is defined in Bluetooth 4.1 specification, Volume 3, Part C, Section 11 and 18.
+     * <p>
+     * All numerical multi-byte entities and values shall use little-endian <strong>byte</strong>
+     * order.
+     *
+     * @param scanRecord The scan record of Bluetooth LE advertisement and/or scan response.
+     */
+    public static ScanRecord parseFromBytes(byte[] scanRecord) {
+        if (scanRecord == null) {
+            return null;
+        }
+
+        int currentPos = 0;
+        int advertiseFlag = -1;
+        List<ParcelUuid> serviceUuids = new ArrayList<ParcelUuid>();
+        String localName = null;
+        int txPowerLevel = Integer.MIN_VALUE;
+        ParcelUuid serviceDataUuid = null;
+        byte[] serviceData = null;
+        int manufacturerId = -1;
+        byte[] manufacturerSpecificData = null;
+
+        try {
+            while (currentPos < scanRecord.length) {
+                // length is unsigned int.
+                int length = scanRecord[currentPos++] & 0xFF;
+                if (length == 0) {
+                    break;
+                }
+                // Note the length includes the length of the field type itself.
+                int dataLength = length - 1;
+                // fieldType is unsigned int.
+                int fieldType = scanRecord[currentPos++] & 0xFF;
+                switch (fieldType) {
+                    case DATA_TYPE_FLAGS:
+                        advertiseFlag = scanRecord[currentPos] & 0xFF;
+                        break;
+                    case DATA_TYPE_SERVICE_UUIDS_16_BIT_PARTIAL:
+                    case DATA_TYPE_SERVICE_UUIDS_16_BIT_COMPLETE:
+                        parseServiceUuid(scanRecord, currentPos,
+                                dataLength, BluetoothUuid.UUID_BYTES_16_BIT, serviceUuids);
+                        break;
+                    case DATA_TYPE_SERVICE_UUIDS_32_BIT_PARTIAL:
+                    case DATA_TYPE_SERVICE_UUIDS_32_BIT_COMPLETE:
+                        parseServiceUuid(scanRecord, currentPos, dataLength,
+                                BluetoothUuid.UUID_BYTES_32_BIT, serviceUuids);
+                        break;
+                    case DATA_TYPE_SERVICE_UUIDS_128_BIT_PARTIAL:
+                    case DATA_TYPE_SERVICE_UUIDS_128_BIT_COMPLETE:
+                        parseServiceUuid(scanRecord, currentPos, dataLength,
+                                BluetoothUuid.UUID_BYTES_128_BIT, serviceUuids);
+                        break;
+                    case DATA_TYPE_LOCAL_NAME_SHORT:
+                    case DATA_TYPE_LOCAL_NAME_COMPLETE:
+                        localName = new String(
+                                extractBytes(scanRecord, currentPos, dataLength));
+                        break;
+                    case DATA_TYPE_TX_POWER_LEVEL:
+                        txPowerLevel = scanRecord[currentPos];
+                        break;
+                    case DATA_TYPE_SERVICE_DATA:
+                        serviceData = extractBytes(scanRecord, currentPos, dataLength);
+                        // The first two bytes of the service data are service data uuid.
+                        int serviceUuidLength = BluetoothUuid.UUID_BYTES_16_BIT;
+                        byte[] serviceDataUuidBytes = extractBytes(scanRecord, currentPos,
+                                serviceUuidLength);
+                        serviceDataUuid = BluetoothUuid.parseUuidFrom(serviceDataUuidBytes);
+                        break;
+                    case DATA_TYPE_MANUFACTURER_SPECIFIC_DATA:
+                        manufacturerSpecificData = extractBytes(scanRecord, currentPos,
+                                dataLength);
+                        // The first two bytes of the manufacturer specific data are
+                        // manufacturer ids in little endian.
+                        manufacturerId = ((manufacturerSpecificData[1] & 0xFF) << 8) +
+                                (manufacturerSpecificData[0] & 0xFF);
+                        break;
+                    default:
+                        // Just ignore, we don't handle such data type.
+                        break;
+                }
+                currentPos += dataLength;
+            }
+
+            if (serviceUuids.isEmpty()) {
+                serviceUuids = null;
+            }
+            return new ScanRecord(serviceUuids, serviceDataUuid, serviceData,
+                    manufacturerId, manufacturerSpecificData, advertiseFlag, txPowerLevel,
+                    localName);
+        } catch (IndexOutOfBoundsException e) {
+            Log.e(TAG, "unable to parse scan record: " + Arrays.toString(scanRecord));
+            return null;
+        }
+    }
+
+    // Parse service uuids.
+    private static int parseServiceUuid(byte[] scanRecord, int currentPos, int dataLength,
+            int uuidLength, List<ParcelUuid> serviceUuids) {
+        while (dataLength > 0) {
+            byte[] uuidBytes = extractBytes(scanRecord, currentPos,
+                    uuidLength);
+            serviceUuids.add(BluetoothUuid.parseUuidFrom(uuidBytes));
+            dataLength -= uuidLength;
+            currentPos += uuidLength;
+        }
+        return currentPos;
+    }
+
+    // Helper method to extract bytes from byte array.
+    private static byte[] extractBytes(byte[] scanRecord, int start, int length) {
+        byte[] bytes = new byte[length];
+        System.arraycopy(scanRecord, start, bytes, 0, length);
+        return bytes;
+    }
+}
diff --git a/core/java/android/tv/TvInputInfo.aidl b/core/java/android/bluetooth/le/ScanResult.aidl
similarity index 91%
copy from core/java/android/tv/TvInputInfo.aidl
copy to core/java/android/bluetooth/le/ScanResult.aidl
index abc4b47..3943035 100644
--- a/core/java/android/tv/TvInputInfo.aidl
+++ b/core/java/android/bluetooth/le/ScanResult.aidl
@@ -14,6 +14,6 @@
  * limitations under the License.
  */
 
-package android.tv;
+package android.bluetooth.le;
 
-parcelable TvInputInfo;
+parcelable ScanResult;
\ No newline at end of file
diff --git a/core/java/android/bluetooth/le/ScanResult.java b/core/java/android/bluetooth/le/ScanResult.java
new file mode 100644
index 0000000..7e6e8f8
--- /dev/null
+++ b/core/java/android/bluetooth/le/ScanResult.java
@@ -0,0 +1,162 @@
+/*
+ * 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.bluetooth.le;
+
+import android.annotation.Nullable;
+import android.bluetooth.BluetoothDevice;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.util.Arrays;
+import java.util.Objects;
+
+/**
+ * ScanResult for Bluetooth LE scan.
+ */
+public final class ScanResult implements Parcelable {
+    // Remote bluetooth device.
+    private BluetoothDevice mDevice;
+
+    // Scan record, including advertising data and scan response data.
+    private byte[] mScanRecord;
+
+    // Received signal strength.
+    private int mRssi;
+
+    // Device timestamp when the result was last seen.
+    private long mTimestampNanos;
+
+    /**
+     * Constructor of scan result.
+     *
+     * @hide
+     */
+    public ScanResult(BluetoothDevice device, byte[] scanRecord, int rssi,
+            long timestampNanos) {
+        mDevice = device;
+        mScanRecord = scanRecord;
+        mRssi = rssi;
+        mTimestampNanos = timestampNanos;
+    }
+
+    private ScanResult(Parcel in) {
+        readFromParcel(in);
+    }
+
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        if (mDevice != null) {
+            dest.writeInt(1);
+            mDevice.writeToParcel(dest, flags);
+        } else {
+            dest.writeInt(0);
+        }
+        if (mScanRecord != null) {
+            dest.writeInt(1);
+            dest.writeByteArray(mScanRecord);
+        } else {
+            dest.writeInt(0);
+        }
+        dest.writeInt(mRssi);
+        dest.writeLong(mTimestampNanos);
+    }
+
+    private void readFromParcel(Parcel in) {
+        if (in.readInt() == 1) {
+            mDevice = BluetoothDevice.CREATOR.createFromParcel(in);
+        }
+        if (in.readInt() == 1) {
+            mScanRecord = in.createByteArray();
+        }
+        mRssi = in.readInt();
+        mTimestampNanos = in.readLong();
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    /**
+     * Returns the remote bluetooth device identified by the bluetooth device address.
+     */
+    @Nullable
+    public BluetoothDevice getDevice() {
+        return mDevice;
+    }
+
+    /**
+     * Returns the scan record, which can be a combination of advertisement and scan response.
+     */
+    @Nullable
+    public byte[] getScanRecord() {
+        return mScanRecord;
+    }
+
+    /**
+     * Returns the received signal strength in dBm. The valid range is [-127, 127].
+     */
+    public int getRssi() {
+        return mRssi;
+    }
+
+    /**
+     * Returns timestamp since boot when the scan record was observed.
+     */
+    public long getTimestampNanos() {
+        return mTimestampNanos;
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(mDevice, mRssi, mScanRecord, mTimestampNanos);
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (this == obj) {
+            return true;
+        }
+        if (obj == null || getClass() != obj.getClass()) {
+            return false;
+        }
+        ScanResult other = (ScanResult) obj;
+        return Objects.equals(mDevice, other.mDevice) && (mRssi == other.mRssi) &&
+                Objects.deepEquals(mScanRecord, other.mScanRecord)
+                && (mTimestampNanos == other.mTimestampNanos);
+    }
+
+    @Override
+    public String toString() {
+        return "ScanResult{" + "mDevice=" + mDevice + ", mScanRecord="
+                + Arrays.toString(mScanRecord) + ", mRssi=" + mRssi + ", mTimestampNanos="
+                + mTimestampNanos + '}';
+    }
+
+    public static final Parcelable.Creator<ScanResult> CREATOR = new Creator<ScanResult>() {
+            @Override
+        public ScanResult createFromParcel(Parcel source) {
+            return new ScanResult(source);
+        }
+
+            @Override
+        public ScanResult[] newArray(int size) {
+            return new ScanResult[size];
+        }
+    };
+
+}
diff --git a/core/java/android/tv/TvInputInfo.aidl b/core/java/android/bluetooth/le/ScanSettings.aidl
similarity index 91%
copy from core/java/android/tv/TvInputInfo.aidl
copy to core/java/android/bluetooth/le/ScanSettings.aidl
index abc4b47..eb169c1 100644
--- a/core/java/android/tv/TvInputInfo.aidl
+++ b/core/java/android/bluetooth/le/ScanSettings.aidl
@@ -14,6 +14,6 @@
  * limitations under the License.
  */
 
-package android.tv;
+package android.bluetooth.le;
 
-parcelable TvInputInfo;
+parcelable ScanSettings;
diff --git a/core/java/android/bluetooth/le/ScanSettings.java b/core/java/android/bluetooth/le/ScanSettings.java
new file mode 100644
index 0000000..0a85675
--- /dev/null
+++ b/core/java/android/bluetooth/le/ScanSettings.java
@@ -0,0 +1,221 @@
+/*
+ * 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.bluetooth.le;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/**
+ * Settings for Bluetooth LE scan.
+ */
+public final class ScanSettings implements Parcelable {
+    /**
+     * Perform Bluetooth LE scan in low power mode. This is the default scan mode as it consumes the
+     * least power.
+     */
+    public static final int SCAN_MODE_LOW_POWER = 0;
+    /**
+     * Perform Bluetooth LE scan in balanced power mode.
+     */
+    public static final int SCAN_MODE_BALANCED = 1;
+    /**
+     * Scan using highest duty cycle. It's recommended only using this mode when the application is
+     * running in foreground.
+     */
+    public static final int SCAN_MODE_LOW_LATENCY = 2;
+
+    /**
+     * Callback each time when a bluetooth advertisement is found.
+     */
+    public static final int CALLBACK_TYPE_ON_UPDATE = 0;
+    /**
+     * Callback when a bluetooth advertisement is found for the first time.
+     *
+     * @hide
+     */
+    public static final int CALLBACK_TYPE_ON_FOUND = 1;
+    /**
+     * Callback when a bluetooth advertisement is found for the first time, then lost.
+     *
+     * @hide
+     */
+    public static final int CALLBACK_TYPE_ON_LOST = 2;
+
+    /**
+     * Full scan result which contains device mac address, rssi, advertising and scan response and
+     * scan timestamp.
+     */
+    public static final int SCAN_RESULT_TYPE_FULL = 0;
+    /**
+     * Truncated scan result which contains device mac address, rssi and scan timestamp. Note it's
+     * possible for an app to get more scan results that it asks if there are multiple apps using
+     * this type. TODO: decide whether we could unhide this setting.
+     *
+     * @hide
+     */
+    public static final int SCAN_RESULT_TYPE_TRUNCATED = 1;
+
+    // Bluetooth LE scan mode.
+    private int mScanMode;
+
+    // Bluetooth LE scan callback type
+    private int mCallbackType;
+
+    // Bluetooth LE scan result type
+    private int mScanResultType;
+
+    // Time of delay for reporting the scan result
+    private long mReportDelayNanos;
+
+    public int getScanMode() {
+        return mScanMode;
+    }
+
+    public int getCallbackType() {
+        return mCallbackType;
+    }
+
+    public int getScanResultType() {
+        return mScanResultType;
+    }
+
+    /**
+     * Returns report delay timestamp based on the device clock.
+     */
+    public long getReportDelayNanos() {
+        return mReportDelayNanos;
+    }
+
+    private ScanSettings(int scanMode, int callbackType, int scanResultType,
+            long reportDelayNanos) {
+        mScanMode = scanMode;
+        mCallbackType = callbackType;
+        mScanResultType = scanResultType;
+        mReportDelayNanos = reportDelayNanos;
+    }
+
+    private ScanSettings(Parcel in) {
+        mScanMode = in.readInt();
+        mCallbackType = in.readInt();
+        mScanResultType = in.readInt();
+        mReportDelayNanos = in.readLong();
+    }
+
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        dest.writeInt(mScanMode);
+        dest.writeInt(mCallbackType);
+        dest.writeInt(mScanResultType);
+        dest.writeLong(mReportDelayNanos);
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    public static final Parcelable.Creator<ScanSettings>
+            CREATOR = new Creator<ScanSettings>() {
+                    @Override
+                public ScanSettings[] newArray(int size) {
+                    return new ScanSettings[size];
+                }
+
+                    @Override
+                public ScanSettings createFromParcel(Parcel in) {
+                    return new ScanSettings(in);
+                }
+            };
+
+    /**
+     * Builder for {@link ScanSettings}.
+     */
+    public static final class Builder {
+        private int mScanMode = SCAN_MODE_LOW_POWER;
+        private int mCallbackType = CALLBACK_TYPE_ON_UPDATE;
+        private int mScanResultType = SCAN_RESULT_TYPE_FULL;
+        private long mReportDelayNanos = 0;
+
+        /**
+         * Set scan mode for Bluetooth LE scan.
+         *
+         * @param scanMode The scan mode can be one of
+         *            {@link ScanSettings#SCAN_MODE_LOW_POWER},
+         *            {@link ScanSettings#SCAN_MODE_BALANCED} or
+         *            {@link ScanSettings#SCAN_MODE_LOW_LATENCY}.
+         * @throws IllegalArgumentException If the {@code scanMode} is invalid.
+         */
+        public Builder setScanMode(int scanMode) {
+            if (scanMode < SCAN_MODE_LOW_POWER || scanMode > SCAN_MODE_LOW_LATENCY) {
+                throw new IllegalArgumentException("invalid scan mode " + scanMode);
+            }
+            mScanMode = scanMode;
+            return this;
+        }
+
+        /**
+         * Set callback type for Bluetooth LE scan.
+         *
+         * @param callbackType The callback type for the scan. Can only be
+         *            {@link ScanSettings#CALLBACK_TYPE_ON_UPDATE}.
+         * @throws IllegalArgumentException If the {@code callbackType} is invalid.
+         */
+        public Builder setCallbackType(int callbackType) {
+            if (callbackType < CALLBACK_TYPE_ON_UPDATE
+                    || callbackType > CALLBACK_TYPE_ON_LOST) {
+                throw new IllegalArgumentException("invalid callback type - " + callbackType);
+            }
+            mCallbackType = callbackType;
+            return this;
+        }
+
+        /**
+         * Set scan result type for Bluetooth LE scan.
+         *
+         * @param scanResultType Type for scan result, could be either
+         *            {@link ScanSettings#SCAN_RESULT_TYPE_FULL} or
+         *            {@link ScanSettings#SCAN_RESULT_TYPE_TRUNCATED}.
+         * @throws IllegalArgumentException If the {@code scanResultType} is invalid.
+         * @hide
+         */
+        public Builder setScanResultType(int scanResultType) {
+            if (scanResultType < SCAN_RESULT_TYPE_FULL
+                    || scanResultType > SCAN_RESULT_TYPE_TRUNCATED) {
+                throw new IllegalArgumentException(
+                        "invalid scanResultType - " + scanResultType);
+            }
+            mScanResultType = scanResultType;
+            return this;
+        }
+
+        /**
+         * Set report delay timestamp for Bluetooth LE scan.
+         */
+        public Builder setReportDelayNanos(long reportDelayNanos) {
+            mReportDelayNanos = reportDelayNanos;
+            return this;
+        }
+
+        /**
+         * Build {@link ScanSettings}.
+         */
+        public ScanSettings build() {
+            return new ScanSettings(mScanMode, mCallbackType, mScanResultType,
+                    mReportDelayNanos);
+        }
+    }
+}
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index b0673b5..a040efb 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -40,6 +40,7 @@
 import android.os.StatFs;
 import android.os.UserHandle;
 import android.os.UserManager;
+import android.provider.MediaStore;
 import android.util.AttributeSet;
 import android.view.DisplayAdjustments;
 import android.view.Display;
@@ -929,6 +930,40 @@
     public abstract File[] getExternalCacheDirs();
 
     /**
+     * Returns absolute paths to application-specific directories on all
+     * external storage devices where the application can place media files.
+     * These files are scanned and made available to other apps through
+     * {@link MediaStore}.
+     * <p>
+     * This is like {@link #getExternalFilesDirs} in that these files will be
+     * deleted when the application is uninstalled, however there are some
+     * important differences:
+     * <ul>
+     * <li>External files are not always available: they will disappear if the
+     * user mounts the external storage on a computer or removes it.
+     * <li>There is no security enforced with these files.
+     * </ul>
+     * <p>
+     * External storage devices returned here are considered a permanent part of
+     * the device, including both emulated external storage and physical media
+     * slots, such as SD cards in a battery compartment. The returned paths do
+     * not include transient devices, such as USB flash drives.
+     * <p>
+     * An application may store data on any or all of the returned devices. For
+     * example, an app may choose to store large files on the device with the
+     * most available space, as measured by {@link StatFs}.
+     * <p>
+     * No permissions are required to read or write to the returned paths; they
+     * are always accessible to the calling app. Write access outside of these
+     * paths on secondary external storage devices is not available.
+     * <p>
+     * Returned paths may be {@code null} if a storage device is unavailable.
+     *
+     * @see Environment#getExternalStorageState(File)
+     */
+    public abstract File[] getExternalMediaDirs();
+
+    /**
      * Returns an array of strings naming the private files associated with
      * this Context's application package.
      *
@@ -2363,6 +2398,7 @@
      *
      * @see #getSystemService
      * @see android.net.wifi.passpoint.WifiPasspointManager
+     * @hide
      */
     public static final String WIFI_PASSPOINT_SERVICE = "wifipasspoint";
 
@@ -2659,6 +2695,15 @@
 
     /**
      * Use with {@link #getSystemService} to retrieve a
+     * {@link android.content.RestrictionsManager} for retrieving application restrictions
+     * and requesting permissions for restricted operations.
+     * @see #getSystemService
+     * @see android.content.RestrictionsManager
+     */
+    public static final String RESTRICTIONS_SERVICE = "restrictions";
+
+    /**
+     * Use with {@link #getSystemService} to retrieve a
      * {@link android.app.AppOpsManager} for tracking application operations
      * on the device.
      *
@@ -2706,11 +2751,11 @@
 
     /**
      * Use with {@link #getSystemService} to retrieve a
-     * {@link android.tv.TvInputManager} for interacting with TV inputs on the
-     * device.
+     * {@link android.media.tv.TvInputManager} for interacting with TV inputs
+     * on the device.
      *
      * @see #getSystemService
-     * @see android.tv.TvInputManager
+     * @see android.media.tv.TvInputManager
      */
     public static final String TV_INPUT_SERVICE = "tv_input";
 
diff --git a/core/java/android/content/ContextWrapper.java b/core/java/android/content/ContextWrapper.java
index c66355b..dbf9122 100644
--- a/core/java/android/content/ContextWrapper.java
+++ b/core/java/android/content/ContextWrapper.java
@@ -237,6 +237,11 @@
     }
 
     @Override
+    public File[] getExternalMediaDirs() {
+        return mBase.getExternalMediaDirs();
+    }
+
+    @Override
     public File getDir(String name, int mode) {
         return mBase.getDir(name, mode);
     }
diff --git a/core/java/android/tv/ITvInputSessionCallback.aidl b/core/java/android/content/IRestrictionsManager.aidl
similarity index 60%
copy from core/java/android/tv/ITvInputSessionCallback.aidl
copy to core/java/android/content/IRestrictionsManager.aidl
index a2bd0d7..b1c0a3a 100644
--- a/core/java/android/tv/ITvInputSessionCallback.aidl
+++ b/core/java/android/content/IRestrictionsManager.aidl
@@ -14,15 +14,17 @@
  * limitations under the License.
  */
 
-package android.tv;
+package android.content;
 
-import android.tv.ITvInputSession;
+import android.os.Bundle;
 
 /**
- * Helper interface for ITvInputSession to allow the TV input to notify the system service when a
- * new session has been created.
+ * Interface used by the RestrictionsManager
  * @hide
  */
-oneway interface ITvInputSessionCallback {
-    void onSessionCreated(ITvInputSession session);
+interface IRestrictionsManager {
+    Bundle getApplicationRestrictions(in String packageName);
+    boolean hasRestrictionsProvider();
+    void requestPermission(in String packageName, in String requestTemplate, in Bundle requestData);
+    void notifyPermissionResponse(in String packageName, in Bundle response);
 }
diff --git a/core/java/android/content/RestrictionEntry.java b/core/java/android/content/RestrictionEntry.java
index 3ff53bf..62f88a9 100644
--- a/core/java/android/content/RestrictionEntry.java
+++ b/core/java/android/content/RestrictionEntry.java
@@ -73,32 +73,38 @@
      */
     public static final int TYPE_MULTI_SELECT = 4;
 
+    /**
+     * A type of restriction. Use this for storing an integer value. The range of values
+     * is from {@link Integer#MIN_VALUE} to {@link Integer#MAX_VALUE}.
+     */
+    public static final int TYPE_INTEGER = 5;
+
     /** The type of restriction. */
-    private int type;
+    private int mType;
 
     /** The unique key that identifies the restriction. */
-    private String key;
+    private String mKey;
 
     /** The user-visible title of the restriction. */
-    private String title;
+    private String mTitle;
 
     /** The user-visible secondary description of the restriction. */
-    private String description;
+    private String mDescription;
 
     /** The user-visible set of choices used for single-select and multi-select lists. */
-    private String [] choices;
+    private String [] mChoiceEntries;
 
     /** The values corresponding to the user-visible choices. The value(s) of this entry will
      * one or more of these, returned by {@link #getAllSelectedStrings()} and
      * {@link #getSelectedString()}.
      */
-    private String [] values;
+    private String [] mChoiceValues;
 
     /* The chosen value, whose content depends on the type of the restriction. */
-    private String currentValue;
+    private String mCurrentValue;
 
     /* List of selected choices in the multi-select case. */
-    private String[] currentValues;
+    private String[] mCurrentValues;
 
     /**
      * Constructor for {@link #TYPE_CHOICE} type.
@@ -106,9 +112,9 @@
      * @param selectedString the current value
      */
     public RestrictionEntry(String key, String selectedString) {
-        this.key = key;
-        this.type = TYPE_CHOICE;
-        this.currentValue = selectedString;
+        this.mKey = key;
+        this.mType = TYPE_CHOICE;
+        this.mCurrentValue = selectedString;
     }
 
     /**
@@ -117,8 +123,8 @@
      * @param selectedState whether this restriction is selected or not
      */
     public RestrictionEntry(String key, boolean selectedState) {
-        this.key = key;
-        this.type = TYPE_BOOLEAN;
+        this.mKey = key;
+        this.mType = TYPE_BOOLEAN;
         setSelectedState(selectedState);
     }
 
@@ -128,9 +134,20 @@
      * @param selectedStrings the list of values that are currently selected
      */
     public RestrictionEntry(String key, String[] selectedStrings) {
-        this.key = key;
-        this.type = TYPE_MULTI_SELECT;
-        this.currentValues = selectedStrings;
+        this.mKey = key;
+        this.mType = TYPE_MULTI_SELECT;
+        this.mCurrentValues = selectedStrings;
+    }
+
+    /**
+     * Constructor for {@link #TYPE_INTEGER} type.
+     * @param key the unique key for this restriction
+     * @param selectedInt the integer value of the restriction
+     */
+    public RestrictionEntry(String key, int selectedInt) {
+        mKey = key;
+        mType = TYPE_INTEGER;
+        setIntValue(selectedInt);
     }
 
     /**
@@ -138,7 +155,7 @@
      * @param type the type for this restriction.
      */
     public void setType(int type) {
-        this.type = type;
+        this.mType = type;
     }
 
     /**
@@ -146,7 +163,7 @@
      * @return the type for this restriction
      */
     public int getType() {
-        return type;
+        return mType;
     }
 
     /**
@@ -155,7 +172,7 @@
      * single string values.
      */
     public String getSelectedString() {
-        return currentValue;
+        return mCurrentValue;
     }
 
     /**
@@ -164,7 +181,7 @@
      *  null otherwise.
      */
     public String[] getAllSelectedStrings() {
-        return currentValues;
+        return mCurrentValues;
     }
 
     /**
@@ -172,7 +189,23 @@
      * @return the current selected state of the entry.
      */
     public boolean getSelectedState() {
-        return Boolean.parseBoolean(currentValue);
+        return Boolean.parseBoolean(mCurrentValue);
+    }
+
+    /**
+     * Returns the value of the entry as an integer when the type is {@link #TYPE_INTEGER}.
+     * @return the integer value of the entry.
+     */
+    public int getIntValue() {
+        return Integer.parseInt(mCurrentValue);
+    }
+
+    /**
+     * Sets the integer value of the entry when the type is {@link #TYPE_INTEGER}.
+     * @param value the integer value to set.
+     */
+    public void setIntValue(int value) {
+        mCurrentValue = Integer.toString(value);
     }
 
     /**
@@ -181,7 +214,7 @@
      * @param selectedString the string value to select.
      */
     public void setSelectedString(String selectedString) {
-        currentValue = selectedString;
+        mCurrentValue = selectedString;
     }
 
     /**
@@ -190,7 +223,7 @@
      * @param state the current selected state
      */
     public void setSelectedState(boolean state) {
-        currentValue = Boolean.toString(state);
+        mCurrentValue = Boolean.toString(state);
     }
 
     /**
@@ -199,7 +232,7 @@
      * @param allSelectedStrings the current list of selected values.
      */
     public void setAllSelectedStrings(String[] allSelectedStrings) {
-        currentValues = allSelectedStrings;
+        mCurrentValues = allSelectedStrings;
     }
 
     /**
@@ -216,7 +249,7 @@
      * @see #getAllSelectedStrings()
      */
     public void setChoiceValues(String[] choiceValues) {
-        values = choiceValues;
+        mChoiceValues = choiceValues;
     }
 
     /**
@@ -227,7 +260,7 @@
      * @see #setChoiceValues(String[])
      */
     public void setChoiceValues(Context context, int stringArrayResId) {
-        values = context.getResources().getStringArray(stringArrayResId);
+        mChoiceValues = context.getResources().getStringArray(stringArrayResId);
     }
 
     /**
@@ -235,7 +268,7 @@
      * @return the list of possible values.
      */
     public String[] getChoiceValues() {
-        return values;
+        return mChoiceValues;
     }
 
     /**
@@ -248,7 +281,7 @@
      * @see #setChoiceValues(String[])
      */
     public void setChoiceEntries(String[] choiceEntries) {
-        choices = choiceEntries;
+        mChoiceEntries = choiceEntries;
     }
 
     /** Sets a list of strings that will be presented as choices to the user. This is similar to
@@ -257,7 +290,7 @@
      * @param stringArrayResId the resource id of a string array containing the possible entries.
      */
     public void setChoiceEntries(Context context, int stringArrayResId) {
-        choices = context.getResources().getStringArray(stringArrayResId);
+        mChoiceEntries = context.getResources().getStringArray(stringArrayResId);
     }
 
     /**
@@ -265,7 +298,7 @@
      * @return the list of choices presented to the user.
      */
     public String[] getChoiceEntries() {
-        return choices;
+        return mChoiceEntries;
     }
 
     /**
@@ -273,7 +306,7 @@
      * @return the user-visible description, null if none was set earlier.
      */
     public String getDescription() {
-        return description;
+        return mDescription;
     }
 
     /**
@@ -283,7 +316,7 @@
      * @param description the user-visible description string.
      */
     public void setDescription(String description) {
-        this.description = description;
+        this.mDescription = description;
     }
 
     /**
@@ -291,7 +324,7 @@
      * @return the key for the restriction.
      */
     public String getKey() {
-        return key;
+        return mKey;
     }
 
     /**
@@ -299,7 +332,7 @@
      * @return the user-visible title for the entry, null if none was set earlier.
      */
     public String getTitle() {
-        return title;
+        return mTitle;
     }
 
     /**
@@ -307,7 +340,7 @@
      * @param title the user-visible title for the entry.
      */
     public void setTitle(String title) {
-        this.title = title;
+        this.mTitle = title;
     }
 
     private boolean equalArrays(String[] one, String[] other) {
@@ -324,23 +357,23 @@
         if (!(o instanceof RestrictionEntry)) return false;
         final RestrictionEntry other = (RestrictionEntry) o;
         // Make sure that either currentValue matches or currentValues matches.
-        return type == other.type && key.equals(other.key)
+        return mType == other.mType && mKey.equals(other.mKey)
                 &&
-                ((currentValues == null && other.currentValues == null
-                  && currentValue != null && currentValue.equals(other.currentValue))
+                ((mCurrentValues == null && other.mCurrentValues == null
+                  && mCurrentValue != null && mCurrentValue.equals(other.mCurrentValue))
                  ||
-                 (currentValue == null && other.currentValue == null
-                  && currentValues != null && equalArrays(currentValues, other.currentValues)));
+                 (mCurrentValue == null && other.mCurrentValue == null
+                  && mCurrentValues != null && equalArrays(mCurrentValues, other.mCurrentValues)));
     }
 
     @Override
     public int hashCode() {
         int result = 17;
-        result = 31 * result + key.hashCode();
-        if (currentValue != null) {
-            result = 31 * result + currentValue.hashCode();
-        } else if (currentValues != null) {
-            for (String value : currentValues) {
+        result = 31 * result + mKey.hashCode();
+        if (mCurrentValue != null) {
+            result = 31 * result + mCurrentValue.hashCode();
+        } else if (mCurrentValues != null) {
+            for (String value : mCurrentValues) {
                 if (value != null) {
                     result = 31 * result + value.hashCode();
                 }
@@ -359,14 +392,14 @@
     }
 
     public RestrictionEntry(Parcel in) {
-        type = in.readInt();
-        key = in.readString();
-        title = in.readString();
-        description = in.readString();
-        choices = readArray(in);
-        values = readArray(in);
-        currentValue = in.readString();
-        currentValues = readArray(in);
+        mType = in.readInt();
+        mKey = in.readString();
+        mTitle = in.readString();
+        mDescription = in.readString();
+        mChoiceEntries = readArray(in);
+        mChoiceValues = readArray(in);
+        mCurrentValue = in.readString();
+        mCurrentValues = readArray(in);
     }
 
     @Override
@@ -387,14 +420,14 @@
 
     @Override
     public void writeToParcel(Parcel dest, int flags) {
-        dest.writeInt(type);
-        dest.writeString(key);
-        dest.writeString(title);
-        dest.writeString(description);
-        writeArray(dest, choices);
-        writeArray(dest, values);
-        dest.writeString(currentValue);
-        writeArray(dest, currentValues);
+        dest.writeInt(mType);
+        dest.writeString(mKey);
+        dest.writeString(mTitle);
+        dest.writeString(mDescription);
+        writeArray(dest, mChoiceEntries);
+        writeArray(dest, mChoiceValues);
+        dest.writeString(mCurrentValue);
+        writeArray(dest, mCurrentValues);
     }
 
     public static final Creator<RestrictionEntry> CREATOR = new Creator<RestrictionEntry>() {
@@ -409,6 +442,6 @@
 
     @Override
     public String toString() {
-        return "RestrictionsEntry {type=" + type + ", key=" + key + ", value=" + currentValue + "}";
+        return "RestrictionsEntry {type=" + mType + ", key=" + mKey + ", value=" + mCurrentValue + "}";
     }
 }
diff --git a/core/java/android/content/RestrictionsManager.java b/core/java/android/content/RestrictionsManager.java
new file mode 100644
index 0000000..0dd0edd
--- /dev/null
+++ b/core/java/android/content/RestrictionsManager.java
@@ -0,0 +1,344 @@
+/*
+ * 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.content;
+
+import android.app.admin.DevicePolicyManager;
+import android.os.Bundle;
+import android.os.RemoteException;
+import android.util.Log;
+
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * Provides a mechanism for apps to query restrictions imposed by an entity that
+ * manages the user. Apps can also send permission requests to a local or remote
+ * device administrator to override default app-specific restrictions or any other
+ * operation that needs explicit authorization from the administrator.
+ * <p>
+ * Apps can expose a set of restrictions via a runtime receiver mechanism or via
+ * static meta data in the manifest.
+ * <p>
+ * If the user has an active restrictions provider, dynamic requests can be made in
+ * addition to the statically imposed restrictions. Dynamic requests are app-specific
+ * and can be expressed via a predefined set of templates.
+ * <p>
+ * The RestrictionsManager forwards the dynamic requests to the active
+ * restrictions provider. The restrictions provider can respond back to requests by calling
+ * {@link #notifyPermissionResponse(String, Bundle)}, when
+ * a response is received from the administrator of the device or user 
+ * The response is relayed back to the application via a protected broadcast,
+ * {@link #ACTION_PERMISSION_RESPONSE_RECEIVED}.
+ * <p>
+ * Static restrictions are specified by an XML file referenced by a meta-data attribute
+ * in the manifest. This enables applications as well as any web administration consoles
+ * to be able to read the template from the apk.
+ * <p>
+ * The syntax of the XML format is as follows:
+ * <pre>
+ * &lt;restrictions&gt;
+ *     &lt;restriction
+ *         android:key="&lt;key&gt;"
+ *         android:restrictionType="boolean|string|integer|multi-select|null"
+ *         ... /&gt;
+ *     &lt;restriction ... /&gt;
+ * &lt;/restrictions&gt;
+ * </pre>
+ * <p>
+ * The attributes for each restriction depend on the restriction type.
+ *
+ * @see RestrictionEntry
+ */
+public class RestrictionsManager {
+
+    /**
+     * Broadcast intent delivered when a response is received for a permission
+     * request. The response is not available for later query, so the receiver
+     * must persist and/or immediately act upon the response. The application
+     * should not interrupt the user by coming to the foreground if it isn't
+     * currently in the foreground. It can post a notification instead, informing
+     * the user of a change in state.
+     * <p>
+     * For instance, if the user requested permission to make an in-app purchase,
+     * the app can post a notification that the request had been granted or denied,
+     * and allow the purchase to go through.
+     * <p>
+     * The broadcast Intent carries the following extra:
+     * {@link #EXTRA_RESPONSE_BUNDLE}.
+     */
+    public static final String ACTION_PERMISSION_RESPONSE_RECEIVED =
+            "android.intent.action.PERMISSION_RESPONSE_RECEIVED";
+
+    /**
+     * Protected broadcast intent sent to the active restrictions provider. The intent
+     * contains the following extras:<p>
+     * <ul>
+     * <li>{@link #EXTRA_PACKAGE_NAME} : String; the package name of the application requesting
+     * permission.</li>
+     * <li>{@link #EXTRA_TEMPLATE_ID} : String; the template of the request.</li>
+     * <li>{@link #EXTRA_REQUEST_BUNDLE} : Bundle; contains the template-specific keys and values
+     * for the request.
+     * </ul>
+     * @see DevicePolicyManager#setRestrictionsProvider(ComponentName, ComponentName)
+     * @see #requestPermission(String, String, Bundle)
+     */
+    public static final String ACTION_REQUEST_PERMISSION =
+            "android.intent.action.REQUEST_PERMISSION";
+
+    /**
+     * The package name of the application making the request.
+     */
+    public static final String EXTRA_PACKAGE_NAME = "package_name";
+
+    /**
+     * The template id that specifies what kind of a request it is and may indicate
+     * how the request is to be presented to the administrator. Must be either one of
+     * the predefined templates or a custom one specified by the application that the
+     * restrictions provider is familiar with.
+     */
+    public static final String EXTRA_TEMPLATE_ID = "template_id";
+
+    /**
+     * A bundle containing the details about the request. The contents depend on the
+     * template id.
+     * @see #EXTRA_TEMPLATE_ID
+     */
+    public static final String EXTRA_REQUEST_BUNDLE = "request_bundle";
+
+    /**
+     * Contains a response from the administrator for specific request.
+     * The bundle contains the following information, at least:
+     * <ul>
+     * <li>{@link #REQUEST_KEY_ID}: The request id.</li>
+     * <li>{@link #REQUEST_KEY_DATA}: The request reference data.</li>
+     * </ul>
+     * <p>
+     * And depending on what the request template was, the bundle will contain the actual
+     * result of the request. For {@link #REQUEST_TEMPLATE_QUESTION}, the result will be in
+     * {@link #RESPONSE_KEY_BOOLEAN}, which is of type boolean; true if the administrator
+     * approved the request, false otherwise.
+     */
+    public static final String EXTRA_RESPONSE_BUNDLE = "response_bundle";
+
+
+    /**
+     * Request template that presents a simple question, with a possible title and icon.
+     * <p>
+     * Required keys are
+     * {@link #REQUEST_KEY_ID} and {@link #REQUEST_KEY_MESSAGE}.
+     * <p>
+     * Optional keys are
+     * {@link #REQUEST_KEY_DATA}, {@link #REQUEST_KEY_ICON}, {@link #REQUEST_KEY_TITLE},
+     * {@link #REQUEST_KEY_APPROVE_LABEL} and {@link #REQUEST_KEY_DENY_LABEL}.
+     */
+    public static final String REQUEST_TEMPLATE_QUESTION = "android.req_template.type.simple";
+
+    /**
+     * Key for request ID contained in the request bundle.
+     * <p>
+     * App-generated request id to identify the specific request when receiving
+     * a response. This value is returned in the {@link #EXTRA_RESPONSE_BUNDLE}.
+     * <p>
+     * Type: String
+     */
+    public static final String REQUEST_KEY_ID = "android.req_template.req_id";
+
+    /**
+     * Key for request data contained in the request bundle.
+     * <p>
+     * Optional, typically used to identify the specific data that is being referred to,
+     * such as the unique identifier for a movie or book. This is not used for display
+     * purposes and is more like a cookie. This value is returned in the
+     * {@link #EXTRA_RESPONSE_BUNDLE}.
+     * <p>
+     * Type: String
+     */
+    public static final String REQUEST_KEY_DATA = "android.req_template.data";
+
+    /**
+     * Key for request title contained in the request bundle.
+     * <p>
+     * Optional, typically used as the title of any notification or dialog presented
+     * to the administrator who approves the request.
+     * <p>
+     * Type: String
+     */
+    public static final String REQUEST_KEY_TITLE = "android.req_template.title";
+
+    /**
+     * Key for request message contained in the request bundle.
+     * <p>
+     * Required, shown as the actual message in a notification or dialog presented
+     * to the administrator who approves the request.
+     * <p>
+     * Type: String
+     */
+    public static final String REQUEST_KEY_MESSAGE = "android.req_template.mesg";
+
+    /**
+     * Key for request icon contained in the request bundle.
+     * <p>
+     * Optional, shown alongside the request message presented to the administrator
+     * who approves the request.
+     * <p>
+     * Type: Bitmap
+     */
+    public static final String REQUEST_KEY_ICON = "android.req_template.icon";
+
+    /**
+     * Key for request approval button label contained in the request bundle.
+     * <p>
+     * Optional, may be shown as a label on the positive button in a dialog or
+     * notification presented to the administrator who approves the request.
+     * <p>
+     * Type: String
+     */
+    public static final String REQUEST_KEY_APPROVE_LABEL = "android.req_template.accept";
+
+    /**
+     * Key for request rejection button label contained in the request bundle.
+     * <p>
+     * Optional, may be shown as a label on the negative button in a dialog or
+     * notification presented to the administrator who approves the request.
+     * <p>
+     * Type: String
+     */
+    public static final String REQUEST_KEY_DENY_LABEL = "android.req_template.reject";
+
+    /**
+     * Key for requestor's name contained in the request bundle. This value is not specified by
+     * the application. It is automatically inserted into the Bundle by the Restrictions Provider
+     * before it is sent to the administrator.
+     * <p>
+     * Type: String
+     */
+    public static final String REQUEST_KEY_REQUESTOR_NAME = "android.req_template.requestor";
+
+    /**
+     * Key for requestor's device name contained in the request bundle. This value is not specified
+     * by the application. It is automatically inserted into the Bundle by the Restrictions Provider
+     * before it is sent to the administrator.
+     * <p>
+     * Type: String
+     */
+    public static final String REQUEST_KEY_DEVICE_NAME = "android.req_template.device";
+
+    /**
+     * Key for the response in the response bundle sent to the application, for a permission
+     * request.
+     * <p>
+     * Type: boolean
+     */
+    public static final String RESPONSE_KEY_BOOLEAN = "android.req_template.response";
+
+    private static final String TAG = "RestrictionsManager";
+
+    private final Context mContext;
+    private final IRestrictionsManager mService;
+
+    /**
+     * @hide
+     */
+    public RestrictionsManager(Context context, IRestrictionsManager service) {
+        mContext = context;
+        mService = service;
+    }
+
+    /**
+     * Returns any available set of application-specific restrictions applicable
+     * to this application.
+     * @return
+     */
+    public Bundle getApplicationRestrictions() {
+        try {
+            if (mService != null) {
+                return mService.getApplicationRestrictions(mContext.getPackageName());
+            }
+        } catch (RemoteException re) {
+            Log.w(TAG, "Couldn't reach service");
+        }
+        return null;
+    }
+
+    /**
+     * Called by an application to check if permission requests can be made. If false,
+     * there is no need to request permission for an operation, unless a static
+     * restriction applies to that operation.
+     * @return
+     */
+    public boolean hasRestrictionsProvider() {
+        try {
+            if (mService != null) {
+                return mService.hasRestrictionsProvider();
+            }
+        } catch (RemoteException re) {
+            Log.w(TAG, "Couldn't reach service");
+        }
+        return false;
+    }
+
+    /**
+     * Called by an application to request permission for an operation. The contents of the
+     * request are passed in a Bundle that contains several pieces of data depending on the
+     * chosen request template.
+     *
+     * @param requestTemplate The request template to use. The template could be one of the
+     * predefined templates specified in this class or a custom template that the specific
+     * Restrictions Provider might understand. For custom templates, the template name should be
+     * namespaced to avoid collisions with predefined templates and templates specified by
+     * other Restrictions Provider vendors.
+     * @param requestData A Bundle containing the data corresponding to the specified request
+     * template. The keys for the data in the bundle depend on the kind of template chosen.
+     */
+    public void requestPermission(String requestTemplate, Bundle requestData) {
+        try {
+            if (mService != null) {
+                mService.requestPermission(mContext.getPackageName(), requestTemplate, requestData);
+            }
+        } catch (RemoteException re) {
+            Log.w(TAG, "Couldn't reach service");
+        }
+    }
+
+    /**
+     * Called by the Restrictions Provider when a response is available to be
+     * delivered to an application.
+     * @param packageName
+     * @param response
+     */
+    public void notifyPermissionResponse(String packageName, Bundle response) {
+        try {
+            if (mService != null) {
+                mService.notifyPermissionResponse(packageName, response);
+            }
+        } catch (RemoteException re) {
+            Log.w(TAG, "Couldn't reach service");
+        }
+    }
+
+    /**
+     * Parse and return the list of restrictions defined in the manifest for the specified
+     * package, if any.
+     * @param packageName The application for which to fetch the restrictions list.
+     * @return The list of RestrictionEntry objects created from the XML file specified
+     * in the manifest, or null if none was specified.
+     */
+    public List<RestrictionEntry> getManifestRestrictions(String packageName) {
+        // TODO:
+        return null;
+    }
+}
diff --git a/core/java/android/content/pm/IPackageManager.aidl b/core/java/android/content/pm/IPackageManager.aidl
index 44a6a5d..70668e1 100644
--- a/core/java/android/content/pm/IPackageManager.aidl
+++ b/core/java/android/content/pm/IPackageManager.aidl
@@ -433,6 +433,13 @@
             in VerificationParams verificationParams,
             in ContainerEncryptionParams encryptionParams);
 
+    void installPackageWithVerificationEncryptionAndAbiOverrideEtc(in Uri packageURI,
+            in IPackageInstallObserver observer, in IPackageInstallObserver2 observer2,
+            int flags, in String installerPackageName,
+            in VerificationParams verificationParams,
+            in ContainerEncryptionParams encryptionParams,
+	    in String packageAbiOverride);
+
     int installExistingPackageAsUser(String packageName, int userId);
 
     void verifyPendingInstall(int id, int verificationCode);
diff --git a/core/java/android/content/pm/LauncherActivityInfo.java b/core/java/android/content/pm/LauncherActivityInfo.java
index 9087338..5d48868 100644
--- a/core/java/android/content/pm/LauncherActivityInfo.java
+++ b/core/java/android/content/pm/LauncherActivityInfo.java
@@ -30,6 +30,7 @@
 import android.os.RemoteException;
 import android.os.UserHandle;
 import android.os.UserManager;
+import android.util.DisplayMetrics;
 import android.util.Log;
 
 /**
@@ -47,21 +48,22 @@
     private ActivityInfo mActivityInfo;
     private ComponentName mComponentName;
     private UserHandle mUser;
-    // TODO: Fetch this value from PM
     private long mFirstInstallTime;
 
     /**
      * Create a launchable activity object for a given ResolveInfo and user.
-     * 
+     *
      * @param context The context for fetching resources.
      * @param info ResolveInfo from which to create the LauncherActivityInfo.
      * @param user The UserHandle of the profile to which this activity belongs.
      */
-    LauncherActivityInfo(Context context, ResolveInfo info, UserHandle user) {
+    LauncherActivityInfo(Context context, ResolveInfo info, UserHandle user,
+            long firstInstallTime) {
         this(context);
-        this.mActivityInfo = info.activityInfo;
-        this.mComponentName = LauncherApps.getComponentName(info);
-        this.mUser = user;
+        mActivityInfo = info.activityInfo;
+        mComponentName = LauncherApps.getComponentName(info);
+        mUser = user;
+        mFirstInstallTime = firstInstallTime;
     }
 
     LauncherActivityInfo(Context context) {
@@ -79,7 +81,13 @@
     }
 
     /**
-     * Returns the user handle of the user profile that this activity belongs to.
+     * Returns the user handle of the user profile that this activity belongs to. In order to
+     * persist the identity of the profile, do not store the UserHandle. Instead retrieve its
+     * serial number from UserManager. You can convert the serial number back to a UserHandle
+     * for later use.
+     *
+     * @see UserManager#getSerialNumberForUser(UserHandle)
+     * @see UserManager#getUserForSerialNumber(long)
      *
      * @return The UserHandle of the profile.
      */
@@ -89,7 +97,7 @@
 
     /**
      * Retrieves the label for the activity.
-     * 
+     *
      * @return The label for the activity.
      */
     public CharSequence getLabel() {
@@ -98,8 +106,10 @@
 
     /**
      * Returns the icon for this activity, without any badging for the profile.
-     * @param density The preferred density of the icon, zero for default density.
+     * @param density The preferred density of the icon, zero for default density. Use
+     * density DPI values from {@link DisplayMetrics}.
      * @see #getBadgedIcon(int)
+     * @see DisplayMetrics
      * @return The drawable associated with the activity
      */
     public Drawable getIcon(int density) {
@@ -109,15 +119,25 @@
 
     /**
      * Returns the application flags from the ApplicationInfo of the activity.
-     * 
+     *
      * @return Application flags
+     * @hide remove before shipping
      */
     public int getApplicationFlags() {
         return mActivityInfo.applicationInfo.flags;
     }
 
     /**
+     * Returns the application info for the appliction this activity belongs to.
+     * @return
+     */
+    public ApplicationInfo getApplicationInfo() {
+        return mActivityInfo.applicationInfo;
+    }
+
+    /**
      * Returns the time at which the package was first installed.
+     *
      * @return The time of installation of the package, in milliseconds.
      */
     public long getFirstInstallTime() {
@@ -134,7 +154,9 @@
 
     /**
      * Returns the activity icon with badging appropriate for the profile.
-     * @param density Optional density for the icon, or 0 to use the default density.
+     * @param density Optional density for the icon, or 0 to use the default density. Use
+     * {@link DisplayMetrics} for DPI values.
+     * @see DisplayMetrics
      * @return A badged icon for the activity.
      */
     public Drawable getBadgedIcon(int density) {
diff --git a/core/java/android/content/pm/LauncherApps.java b/core/java/android/content/pm/LauncherApps.java
index 8025b60..04c0b9f 100644
--- a/core/java/android/content/pm/LauncherApps.java
+++ b/core/java/android/content/pm/LauncherApps.java
@@ -16,15 +16,18 @@
 
 package android.content.pm;
 
+import android.app.AppGlobals;
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
 import android.content.pm.ILauncherApps;
 import android.content.pm.IOnAppsChangedListener;
+import android.content.pm.PackageManager.NameNotFoundException;
 import android.graphics.Rect;
 import android.os.Bundle;
 import android.os.RemoteException;
 import android.os.UserHandle;
+import android.os.UserManager;
 import android.util.Log;
 
 import java.util.ArrayList;
@@ -36,6 +39,12 @@
  * managed profiles. This is mainly for use by launchers. Apps can be queried for each user profile.
  * Since the PackageManager will not deliver package broadcasts for other profiles, you can register
  * for package changes here.
+ * <p>
+ * To watch for managed profiles being added or removed, register for the following broadcasts:
+ * {@link Intent#ACTION_MANAGED_PROFILE_ADDED} and {@link Intent#ACTION_MANAGED_PROFILE_REMOVED}.
+ * <p>
+ * You can retrieve the list of profiles associated with this user with
+ * {@link UserManager#getUserProfiles()}.
  */
 public class LauncherApps {
 
@@ -44,12 +53,13 @@
 
     private Context mContext;
     private ILauncherApps mService;
+    private PackageManager mPm;
 
     private List<OnAppsChangedListener> mListeners
             = new ArrayList<OnAppsChangedListener>();
 
     /**
-     * Callbacks for changes to this and related managed profiles.
+     * Callbacks for package changes to this and related managed profiles.
      */
     public interface OnAppsChangedListener {
         /**
@@ -57,6 +67,7 @@
          *
          * @param user The UserHandle of the profile that generated the change.
          * @param packageName The name of the package that was removed.
+         * @hide remove before ship
          */
         void onPackageRemoved(UserHandle user, String packageName);
 
@@ -65,6 +76,7 @@
          *
          * @param user The UserHandle of the profile that generated the change.
          * @param packageName The name of the package that was added.
+         * @hide remove before ship
          */
         void onPackageAdded(UserHandle user, String packageName);
 
@@ -73,6 +85,7 @@
          *
          * @param user The UserHandle of the profile that generated the change.
          * @param packageName The name of the package that has changed.
+         * @hide remove before ship
          */
         void onPackageChanged(UserHandle user, String packageName);
 
@@ -86,6 +99,7 @@
          *            available.
          * @param replacing Indicates whether these packages are replacing
          *            existing ones.
+         * @hide remove before ship
          */
         void onPackagesAvailable(UserHandle user, String[] packageNames, boolean replacing);
 
@@ -99,14 +113,66 @@
          *            unavailable.
          * @param replacing Indicates whether the packages are about to be
          *            replaced with new versions.
+         * @hide remove before ship
          */
         void onPackagesUnavailable(UserHandle user, String[] packageNames, boolean replacing);
+
+        /**
+         * 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.
+         */
+        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.
+         */
+        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.
+         */
+        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.
+         */
+        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.
+         */
+        void onPackagesUnavailable(String[] packageNames, UserHandle user, boolean replacing);
     }
 
     /** @hide */
     public LauncherApps(Context context, ILauncherApps service) {
         mContext = context;
         mService = service;
+        mPm = context.getPackageManager();
     }
 
     /**
@@ -131,7 +197,15 @@
         final int count = activities.size();
         for (int i = 0; i < count; i++) {
             ResolveInfo ri = activities.get(i);
-            LauncherActivityInfo lai = new LauncherActivityInfo(mContext, ri, user);
+            long firstInstallTime = 0;
+            try {
+                firstInstallTime = mPm.getPackageInfo(ri.activityInfo.packageName,
+                    PackageManager.GET_UNINSTALLED_PACKAGES).firstInstallTime;
+            } catch (NameNotFoundException nnfe) {
+                // Sorry, can't find package
+            }
+            LauncherActivityInfo lai = new LauncherActivityInfo(mContext, ri, user,
+                    firstInstallTime);
             if (DEBUG) {
                 Log.v(TAG, "Returning activity for profile " + user + " : "
                         + lai.getComponentName());
@@ -157,7 +231,15 @@
         try {
             ResolveInfo ri = mService.resolveActivity(intent, user);
             if (ri != null) {
-                LauncherActivityInfo info = new LauncherActivityInfo(mContext, ri, user);
+                long firstInstallTime = 0;
+                try {
+                    firstInstallTime = mPm.getPackageInfo(ri.activityInfo.packageName,
+                            PackageManager.GET_UNINSTALLED_PACKAGES).firstInstallTime;
+                } catch (NameNotFoundException nnfe) {
+                    // Sorry, can't find package
+                }
+                LauncherActivityInfo info = new LauncherActivityInfo(mContext, ri, user,
+                        firstInstallTime);
                 return info;
             }
         } catch (RemoteException re) {
@@ -173,9 +255,23 @@
      * @param sourceBounds The Rect containing the source bounds of the clicked icon
      * @param opts Options to pass to startActivity
      * @param user The UserHandle of the profile
+     * @hide remove before ship
      */
     public void startActivityForProfile(ComponentName component, Rect sourceBounds,
             Bundle opts, UserHandle user) {
+        startActivityForProfile(component, user, sourceBounds, opts);
+    }
+
+    /**
+     * Starts an activity in the specified profile.
+     *
+     * @param component The ComponentName of the activity to launch
+     * @param user The UserHandle of the profile
+     * @param sourceBounds The Rect containing the source bounds of the clicked icon
+     * @param opts Options to pass to startActivity
+     */
+    public void startActivityForProfile(ComponentName component, UserHandle user, Rect sourceBounds,
+            Bundle opts) {
         if (DEBUG) {
             Log.i(TAG, "StartActivityForProfile " + component + " " + user.getIdentifier());
         }
@@ -224,13 +320,15 @@
      *
      * @param listener The listener to add.
      */
-    public synchronized void addOnAppsChangedListener(OnAppsChangedListener listener) {
-        if (listener != null && !mListeners.contains(listener)) {
-            mListeners.add(listener);
-            if (mListeners.size() == 1) {
-                try {
-                    mService.addOnAppsChangedListener(mAppsChangedListener);
-                } catch (RemoteException re) {
+    public void addOnAppsChangedListener(OnAppsChangedListener listener) {
+        synchronized (this) {
+            if (listener != null && !mListeners.contains(listener)) {
+                mListeners.add(listener);
+                if (mListeners.size() == 1) {
+                    try {
+                        mService.addOnAppsChangedListener(mAppsChangedListener);
+                    } catch (RemoteException re) {
+                    }
                 }
             }
         }
@@ -242,12 +340,14 @@
      * @param listener The listener to remove.
      * @see #addOnAppsChangedListener(OnAppsChangedListener)
      */
-    public synchronized void removeOnAppsChangedListener(OnAppsChangedListener listener) {
-        mListeners.remove(listener);
-        if (mListeners.size() == 0) {
-            try {
-                mService.removeOnAppsChangedListener(mAppsChangedListener);
-            } catch (RemoteException re) {
+    public void removeOnAppsChangedListener(OnAppsChangedListener listener) {
+        synchronized (this) {
+            mListeners.remove(listener);
+            if (mListeners.size() == 0) {
+                try {
+                    mService.removeOnAppsChangedListener(mAppsChangedListener);
+                } catch (RemoteException re) {
+                }
             }
         }
     }
@@ -261,7 +361,8 @@
             }
             synchronized (LauncherApps.this) {
                 for (OnAppsChangedListener listener : mListeners) {
-                    listener.onPackageRemoved(user, packageName);
+                    listener.onPackageRemoved(user, packageName); // TODO: Remove before ship
+                    listener.onPackageRemoved(packageName, user);
                 }
             }
         }
@@ -273,7 +374,8 @@
             }
             synchronized (LauncherApps.this) {
                 for (OnAppsChangedListener listener : mListeners) {
-                    listener.onPackageChanged(user, packageName);
+                    listener.onPackageChanged(user, packageName); // TODO: Remove before ship
+                    listener.onPackageChanged(packageName, user);
                 }
             }
         }
@@ -285,7 +387,8 @@
             }
             synchronized (LauncherApps.this) {
                 for (OnAppsChangedListener listener : mListeners) {
-                    listener.onPackageAdded(user, packageName);
+                    listener.onPackageAdded(user, packageName); // TODO: Remove before ship
+                    listener.onPackageAdded(packageName, user);
                 }
             }
         }
@@ -298,7 +401,8 @@
             }
             synchronized (LauncherApps.this) {
                 for (OnAppsChangedListener listener : mListeners) {
-                    listener.onPackagesAvailable(user, packageNames, replacing);
+                    listener.onPackagesAvailable(user, packageNames, replacing); // TODO: Remove
+                    listener.onPackagesAvailable(packageNames, user, replacing);
                 }
             }
         }
@@ -311,7 +415,8 @@
             }
             synchronized (LauncherApps.this) {
                 for (OnAppsChangedListener listener : mListeners) {
-                    listener.onPackagesUnavailable(user, packageNames, replacing);
+                    listener.onPackagesUnavailable(user, packageNames, replacing); // TODO: Remove
+                    listener.onPackagesUnavailable(packageNames, user, replacing);
                 }
             }
         }
diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java
index 1c838c3..ab8bf61 100644
--- a/core/java/android/content/pm/PackageParser.java
+++ b/core/java/android/content/pm/PackageParser.java
@@ -2174,7 +2174,6 @@
         }
 
         final int innerDepth = parser.getDepth();
-
         int type;
         while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
                 && (type != XmlPullParser.END_TAG || parser.getDepth() > innerDepth)) {
@@ -2548,13 +2547,13 @@
                     com.android.internal.R.styleable.AndroidManifestActivity_singleUser,
                     false)) {
                 a.info.flags |= ActivityInfo.FLAG_SINGLE_USER;
-                if (a.info.exported) {
+                if (a.info.exported && (flags & PARSE_IS_PRIVILEGED) == 0) {
                     Slog.w(TAG, "Activity exported request ignored due to singleUser: "
                             + a.className + " at " + mArchiveSourcePath + " "
                             + parser.getPositionDescription());
                     a.info.exported = false;
+                    setExported = true;
                 }
-                setExported = true;
             }
             if (sa.getBoolean(
                     com.android.internal.R.styleable.AndroidManifestActivity_primaryUserOnly,
@@ -2907,7 +2906,7 @@
                 com.android.internal.R.styleable.AndroidManifestProvider_singleUser,
                 false)) {
             p.info.flags |= ProviderInfo.FLAG_SINGLE_USER;
-            if (p.info.exported) {
+            if (p.info.exported && (flags & PARSE_IS_PRIVILEGED) == 0) {
                 Slog.w(TAG, "Provider exported request ignored due to singleUser: "
                         + p.className + " at " + mArchiveSourcePath + " "
                         + parser.getPositionDescription());
@@ -3181,13 +3180,13 @@
                 com.android.internal.R.styleable.AndroidManifestService_singleUser,
                 false)) {
             s.info.flags |= ServiceInfo.FLAG_SINGLE_USER;
-            if (s.info.exported) {
+            if (s.info.exported && (flags & PARSE_IS_PRIVILEGED) == 0) {
                 Slog.w(TAG, "Service exported request ignored due to singleUser: "
                         + s.className + " at " + mArchiveSourcePath + " "
                         + parser.getPositionDescription());
                 s.info.exported = false;
+                setExported = true;
             }
-            setExported = true;
         }
 
         sa.recycle();
diff --git a/core/java/android/content/res/Resources.java b/core/java/android/content/res/Resources.java
index 3737638..ed3f9aa 100644
--- a/core/java/android/content/res/Resources.java
+++ b/core/java/android/content/res/Resources.java
@@ -702,12 +702,17 @@
      * Context.obtainStyledAttributes} with
      * an array containing the resource ID of interest to create the TypedArray.</p>
      *
+     * <p class="note"><strong>Note:</strong> To obtain a themed drawable, use
+     * {@link android.content.Context#getDrawable(int) Context.getDrawable(int)}
+     * or {@link #getDrawable(int, Theme)} passing the desired theme.</p>
+     *
      * @param id The desired resource identifier, as generated by the aapt
      *           tool. This integer encodes the package, type, and resource
      *           entry. The value 0 is an invalid identifier.
      * @return Drawable An object that can be used to draw this resource.
      * @throws NotFoundException Throws NotFoundException if the given ID does
      *         not exist.
+     * @see #getDrawable(int, Theme)
      */
     public Drawable getDrawable(int id) throws NotFoundException {
         return getDrawable(id, null);
@@ -715,7 +720,9 @@
 
     /**
      * Return a drawable object associated with a particular resource ID and
-     * styled for the specified theme.
+     * styled for the specified theme. Various types of objects will be
+     * returned depending on the underlying resource -- for example, a solid
+     * color, PNG image, scalable image, etc.
      *
      * @param id The desired resource identifier, as generated by the aapt
      *           tool. This integer encodes the package, type, and resource
@@ -755,6 +762,11 @@
      * image, scalable image, etc. The Drawable API hides these implementation
      * details.
      *
+     * <p class="note"><strong>Note:</strong> To obtain a themed drawable, use
+     * {@link android.content.Context#getDrawable(int) Context.getDrawable(int)}
+     * or {@link #getDrawableForDensity(int, int, Theme)} passing the desired
+     * theme.</p>
+     *
      * @param id The desired resource identifier, as generated by the aapt tool.
      *            This integer encodes the package, type, and resource entry.
      *            The value 0 is an invalid identifier.
@@ -2340,12 +2352,12 @@
             if (file.endsWith(".xml")) {
                 final XmlResourceParser rp = loadXmlResourceParser(
                         file, id, value.assetCookie, "drawable");
-                dr = Drawable.createFromXmlThemed(this, rp, theme);
+                dr = Drawable.createFromXml(this, rp, theme);
                 rp.close();
             } else {
                 final InputStream is = mAssets.openNonAsset(
                         value.assetCookie, file, AssetManager.ACCESS_STREAMING);
-                dr = Drawable.createFromResourceStreamThemed(this, value, is, file, null, theme);
+                dr = Drawable.createFromResourceStream(this, value, is, file, null);
                 is.close();
             }
         } catch (Exception e) {
diff --git a/core/java/android/hardware/Sensor.java b/core/java/android/hardware/Sensor.java
index c593e9e..c8de2f1 100644
--- a/core/java/android/hardware/Sensor.java
+++ b/core/java/android/hardware/Sensor.java
@@ -142,9 +142,10 @@
     public static final String STRING_TYPE_TEMPERATURE = "android.sensor.temperature";
 
     /**
-     * A constant describing a proximity sensor type.
+     * A constant describing a proximity sensor type. This is a wake up sensor.
      * <p>See {@link android.hardware.SensorEvent#values SensorEvent.values}
      * for more details.
+     * @see #isWakeUpSensor()
      */
     public static final int TYPE_PROXIMITY = 8;
 
@@ -307,8 +308,10 @@
      * itself. The sensor continues to operate while the device is asleep
      * and will automatically wake the device to notify when significant
      * motion is detected. The application does not need to hold any wake
-     * locks for this sensor to trigger.
+     * locks for this sensor to trigger. This is a wake up sensor.
      * <p>See {@link TriggerEvent} for more details.
+     *
+     * @see #isWakeUpSensor()
      */
     public static final int TYPE_SIGNIFICANT_MOTION = 17;
 
@@ -381,11 +384,17 @@
     /**
      * A constant describing a heart rate monitor.
      * <p>
-     * A sensor that measures the heart rate in beats per minute.
+     * The reported value is the heart rate in beats per minute.
      * <p>
-     * value[0] represents the beats per minute when the measurement was taken.
-     * value[0] is 0 if the heart rate monitor could not measure the rate or the
-     * rate is 0 beat per minute.
+     * The reported accuracy represents the status of the monitor during the reading. See the
+     * {@code SENSOR_STATUS_*} constants in {@link android.hardware.SensorManager SensorManager}
+     * for more details on accuracy/status values. In particular, when the accuracy is
+     * {@code SENSOR_STATUS_UNRELIABLE} or {@code SENSOR_STATUS_NO_CONTACT}, the heart rate
+     * value should be discarded.
+     * <p>
+     * This sensor requires permission {@code android.permission.BODY_SENSORS}.
+     * It will not be returned by {@code SensorManager.getSensorsList} nor
+     * {@code SensorManager.getDefaultSensor} if the application doesn't have this permission.
      */
     public static final int TYPE_HEART_RATE = 21;
 
@@ -397,6 +406,321 @@
     public static final String STRING_TYPE_HEART_RATE = "android.sensor.heart_rate";
 
     /**
+     * A non-wake up variant of proximity sensor.
+     *
+     * @see #TYPE_PROXIMITY
+     */
+    public static final int TYPE_NON_WAKE_UP_PROXIMITY_SENSOR = 22;
+
+    /**
+     * A constant string describing a non-wake up proximity sensor type.
+     *
+     * @see #TYPE_NON_WAKE_UP_PROXIMITY_SENSOR
+     */
+    public static final String SENSOR_STRING_TYPE_NON_WAKE_UP_PROXIMITY_SENSOR =
+            "android.sensor.non_wake_up_proximity_sensor";
+
+    /**
+     * A constant describing a wake up variant of accelerometer sensor type.
+     *
+     * @see #isWakeUpSensor()
+     * @see #TYPE_ACCELEROMETER
+     */
+    public static final int TYPE_WAKE_UP_ACCELEROMETER = 23;
+
+    /**
+     * A constant string describing a wake up accelerometer.
+     *
+     * @see #TYPE_WAKE_UP_ACCELEROMETER
+     */
+    public static final String STRING_TYPE_WAKE_UP_ACCELEROMETER =
+            "android.sensor.wake_up_accelerometer";
+
+    /**
+     * A constant describing a wake up variant of a magnetic field sensor type.
+     *
+     * @see #isWakeUpSensor()
+     * @see #TYPE_MAGNETIC_FIELD
+     */
+    public static final int TYPE_WAKE_UP_MAGNETIC_FIELD = 24;
+
+    /**
+     * A constant string describing a wake up magnetic field sensor.
+     *
+     * @see #TYPE_WAKE_UP_MAGNETIC_FIELD
+     */
+    public static final String STRING_TYPE_WAKE_UP_MAGNETIC_FIELD =
+            "android.sensor.wake_up_magnetic_field";
+
+    /**
+     * A constant describing a wake up variant of an orientation sensor type.
+     *
+     * @see #isWakeUpSensor()
+     * @see #TYPE_ORIENTATION
+     */
+    public static final int TYPE_WAKE_UP_ORIENTATION = 25;
+
+    /**
+     * A constant string describing a wake up orientation sensor.
+     *
+     * @see #TYPE_WAKE_UP_ORIENTATION
+     */
+    public static final String STRING_TYPE_WAKE_UP_ORIENTATION =
+            "android.sensor.wake_up_orientation";
+
+    /**
+     * A constant describing a wake up variant of a gyroscope sensor type.
+     *
+     * @see #isWakeUpSensor()
+     * @see #TYPE_GYROSCOPE
+     */
+    public static final int TYPE_WAKE_UP_GYROSCOPE = 26;
+
+    /**
+     * A constant string describing a wake up gyroscope sensor type.
+     *
+     * @see #TYPE_WAKE_UP_GYROSCOPE
+     */
+    public static final String STRING_TYPE_WAKE_UP_GYROSCOPE = "android.sensor.wake_up_gyroscope";
+
+    /**
+     * A constant describing a wake up variant of a light sensor type.
+     *
+     * @see #isWakeUpSensor()
+     * @see #TYPE_LIGHT
+     */
+    public static final int TYPE_WAKE_UP_LIGHT = 27;
+
+    /**
+     * A constant string describing a wake up light sensor type.
+     *
+     * @see #TYPE_WAKE_UP_LIGHT
+     */
+    public static final String STRING_TYPE_WAKE_UP_LIGHT = "android.sensor.wake_up_light";
+
+    /**
+     * A constant describing a wake up variant of a pressure sensor type.
+     *
+     * @see #isWakeUpSensor()
+     * @see #TYPE_PRESSURE
+     */
+    public static final int TYPE_WAKE_UP_PRESSURE = 28;
+
+    /**
+     * A constant string describing a wake up pressure sensor type.
+     *
+     * @see #TYPE_WAKE_UP_PRESSURE
+     */
+    public static final String STRING_TYPE_WAKE_UP_PRESSURE = "android.sensor.wake_up_pressure";
+
+    /**
+     * A constant describing a wake up variant of a gravity sensor type.
+     *
+     * @see #isWakeUpSensor()
+     * @see #TYPE_GRAVITY
+     */
+    public static final int TYPE_WAKE_UP_GRAVITY = 29;
+
+    /**
+     * A constant string describing a wake up gravity sensor type.
+     *
+     * @see #TYPE_WAKE_UP_GRAVITY
+     */
+    public static final String STRING_TYPE_WAKE_UP_GRAVITY = "android.sensor.wake_up_gravity";
+
+    /**
+     * A constant describing a wake up variant of a linear acceleration sensor type.
+     *
+     * @see #isWakeUpSensor()
+     * @see #TYPE_LINEAR_ACCELERATION
+     */
+    public static final int TYPE_WAKE_UP_LINEAR_ACCELERATION = 30;
+
+    /**
+     * A constant string describing a wake up linear acceleration sensor type.
+     *
+     * @see #TYPE_WAKE_UP_LINEAR_ACCELERATION
+     */
+    public static final String STRING_TYPE_WAKE_UP_LINEAR_ACCELERATION =
+            "android.sensor.wake_up_linear_acceleration";
+
+    /**
+     * A constant describing a wake up variant of a rotation vector sensor type.
+     *
+     * @see #isWakeUpSensor()
+     * @see #TYPE_ROTATION_VECTOR
+     */
+    public static final int TYPE_WAKE_UP_ROTATION_VECTOR = 31;
+
+    /**
+     * A constant string describing a wake up rotation vector sensor type.
+     *
+     * @see #TYPE_WAKE_UP_ROTATION_VECTOR
+     */
+    public static final String STRING_TYPE_WAKE_UP_ROTATION_VECTOR =
+            "android.sensor.wake_up_rotation_vector";
+
+    /**
+     * A constant describing a wake up variant of a relative humidity sensor type.
+     *
+     * @see #isWakeUpSensor()
+     * @see #TYPE_RELATIVE_HUMIDITY
+     */
+    public static final int TYPE_WAKE_UP_RELATIVE_HUMIDITY = 32;
+
+    /**
+     * A constant string describing a wake up relative humidity sensor type.
+     *
+     * @see #TYPE_WAKE_UP_RELATIVE_HUMIDITY
+     */
+    public static final String STRING_TYPE_WAKE_UP_RELATIVE_HUMIDITY =
+            "android.sensor.wake_up_relative_humidity";
+
+    /**
+     * A constant describing a wake up variant of an ambient temperature sensor type.
+     *
+     * @see #isWakeUpSensor()
+     * @see #TYPE_AMBIENT_TEMPERATURE
+     */
+    public static final int TYPE_WAKE_UP_AMBIENT_TEMPERATURE = 33;
+
+    /**
+     * A constant string describing a wake up ambient temperature sensor type.
+     *
+     * @see #TYPE_WAKE_UP_AMBIENT_TEMPERATURE
+     */
+    public static final String STRING_TYPE_WAKE_UP_AMBIENT_TEMPERATURE =
+            "android.sensor.wake_up_ambient_temperature";
+
+    /**
+     * A constant describing a wake up variant of an uncalibrated magnetic field sensor type.
+     *
+     * @see #isWakeUpSensor()
+     * @see #TYPE_MAGNETIC_FIELD_UNCALIBRATED
+     */
+    public static final int TYPE_WAKE_UP_MAGNETIC_FIELD_UNCALIBRATED = 34;
+
+    /**
+     * A constant string describing a wake up uncalibrated magnetic field sensor type.
+     *
+     * @see #TYPE_WAKE_UP_MAGNETIC_FIELD_UNCALIBRATED
+     */
+    public static final String STRING_TYPE_WAKE_UP_MAGNETIC_FIELD_UNCALIBRATED =
+            "android.sensor.wake_up_magnetic_field_uncalibrated";
+
+    /**
+     * A constant describing a wake up variant of a game rotation vector sensor type.
+     *
+     * @see #isWakeUpSensor()
+     * @see #TYPE_GAME_ROTATION_VECTOR
+     */
+    public static final int TYPE_WAKE_UP_GAME_ROTATION_VECTOR = 35;
+
+    /**
+     * A constant string describing a wake up game rotation vector sensor type.
+     *
+     * @see #TYPE_WAKE_UP_GAME_ROTATION_VECTOR
+     */
+    public static final String STRING_TYPE_WAKE_UP_GAME_ROTATION_VECTOR =
+            "android.sensor.wake_up_game_rotation_vector";
+
+    /**
+     * A constant describing a wake up variant of an uncalibrated gyroscope sensor type.
+     *
+     * @see #isWakeUpSensor()
+     * @see #TYPE_GYROSCOPE_UNCALIBRATED
+     */
+    public static final int TYPE_WAKE_UP_GYROSCOPE_UNCALIBRATED = 36;
+
+    /**
+     * A constant string describing a wake up uncalibrated gyroscope sensor type.
+     *
+     * @see #TYPE_WAKE_UP_GYROSCOPE_UNCALIBRATED
+     */
+    public static final String STRING_TYPE_WAKE_UP_GYROSCOPE_UNCALIBRATED =
+            "android.sensor.wake_up_gyroscope_uncalibrated";
+
+    /**
+     * A constant describing a wake up variant of a step detector sensor type.
+     *
+     * @see #isWakeUpSensor()
+     * @see #TYPE_STEP_DETECTOR
+     */
+    public static final int TYPE_WAKE_UP_STEP_DETECTOR = 37;
+
+    /**
+     * A constant string describing a wake up step detector sensor type.
+     *
+     * @see #TYPE_WAKE_UP_STEP_DETECTOR
+     */
+    public static final String STRING_TYPE_WAKE_UP_STEP_DETECTOR =
+            "android.sensor.wake_up_step_detector";
+
+    /**
+     * A constant describing a wake up variant of a step counter sensor type.
+     *
+     * @see #isWakeUpSensor()
+     * @see #TYPE_STEP_COUNTER
+     */
+    public static final int TYPE_WAKE_UP_STEP_COUNTER = 38;
+
+    /**
+     * A constant string describing a wake up step counter sensor type.
+     *
+     * @see #TYPE_WAKE_UP_STEP_COUNTER
+     */
+    public static final String STRING_TYPE_WAKE_UP_STEP_COUNTER =
+            "android.sensor.wake_up_step_counter";
+
+    /**
+     * A constant describing a wake up variant of a geomagnetic rotation vector sensor type.
+     *
+     * @see #isWakeUpSensor()
+     * @see #TYPE_GEOMAGNETIC_ROTATION_VECTOR
+     */
+    public static final int TYPE_WAKE_UP_GEOMAGNETIC_ROTATION_VECTOR = 39;
+
+    /**
+     * A constant string describing a wake up geomagnetic rotation vector sensor type.
+     *
+     * @see #TYPE_WAKE_UP_GEOMAGNETIC_ROTATION_VECTOR
+     */
+    public static final String STRING_TYPE_WAKE_UP_GEOMAGNETIC_ROTATION_VECTOR =
+            "android.sensor.wake_up_geomagnetic_rotation_vector";
+
+    /**
+     * A constant describing a wake up variant of a heart rate sensor type.
+     *
+     * @see #isWakeUpSensor()
+     * @see #TYPE_HEART_RATE
+     */
+    public static final int TYPE_WAKE_UP_HEART_RATE = 40;
+
+    /**
+     * A constant string describing a wake up heart rate sensor type.
+     *
+     * @see #TYPE_WAKE_UP_HEART_RATE
+     */
+    public static final String STRING_TYPE_WAKE_UP_HEART_RATE = "android.sensor.wake_up_heart_rate";
+
+    /**
+     * A sensor of this type generates an event each time a tilt event is detected. A tilt event
+     * is generated if the direction of the 2-seconds window average gravity changed by at
+     * least 35 degrees since the activation of the sensor. It is a wake up sensor.
+     *
+     * @see #isWakeUpSensor()
+     */
+    public static final int TYPE_WAKE_UP_TILT_DETECTOR = 41;
+
+    /**
+     * A constant string describing a wake up tilt detector sensor type.
+     *
+     * @see #TYPE_WAKE_UP_TILT_DETECTOR
+     */
+    public static final String SENSOR_STRING_TYPE_WAKE_UP_TILT_DETECTOR =
+            "android.sensor.wake_up_tilt_detector";
+
+    /**
      * A constant describing a wake gesture sensor.
      * <p>
      * Wake gesture sensors enable waking up the device based on a device specific motion.
@@ -410,6 +734,7 @@
      * the device. This sensor must be low power, as it is likely to be activated 24/7.
      * Values of events created by this sensors should not be used.
      *
+     * @see #isWakeUpSensor()
      * @hide This sensor is expected to only be used by the power manager
      */
     public static final int TYPE_WAKE_GESTURE = 42;
@@ -467,7 +792,29 @@
             REPORTING_MODE_ON_CHANGE,  1, // SENSOR_TYPE_STEP_DETECTOR
             REPORTING_MODE_ON_CHANGE,  1, // SENSOR_TYPE_STEP_COUNTER
             REPORTING_MODE_CONTINUOUS, 5, // SENSOR_TYPE_GEOMAGNETIC_ROTATION_VECTOR
-            REPORTING_MODE_ON_CHANGE, 1  // SENSOR_TYPE_HEART_RATE_MONITOR
+            REPORTING_MODE_ON_CHANGE,  1, // SENSOR_TYPE_HEART_RATE_MONITOR
+            REPORTING_MODE_ON_CHANGE,  3, // SENSOR_TYPE_NON_WAKE_UP_PROXIMITY_SENSOR
+            // wake up variants of base sensors
+            REPORTING_MODE_CONTINUOUS, 3, // SENSOR_TYPE_WAKE_UP_ACCELEROMETER
+            REPORTING_MODE_CONTINUOUS, 3, // SENSOR_TYPE_WAKE_UP_MAGNETIC_FIELD
+            REPORTING_MODE_CONTINUOUS, 3, // SENSOR_TYPE_WAKE_UP_ORIENTATION
+            REPORTING_MODE_CONTINUOUS, 3, // SENSOR_TYPE_WAKE_UP_GYROSCOPE
+            REPORTING_MODE_ON_CHANGE,  3, // SENSOR_TYPE_WAKE_UP_LIGHT
+            REPORTING_MODE_CONTINUOUS, 3, // SENSOR_TYPE_WAKE_UP_PRESSURE
+            REPORTING_MODE_CONTINUOUS, 3, // SENSOR_TYPE_WAKE_UP_GRAVITY
+            REPORTING_MODE_CONTINUOUS, 3, // SENSOR_TYPE_WAKE_UP_LINEAR_ACCELERATION
+            REPORTING_MODE_CONTINUOUS, 5, // SENSOR_TYPE_WAKE_UP_ROTATION_VECTOR
+            REPORTING_MODE_ON_CHANGE,  3, // SENSOR_TYPE_WAKE_UP_RELATIVE_HUMIDITY
+            REPORTING_MODE_ON_CHANGE,  3, // SENSOR_TYPE_WAKE_UP_AMBIENT_TEMPERATURE
+            REPORTING_MODE_CONTINUOUS, 6, // SENSOR_TYPE_WAKE_UP_MAGNETIC_FIELD_UNCALIBRATED
+            REPORTING_MODE_CONTINUOUS, 4, // SENSOR_TYPE_WAKE_UP_GAME_ROTATION_VECTOR
+            REPORTING_MODE_CONTINUOUS, 6, // SENSOR_TYPE_WAKE_UP_GYROSCOPE_UNCALIBRATED
+            REPORTING_MODE_ON_CHANGE,  1, // SENSOR_TYPE_WAKE_UP_STEP_DETECTOR
+            REPORTING_MODE_ON_CHANGE,  1, // SENSOR_TYPE_WAKE_UP_STEP_COUNTER
+            REPORTING_MODE_CONTINUOUS, 5, // SENSOR_TYPE_WAKE_UP_GEOMAGNETIC_ROTATION_VECTOR
+            REPORTING_MODE_ON_CHANGE,  1, // SENSOR_TYPE_WAKE_UP_HEART_RATE_MONITOR
+            REPORTING_MODE_ON_CHANGE,  1, // SENSOR_TYPE_WAKE_UP_TILT_DETECTOR
+            REPORTING_MODE_ONE_SHOT,   1, // SENSOR_TYPE_WAKE_GESTURE
     };
 
     static int getReportingMode(Sensor sensor) {
@@ -525,6 +872,8 @@
     private int     mFifoMaxEventCount;
     private String  mStringType;
     private String  mRequiredPermission;
+    private int     mMaxDelay;
+    private boolean mWakeUpSensor;
 
     Sensor() {
     }
@@ -625,6 +974,51 @@
         return mHandle;
     }
 
+    /**
+     * This value is defined only for continuous mode sensors. It is the delay between two
+     * sensor events corresponding to the lowest frequency that this sensor supports. When
+     * lower frequencies are requested through registerListener() the events will be generated
+     * at this frequency instead. It can be used to estimate when the batch FIFO may be full.
+     * Older devices may set this value to zero. Ignore this value in case it is negative
+     * or zero.
+     *
+     * @return The max delay for this sensor in microseconds.
+     */
+    public int getMaxDelay() {
+        return mMaxDelay;
+    }
+
+    /**
+     * Returns whether this sensor is a wake-up sensor.
+     * <p>
+     * Wake up sensors wake the application processor up when they have events to deliver. When a
+     * wake up sensor is registered to without batching enabled, each event will wake the
+     * application processor up.
+     * <p>
+     * When a wake up sensor is registered to with batching enabled, it
+     * wakes the application processor up when maxReportingLatency has elapsed or when the hardware
+     * FIFO storing the events from wake up sensors is getting full.
+     * <p>
+     * Non-wake up sensors never wake the application processor up. Their events are only reported
+     * when the application processor is awake, for example because the application holds a wake
+     * lock, or another source woke the application processor up.
+     * <p>
+     * When a non-wake up sensor is registered to without batching enabled, the measurements made
+     * while the application processor is asleep might be lost and never returned.
+     * <p>
+     * When a non-wake up sensor is registered to with batching enabled, the measurements made while
+     * the application processor is asleep are stored in the hardware FIFO for non-wake up sensors.
+     * When this FIFO gets full, new events start overwriting older events. When the application
+     * then wakes up, the latest events are returned, and some old events might be lost. The number
+     * of events actually returned depends on the hardware FIFO size, as well as on what other
+     * sensors are activated. If losing sensor events is not acceptable during batching, you must
+     * use the wake-up version of the sensor.
+     * @return true if this is a wake up sensor, false otherwise.
+     */
+    public boolean isWakeUpSensor() {
+        return mWakeUpSensor;
+    }
+
     void setRange(float max, float res) {
         mMaxRange = max;
         mResolution = res;
diff --git a/core/java/android/hardware/SensorEventListener.java b/core/java/android/hardware/SensorEventListener.java
index 677d244..0d859fb 100644
--- a/core/java/android/hardware/SensorEventListener.java
+++ b/core/java/android/hardware/SensorEventListener.java
@@ -39,11 +39,13 @@
     public void onSensorChanged(SensorEvent event);
 
     /**
-     * Called when the accuracy of a sensor has changed.
-     * <p>See {@link android.hardware.SensorManager SensorManager}
-     * for details.
+     * Called when the accuracy of the registered sensor has changed.
      *
-     * @param accuracy The new accuracy of this sensor
+     * <p>See the SENSOR_STATUS_* constants in
+     * {@link android.hardware.SensorManager SensorManager} for details.
+     *
+     * @param accuracy The new accuracy of this sensor, one of
+     *         {@code SensorManager.SENSOR_STATUS_*}
      */
-    public void onAccuracyChanged(Sensor sensor, int accuracy);    
+    public void onAccuracyChanged(Sensor sensor, int accuracy);
 }
diff --git a/core/java/android/hardware/SensorManager.java b/core/java/android/hardware/SensorManager.java
index 5f2b5f0..25c7630 100644
--- a/core/java/android/hardware/SensorManager.java
+++ b/core/java/android/hardware/SensorManager.java
@@ -321,6 +321,13 @@
 
 
     /**
+      * The values returned by this sensor cannot be trusted because the sensor
+      * had no contact with what it was measuring (for example, the heart rate
+      * monitor is not in contact with the user).
+      */
+    public static final int SENSOR_STATUS_NO_CONTACT = -1;
+
+    /**
      * The values returned by this sensor cannot be trusted, calibration is
      * needed or the environment doesn't allow readings
      */
@@ -421,9 +428,10 @@
      * {@link SensorManager#getSensorList(int) getSensorList}.
      *
      * @param type
-     *        of sensors requested
+     *         of sensors requested
      *
-     * @return the default sensors matching the asked type.
+     * @return the default sensor matching the requested type if one exists and the application
+     *         has the necessary permissions, or null otherwise.
      *
      * @see #getSensorList(int)
      * @see Sensor
diff --git a/core/java/android/hardware/SystemSensorManager.java b/core/java/android/hardware/SystemSensorManager.java
index 8684a04..b66ec86 100644
--- a/core/java/android/hardware/SystemSensorManager.java
+++ b/core/java/android/hardware/SystemSensorManager.java
@@ -395,25 +395,12 @@
             t.timestamp = timestamp;
             t.accuracy = inAccuracy;
             t.sensor = sensor;
-            switch (t.sensor.getType()) {
-                // Only report accuracy for sensors that support it.
-                case Sensor.TYPE_MAGNETIC_FIELD:
-                case Sensor.TYPE_ORIENTATION:
-                    // call onAccuracyChanged() only if the value changes
-                    final int accuracy = mSensorAccuracies.get(handle);
-                    if ((t.accuracy >= 0) && (accuracy != t.accuracy)) {
-                        mSensorAccuracies.put(handle, t.accuracy);
-                        mListener.onAccuracyChanged(t.sensor, t.accuracy);
-                    }
-                    break;
-                default:
-                    // For other sensors, just report the accuracy once
-                    if (mFirstEvent.get(handle) == false) {
-                        mFirstEvent.put(handle, true);
-                        mListener.onAccuracyChanged(
-                                t.sensor, SENSOR_STATUS_ACCURACY_HIGH);
-                    }
-                    break;
+
+            // call onAccuracyChanged() only if the value changes
+            final int accuracy = mSensorAccuracies.get(handle);
+            if ((t.accuracy >= 0) && (accuracy != t.accuracy)) {
+                mSensorAccuracies.put(handle, t.accuracy);
+                mListener.onAccuracyChanged(t.sensor, t.accuracy);
             }
             mListener.onSensorChanged(t);
         }
diff --git a/core/java/android/hardware/camera2/CameraCaptureSession.java b/core/java/android/hardware/camera2/CameraCaptureSession.java
index 8391209..7738d2d 100644
--- a/core/java/android/hardware/camera2/CameraCaptureSession.java
+++ b/core/java/android/hardware/camera2/CameraCaptureSession.java
@@ -508,14 +508,62 @@
         }
 
         /**
-         * This method is called when an image capture has completed and the
-         * result metadata is available.
+         * This method is called when an image capture makes partial forward progress; some
+         * (but not all) results from an image capture are available.
+         *
+         * <p>The result provided here will contain some subset of the fields of
+         * a full result. Multiple {@link #onCaptureProgressed} calls may happen per
+         * capture; a given result field will only be present in one partial
+         * capture at most. The final {@link #onCaptureCompleted} call will always
+         * contain all the fields (in particular, the union of all the fields of all
+         * the partial results composing the total result).</p>
+         *
+         * <p>For each request, some result data might be available earlier than others. The typical
+         * delay between each partial result (per request) is a single frame interval.
+         * For performance-oriented use-cases, applications should query the metadata they need
+         * to make forward progress from the partial results and avoid waiting for the completed
+         * result.</p>
+         *
+         * <p>Each request will generate at least {@code 1} partial results, and at most
+         * {@link CameraCharacteristics#REQUEST_PARTIAL_RESULT_COUNT} partial results.</p>
+         *
+         * <p>Depending on the request settings, the number of partial results per request
+         * will vary, although typically the partial count could be the same as long as the
+         * camera device subsystems enabled stay the same.</p>
          *
          * <p>The default implementation of this method does nothing.</p>
          *
          * @param camera The CameraDevice sending the callback.
          * @param request The request that was given to the CameraDevice
-         * @param result The output metadata from the capture, including the
+         * @param partialResult The partial output metadata from the capture, which
+         * includes a subset of the {@link TotalCaptureResult} fields.
+         *
+         * @see #capture
+         * @see #captureBurst
+         * @see #setRepeatingRequest
+         * @see #setRepeatingBurst
+         */
+        public void onCaptureProgressed(CameraDevice camera,
+                CaptureRequest request, CaptureResult partialResult) {
+            // default empty implementation
+        }
+
+        /**
+         * This method is called when an image capture has fully completed and all the
+         * result metadata is available.
+         *
+         * <p>This callback will always fire after the last {@link #onCaptureProgressed};
+         * in other words, no more partial results will be delivered once the completed result
+         * is available.</p>
+         *
+         * <p>For performance-intensive use-cases where latency is a factor, consider
+         * using {@link #onCaptureProgressed} instead.</p>
+         *
+         * <p>The default implementation of this method does nothing.</p>
+         *
+         * @param camera The CameraDevice sending the callback.
+         * @param request The request that was given to the CameraDevice
+         * @param result The total output metadata from the capture, including the
          * final capture parameters and the state of the camera system during
          * capture.
          *
@@ -525,7 +573,7 @@
          * @see #setRepeatingBurst
          */
         public void onCaptureCompleted(CameraDevice camera,
-                CaptureRequest request, CaptureResult result) {
+                CaptureRequest request, TotalCaptureResult result) {
             // default empty implementation
         }
 
@@ -563,24 +611,57 @@
          * when a capture sequence finishes and all {@link CaptureResult}
          * or {@link CaptureFailure} for it have been returned via this listener.
          *
+         * <p>In total, there will be at least one result/failure returned by this listener
+         * before this callback is invoked. If the capture sequence is aborted before any
+         * requests have been processed, {@link #onCaptureSequenceAborted} is invoked instead.</p>
+         *
+         * <p>The default implementation does nothing.</p>
+         *
          * @param camera
          *            The CameraDevice sending the callback.
          * @param sequenceId
          *            A sequence ID returned by the {@link #capture} family of functions.
-         * @param lastFrameNumber
+         * @param frameNumber
          *            The last frame number (returned by {@link CaptureResult#getFrameNumber}
          *            or {@link CaptureFailure#getFrameNumber}) in the capture sequence.
-         *            The last frame number may be equal to NO_FRAMES_CAPTURED if no images
-         *            were captured for this sequence. This can happen, for example, when a
-         *            repeating request or burst is cleared right after being set.
          *
          * @see CaptureResult#getFrameNumber()
          * @see CaptureFailure#getFrameNumber()
          * @see CaptureResult#getSequenceId()
          * @see CaptureFailure#getSequenceId()
+         * @see #onCaptureSequenceAborted
          */
         public void onCaptureSequenceCompleted(CameraDevice camera,
-                int sequenceId, int lastFrameNumber) {
+                int sequenceId, long frameNumber) {
+            // default empty implementation
+        }
+
+        /**
+         * This method is called independently of the others in CaptureListener,
+         * when a capture sequence aborts before any {@link CaptureResult}
+         * or {@link CaptureFailure} for it have been returned via this listener.
+         *
+         * <p>Due to the asynchronous nature of the camera device, not all submitted captures
+         * are immediately processed. It is possible to clear out the pending requests
+         * by a variety of operations such as {@link CameraDevice#stopRepeating} or
+         * {@link CameraDevice#flush}. When such an event happens,
+         * {@link #onCaptureSequenceCompleted} will not be called.</p>
+         *
+         * <p>The default implementation does nothing.</p>
+         *
+         * @param camera
+         *            The CameraDevice sending the callback.
+         * @param sequenceId
+         *            A sequence ID returned by the {@link #capture} family of functions.
+         *
+         * @see CaptureResult#getFrameNumber()
+         * @see CaptureFailure#getFrameNumber()
+         * @see CaptureResult#getSequenceId()
+         * @see CaptureFailure#getSequenceId()
+         * @see #onCaptureSequenceCompleted
+         */
+        public void onCaptureSequenceAborted(CameraDevice camera,
+                int sequenceId) {
             // default empty implementation
         }
     }
diff --git a/core/java/android/hardware/camera2/CameraCharacteristics.java b/core/java/android/hardware/camera2/CameraCharacteristics.java
index c08424a..2f5b4fe 100644
--- a/core/java/android/hardware/camera2/CameraCharacteristics.java
+++ b/core/java/android/hardware/camera2/CameraCharacteristics.java
@@ -296,8 +296,8 @@
      * valid anti-banding modes that the application may request
      * for this camera device; they must include AUTO.</p>
      */
-    public static final Key<byte[]> CONTROL_AE_AVAILABLE_ANTIBANDING_MODES =
-            new Key<byte[]>("android.control.aeAvailableAntibandingModes", byte[].class);
+    public static final Key<int[]> CONTROL_AE_AVAILABLE_ANTIBANDING_MODES =
+            new Key<int[]>("android.control.aeAvailableAntibandingModes", int[].class);
 
     /**
      * <p>The set of auto-exposure modes that are supported by this
@@ -315,15 +315,15 @@
      *
      * @see CaptureRequest#CONTROL_AE_MODE
      */
-    public static final Key<byte[]> CONTROL_AE_AVAILABLE_MODES =
-            new Key<byte[]>("android.control.aeAvailableModes", byte[].class);
+    public static final Key<int[]> CONTROL_AE_AVAILABLE_MODES =
+            new Key<int[]>("android.control.aeAvailableModes", int[].class);
 
     /**
      * <p>List of frame rate ranges supported by the
      * AE algorithm/hardware</p>
      */
-    public static final Key<int[]> CONTROL_AE_AVAILABLE_TARGET_FPS_RANGES =
-            new Key<int[]>("android.control.aeAvailableTargetFpsRanges", int[].class);
+    public static final Key<android.util.Range<Integer>[]> CONTROL_AE_AVAILABLE_TARGET_FPS_RANGES =
+            new Key<android.util.Range<Integer>[]>("android.control.aeAvailableTargetFpsRanges", new TypeReference<android.util.Range<Integer>[]>() {{ }});
 
     /**
      * <p>Maximum and minimum exposure compensation
@@ -332,8 +332,8 @@
      *
      * @see CameraCharacteristics#CONTROL_AE_COMPENSATION_STEP
      */
-    public static final Key<int[]> CONTROL_AE_COMPENSATION_RANGE =
-            new Key<int[]>("android.control.aeCompensationRange", int[].class);
+    public static final Key<android.util.Range<Integer>> CONTROL_AE_COMPENSATION_RANGE =
+            new Key<android.util.Range<Integer>>("android.control.aeCompensationRange", new TypeReference<android.util.Range<Integer>>() {{ }});
 
     /**
      * <p>Smallest step by which exposure compensation
@@ -355,8 +355,8 @@
      * @see CaptureRequest#CONTROL_AF_MODE
      * @see CameraCharacteristics#LENS_INFO_MINIMUM_FOCUS_DISTANCE
      */
-    public static final Key<byte[]> CONTROL_AF_AVAILABLE_MODES =
-            new Key<byte[]>("android.control.afAvailableModes", byte[].class);
+    public static final Key<int[]> CONTROL_AF_AVAILABLE_MODES =
+            new Key<int[]>("android.control.afAvailableModes", int[].class);
 
     /**
      * <p>List containing the subset of color effects
@@ -374,8 +374,8 @@
      * @see CaptureRequest#CONTROL_EFFECT_MODE
      * @see CaptureRequest#CONTROL_MODE
      */
-    public static final Key<byte[]> CONTROL_AVAILABLE_EFFECTS =
-            new Key<byte[]>("android.control.availableEffects", byte[].class);
+    public static final Key<int[]> CONTROL_AVAILABLE_EFFECTS =
+            new Key<int[]>("android.control.availableEffects", int[].class);
 
     /**
      * <p>List containing a subset of scene modes
@@ -388,15 +388,15 @@
      *
      * @see CaptureRequest#CONTROL_SCENE_MODE
      */
-    public static final Key<byte[]> CONTROL_AVAILABLE_SCENE_MODES =
-            new Key<byte[]>("android.control.availableSceneModes", byte[].class);
+    public static final Key<int[]> CONTROL_AVAILABLE_SCENE_MODES =
+            new Key<int[]>("android.control.availableSceneModes", int[].class);
 
     /**
      * <p>List of video stabilization modes that can
      * be supported</p>
      */
-    public static final Key<byte[]> CONTROL_AVAILABLE_VIDEO_STABILIZATION_MODES =
-            new Key<byte[]>("android.control.availableVideoStabilizationModes", byte[].class);
+    public static final Key<int[]> CONTROL_AVAILABLE_VIDEO_STABILIZATION_MODES =
+            new Key<int[]>("android.control.availableVideoStabilizationModes", int[].class);
 
     /**
      * <p>The set of auto-white-balance modes ({@link CaptureRequest#CONTROL_AWB_MODE android.control.awbMode})
@@ -414,8 +414,8 @@
      * @see CaptureRequest#COLOR_CORRECTION_TRANSFORM
      * @see CaptureRequest#CONTROL_AWB_MODE
      */
-    public static final Key<byte[]> CONTROL_AWB_AVAILABLE_MODES =
-            new Key<byte[]>("android.control.awbAvailableModes", byte[].class);
+    public static final Key<int[]> CONTROL_AWB_AVAILABLE_MODES =
+            new Key<int[]>("android.control.awbAvailableModes", int[].class);
 
     /**
      * <p>List of the maximum number of regions that can be used for metering in
@@ -427,19 +427,53 @@
      * @see CaptureRequest#CONTROL_AE_REGIONS
      * @see CaptureRequest#CONTROL_AF_REGIONS
      * @see CaptureRequest#CONTROL_AWB_REGIONS
+     * @hide
      */
     public static final Key<int[]> CONTROL_MAX_REGIONS =
             new Key<int[]>("android.control.maxRegions", int[].class);
 
     /**
+     * <p>List of the maximum number of regions that can be used for metering in
+     * auto-exposure (AE);
+     * this corresponds to the the maximum number of elements in
+     * {@link CaptureRequest#CONTROL_AE_REGIONS android.control.aeRegions}.</p>
+     *
+     * @see CaptureRequest#CONTROL_AE_REGIONS
+     */
+    public static final Key<Integer> CONTROL_MAX_REGIONS_AE =
+            new Key<Integer>("android.control.maxRegionsAe", int.class);
+
+    /**
+     * <p>List of the maximum number of regions that can be used for metering in
+     * auto-white balance (AWB);
+     * this corresponds to the the maximum number of elements in
+     * {@link CaptureRequest#CONTROL_AWB_REGIONS android.control.awbRegions}.</p>
+     *
+     * @see CaptureRequest#CONTROL_AWB_REGIONS
+     */
+    public static final Key<Integer> CONTROL_MAX_REGIONS_AWB =
+            new Key<Integer>("android.control.maxRegionsAwb", int.class);
+
+    /**
+     * <p>List of the maximum number of regions that can be used for metering in
+     * auto-focus (AF);
+     * this corresponds to the the maximum number of elements in
+     * {@link CaptureRequest#CONTROL_AF_REGIONS android.control.afRegions}.</p>
+     *
+     * @see CaptureRequest#CONTROL_AF_REGIONS
+     */
+    public static final Key<Integer> CONTROL_MAX_REGIONS_AF =
+            new Key<Integer>("android.control.maxRegionsAf", int.class);
+
+    /**
      * <p>The set of edge enhancement modes supported by this camera device.</p>
      * <p>This tag lists the valid modes for {@link CaptureRequest#EDGE_MODE android.edge.mode}.</p>
      * <p>Full-capability camera devices must always support OFF and FAST.</p>
      *
      * @see CaptureRequest#EDGE_MODE
      */
-    public static final Key<byte[]> EDGE_AVAILABLE_EDGE_MODES =
-            new Key<byte[]>("android.edge.availableEdgeModes", byte[].class);
+    public static final Key<int[]> EDGE_AVAILABLE_EDGE_MODES =
+            new Key<int[]>("android.edge.availableEdgeModes", int[].class);
 
     /**
      * <p>Whether this camera device has a
@@ -458,8 +492,8 @@
      *
      * @see CaptureRequest#HOT_PIXEL_MODE
      */
-    public static final Key<byte[]> HOT_PIXEL_AVAILABLE_HOT_PIXEL_MODES =
-            new Key<byte[]>("android.hotPixel.availableHotPixelModes", byte[].class);
+    public static final Key<int[]> HOT_PIXEL_AVAILABLE_HOT_PIXEL_MODES =
+            new Key<int[]>("android.hotPixel.availableHotPixelModes", int[].class);
 
     /**
      * <p>Supported resolutions for the JPEG thumbnail</p>
@@ -526,8 +560,8 @@
      *
      * @see CaptureRequest#LENS_OPTICAL_STABILIZATION_MODE
      */
-    public static final Key<byte[]> LENS_INFO_AVAILABLE_OPTICAL_STABILIZATION =
-            new Key<byte[]>("android.lens.info.availableOpticalStabilization", byte[].class);
+    public static final Key<int[]> LENS_INFO_AVAILABLE_OPTICAL_STABILIZATION =
+            new Key<int[]>("android.lens.info.availableOpticalStabilization", int[].class);
 
     /**
      * <p>Optional. Hyperfocal distance for this lens.</p>
@@ -553,6 +587,7 @@
      * <p>Dimensions of lens shading map.</p>
      * <p>The map should be on the order of 30-40 rows and columns, and
      * must be smaller than 64x64.</p>
+     * @hide
      */
     public static final Key<android.util.Size> LENS_INFO_SHADING_MAP_SIZE =
             new Key<android.util.Size>("android.lens.info.shadingMapSize", android.util.Size.class);
@@ -591,8 +626,8 @@
      *
      * @see CaptureRequest#NOISE_REDUCTION_MODE
      */
-    public static final Key<byte[]> NOISE_REDUCTION_AVAILABLE_NOISE_REDUCTION_MODES =
-            new Key<byte[]>("android.noiseReduction.availableNoiseReductionModes", byte[].class);
+    public static final Key<int[]> NOISE_REDUCTION_AVAILABLE_NOISE_REDUCTION_MODES =
+            new Key<int[]>("android.noiseReduction.availableNoiseReductionModes", int[].class);
 
     /**
      * <p>If set to 1, the HAL will always split result
@@ -621,7 +656,7 @@
      * number is 3, and max JPEG stream number is 2, then this tuple should be <code>(1, 3, 2)</code>.</p>
      * <p>This lists the upper bound of the number of output streams supported by
      * the camera device. Using more streams simultaneously may require more hardware and
-     * CPU resources that will consume more power. The image format for a output stream can
+     * CPU resources that will consume more power. The image format for an output stream can
      * be any supported format provided by android.scaler.availableStreamConfigurations.
      * The formats defined in android.scaler.availableStreamConfigurations can be catergorized
      * into the 3 stream types as below:</p>
@@ -632,11 +667,79 @@
      * <li>Processed (but not-stalling): any non-RAW format without a stall duration.
      * Typically ImageFormat#YUV_420_888, ImageFormat#NV21, ImageFormat#YV12.</li>
      * </ul>
+     * @hide
      */
     public static final Key<int[]> REQUEST_MAX_NUM_OUTPUT_STREAMS =
             new Key<int[]>("android.request.maxNumOutputStreams", int[].class);
 
     /**
+     * <p>The maximum numbers of different types of output streams
+     * that can be configured and used simultaneously by a camera device
+     * for any <code>RAW</code> formats.</p>
+     * <p>This value contains the max number of output simultaneous
+     * streams from the raw sensor.</p>
+     * <p>This lists the upper bound of the number of output streams supported by
+     * the camera device. Using more streams simultaneously may require more hardware and
+     * CPU resources that will consume more power. The image format for this kind of an output stream can
+     * be any <code>RAW</code> and supported format provided by {@link CameraCharacteristics#SCALER_STREAM_CONFIGURATION_MAP android.scaler.streamConfigurationMap}.</p>
+     * <p>In particular, a <code>RAW</code> format is typically one of:</p>
+     * <ul>
+     * <li>ImageFormat#RAW_SENSOR</li>
+     * <li>Opaque <code>RAW</code></li>
+     * </ul>
+     *
+     * @see CameraCharacteristics#SCALER_STREAM_CONFIGURATION_MAP
+     */
+    public static final Key<Integer> REQUEST_MAX_NUM_OUTPUT_RAW =
+            new Key<Integer>("android.request.maxNumOutputRaw", int.class);
+
+    /**
+     * <p>The maximum numbers of different types of output streams
+     * that can be configured and used simultaneously by a camera device
+     * for any processed (but not-stalling) formats.</p>
+     * <p>This value contains the max number of output simultaneous
+     * streams for any processed (but not-stalling) formats.</p>
+     * <p>This lists the upper bound of the number of output streams supported by
+     * the camera device. Using more streams simultaneously may require more hardware and
+     * CPU resources that will consume more power. The image format for this kind of an output stream can
+     * be any non-<code>RAW</code> and supported format provided by {@link CameraCharacteristics#SCALER_STREAM_CONFIGURATION_MAP android.scaler.streamConfigurationMap}.</p>
+     * <p>Processed (but not-stalling) is defined as any non-RAW format without a stall duration.
+     * Typically:</p>
+     * <ul>
+     * <li>ImageFormat#YUV_420_888</li>
+     * <li>ImageFormat#NV21</li>
+     * <li>ImageFormat#YV12</li>
+     * <li>Implementation-defined formats, i.e. StreamConfiguration#isOutputSupportedFor(Class)</li>
+     * </ul>
+     * <p>For full guarantees, query StreamConfigurationMap#getOutputStallDuration with
+     * a processed format -- it will return 0 for a non-stalling stream.</p>
+     *
+     * @see CameraCharacteristics#SCALER_STREAM_CONFIGURATION_MAP
+     */
+    public static final Key<Integer> REQUEST_MAX_NUM_OUTPUT_PROC =
+            new Key<Integer>("android.request.maxNumOutputProc", int.class);
+
+    /**
+     * <p>The maximum numbers of different types of output streams
+     * that can be configured and used simultaneously by a camera device
+     * for any processed (and stalling) formats.</p>
+     * <p>This value contains the max number of output simultaneous
+     * streams for any processed (but not-stalling) formats.</p>
+     * <p>This lists the upper bound of the number of output streams supported by
+     * the camera device. Using more streams simultaneously may require more hardware and
+     * CPU resources that will consume more power. The image format for this kind of an output stream can
+     * be any non-<code>RAW</code> and supported format provided by {@link CameraCharacteristics#SCALER_STREAM_CONFIGURATION_MAP android.scaler.streamConfigurationMap}.</p>
+     * <p>A processed and stalling format is defined as any non-RAW format with a stallDurations &gt; 0.
+     * Typically only the <code>JPEG</code> format (ImageFormat#JPEG)</p>
+     * <p>For full guarantees, query StreamConfigurationMap#getOutputStallDuration with
+     * a processed format -- it will return a non-0 value for a stalling stream.</p>
+     *
+     * @see CameraCharacteristics#SCALER_STREAM_CONFIGURATION_MAP
+     */
+    public static final Key<Integer> REQUEST_MAX_NUM_OUTPUT_PROC_STALLING =
+            new Key<Integer>("android.request.maxNumOutputProcStalling", int.class);
+
+    /**
      * <p>The maximum numbers of any type of input streams
      * that can be configured and used simultaneously by a camera device.</p>
      * <p>When set to 0, it means no input stream is supported.</p>
@@ -707,7 +810,6 @@
      * {@link CameraCharacteristics#INFO_SUPPORTED_HARDWARE_LEVEL android.info.supportedHardwareLevel} <code>==</code> FULL devices:</p>
      * <ul>
      * <li>MANUAL_SENSOR</li>
-     * <li>ZSL</li>
      * </ul>
      * <p>Other capabilities may be available on either FULL or LIMITED
      * devices, but the app. should query this field to be sure.</p>
@@ -716,12 +818,12 @@
      * @see #REQUEST_AVAILABLE_CAPABILITIES_BACKWARD_COMPATIBLE
      * @see #REQUEST_AVAILABLE_CAPABILITIES_OPTIONAL
      * @see #REQUEST_AVAILABLE_CAPABILITIES_MANUAL_SENSOR
-     * @see #REQUEST_AVAILABLE_CAPABILITIES_GCAM
+     * @see #REQUEST_AVAILABLE_CAPABILITIES_MANUAL_POST_PROCESSING
      * @see #REQUEST_AVAILABLE_CAPABILITIES_ZSL
      * @see #REQUEST_AVAILABLE_CAPABILITIES_DNG
      */
-    public static final Key<Integer> REQUEST_AVAILABLE_CAPABILITIES =
-            new Key<Integer>("android.request.availableCapabilities", int.class);
+    public static final Key<int[]> REQUEST_AVAILABLE_CAPABILITIES =
+            new Key<int[]>("android.request.availableCapabilities", int[].class);
 
     /**
      * <p>A list of all keys that the camera device has available
@@ -750,7 +852,7 @@
      * value.</p>
      * <p>The following keys may return <code>null</code> unless they are enabled:</p>
      * <ul>
-     * <li>{@link CaptureResult#STATISTICS_LENS_SHADING_MAP android.statistics.lensShadingMap} (non-null iff {@link CaptureRequest#STATISTICS_LENS_SHADING_MAP_MODE android.statistics.lensShadingMapMode} == ON)</li>
+     * <li>android.statistics.lensShadingMap (non-null iff {@link CaptureRequest#STATISTICS_LENS_SHADING_MAP_MODE android.statistics.lensShadingMapMode} == ON)</li>
      * </ul>
      * <p>(Those sometimes-null keys should nevertheless be listed here
      * if they are available.)</p>
@@ -761,7 +863,6 @@
      * <p>TODO: This should be used by #getAvailableCaptureResultKeys.</p>
      *
      * @see CameraCharacteristics#REQUEST_AVAILABLE_CAPABILITIES
-     * @see CaptureResult#STATISTICS_LENS_SHADING_MAP
      * @see CaptureRequest#STATISTICS_LENS_SHADING_MAP_MODE
      * @hide
      */
@@ -1229,8 +1330,8 @@
     /**
      * <p>Range of valid sensitivities</p>
      */
-    public static final Key<int[]> SENSOR_INFO_SENSITIVITY_RANGE =
-            new Key<int[]>("android.sensor.info.sensitivityRange", int[].class);
+    public static final Key<android.util.Range<Integer>> SENSOR_INFO_SENSITIVITY_RANGE =
+            new Key<android.util.Range<Integer>>("android.sensor.info.sensitivityRange", new TypeReference<android.util.Range<Integer>>() {{ }});
 
     /**
      * <p>Arrangement of color filters on sensor;
@@ -1251,8 +1352,8 @@
      *
      * @see CaptureRequest#SENSOR_EXPOSURE_TIME
      */
-    public static final Key<long[]> SENSOR_INFO_EXPOSURE_TIME_RANGE =
-            new Key<long[]>("android.sensor.info.exposureTimeRange", long[].class);
+    public static final Key<android.util.Range<Long>> SENSOR_INFO_EXPOSURE_TIME_RANGE =
+            new Key<android.util.Range<Long>>("android.sensor.info.exposureTimeRange", new TypeReference<android.util.Range<Long>>() {{ }});
 
     /**
      * <p>Maximum possible frame duration (minimum frame
@@ -1276,8 +1377,8 @@
      * array</p>
      * <p>Needed for FOV calculation for old API</p>
      */
-    public static final Key<float[]> SENSOR_INFO_PHYSICAL_SIZE =
-            new Key<float[]>("android.sensor.info.physicalSize", float[].class);
+    public static final Key<android.util.SizeF> SENSOR_INFO_PHYSICAL_SIZE =
+            new Key<android.util.SizeF>("android.sensor.info.physicalSize", android.util.SizeF.class);
 
     /**
      * <p>Dimensions of full pixel array, possibly
@@ -1383,8 +1484,8 @@
      *
      * @see CameraCharacteristics#SENSOR_REFERENCE_ILLUMINANT1
      */
-    public static final Key<Rational[]> SENSOR_CALIBRATION_TRANSFORM1 =
-            new Key<Rational[]>("android.sensor.calibrationTransform1", Rational[].class);
+    public static final Key<android.hardware.camera2.params.ColorSpaceTransform> SENSOR_CALIBRATION_TRANSFORM1 =
+            new Key<android.hardware.camera2.params.ColorSpaceTransform>("android.sensor.calibrationTransform1", android.hardware.camera2.params.ColorSpaceTransform.class);
 
     /**
      * <p>A per-device calibration transform matrix that maps from the
@@ -1404,8 +1505,8 @@
      *
      * @see CameraCharacteristics#SENSOR_REFERENCE_ILLUMINANT2
      */
-    public static final Key<Rational[]> SENSOR_CALIBRATION_TRANSFORM2 =
-            new Key<Rational[]>("android.sensor.calibrationTransform2", Rational[].class);
+    public static final Key<android.hardware.camera2.params.ColorSpaceTransform> SENSOR_CALIBRATION_TRANSFORM2 =
+            new Key<android.hardware.camera2.params.ColorSpaceTransform>("android.sensor.calibrationTransform2", android.hardware.camera2.params.ColorSpaceTransform.class);
 
     /**
      * <p>A matrix that transforms color values from CIE XYZ color space to
@@ -1426,8 +1527,8 @@
      *
      * @see CameraCharacteristics#SENSOR_REFERENCE_ILLUMINANT1
      */
-    public static final Key<Rational[]> SENSOR_COLOR_TRANSFORM1 =
-            new Key<Rational[]>("android.sensor.colorTransform1", Rational[].class);
+    public static final Key<android.hardware.camera2.params.ColorSpaceTransform> SENSOR_COLOR_TRANSFORM1 =
+            new Key<android.hardware.camera2.params.ColorSpaceTransform>("android.sensor.colorTransform1", android.hardware.camera2.params.ColorSpaceTransform.class);
 
     /**
      * <p>A matrix that transforms color values from CIE XYZ color space to
@@ -1450,8 +1551,8 @@
      *
      * @see CameraCharacteristics#SENSOR_REFERENCE_ILLUMINANT2
      */
-    public static final Key<Rational[]> SENSOR_COLOR_TRANSFORM2 =
-            new Key<Rational[]>("android.sensor.colorTransform2", Rational[].class);
+    public static final Key<android.hardware.camera2.params.ColorSpaceTransform> SENSOR_COLOR_TRANSFORM2 =
+            new Key<android.hardware.camera2.params.ColorSpaceTransform>("android.sensor.colorTransform2", android.hardware.camera2.params.ColorSpaceTransform.class);
 
     /**
      * <p>A matrix that transforms white balanced camera colors from the reference
@@ -1470,8 +1571,8 @@
      *
      * @see CameraCharacteristics#SENSOR_REFERENCE_ILLUMINANT1
      */
-    public static final Key<Rational[]> SENSOR_FORWARD_MATRIX1 =
-            new Key<Rational[]>("android.sensor.forwardMatrix1", Rational[].class);
+    public static final Key<android.hardware.camera2.params.ColorSpaceTransform> SENSOR_FORWARD_MATRIX1 =
+            new Key<android.hardware.camera2.params.ColorSpaceTransform>("android.sensor.forwardMatrix1", android.hardware.camera2.params.ColorSpaceTransform.class);
 
     /**
      * <p>A matrix that transforms white balanced camera colors from the reference
@@ -1492,8 +1593,8 @@
      *
      * @see CameraCharacteristics#SENSOR_REFERENCE_ILLUMINANT2
      */
-    public static final Key<Rational[]> SENSOR_FORWARD_MATRIX2 =
-            new Key<Rational[]>("android.sensor.forwardMatrix2", Rational[].class);
+    public static final Key<android.hardware.camera2.params.ColorSpaceTransform> SENSOR_FORWARD_MATRIX2 =
+            new Key<android.hardware.camera2.params.ColorSpaceTransform>("android.sensor.forwardMatrix2", android.hardware.camera2.params.ColorSpaceTransform.class);
 
     /**
      * <p>A fixed black level offset for each of the color filter arrangement
@@ -1560,8 +1661,8 @@
      * android.statistics.faceIds and
      * android.statistics.faceLandmarks outputs.</p>
      */
-    public static final Key<byte[]> STATISTICS_INFO_AVAILABLE_FACE_DETECT_MODES =
-            new Key<byte[]>("android.statistics.info.availableFaceDetectModes", byte[].class);
+    public static final Key<int[]> STATISTICS_INFO_AVAILABLE_FACE_DETECT_MODES =
+            new Key<int[]>("android.statistics.info.availableFaceDetectModes", int[].class);
 
     /**
      * <p>Maximum number of simultaneously detectable
@@ -1584,19 +1685,16 @@
 
     /**
      * <p>Maximum number of supported points in the
-     * tonemap curve that can be used for {@link CaptureRequest#TONEMAP_CURVE_RED android.tonemap.curveRed}, or
-     * {@link CaptureRequest#TONEMAP_CURVE_GREEN android.tonemap.curveGreen}, or {@link CaptureRequest#TONEMAP_CURVE_BLUE android.tonemap.curveBlue}.</p>
+     * tonemap curve that can be used for {@link CaptureRequest#TONEMAP_CURVE android.tonemap.curve}.</p>
      * <p>If the actual number of points provided by the application (in
-     * android.tonemap.curve*)  is less than max, the camera device will
+     * {@link CaptureRequest#TONEMAP_CURVE android.tonemap.curve}*)  is less than max, the camera device will
      * resample the curve to its internal representation, using linear
      * interpolation.</p>
      * <p>The output curves in the result metadata may have a different number
      * of points than the input curves, and will represent the actual
      * hardware curves used as closely as possible when linearly interpolated.</p>
      *
-     * @see CaptureRequest#TONEMAP_CURVE_BLUE
-     * @see CaptureRequest#TONEMAP_CURVE_GREEN
-     * @see CaptureRequest#TONEMAP_CURVE_RED
+     * @see CaptureRequest#TONEMAP_CURVE
      */
     public static final Key<Integer> TONEMAP_MAX_CURVE_POINTS =
             new Key<Integer>("android.tonemap.maxCurvePoints", int.class);
@@ -1609,8 +1707,8 @@
      *
      * @see CaptureRequest#TONEMAP_MODE
      */
-    public static final Key<byte[]> TONEMAP_AVAILABLE_TONE_MAP_MODES =
-            new Key<byte[]>("android.tonemap.availableToneMapModes", byte[].class);
+    public static final Key<int[]> TONEMAP_AVAILABLE_TONE_MAP_MODES =
+            new Key<int[]>("android.tonemap.availableToneMapModes", int[].class);
 
     /**
      * <p>A list of camera LEDs that are available on this system.</p>
@@ -1626,15 +1724,16 @@
      * <p>A FULL device has the most support possible and will enable the
      * widest range of use cases such as:</p>
      * <ul>
-     * <li>30 FPS at maximum resolution (== sensor resolution)</li>
-     * <li>Per frame control</li>
-     * <li>Manual sensor control</li>
-     * <li>Zero Shutter Lag (ZSL)</li>
+     * <li>30fps at maximum resolution (== sensor resolution) is preferred, more than 20fps is required.</li>
+     * <li>Per frame control ({@link CameraCharacteristics#SYNC_MAX_LATENCY android.sync.maxLatency} <code>==</code> PER_FRAME_CONTROL)</li>
+     * <li>Manual sensor control ({@link CameraCharacteristics#REQUEST_AVAILABLE_CAPABILITIES android.request.availableCapabilities} contains MANUAL_SENSOR)</li>
+     * <li>Manual post-processing control ({@link CameraCharacteristics#REQUEST_AVAILABLE_CAPABILITIES android.request.availableCapabilities} contains MANUAL_POST_PROCESSING)</li>
      * </ul>
      * <p>A LIMITED device may have some or none of the above characteristics.
      * To find out more refer to {@link CameraCharacteristics#REQUEST_AVAILABLE_CAPABILITIES android.request.availableCapabilities}.</p>
      *
      * @see CameraCharacteristics#REQUEST_AVAILABLE_CAPABILITIES
+     * @see CameraCharacteristics#SYNC_MAX_LATENCY
      * @see #INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED
      * @see #INFO_SUPPORTED_HARDWARE_LEVEL_FULL
      */
diff --git a/core/java/android/hardware/camera2/CameraDevice.java b/core/java/android/hardware/camera2/CameraDevice.java
index 77640d1..6f5099b 100644
--- a/core/java/android/hardware/camera2/CameraDevice.java
+++ b/core/java/android/hardware/camera2/CameraDevice.java
@@ -766,14 +766,62 @@
         }
 
         /**
-         * This method is called when an image capture has completed and the
-         * result metadata is available.
+         * This method is called when an image capture makes partial forward progress; some
+         * (but not all) results from an image capture are available.
+         *
+         * <p>The result provided here will contain some subset of the fields of
+         * a full result. Multiple {@link #onCaptureProgressed} calls may happen per
+         * capture; a given result field will only be present in one partial
+         * capture at most. The final {@link #onCaptureCompleted} call will always
+         * contain all the fields (in particular, the union of all the fields of all
+         * the partial results composing the total result).</p>
+         *
+         * <p>For each request, some result data might be available earlier than others. The typical
+         * delay between each partial result (per request) is a single frame interval.
+         * For performance-oriented use-cases, applications should query the metadata they need
+         * to make forward progress from the partial results and avoid waiting for the completed
+         * result.</p>
+         *
+         * <p>Each request will generate at least {@code 1} partial results, and at most
+         * {@link CameraCharacteristics#REQUEST_PARTIAL_RESULT_COUNT} partial results.</p>
+         *
+         * <p>Depending on the request settings, the number of partial results per request
+         * will vary, although typically the partial count could be the same as long as the
+         * camera device subsystems enabled stay the same.</p>
          *
          * <p>The default implementation of this method does nothing.</p>
          *
          * @param camera The CameraDevice sending the callback.
          * @param request The request that was given to the CameraDevice
-         * @param result The output metadata from the capture, including the
+         * @param partialResult The partial output metadata from the capture, which
+         * includes a subset of the {@link TotalCaptureResult} fields.
+         *
+         * @see #capture
+         * @see #captureBurst
+         * @see #setRepeatingRequest
+         * @see #setRepeatingBurst
+         */
+        public void onCaptureProgressed(CameraDevice camera,
+                CaptureRequest request, CaptureResult partialResult) {
+            // default empty implementation
+        }
+
+        /**
+         * This method is called when an image capture has fully completed and all the
+         * result metadata is available.
+         *
+         * <p>This callback will always fire after the last {@link #onCaptureProgressed};
+         * in other words, no more partial results will be delivered once the completed result
+         * is available.</p>
+         *
+         * <p>For performance-intensive use-cases where latency is a factor, consider
+         * using {@link #onCaptureProgressed} instead.</p>
+         *
+         * <p>The default implementation of this method does nothing.</p>
+         *
+         * @param camera The CameraDevice sending the callback.
+         * @param request The request that was given to the CameraDevice
+         * @param result The total output metadata from the capture, including the
          * final capture parameters and the state of the camera system during
          * capture.
          *
@@ -783,7 +831,7 @@
          * @see #setRepeatingBurst
          */
         public void onCaptureCompleted(CameraDevice camera,
-                CaptureRequest request, CaptureResult result) {
+                CaptureRequest request, TotalCaptureResult result) {
             // default empty implementation
         }
 
@@ -796,6 +844,10 @@
          * the capture may have been pushed to their respective output
          * streams.</p>
          *
+         * <p>Some partial results may have been delivered before the capture fails;
+         * however after this callback fires, no more partial results will be delivered by
+         * {@link #onCaptureProgressed}.</p>
+         *
          * <p>The default implementation of this method does nothing.</p>
          *
          * @param camera
@@ -821,24 +873,57 @@
          * when a capture sequence finishes and all {@link CaptureResult}
          * or {@link CaptureFailure} for it have been returned via this listener.
          *
+         * <p>In total, there will be at least one result/failure returned by this listener
+         * before this callback is invoked. If the capture sequence is aborted before any
+         * requests have been processed, {@link #onCaptureSequenceAborted} is invoked instead.</p>
+         *
+         * <p>The default implementation does nothing.</p>
+         *
          * @param camera
          *            The CameraDevice sending the callback.
          * @param sequenceId
          *            A sequence ID returned by the {@link #capture} family of functions.
-         * @param lastFrameNumber
+         * @param frameNumber
          *            The last frame number (returned by {@link CaptureResult#getFrameNumber}
          *            or {@link CaptureFailure#getFrameNumber}) in the capture sequence.
-         *            The last frame number may be equal to NO_FRAMES_CAPTURED if no images
-         *            were captured for this sequence. This can happen, for example, when a
-         *            repeating request or burst is cleared right after being set.
          *
          * @see CaptureResult#getFrameNumber()
          * @see CaptureFailure#getFrameNumber()
          * @see CaptureResult#getSequenceId()
          * @see CaptureFailure#getSequenceId()
+         * @see #onCaptureSequenceAborted
          */
         public void onCaptureSequenceCompleted(CameraDevice camera,
-                int sequenceId, int lastFrameNumber) {
+                int sequenceId, long frameNumber) {
+            // default empty implementation
+        }
+
+        /**
+         * This method is called independently of the others in CaptureListener,
+         * when a capture sequence aborts before any {@link CaptureResult}
+         * or {@link CaptureFailure} for it have been returned via this listener.
+         *
+         * <p>Due to the asynchronous nature of the camera device, not all submitted captures
+         * are immediately processed. It is possible to clear out the pending requests
+         * by a variety of operations such as {@link CameraDevice#stopRepeating} or
+         * {@link CameraDevice#flush}. When such an event happens,
+         * {@link #onCaptureSequenceCompleted} will not be called.</p>
+         *
+         * <p>The default implementation does nothing.</p>
+         *
+         * @param camera
+         *            The CameraDevice sending the callback.
+         * @param sequenceId
+         *            A sequence ID returned by the {@link #capture} family of functions.
+         *
+         * @see CaptureResult#getFrameNumber()
+         * @see CaptureFailure#getFrameNumber()
+         * @see CaptureResult#getSequenceId()
+         * @see CaptureFailure#getSequenceId()
+         * @see #onCaptureSequenceCompleted
+         */
+        public void onCaptureSequenceAborted(CameraDevice camera,
+                int sequenceId) {
             // default empty implementation
         }
     }
diff --git a/core/java/android/hardware/camera2/CameraManager.java b/core/java/android/hardware/camera2/CameraManager.java
index accceb1..4a89fe7 100644
--- a/core/java/android/hardware/camera2/CameraManager.java
+++ b/core/java/android/hardware/camera2/CameraManager.java
@@ -219,6 +219,7 @@
     private CameraDevice openCameraDeviceUserAsync(String cameraId,
             CameraDevice.StateListener listener, Handler handler)
             throws CameraAccessException {
+        CameraCharacteristics characteristics = getCameraCharacteristics(cameraId);
         CameraDevice device = null;
         try {
 
@@ -230,7 +231,8 @@
                         new android.hardware.camera2.impl.CameraDevice(
                                 cameraId,
                                 listener,
-                                handler);
+                                handler,
+                                characteristics);
 
                 BinderHolder holder = new BinderHolder();
 
diff --git a/core/java/android/hardware/camera2/CameraMetadata.java b/core/java/android/hardware/camera2/CameraMetadata.java
index 4cde601..b3e165e 100644
--- a/core/java/android/hardware/camera2/CameraMetadata.java
+++ b/core/java/android/hardware/camera2/CameraMetadata.java
@@ -236,9 +236,17 @@
 
     /**
      * <p>The camera device can be manually controlled (3A algorithms such
-     * as auto exposure, and auto focus can be
-     * bypassed), this includes but is not limited to:</p>
+     * as auto exposure, and auto focus can be bypassed).
+     * The camera device supports basic manual control of the sensor image
+     * acquisition related stages. This means the following controls are
+     * guaranteed to be supported:</p>
      * <ul>
+     * <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>
      * <li>{@link CaptureRequest#SENSOR_EXPOSURE_TIME android.sensor.exposureTime}</li>
      * <li>{@link CameraCharacteristics#SENSOR_INFO_EXPOSURE_TIME_RANGE android.sensor.info.exposureTimeRange}</li>
@@ -265,10 +273,15 @@
      * <p>If any of the above 3A algorithms are enabled, then the camera
      * device will accurately report the values applied by 3A in the
      * 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>
      *
      * @see CaptureRequest#BLACK_LEVEL_LOCK
+     * @see CameraCharacteristics#SCALER_STREAM_CONFIGURATION_MAP
      * @see CaptureRequest#SENSOR_EXPOSURE_TIME
+     * @see CaptureRequest#SENSOR_FRAME_DURATION
      * @see CameraCharacteristics#SENSOR_INFO_EXPOSURE_TIME_RANGE
+     * @see CameraCharacteristics#SENSOR_INFO_MAX_FRAME_DURATION
      * @see CameraCharacteristics#SENSOR_INFO_SENSITIVITY_RANGE
      * @see CaptureRequest#SENSOR_SENSITIVITY
      * @see CameraCharacteristics#REQUEST_AVAILABLE_CAPABILITIES
@@ -276,12 +289,12 @@
     public static final int REQUEST_AVAILABLE_CAPABILITIES_MANUAL_SENSOR = 2;
 
     /**
-     * <p>TODO: This should be @hide</p>
+     * <p>The camera device post-processing stages can be manually controlled.
+     * The camera device supports basic manual control of the image post-processing
+     * stages. This means the following controls are guaranteed to be supported:</p>
      * <ul>
      * <li>Manual tonemap control<ul>
-     * <li>{@link CaptureRequest#TONEMAP_CURVE_BLUE android.tonemap.curveBlue}</li>
-     * <li>{@link CaptureRequest#TONEMAP_CURVE_GREEN android.tonemap.curveGreen}</li>
-     * <li>{@link CaptureRequest#TONEMAP_CURVE_RED android.tonemap.curveRed}</li>
+     * <li>{@link CaptureRequest#TONEMAP_CURVE android.tonemap.curve}</li>
      * <li>{@link CaptureRequest#TONEMAP_MODE android.tonemap.mode}</li>
      * <li>{@link CameraCharacteristics#TONEMAP_MAX_CURVE_POINTS android.tonemap.maxCurvePoints}</li>
      * </ul>
@@ -292,8 +305,8 @@
      * </ul>
      * </li>
      * <li>Lens shading map information<ul>
-     * <li>{@link CaptureResult#STATISTICS_LENS_SHADING_MAP android.statistics.lensShadingMap}</li>
-     * <li>{@link CameraCharacteristics#LENS_INFO_SHADING_MAP_SIZE android.lens.info.shadingMapSize}</li>
+     * <li>android.statistics.lensShadingMap</li>
+     * <li>android.lens.info.shadingMapSize</li>
      * </ul>
      * </li>
      * </ul>
@@ -301,19 +314,17 @@
      * will accurately report the values applied by AWB in the result.</p>
      * <p>The camera device will also support everything in MANUAL_SENSOR
      * except manual lens control and manual flash control.</p>
+     * <p>A given camera device may also support additional post-processing
+     * controls, but this capability only covers the above list of controls.</p>
      *
      * @see CaptureRequest#COLOR_CORRECTION_GAINS
      * @see CaptureRequest#COLOR_CORRECTION_TRANSFORM
-     * @see CameraCharacteristics#LENS_INFO_SHADING_MAP_SIZE
-     * @see CaptureResult#STATISTICS_LENS_SHADING_MAP
-     * @see CaptureRequest#TONEMAP_CURVE_BLUE
-     * @see CaptureRequest#TONEMAP_CURVE_GREEN
-     * @see CaptureRequest#TONEMAP_CURVE_RED
+     * @see CaptureRequest#TONEMAP_CURVE
      * @see CameraCharacteristics#TONEMAP_MAX_CURVE_POINTS
      * @see CaptureRequest#TONEMAP_MODE
      * @see CameraCharacteristics#REQUEST_AVAILABLE_CAPABILITIES
      */
-    public static final int REQUEST_AVAILABLE_CAPABILITIES_GCAM = 3;
+    public static final int REQUEST_AVAILABLE_CAPABILITIES_MANUAL_POST_PROCESSING = 3;
 
     /**
      * <p>The camera device supports the Zero Shutter Lag use case.</p>
@@ -1548,17 +1559,14 @@
 
     /**
      * <p>Use the tone mapping curve specified in
-     * the android.tonemap.curve* entries.</p>
+     * the {@link CaptureRequest#TONEMAP_CURVE android.tonemap.curve}* entries.</p>
      * <p>All color enhancement and tonemapping must be disabled, except
      * for applying the tonemapping curve specified by
-     * {@link CaptureRequest#TONEMAP_CURVE_RED android.tonemap.curveRed}, {@link CaptureRequest#TONEMAP_CURVE_BLUE android.tonemap.curveBlue}, or
-     * {@link CaptureRequest#TONEMAP_CURVE_GREEN android.tonemap.curveGreen}.</p>
+     * {@link CaptureRequest#TONEMAP_CURVE android.tonemap.curve}.</p>
      * <p>Must not slow down frame rate relative to raw
      * sensor output.</p>
      *
-     * @see CaptureRequest#TONEMAP_CURVE_BLUE
-     * @see CaptureRequest#TONEMAP_CURVE_GREEN
-     * @see CaptureRequest#TONEMAP_CURVE_RED
+     * @see CaptureRequest#TONEMAP_CURVE
      * @see CaptureRequest#TONEMAP_MODE
      */
     public static final int TONEMAP_MODE_CONTRAST_CURVE = 0;
diff --git a/core/java/android/hardware/camera2/CaptureRequest.java b/core/java/android/hardware/camera2/CaptureRequest.java
index a4aa296..d4dfdd5 100644
--- a/core/java/android/hardware/camera2/CaptureRequest.java
+++ b/core/java/android/hardware/camera2/CaptureRequest.java
@@ -537,30 +537,24 @@
      *
      * @see CaptureRequest#COLOR_CORRECTION_MODE
      */
-    public static final Key<Rational[]> COLOR_CORRECTION_TRANSFORM =
-            new Key<Rational[]>("android.colorCorrection.transform", Rational[].class);
+    public static final Key<android.hardware.camera2.params.ColorSpaceTransform> COLOR_CORRECTION_TRANSFORM =
+            new Key<android.hardware.camera2.params.ColorSpaceTransform>("android.colorCorrection.transform", android.hardware.camera2.params.ColorSpaceTransform.class);
 
     /**
      * <p>Gains applying to Bayer raw color channels for
      * white-balance.</p>
-     * <p>The 4-channel white-balance gains are defined in
-     * the order of <code>[R G_even G_odd B]</code>, where <code>G_even</code> is the gain
-     * for green pixels on even rows of the output, and <code>G_odd</code>
-     * is the gain for green pixels on the odd rows. if a HAL
-     * does not support a separate gain for even/odd green channels,
-     * it should use the <code>G_even</code> value, and write <code>G_odd</code> equal to
-     * <code>G_even</code> in the output result metadata.</p>
-     * <p>This array is either set by the camera device when the request
-     * {@link CaptureRequest#COLOR_CORRECTION_MODE android.colorCorrection.mode} is not TRANSFORM_MATRIX, or
-     * directly by the application in the request when the
-     * {@link CaptureRequest#COLOR_CORRECTION_MODE android.colorCorrection.mode} is TRANSFORM_MATRIX.</p>
-     * <p>The output should be the gains actually applied by the camera device to
-     * the current frame.</p>
+     * <p>These per-channel gains are either set by the camera device
+     * when the request {@link CaptureRequest#COLOR_CORRECTION_MODE android.colorCorrection.mode} is not
+     * TRANSFORM_MATRIX, or directly by the application in the
+     * request when the {@link CaptureRequest#COLOR_CORRECTION_MODE android.colorCorrection.mode} is
+     * TRANSFORM_MATRIX.</p>
+     * <p>The gains in the result metadata are the gains actually
+     * applied by the camera device to the current frame.</p>
      *
      * @see CaptureRequest#COLOR_CORRECTION_MODE
      */
-    public static final Key<float[]> COLOR_CORRECTION_GAINS =
-            new Key<float[]>("android.colorCorrection.gains", float[].class);
+    public static final Key<android.hardware.camera2.params.RggbChannelVector> COLOR_CORRECTION_GAINS =
+            new Key<android.hardware.camera2.params.RggbChannelVector>("android.colorCorrection.gains", android.hardware.camera2.params.RggbChannelVector.class);
 
     /**
      * <p>The desired setting for the camera device's auto-exposure
@@ -693,15 +687,16 @@
     /**
      * <p>List of areas to use for
      * metering.</p>
-     * <p>Each area is a rectangle plus weight: xmin, ymin,
-     * xmax, ymax, weight. The rectangle is defined to be inclusive of the
-     * specified coordinates.</p>
      * <p>The coordinate system is based on the active pixel array,
      * with (0,0) being the top-left pixel in the active pixel array, and
      * ({@link CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE android.sensor.info.activeArraySize}.width - 1,
      * {@link CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE android.sensor.info.activeArraySize}.height - 1) being the
-     * bottom-right pixel in the active pixel array. The weight
-     * should be nonnegative.</p>
+     * bottom-right pixel in the active pixel array.</p>
+     * <p>The weight must range from 0 to 1000, and represents a weight
+     * for every pixel in the area. This means that a large metering area
+     * with the same weight as a smaller area will have more effect in
+     * the metering result. Metering areas can partially overlap and the
+     * camera device will add the weights in the overlap region.</p>
      * <p>If all regions have 0 weight, then no specific metering area
      * needs to be used by the camera device. If the metering region is
      * outside the used {@link CaptureRequest#SCALER_CROP_REGION android.scaler.cropRegion} returned in capture result metadata,
@@ -711,8 +706,8 @@
      * @see CaptureRequest#SCALER_CROP_REGION
      * @see CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE
      */
-    public static final Key<int[]> CONTROL_AE_REGIONS =
-            new Key<int[]>("android.control.aeRegions", int[].class);
+    public static final Key<android.hardware.camera2.params.MeteringRectangle[]> CONTROL_AE_REGIONS =
+            new Key<android.hardware.camera2.params.MeteringRectangle[]>("android.control.aeRegions", android.hardware.camera2.params.MeteringRectangle[].class);
 
     /**
      * <p>Range over which fps can be adjusted to
@@ -722,8 +717,8 @@
      *
      * @see CaptureRequest#SENSOR_EXPOSURE_TIME
      */
-    public static final Key<int[]> CONTROL_AE_TARGET_FPS_RANGE =
-            new Key<int[]>("android.control.aeTargetFpsRange", int[].class);
+    public static final Key<android.util.Range<Integer>> CONTROL_AE_TARGET_FPS_RANGE =
+            new Key<android.util.Range<Integer>>("android.control.aeTargetFpsRange", new TypeReference<android.util.Range<Integer>>() {{ }});
 
     /**
      * <p>Whether the camera device will trigger a precapture
@@ -768,26 +763,27 @@
     /**
      * <p>List of areas to use for focus
      * estimation.</p>
-     * <p>Each area is a rectangle plus weight: xmin, ymin,
-     * xmax, ymax, weight. The rectangle is defined to be inclusive of the
-     * specified coordinates.</p>
      * <p>The coordinate system is based on the active pixel array,
      * with (0,0) being the top-left pixel in the active pixel array, and
      * ({@link CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE android.sensor.info.activeArraySize}.width - 1,
      * {@link CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE android.sensor.info.activeArraySize}.height - 1) being the
-     * bottom-right pixel in the active pixel array. The weight
-     * should be nonnegative.</p>
-     * <p>If all regions have 0 weight, then no specific focus area
-     * needs to be used by the camera device. If the focusing region is
-     * outside the the used {@link CaptureRequest#SCALER_CROP_REGION android.scaler.cropRegion} returned in capture
-     * result metadata, the camera device will ignore the sections outside
-     * the region and output the used sections in the result metadata.</p>
+     * bottom-right pixel in the active pixel array.</p>
+     * <p>The weight must range from 0 to 1000, and represents a weight
+     * for every pixel in the area. This means that a large metering area
+     * with the same weight as a smaller area will have more effect in
+     * the metering result. Metering areas can partially overlap and the
+     * camera device will add the weights in the overlap region.</p>
+     * <p>If all regions have 0 weight, then no specific metering area
+     * needs to be used by the camera device. If the metering region is
+     * outside the used {@link CaptureRequest#SCALER_CROP_REGION android.scaler.cropRegion} returned in capture result metadata,
+     * the camera device will ignore the sections outside the region and output the
+     * used sections in the result metadata.</p>
      *
      * @see CaptureRequest#SCALER_CROP_REGION
      * @see CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE
      */
-    public static final Key<int[]> CONTROL_AF_REGIONS =
-            new Key<int[]>("android.control.afRegions", int[].class);
+    public static final Key<android.hardware.camera2.params.MeteringRectangle[]> CONTROL_AF_REGIONS =
+            new Key<android.hardware.camera2.params.MeteringRectangle[]>("android.control.afRegions", android.hardware.camera2.params.MeteringRectangle[].class);
 
     /**
      * <p>Whether the camera device will trigger autofocus for this request.</p>
@@ -854,27 +850,27 @@
     /**
      * <p>List of areas to use for illuminant
      * estimation.</p>
-     * <p>Only used in AUTO mode.</p>
-     * <p>Each area is a rectangle plus weight: xmin, ymin,
-     * xmax, ymax, weight. The rectangle is defined to be inclusive of the
-     * specified coordinates.</p>
      * <p>The coordinate system is based on the active pixel array,
      * with (0,0) being the top-left pixel in the active pixel array, and
      * ({@link CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE android.sensor.info.activeArraySize}.width - 1,
      * {@link CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE android.sensor.info.activeArraySize}.height - 1) being the
-     * bottom-right pixel in the active pixel array. The weight
-     * should be nonnegative.</p>
-     * <p>If all regions have 0 weight, then no specific auto-white balance (AWB) area
-     * needs to be used by the camera device. If the AWB region is
-     * outside the the used {@link CaptureRequest#SCALER_CROP_REGION android.scaler.cropRegion} returned in capture result metadata,
+     * bottom-right pixel in the active pixel array.</p>
+     * <p>The weight must range from 0 to 1000, and represents a weight
+     * for every pixel in the area. This means that a large metering area
+     * with the same weight as a smaller area will have more effect in
+     * the metering result. Metering areas can partially overlap and the
+     * camera device will add the weights in the overlap region.</p>
+     * <p>If all regions have 0 weight, then no specific metering area
+     * needs to be used by the camera device. If the metering region is
+     * outside the used {@link CaptureRequest#SCALER_CROP_REGION android.scaler.cropRegion} returned in capture result metadata,
      * the camera device will ignore the sections outside the region and output the
      * used sections in the result metadata.</p>
      *
      * @see CaptureRequest#SCALER_CROP_REGION
      * @see CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE
      */
-    public static final Key<int[]> CONTROL_AWB_REGIONS =
-            new Key<int[]>("android.control.awbRegions", int[].class);
+    public static final Key<android.hardware.camera2.params.MeteringRectangle[]> CONTROL_AWB_REGIONS =
+            new Key<android.hardware.camera2.params.MeteringRectangle[]>("android.control.awbRegions", android.hardware.camera2.params.MeteringRectangle[].class);
 
     /**
      * <p>Information to the camera device 3A (auto-exposure,
@@ -1068,8 +1064,15 @@
             new Key<Integer>("android.hotPixel.mode", int.class);
 
     /**
+     * <p>A location object to use when generating image GPS metadata.</p>
+     */
+    public static final Key<android.location.Location> JPEG_GPS_LOCATION =
+            new Key<android.location.Location>("android.jpeg.gpsLocation", android.location.Location.class);
+
+    /**
      * <p>GPS coordinates to include in output JPEG
      * EXIF</p>
+     * @hide
      */
     public static final Key<double[]> JPEG_GPS_COORDINATES =
             new Key<double[]>("android.jpeg.gpsCoordinates", double[].class);
@@ -1077,6 +1080,7 @@
     /**
      * <p>32 characters describing GPS algorithm to
      * include in EXIF</p>
+     * @hide
      */
     public static final Key<String> JPEG_GPS_PROCESSING_METHOD =
             new Key<String>("android.jpeg.gpsProcessingMethod", String.class);
@@ -1084,6 +1088,7 @@
     /**
      * <p>Time GPS fix was made to include in
      * EXIF</p>
+     * @hide
      */
     public static final Key<Long> JPEG_GPS_TIMESTAMP =
             new Key<Long>("android.jpeg.gpsTimestamp", long.class);
@@ -1116,6 +1121,12 @@
      * but the captured JPEG will still be a valid image.</p>
      * <p>When a jpeg image capture is issued, the thumbnail size selected should have
      * the same aspect ratio as the jpeg image.</p>
+     * <p>If the thumbnail image aspect ratio differs from the JPEG primary image aspect
+     * ratio, the camera device creates the thumbnail by cropping it from the primary image.
+     * For example, if the primary image has 4:3 aspect ratio, the thumbnail image has
+     * 16:9 aspect ratio, the primary image will be cropped vertically (letterbox) to
+     * generate the thumbnail image. The thumbnail image will always have a smaller Field
+     * Of View (FOV) than the primary image when aspect ratios differ.</p>
      */
     public static final Key<android.util.Size> JPEG_THUMBNAIL_SIZE =
             new Key<android.util.Size>("android.jpeg.thumbnailSize", android.util.Size.class);
@@ -1432,8 +1443,8 @@
      * <p>When set to OFF mode, no lens shading correction will be applied by the
      * camera device, and an identity lens shading map data will be provided
      * if <code>{@link CaptureRequest#STATISTICS_LENS_SHADING_MAP_MODE android.statistics.lensShadingMapMode} == ON</code>. For example, for lens
-     * shading map with size specified as <code>{@link CameraCharacteristics#LENS_INFO_SHADING_MAP_SIZE android.lens.info.shadingMapSize} = [ 4, 3 ]</code>,
-     * the output {@link CaptureResult#STATISTICS_LENS_SHADING_MAP android.statistics.lensShadingMap} for this case will be an identity map
+     * shading map with size specified as <code>android.lens.info.shadingMapSize = [ 4, 3 ]</code>,
+     * the output android.statistics.lensShadingMap for this case will be an identity map
      * shown below:</p>
      * <pre><code>[ 1.0, 1.0, 1.0, 1.0,  1.0, 1.0, 1.0, 1.0,
      * 1.0, 1.0, 1.0, 1.0,  1.0, 1.0, 1.0, 1.0,
@@ -1445,8 +1456,8 @@
      * <p>When set to other modes, lens shading correction will be applied by the
      * camera device. Applications can request lens shading map data by setting
      * {@link CaptureRequest#STATISTICS_LENS_SHADING_MAP_MODE android.statistics.lensShadingMapMode} to ON, and then the camera device will provide
-     * lens shading map data in {@link CaptureResult#STATISTICS_LENS_SHADING_MAP android.statistics.lensShadingMap}, with size specified
-     * by {@link CameraCharacteristics#LENS_INFO_SHADING_MAP_SIZE android.lens.info.shadingMapSize}; the returned shading map data will be the one
+     * lens shading map data in android.statistics.lensShadingMap, with size specified
+     * by android.lens.info.shadingMapSize; the returned shading map data will be the one
      * applied by the camera device for this capture request.</p>
      * <p>The shading map data may depend on the AE and AWB statistics, therefore the reliability
      * of the map data may be affected by the AE and AWB algorithms. When AE and AWB are in
@@ -1456,8 +1467,6 @@
      *
      * @see CaptureRequest#CONTROL_AE_MODE
      * @see CaptureRequest#CONTROL_AWB_MODE
-     * @see CameraCharacteristics#LENS_INFO_SHADING_MAP_SIZE
-     * @see CaptureResult#STATISTICS_LENS_SHADING_MAP
      * @see CaptureRequest#STATISTICS_LENS_SHADING_MAP_MODE
      * @see #SHADING_MODE_OFF
      * @see #SHADING_MODE_FAST
@@ -1498,10 +1507,8 @@
      * <p>Whether the camera device will output the lens
      * shading map in output result metadata.</p>
      * <p>When set to ON,
-     * {@link CaptureResult#STATISTICS_LENS_SHADING_MAP android.statistics.lensShadingMap} must be provided in
+     * android.statistics.lensShadingMap must be provided in
      * the output result metadata.</p>
-     *
-     * @see CaptureResult#STATISTICS_LENS_SHADING_MAP
      * @see #STATISTICS_LENS_SHADING_MAP_MODE_OFF
      * @see #STATISTICS_LENS_SHADING_MAP_MODE_ON
      */
@@ -1512,10 +1519,10 @@
      * <p>Tonemapping / contrast / gamma curve for the blue
      * channel, to use when {@link CaptureRequest#TONEMAP_MODE android.tonemap.mode} is
      * CONTRAST_CURVE.</p>
-     * <p>See {@link CaptureRequest#TONEMAP_CURVE_RED android.tonemap.curveRed} for more details.</p>
+     * <p>See android.tonemap.curveRed for more details.</p>
      *
-     * @see CaptureRequest#TONEMAP_CURVE_RED
      * @see CaptureRequest#TONEMAP_MODE
+     * @hide
      */
     public static final Key<float[]> TONEMAP_CURVE_BLUE =
             new Key<float[]>("android.tonemap.curveBlue", float[].class);
@@ -1524,10 +1531,10 @@
      * <p>Tonemapping / contrast / gamma curve for the green
      * channel, to use when {@link CaptureRequest#TONEMAP_MODE android.tonemap.mode} is
      * CONTRAST_CURVE.</p>
-     * <p>See {@link CaptureRequest#TONEMAP_CURVE_RED android.tonemap.curveRed} for more details.</p>
+     * <p>See android.tonemap.curveRed for more details.</p>
      *
-     * @see CaptureRequest#TONEMAP_CURVE_RED
      * @see CaptureRequest#TONEMAP_MODE
+     * @hide
      */
     public static final Key<float[]> TONEMAP_CURVE_GREEN =
             new Key<float[]>("android.tonemap.curveGreen", float[].class);
@@ -1537,7 +1544,7 @@
      * channel, to use when {@link CaptureRequest#TONEMAP_MODE android.tonemap.mode} is
      * CONTRAST_CURVE.</p>
      * <p>Each channel's curve is defined by an array of control points:</p>
-     * <pre><code>{@link CaptureRequest#TONEMAP_CURVE_RED android.tonemap.curveRed} =
+     * <pre><code>android.tonemap.curveRed =
      * [ P0in, P0out, P1in, P1out, P2in, P2out, P3in, P3out, ..., PNin, PNout ]
      * 2 &lt;= N &lt;= {@link CameraCharacteristics#TONEMAP_MAX_CURVE_POINTS android.tonemap.maxCurvePoints}</code></pre>
      * <p>These are sorted in order of increasing <code>Pin</code>; it is always
@@ -1553,15 +1560,15 @@
      * only specify the red channel and the precision is limited to 4
      * digits, for conciseness.</p>
      * <p>Linear mapping:</p>
-     * <pre><code>{@link CaptureRequest#TONEMAP_CURVE_RED android.tonemap.curveRed} = [ 0, 0, 1.0, 1.0 ]
+     * <pre><code>android.tonemap.curveRed = [ 0, 0, 1.0, 1.0 ]
      * </code></pre>
      * <p><img alt="Linear mapping curve" src="../../../../images/camera2/metadata/android.tonemap.curveRed/linear_tonemap.png" /></p>
      * <p>Invert mapping:</p>
-     * <pre><code>{@link CaptureRequest#TONEMAP_CURVE_RED android.tonemap.curveRed} = [ 0, 1.0, 1.0, 0 ]
+     * <pre><code>android.tonemap.curveRed = [ 0, 1.0, 1.0, 0 ]
      * </code></pre>
      * <p><img alt="Inverting mapping curve" src="../../../../images/camera2/metadata/android.tonemap.curveRed/inverse_tonemap.png" /></p>
      * <p>Gamma 1/2.2 mapping, with 16 control points:</p>
-     * <pre><code>{@link CaptureRequest#TONEMAP_CURVE_RED android.tonemap.curveRed} = [
+     * <pre><code>android.tonemap.curveRed = [
      * 0.0000, 0.0000, 0.0667, 0.2920, 0.1333, 0.4002, 0.2000, 0.4812,
      * 0.2667, 0.5484, 0.3333, 0.6069, 0.4000, 0.6594, 0.4667, 0.7072,
      * 0.5333, 0.7515, 0.6000, 0.7928, 0.6667, 0.8317, 0.7333, 0.8685,
@@ -1569,7 +1576,7 @@
      * </code></pre>
      * <p><img alt="Gamma = 1/2.2 tonemapping curve" src="../../../../images/camera2/metadata/android.tonemap.curveRed/gamma_tonemap.png" /></p>
      * <p>Standard sRGB gamma mapping, per IEC 61966-2-1:1999, with 16 control points:</p>
-     * <pre><code>{@link CaptureRequest#TONEMAP_CURVE_RED android.tonemap.curveRed} = [
+     * <pre><code>android.tonemap.curveRed = [
      * 0.0000, 0.0000, 0.0667, 0.2864, 0.1333, 0.4007, 0.2000, 0.4845,
      * 0.2667, 0.5532, 0.3333, 0.6125, 0.4000, 0.6652, 0.4667, 0.7130,
      * 0.5333, 0.7569, 0.6000, 0.7977, 0.6667, 0.8360, 0.7333, 0.8721,
@@ -1577,14 +1584,67 @@
      * </code></pre>
      * <p><img alt="sRGB tonemapping curve" src="../../../../images/camera2/metadata/android.tonemap.curveRed/srgb_tonemap.png" /></p>
      *
-     * @see CaptureRequest#TONEMAP_CURVE_RED
      * @see CameraCharacteristics#TONEMAP_MAX_CURVE_POINTS
      * @see CaptureRequest#TONEMAP_MODE
+     * @hide
      */
     public static final Key<float[]> TONEMAP_CURVE_RED =
             new Key<float[]>("android.tonemap.curveRed", float[].class);
 
     /**
+     * <p>Tonemapping / contrast / gamma curve to use when {@link CaptureRequest#TONEMAP_MODE android.tonemap.mode}
+     * is CONTRAST_CURVE.</p>
+     * <p>The tonemapCurve consist of three curves for each of red, green, and blue
+     * channels respectively. The following example uses the red channel as an
+     * example. The same logic applies to green and blue channel.
+     * Each channel's curve is defined by an array of control points:</p>
+     * <pre><code>curveRed =
+     * [ P0(in, out), P1(in, out), P2(in, out), P3(in, out), ..., PN(in, out) ]
+     * 2 &lt;= N &lt;= {@link CameraCharacteristics#TONEMAP_MAX_CURVE_POINTS android.tonemap.maxCurvePoints}</code></pre>
+     * <p>These are sorted in order of increasing <code>Pin</code>; it is always
+     * guaranteed that input values 0.0 and 1.0 are included in the list to
+     * define a complete mapping. For input values between control points,
+     * the camera device must linearly interpolate between the control
+     * points.</p>
+     * <p>Each curve can have an independent number of points, and the number
+     * of points can be less than max (that is, the request doesn't have to
+     * always provide a curve with number of points equivalent to
+     * {@link CameraCharacteristics#TONEMAP_MAX_CURVE_POINTS android.tonemap.maxCurvePoints}).</p>
+     * <p>A few examples, and their corresponding graphical mappings; these
+     * only specify the red channel and the precision is limited to 4
+     * digits, for conciseness.</p>
+     * <p>Linear mapping:</p>
+     * <pre><code>curveRed = [ (0, 0), (1.0, 1.0) ]
+     * </code></pre>
+     * <p><img alt="Linear mapping curve" src="../../../../images/camera2/metadata/android.tonemap.curveRed/linear_tonemap.png" /></p>
+     * <p>Invert mapping:</p>
+     * <pre><code>curveRed = [ (0, 1.0), (1.0, 0) ]
+     * </code></pre>
+     * <p><img alt="Inverting mapping curve" src="../../../../images/camera2/metadata/android.tonemap.curveRed/inverse_tonemap.png" /></p>
+     * <p>Gamma 1/2.2 mapping, with 16 control points:</p>
+     * <pre><code>curveRed = [
+     * (0.0000, 0.0000), (0.0667, 0.2920), (0.1333, 0.4002), (0.2000, 0.4812),
+     * (0.2667, 0.5484), (0.3333, 0.6069), (0.4000, 0.6594), (0.4667, 0.7072),
+     * (0.5333, 0.7515), (0.6000, 0.7928), (0.6667, 0.8317), (0.7333, 0.8685),
+     * (0.8000, 0.9035), (0.8667, 0.9370), (0.9333, 0.9691), (1.0000, 1.0000) ]
+     * </code></pre>
+     * <p><img alt="Gamma = 1/2.2 tonemapping curve" src="../../../../images/camera2/metadata/android.tonemap.curveRed/gamma_tonemap.png" /></p>
+     * <p>Standard sRGB gamma mapping, per IEC 61966-2-1:1999, with 16 control points:</p>
+     * <pre><code>curveRed = [
+     * (0.0000, 0.0000), (0.0667, 0.2864), (0.1333, 0.4007), (0.2000, 0.4845),
+     * (0.2667, 0.5532), (0.3333, 0.6125), (0.4000, 0.6652), (0.4667, 0.7130),
+     * (0.5333, 0.7569), (0.6000, 0.7977), (0.6667, 0.8360), (0.7333, 0.8721),
+     * (0.8000, 0.9063), (0.8667, 0.9389), (0.9333, 0.9701), (1.0000, 1.0000) ]
+     * </code></pre>
+     * <p><img alt="sRGB tonemapping curve" src="../../../../images/camera2/metadata/android.tonemap.curveRed/srgb_tonemap.png" /></p>
+     *
+     * @see CameraCharacteristics#TONEMAP_MAX_CURVE_POINTS
+     * @see CaptureRequest#TONEMAP_MODE
+     */
+    public static final Key<android.hardware.camera2.params.TonemapCurve> TONEMAP_CURVE =
+            new Key<android.hardware.camera2.params.TonemapCurve>("android.tonemap.curve", android.hardware.camera2.params.TonemapCurve.class);
+
+    /**
      * <p>High-level global contrast/gamma/tonemapping control.</p>
      * <p>When switching to an application-defined contrast curve by setting
      * {@link CaptureRequest#TONEMAP_MODE android.tonemap.mode} to CONTRAST_CURVE, the curve is defined
@@ -1600,8 +1660,7 @@
      * <p>This must be set to a valid mode in
      * {@link CameraCharacteristics#TONEMAP_AVAILABLE_TONE_MAP_MODES android.tonemap.availableToneMapModes}.</p>
      * <p>When using either FAST or HIGH_QUALITY, the camera device will
-     * emit its own tonemap curve in {@link CaptureRequest#TONEMAP_CURVE_RED android.tonemap.curveRed},
-     * {@link CaptureRequest#TONEMAP_CURVE_GREEN android.tonemap.curveGreen}, and {@link CaptureRequest#TONEMAP_CURVE_BLUE android.tonemap.curveBlue}.
+     * emit its own tonemap curve in {@link CaptureRequest#TONEMAP_CURVE android.tonemap.curve}.
      * These values are always available, and as close as possible to the
      * actually used nonlinear/nonglobal transforms.</p>
      * <p>If a request is sent with CONTRAST_CURVE with the camera device's
@@ -1609,9 +1668,7 @@
      * roughly the same.</p>
      *
      * @see CameraCharacteristics#TONEMAP_AVAILABLE_TONE_MAP_MODES
-     * @see CaptureRequest#TONEMAP_CURVE_BLUE
-     * @see CaptureRequest#TONEMAP_CURVE_GREEN
-     * @see CaptureRequest#TONEMAP_CURVE_RED
+     * @see CaptureRequest#TONEMAP_CURVE
      * @see CaptureRequest#TONEMAP_MODE
      * @see #TONEMAP_MODE_CONTRAST_CURVE
      * @see #TONEMAP_MODE_FAST
diff --git a/core/java/android/hardware/camera2/CaptureResult.java b/core/java/android/hardware/camera2/CaptureResult.java
index 61d491b..7d07c92 100644
--- a/core/java/android/hardware/camera2/CaptureResult.java
+++ b/core/java/android/hardware/camera2/CaptureResult.java
@@ -24,9 +24,9 @@
 import java.util.List;
 
 /**
- * <p>The results of a single image capture from the image sensor.</p>
+ * <p>The subset of the results of a single image capture from the image sensor.</p>
  *
- * <p>Contains the final configuration for the capture hardware (sensor, lens,
+ * <p>Contains a subset of the final configuration for the capture hardware (sensor, lens,
  * flash), the processing pipeline, the control algorithms, and the output
  * buffers.</p>
  *
@@ -36,10 +36,15 @@
  * capture. The result also includes additional metadata about the state of the
  * camera device during the capture.</p>
  *
- * <p>{@link CameraCharacteristics} objects are immutable.</p>
+ * <p>Not all properties returned by {@link CameraCharacteristics#getAvailableCaptureResultKeys()}
+ * are necessarily available. Some results are {@link CaptureResult partial} and will
+ * not have every key set. Only {@link TotalCaptureResult total} results are guaranteed to have
+ * every key available that was enabled by the request.</p>
+ *
+ * <p>{@link CaptureResult} objects are immutable.</p>
  *
  */
-public final class CaptureResult extends CameraMetadata<CaptureResult.Key<?>> {
+public class CaptureResult extends CameraMetadata<CaptureResult.Key<?>> {
 
     private static final String TAG = "CaptureResult";
     private static final boolean VERBOSE = false;
@@ -249,18 +254,19 @@
     /**
      * Get the request associated with this result.
      *
-     * <p>Whenever a request is successfully captured, with
-     * {@link CameraDevice.CaptureListener#onCaptureCompleted},
-     * the {@code result}'s {@code getRequest()} will return that {@code request}.
+     * <p>Whenever a request has been fully or partially captured, with
+     * {@link CameraDevice.CaptureListener#onCaptureCompleted} or
+     * {@link CameraDevice.CaptureListener#onCaptureProgressed}, the {@code result}'s
+     * {@code getRequest()} will return that {@code request}.
      * </p>
      *
-     * <p>In particular,
+     * <p>For example,
      * <code><pre>cameraDevice.capture(someRequest, new CaptureListener() {
      *     {@literal @}Override
      *     void onCaptureCompleted(CaptureRequest myRequest, CaptureResult myResult) {
      *         assert(myResult.getRequest.equals(myRequest) == true);
      *     }
-     * };
+     * }, null);
      * </code></pre>
      * </p>
      *
@@ -297,6 +303,7 @@
      * @return int The ID for the sequence of requests that this capture result is a part of
      *
      * @see CameraDevice.CaptureListener#onCaptureSequenceCompleted
+     * @see CameraDevice.CaptureListener#onCaptureSequenceAborted
      */
     public int getSequenceId() {
         return mSequenceId;
@@ -376,30 +383,24 @@
      *
      * @see CaptureRequest#COLOR_CORRECTION_MODE
      */
-    public static final Key<Rational[]> COLOR_CORRECTION_TRANSFORM =
-            new Key<Rational[]>("android.colorCorrection.transform", Rational[].class);
+    public static final Key<android.hardware.camera2.params.ColorSpaceTransform> COLOR_CORRECTION_TRANSFORM =
+            new Key<android.hardware.camera2.params.ColorSpaceTransform>("android.colorCorrection.transform", android.hardware.camera2.params.ColorSpaceTransform.class);
 
     /**
      * <p>Gains applying to Bayer raw color channels for
      * white-balance.</p>
-     * <p>The 4-channel white-balance gains are defined in
-     * the order of <code>[R G_even G_odd B]</code>, where <code>G_even</code> is the gain
-     * for green pixels on even rows of the output, and <code>G_odd</code>
-     * is the gain for green pixels on the odd rows. if a HAL
-     * does not support a separate gain for even/odd green channels,
-     * it should use the <code>G_even</code> value, and write <code>G_odd</code> equal to
-     * <code>G_even</code> in the output result metadata.</p>
-     * <p>This array is either set by the camera device when the request
-     * {@link CaptureRequest#COLOR_CORRECTION_MODE android.colorCorrection.mode} is not TRANSFORM_MATRIX, or
-     * directly by the application in the request when the
-     * {@link CaptureRequest#COLOR_CORRECTION_MODE android.colorCorrection.mode} is TRANSFORM_MATRIX.</p>
-     * <p>The output should be the gains actually applied by the camera device to
-     * the current frame.</p>
+     * <p>These per-channel gains are either set by the camera device
+     * when the request {@link CaptureRequest#COLOR_CORRECTION_MODE android.colorCorrection.mode} is not
+     * TRANSFORM_MATRIX, or directly by the application in the
+     * request when the {@link CaptureRequest#COLOR_CORRECTION_MODE android.colorCorrection.mode} is
+     * TRANSFORM_MATRIX.</p>
+     * <p>The gains in the result metadata are the gains actually
+     * applied by the camera device to the current frame.</p>
      *
      * @see CaptureRequest#COLOR_CORRECTION_MODE
      */
-    public static final Key<float[]> COLOR_CORRECTION_GAINS =
-            new Key<float[]>("android.colorCorrection.gains", float[].class);
+    public static final Key<android.hardware.camera2.params.RggbChannelVector> COLOR_CORRECTION_GAINS =
+            new Key<android.hardware.camera2.params.RggbChannelVector>("android.colorCorrection.gains", android.hardware.camera2.params.RggbChannelVector.class);
 
     /**
      * <p>The desired setting for the camera device's auto-exposure
@@ -532,15 +533,16 @@
     /**
      * <p>List of areas to use for
      * metering.</p>
-     * <p>Each area is a rectangle plus weight: xmin, ymin,
-     * xmax, ymax, weight. The rectangle is defined to be inclusive of the
-     * specified coordinates.</p>
      * <p>The coordinate system is based on the active pixel array,
      * with (0,0) being the top-left pixel in the active pixel array, and
      * ({@link CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE android.sensor.info.activeArraySize}.width - 1,
      * {@link CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE android.sensor.info.activeArraySize}.height - 1) being the
-     * bottom-right pixel in the active pixel array. The weight
-     * should be nonnegative.</p>
+     * bottom-right pixel in the active pixel array.</p>
+     * <p>The weight must range from 0 to 1000, and represents a weight
+     * for every pixel in the area. This means that a large metering area
+     * with the same weight as a smaller area will have more effect in
+     * the metering result. Metering areas can partially overlap and the
+     * camera device will add the weights in the overlap region.</p>
      * <p>If all regions have 0 weight, then no specific metering area
      * needs to be used by the camera device. If the metering region is
      * outside the used {@link CaptureRequest#SCALER_CROP_REGION android.scaler.cropRegion} returned in capture result metadata,
@@ -550,8 +552,8 @@
      * @see CaptureRequest#SCALER_CROP_REGION
      * @see CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE
      */
-    public static final Key<int[]> CONTROL_AE_REGIONS =
-            new Key<int[]>("android.control.aeRegions", int[].class);
+    public static final Key<android.hardware.camera2.params.MeteringRectangle[]> CONTROL_AE_REGIONS =
+            new Key<android.hardware.camera2.params.MeteringRectangle[]>("android.control.aeRegions", android.hardware.camera2.params.MeteringRectangle[].class);
 
     /**
      * <p>Range over which fps can be adjusted to
@@ -561,8 +563,8 @@
      *
      * @see CaptureRequest#SENSOR_EXPOSURE_TIME
      */
-    public static final Key<int[]> CONTROL_AE_TARGET_FPS_RANGE =
-            new Key<int[]>("android.control.aeTargetFpsRange", int[].class);
+    public static final Key<android.util.Range<Integer>> CONTROL_AE_TARGET_FPS_RANGE =
+            new Key<android.util.Range<Integer>>("android.control.aeTargetFpsRange", new TypeReference<android.util.Range<Integer>>() {{ }});
 
     /**
      * <p>Whether the camera device will trigger a precapture
@@ -805,26 +807,27 @@
     /**
      * <p>List of areas to use for focus
      * estimation.</p>
-     * <p>Each area is a rectangle plus weight: xmin, ymin,
-     * xmax, ymax, weight. The rectangle is defined to be inclusive of the
-     * specified coordinates.</p>
      * <p>The coordinate system is based on the active pixel array,
      * with (0,0) being the top-left pixel in the active pixel array, and
      * ({@link CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE android.sensor.info.activeArraySize}.width - 1,
      * {@link CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE android.sensor.info.activeArraySize}.height - 1) being the
-     * bottom-right pixel in the active pixel array. The weight
-     * should be nonnegative.</p>
-     * <p>If all regions have 0 weight, then no specific focus area
-     * needs to be used by the camera device. If the focusing region is
-     * outside the the used {@link CaptureRequest#SCALER_CROP_REGION android.scaler.cropRegion} returned in capture
-     * result metadata, the camera device will ignore the sections outside
-     * the region and output the used sections in the result metadata.</p>
+     * bottom-right pixel in the active pixel array.</p>
+     * <p>The weight must range from 0 to 1000, and represents a weight
+     * for every pixel in the area. This means that a large metering area
+     * with the same weight as a smaller area will have more effect in
+     * the metering result. Metering areas can partially overlap and the
+     * camera device will add the weights in the overlap region.</p>
+     * <p>If all regions have 0 weight, then no specific metering area
+     * needs to be used by the camera device. If the metering region is
+     * outside the used {@link CaptureRequest#SCALER_CROP_REGION android.scaler.cropRegion} returned in capture result metadata,
+     * the camera device will ignore the sections outside the region and output the
+     * used sections in the result metadata.</p>
      *
      * @see CaptureRequest#SCALER_CROP_REGION
      * @see CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE
      */
-    public static final Key<int[]> CONTROL_AF_REGIONS =
-            new Key<int[]>("android.control.afRegions", int[].class);
+    public static final Key<android.hardware.camera2.params.MeteringRectangle[]> CONTROL_AF_REGIONS =
+            new Key<android.hardware.camera2.params.MeteringRectangle[]>("android.control.afRegions", android.hardware.camera2.params.MeteringRectangle[].class);
 
     /**
      * <p>Whether the camera device will trigger autofocus for this request.</p>
@@ -1288,27 +1291,27 @@
     /**
      * <p>List of areas to use for illuminant
      * estimation.</p>
-     * <p>Only used in AUTO mode.</p>
-     * <p>Each area is a rectangle plus weight: xmin, ymin,
-     * xmax, ymax, weight. The rectangle is defined to be inclusive of the
-     * specified coordinates.</p>
      * <p>The coordinate system is based on the active pixel array,
      * with (0,0) being the top-left pixel in the active pixel array, and
      * ({@link CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE android.sensor.info.activeArraySize}.width - 1,
      * {@link CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE android.sensor.info.activeArraySize}.height - 1) being the
-     * bottom-right pixel in the active pixel array. The weight
-     * should be nonnegative.</p>
-     * <p>If all regions have 0 weight, then no specific auto-white balance (AWB) area
-     * needs to be used by the camera device. If the AWB region is
-     * outside the the used {@link CaptureRequest#SCALER_CROP_REGION android.scaler.cropRegion} returned in capture result metadata,
+     * bottom-right pixel in the active pixel array.</p>
+     * <p>The weight must range from 0 to 1000, and represents a weight
+     * for every pixel in the area. This means that a large metering area
+     * with the same weight as a smaller area will have more effect in
+     * the metering result. Metering areas can partially overlap and the
+     * camera device will add the weights in the overlap region.</p>
+     * <p>If all regions have 0 weight, then no specific metering area
+     * needs to be used by the camera device. If the metering region is
+     * outside the used {@link CaptureRequest#SCALER_CROP_REGION android.scaler.cropRegion} returned in capture result metadata,
      * the camera device will ignore the sections outside the region and output the
      * used sections in the result metadata.</p>
      *
      * @see CaptureRequest#SCALER_CROP_REGION
      * @see CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE
      */
-    public static final Key<int[]> CONTROL_AWB_REGIONS =
-            new Key<int[]>("android.control.awbRegions", int[].class);
+    public static final Key<android.hardware.camera2.params.MeteringRectangle[]> CONTROL_AWB_REGIONS =
+            new Key<android.hardware.camera2.params.MeteringRectangle[]>("android.control.awbRegions", android.hardware.camera2.params.MeteringRectangle[].class);
 
     /**
      * <p>Information to the camera device 3A (auto-exposure,
@@ -1649,8 +1652,15 @@
             new Key<Integer>("android.hotPixel.mode", int.class);
 
     /**
+     * <p>A location object to use when generating image GPS metadata.</p>
+     */
+    public static final Key<android.location.Location> JPEG_GPS_LOCATION =
+            new Key<android.location.Location>("android.jpeg.gpsLocation", android.location.Location.class);
+
+    /**
      * <p>GPS coordinates to include in output JPEG
      * EXIF</p>
+     * @hide
      */
     public static final Key<double[]> JPEG_GPS_COORDINATES =
             new Key<double[]>("android.jpeg.gpsCoordinates", double[].class);
@@ -1658,6 +1668,7 @@
     /**
      * <p>32 characters describing GPS algorithm to
      * include in EXIF</p>
+     * @hide
      */
     public static final Key<String> JPEG_GPS_PROCESSING_METHOD =
             new Key<String>("android.jpeg.gpsProcessingMethod", String.class);
@@ -1665,6 +1676,7 @@
     /**
      * <p>Time GPS fix was made to include in
      * EXIF</p>
+     * @hide
      */
     public static final Key<Long> JPEG_GPS_TIMESTAMP =
             new Key<Long>("android.jpeg.gpsTimestamp", long.class);
@@ -1697,6 +1709,12 @@
      * but the captured JPEG will still be a valid image.</p>
      * <p>When a jpeg image capture is issued, the thumbnail size selected should have
      * the same aspect ratio as the jpeg image.</p>
+     * <p>If the thumbnail image aspect ratio differs from the JPEG primary image aspect
+     * ratio, the camera device creates the thumbnail by cropping it from the primary image.
+     * For example, if the primary image has 4:3 aspect ratio, the thumbnail image has
+     * 16:9 aspect ratio, the primary image will be cropped vertically (letterbox) to
+     * generate the thumbnail image. The thumbnail image will always have a smaller Field
+     * Of View (FOV) than the primary image when aspect ratios differ.</p>
      */
     public static final Key<android.util.Size> JPEG_THUMBNAIL_SIZE =
             new Key<android.util.Size>("android.jpeg.thumbnailSize", android.util.Size.class);
@@ -1786,8 +1804,8 @@
      * <p>If variable focus not supported, can still report
      * fixed depth of field range</p>
      */
-    public static final Key<float[]> LENS_FOCUS_RANGE =
-            new Key<float[]>("android.lens.focusRange", float[].class);
+    public static final Key<android.util.Pair<Float,Float>> LENS_FOCUS_RANGE =
+            new Key<android.util.Pair<Float,Float>>("android.lens.focusRange", new TypeReference<android.util.Pair<Float,Float>>() {{ }});
 
     /**
      * <p>Sets whether the camera device uses optical image stabilization (OIS)
@@ -2154,8 +2172,8 @@
      * <p>When set to OFF mode, no lens shading correction will be applied by the
      * camera device, and an identity lens shading map data will be provided
      * if <code>{@link CaptureRequest#STATISTICS_LENS_SHADING_MAP_MODE android.statistics.lensShadingMapMode} == ON</code>. For example, for lens
-     * shading map with size specified as <code>{@link CameraCharacteristics#LENS_INFO_SHADING_MAP_SIZE android.lens.info.shadingMapSize} = [ 4, 3 ]</code>,
-     * the output {@link CaptureResult#STATISTICS_LENS_SHADING_MAP android.statistics.lensShadingMap} for this case will be an identity map
+     * shading map with size specified as <code>android.lens.info.shadingMapSize = [ 4, 3 ]</code>,
+     * the output android.statistics.lensShadingMap for this case will be an identity map
      * shown below:</p>
      * <pre><code>[ 1.0, 1.0, 1.0, 1.0,  1.0, 1.0, 1.0, 1.0,
      * 1.0, 1.0, 1.0, 1.0,  1.0, 1.0, 1.0, 1.0,
@@ -2167,8 +2185,8 @@
      * <p>When set to other modes, lens shading correction will be applied by the
      * camera device. Applications can request lens shading map data by setting
      * {@link CaptureRequest#STATISTICS_LENS_SHADING_MAP_MODE android.statistics.lensShadingMapMode} to ON, and then the camera device will provide
-     * lens shading map data in {@link CaptureResult#STATISTICS_LENS_SHADING_MAP android.statistics.lensShadingMap}, with size specified
-     * by {@link CameraCharacteristics#LENS_INFO_SHADING_MAP_SIZE android.lens.info.shadingMapSize}; the returned shading map data will be the one
+     * lens shading map data in android.statistics.lensShadingMap, with size specified
+     * by android.lens.info.shadingMapSize; the returned shading map data will be the one
      * applied by the camera device for this capture request.</p>
      * <p>The shading map data may depend on the AE and AWB statistics, therefore the reliability
      * of the map data may be affected by the AE and AWB algorithms. When AE and AWB are in
@@ -2178,8 +2196,6 @@
      *
      * @see CaptureRequest#CONTROL_AE_MODE
      * @see CaptureRequest#CONTROL_AWB_MODE
-     * @see CameraCharacteristics#LENS_INFO_SHADING_MAP_SIZE
-     * @see CaptureResult#STATISTICS_LENS_SHADING_MAP
      * @see CaptureRequest#STATISTICS_LENS_SHADING_MAP_MODE
      * @see #SHADING_MODE_OFF
      * @see #SHADING_MODE_FAST
@@ -2269,13 +2285,12 @@
      * The map is assumed to be bilinearly interpolated between the sample points.</p>
      * <p>The channel order is [R, Geven, Godd, B], where Geven is the green
      * channel for the even rows of a Bayer pattern, and Godd is the odd rows.
-     * The shading map is stored in a fully interleaved format, and its size
-     * is provided in the camera static metadata by {@link CameraCharacteristics#LENS_INFO_SHADING_MAP_SIZE android.lens.info.shadingMapSize}.</p>
+     * The shading map is stored in a fully interleaved format.</p>
      * <p>The shading map should have on the order of 30-40 rows and columns,
      * and must be smaller than 64x64.</p>
      * <p>As an example, given a very small map defined as:</p>
-     * <pre><code>{@link CameraCharacteristics#LENS_INFO_SHADING_MAP_SIZE android.lens.info.shadingMapSize} = [ 4, 3 ]
-     * {@link CaptureResult#STATISTICS_LENS_SHADING_MAP android.statistics.lensShadingMap} =
+     * <pre><code>width,height = [ 4, 3 ]
+     * values =
      * [ 1.3, 1.2, 1.15, 1.2,  1.2, 1.2, 1.15, 1.2,
      * 1.1, 1.2, 1.2, 1.2,  1.3, 1.2, 1.3, 1.3,
      * 1.2, 1.2, 1.25, 1.1,  1.1, 1.1, 1.1, 1.0,
@@ -2294,8 +2309,54 @@
      * <p><img alt="Image of a uniform white wall (inverse shading map)" src="../../../../images/camera2/metadata/android.statistics.lensShadingMap/inv_shading.png" /></p>
      *
      * @see CaptureRequest#COLOR_CORRECTION_MODE
-     * @see CameraCharacteristics#LENS_INFO_SHADING_MAP_SIZE
-     * @see CaptureResult#STATISTICS_LENS_SHADING_MAP
+     */
+    public static final Key<android.hardware.camera2.params.LensShadingMap> STATISTICS_LENS_SHADING_CORRECTION_MAP =
+            new Key<android.hardware.camera2.params.LensShadingMap>("android.statistics.lensShadingCorrectionMap", android.hardware.camera2.params.LensShadingMap.class);
+
+    /**
+     * <p>The shading map is a low-resolution floating-point map
+     * that lists the coefficients used to correct for vignetting, for each
+     * Bayer color channel.</p>
+     * <p>The least shaded section of the image should have a gain factor
+     * of 1; all other sections should have gains above 1.</p>
+     * <p>When {@link CaptureRequest#COLOR_CORRECTION_MODE android.colorCorrection.mode} = TRANSFORM_MATRIX, the map
+     * must take into account the colorCorrection settings.</p>
+     * <p>The shading map is for the entire active pixel array, and is not
+     * affected by the crop region specified in the request. Each shading map
+     * entry is the value of the shading compensation map over a specific
+     * pixel on the sensor.  Specifically, with a (N x M) resolution shading
+     * map, and an active pixel array size (W x H), shading map entry
+     * (x,y) ϵ (0 ... N-1, 0 ... M-1) is the value of the shading map at
+     * pixel ( ((W-1)/(N-1)) * x, ((H-1)/(M-1)) * y) for the four color channels.
+     * The map is assumed to be bilinearly interpolated between the sample points.</p>
+     * <p>The channel order is [R, Geven, Godd, B], where Geven is the green
+     * channel for the even rows of a Bayer pattern, and Godd is the odd rows.
+     * The shading map is stored in a fully interleaved format, and its size
+     * is provided in the camera static metadata by android.lens.info.shadingMapSize.</p>
+     * <p>The shading map should have on the order of 30-40 rows and columns,
+     * and must be smaller than 64x64.</p>
+     * <p>As an example, given a very small map defined as:</p>
+     * <pre><code>android.lens.info.shadingMapSize = [ 4, 3 ]
+     * android.statistics.lensShadingMap =
+     * [ 1.3, 1.2, 1.15, 1.2,  1.2, 1.2, 1.15, 1.2,
+     * 1.1, 1.2, 1.2, 1.2,  1.3, 1.2, 1.3, 1.3,
+     * 1.2, 1.2, 1.25, 1.1,  1.1, 1.1, 1.1, 1.0,
+     * 1.0, 1.0, 1.0, 1.0,  1.2, 1.3, 1.25, 1.2,
+     * 1.3, 1.2, 1.2, 1.3,   1.2, 1.15, 1.1, 1.2,
+     * 1.2, 1.1, 1.0, 1.2,  1.3, 1.15, 1.2, 1.3 ]
+     * </code></pre>
+     * <p>The low-resolution scaling map images for each channel are
+     * (displayed using nearest-neighbor interpolation):</p>
+     * <p><img alt="Red lens shading map" src="../../../../images/camera2/metadata/android.statistics.lensShadingMap/red_shading.png" />
+     * <img alt="Green (even rows) lens shading map" src="../../../../images/camera2/metadata/android.statistics.lensShadingMap/green_e_shading.png" />
+     * <img alt="Green (odd rows) lens shading map" src="../../../../images/camera2/metadata/android.statistics.lensShadingMap/green_o_shading.png" />
+     * <img alt="Blue lens shading map" src="../../../../images/camera2/metadata/android.statistics.lensShadingMap/blue_shading.png" /></p>
+     * <p>As a visualization only, inverting the full-color map to recover an
+     * image of a gray wall (using bicubic interpolation for visual quality) as captured by the sensor gives:</p>
+     * <p><img alt="Image of a uniform white wall (inverse shading map)" src="../../../../images/camera2/metadata/android.statistics.lensShadingMap/inv_shading.png" /></p>
+     *
+     * @see CaptureRequest#COLOR_CORRECTION_MODE
+     * @hide
      */
     public static final Key<float[]> STATISTICS_LENS_SHADING_MAP =
             new Key<float[]>("android.statistics.lensShadingMap", float[].class);
@@ -2395,17 +2456,15 @@
      * @see CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE
      * @see CameraCharacteristics#SENSOR_INFO_PIXEL_ARRAY_SIZE
      */
-    public static final Key<int[]> STATISTICS_HOT_PIXEL_MAP =
-            new Key<int[]>("android.statistics.hotPixelMap", int[].class);
+    public static final Key<android.graphics.Point[]> STATISTICS_HOT_PIXEL_MAP =
+            new Key<android.graphics.Point[]>("android.statistics.hotPixelMap", android.graphics.Point[].class);
 
     /**
      * <p>Whether the camera device will output the lens
      * shading map in output result metadata.</p>
      * <p>When set to ON,
-     * {@link CaptureResult#STATISTICS_LENS_SHADING_MAP android.statistics.lensShadingMap} must be provided in
+     * android.statistics.lensShadingMap must be provided in
      * the output result metadata.</p>
-     *
-     * @see CaptureResult#STATISTICS_LENS_SHADING_MAP
      * @see #STATISTICS_LENS_SHADING_MAP_MODE_OFF
      * @see #STATISTICS_LENS_SHADING_MAP_MODE_ON
      */
@@ -2416,10 +2475,10 @@
      * <p>Tonemapping / contrast / gamma curve for the blue
      * channel, to use when {@link CaptureRequest#TONEMAP_MODE android.tonemap.mode} is
      * CONTRAST_CURVE.</p>
-     * <p>See {@link CaptureRequest#TONEMAP_CURVE_RED android.tonemap.curveRed} for more details.</p>
+     * <p>See android.tonemap.curveRed for more details.</p>
      *
-     * @see CaptureRequest#TONEMAP_CURVE_RED
      * @see CaptureRequest#TONEMAP_MODE
+     * @hide
      */
     public static final Key<float[]> TONEMAP_CURVE_BLUE =
             new Key<float[]>("android.tonemap.curveBlue", float[].class);
@@ -2428,10 +2487,10 @@
      * <p>Tonemapping / contrast / gamma curve for the green
      * channel, to use when {@link CaptureRequest#TONEMAP_MODE android.tonemap.mode} is
      * CONTRAST_CURVE.</p>
-     * <p>See {@link CaptureRequest#TONEMAP_CURVE_RED android.tonemap.curveRed} for more details.</p>
+     * <p>See android.tonemap.curveRed for more details.</p>
      *
-     * @see CaptureRequest#TONEMAP_CURVE_RED
      * @see CaptureRequest#TONEMAP_MODE
+     * @hide
      */
     public static final Key<float[]> TONEMAP_CURVE_GREEN =
             new Key<float[]>("android.tonemap.curveGreen", float[].class);
@@ -2441,7 +2500,7 @@
      * channel, to use when {@link CaptureRequest#TONEMAP_MODE android.tonemap.mode} is
      * CONTRAST_CURVE.</p>
      * <p>Each channel's curve is defined by an array of control points:</p>
-     * <pre><code>{@link CaptureRequest#TONEMAP_CURVE_RED android.tonemap.curveRed} =
+     * <pre><code>android.tonemap.curveRed =
      * [ P0in, P0out, P1in, P1out, P2in, P2out, P3in, P3out, ..., PNin, PNout ]
      * 2 &lt;= N &lt;= {@link CameraCharacteristics#TONEMAP_MAX_CURVE_POINTS android.tonemap.maxCurvePoints}</code></pre>
      * <p>These are sorted in order of increasing <code>Pin</code>; it is always
@@ -2457,15 +2516,15 @@
      * only specify the red channel and the precision is limited to 4
      * digits, for conciseness.</p>
      * <p>Linear mapping:</p>
-     * <pre><code>{@link CaptureRequest#TONEMAP_CURVE_RED android.tonemap.curveRed} = [ 0, 0, 1.0, 1.0 ]
+     * <pre><code>android.tonemap.curveRed = [ 0, 0, 1.0, 1.0 ]
      * </code></pre>
      * <p><img alt="Linear mapping curve" src="../../../../images/camera2/metadata/android.tonemap.curveRed/linear_tonemap.png" /></p>
      * <p>Invert mapping:</p>
-     * <pre><code>{@link CaptureRequest#TONEMAP_CURVE_RED android.tonemap.curveRed} = [ 0, 1.0, 1.0, 0 ]
+     * <pre><code>android.tonemap.curveRed = [ 0, 1.0, 1.0, 0 ]
      * </code></pre>
      * <p><img alt="Inverting mapping curve" src="../../../../images/camera2/metadata/android.tonemap.curveRed/inverse_tonemap.png" /></p>
      * <p>Gamma 1/2.2 mapping, with 16 control points:</p>
-     * <pre><code>{@link CaptureRequest#TONEMAP_CURVE_RED android.tonemap.curveRed} = [
+     * <pre><code>android.tonemap.curveRed = [
      * 0.0000, 0.0000, 0.0667, 0.2920, 0.1333, 0.4002, 0.2000, 0.4812,
      * 0.2667, 0.5484, 0.3333, 0.6069, 0.4000, 0.6594, 0.4667, 0.7072,
      * 0.5333, 0.7515, 0.6000, 0.7928, 0.6667, 0.8317, 0.7333, 0.8685,
@@ -2473,7 +2532,7 @@
      * </code></pre>
      * <p><img alt="Gamma = 1/2.2 tonemapping curve" src="../../../../images/camera2/metadata/android.tonemap.curveRed/gamma_tonemap.png" /></p>
      * <p>Standard sRGB gamma mapping, per IEC 61966-2-1:1999, with 16 control points:</p>
-     * <pre><code>{@link CaptureRequest#TONEMAP_CURVE_RED android.tonemap.curveRed} = [
+     * <pre><code>android.tonemap.curveRed = [
      * 0.0000, 0.0000, 0.0667, 0.2864, 0.1333, 0.4007, 0.2000, 0.4845,
      * 0.2667, 0.5532, 0.3333, 0.6125, 0.4000, 0.6652, 0.4667, 0.7130,
      * 0.5333, 0.7569, 0.6000, 0.7977, 0.6667, 0.8360, 0.7333, 0.8721,
@@ -2481,14 +2540,67 @@
      * </code></pre>
      * <p><img alt="sRGB tonemapping curve" src="../../../../images/camera2/metadata/android.tonemap.curveRed/srgb_tonemap.png" /></p>
      *
-     * @see CaptureRequest#TONEMAP_CURVE_RED
      * @see CameraCharacteristics#TONEMAP_MAX_CURVE_POINTS
      * @see CaptureRequest#TONEMAP_MODE
+     * @hide
      */
     public static final Key<float[]> TONEMAP_CURVE_RED =
             new Key<float[]>("android.tonemap.curveRed", float[].class);
 
     /**
+     * <p>Tonemapping / contrast / gamma curve to use when {@link CaptureRequest#TONEMAP_MODE android.tonemap.mode}
+     * is CONTRAST_CURVE.</p>
+     * <p>The tonemapCurve consist of three curves for each of red, green, and blue
+     * channels respectively. The following example uses the red channel as an
+     * example. The same logic applies to green and blue channel.
+     * Each channel's curve is defined by an array of control points:</p>
+     * <pre><code>curveRed =
+     * [ P0(in, out), P1(in, out), P2(in, out), P3(in, out), ..., PN(in, out) ]
+     * 2 &lt;= N &lt;= {@link CameraCharacteristics#TONEMAP_MAX_CURVE_POINTS android.tonemap.maxCurvePoints}</code></pre>
+     * <p>These are sorted in order of increasing <code>Pin</code>; it is always
+     * guaranteed that input values 0.0 and 1.0 are included in the list to
+     * define a complete mapping. For input values between control points,
+     * the camera device must linearly interpolate between the control
+     * points.</p>
+     * <p>Each curve can have an independent number of points, and the number
+     * of points can be less than max (that is, the request doesn't have to
+     * always provide a curve with number of points equivalent to
+     * {@link CameraCharacteristics#TONEMAP_MAX_CURVE_POINTS android.tonemap.maxCurvePoints}).</p>
+     * <p>A few examples, and their corresponding graphical mappings; these
+     * only specify the red channel and the precision is limited to 4
+     * digits, for conciseness.</p>
+     * <p>Linear mapping:</p>
+     * <pre><code>curveRed = [ (0, 0), (1.0, 1.0) ]
+     * </code></pre>
+     * <p><img alt="Linear mapping curve" src="../../../../images/camera2/metadata/android.tonemap.curveRed/linear_tonemap.png" /></p>
+     * <p>Invert mapping:</p>
+     * <pre><code>curveRed = [ (0, 1.0), (1.0, 0) ]
+     * </code></pre>
+     * <p><img alt="Inverting mapping curve" src="../../../../images/camera2/metadata/android.tonemap.curveRed/inverse_tonemap.png" /></p>
+     * <p>Gamma 1/2.2 mapping, with 16 control points:</p>
+     * <pre><code>curveRed = [
+     * (0.0000, 0.0000), (0.0667, 0.2920), (0.1333, 0.4002), (0.2000, 0.4812),
+     * (0.2667, 0.5484), (0.3333, 0.6069), (0.4000, 0.6594), (0.4667, 0.7072),
+     * (0.5333, 0.7515), (0.6000, 0.7928), (0.6667, 0.8317), (0.7333, 0.8685),
+     * (0.8000, 0.9035), (0.8667, 0.9370), (0.9333, 0.9691), (1.0000, 1.0000) ]
+     * </code></pre>
+     * <p><img alt="Gamma = 1/2.2 tonemapping curve" src="../../../../images/camera2/metadata/android.tonemap.curveRed/gamma_tonemap.png" /></p>
+     * <p>Standard sRGB gamma mapping, per IEC 61966-2-1:1999, with 16 control points:</p>
+     * <pre><code>curveRed = [
+     * (0.0000, 0.0000), (0.0667, 0.2864), (0.1333, 0.4007), (0.2000, 0.4845),
+     * (0.2667, 0.5532), (0.3333, 0.6125), (0.4000, 0.6652), (0.4667, 0.7130),
+     * (0.5333, 0.7569), (0.6000, 0.7977), (0.6667, 0.8360), (0.7333, 0.8721),
+     * (0.8000, 0.9063), (0.8667, 0.9389), (0.9333, 0.9701), (1.0000, 1.0000) ]
+     * </code></pre>
+     * <p><img alt="sRGB tonemapping curve" src="../../../../images/camera2/metadata/android.tonemap.curveRed/srgb_tonemap.png" /></p>
+     *
+     * @see CameraCharacteristics#TONEMAP_MAX_CURVE_POINTS
+     * @see CaptureRequest#TONEMAP_MODE
+     */
+    public static final Key<android.hardware.camera2.params.TonemapCurve> TONEMAP_CURVE =
+            new Key<android.hardware.camera2.params.TonemapCurve>("android.tonemap.curve", android.hardware.camera2.params.TonemapCurve.class);
+
+    /**
      * <p>High-level global contrast/gamma/tonemapping control.</p>
      * <p>When switching to an application-defined contrast curve by setting
      * {@link CaptureRequest#TONEMAP_MODE android.tonemap.mode} to CONTRAST_CURVE, the curve is defined
@@ -2504,8 +2616,7 @@
      * <p>This must be set to a valid mode in
      * {@link CameraCharacteristics#TONEMAP_AVAILABLE_TONE_MAP_MODES android.tonemap.availableToneMapModes}.</p>
      * <p>When using either FAST or HIGH_QUALITY, the camera device will
-     * emit its own tonemap curve in {@link CaptureRequest#TONEMAP_CURVE_RED android.tonemap.curveRed},
-     * {@link CaptureRequest#TONEMAP_CURVE_GREEN android.tonemap.curveGreen}, and {@link CaptureRequest#TONEMAP_CURVE_BLUE android.tonemap.curveBlue}.
+     * emit its own tonemap curve in {@link CaptureRequest#TONEMAP_CURVE android.tonemap.curve}.
      * These values are always available, and as close as possible to the
      * actually used nonlinear/nonglobal transforms.</p>
      * <p>If a request is sent with CONTRAST_CURVE with the camera device's
@@ -2513,9 +2624,7 @@
      * roughly the same.</p>
      *
      * @see CameraCharacteristics#TONEMAP_AVAILABLE_TONE_MAP_MODES
-     * @see CaptureRequest#TONEMAP_CURVE_BLUE
-     * @see CaptureRequest#TONEMAP_CURVE_GREEN
-     * @see CaptureRequest#TONEMAP_CURVE_RED
+     * @see CaptureRequest#TONEMAP_CURVE
      * @see CaptureRequest#TONEMAP_MODE
      * @see #TONEMAP_MODE_CONTRAST_CURVE
      * @see #TONEMAP_MODE_FAST
diff --git a/core/java/android/hardware/camera2/TotalCaptureResult.java b/core/java/android/hardware/camera2/TotalCaptureResult.java
new file mode 100644
index 0000000..2647959
--- /dev/null
+++ b/core/java/android/hardware/camera2/TotalCaptureResult.java
@@ -0,0 +1,84 @@
+/*
+ * 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;
+
+import android.hardware.camera2.impl.CameraMetadataNative;
+
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * <p>The total assembled results of a single image capture from the image sensor.</p>
+ *
+ * <p>Contains the final configuration for the capture hardware (sensor, lens,
+ * flash), the processing pipeline, the control algorithms, and the output
+ * buffers.</p>
+ *
+ * <p>A {@code TotalCaptureResult} is produced by a {@link CameraDevice} after processing a
+ * {@link CaptureRequest}. All properties listed for capture requests can also
+ * be queried on the capture result, to determine the final values used for
+ * capture. The result also includes additional metadata about the state of the
+ * camera device during the capture.</p>
+ *
+ * <p>All properties returned by {@link CameraCharacteristics#getAvailableCaptureResultKeys()}
+ * are available (that is {@link CaptureResult#get} will return non-{@code null}, if and only if
+ * that key that was enabled by the request. A few keys such as
+ * {@link CaptureResult#STATISTICS_FACES} are disabled by default unless enabled with a switch (such
+ * as {@link CaptureRequest#STATISTICS_FACE_DETECT_MODE}). Refer to each key documentation on
+ * a case-by-case basis.</p>
+ *
+ * <p>{@link TotalCaptureResult} objects are immutable.</p>
+ *
+ * @see CameraDevice.CaptureListener#onCaptureCompleted
+ */
+public final class TotalCaptureResult extends CaptureResult {
+
+    /**
+     * Takes ownership of the passed-in properties object
+     * @hide
+     */
+    public TotalCaptureResult(CameraMetadataNative results, CaptureRequest parent, int sequenceId) {
+        super(results, parent, sequenceId);
+    }
+
+    /**
+     * Creates a request-less result.
+     *
+     * <p><strong>For testing only.</strong></p>
+     * @hide
+     */
+    public TotalCaptureResult(CameraMetadataNative results, int sequenceId) {
+        super(results, sequenceId);
+    }
+
+    /**
+     * Get the read-only list of partial results that compose this total result.
+     *
+     * <p>The list is returned is unmodifiable; attempting to modify it will result in a
+     * {@code UnsupportedOperationException} being thrown.</p>
+     *
+     * <p>The list size will be inclusive between {@code 1} and
+     * {@link CameraCharacteristics#REQUEST_PARTIAL_RESULT_COUNT}, in ascending order
+     * of when {@link CameraDevice.CaptureListener#onCaptureProgressed} was invoked.</p>
+     *
+     * @return unmodifiable list of partial results
+     */
+    public List<CaptureResult> getPartialResults() {
+        // TODO
+        return Collections.unmodifiableList(null);
+    }
+}
diff --git a/core/java/android/hardware/camera2/impl/CameraDevice.java b/core/java/android/hardware/camera2/impl/CameraDevice.java
index b082a70..9a4c531 100644
--- a/core/java/android/hardware/camera2/impl/CameraDevice.java
+++ b/core/java/android/hardware/camera2/impl/CameraDevice.java
@@ -20,10 +20,13 @@
 
 import android.hardware.camera2.CameraAccessException;
 import android.hardware.camera2.CameraCaptureSession;
+import android.hardware.camera2.CameraCharacteristics;
+import android.hardware.camera2.CameraManager;
 import android.hardware.camera2.CaptureRequest;
 import android.hardware.camera2.CaptureResult;
 import android.hardware.camera2.ICameraDeviceCallbacks;
 import android.hardware.camera2.ICameraDeviceUser;
+import android.hardware.camera2.TotalCaptureResult;
 import android.hardware.camera2.utils.CameraBinderDecorator;
 import android.hardware.camera2.utils.CameraRuntimeException;
 import android.hardware.camera2.utils.LongParcelable;
@@ -72,6 +75,7 @@
     private final SparseArray<Surface> mConfiguredOutputs = new SparseArray<Surface>();
 
     private final String mCameraId;
+    private final CameraCharacteristics mCharacteristics;
 
     /**
      * A list tracking request and its expected last frame.
@@ -150,13 +154,15 @@
         }
     };
 
-    public CameraDevice(String cameraId, StateListener listener, Handler handler) {
+    public CameraDevice(String cameraId, StateListener listener, Handler handler,
+                        CameraCharacteristics characteristics) {
         if (cameraId == null || listener == null || handler == null) {
             throw new IllegalArgumentException("Null argument given");
         }
         mCameraId = cameraId;
         mDeviceListener = listener;
         mDeviceHandler = handler;
+        mCharacteristics = characteristics;
 
         final int MAX_TAG_LEN = 23;
         String tag = String.format("CameraDevice-JV-%s", mCameraId);
@@ -359,7 +365,7 @@
                             holder.getListener().onCaptureSequenceCompleted(
                                     CameraDevice.this,
                                     requestId,
-                                    (int)lastFrameNumber);
+                                    lastFrameNumber);
                         }
                     }
                 };
@@ -717,7 +723,7 @@
                                 holder.getListener().onCaptureSequenceCompleted(
                                     CameraDevice.this,
                                     requestId,
-                                    (int)lastFrameNumber);
+                                    lastFrameNumber);
                             }
                         }
                     };
@@ -850,11 +856,18 @@
         @Override
         public void onResultReceived(CameraMetadataNative result,
                 CaptureResultExtras resultExtras) throws RemoteException {
+
             int requestId = resultExtras.getRequestId();
             if (DEBUG) {
                 Log.v(TAG, "Received result frame " + resultExtras.getFrameNumber() + " for id "
                         + requestId);
             }
+
+
+            // 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 = CameraDevice.this.mCaptureListenerMap.get(requestId);
@@ -888,12 +901,15 @@
             }
 
             final CaptureRequest request = holder.getRequest(resultExtras.getSubsequenceId());
-            final CaptureResult resultAsCapture = new CaptureResult(result, request, requestId);
+
 
             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
@@ -907,6 +923,9 @@
                     }
                 };
             } else {
+                final TotalCaptureResult resultAsCapture =
+                        new TotalCaptureResult(result, request, requestId);
+
                 // Final capture result
                 resultDispatch = new Runnable() {
                     @Override
@@ -958,4 +977,8 @@
             return (mRemoteDevice == null);
         }
     }
+
+    private CameraCharacteristics getCharacteristics() {
+        return mCharacteristics;
+    }
 }
diff --git a/core/java/android/hardware/camera2/impl/CameraMetadataNative.java b/core/java/android/hardware/camera2/impl/CameraMetadataNative.java
index ab2c49a..83aee5d 100644
--- a/core/java/android/hardware/camera2/impl/CameraMetadataNative.java
+++ b/core/java/android/hardware/camera2/impl/CameraMetadataNative.java
@@ -31,6 +31,7 @@
 import android.hardware.camera2.marshal.impl.MarshalQueryableEnum;
 import android.hardware.camera2.marshal.impl.MarshalQueryableMeteringRectangle;
 import android.hardware.camera2.marshal.impl.MarshalQueryableNativeByteToInteger;
+import android.hardware.camera2.marshal.impl.MarshalQueryablePair;
 import android.hardware.camera2.marshal.impl.MarshalQueryableParcelable;
 import android.hardware.camera2.marshal.impl.MarshalQueryablePrimitive;
 import android.hardware.camera2.marshal.impl.MarshalQueryableRange;
@@ -43,13 +44,19 @@
 import android.hardware.camera2.marshal.impl.MarshalQueryableStreamConfigurationDuration;
 import android.hardware.camera2.marshal.impl.MarshalQueryableString;
 import android.hardware.camera2.params.Face;
+import android.hardware.camera2.params.LensShadingMap;
 import android.hardware.camera2.params.StreamConfiguration;
 import android.hardware.camera2.params.StreamConfigurationDuration;
 import android.hardware.camera2.params.StreamConfigurationMap;
+import android.hardware.camera2.params.TonemapCurve;
 import android.hardware.camera2.utils.TypeReference;
+import android.location.Location;
+import android.location.LocationManager;
 import android.os.Parcelable;
 import android.os.Parcel;
 import android.util.Log;
+import android.util.Pair;
+import android.util.Size;
 
 import com.android.internal.util.Preconditions;
 
@@ -57,6 +64,7 @@
 import java.nio.ByteBuffer;
 import java.nio.ByteOrder;
 import java.util.ArrayList;
+import java.util.HashMap;
 
 /**
  * Implementation of camera metadata marshal/unmarshal across Binder to
@@ -209,6 +217,37 @@
     // this should be in sync with HAL_PIXEL_FORMAT_BLOB defined in graphics.h
     public static final int NATIVE_JPEG_FORMAT = 0x21;
 
+    private static final String CELLID_PROCESS = "CELLID";
+    private static final String GPS_PROCESS = "GPS";
+
+    private static String translateLocationProviderToProcess(final String provider) {
+        if (provider == null) {
+            return null;
+        }
+        switch(provider) {
+            case LocationManager.GPS_PROVIDER:
+                return GPS_PROCESS;
+            case LocationManager.NETWORK_PROVIDER:
+                return CELLID_PROCESS;
+            default:
+                return null;
+        }
+    }
+
+    private static String translateProcessToLocationProvider(final String process) {
+        if (process == null) {
+            return null;
+        }
+        switch(process) {
+            case GPS_PROCESS:
+                return LocationManager.GPS_PROVIDER;
+            case CELLID_PROCESS:
+                return LocationManager.NETWORK_PROVIDER;
+            default:
+                return null;
+        }
+    }
+
     public CameraMetadataNative() {
         super();
         mMetadataPtr = nativeAllocate();
@@ -297,9 +336,9 @@
     public <T> T get(Key<T> key) {
         Preconditions.checkNotNull(key, "key must not be null");
 
-        T value = getOverride(key);
-        if (value != null) {
-            return value;
+        Pair<T, Boolean> override = getOverride(key);
+        if (override.second) {
+            return override.first;
         }
 
         return getBase(key);
@@ -409,23 +448,44 @@
         ByteBuffer buffer = ByteBuffer.wrap(values).order(ByteOrder.nativeOrder());
         return marshaler.unmarshal(buffer);
     }
-
     // Need overwrite some metadata that has different definitions between native
     // and managed sides.
     @SuppressWarnings("unchecked")
-    private <T> T getOverride(Key<T> key) {
+    private <T> Pair<T, Boolean> getOverride(Key<T> key) {
+        T value = null;
+        boolean override = true;
+
         if (key.equals(CameraCharacteristics.SCALER_AVAILABLE_FORMATS)) {
-            return (T) getAvailableFormats();
+            value = (T) getAvailableFormats();
         } else if (key.equals(CaptureResult.STATISTICS_FACES)) {
-            return (T) getFaces();
+            value = (T) getFaces();
         } else if (key.equals(CaptureResult.STATISTICS_FACE_RECTANGLES)) {
-            return (T) getFaceRectangles();
+            value = (T) getFaceRectangles();
         } else if (key.equals(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP)) {
-            return (T) getStreamConfigurationMap();
+            value = (T) getStreamConfigurationMap();
+        } else if (key.equals(CameraCharacteristics.CONTROL_MAX_REGIONS_AE)) {
+            value = (T) getMaxRegions(key);
+        } else if (key.equals(CameraCharacteristics.CONTROL_MAX_REGIONS_AWB)) {
+            value = (T) getMaxRegions(key);
+        } else if (key.equals(CameraCharacteristics.CONTROL_MAX_REGIONS_AF)) {
+            value = (T) getMaxRegions(key);
+        } else if (key.equals(CameraCharacteristics.REQUEST_MAX_NUM_OUTPUT_RAW)) {
+            value = (T) getMaxNumOutputs(key);
+        } else if (key.equals(CameraCharacteristics.REQUEST_MAX_NUM_OUTPUT_PROC)) {
+            value = (T) getMaxNumOutputs(key);
+        } else if (key.equals(CameraCharacteristics.REQUEST_MAX_NUM_OUTPUT_PROC_STALLING)) {
+            value = (T) getMaxNumOutputs(key);
+        } else if (key.equals(CaptureRequest.TONEMAP_CURVE)) {
+            value = (T) getTonemapCurve();
+        } else if (key.equals(CaptureResult.JPEG_GPS_LOCATION)) {
+            value = (T) getGpsLocation();
+        } else if (key.equals(CaptureResult.STATISTICS_LENS_SHADING_CORRECTION_MAP)) {
+            value = (T) getLensShadingMap();
+        } else {
+            override = false;
         }
 
-        // For other keys, get() falls back to getBase()
-        return null;
+        return Pair.create(value, override);
     }
 
     private int[] getAvailableFormats() {
@@ -541,6 +601,62 @@
         return fixedFaceRectangles;
     }
 
+    private LensShadingMap getLensShadingMap() {
+        float[] lsmArray = getBase(CaptureResult.STATISTICS_LENS_SHADING_MAP);
+        if (lsmArray == null) {
+            Log.w(TAG, "getLensShadingMap - Lens shading map was null.");
+            return null;
+        }
+        Size s = get(CameraCharacteristics.LENS_INFO_SHADING_MAP_SIZE);
+        LensShadingMap map = new LensShadingMap(lsmArray, s.getHeight(), s.getWidth());
+        return map;
+    }
+
+    private Location getGpsLocation() {
+        String processingMethod = get(CaptureResult.JPEG_GPS_PROCESSING_METHOD);
+        Location l = new Location(translateProcessToLocationProvider(processingMethod));
+
+        double[] coords = get(CaptureResult.JPEG_GPS_COORDINATES);
+        Long timeStamp = get(CaptureResult.JPEG_GPS_TIMESTAMP);
+
+        if (timeStamp != null) {
+            l.setTime(timeStamp);
+        } else {
+            Log.w(TAG, "getGpsLocation - No timestamp for GPS location.");
+        }
+
+        if (coords != null) {
+            l.setLatitude(coords[0]);
+            l.setLongitude(coords[1]);
+            l.setAltitude(coords[2]);
+        } else {
+            Log.w(TAG, "getGpsLocation - No coordinates for GPS location");
+        }
+
+        return l;
+    }
+
+    private boolean setGpsLocation(Location l) {
+        if (l == null) {
+            return false;
+        }
+
+        double[] coords = { l.getLatitude(), l.getLongitude(), l.getAltitude() };
+        String processMethod = translateLocationProviderToProcess(l.getProvider());
+        long timestamp = l.getTime();
+
+        set(CaptureRequest.JPEG_GPS_TIMESTAMP, timestamp);
+        set(CaptureRequest.JPEG_GPS_COORDINATES, coords);
+
+        if (processMethod == null) {
+            Log.w(TAG, "setGpsLocation - No process method, Location is not from a GPS or NETWORK" +
+                    "provider");
+        } else {
+            setBase(CaptureRequest.JPEG_GPS_PROCESSING_METHOD, processMethod);
+        }
+        return true;
+    }
+
     private StreamConfigurationMap getStreamConfigurationMap() {
         StreamConfiguration[] configurations = getBase(
                 CameraCharacteristics.SCALER_AVAILABLE_STREAM_CONFIGURATIONS);
@@ -552,6 +668,63 @@
         return new StreamConfigurationMap(configurations, minFrameDurations, stallDurations);
     }
 
+    private <T> Integer getMaxRegions(Key<T> key) {
+        final int AE = 0;
+        final int AWB = 1;
+        final int AF = 2;
+
+        // The order of the elements is: (AE, AWB, AF)
+        int[] maxRegions = getBase(CameraCharacteristics.CONTROL_MAX_REGIONS);
+
+        if (maxRegions == null) {
+            return null;
+        }
+
+        if (key.equals(CameraCharacteristics.CONTROL_MAX_REGIONS_AE)) {
+            return maxRegions[AE];
+        } else if (key.equals(CameraCharacteristics.CONTROL_MAX_REGIONS_AWB)) {
+            return maxRegions[AWB];
+        } else if (key.equals(CameraCharacteristics.CONTROL_MAX_REGIONS_AF)) {
+            return maxRegions[AF];
+        } else {
+            throw new AssertionError("Invalid key " + key);
+        }
+    }
+
+    private <T> Integer getMaxNumOutputs(Key<T> key) {
+        final int RAW = 0;
+        final int PROC = 1;
+        final int PROC_STALLING = 2;
+
+        // The order of the elements is: (raw, proc+nonstalling, proc+stalling)
+        int[] maxNumOutputs = getBase(CameraCharacteristics.REQUEST_MAX_NUM_OUTPUT_STREAMS);
+
+        if (maxNumOutputs == null) {
+            return null;
+        }
+
+        if (key.equals(CameraCharacteristics.REQUEST_MAX_NUM_OUTPUT_RAW)) {
+            return maxNumOutputs[RAW];
+        } else if (key.equals(CameraCharacteristics.REQUEST_MAX_NUM_OUTPUT_PROC)) {
+            return maxNumOutputs[PROC];
+        } else if (key.equals(CameraCharacteristics.REQUEST_MAX_NUM_OUTPUT_PROC_STALLING)) {
+            return maxNumOutputs[PROC_STALLING];
+        } else {
+            throw new AssertionError("Invalid key " + key);
+        }
+    }
+
+    private <T> TonemapCurve getTonemapCurve() {
+        float[] red = getBase(CaptureRequest.TONEMAP_CURVE_RED);
+        float[] green = getBase(CaptureRequest.TONEMAP_CURVE_GREEN);
+        float[] blue = getBase(CaptureRequest.TONEMAP_CURVE_BLUE);
+        if (red == null || green == null || blue == null) {
+            return null;
+        }
+        TonemapCurve tc = new TonemapCurve(red, green, blue);
+        return tc;
+    }
+
     private <T> void setBase(CameraCharacteristics.Key<T> key, T value) {
         setBase(key.getNativeKey(), value);
     }
@@ -591,8 +764,11 @@
             return setAvailableFormats((int[]) value);
         } else if (key.equals(CaptureResult.STATISTICS_FACE_RECTANGLES)) {
             return setFaceRectangles((Rect[]) value);
+        } else if (key.equals(CaptureRequest.TONEMAP_CURVE)) {
+            return setTonemapCurve((TonemapCurve) value);
+        } else if (key.equals(CaptureResult.JPEG_GPS_LOCATION)) {
+            return setGpsLocation((Location) value);
         }
-
         // For other keys, set() falls back to setBase().
         return false;
     }
@@ -646,6 +822,24 @@
         return true;
     }
 
+    private <T> boolean setTonemapCurve(TonemapCurve tc) {
+        if (tc == null) {
+            return false;
+        }
+
+        float[][] curve = new float[3][];
+        for (int i = TonemapCurve.CHANNEL_RED; i <= TonemapCurve.CHANNEL_BLUE; i++) {
+            int pointCount = tc.getPointCount(i);
+            curve[i] = new float[pointCount * TonemapCurve.POINT_SIZE];
+            tc.copyColorCurve(i, curve[i], 0);
+        }
+        setBase(CaptureRequest.TONEMAP_CURVE_RED, curve[0]);
+        setBase(CaptureRequest.TONEMAP_CURVE_GREEN, curve[1]);
+        setBase(CaptureRequest.TONEMAP_CURVE_BLUE, curve[2]);
+
+        return true;
+    }
+
     private long mMetadataPtr; // native CameraMetadata*
 
     private native long nativeAllocate();
@@ -813,6 +1007,7 @@
                 new MarshalQueryableString(),
                 new MarshalQueryableReprocessFormatsMap(),
                 new MarshalQueryableRange(),
+                new MarshalQueryablePair(),
                 new MarshalQueryableMeteringRectangle(),
                 new MarshalQueryableColorSpaceTransform(),
                 new MarshalQueryableStreamConfiguration(),
diff --git a/core/java/android/hardware/camera2/marshal/impl/MarshalQueryablePair.java b/core/java/android/hardware/camera2/marshal/impl/MarshalQueryablePair.java
new file mode 100644
index 0000000..0a9935d
--- /dev/null
+++ b/core/java/android/hardware/camera2/marshal/impl/MarshalQueryablePair.java
@@ -0,0 +1,158 @@
+/*
+ * 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.marshal.impl;
+
+import android.hardware.camera2.marshal.Marshaler;
+import android.hardware.camera2.marshal.MarshalQueryable;
+import android.hardware.camera2.marshal.MarshalRegistry;
+import android.hardware.camera2.utils.TypeReference;
+import android.util.Pair;
+
+import java.lang.reflect.Constructor;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.ParameterizedType;
+import java.lang.reflect.Type;
+import java.nio.ByteBuffer;
+
+/**
+ * Marshal {@link Pair} to/from any native type
+ */
+public class MarshalQueryablePair<T1, T2>
+        implements MarshalQueryable<Pair<T1, T2>> {
+
+    private class MarshalerPair extends Marshaler<Pair<T1, T2>> {
+        private final Class<? super Pair<T1, T2>> mClass;
+        private final Constructor<Pair<T1, T2>> mConstructor;
+        /** Marshal the {@code T1} inside of {@code Pair<T1, T2>} */
+        private final Marshaler<T1> mNestedTypeMarshalerFirst;
+        /** Marshal the {@code T1} inside of {@code Pair<T1, T2>} */
+        private final Marshaler<T2> mNestedTypeMarshalerSecond;
+
+        @SuppressWarnings("unchecked")
+        protected MarshalerPair(TypeReference<Pair<T1, T2>> typeReference,
+                int nativeType) {
+            super(MarshalQueryablePair.this, typeReference, nativeType);
+
+            mClass = typeReference.getRawType();
+
+            /*
+             * Lookup the actual type arguments, e.g. Pair<Integer, Float> --> [Integer, Float]
+             * and then get the marshalers for that managed type.
+             */
+            ParameterizedType paramType;
+            try {
+                paramType = (ParameterizedType) typeReference.getType();
+            } catch (ClassCastException e) {
+                throw new AssertionError("Raw use of Pair is not supported", e);
+            }
+
+            // Get type marshaler for T1
+            {
+                Type actualTypeArgument = paramType.getActualTypeArguments()[0];
+
+                TypeReference<?> actualTypeArgToken =
+                        TypeReference.createSpecializedTypeReference(actualTypeArgument);
+
+                mNestedTypeMarshalerFirst = (Marshaler<T1>)MarshalRegistry.getMarshaler(
+                        actualTypeArgToken, mNativeType);
+            }
+            // Get type marshaler for T2
+            {
+                Type actualTypeArgument = paramType.getActualTypeArguments()[1];
+
+                TypeReference<?> actualTypeArgToken =
+                        TypeReference.createSpecializedTypeReference(actualTypeArgument);
+
+                mNestedTypeMarshalerSecond = (Marshaler<T2>)MarshalRegistry.getMarshaler(
+                        actualTypeArgToken, mNativeType);
+            }
+            try {
+                mConstructor = (Constructor<Pair<T1, T2>>)mClass.getConstructor(
+                        Object.class, Object.class);
+            } catch (NoSuchMethodException e) {
+                throw new AssertionError(e);
+            }
+        }
+
+        @Override
+        public void marshal(Pair<T1, T2> value, ByteBuffer buffer) {
+            if (value.first == null) {
+                throw new UnsupportedOperationException("Pair#first must not be null");
+            } else if (value.second == null) {
+                throw new UnsupportedOperationException("Pair#second must not be null");
+            }
+
+            mNestedTypeMarshalerFirst.marshal(value.first, buffer);
+            mNestedTypeMarshalerSecond.marshal(value.second, buffer);
+        }
+
+        @Override
+        public Pair<T1, T2> unmarshal(ByteBuffer buffer) {
+            T1 first = mNestedTypeMarshalerFirst.unmarshal(buffer);
+            T2 second = mNestedTypeMarshalerSecond.unmarshal(buffer);
+
+            try {
+                return mConstructor.newInstance(first, second);
+            } catch (InstantiationException e) {
+                throw new AssertionError(e);
+            } catch (IllegalAccessException e) {
+                throw new AssertionError(e);
+            } catch (IllegalArgumentException e) {
+                throw new AssertionError(e);
+            } catch (InvocationTargetException e) {
+                throw new AssertionError(e);
+            }
+        }
+
+        @Override
+        public int getNativeSize() {
+            int firstSize = mNestedTypeMarshalerFirst.getNativeSize();
+            int secondSize = mNestedTypeMarshalerSecond.getNativeSize();
+
+            if (firstSize != NATIVE_SIZE_DYNAMIC && secondSize != NATIVE_SIZE_DYNAMIC) {
+                return firstSize + secondSize;
+            } else {
+                return NATIVE_SIZE_DYNAMIC;
+            }
+        }
+
+        @Override
+        public int calculateMarshalSize(Pair<T1, T2> value) {
+            int nativeSize = getNativeSize();
+
+            if (nativeSize != NATIVE_SIZE_DYNAMIC) {
+                return nativeSize;
+            } else {
+                int firstSize = mNestedTypeMarshalerFirst.calculateMarshalSize(value.first);
+                int secondSize = mNestedTypeMarshalerSecond.calculateMarshalSize(value.second);
+
+                return firstSize + secondSize;
+            }
+        }
+    }
+
+    @Override
+    public Marshaler<Pair<T1, T2>> createMarshaler(TypeReference<Pair<T1, T2>> managedType,
+            int nativeType) {
+        return new MarshalerPair(managedType, nativeType);
+    }
+
+    @Override
+    public boolean isTypeMappingSupported(TypeReference<Pair<T1, T2>> managedType, int nativeType) {
+        return (Pair.class.equals(managedType.getRawType()));
+    }
+
+}
diff --git a/core/java/android/hardware/camera2/params/ColorSpaceTransform.java b/core/java/android/hardware/camera2/params/ColorSpaceTransform.java
index fa8c8ea..b4289db 100644
--- a/core/java/android/hardware/camera2/params/ColorSpaceTransform.java
+++ b/core/java/android/hardware/camera2/params/ColorSpaceTransform.java
@@ -139,8 +139,8 @@
             throw new IllegalArgumentException("row out of range");
         }
 
-        int numerator = mElements[row * ROWS * RATIONAL_SIZE + column + OFFSET_NUMERATOR];
-        int denominator = mElements[row * ROWS * RATIONAL_SIZE + column + OFFSET_DENOMINATOR];
+        int numerator = mElements[(row * COLUMNS + column) * RATIONAL_SIZE + OFFSET_NUMERATOR];
+        int denominator = mElements[(row * COLUMNS + column) * RATIONAL_SIZE + OFFSET_DENOMINATOR];
 
         return new Rational(numerator, denominator);
     }
@@ -162,7 +162,7 @@
     public void copyElements(Rational[] destination, int offset) {
         checkArgumentNonnegative(offset, "offset must not be negative");
         checkNotNull(destination, "destination must not be null");
-        if (destination.length + offset < COUNT) {
+        if (destination.length - offset < COUNT) {
             throw new ArrayIndexOutOfBoundsException("destination too small to fit elements");
         }
 
@@ -197,7 +197,7 @@
     public void copyElements(int[] destination, int offset) {
         checkArgumentNonnegative(offset, "offset must not be negative");
         checkNotNull(destination, "destination must not be null");
-        if (destination.length + offset < COUNT_INT) {
+        if (destination.length - offset < COUNT_INT) {
             throw new ArrayIndexOutOfBoundsException("destination too small to fit elements");
         }
 
diff --git a/core/java/android/hardware/camera2/params/LensShadingMap.java b/core/java/android/hardware/camera2/params/LensShadingMap.java
index b328f57..9bbc33a 100644
--- a/core/java/android/hardware/camera2/params/LensShadingMap.java
+++ b/core/java/android/hardware/camera2/params/LensShadingMap.java
@@ -28,7 +28,7 @@
 /**
  * Immutable class for describing a {@code 4 x N x M} lens shading map of floats.
  *
- * @see CameraCharacteristics#LENS_SHADING_MAP
+ * @see CaptureResult#STATISTICS_LENS_SHADING_CORRECTION_MAP
  */
 public final class LensShadingMap {
 
@@ -62,12 +62,12 @@
     public LensShadingMap(final float[] elements, final int rows, final int columns) {
 
         mRows = checkArgumentPositive(rows, "rows must be positive");
-        mColumns = checkArgumentPositive(rows, "columns must be positive");
+        mColumns = checkArgumentPositive(columns, "columns must be positive");
         mElements = checkNotNull(elements, "elements must not be null");
 
         if (elements.length != getGainFactorCount()) {
             throw new IllegalArgumentException("elements must be " + getGainFactorCount() +
-                    " length");
+                    " length, received " + elements.length);
         }
 
         // Every element must be finite and >= 1.0f
@@ -242,4 +242,4 @@
     private final int mRows;
     private final int mColumns;
     private final float[] mElements;
-};
+}
diff --git a/core/java/android/hardware/camera2/params/MeteringRectangle.java b/core/java/android/hardware/camera2/params/MeteringRectangle.java
index a26c57d..93fd053 100644
--- a/core/java/android/hardware/camera2/params/MeteringRectangle.java
+++ b/core/java/android/hardware/camera2/params/MeteringRectangle.java
@@ -13,6 +13,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+
 package android.hardware.camera2.params;
 
 import android.util.Size;
@@ -25,22 +26,50 @@
 import android.hardware.camera2.utils.HashCodeHelpers;
 
 /**
- * An immutable class to represent a rectangle {@code (x,y, width, height)} with an
- * additional weight component.
- *
- * </p>The rectangle is defined to be inclusive of the specified coordinates.</p>
- *
- * <p>When used with a {@link CaptureRequest}, the coordinate system is based on the active pixel
+ * An immutable class to represent a rectangle {@code (x, y, width, height)} with an additional
+ * weight component.
+ * <p>
+ * The rectangle is defined to be inclusive of the specified coordinates.
+ * </p>
+ * <p>
+ * When used with a {@link CaptureRequest}, the coordinate system is based on the active pixel
  * array, with {@code (0,0)} being the top-left pixel in the
  * {@link CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE active pixel array}, and
  * {@code (android.sensor.info.activeArraySize.width - 1,
- * android.sensor.info.activeArraySize.height - 1)}
- * being the bottom-right pixel in the active pixel array.
+ * android.sensor.info.activeArraySize.height - 1)} being the bottom-right pixel in the active pixel
+ * array.
  * </p>
- *
- * <p>The metering weight is nonnegative.</p>
+ * <p>
+ * The weight must range from {@value #METERING_WEIGHT_MIN} to {@value #METERING_WEIGHT_MAX}
+ * inclusively, and represents a weight for every pixel in the area. This means that a large
+ * metering area with the same weight as a smaller area will have more effect in the metering
+ * result. Metering areas can partially overlap and the camera device will add the weights in the
+ * overlap rectangle.
+ * </p>
+ * <p>
+ * If all rectangles have 0 weight, then no specific metering area needs to be used by the camera
+ * device. If the metering rectangle is outside the used android.scaler.cropRegion returned in
+ * capture result metadata, the camera device will ignore the sections outside the rectangle and
+ * output the used sections in the result metadata.
+ * </p>
  */
 public final class MeteringRectangle {
+    /**
+     * The minimum value of valid metering weight.
+     */
+    public static final int METERING_WEIGHT_MIN = 0;
+
+    /**
+     * The maximum value of valid metering weight.
+     */
+    public static final int METERING_WEIGHT_MAX = 1000;
+
+    /**
+     * Weights set to this value will cause the camera device to ignore this rectangle.
+     * If all metering rectangles are weighed with 0, the camera device will choose its own metering
+     * rectangles.
+     */
+    public static final int METERING_WEIGHT_DONT_CARE = 0;
 
     private final int mX;
     private final int mY;
@@ -55,16 +84,17 @@
      * @param y coordinate >= 0
      * @param width width >= 0
      * @param height height >= 0
-     * @param meteringWeight weight >= 0
-     *
-     * @throws IllegalArgumentException if any of the parameters were non-negative
+     * @param meteringWeight weight between {@value #METERING_WEIGHT_MIN} and
+     *        {@value #METERING_WEIGHT_MAX} inclusively
+     * @throws IllegalArgumentException if any of the parameters were negative
      */
     public MeteringRectangle(int x, int y, int width, int height, int meteringWeight) {
         mX = checkArgumentNonnegative(x, "x must be nonnegative");
         mY = checkArgumentNonnegative(y, "y must be nonnegative");
         mWidth = checkArgumentNonnegative(width, "width must be nonnegative");
         mHeight = checkArgumentNonnegative(height, "height must be nonnegative");
-        mWeight = checkArgumentNonnegative(meteringWeight, "meteringWeight must be nonnegative");
+        mWeight = checkArgumentInRange(
+                meteringWeight, METERING_WEIGHT_MIN, METERING_WEIGHT_MAX, "meteringWeight");
     }
 
     /**
@@ -74,7 +104,7 @@
      * @param dimensions a non-{@code null} {@link android.util.Size Size} with width, height >= 0
      * @param meteringWeight weight >= 0
      *
-     * @throws IllegalArgumentException if any of the parameters were non-negative
+     * @throws IllegalArgumentException if any of the parameters were negative
      * @throws NullPointerException if any of the arguments were null
      */
     public MeteringRectangle(Point xy, Size dimensions, int meteringWeight) {
@@ -94,7 +124,7 @@
      * @param rect a non-{@code null} rectangle with all x,y,w,h dimensions >= 0
      * @param meteringWeight weight >= 0
      *
-     * @throws IllegalArgumentException if any of the parameters were non-negative
+     * @throws IllegalArgumentException if any of the parameters were negative
      * @throws NullPointerException if any of the arguments were null
      */
     public MeteringRectangle(Rect rect, int meteringWeight) {
@@ -210,7 +240,7 @@
                 && mY == other.mY
                 && mWidth == other.mWidth
                 && mHeight == other.mHeight
-                && mWidth == other.mWidth);
+                && mWeight == other.mWeight);
     }
 
     /**
diff --git a/core/java/android/hardware/hdmi/HdmiCec.java b/core/java/android/hardware/hdmi/HdmiCec.java
index a71a74d..723eda1 100644
--- a/core/java/android/hardware/hdmi/HdmiCec.java
+++ b/core/java/android/hardware/hdmi/HdmiCec.java
@@ -194,6 +194,8 @@
         DEVICE_RECORDER,  // ADDR_RECORDER_3
         DEVICE_TUNER,  // ADDR_TUNER_4
         DEVICE_PLAYBACK,  // ADDR_PLAYBACK_3
+        DEVICE_RESERVED,
+        DEVICE_RESERVED,
         DEVICE_TV,  // ADDR_SPECIFIC_USE
     };
 
@@ -210,6 +212,8 @@
         "Recorder_3",
         "Tuner_4",
         "Playback_3",
+        "Reserved_1",
+        "Reserved_2",
         "Secondary_TV",
     };
 
diff --git a/core/java/android/hardware/hdmi/HdmiCecClient.java b/core/java/android/hardware/hdmi/HdmiCecClient.java
index cd86cd8..dcb3624 100644
--- a/core/java/android/hardware/hdmi/HdmiCecClient.java
+++ b/core/java/android/hardware/hdmi/HdmiCecClient.java
@@ -69,44 +69,28 @@
      * Send &lt;Active Source&gt; message.
      */
     public void sendActiveSource() {
-        try {
-            mService.sendActiveSource(mBinder);
-        } catch (RemoteException e) {
-            Log.e(TAG, "sendActiveSource threw exception ", e);
-        }
+        Log.w(TAG, "In transition to HdmiControlManager. Will not work.");
     }
 
     /**
      * Send &lt;Inactive Source&gt; message.
      */
     public void sendInactiveSource() {
-        try {
-            mService.sendInactiveSource(mBinder);
-        } catch (RemoteException e) {
-            Log.e(TAG, "sendInactiveSource threw exception ", e);
-        }
+        Log.w(TAG, "In transition to HdmiControlManager. Will not work.");
     }
 
     /**
      * Send &lt;Text View On&gt; message.
      */
     public void sendTextViewOn() {
-        try {
-            mService.sendTextViewOn(mBinder);
-        } catch (RemoteException e) {
-            Log.e(TAG, "sendTextViewOn threw exception ", e);
-        }
+        Log.w(TAG, "In transition to HdmiControlManager. Will not work.");
     }
 
     /**
      * Send &lt;Image View On&gt; message.
      */
     public void sendImageViewOn() {
-        try {
-            mService.sendImageViewOn(mBinder);
-        } catch (RemoteException e) {
-            Log.e(TAG, "sendImageViewOn threw exception ", e);
-        }
+        Log.w(TAG, "In transition to HdmiControlManager. Will not work.");
     }
 
     /**
@@ -116,11 +100,7 @@
      *        {@link HdmiCec#ADDR_TV}.
      */
     public void sendGiveDevicePowerStatus(int address) {
-        try {
-            mService.sendGiveDevicePowerStatus(mBinder, address);
-        } catch (RemoteException e) {
-            Log.e(TAG, "sendGiveDevicePowerStatus threw exception ", e);
-        }
+        Log.w(TAG, "In transition to HdmiControlManager. Will not work.");
     }
 
     /**
@@ -133,11 +113,7 @@
      * @return true if TV is on; otherwise false.
      */
     public boolean isTvOn() {
-        try {
-            return mService.isTvOn(mBinder);
-        } catch (RemoteException e) {
-            Log.e(TAG, "isTvOn threw exception ", e);
-        }
-        return false;
+        Log.w(TAG, "In transition to HdmiControlManager. Will not work.");
+        return true;
     }
 }
diff --git a/core/java/android/hardware/hdmi/HdmiCecManager.java b/core/java/android/hardware/hdmi/HdmiCecManager.java
index 10b058c..03c46d8 100644
--- a/core/java/android/hardware/hdmi/HdmiCecManager.java
+++ b/core/java/android/hardware/hdmi/HdmiCecManager.java
@@ -45,15 +45,7 @@
      * @return {@link HdmiCecClient} instance. {@code null} on failure.
      */
     public HdmiCecClient getClient(int type, HdmiCecClient.Listener listener) {
-        if (mService == null) {
-            return null;
-        }
-        try {
-            IBinder b = mService.allocateLogicalDevice(type, getListenerWrapper(listener));
-            return HdmiCecClient.create(mService, b);
-        } catch (RemoteException e) {
-            return null;
-        }
+        return HdmiCecClient.create(mService, null);
     }
 
     private IHdmiCecListener getListenerWrapper(final HdmiCecClient.Listener listener) {
diff --git a/core/java/android/hardware/hdmi/HdmiCecMessage.java b/core/java/android/hardware/hdmi/HdmiCecMessage.java
index a8aa376..62fa279 100644
--- a/core/java/android/hardware/hdmi/HdmiCecMessage.java
+++ b/core/java/android/hardware/hdmi/HdmiCecMessage.java
@@ -46,7 +46,7 @@
     public HdmiCecMessage(int source, int destination, int opcode, byte[] params) {
         mSource = source;
         mDestination = destination;
-        mOpcode = opcode;
+        mOpcode = opcode & 0xFF;
         mParams = Arrays.copyOf(params, params.length);
     }
 
diff --git a/core/java/android/hardware/hdmi/HdmiPlaybackClient.java b/core/java/android/hardware/hdmi/HdmiPlaybackClient.java
index 83da29a..f0bd237 100644
--- a/core/java/android/hardware/hdmi/HdmiPlaybackClient.java
+++ b/core/java/android/hardware/hdmi/HdmiPlaybackClient.java
@@ -90,7 +90,7 @@
     public void queryDisplayStatus(DisplayStatusCallback callback) {
         // TODO: PendingResult.
         try {
-            mService.oneTouchPlay(getCallbackWrapper(callback));
+            mService.queryDisplayStatus(getCallbackWrapper(callback));
         } catch (RemoteException e) {
             Log.e(TAG, "queryDisplayStatus threw exception ", e);
         }
diff --git a/core/java/android/inputmethodservice/IInputMethodWrapper.java b/core/java/android/inputmethodservice/IInputMethodWrapper.java
index 06d8e4a..857e335 100644
--- a/core/java/android/inputmethodservice/IInputMethodWrapper.java
+++ b/core/java/android/inputmethodservice/IInputMethodWrapper.java
@@ -69,6 +69,7 @@
     private static final int DO_CHANGE_INPUTMETHOD_SUBTYPE = 80;
    
     final WeakReference<AbstractInputMethodService> mTarget;
+    final Context mContext;
     final HandlerCaller mCaller;
     final WeakReference<InputMethod> mInputMethod;
     final int mTargetSdkVersion;
@@ -111,8 +112,8 @@
     public IInputMethodWrapper(AbstractInputMethodService context,
             InputMethod inputMethod) {
         mTarget = new WeakReference<AbstractInputMethodService>(context);
-        mCaller = new HandlerCaller(context.getApplicationContext(), null,
-                this, true /*asyncHandler*/);
+        mContext = context.getApplicationContext();
+        mCaller = new HandlerCaller(mContext, null, this, true /*asyncHandler*/);
         mInputMethod = new WeakReference<InputMethod>(inputMethod);
         mTargetSdkVersion = context.getApplicationInfo().targetSdkVersion;
     }
@@ -186,7 +187,7 @@
             case DO_CREATE_SESSION: {
                 SomeArgs args = (SomeArgs)msg.obj;
                 inputMethod.createSession(new InputMethodSessionCallbackWrapper(
-                        mCaller.mContext, (InputChannel)args.arg1,
+                        mContext, (InputChannel)args.arg1,
                         (IInputSessionCallback)args.arg2));
                 args.recycle();
                 return;
diff --git a/core/java/android/inputmethodservice/InputMethodService.java b/core/java/android/inputmethodservice/InputMethodService.java
index 4bccaf1..3417de1 100644
--- a/core/java/android/inputmethodservice/InputMethodService.java
+++ b/core/java/android/inputmethodservice/InputMethodService.java
@@ -39,6 +39,7 @@
 import android.util.Log;
 import android.util.PrintWriterPrinter;
 import android.util.Printer;
+import android.view.Gravity;
 import android.view.KeyCharacterMap;
 import android.view.KeyEvent;
 import android.view.LayoutInflater;
@@ -679,7 +680,7 @@
         mInflater = (LayoutInflater)getSystemService(
                 Context.LAYOUT_INFLATER_SERVICE);
         mWindow = new SoftInputWindow(this, "InputMethod", mTheme, null, null, mDispatcherState,
-                false);
+                WindowManager.LayoutParams.TYPE_INPUT_METHOD, Gravity.BOTTOM, false);
         if (mHardwareAccelerated) {
             mWindow.getWindow().addFlags(WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED);
         }
diff --git a/core/java/android/inputmethodservice/SoftInputWindow.java b/core/java/android/inputmethodservice/SoftInputWindow.java
index a9bace1..795117e 100644
--- a/core/java/android/inputmethodservice/SoftInputWindow.java
+++ b/core/java/android/inputmethodservice/SoftInputWindow.java
@@ -37,6 +37,8 @@
     final Callback mCallback;
     final KeyEvent.Callback mKeyEventCallback;
     final KeyEvent.DispatcherState mDispatcherState;
+    final int mWindowType;
+    final int mGravity;
     final boolean mTakesFocus;
     private final Rect mBounds = new Rect();
 
@@ -64,12 +66,14 @@
      */
     public SoftInputWindow(Context context, String name, int theme, Callback callback,
             KeyEvent.Callback keyEventCallback, KeyEvent.DispatcherState dispatcherState,
-            boolean takesFocus) {
+            int windowType, int gravity, boolean takesFocus) {
         super(context, theme);
         mName = name;
         mCallback = callback;
         mKeyEventCallback = keyEventCallback;
         mDispatcherState = dispatcherState;
+        mWindowType = windowType;
+        mGravity = gravity;
         mTakesFocus = takesFocus;
         initDockWindow();
     }
@@ -97,47 +101,6 @@
     }
 
     /**
-     * Get the size of the DockWindow.
-     * 
-     * @return If the DockWindow sticks to the top or bottom of the screen, the
-     *         return value is the height of the DockWindow, and its width is
-     *         equal to the width of the screen; If the DockWindow sticks to the
-     *         left or right of the screen, the return value is the width of the
-     *         DockWindow, and its height is equal to the height of the screen.
-     */
-    public int getSize() {
-        WindowManager.LayoutParams lp = getWindow().getAttributes();
-
-        if (lp.gravity == Gravity.TOP || lp.gravity == Gravity.BOTTOM) {
-            return lp.height;
-        } else {
-            return lp.width;
-        }
-    }
-
-    /**
-     * Set the size of the DockWindow.
-     * 
-     * @param size If the DockWindow sticks to the top or bottom of the screen,
-     *        <var>size</var> is the height of the DockWindow, and its width is
-     *        equal to the width of the screen; If the DockWindow sticks to the
-     *        left or right of the screen, <var>size</var> is the width of the
-     *        DockWindow, and its height is equal to the height of the screen.
-     */
-    public void setSize(int size) {
-        WindowManager.LayoutParams lp = getWindow().getAttributes();
-
-        if (lp.gravity == Gravity.TOP || lp.gravity == Gravity.BOTTOM) {
-            lp.width = -1;
-            lp.height = size;
-        } else {
-            lp.width = size;
-            lp.height = -1;
-        }
-        getWindow().setAttributes(lp);
-    }
-
-    /**
      * Set which boundary of the screen the DockWindow sticks to.
      * 
      * @param gravity The boundary of the screen to stick. See {#link
@@ -147,18 +110,22 @@
      */
     public void setGravity(int gravity) {
         WindowManager.LayoutParams lp = getWindow().getAttributes();
-
-        boolean oldIsVertical = (lp.gravity == Gravity.TOP || lp.gravity == Gravity.BOTTOM);
-
         lp.gravity = gravity;
+        updateWidthHeight(lp);
+        getWindow().setAttributes(lp);
+    }
 
-        boolean newIsVertical = (lp.gravity == Gravity.TOP || lp.gravity == Gravity.BOTTOM);
+    public int getGravity() {
+        return getWindow().getAttributes().gravity;
+    }
 
-        if (oldIsVertical != newIsVertical) {
-            int tmp = lp.width;
-            lp.width = lp.height;
-            lp.height = tmp;
-            getWindow().setAttributes(lp);
+    private void updateWidthHeight(WindowManager.LayoutParams lp) {
+        if (lp.gravity == Gravity.TOP || lp.gravity == Gravity.BOTTOM) {
+            lp.width = WindowManager.LayoutParams.MATCH_PARENT;
+            lp.height = WindowManager.LayoutParams.WRAP_CONTENT;
+        } else {
+            lp.width = WindowManager.LayoutParams.WRAP_CONTENT;
+            lp.height = WindowManager.LayoutParams.MATCH_PARENT;
         }
     }
 
@@ -201,14 +168,11 @@
     private void initDockWindow() {
         WindowManager.LayoutParams lp = getWindow().getAttributes();
 
-        lp.type = WindowManager.LayoutParams.TYPE_INPUT_METHOD;
+        lp.type = mWindowType;
         lp.setTitle(mName);
 
-        lp.gravity = Gravity.BOTTOM;
-        lp.width = -1;
-        // Let the input method window's orientation follow sensor based rotation
-        // Turn this off for now, it is very problematic.
-        //lp.screenOrientation = ActivityInfo.SCREEN_ORIENTATION_USER;
+        lp.gravity = mGravity;
+        updateWidthHeight(lp);
 
         getWindow().setAttributes(lp);
 
diff --git a/core/java/android/net/ConnectivityManager.java b/core/java/android/net/ConnectivityManager.java
index 2f2aba3..a48a388 100644
--- a/core/java/android/net/ConnectivityManager.java
+++ b/core/java/android/net/ConnectivityManager.java
@@ -40,6 +40,7 @@
 import android.util.Log;
 
 import com.android.internal.telephony.ITelephony;
+import com.android.internal.telephony.PhoneConstants;
 import com.android.internal.util.Protocol;
 
 import java.net.InetAddress;
@@ -807,11 +808,34 @@
      * @deprecated Deprecated in favor of the cleaner {@link #requestNetwork} api.
      */
     public int startUsingNetworkFeature(int networkType, String feature) {
-        try {
-            return mService.startUsingNetworkFeature(networkType, feature,
-                    new Binder());
-        } catch (RemoteException e) {
-            return -1;
+        NetworkCapabilities netCap = networkCapabilitiesForFeature(networkType, feature);
+        if (netCap == null) {
+            Log.d(TAG, "Can't satisfy startUsingNetworkFeature for " + networkType + ", " +
+                    feature);
+            return PhoneConstants.APN_REQUEST_FAILED;
+        }
+
+        NetworkRequest request = null;
+        synchronized (sLegacyRequests) {
+            LegacyRequest l = sLegacyRequests.get(netCap);
+            if (l != null) {
+                Log.d(TAG, "renewing startUsingNetworkFeature request " + l.networkRequest);
+                renewRequestLocked(l);
+                if (l.currentNetwork != null) {
+                    return PhoneConstants.APN_ALREADY_ACTIVE;
+                } else {
+                    return PhoneConstants.APN_REQUEST_STARTED;
+                }
+            }
+
+            request = requestNetworkForFeatureLocked(netCap);
+        }
+        if (request != null) {
+            Log.d(TAG, "starting startUsingNeworkFeature for request " + request);
+            return PhoneConstants.APN_REQUEST_STARTED;
+        } else {
+            Log.d(TAG, " request Failed");
+            return PhoneConstants.APN_REQUEST_FAILED;
         }
     }
 
@@ -831,11 +855,172 @@
      * @deprecated Deprecated in favor of the cleaner {@link #requestNetwork} api.
      */
     public int stopUsingNetworkFeature(int networkType, String feature) {
-        try {
-            return mService.stopUsingNetworkFeature(networkType, feature);
-        } catch (RemoteException e) {
+        NetworkCapabilities netCap = networkCapabilitiesForFeature(networkType, feature);
+        if (netCap == null) {
+            Log.d(TAG, "Can't satisfy stopUsingNetworkFeature for " + networkType + ", " +
+                    feature);
             return -1;
         }
+
+        NetworkRequest request = removeRequestForFeature(netCap);
+        if (request != null) {
+            Log.d(TAG, "stopUsingNetworkFeature for " + networkType + ", " + feature);
+            releaseNetworkRequest(request);
+        }
+        return 1;
+    }
+
+    private NetworkCapabilities networkCapabilitiesForFeature(int networkType, String feature) {
+        if (networkType == TYPE_MOBILE) {
+            int cap = -1;
+            if ("enableMMS".equals(feature)) {
+                cap = NetworkCapabilities.NET_CAPABILITY_MMS;
+            } else if ("enableSUPL".equals(feature)) {
+                cap = NetworkCapabilities.NET_CAPABILITY_SUPL;
+            } else if ("enableDUN".equals(feature) || "enableDUNAlways".equals(feature)) {
+                cap = NetworkCapabilities.NET_CAPABILITY_DUN;
+            } else if ("enableHIPRI".equals(feature)) {
+                cap = NetworkCapabilities.NET_CAPABILITY_INTERNET;
+            } else if ("enableFOTA".equals(feature)) {
+                cap = NetworkCapabilities.NET_CAPABILITY_FOTA;
+            } else if ("enableIMS".equals(feature)) {
+                cap = NetworkCapabilities.NET_CAPABILITY_IMS;
+            } else if ("enableCBS".equals(feature)) {
+                cap = NetworkCapabilities.NET_CAPABILITY_CBS;
+            } else {
+                return null;
+            }
+            NetworkCapabilities netCap = new NetworkCapabilities();
+            netCap.addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR);
+            netCap.addNetworkCapability(cap);
+            return netCap;
+        } else if (networkType == TYPE_WIFI) {
+            if ("p2p".equals(feature)) {
+                NetworkCapabilities netCap = new NetworkCapabilities();
+                netCap.addTransportType(NetworkCapabilities.TRANSPORT_WIFI);
+                netCap.addNetworkCapability(NetworkCapabilities.NET_CAPABILITY_WIFI_P2P);
+                return netCap;
+            }
+        }
+        return null;
+    }
+
+    private int legacyTypeForNetworkCapabilities(NetworkCapabilities netCap) {
+        if (netCap == null) return TYPE_NONE;
+        if (netCap.hasCapability(NetworkCapabilities.NET_CAPABILITY_CBS)) {
+            return TYPE_MOBILE_CBS;
+        }
+        if (netCap.hasCapability(NetworkCapabilities.NET_CAPABILITY_IMS)) {
+            return TYPE_MOBILE_IMS;
+        }
+        if (netCap.hasCapability(NetworkCapabilities.NET_CAPABILITY_FOTA)) {
+            return TYPE_MOBILE_FOTA;
+        }
+        if (netCap.hasCapability(NetworkCapabilities.NET_CAPABILITY_DUN)) {
+            return TYPE_MOBILE_DUN;
+        }
+        if (netCap.hasCapability(NetworkCapabilities.NET_CAPABILITY_SUPL)) {
+            return TYPE_MOBILE_SUPL;
+        }
+        if (netCap.hasCapability(NetworkCapabilities.NET_CAPABILITY_MMS)) {
+            return TYPE_MOBILE_MMS;
+        }
+        if (netCap.hasCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)) {
+            return TYPE_MOBILE_HIPRI;
+        }
+        if (netCap.hasCapability(NetworkCapabilities.NET_CAPABILITY_WIFI_P2P)) {
+            return TYPE_WIFI_P2P;
+        }
+        return TYPE_NONE;
+    }
+
+    private static class LegacyRequest {
+        NetworkCapabilities networkCapabilities;
+        NetworkRequest networkRequest;
+        int expireSequenceNumber;
+        Network currentNetwork;
+        int delay = -1;
+        NetworkCallbackListener networkCallbackListener = new NetworkCallbackListener() {
+            @Override
+            public void onAvailable(NetworkRequest request, Network network) {
+                currentNetwork = network;
+                Log.d(TAG, "startUsingNetworkFeature got Network:" + network);
+                network.bindProcessForHostResolution();
+            }
+            @Override
+            public void onLost(NetworkRequest request, Network network) {
+                if (network.equals(currentNetwork)) {
+                    currentNetwork = null;
+                    network.unbindProcessForHostResolution();
+                }
+                Log.d(TAG, "startUsingNetworkFeature lost Network:" + network);
+            }
+        };
+    }
+
+    private HashMap<NetworkCapabilities, LegacyRequest> sLegacyRequests =
+            new HashMap<NetworkCapabilities, LegacyRequest>();
+
+    private NetworkRequest findRequestForFeature(NetworkCapabilities netCap) {
+        synchronized (sLegacyRequests) {
+            LegacyRequest l = sLegacyRequests.get(netCap);
+            if (l != null) return l.networkRequest;
+        }
+        return null;
+    }
+
+    private void renewRequestLocked(LegacyRequest l) {
+        l.expireSequenceNumber++;
+        Log.d(TAG, "renewing request to seqNum " + l.expireSequenceNumber);
+        sendExpireMsgForFeature(l.networkCapabilities, l.expireSequenceNumber, l.delay);
+    }
+
+    private void expireRequest(NetworkCapabilities netCap, int sequenceNum) {
+        int ourSeqNum = -1;
+        synchronized (sLegacyRequests) {
+            LegacyRequest l = sLegacyRequests.get(netCap);
+            if (l == null) return;
+            ourSeqNum = l.expireSequenceNumber;
+            if (l.expireSequenceNumber == sequenceNum) {
+                releaseNetworkRequest(l.networkRequest);
+                sLegacyRequests.remove(netCap);
+            }
+        }
+        Log.d(TAG, "expireRequest with " + ourSeqNum + ", " + sequenceNum);
+    }
+
+    private NetworkRequest requestNetworkForFeatureLocked(NetworkCapabilities netCap) {
+        int delay = -1;
+        int type = legacyTypeForNetworkCapabilities(netCap);
+        try {
+            delay = mService.getRestoreDefaultNetworkDelay(type);
+        } catch (RemoteException e) {}
+        LegacyRequest l = new LegacyRequest();
+        l.networkCapabilities = netCap;
+        l.delay = delay;
+        l.expireSequenceNumber = 0;
+        l.networkRequest = sendRequestForNetwork(netCap, l.networkCallbackListener, 0,
+                REQUEST, type);
+        if (l.networkRequest == null) return null;
+        sLegacyRequests.put(netCap, l);
+        sendExpireMsgForFeature(netCap, l.expireSequenceNumber, delay);
+        return l.networkRequest;
+    }
+
+    private void sendExpireMsgForFeature(NetworkCapabilities netCap, int seqNum, int delay) {
+        if (delay >= 0) {
+            Log.d(TAG, "sending expire msg with seqNum " + seqNum + " and delay " + delay);
+            Message msg = sCallbackHandler.obtainMessage(EXPIRE_LEGACY_REQUEST, seqNum, 0, netCap);
+            sCallbackHandler.sendMessageDelayed(msg, delay);
+        }
+    }
+
+    private NetworkRequest removeRequestForFeature(NetworkCapabilities netCap) {
+        synchronized (sLegacyRequests) {
+            LegacyRequest l = sLegacyRequests.remove(netCap);
+            if (l == null) return null;
+            return l.networkRequest;
+        }
     }
 
     /**
@@ -1782,8 +1967,10 @@
     public static final int CALLBACK_RELEASED           = BASE + 8;
     /** @hide */
     public static final int CALLBACK_EXIT               = BASE + 9;
+    /** @hide obj = NetworkCapabilities, arg1 = seq number */
+    private static final int EXPIRE_LEGACY_REQUEST      = BASE + 10;
 
-    private static class CallbackHandler extends Handler {
+    private class CallbackHandler extends Handler {
         private final HashMap<NetworkRequest, NetworkCallbackListener>mCallbackMap;
         private final AtomicInteger mRefCount;
         private static final String TAG = "ConnectivityManager.CallbackHandler";
@@ -1903,6 +2090,10 @@
                     getLooper().quit();
                     break;
                 }
+                case EXPIRE_LEGACY_REQUEST: {
+                    expireRequest((NetworkCapabilities)message.obj, message.arg1);
+                    break;
+                }
             }
         }
 
@@ -1954,8 +2145,9 @@
     private final static int LISTEN  = 1;
     private final static int REQUEST = 2;
 
-    private NetworkRequest somethingForNetwork(NetworkCapabilities need,
-            NetworkCallbackListener networkCallbackListener, int timeoutSec, int action) {
+    private NetworkRequest sendRequestForNetwork(NetworkCapabilities need,
+            NetworkCallbackListener networkCallbackListener, int timeoutSec, int action,
+            int legacyType) {
         NetworkRequest networkRequest = null;
         if (networkCallbackListener == null) {
             throw new IllegalArgumentException("null NetworkCallbackListener");
@@ -1968,7 +2160,7 @@
                         new Binder());
             } else {
                 networkRequest = mService.requestNetwork(need, new Messenger(sCallbackHandler),
-                        timeoutSec, new Binder());
+                        timeoutSec, new Binder(), legacyType);
             }
             if (networkRequest != null) {
                 synchronized(sNetworkCallbackListener) {
@@ -1998,7 +2190,7 @@
      */
     public NetworkRequest requestNetwork(NetworkCapabilities need,
             NetworkCallbackListener networkCallbackListener) {
-        return somethingForNetwork(need, networkCallbackListener, 0, REQUEST);
+        return sendRequestForNetwork(need, networkCallbackListener, 0, REQUEST, TYPE_NONE);
     }
 
     /**
@@ -2021,7 +2213,8 @@
      */
     public NetworkRequest requestNetwork(NetworkCapabilities need,
             NetworkCallbackListener networkCallbackListener, int timeoutSec) {
-        return somethingForNetwork(need, networkCallbackListener, timeoutSec, REQUEST);
+        return sendRequestForNetwork(need, networkCallbackListener, timeoutSec, REQUEST,
+                TYPE_NONE);
     }
 
     /**
@@ -2099,7 +2292,7 @@
      */
     public NetworkRequest listenForNetwork(NetworkCapabilities need,
             NetworkCallbackListener networkCallbackListener) {
-        return somethingForNetwork(need, networkCallbackListener, 0, LISTEN);
+        return sendRequestForNetwork(need, networkCallbackListener, 0, LISTEN, TYPE_NONE);
     }
 
     /**
diff --git a/core/java/android/net/ConnectivityServiceProtocol.java b/core/java/android/net/ConnectivityServiceProtocol.java
deleted file mode 100644
index 74096b4..0000000
--- a/core/java/android/net/ConnectivityServiceProtocol.java
+++ /dev/null
@@ -1,70 +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.net;
-
-import static com.android.internal.util.Protocol.BASE_CONNECTIVITY_SERVICE;
-
-/**
- * Describes the Internal protocols used to communicate with ConnectivityService.
- * @hide
- */
-public class ConnectivityServiceProtocol {
-
-    private static final int BASE = BASE_CONNECTIVITY_SERVICE;
-
-    private ConnectivityServiceProtocol() {}
-
-    /**
-     * This is a contract between ConnectivityService and various bearers.
-     * A NetworkFactory is an abstract entity that creates NetworkAgent objects.
-     * The bearers register with ConnectivityService using
-     * ConnectivityManager.registerNetworkFactory, where they pass in a Messenger
-     * to be used to deliver the following Messages.
-     */
-    public static class NetworkFactoryProtocol {
-        private NetworkFactoryProtocol() {}
-        /**
-         * Pass a network request to the bearer.  If the bearer believes it can
-         * satisfy the request it should connect to the network and create a
-         * NetworkAgent.  Once the NetworkAgent is fully functional it will
-         * register itself with ConnectivityService using registerNetworkAgent.
-         * If the bearer cannot immediately satisfy the request (no network,
-         * user disabled the radio, lower-scored network) it should remember
-         * any NetworkRequests it may be able to satisfy in the future.  It may
-         * disregard any that it will never be able to service, for example
-         * those requiring a different bearer.
-         * msg.obj = NetworkRequest
-         * msg.arg1 = score - the score of the any network currently satisfying this
-         *            request.  If this bearer knows in advance it cannot
-         *            exceed this score it should not try to connect, holding the request
-         *            for the future.
-         *            Note that subsequent events may give a different (lower
-         *            or higher) score for this request, transmitted to each
-         *            NetworkFactory through additional CMD_REQUEST_NETWORK msgs
-         *            with the same NetworkRequest but an updated score.
-         *            Also, network conditions may change for this bearer
-         *            allowing for a better score in the future.
-         */
-        public static final int CMD_REQUEST_NETWORK = BASE;
-
-        /**
-         * Cancel a network request
-         * msg.obj = NetworkRequest
-         */
-        public static final int CMD_CANCEL_REQUEST = BASE + 1;
-    }
-}
diff --git a/core/java/android/net/IConnectivityManager.aidl b/core/java/android/net/IConnectivityManager.aidl
index baec36a..5f1ff3e 100644
--- a/core/java/android/net/IConnectivityManager.aidl
+++ b/core/java/android/net/IConnectivityManager.aidl
@@ -158,7 +158,7 @@
             in NetworkCapabilities nc, int score);
 
     NetworkRequest requestNetwork(in NetworkCapabilities networkCapabilities,
-            in Messenger messenger, int timeoutSec, in IBinder binder);
+            in Messenger messenger, int timeoutSec, in IBinder binder, int legacy);
 
     NetworkRequest pendingRequestForNetwork(in NetworkCapabilities networkCapabilities,
             in PendingIntent operation);
@@ -170,4 +170,6 @@
             in PendingIntent operation);
 
     void releaseNetworkRequest(in NetworkRequest networkRequest);
+
+    int getRestoreDefaultNetworkDelay(int networkType);
 }
diff --git a/core/java/android/net/Network.java b/core/java/android/net/Network.java
index e489e05..64516e6 100644
--- a/core/java/android/net/Network.java
+++ b/core/java/android/net/Network.java
@@ -94,11 +94,26 @@
             mNetId = netId;
         }
 
+        private void connectToHost(Socket socket, String host, int port) throws IOException {
+            // Lookup addresses only on this Network.
+            InetAddress[] hostAddresses = getAllByName(host);
+            // Try all but last address ignoring exceptions.
+            for (int i = 0; i < hostAddresses.length - 1; i++) {
+                try {
+                    socket.connect(new InetSocketAddress(hostAddresses[i], port));
+                    return;
+                } catch (IOException e) {
+                }
+            }
+            // Try last address.  Do throw exceptions.
+            socket.connect(new InetSocketAddress(hostAddresses[hostAddresses.length - 1], port));
+        }
+
         @Override
         public Socket createSocket(String host, int port, InetAddress localHost, int localPort) throws IOException {
             Socket socket = createSocket();
             socket.bind(new InetSocketAddress(localHost, localPort));
-            socket.connect(new InetSocketAddress(host, port));
+            connectToHost(socket, host, port);
             return socket;
         }
 
@@ -121,7 +136,7 @@
         @Override
         public Socket createSocket(String host, int port) throws IOException {
             Socket socket = createSocket();
-            socket.connect(new InetSocketAddress(host, port));
+            connectToHost(socket, host, port);
             return socket;
         }
 
diff --git a/core/java/android/net/NetworkAgent.java b/core/java/android/net/NetworkAgent.java
index c2b06a2..7e8b1f1 100644
--- a/core/java/android/net/NetworkAgent.java
+++ b/core/java/android/net/NetworkAgent.java
@@ -24,85 +24,39 @@
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.util.Log;
-import android.util.SparseArray;
 
 import com.android.internal.util.AsyncChannel;
 import com.android.internal.util.Protocol;
 
+import java.util.ArrayList;
 import java.util.concurrent.atomic.AtomicBoolean;
 
 /**
- * A Utility class for handling NetworkRequests.
- *
- * Created by bearer-specific code to handle tracking requests, scores,
- * network data and handle communicating with ConnectivityService.  Two
- * abstract methods: connect and disconnect are used to act on the
- * underlying bearer code.  Connect is called when we have a NetworkRequest
- * and our score is better than the current handling network's score, while
- * disconnect is used when ConnectivityService requests a disconnect.
+ * A Utility class for handling for communicating between bearer-specific
+ * code and ConnectivityService.
  *
  * A bearer may have more than one NetworkAgent if it can simultaneously
  * support separate networks (IMS / Internet / MMS Apns on cellular, or
- * perhaps connections with different SSID or P2P for Wi-Fi).  The bearer
- * code should pass its NetworkAgents the NetworkRequests each NetworkAgent
- * can handle, demultiplexing for different network types.  The bearer code
- * can also filter out requests it can never handle.
+ * perhaps connections with different SSID or P2P for Wi-Fi).
  *
- * Each NetworkAgent needs to be given a score and NetworkCapabilities for
- * their potential network.  While disconnected, the NetworkAgent will check
- * each time its score changes or a NetworkRequest changes to see if
- * the NetworkAgent can provide a higher scored network for a NetworkRequest
- * that the NetworkAgent's NetworkCapabilties can satisfy.  This condition will
- * trigger a connect request via connect().  After connection, connection data
- * should be given to the NetworkAgent by the bearer, including LinkProperties
- * NetworkCapabilties and NetworkInfo.  After that the NetworkAgent will register
- * with ConnectivityService and forward the data on.
  * @hide
  */
 public abstract class NetworkAgent extends Handler {
-    private final SparseArray<NetworkRequestAndScore> mNetworkRequests = new SparseArray<>();
-    private boolean mConnectionRequested = false;
-
-    private AsyncChannel mAsyncChannel;
+    private volatile AsyncChannel mAsyncChannel;
     private final String LOG_TAG;
     private static final boolean DBG = true;
     private static final boolean VDBG = true;
-    // TODO - this class shouldn't cache data or it runs the risk of getting out of sync
-    // Make the API require each of these when any is updated so we have the data we need,
-    // without caching.
-    private LinkProperties mLinkProperties;
-    private NetworkInfo mNetworkInfo;
-    private NetworkCapabilities mNetworkCapabilities;
-    private int mNetworkScore;
-    private boolean mRegistered = false;
     private final Context mContext;
-    private AtomicBoolean mHasRequests = new AtomicBoolean(false);
-
-    // TODO - add a name member for logging purposes.
-
-    protected final Object mLockObj = new Object();
-
+    private final ArrayList<Message>mPreConnectedQueue = new ArrayList<Message>();
 
     private static final int BASE = Protocol.BASE_NETWORK_AGENT;
 
     /**
-     * Sent by self to queue up a new/modified request.
-     * obj = NetworkRequestAndScore
-     */
-    private static final int CMD_ADD_REQUEST = BASE + 1;
-
-    /**
-     * Sent by self to queue up the removal of a request.
-     * obj = NetworkRequest
-     */
-    private static final int CMD_REMOVE_REQUEST = BASE + 2;
-
-    /**
      * Sent by ConnectivityService to the NetworkAgent to inform it of
      * suspected connectivity problems on its network.  The NetworkAgent
      * should take steps to verify and correct connectivity.
      */
-    public static final int CMD_SUSPECT_BAD = BASE + 3;
+    public static final int CMD_SUSPECT_BAD = BASE;
 
     /**
      * Sent by the NetworkAgent (note the EVENT vs CMD prefix) to
@@ -110,84 +64,63 @@
      * Sent when the NetworkInfo changes, mainly due to change of state.
      * obj = NetworkInfo
      */
-    public static final int EVENT_NETWORK_INFO_CHANGED = BASE + 4;
+    public static final int EVENT_NETWORK_INFO_CHANGED = BASE + 1;
 
     /**
      * Sent by the NetworkAgent to ConnectivityService to pass the current
      * NetworkCapabilties.
      * obj = NetworkCapabilities
      */
-    public static final int EVENT_NETWORK_CAPABILITIES_CHANGED = BASE + 5;
+    public static final int EVENT_NETWORK_CAPABILITIES_CHANGED = BASE + 2;
 
     /**
      * Sent by the NetworkAgent to ConnectivityService to pass the current
      * NetworkProperties.
      * obj = NetworkProperties
      */
-    public static final int EVENT_NETWORK_PROPERTIES_CHANGED = BASE + 6;
+    public static final int EVENT_NETWORK_PROPERTIES_CHANGED = BASE + 3;
 
     /**
      * Sent by the NetworkAgent to ConnectivityService to pass the current
      * network score.
-     * arg1 = network score int
+     * obj = network score Integer
      */
-    public static final int EVENT_NETWORK_SCORE_CHANGED = BASE + 7;
+    public static final int EVENT_NETWORK_SCORE_CHANGED = BASE + 4;
 
-    public NetworkAgent(Looper looper, Context context, String logTag) {
+    public NetworkAgent(Looper looper, Context context, String logTag, NetworkInfo ni,
+            NetworkCapabilities nc, LinkProperties lp, int score) {
         super(looper);
         LOG_TAG = logTag;
         mContext = context;
-    }
-
-    /**
-     * When conditions are right, register with ConnectivityService.
-     * Connditions include having a well defined network and a request
-     * that justifies it.  The NetworkAgent will remain registered until
-     * disconnected.
-     * TODO - this should have all data passed in rather than caching
-     */
-    private void registerSelf() {
-        synchronized(mLockObj) {
-            if (!mRegistered && mConnectionRequested &&
-                    mNetworkInfo != null && mNetworkInfo.isConnected() &&
-                    mNetworkCapabilities != null &&
-                    mLinkProperties != null &&
-                    mNetworkScore != 0) {
-                if (DBG) log("Registering NetworkAgent");
-                mRegistered = true;
-                ConnectivityManager cm = (ConnectivityManager)mContext.getSystemService(
-                        Context.CONNECTIVITY_SERVICE);
-                cm.registerNetworkAgent(new Messenger(this), new NetworkInfo(mNetworkInfo),
-                        new LinkProperties(mLinkProperties),
-                        new NetworkCapabilities(mNetworkCapabilities), mNetworkScore);
-            } else if (DBG && !mRegistered) {
-                String err = "Not registering due to ";
-                if (mConnectionRequested == false) err += "no Connect requested ";
-                if (mNetworkInfo == null) err += "null NetworkInfo ";
-                if (mNetworkInfo != null && mNetworkInfo.isConnected() == false) {
-                    err += "NetworkInfo disconnected ";
-                }
-                if (mLinkProperties == null) err += "null LinkProperties ";
-                if (mNetworkCapabilities == null) err += "null NetworkCapabilities ";
-                if (mNetworkScore == 0) err += "null NetworkScore";
-                log(err);
-            }
+        if (ni == null || nc == null || lp == null) {
+            throw new IllegalArgumentException();
         }
+
+        if (DBG) log("Registering NetworkAgent");
+        ConnectivityManager cm = (ConnectivityManager)mContext.getSystemService(
+                Context.CONNECTIVITY_SERVICE);
+        cm.registerNetworkAgent(new Messenger(this), new NetworkInfo(ni),
+                new LinkProperties(lp), new NetworkCapabilities(nc), score);
     }
 
     @Override
     public void handleMessage(Message msg) {
         switch (msg.what) {
             case AsyncChannel.CMD_CHANNEL_FULL_CONNECTION: {
-                synchronized (mLockObj) {
-                    if (mAsyncChannel != null) {
-                        log("Received new connection while already connected!");
-                    } else {
-                        if (DBG) log("NetworkAgent fully connected");
-                        mAsyncChannel = new AsyncChannel();
-                        mAsyncChannel.connected(null, this, msg.replyTo);
-                        mAsyncChannel.replyToMessage(msg, AsyncChannel.CMD_CHANNEL_FULLY_CONNECTED,
-                                AsyncChannel.STATUS_SUCCESSFUL);
+                if (mAsyncChannel != null) {
+                    log("Received new connection while already connected!");
+                } else {
+                    if (DBG) log("NetworkAgent fully connected");
+                    AsyncChannel ac = new AsyncChannel();
+                    ac.connected(null, this, msg.replyTo);
+                    ac.replyToMessage(msg, AsyncChannel.CMD_CHANNEL_FULLY_CONNECTED,
+                            AsyncChannel.STATUS_SUCCESSFUL);
+                    synchronized (mPreConnectedQueue) {
+                        mAsyncChannel = ac;
+                        for (Message m : mPreConnectedQueue) {
+                            ac.sendMessage(m);
+                        }
+                        mPreConnectedQueue.clear();
                     }
                 }
                 break;
@@ -199,201 +132,69 @@
             }
             case AsyncChannel.CMD_CHANNEL_DISCONNECTED: {
                 if (DBG) log("NetworkAgent channel lost");
-                disconnect();
-                clear();
+                // let the client know CS is done with us.
+                unwanted();
+                synchronized (mPreConnectedQueue) {
+                    mAsyncChannel = null;
+                }
                 break;
             }
             case CMD_SUSPECT_BAD: {
                 log("Unhandled Message " + msg);
                 break;
             }
-            case CMD_ADD_REQUEST: {
-                handleAddRequest(msg);
-                break;
-            }
-            case CMD_REMOVE_REQUEST: {
-                handleRemoveRequest(msg);
-                break;
+        }
+    }
+
+    private void queueOrSendMessage(int what, Object obj) {
+        synchronized (mPreConnectedQueue) {
+            if (mAsyncChannel != null) {
+                mAsyncChannel.sendMessage(what, obj);
+            } else {
+                Message msg = Message.obtain();
+                msg.what = what;
+                msg.obj = obj;
+                mPreConnectedQueue.add(msg);
             }
         }
     }
 
-    private void clear() {
-        synchronized(mLockObj) {
-            mNetworkRequests.clear();
-            mHasRequests.set(false);
-            mConnectionRequested = false;
-            mAsyncChannel = null;
-            mRegistered = false;
-        }
-    }
-
-    private static class NetworkRequestAndScore {
-        NetworkRequest req;
-        int score;
-
-        NetworkRequestAndScore(NetworkRequest networkRequest, int score) {
-            req = networkRequest;
-            this.score = score;
-        }
-    }
-
-    private void handleAddRequest(Message msg) {
-        NetworkRequestAndScore n = (NetworkRequestAndScore)msg.obj;
-        // replaces old request, updating score
-        mNetworkRequests.put(n.req.requestId, n);
-        mHasRequests.set(true);
-        evalScores();
-    }
-
-    private void handleRemoveRequest(Message msg) {
-        NetworkRequest networkRequest = (NetworkRequest)msg.obj;
-
-        if (mNetworkRequests.get(networkRequest.requestId) != null) {
-            mNetworkRequests.remove(networkRequest.requestId);
-            if (mNetworkRequests.size() == 0) mHasRequests.set(false);
-            evalScores();
-        }
-    }
-
-    /**
-     * called to go through our list of requests and see if we're
-     * good enough to try connecting.
-     *
-     * Only does connects - we disconnect when requested via
-     * CMD_CHANNEL_DISCONNECTED, generated by either a loss of connection
-     * between modules (bearer or ConnectivityService dies) or more commonly
-     * when the NetworkInfo reports to ConnectivityService it is disconnected.
-     */
-    private void evalScores() {
-        if (mConnectionRequested) {
-            if (VDBG) log("evalScores - already trying - size=" + mNetworkRequests.size());
-            // already trying
-            return;
-        }
-        if (VDBG) log("evalScores!");
-        for (int i=0; i < mNetworkRequests.size(); i++) {
-            int score = mNetworkRequests.valueAt(i).score;
-            if (VDBG) log(" checking request Min " + score + " vs my score " + mNetworkScore);
-            if (score < mNetworkScore) {
-                // have a request that has a lower scored network servicing it
-                // (or no network) than we could provide, so lets connect!
-                mConnectionRequested = true;
-                connect();
-                return;
-            }
-        }
-    }
-
-    public void addNetworkRequest(NetworkRequest networkRequest, int score) {
-        if (DBG) log("adding NetworkRequest " + networkRequest + " with score " + score);
-        sendMessage(obtainMessage(CMD_ADD_REQUEST,
-                new NetworkRequestAndScore(networkRequest, score)));
-    }
-
-    public void removeNetworkRequest(NetworkRequest networkRequest) {
-        if (DBG) log("removing NetworkRequest " + networkRequest);
-        sendMessage(obtainMessage(CMD_REMOVE_REQUEST, networkRequest));
-    }
-
     /**
      * Called by the bearer code when it has new LinkProperties data.
-     * If we're a registered NetworkAgent, this new data will get forwarded on,
-     * otherwise we store a copy in anticipation of registering.  This call
-     * may also prompt registration if it causes the NetworkAgent to meet
-     * the conditions (fully configured, connected, satisfys a request and
-     * has sufficient score).
      */
     public void sendLinkProperties(LinkProperties linkProperties) {
-        linkProperties = new LinkProperties(linkProperties);
-        synchronized(mLockObj) {
-            mLinkProperties = linkProperties;
-            if (mAsyncChannel != null) {
-                mAsyncChannel.sendMessage(EVENT_NETWORK_PROPERTIES_CHANGED, linkProperties);
-            } else {
-                registerSelf();
-            }
-        }
+        queueOrSendMessage(EVENT_NETWORK_PROPERTIES_CHANGED, new LinkProperties(linkProperties));
     }
 
     /**
      * Called by the bearer code when it has new NetworkInfo data.
-     * If we're a registered NetworkAgent, this new data will get forwarded on,
-     * otherwise we store a copy in anticipation of registering.  This call
-     * may also prompt registration if it causes the NetworkAgent to meet
-     * the conditions (fully configured, connected, satisfys a request and
-     * has sufficient score).
      */
     public void sendNetworkInfo(NetworkInfo networkInfo) {
-        networkInfo = new NetworkInfo(networkInfo);
-        synchronized(mLockObj) {
-            mNetworkInfo = networkInfo;
-            if (mAsyncChannel != null) {
-                mAsyncChannel.sendMessage(EVENT_NETWORK_INFO_CHANGED, networkInfo);
-            } else {
-                registerSelf();
-            }
-        }
+        queueOrSendMessage(EVENT_NETWORK_INFO_CHANGED, new NetworkInfo(networkInfo));
     }
 
     /**
      * Called by the bearer code when it has new NetworkCapabilities data.
-     * If we're a registered NetworkAgent, this new data will get forwarded on,
-     * otherwise we store a copy in anticipation of registering.  This call
-     * may also prompt registration if it causes the NetworkAgent to meet
-     * the conditions (fully configured, connected, satisfys a request and
-     * has sufficient score).
-     * Note that if these capabilities make the network non-useful,
-     * ConnectivityServce will tear this network down.
      */
     public void sendNetworkCapabilities(NetworkCapabilities networkCapabilities) {
-        networkCapabilities = new NetworkCapabilities(networkCapabilities);
-        synchronized(mLockObj) {
-            mNetworkCapabilities = networkCapabilities;
-            if (mAsyncChannel != null) {
-                mAsyncChannel.sendMessage(EVENT_NETWORK_CAPABILITIES_CHANGED, networkCapabilities);
-            } else {
-                registerSelf();
-            }
-        }
-    }
-
-    public NetworkCapabilities getNetworkCapabilities() {
-        synchronized(mLockObj) {
-            return new NetworkCapabilities(mNetworkCapabilities);
-        }
+        queueOrSendMessage(EVENT_NETWORK_CAPABILITIES_CHANGED,
+                new NetworkCapabilities(networkCapabilities));
     }
 
     /**
      * Called by the bearer code when it has a new score for this network.
-     * If we're a registered NetworkAgent, this new data will get forwarded on,
-     * otherwise we store a copy.
      */
-    public synchronized void sendNetworkScore(int score) {
-        synchronized(mLockObj) {
-            mNetworkScore = score;
-            evalScores();
-            if (mAsyncChannel != null) {
-                mAsyncChannel.sendMessage(EVENT_NETWORK_SCORE_CHANGED, mNetworkScore);
-            } else {
-                registerSelf();
-            }
-        }
+    public void sendNetworkScore(int score) {
+        queueOrSendMessage(EVENT_NETWORK_SCORE_CHANGED, new Integer(score));
     }
 
-    public boolean hasRequests() {
-        return mHasRequests.get();
-    }
-
-    public boolean isConnectionRequested() {
-        synchronized(mLockObj) {
-            return mConnectionRequested;
-        }
-    }
-
-
-    abstract protected void connect();
-    abstract protected void disconnect();
+    /**
+     * Called when ConnectivityService has indicated they no longer want this network.
+     * The parent factory should (previously) have received indication of the change
+     * as well, either canceling NetworkRequests or altering their score such that this
+     * network won't be immediately requested again.
+     */
+    abstract protected void unwanted();
 
     protected void log(String s) {
         Log.d(LOG_TAG, "NetworkAgent: " + s);
diff --git a/core/java/android/net/NetworkFactory.java b/core/java/android/net/NetworkFactory.java
new file mode 100644
index 0000000..a20e8e7
--- /dev/null
+++ b/core/java/android/net/NetworkFactory.java
@@ -0,0 +1,275 @@
+/*
+ * 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.net;
+
+import android.content.Context;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.Message;
+import android.os.Messenger;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.util.Log;
+import android.util.SparseArray;
+
+import com.android.internal.util.AsyncChannel;
+import com.android.internal.util.Protocol;
+
+/**
+ * A NetworkFactory is an entity that creates NetworkAgent objects.
+ * The bearers register with ConnectivityService using {@link #register} and
+ * their factory will start receiving scored NetworkRequests.  NetworkRequests
+ * can be filtered 3 ways: by NetworkCapabilities, by score and more complexly by
+ * overridden function.  All of these can be dynamic - changing NetworkCapabilities
+ * or score forces re-evaluation of all current requests.
+ *
+ * If any requests pass the filter some overrideable functions will be called.
+ * If the bearer only cares about very simple start/stopNetwork callbacks, those
+ * functions can be overridden.  If the bearer needs more interaction, it can
+ * override addNetworkRequest and removeNetworkRequest which will give it each
+ * request that passes their current filters.
+ * @hide
+ **/
+public class NetworkFactory extends Handler {
+    private static final boolean DBG = true;
+
+    private static final int BASE = Protocol.BASE_NETWORK_FACTORY;
+    /**
+     * Pass a network request to the bearer.  If the bearer believes it can
+     * satisfy the request it should connect to the network and create a
+     * NetworkAgent.  Once the NetworkAgent is fully functional it will
+     * register itself with ConnectivityService using registerNetworkAgent.
+     * If the bearer cannot immediately satisfy the request (no network,
+     * user disabled the radio, lower-scored network) it should remember
+     * any NetworkRequests it may be able to satisfy in the future.  It may
+     * disregard any that it will never be able to service, for example
+     * those requiring a different bearer.
+     * msg.obj = NetworkRequest
+     * msg.arg1 = score - the score of the any network currently satisfying this
+     *            request.  If this bearer knows in advance it cannot
+     *            exceed this score it should not try to connect, holding the request
+     *            for the future.
+     *            Note that subsequent events may give a different (lower
+     *            or higher) score for this request, transmitted to each
+     *            NetworkFactory through additional CMD_REQUEST_NETWORK msgs
+     *            with the same NetworkRequest but an updated score.
+     *            Also, network conditions may change for this bearer
+     *            allowing for a better score in the future.
+     */
+    public static final int CMD_REQUEST_NETWORK = BASE;
+
+    /**
+     * Cancel a network request
+     * msg.obj = NetworkRequest
+     */
+    public static final int CMD_CANCEL_REQUEST = BASE + 1;
+
+    /**
+     * Internally used to set our best-guess score.
+     * msg.arg1 = new score
+     */
+    private static final int CMD_SET_SCORE = BASE + 2;
+
+    /**
+     * Internally used to set our current filter for coarse bandwidth changes with
+     * technology changes.
+     * msg.obj = new filter
+     */
+    private static final int CMD_SET_FILTER = BASE + 3;
+
+    private final Context mContext;
+    private final String LOG_TAG;
+
+    private final SparseArray<NetworkRequestInfo> mNetworkRequests =
+            new SparseArray<NetworkRequestInfo>();
+
+    private int mScore;
+    private NetworkCapabilities mCapabilityFilter;
+
+    private int mRefCount = 0;
+    private Messenger mMessenger = null;
+
+    public NetworkFactory(Looper looper, Context context, String logTag,
+            NetworkCapabilities filter) {
+        super(looper);
+        LOG_TAG = logTag;
+        mContext = context;
+        mCapabilityFilter = filter;
+    }
+
+    public void register() {
+        if (DBG) log("Registering NetworkFactory");
+        if (mMessenger == null) {
+            mMessenger = new Messenger(this);
+            ConnectivityManager.from(mContext).registerNetworkFactory(mMessenger, LOG_TAG);
+        }
+    }
+
+    public void unregister() {
+        if (DBG) log("Unregistering NetworkFactory");
+        if (mMessenger != null) {
+            ConnectivityManager.from(mContext).unregisterNetworkFactory(mMessenger);
+            mMessenger = null;
+        }
+    }
+
+    @Override
+    public void handleMessage(Message msg) {
+        switch (msg.what) {
+            case CMD_REQUEST_NETWORK: {
+                handleAddRequest((NetworkRequest)msg.obj, msg.arg1);
+                break;
+            }
+            case CMD_CANCEL_REQUEST: {
+                handleRemoveRequest((NetworkRequest) msg.obj);
+                break;
+            }
+            case CMD_SET_SCORE: {
+                handleSetScore(msg.arg1);
+                break;
+            }
+            case CMD_SET_FILTER: {
+                handleSetFilter((NetworkCapabilities) msg.obj);
+                break;
+            }
+        }
+    }
+
+    private class NetworkRequestInfo {
+        public final NetworkRequest request;
+        public int score;
+        public boolean requested; // do we have a request outstanding, limited by score
+
+        public NetworkRequestInfo(NetworkRequest request, int score) {
+            this.request = request;
+            this.score = score;
+            this.requested = false;
+        }
+    }
+
+    private void handleAddRequest(NetworkRequest request, int score) {
+        NetworkRequestInfo n = mNetworkRequests.get(request.requestId);
+        if (n == null) {
+            n = new NetworkRequestInfo(request, score);
+            mNetworkRequests.put(n.request.requestId, n);
+        } else {
+            n.score = score;
+        }
+        if (DBG) log("got request " + request + " with score " + score);
+        if (DBG) log("  my score=" + mScore + ", my filter=" + mCapabilityFilter);
+
+        evalRequest(n);
+    }
+
+    private void handleRemoveRequest(NetworkRequest request) {
+        NetworkRequestInfo n = mNetworkRequests.get(request.requestId);
+        if (n != null && n.requested) {
+            mNetworkRequests.remove(request.requestId);
+            releaseNetworkFor(n.request);
+        }
+    }
+
+    private void handleSetScore(int score) {
+        mScore = score;
+        evalRequests();
+    }
+
+    private void handleSetFilter(NetworkCapabilities netCap) {
+        mCapabilityFilter = netCap;
+        evalRequests();
+    }
+
+    /**
+     * Overridable function to provide complex filtering.
+     * Called for every request every time a new NetworkRequest is seen
+     * and whenever the filterScore or filterNetworkCapabilities change.
+     *
+     * acceptRequest can be overriden to provide complex filter behavior
+     * for the incoming requests
+     *
+     * For output, this class will call {@link #needNetworkFor} and
+     * {@link #releaseNetworkFor} for every request that passes the filters.
+     * If you don't need to see every request, you can leave the base
+     * implementations of those two functions and instead override
+     * {@link #startNetwork} and {@link #stopNetwork}.
+     *
+     * If you want to see every score fluctuation on every request, set
+     * your score filter to a very high number and watch {@link #needNetworkFor}.
+     *
+     * @return {@code true} to accept the request.
+     */
+    public boolean acceptRequest(NetworkRequest request, int score) {
+        return true;
+    }
+
+    private void evalRequest(NetworkRequestInfo n) {
+        if (n.requested == false && n.score < mScore &&
+                n.request.networkCapabilities.satisfiedByNetworkCapabilities(
+                mCapabilityFilter) && acceptRequest(n.request, n.score)) {
+            needNetworkFor(n.request, n.score);
+            n.requested = true;
+        } else if (n.requested == true &&
+                (n.score > mScore || n.request.networkCapabilities.satisfiedByNetworkCapabilities(
+                mCapabilityFilter) == false || acceptRequest(n.request, n.score) == false)) {
+            releaseNetworkFor(n.request);
+            n.requested = false;
+        }
+    }
+
+    private void evalRequests() {
+        for (int i = 0; i < mNetworkRequests.size(); i++) {
+            NetworkRequestInfo n = mNetworkRequests.valueAt(i);
+
+            evalRequest(n);
+        }
+    }
+
+    // override to do simple mode (request independent)
+    protected void startNetwork() { }
+    protected void stopNetwork() { }
+
+    // override to do fancier stuff
+    protected void needNetworkFor(NetworkRequest networkRequest, int score) {
+        if (++mRefCount == 1) startNetwork();
+    }
+
+    protected void releaseNetworkFor(NetworkRequest networkRequest) {
+        if (--mRefCount == 0) stopNetwork();
+    }
+
+
+    public void addNetworkRequest(NetworkRequest networkRequest, int score) {
+        sendMessage(obtainMessage(CMD_REQUEST_NETWORK,
+                new NetworkRequestInfo(networkRequest, score)));
+    }
+
+    public void removeNetworkRequest(NetworkRequest networkRequest) {
+        sendMessage(obtainMessage(CMD_CANCEL_REQUEST, networkRequest));
+    }
+
+    public void setScoreFilter(int score) {
+        sendMessage(obtainMessage(CMD_SET_SCORE, score, 0));
+    }
+
+    public void setCapabilityFilter(NetworkCapabilities netCap) {
+        sendMessage(obtainMessage(CMD_SET_FILTER, new NetworkCapabilities(netCap)));
+    }
+
+    protected void log(String s) {
+        Log.d(LOG_TAG, s);
+    }
+}
diff --git a/core/java/android/net/NetworkInfo.java b/core/java/android/net/NetworkInfo.java
index 9e656ee..d279412 100644
--- a/core/java/android/net/NetworkInfo.java
+++ b/core/java/android/net/NetworkInfo.java
@@ -188,6 +188,15 @@
     }
 
     /**
+     * @hide
+     */
+    public void setType(int type) {
+        synchronized (this) {
+            mNetworkType = type;
+        }
+    }
+
+    /**
      * Return a network-type-specific integer describing the subtype
      * of the network.
      * @return the network subtype
@@ -198,7 +207,10 @@
         }
     }
 
-    void setSubtype(int subtype, String subtypeName) {
+    /**
+     * @hide
+     */
+    public void setSubtype(int subtype, String subtypeName) {
         synchronized (this) {
             mSubtype = subtype;
             mSubtypeName = subtypeName;
diff --git a/core/java/android/net/NetworkRequest.java b/core/java/android/net/NetworkRequest.java
index 480cb05..47377e9 100644
--- a/core/java/android/net/NetworkRequest.java
+++ b/core/java/android/net/NetworkRequest.java
@@ -47,19 +47,19 @@
     public final int requestId;
 
     /**
-     * Set for legacy requests and the default.
+     * Set for legacy requests and the default.  Set to TYPE_NONE for none.
      * Causes CONNECTIVITY_ACTION broadcasts to be sent.
      * @hide
      */
-    public final boolean needsBroadcasts;
+    public final int legacyType;
 
     /**
      * @hide
      */
-    public NetworkRequest(NetworkCapabilities nc, boolean needsBroadcasts, int rId) {
+    public NetworkRequest(NetworkCapabilities nc, int legacyType, int rId) {
         requestId = rId;
         networkCapabilities = nc;
-        this.needsBroadcasts = needsBroadcasts;
+        this.legacyType = legacyType;
     }
 
     /**
@@ -68,7 +68,7 @@
     public NetworkRequest(NetworkRequest that) {
         networkCapabilities = new NetworkCapabilities(that.networkCapabilities);
         requestId = that.requestId;
-        needsBroadcasts = that.needsBroadcasts;
+        this.legacyType = that.legacyType;
     }
 
     // implement the Parcelable interface
@@ -77,16 +77,16 @@
     }
     public void writeToParcel(Parcel dest, int flags) {
         dest.writeParcelable(networkCapabilities, flags);
-        dest.writeInt(needsBroadcasts ? 1 : 0);
+        dest.writeInt(legacyType);
         dest.writeInt(requestId);
     }
     public static final Creator<NetworkRequest> CREATOR =
         new Creator<NetworkRequest>() {
             public NetworkRequest createFromParcel(Parcel in) {
                 NetworkCapabilities nc = (NetworkCapabilities)in.readParcelable(null);
-                boolean needsBroadcasts = (in.readInt() == 1);
+                int legacyType = in.readInt();
                 int requestId = in.readInt();
-                NetworkRequest result = new NetworkRequest(nc, needsBroadcasts, requestId);
+                NetworkRequest result = new NetworkRequest(nc, legacyType, requestId);
                 return result;
             }
             public NetworkRequest[] newArray(int size) {
@@ -95,14 +95,14 @@
         };
 
     public String toString() {
-        return "NetworkRequest [ id=" + requestId + ", needsBroadcasts=" + needsBroadcasts +
+        return "NetworkRequest [ id=" + requestId + ", legacyType=" + legacyType +
                 ", " + networkCapabilities.toString() + " ]";
     }
 
     public boolean equals(Object obj) {
         if (obj instanceof NetworkRequest == false) return false;
         NetworkRequest that = (NetworkRequest)obj;
-        return (that.needsBroadcasts == this.needsBroadcasts &&
+        return (that.legacyType == this.legacyType &&
                 that.requestId == this.requestId &&
                 ((that.networkCapabilities == null && this.networkCapabilities == null) ||
                  (that.networkCapabilities != null &&
@@ -110,7 +110,7 @@
     }
 
     public int hashCode() {
-        return requestId + (needsBroadcasts ? 1013 : 2026) +
+        return requestId + (legacyType * 1013) +
                 (networkCapabilities.hashCode() * 1051);
     }
 }
diff --git a/core/java/android/nfc/cardemulation/AidGroup.java b/core/java/android/nfc/cardemulation/AidGroup.java
index b0449224..cabda5d 100644
--- a/core/java/android/nfc/cardemulation/AidGroup.java
+++ b/core/java/android/nfc/cardemulation/AidGroup.java
@@ -2,6 +2,7 @@
 
 import java.io.IOException;
 import java.util.ArrayList;
+import java.util.List;
 
 import org.xmlpull.v1.XmlPullParser;
 import org.xmlpull.v1.XmlPullParserException;
@@ -21,6 +22,8 @@
  * <p>The format of AIDs is defined in the ISO/IEC 7816-4 specification. This class
  * requires the AIDs to be input as a hexadecimal string, with an even amount of
  * hexadecimal characters, e.g. "F014811481".
+ *
+ * @hide
  */
 public final class AidGroup implements Parcelable {
     /**
@@ -30,7 +33,7 @@
 
     static final String TAG = "AidGroup";
 
-    final ArrayList<String> aids;
+    final List<String> aids;
     final String category;
     final String description;
 
@@ -40,7 +43,7 @@
      * @param aids The list of AIDs present in the group
      * @param category The category of this group, e.g. {@link CardEmulation#CATEGORY_PAYMENT}
      */
-    public AidGroup(ArrayList<String> aids, String category) {
+    public AidGroup(List<String> aids, String category) {
         if (aids == null || aids.size() == 0) {
             throw new IllegalArgumentException("No AIDS in AID group.");
         }
@@ -72,7 +75,7 @@
     /**
      * @return the list of  AIDs in this group
      */
-    public ArrayList<String> getAids() {
+    public List<String> getAids() {
         return aids;
     }
 
@@ -121,11 +124,6 @@
         }
     };
 
-    /**
-     * @hide
-     * Note: description is not serialized, since it's not localized
-     * and resource identifiers don't make sense to persist.
-     */
     static public AidGroup createFromXml(XmlPullParser parser) throws XmlPullParserException, IOException {
         String category = parser.getAttributeValue(null, "category");
         ArrayList<String> aids = new ArrayList<String>();
@@ -152,9 +150,6 @@
         }
     }
 
-    /**
-     * @hide
-     */
     public void writeAsXml(XmlSerializer out) throws IOException {
         out.attribute(null, "category", category);
         for (String aid : aids) {
diff --git a/core/java/android/nfc/cardemulation/CardEmulation.java b/core/java/android/nfc/cardemulation/CardEmulation.java
index e24a22a..4b9e890 100644
--- a/core/java/android/nfc/cardemulation/CardEmulation.java
+++ b/core/java/android/nfc/cardemulation/CardEmulation.java
@@ -303,12 +303,13 @@
     }
 
     /**
-     * Registers a group of AIDs for the specified service.
+     * Registers a list of AIDs for a specific category for the
+     * specified service.
      *
-     * <p>If an AID group for that category was previously
+     * <p>If a list of AIDs for that category was previously
      * registered for this service (either statically
      * through the manifest, or dynamically by using this API),
-     * that AID group will be replaced with this one.
+     * that list of AIDs will be replaced with this one.
      *
      * <p>Note that you can only register AIDs for a service that
      * is running under the same UID as the caller of this API. Typically
@@ -317,10 +318,13 @@
      * be shared between packages using shared UIDs.
      *
      * @param service The component name of the service
-     * @param aidGroup The group of AIDs to be registered
+     * @param category The category of AIDs to be registered
+     * @param aids A list containing the AIDs to be registered
      * @return whether the registration was successful.
      */
-    public boolean registerAidGroupForService(ComponentName service, AidGroup aidGroup) {
+    public boolean registerAidsForService(ComponentName service, String category,
+            List<String> aids) {
+        AidGroup aidGroup = new AidGroup(aids, category);
         try {
             return sService.registerAidGroupForService(UserHandle.myUserId(), service, aidGroup);
         } catch (RemoteException e) {
@@ -341,21 +345,24 @@
     }
 
     /**
-     * Retrieves the currently registered AID group for the specified
+     * Retrieves the currently registered AIDs for the specified
      * category for a service.
      *
-     * <p>Note that this will only return AID groups that were dynamically
-     * registered using {@link #registerAidGroupForService(ComponentName, AidGroup)}
-     * method. It will *not* return AID groups that were statically registered
+     * <p>Note that this will only return AIDs that were dynamically
+     * registered using {@link #registerAidsForService(ComponentName, String, List)}
+     * method. It will *not* return AIDs that were statically registered
      * in the manifest.
      *
      * @param service The component name of the service
-     * @param category The category of the AID group to be returned, e.g. {@link #CATEGORY_PAYMENT}
-     * @return The AID group, or null if it couldn't be found
+     * @param category The category for which the AIDs were registered,
+     *                 e.g. {@link #CATEGORY_PAYMENT}
+     * @return The list of AIDs registered for this category, or null if it couldn't be found.
      */
-    public AidGroup getAidGroupForService(ComponentName service, String category) {
+    public List<String> getAidsForService(ComponentName service, String category) {
         try {
-            return sService.getAidGroupForService(UserHandle.myUserId(), service, category);
+            AidGroup group =  sService.getAidGroupForService(UserHandle.myUserId(), service,
+                    category);
+            return (group != null ? group.getAids() : null);
         } catch (RemoteException e) {
             recoverService();
             if (sService == null) {
@@ -363,7 +370,9 @@
                 return null;
             }
             try {
-                return sService.getAidGroupForService(UserHandle.myUserId(), service, category);
+                AidGroup group = sService.getAidGroupForService(UserHandle.myUserId(), service,
+                        category);
+                return (group != null ? group.getAids() : null);
             } catch (RemoteException ee) {
                 Log.e(TAG, "Failed to recover CardEmulationService.");
                 return null;
@@ -372,21 +381,21 @@
     }
 
     /**
-     * Removes a registered AID group for the specified category for the
+     * Removes a previously registered list of AIDs for the specified category for the
      * service provided.
      *
-     * <p>Note that this will only remove AID groups that were dynamically
-     * registered using the {@link #registerAidGroupForService(ComponentName, AidGroup)}
-     * method. It will *not* remove AID groups that were statically registered in
-     * the manifest. If a dynamically registered AID group is removed using
+     * <p>Note that this will only remove AIDs that were dynamically
+     * registered using the {@link #registerAidsForService(ComponentName, String, List)}
+     * method. It will *not* remove AIDs that were statically registered in
+     * the manifest. If dynamically registered AIDs are removed using
      * this method, and a statically registered AID group for the same category
      * exists in the manifest, the static AID group will become active again.
      *
      * @param service The component name of the service
-     * @param category The category of the AID group to be removed, e.g. {@link #CATEGORY_PAYMENT}
+     * @param category The category of the AIDs to be removed, e.g. {@link #CATEGORY_PAYMENT}
      * @return whether the group was successfully removed.
      */
-    public boolean removeAidGroupForService(ComponentName service, String category) {
+    public boolean removeAidsForService(ComponentName service, String category) {
         try {
             return sService.removeAidGroupForService(UserHandle.myUserId(), service, category);
         } catch (RemoteException e) {
diff --git a/core/java/android/os/CommonBundle.java b/core/java/android/os/BaseBundle.java
similarity index 93%
rename from core/java/android/os/CommonBundle.java
rename to core/java/android/os/BaseBundle.java
index c1b202c..c2a45ba 100644
--- a/core/java/android/os/CommonBundle.java
+++ b/core/java/android/os/BaseBundle.java
@@ -27,7 +27,7 @@
 /**
  * A mapping from String values to various types.
  */
-abstract class CommonBundle implements Parcelable, Cloneable {
+public class BaseBundle {
     private static final String TAG = "Bundle";
     static final boolean DEBUG = false;
 
@@ -63,7 +63,7 @@
      * inside of the Bundle.
      * @param capacity Initial size of the ArrayMap.
      */
-    CommonBundle(ClassLoader loader, int capacity) {
+    BaseBundle(ClassLoader loader, int capacity) {
         mMap = capacity > 0 ?
                 new ArrayMap<String, Object>(capacity) : new ArrayMap<String, Object>();
         mClassLoader = loader == null ? getClass().getClassLoader() : loader;
@@ -72,7 +72,7 @@
     /**
      * Constructs a new, empty Bundle.
      */
-    CommonBundle() {
+    BaseBundle() {
         this((ClassLoader) null, 0);
     }
 
@@ -82,11 +82,11 @@
      *
      * @param parcelledData a Parcel containing a Bundle
      */
-    CommonBundle(Parcel parcelledData) {
+    BaseBundle(Parcel parcelledData) {
         readFromParcelInner(parcelledData);
     }
 
-    CommonBundle(Parcel parcelledData, int length) {
+    BaseBundle(Parcel parcelledData, int length) {
         readFromParcelInner(parcelledData, length);
     }
 
@@ -97,7 +97,7 @@
      * @param loader An explicit ClassLoader to use when instantiating objects
      * inside of the Bundle.
      */
-    CommonBundle(ClassLoader loader) {
+    BaseBundle(ClassLoader loader) {
         this(loader, 0);
     }
 
@@ -107,7 +107,7 @@
      *
      * @param capacity the initial capacity of the Bundle
      */
-    CommonBundle(int capacity) {
+    BaseBundle(int capacity) {
         this((ClassLoader) null, capacity);
     }
 
@@ -117,7 +117,7 @@
      *
      * @param b a Bundle to be copied.
      */
-    CommonBundle(CommonBundle b) {
+    BaseBundle(BaseBundle b) {
         if (b.mParcelledData != null) {
             if (b.mParcelledData == EMPTY_PARCEL) {
                 mParcelledData = EMPTY_PARCEL;
@@ -148,7 +148,7 @@
      *
      * @hide
      */
-    String getPairValue() {
+    public String getPairValue() {
         unparcel();
         int size = mMap.size();
         if (size > 1) {
@@ -228,7 +228,7 @@
     /**
      * @hide
      */
-    boolean isParcelled() {
+    public boolean isParcelled() {
         return mParcelledData != null;
     }
 
@@ -237,7 +237,7 @@
      *
      * @return the number of mappings as an int.
      */
-    int size() {
+    public int size() {
         unparcel();
         return mMap.size();
     }
@@ -245,7 +245,7 @@
     /**
      * Returns true if the mapping of this Bundle is empty, false otherwise.
      */
-    boolean isEmpty() {
+    public boolean isEmpty() {
         unparcel();
         return mMap.isEmpty();
     }
@@ -253,7 +253,7 @@
     /**
      * Removes all elements from the mapping of this Bundle.
      */
-    void clear() {
+    public void clear() {
         unparcel();
         mMap.clear();
     }
@@ -265,7 +265,7 @@
      * @param key a String key
      * @return true if the key is part of the mapping, false otherwise
      */
-    boolean containsKey(String key) {
+    public boolean containsKey(String key) {
         unparcel();
         return mMap.containsKey(key);
     }
@@ -276,7 +276,7 @@
      * @param key a String key
      * @return an Object, or null
      */
-    Object get(String key) {
+    public Object get(String key) {
         unparcel();
         return mMap.get(key);
     }
@@ -286,24 +286,24 @@
      *
      * @param key a String key
      */
-    void remove(String key) {
+    public void remove(String key) {
         unparcel();
         mMap.remove(key);
     }
 
     /**
-     * Inserts all mappings from the given PersistableBundle into this CommonBundle.
+     * Inserts all mappings from the given PersistableBundle into this BaseBundle.
      *
      * @param bundle a PersistableBundle
      */
-    void putAll(PersistableBundle bundle) {
+    public void putAll(PersistableBundle bundle) {
         unparcel();
         bundle.unparcel();
         mMap.putAll(bundle.mMap);
     }
 
     /**
-     * Inserts all mappings from the given Map into this CommonBundle.
+     * Inserts all mappings from the given Map into this BaseBundle.
      *
      * @param map a Map
      */
@@ -317,7 +317,7 @@
      *
      * @return a Set of String keys
      */
-    Set<String> keySet() {
+    public Set<String> keySet() {
         unparcel();
         return mMap.keySet();
     }
@@ -377,7 +377,7 @@
      * @param key a String, or null
      * @param value an int, or null
      */
-    void putInt(String key, int value) {
+    public void putInt(String key, int value) {
         unparcel();
         mMap.put(key, value);
     }
@@ -389,7 +389,7 @@
      * @param key a String, or null
      * @param value a long
      */
-    void putLong(String key, long value) {
+    public void putLong(String key, long value) {
         unparcel();
         mMap.put(key, value);
     }
@@ -413,7 +413,7 @@
      * @param key a String, or null
      * @param value a double
      */
-    void putDouble(String key, double value) {
+    public void putDouble(String key, double value) {
         unparcel();
         mMap.put(key, value);
     }
@@ -425,7 +425,7 @@
      * @param key a String, or null
      * @param value a String, or null
      */
-    void putString(String key, String value) {
+    public void putString(String key, String value) {
         unparcel();
         mMap.put(key, value);
     }
@@ -545,7 +545,7 @@
      * @param key a String, or null
      * @param value an int array object, or null
      */
-    void putIntArray(String key, int[] value) {
+    public void putIntArray(String key, int[] value) {
         unparcel();
         mMap.put(key, value);
     }
@@ -557,7 +557,7 @@
      * @param key a String, or null
      * @param value a long array object, or null
      */
-    void putLongArray(String key, long[] value) {
+    public void putLongArray(String key, long[] value) {
         unparcel();
         mMap.put(key, value);
     }
@@ -581,7 +581,7 @@
      * @param key a String, or null
      * @param value a double array object, or null
      */
-    void putDoubleArray(String key, double[] value) {
+    public void putDoubleArray(String key, double[] value) {
         unparcel();
         mMap.put(key, value);
     }
@@ -593,7 +593,7 @@
      * @param key a String, or null
      * @param value a String array object, or null
      */
-    void putStringArray(String key, String[] value) {
+    public void putStringArray(String key, String[] value) {
         unparcel();
         mMap.put(key, value);
     }
@@ -611,18 +611,6 @@
     }
 
     /**
-     * Inserts a PersistableBundle value into the mapping of this Bundle, replacing
-     * any existing value for the given key.  Either key or value may be null.
-     *
-     * @param key a String, or null
-     * @param value a Bundle object, or null
-     */
-    void putPersistableBundle(String key, PersistableBundle value) {
-        unparcel();
-        mMap.put(key, value);
-    }
-
-    /**
      * Returns the value associated with the given key, or false if
      * no mapping of the desired type exists for the given key.
      *
@@ -789,7 +777,7 @@
      * @param key a String
      * @return an int value
      */
-    int getInt(String key) {
+    public int getInt(String key) {
         unparcel();
         return getInt(key, 0);
     }
@@ -802,7 +790,7 @@
      * @param defaultValue Value to return if key does not exist
      * @return an int value
      */
-    int getInt(String key, int defaultValue) {
+   public int getInt(String key, int defaultValue) {
         unparcel();
         Object o = mMap.get(key);
         if (o == null) {
@@ -823,7 +811,7 @@
      * @param key a String
      * @return a long value
      */
-    long getLong(String key) {
+    public long getLong(String key) {
         unparcel();
         return getLong(key, 0L);
     }
@@ -836,7 +824,7 @@
      * @param defaultValue Value to return if key does not exist
      * @return a long value
      */
-    long getLong(String key, long defaultValue) {
+    public long getLong(String key, long defaultValue) {
         unparcel();
         Object o = mMap.get(key);
         if (o == null) {
@@ -891,7 +879,7 @@
      * @param key a String
      * @return a double value
      */
-    double getDouble(String key) {
+    public double getDouble(String key) {
         unparcel();
         return getDouble(key, 0.0);
     }
@@ -904,7 +892,7 @@
      * @param defaultValue Value to return if key does not exist
      * @return a double value
      */
-    double getDouble(String key, double defaultValue) {
+    public double getDouble(String key, double defaultValue) {
         unparcel();
         Object o = mMap.get(key);
         if (o == null) {
@@ -926,7 +914,7 @@
      * @param key a String, or null
      * @return a String value, or null
      */
-    String getString(String key) {
+    public String getString(String key) {
         unparcel();
         final Object o = mMap.get(key);
         try {
@@ -946,7 +934,7 @@
      * @return the String value associated with the given key, or defaultValue
      *     if no valid String object is currently mapped to that key.
      */
-    String getString(String key, String defaultValue) {
+    public String getString(String key, String defaultValue) {
         final String s = getString(key);
         return (s == null) ? defaultValue : s;
     }
@@ -990,28 +978,6 @@
      * value is explicitly associated with the key.
      *
      * @param key a String, or null
-     * @return a Bundle value, or null
-     */
-    PersistableBundle getPersistableBundle(String key) {
-        unparcel();
-        Object o = mMap.get(key);
-        if (o == null) {
-            return null;
-        }
-        try {
-            return (PersistableBundle) o;
-        } catch (ClassCastException e) {
-            typeWarning(key, o, "Bundle", e);
-            return null;
-        }
-    }
-
-    /**
-     * Returns the value associated with the given key, or null if
-     * no mapping of the desired type exists for the given key or a null
-     * value is explicitly associated with the key.
-     *
-     * @param key a String, or null
      * @return a Serializable value, or null
      */
     Serializable getSerializable(String key) {
@@ -1190,7 +1156,7 @@
      * @param key a String, or null
      * @return an int[] value, or null
      */
-    int[] getIntArray(String key) {
+    public int[] getIntArray(String key) {
         unparcel();
         Object o = mMap.get(key);
         if (o == null) {
@@ -1212,7 +1178,7 @@
      * @param key a String, or null
      * @return a long[] value, or null
      */
-    long[] getLongArray(String key) {
+    public long[] getLongArray(String key) {
         unparcel();
         Object o = mMap.get(key);
         if (o == null) {
@@ -1256,7 +1222,7 @@
      * @param key a String, or null
      * @return a double[] value, or null
      */
-    double[] getDoubleArray(String key) {
+    public double[] getDoubleArray(String key) {
         unparcel();
         Object o = mMap.get(key);
         if (o == null) {
@@ -1278,7 +1244,7 @@
      * @param key a String, or null
      * @return a String[] value, or null
      */
-    String[] getStringArray(String key) {
+    public String[] getStringArray(String key) {
         unparcel();
         Object o = mMap.get(key);
         if (o == null) {
diff --git a/core/java/android/os/BatteryStats.java b/core/java/android/os/BatteryStats.java
index d3c6fd5..e627d49 100644
--- a/core/java/android/os/BatteryStats.java
+++ b/core/java/android/os/BatteryStats.java
@@ -931,6 +931,14 @@
         }
     }
 
+    /**
+     * Don't allow any more batching in to the current history event.  This
+     * is called when printing partial histories, so to ensure that the next
+     * history event will go in to a new batch after what was printed in the
+     * last partial history.
+     */
+    public abstract void commitCurrentHistoryBatchLocked();
+
     public abstract int getHistoryTotalSize();
 
     public abstract int getHistoryUsedSize();
@@ -3303,7 +3311,8 @@
             if (rec.time >= histStart) {
                 if (histStart >= 0 && !printed) {
                     if (rec.cmd == HistoryItem.CMD_CURRENT_TIME
-                            || rec.cmd == HistoryItem.CMD_RESET) {
+                            || rec.cmd == HistoryItem.CMD_RESET
+                            || rec.cmd == HistoryItem.CMD_START) {
                         printed = true;
                         hprinter.printNextItem(pw, rec, baseTime, checkin,
                                 (flags&DUMP_VERBOSE) != 0);
@@ -3365,6 +3374,7 @@
             }
         }
         if (histStart >= 0) {
+            commitCurrentHistoryBatchLocked();
             pw.print(checkin ? "NEXT: " : "  NEXT: "); pw.println(lastTime+1);
         }
     }
diff --git a/core/java/android/os/Bundle.java b/core/java/android/os/Bundle.java
index c85e418..e42c3fe 100644
--- a/core/java/android/os/Bundle.java
+++ b/core/java/android/os/Bundle.java
@@ -28,14 +28,14 @@
  * A mapping from String values to various Parcelable types.
  *
  */
-public final class Bundle extends CommonBundle {
+public final class Bundle extends BaseBundle implements Cloneable, Parcelable {
     public static final Bundle EMPTY;
     static final Parcel EMPTY_PARCEL;
 
     static {
         EMPTY = new Bundle();
         EMPTY.mMap = ArrayMap.EMPTY;
-        EMPTY_PARCEL = CommonBundle.EMPTY_PARCEL;
+        EMPTY_PARCEL = BaseBundle.EMPTY_PARCEL;
     }
 
     private boolean mHasFds = false;
@@ -125,14 +125,6 @@
     }
 
     /**
-     * @hide
-     */
-    @Override
-    public String getPairValue() {
-        return super.getPairValue();
-    }
-
-    /**
      * Changes the ClassLoader this Bundle uses when instantiating objects.
      *
      * @param loader An explicit ClassLoader to use when instantiating objects
@@ -168,32 +160,6 @@
     }
 
     /**
-     * @hide
-     */
-    @Override
-    public boolean isParcelled() {
-        return super.isParcelled();
-    }
-
-    /**
-     * Returns the number of mappings contained in this Bundle.
-     *
-     * @return the number of mappings as an int.
-     */
-    @Override
-    public int size() {
-        return super.size();
-    }
-
-    /**
-     * Returns true if the mapping of this Bundle is empty, false otherwise.
-     */
-    @Override
-    public boolean isEmpty() {
-        return super.isEmpty();
-    }
-
-    /**
      * Removes all elements from the mapping of this Bundle.
      */
     @Override
@@ -205,39 +171,6 @@
     }
 
     /**
-     * Returns true if the given key is contained in the mapping
-     * of this Bundle.
-     *
-     * @param key a String key
-     * @return true if the key is part of the mapping, false otherwise
-     */
-    @Override
-    public boolean containsKey(String key) {
-        return super.containsKey(key);
-    }
-
-    /**
-     * Returns the entry with the given key as an object.
-     *
-     * @param key a String key
-     * @return an Object, or null
-     */
-    @Override
-    public Object get(String key) {
-        return super.get(key);
-    }
-
-    /**
-     * Removes any entry with the given key from the mapping of this Bundle.
-     *
-     * @param key a String key
-     */
-    @Override
-    public void remove(String key) {
-        super.remove(key);
-    }
-
-    /**
      * Inserts all mappings from the given Bundle into this Bundle.
      *
      * @param bundle a Bundle
@@ -253,25 +186,6 @@
     }
 
     /**
-     * Inserts all mappings from the given PersistableBundle into this Bundle.
-     *
-     * @param bundle a PersistableBundle
-     */
-    public void putAll(PersistableBundle bundle) {
-        super.putAll(bundle);
-    }
-
-    /**
-     * Returns a Set containing the Strings used as keys in this Bundle.
-     *
-     * @return a Set of String keys
-     */
-    @Override
-    public Set<String> keySet() {
-        return super.keySet();
-    }
-
-    /**
      * Reports whether the bundle contains any parcelled file descriptors.
      */
     public boolean hasFileDescriptors() {
@@ -384,30 +298,6 @@
     }
 
     /**
-     * Inserts an int value into the mapping of this Bundle, replacing
-     * any existing value for the given key.
-     *
-     * @param key a String, or null
-     * @param value an int, or null
-     */
-    @Override
-    public void putInt(String key, int value) {
-        super.putInt(key, value);
-    }
-
-    /**
-     * Inserts a long value into the mapping of this Bundle, replacing
-     * any existing value for the given key.
-     *
-     * @param key a String, or null
-     * @param value a long
-     */
-    @Override
-    public void putLong(String key, long value) {
-        super.putLong(key, value);
-    }
-
-    /**
      * Inserts a float value into the mapping of this Bundle, replacing
      * any existing value for the given key.
      *
@@ -420,30 +310,6 @@
     }
 
     /**
-     * Inserts a double value into the mapping of this Bundle, replacing
-     * any existing value for the given key.
-     *
-     * @param key a String, or null
-     * @param value a double
-     */
-    @Override
-    public void putDouble(String key, double value) {
-        super.putDouble(key, value);
-    }
-
-    /**
-     * Inserts a String value into the mapping of this Bundle, replacing
-     * any existing value for the given key.  Either key or value may be null.
-     *
-     * @param key a String, or null
-     * @param value a String, or null
-     */
-    @Override
-    public void putString(String key, String value) {
-        super.putString(key, value);
-    }
-
-    /**
      * Inserts a CharSequence value into the mapping of this Bundle, replacing
      * any existing value for the given key.  Either key or value may be null.
      *
@@ -616,30 +482,6 @@
     }
 
     /**
-     * Inserts an int array value into the mapping of this Bundle, replacing
-     * any existing value for the given key.  Either key or value may be null.
-     *
-     * @param key a String, or null
-     * @param value an int array object, or null
-     */
-    @Override
-    public void putIntArray(String key, int[] value) {
-        super.putIntArray(key, value);
-    }
-
-    /**
-     * Inserts a long array value into the mapping of this Bundle, replacing
-     * any existing value for the given key.  Either key or value may be null.
-     *
-     * @param key a String, or null
-     * @param value a long array object, or null
-     */
-    @Override
-    public void putLongArray(String key, long[] value) {
-        super.putLongArray(key, value);
-    }
-
-    /**
      * Inserts a float array value into the mapping of this Bundle, replacing
      * any existing value for the given key.  Either key or value may be null.
      *
@@ -652,30 +494,6 @@
     }
 
     /**
-     * Inserts a double array value into the mapping of this Bundle, replacing
-     * any existing value for the given key.  Either key or value may be null.
-     *
-     * @param key a String, or null
-     * @param value a double array object, or null
-     */
-    @Override
-    public void putDoubleArray(String key, double[] value) {
-        super.putDoubleArray(key, value);
-    }
-
-    /**
-     * Inserts a String array value into the mapping of this Bundle, replacing
-     * any existing value for the given key.  Either key or value may be null.
-     *
-     * @param key a String, or null
-     * @param value a String array object, or null
-     */
-    @Override
-    public void putStringArray(String key, String[] value) {
-        super.putStringArray(key, value);
-    }
-
-    /**
      * Inserts a CharSequence array value into the mapping of this Bundle, replacing
      * any existing value for the given key.  Either key or value may be null.
      *
@@ -700,17 +518,6 @@
     }
 
     /**
-     * Inserts a PersistableBundle value into the mapping of this Bundle, replacing
-     * any existing value for the given key.  Either key or value may be null.
-     *
-     * @param key a String, or null
-     * @param value a Bundle object, or null
-     */
-    public void putPersistableBundle(String key, PersistableBundle value) {
-        super.putPersistableBundle(key, value);
-    }
-
-    /**
      * Inserts an {@link IBinder} value into the mapping of this Bundle, replacing
      * any existing value for the given key.  Either key or value may be null.
      *
@@ -846,56 +653,6 @@
     }
 
     /**
-     * Returns the value associated with the given key, or 0 if
-     * no mapping of the desired type exists for the given key.
-     *
-     * @param key a String
-     * @return an int value
-     */
-    @Override
-    public int getInt(String key) {
-        return super.getInt(key);
-    }
-
-    /**
-     * Returns the value associated with the given key, or defaultValue if
-     * no mapping of the desired type exists for the given key.
-     *
-     * @param key a String
-     * @param defaultValue Value to return if key does not exist
-     * @return an int value
-     */
-    @Override
-    public int getInt(String key, int defaultValue) {
-        return super.getInt(key, defaultValue);
-    }
-
-    /**
-     * Returns the value associated with the given key, or 0L if
-     * no mapping of the desired type exists for the given key.
-     *
-     * @param key a String
-     * @return a long value
-     */
-    @Override
-    public long getLong(String key) {
-        return super.getLong(key);
-    }
-
-    /**
-     * Returns the value associated with the given key, or defaultValue if
-     * no mapping of the desired type exists for the given key.
-     *
-     * @param key a String
-     * @param defaultValue Value to return if key does not exist
-     * @return a long value
-     */
-    @Override
-    public long getLong(String key, long defaultValue) {
-        return super.getLong(key, defaultValue);
-    }
-
-    /**
      * Returns the value associated with the given key, or 0.0f if
      * no mapping of the desired type exists for the given key.
      *
@@ -921,58 +678,6 @@
     }
 
     /**
-     * Returns the value associated with the given key, or 0.0 if
-     * no mapping of the desired type exists for the given key.
-     *
-     * @param key a String
-     * @return a double value
-     */
-    @Override
-    public double getDouble(String key) {
-        return super.getDouble(key);
-    }
-
-    /**
-     * Returns the value associated with the given key, or defaultValue if
-     * no mapping of the desired type exists for the given key.
-     *
-     * @param key a String
-     * @param defaultValue Value to return if key does not exist
-     * @return a double value
-     */
-    @Override
-    public double getDouble(String key, double defaultValue) {
-        return super.getDouble(key, defaultValue);
-    }
-
-    /**
-     * Returns the value associated with the given key, or null if
-     * no mapping of the desired type exists for the given key or a null
-     * value is explicitly associated with the key.
-     *
-     * @param key a String, or null
-     * @return a String value, or null
-     */
-    @Override
-    public String getString(String key) {
-        return super.getString(key);
-    }
-
-    /**
-     * Returns the value associated with the given key, or defaultValue if
-     * no mapping of the desired type exists for the given key.
-     *
-     * @param key a String, or null
-     * @param defaultValue Value to return if key does not exist
-     * @return the String value associated with the given key, or defaultValue
-     *     if no valid String object is currently mapped to that key.
-     */
-    @Override
-    public String getString(String key, String defaultValue) {
-        return super.getString(key, defaultValue);
-    }
-
-    /**
      * Returns the value associated with the given key, or null if
      * no mapping of the desired type exists for the given key or a null
      * value is explicitly associated with the key.
@@ -1027,18 +732,6 @@
      * value is explicitly associated with the key.
      *
      * @param key a String, or null
-     * @return a PersistableBundle value, or null
-     */
-    public PersistableBundle getPersistableBundle(String key) {
-        return super.getPersistableBundle(key);
-    }
-
-    /**
-     * Returns the value associated with the given key, or null if
-     * no mapping of the desired type exists for the given key or a null
-     * value is explicitly associated with the key.
-     *
-     * @param key a String, or null
      * @return a Parcelable value, or null
      */
     public <T extends Parcelable> T getParcelable(String key) {
@@ -1232,32 +925,6 @@
      * value is explicitly associated with the key.
      *
      * @param key a String, or null
-     * @return an int[] value, or null
-     */
-    @Override
-    public int[] getIntArray(String key) {
-        return super.getIntArray(key);
-    }
-
-    /**
-     * Returns the value associated with the given key, or null if
-     * no mapping of the desired type exists for the given key or a null
-     * value is explicitly associated with the key.
-     *
-     * @param key a String, or null
-     * @return a long[] value, or null
-     */
-    @Override
-    public long[] getLongArray(String key) {
-        return super.getLongArray(key);
-    }
-
-    /**
-     * Returns the value associated with the given key, or null if
-     * no mapping of the desired type exists for the given key or a null
-     * value is explicitly associated with the key.
-     *
-     * @param key a String, or null
      * @return a float[] value, or null
      */
     @Override
@@ -1271,32 +938,6 @@
      * value is explicitly associated with the key.
      *
      * @param key a String, or null
-     * @return a double[] value, or null
-     */
-    @Override
-    public double[] getDoubleArray(String key) {
-        return super.getDoubleArray(key);
-    }
-
-    /**
-     * Returns the value associated with the given key, or null if
-     * no mapping of the desired type exists for the given key or a null
-     * value is explicitly associated with the key.
-     *
-     * @param key a String, or null
-     * @return a String[] value, or null
-     */
-    @Override
-    public String[] getStringArray(String key) {
-        return super.getStringArray(key);
-    }
-
-    /**
-     * Returns the value associated with the given key, or null if
-     * no mapping of the desired type exists for the given key or a null
-     * value is explicitly associated with the key.
-     *
-     * @param key a String, or null
      * @return a CharSequence[] value, or null
      */
     @Override
diff --git a/core/java/android/os/Environment.java b/core/java/android/os/Environment.java
index e98a26b..e84b695 100644
--- a/core/java/android/os/Environment.java
+++ b/core/java/android/os/Environment.java
@@ -191,6 +191,10 @@
             return buildPaths(mExternalDirsForApp, DIR_ANDROID, DIR_MEDIA, packageName);
         }
 
+        public File[] buildExternalStorageAppMediaDirsForVold(String packageName) {
+            return buildPaths(mExternalDirsForVold, DIR_ANDROID, DIR_MEDIA, packageName);
+        }
+
         public File[] buildExternalStorageAppObbDirs(String packageName) {
             return buildPaths(mExternalDirsForApp, DIR_ANDROID, DIR_OBB, packageName);
         }
diff --git a/core/java/android/os/PersistableBundle.java b/core/java/android/os/PersistableBundle.java
index cd8d515..c01f688 100644
--- a/core/java/android/os/PersistableBundle.java
+++ b/core/java/android/os/PersistableBundle.java
@@ -32,7 +32,8 @@
  * restored.
  *
  */
-public final class PersistableBundle extends CommonBundle implements XmlUtils.WriteMapCallback {
+public final class PersistableBundle extends BaseBundle implements Cloneable, Parcelable,
+        XmlUtils.WriteMapCallback {
     private static final String TAG_PERSISTABLEMAP = "pbundle_as_map";
     public static final PersistableBundle EMPTY;
     static final Parcel EMPTY_PARCEL;
@@ -40,7 +41,7 @@
     static {
         EMPTY = new PersistableBundle();
         EMPTY.mMap = ArrayMap.EMPTY;
-        EMPTY_PARCEL = CommonBundle.EMPTY_PARCEL;
+        EMPTY_PARCEL = BaseBundle.EMPTY_PARCEL;
     }
 
     /**
@@ -51,31 +52,6 @@
     }
 
     /**
-     * Constructs a PersistableBundle whose data is stored as a Parcel.  The data
-     * will be unparcelled on first contact, using the assigned ClassLoader.
-     *
-     * @param parcelledData a Parcel containing a PersistableBundle
-     */
-    PersistableBundle(Parcel parcelledData) {
-        super(parcelledData);
-    }
-
-    /* package */ PersistableBundle(Parcel parcelledData, int length) {
-        super(parcelledData, length);
-    }
-
-    /**
-     * Constructs a new, empty PersistableBundle that uses a specific ClassLoader for
-     * instantiating Parcelable and Serializable objects.
-     *
-     * @param loader An explicit ClassLoader to use when instantiating objects
-     * inside of the PersistableBundle.
-     */
-    public PersistableBundle(ClassLoader loader) {
-        super(loader);
-    }
-
-    /**
      * Constructs a new, empty PersistableBundle sized to hold the given number of
      * elements. The PersistableBundle will grow as needed.
      *
@@ -127,6 +103,10 @@
         }
     }
 
+    /* package */ PersistableBundle(Parcel parcelledData, int length) {
+        super(parcelledData, length);
+    }
+
     /**
      * Make a PersistableBundle for a single key/value pair.
      *
@@ -139,33 +119,6 @@
     }
 
     /**
-     * @hide
-     */
-    @Override
-    public String getPairValue() {
-        return super.getPairValue();
-    }
-
-    /**
-     * Changes the ClassLoader this PersistableBundle uses when instantiating objects.
-     *
-     * @param loader An explicit ClassLoader to use when instantiating objects
-     * inside of the PersistableBundle.
-     */
-    @Override
-    public void setClassLoader(ClassLoader loader) {
-        super.setClassLoader(loader);
-    }
-
-    /**
-     * Return the ClassLoader currently associated with this PersistableBundle.
-     */
-    @Override
-    public ClassLoader getClassLoader() {
-        return super.getClassLoader();
-    }
-
-    /**
      * Clones the current PersistableBundle. The internal map is cloned, but the keys and
      * values to which it refers are copied by reference.
      */
@@ -175,300 +128,15 @@
     }
 
     /**
-     * @hide
-     */
-    @Override
-    public boolean isParcelled() {
-        return super.isParcelled();
-    }
-
-    /**
-     * Returns the number of mappings contained in this PersistableBundle.
-     *
-     * @return the number of mappings as an int.
-     */
-    @Override
-    public int size() {
-        return super.size();
-    }
-
-    /**
-     * Returns true if the mapping of this PersistableBundle is empty, false otherwise.
-     */
-    @Override
-    public boolean isEmpty() {
-        return super.isEmpty();
-    }
-
-    /**
-     * Removes all elements from the mapping of this PersistableBundle.
-     */
-    @Override
-    public void clear() {
-        super.clear();
-    }
-
-    /**
-     * Returns true if the given key is contained in the mapping
-     * of this PersistableBundle.
-     *
-     * @param key a String key
-     * @return true if the key is part of the mapping, false otherwise
-     */
-    @Override
-    public boolean containsKey(String key) {
-        return super.containsKey(key);
-    }
-
-    /**
-     * Returns the entry with the given key as an object.
-     *
-     * @param key a String key
-     * @return an Object, or null
-     */
-    @Override
-    public Object get(String key) {
-        return super.get(key);
-    }
-
-    /**
-     * Removes any entry with the given key from the mapping of this PersistableBundle.
-     *
-     * @param key a String key
-     */
-    @Override
-    public void remove(String key) {
-        super.remove(key);
-    }
-
-    /**
-     * Inserts all mappings from the given PersistableBundle into this Bundle.
-     *
-     * @param bundle a PersistableBundle
-     */
-    @Override
-    public void putAll(PersistableBundle bundle) {
-        super.putAll(bundle);
-    }
-
-    /**
-     * Returns a Set containing the Strings used as keys in this PersistableBundle.
-     *
-     * @return a Set of String keys
-     */
-    @Override
-    public Set<String> keySet() {
-        return super.keySet();
-    }
-
-    /**
-     * Inserts an int value into the mapping of this PersistableBundle, replacing
-     * any existing value for the given key.
-     *
-     * @param key a String, or null
-     * @param value an int, or null
-     */
-    @Override
-    public void putInt(String key, int value) {
-        super.putInt(key, value);
-    }
-
-    /**
-     * Inserts a long value into the mapping of this PersistableBundle, replacing
-     * any existing value for the given key.
-     *
-     * @param key a String, or null
-     * @param value a long
-     */
-    @Override
-    public void putLong(String key, long value) {
-        super.putLong(key, value);
-    }
-
-    /**
-     * Inserts a double value into the mapping of this PersistableBundle, replacing
-     * any existing value for the given key.
-     *
-     * @param key a String, or null
-     * @param value a double
-     */
-    @Override
-    public void putDouble(String key, double value) {
-        super.putDouble(key, value);
-    }
-
-    /**
-     * Inserts a String value into the mapping of this PersistableBundle, replacing
-     * any existing value for the given key.  Either key or value may be null.
-     *
-     * @param key a String, or null
-     * @param value a String, or null
-     */
-    @Override
-    public void putString(String key, String value) {
-        super.putString(key, value);
-    }
-
-    /**
-     * Inserts an int array value into the mapping of this PersistableBundle, replacing
-     * any existing value for the given key.  Either key or value may be null.
-     *
-     * @param key a String, or null
-     * @param value an int array object, or null
-     */
-    @Override
-    public void putIntArray(String key, int[] value) {
-        super.putIntArray(key, value);
-    }
-
-    /**
-     * Inserts a long array value into the mapping of this PersistableBundle, replacing
-     * any existing value for the given key.  Either key or value may be null.
-     *
-     * @param key a String, or null
-     * @param value a long array object, or null
-     */
-    @Override
-    public void putLongArray(String key, long[] value) {
-        super.putLongArray(key, value);
-    }
-
-    /**
-     * Inserts a double array value into the mapping of this PersistableBundle, replacing
-     * any existing value for the given key.  Either key or value may be null.
-     *
-     * @param key a String, or null
-     * @param value a double array object, or null
-     */
-    @Override
-    public void putDoubleArray(String key, double[] value) {
-        super.putDoubleArray(key, value);
-    }
-
-    /**
-     * Inserts a String array value into the mapping of this PersistableBundle, replacing
-     * any existing value for the given key.  Either key or value may be null.
-     *
-     * @param key a String, or null
-     * @param value a String array object, or null
-     */
-    @Override
-    public void putStringArray(String key, String[] value) {
-        super.putStringArray(key, value);
-    }
-
-    /**
      * Inserts a PersistableBundle value into the mapping of this Bundle, replacing
      * any existing value for the given key.  Either key or value may be null.
      *
      * @param key a String, or null
      * @param value a Bundle object, or null
      */
-    @Override
     public void putPersistableBundle(String key, PersistableBundle value) {
-        super.putPersistableBundle(key, value);
-    }
-
-    /**
-     * Returns the value associated with the given key, or 0 if
-     * no mapping of the desired type exists for the given key.
-     *
-     * @param key a String
-     * @return an int value
-     */
-    @Override
-    public int getInt(String key) {
-        return super.getInt(key);
-    }
-
-    /**
-     * Returns the value associated with the given key, or defaultValue if
-     * no mapping of the desired type exists for the given key.
-     *
-     * @param key a String
-     * @param defaultValue Value to return if key does not exist
-     * @return an int value
-     */
-    @Override
-    public int getInt(String key, int defaultValue) {
-        return super.getInt(key, defaultValue);
-    }
-
-    /**
-     * Returns the value associated with the given key, or 0L if
-     * no mapping of the desired type exists for the given key.
-     *
-     * @param key a String
-     * @return a long value
-     */
-    @Override
-    public long getLong(String key) {
-        return super.getLong(key);
-    }
-
-    /**
-     * Returns the value associated with the given key, or defaultValue if
-     * no mapping of the desired type exists for the given key.
-     *
-     * @param key a String
-     * @param defaultValue Value to return if key does not exist
-     * @return a long value
-     */
-    @Override
-    public long getLong(String key, long defaultValue) {
-        return super.getLong(key, defaultValue);
-    }
-
-    /**
-     * Returns the value associated with the given key, or 0.0 if
-     * no mapping of the desired type exists for the given key.
-     *
-     * @param key a String
-     * @return a double value
-     */
-    @Override
-    public double getDouble(String key) {
-        return super.getDouble(key);
-    }
-
-    /**
-     * Returns the value associated with the given key, or defaultValue if
-     * no mapping of the desired type exists for the given key.
-     *
-     * @param key a String
-     * @param defaultValue Value to return if key does not exist
-     * @return a double value
-     */
-    @Override
-    public double getDouble(String key, double defaultValue) {
-        return super.getDouble(key, defaultValue);
-    }
-
-    /**
-     * Returns the value associated with the given key, or null if
-     * no mapping of the desired type exists for the given key or a null
-     * value is explicitly associated with the key.
-     *
-     * @param key a String, or null
-     * @return a String value, or null
-     */
-    @Override
-    public String getString(String key) {
-        return super.getString(key);
-    }
-
-    /**
-     * Returns the value associated with the given key, or defaultValue if
-     * no mapping of the desired type exists for the given key.
-     *
-     * @param key a String, or null
-     * @param defaultValue Value to return if key does not exist
-     * @return the String value associated with the given key, or defaultValue
-     *     if no valid String object is currently mapped to that key.
-     */
-    @Override
-    public String getString(String key, String defaultValue) {
-        return super.getString(key, defaultValue);
+        unparcel();
+        mMap.put(key, value);
     }
 
     /**
@@ -479,61 +147,18 @@
      * @param key a String, or null
      * @return a Bundle value, or null
      */
-    @Override
     public PersistableBundle getPersistableBundle(String key) {
-        return super.getPersistableBundle(key);
-    }
-
-    /**
-     * Returns the value associated with the given key, or null if
-     * no mapping of the desired type exists for the given key or a null
-     * value is explicitly associated with the key.
-     *
-     * @param key a String, or null
-     * @return an int[] value, or null
-     */
-    @Override
-    public int[] getIntArray(String key) {
-        return super.getIntArray(key);
-    }
-
-    /**
-     * Returns the value associated with the given key, or null if
-     * no mapping of the desired type exists for the given key or a null
-     * value is explicitly associated with the key.
-     *
-     * @param key a String, or null
-     * @return a long[] value, or null
-     */
-    @Override
-    public long[] getLongArray(String key) {
-        return super.getLongArray(key);
-    }
-
-    /**
-     * Returns the value associated with the given key, or null if
-     * no mapping of the desired type exists for the given key or a null
-     * value is explicitly associated with the key.
-     *
-     * @param key a String, or null
-     * @return a double[] value, or null
-     */
-    @Override
-    public double[] getDoubleArray(String key) {
-        return super.getDoubleArray(key);
-    }
-
-    /**
-     * Returns the value associated with the given key, or null if
-     * no mapping of the desired type exists for the given key or a null
-     * value is explicitly associated with the key.
-     *
-     * @param key a String, or null
-     * @return a String[] value, or null
-     */
-    @Override
-    public String[] getStringArray(String key) {
-        return super.getStringArray(key);
+        unparcel();
+        Object o = mMap.get(key);
+        if (o == null) {
+            return null;
+        }
+        try {
+            return (PersistableBundle) o;
+        } catch (ClassCastException e) {
+            typeWarning(key, o, "Bundle", e);
+            return null;
+        }
     }
 
     public static final Parcelable.Creator<PersistableBundle> CREATOR =
@@ -549,38 +174,6 @@
                 }
             };
 
-    /**
-     * Report the nature of this Parcelable's contents
-     */
-    @Override
-    public int describeContents() {
-        return 0;
-    }
-
-    /**
-     * Writes the PersistableBundle contents to a Parcel, typically in order for
-     * it to be passed through an IBinder connection.
-     * @param parcel The parcel to copy this bundle to.
-     */
-    @Override
-    public void writeToParcel(Parcel parcel, int flags) {
-        final boolean oldAllowFds = parcel.pushAllowFds(false);
-        try {
-            super.writeToParcelInner(parcel, flags);
-        } finally {
-            parcel.restoreAllowFds(oldAllowFds);
-        }
-    }
-
-    /**
-     * Reads the Parcel contents into this PersistableBundle, typically in order for
-     * it to be passed through an IBinder connection.
-     * @param parcel The parcel to overwrite this bundle from.
-     */
-    public void readFromParcel(Parcel parcel) {
-        super.readFromParcelInner(parcel);
-    }
-
     /** @hide */
     @Override
     public void writeUnknownObject(Object v, String name, XmlSerializer out)
@@ -614,8 +207,29 @@
     }
 
     /**
-     * @hide
+     * Report the nature of this Parcelable's contents
      */
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    /**
+     * Writes the PersistableBundle contents to a Parcel, typically in order for
+     * it to be passed through an IBinder connection.
+     * @param parcel The parcel to copy this bundle to.
+     */
+    @Override
+    public void writeToParcel(Parcel parcel, int flags) {
+        final boolean oldAllowFds = parcel.pushAllowFds(false);
+        try {
+            writeToParcelInner(parcel, flags);
+        } finally {
+            parcel.restoreAllowFds(oldAllowFds);
+        }
+    }
+
+    /** @hide */
     public static PersistableBundle restoreFromXml(XmlPullParser in) throws IOException,
             XmlPullParserException {
         final int outerDepth = in.getDepth();
diff --git a/core/java/android/os/Trace.java b/core/java/android/os/Trace.java
index 57ed979..474192f 100644
--- a/core/java/android/os/Trace.java
+++ b/core/java/android/os/Trace.java
@@ -70,6 +70,8 @@
     public static final long TRACE_TAG_DALVIK = 1L << 14;
     /** @hide */
     public static final long TRACE_TAG_RS = 1L << 15;
+    /** @hide */
+    public static final long TRACE_TAG_BIONIC = 1L << 16;
 
     private static final long TRACE_TAG_NOT_READY = 1L << 63;
     private static final int MAX_SECTION_NAME_LEN = 127;
diff --git a/core/java/android/os/storage/StorageManager.java b/core/java/android/os/storage/StorageManager.java
index 4963991..68b91cb 100644
--- a/core/java/android/os/storage/StorageManager.java
+++ b/core/java/android/os/storage/StorageManager.java
@@ -58,24 +58,6 @@
  * argument of {@link android.content.Context#STORAGE_SERVICE}.
  */
 public class StorageManager {
-
-    /// Consts to match the password types in cryptfs.h
-    /** Master key is encrypted with a password.
-     */
-    public static final int CRYPT_TYPE_PASSWORD = 0;
-
-    /** Master key is encrypted with the default password.
-     */
-    public static final int CRYPT_TYPE_DEFAULT = 1;
-
-    /** Master key is encrypted with a pattern.
-     */
-    public static final int CRYPT_TYPE_PATTERN = 2;
-
-    /** Master key is encrypted with a pin.
-     */
-    public static final int CRYPT_TYPE_PIN = 3;
-
     private static final String TAG = "StorageManager";
 
     private final ContentResolver mResolver;
@@ -663,4 +645,14 @@
         return Settings.Global.getLong(mResolver, Settings.Global.SYS_STORAGE_FULL_THRESHOLD_BYTES,
                 DEFAULT_FULL_THRESHOLD_BYTES);
     }
+
+    /// Consts to match the password types in cryptfs.h
+    /** @hide */
+    public static final int CRYPT_TYPE_PASSWORD = 0;
+    /** @hide */
+    public static final int CRYPT_TYPE_DEFAULT = 1;
+    /** @hide */
+    public static final int CRYPT_TYPE_PATTERN = 2;
+    /** @hide */
+    public static final int CRYPT_TYPE_PIN = 3;
 }
diff --git a/core/java/android/preference/SeekBarVolumizer.java b/core/java/android/preference/SeekBarVolumizer.java
index d2d6ade..d66fc0f 100644
--- a/core/java/android/preference/SeekBarVolumizer.java
+++ b/core/java/android/preference/SeekBarVolumizer.java
@@ -16,7 +16,10 @@
 
 package android.preference;
 
+import android.content.BroadcastReceiver;
 import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
 import android.database.ContentObserver;
 import android.media.AudioManager;
 import android.media.Ringtone;
@@ -37,20 +40,25 @@
  * @hide
  */
 public class SeekBarVolumizer implements OnSeekBarChangeListener, Handler.Callback {
+    private static final String TAG = "SeekBarVolumizer";
 
     public interface Callback {
         void onSampleStarting(SeekBarVolumizer sbv);
     }
 
-    private Context mContext;
-    private Handler mHandler;
+    private final Context mContext;
+    private final Handler mHandler;
+    private final H mUiHandler = new H();
     private final Callback mCallback;
+    private final Uri mDefaultUri;
+    private final AudioManager mAudioManager;
+    private final int mStreamType;
+    private final int mMaxStreamVolume;
+    private final Receiver mReceiver = new Receiver();
+    private final Observer mVolumeObserver;
 
-    private AudioManager mAudioManager;
-    private int mStreamType;
     private int mOriginalStreamVolume;
     private Ringtone mRingtone;
-
     private int mLastProgress = -1;
     private SeekBar mSeekBar;
     private int mVolumeBeforeMute = -1;
@@ -58,44 +66,25 @@
     private static final int MSG_SET_STREAM_VOLUME = 0;
     private static final int MSG_START_SAMPLE = 1;
     private static final int MSG_STOP_SAMPLE = 2;
+    private static final int MSG_INIT_SAMPLE = 3;
     private static final int CHECK_RINGTONE_PLAYBACK_DELAY_MS = 1000;
 
-    private ContentObserver mVolumeObserver = new ContentObserver(mHandler) {
-        @Override
-        public void onChange(boolean selfChange) {
-            super.onChange(selfChange);
-            if (mSeekBar != null && mAudioManager != null) {
-                int volume = mAudioManager.getStreamVolume(mStreamType);
-                mSeekBar.setProgress(volume);
-            }
-        }
-    };
-
-    public SeekBarVolumizer(Context context, SeekBar seekBar, int streamType, Uri defaultUri,
+    public SeekBarVolumizer(Context context, int streamType, Uri defaultUri,
             Callback callback) {
         mContext = context;
         mAudioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
         mStreamType = streamType;
-        mSeekBar = seekBar;
-
-        HandlerThread thread = new HandlerThread(VolumePreference.TAG + ".CallbackHandler");
+        mMaxStreamVolume = mAudioManager.getStreamMaxVolume(mStreamType);
+        HandlerThread thread = new HandlerThread(TAG + ".CallbackHandler");
         thread.start();
         mHandler = new Handler(thread.getLooper(), this);
         mCallback = callback;
-
-        initSeekBar(seekBar, defaultUri);
-    }
-
-    private void initSeekBar(SeekBar seekBar, Uri defaultUri) {
-        seekBar.setMax(mAudioManager.getStreamMaxVolume(mStreamType));
         mOriginalStreamVolume = mAudioManager.getStreamVolume(mStreamType);
-        seekBar.setProgress(mOriginalStreamVolume);
-        seekBar.setOnSeekBarChangeListener(this);
-
+        mVolumeObserver = new Observer(mHandler);
         mContext.getContentResolver().registerContentObserver(
                 System.getUriFor(System.VOLUME_SETTINGS[mStreamType]),
                 false, mVolumeObserver);
-
+        mReceiver.setListening(true);
         if (defaultUri == null) {
             if (mStreamType == AudioManager.STREAM_RING) {
                 defaultUri = Settings.System.DEFAULT_RINGTONE_URI;
@@ -105,12 +94,19 @@
                 defaultUri = Settings.System.DEFAULT_ALARM_ALERT_URI;
             }
         }
+        mDefaultUri = defaultUri;
+        mHandler.sendEmptyMessage(MSG_INIT_SAMPLE);
+    }
 
-        mRingtone = RingtoneManager.getRingtone(mContext, defaultUri);
-
-        if (mRingtone != null) {
-            mRingtone.setStreamType(mStreamType);
+    public void setSeekBar(SeekBar seekBar) {
+        if (mSeekBar != null) {
+            mSeekBar.setOnSeekBarChangeListener(null);
         }
+        mSeekBar = seekBar;
+        mSeekBar.setOnSeekBarChangeListener(null);
+        mSeekBar.setMax(mMaxStreamVolume);
+        mSeekBar.setProgress(mLastProgress > -1 ? mLastProgress : mOriginalStreamVolume);
+        mSeekBar.setOnSeekBarChangeListener(this);
     }
 
     @Override
@@ -125,12 +121,22 @@
             case MSG_STOP_SAMPLE:
                 onStopSample();
                 break;
+            case MSG_INIT_SAMPLE:
+                onInitSample();
+                break;
             default:
-                Log.e(VolumePreference.TAG, "invalid SeekBarVolumizer message: "+msg.what);
+                Log.e(TAG, "invalid SeekBarVolumizer message: "+msg.what);
         }
         return true;
     }
 
+    private void onInitSample() {
+        mRingtone = RingtoneManager.getRingtone(mContext, mDefaultUri);
+        if (mRingtone != null) {
+            mRingtone.setStreamType(mStreamType);
+        }
+    }
+
     private void postStartSample() {
         mHandler.removeMessages(MSG_START_SAMPLE);
         mHandler.sendMessageDelayed(mHandler.obtainMessage(MSG_START_SAMPLE),
@@ -143,7 +149,11 @@
                 mCallback.onSampleStarting(this);
             }
             if (mRingtone != null) {
-                mRingtone.play();
+                try {
+                    mRingtone.play();
+                } catch (Throwable e) {
+                    Log.w(TAG, "Error playing ringtone, stream " + mStreamType, e);
+                }
             }
         }
     }
@@ -165,6 +175,8 @@
         postStopSample();
         mContext.getContentResolver().unregisterContentObserver(mVolumeObserver);
         mSeekBar.setOnSeekBarChangeListener(null);
+        mReceiver.setListening(false);
+        mHandler.getLooper().quitSafely();
     }
 
     public void revertVolume() {
@@ -245,4 +257,62 @@
             postSetVolume(mLastProgress);
         }
     }
-}
\ No newline at end of file
+
+    private final class H extends Handler {
+        private static final int UPDATE_SLIDER = 1;
+
+        @Override
+        public void handleMessage(Message msg) {
+            if (msg.what == UPDATE_SLIDER) {
+                if (mSeekBar != null) {
+                    mSeekBar.setProgress(msg.arg1);
+                    mLastProgress = mSeekBar.getProgress();
+                }
+            }
+        }
+
+        public void postUpdateSlider(int volume) {
+            obtainMessage(UPDATE_SLIDER, volume, 0).sendToTarget();
+        }
+    }
+
+    private final class Observer extends ContentObserver {
+        public Observer(Handler handler) {
+            super(handler);
+        }
+
+        @Override
+        public void onChange(boolean selfChange) {
+            super.onChange(selfChange);
+            if (mSeekBar != null && mAudioManager != null) {
+                final int volume = mAudioManager.getStreamVolume(mStreamType);
+                mUiHandler.postUpdateSlider(volume);
+            }
+        }
+    }
+
+    private final class Receiver extends BroadcastReceiver {
+        private boolean mListening;
+
+        public void setListening(boolean listening) {
+            if (mListening == listening) return;
+            mListening = listening;
+            if (listening) {
+                final IntentFilter filter = new IntentFilter(AudioManager.VOLUME_CHANGED_ACTION);
+                mContext.registerReceiver(this, filter);
+            } else {
+                mContext.unregisterReceiver(this);
+            }
+        }
+
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            if (!AudioManager.VOLUME_CHANGED_ACTION.equals(intent.getAction())) return;
+            final int streamType = intent.getIntExtra(AudioManager.EXTRA_VOLUME_STREAM_TYPE, -1);
+            final int streamValue = intent.getIntExtra(AudioManager.EXTRA_VOLUME_STREAM_VALUE, -1);
+            if (mSeekBar != null && streamType == mStreamType && streamValue != -1) {
+                mUiHandler.postUpdateSlider(streamValue);
+            }
+        }
+    }
+}
diff --git a/core/java/android/preference/VolumePreference.java b/core/java/android/preference/VolumePreference.java
index 171e5c3..df9e10e 100644
--- a/core/java/android/preference/VolumePreference.java
+++ b/core/java/android/preference/VolumePreference.java
@@ -66,7 +66,8 @@
         super.onBindDialogView(view);
 
         final SeekBar seekBar = (SeekBar) view.findViewById(com.android.internal.R.id.seekbar);
-        mSeekBarVolumizer = new SeekBarVolumizer(getContext(), seekBar, mStreamType, null, this);
+        mSeekBarVolumizer = new SeekBarVolumizer(getContext(), mStreamType, null, this);
+        mSeekBarVolumizer.setSeekBar(seekBar);
 
         getPreferenceManager().registerOnActivityStopListener(this);
 
diff --git a/core/java/android/provider/ContactsContract.java b/core/java/android/provider/ContactsContract.java
index b53ea81..6db78f4 100644
--- a/core/java/android/provider/ContactsContract.java
+++ b/core/java/android/provider/ContactsContract.java
@@ -1152,8 +1152,6 @@
      * address book index, which is usually the first letter of the sort key.
      * When this parameter is supplied, the row counts are returned in the
      * cursor extras bundle.
-     *
-     * @hide
      */
     public final static class ContactCounts {
 
@@ -1163,7 +1161,24 @@
          * first letter of the sort key. This parameter does not affect the main
          * content of the cursor.
          *
-         * @hide
+         * <p>
+         * <pre>
+         * Example:
+         * Uri uri = Contacts.CONTENT_URI.buildUpon()
+         *          .appendQueryParameter(ContactCounts.ADDRESS_BOOK_INDEX_EXTRAS, "true")
+         *          .build();
+         * Cursor cursor = getContentResolver().query(uri,
+         *          new String[] {Contacts.DISPLAY_NAME},
+         *          null, null, null);
+         * Bundle bundle = cursor.getExtras();
+         * if (bundle.containsKey(ContactCounts.EXTRA_ADDRESS_BOOK_INDEX_TITLES) &&
+         *         bundle.containsKey(ContactCounts.EXTRA_ADDRESS_BOOK_INDEX_COUNTS)) {
+         *     String sections[] =
+         *             bundle.getStringArray(ContactCounts.EXTRA_ADDRESS_BOOK_INDEX_TITLES);
+         *     int counts[] = bundle.getIntArray(ContactCounts.EXTRA_ADDRESS_BOOK_INDEX_COUNTS);
+         * }
+         * </pre>
+         * </p>
          */
         public static final String ADDRESS_BOOK_INDEX_EXTRAS = "address_book_index_extras";
 
@@ -1171,8 +1186,6 @@
          * The array of address book index titles, which are returned in the
          * same order as the data in the cursor.
          * <p>TYPE: String[]</p>
-         *
-         * @hide
          */
         public static final String EXTRA_ADDRESS_BOOK_INDEX_TITLES = "address_book_index_titles";
 
@@ -1180,8 +1193,6 @@
          * The array of group counts for the corresponding group.  Contains the same number
          * of elements as the EXTRA_ADDRESS_BOOK_INDEX_TITLES array.
          * <p>TYPE: int[]</p>
-         *
-         * @hide
          */
         public static final String EXTRA_ADDRESS_BOOK_INDEX_COUNTS = "address_book_index_counts";
     }
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index e9ffc52..bec401e 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -4461,6 +4461,12 @@
                 INCALL_POWER_BUTTON_BEHAVIOR_SCREEN_OFF;
 
         /**
+         * Whether the device should wake when the wake gesture sensor detects motion.
+         * @hide
+         */
+        public static final String WAKE_GESTURE_ENABLED = "wake_gesture_enabled";
+
+        /**
          * The current night mode that has been selected by the user.  Owned
          * and controlled by UiModeManagerService.  Constants are as per
          * UiModeManager.
diff --git a/core/java/android/service/notification/ZenModeConfig.java b/core/java/android/service/notification/ZenModeConfig.java
index 846e292..d02fc7b 100644
--- a/core/java/android/service/notification/ZenModeConfig.java
+++ b/core/java/android/service/notification/ZenModeConfig.java
@@ -41,12 +41,18 @@
     public static final String SLEEP_MODE_NIGHTS = "nights";
     public static final String SLEEP_MODE_WEEKNIGHTS = "weeknights";
 
+    public static final int SOURCE_ANYONE = 0;
+    public static final int SOURCE_CONTACT = 1;
+    public static final int SOURCE_STAR = 2;
+    public static final int MAX_SOURCE = SOURCE_STAR;
+
     private static final int XML_VERSION = 1;
     private static final String ZEN_TAG = "zen";
     private static final String ZEN_ATT_VERSION = "version";
     private static final String ALLOW_TAG = "allow";
     private static final String ALLOW_ATT_CALLS = "calls";
     private static final String ALLOW_ATT_MESSAGES = "messages";
+    private static final String ALLOW_ATT_FROM = "from";
     private static final String SLEEP_TAG = "sleep";
     private static final String SLEEP_ATT_MODE = "mode";
 
@@ -61,6 +67,7 @@
 
     public boolean allowCalls;
     public boolean allowMessages;
+    public int allowFrom = SOURCE_ANYONE;
 
     public String sleepMode;
     public int sleepStartHour;
@@ -92,6 +99,7 @@
             conditionIds = new Uri[len];
             source.readTypedArray(conditionIds, Uri.CREATOR);
         }
+        allowFrom = source.readInt();
     }
 
     @Override
@@ -120,6 +128,7 @@
         } else {
             dest.writeInt(0);
         }
+        dest.writeInt(allowFrom);
     }
 
     @Override
@@ -127,6 +136,7 @@
         return new StringBuilder(ZenModeConfig.class.getSimpleName()).append('[')
             .append("allowCalls=").append(allowCalls)
             .append(",allowMessages=").append(allowMessages)
+            .append(",allowFrom=").append(sourceToString(allowFrom))
             .append(",sleepMode=").append(sleepMode)
             .append(",sleepStart=").append(sleepStartHour).append('.').append(sleepStartMinute)
             .append(",sleepEnd=").append(sleepEndHour).append('.').append(sleepEndMinute)
@@ -137,6 +147,19 @@
             .append(']').toString();
     }
 
+    public static String sourceToString(int source) {
+        switch (source) {
+            case SOURCE_ANYONE:
+                return "anyone";
+            case SOURCE_CONTACT:
+                return "contacts";
+            case SOURCE_STAR:
+                return "stars";
+            default:
+                return "UNKNOWN";
+        }
+    }
+
     @Override
     public boolean equals(Object o) {
         if (!(o instanceof ZenModeConfig)) return false;
@@ -144,6 +167,7 @@
         final ZenModeConfig other = (ZenModeConfig) o;
         return other.allowCalls == allowCalls
                 && other.allowMessages == allowMessages
+                && other.allowFrom == allowFrom
                 && Objects.equals(other.sleepMode, sleepMode)
                 && other.sleepStartHour == sleepStartHour
                 && other.sleepStartMinute == sleepStartMinute
@@ -155,8 +179,8 @@
 
     @Override
     public int hashCode() {
-        return Objects.hash(allowCalls, allowMessages, sleepMode, sleepStartHour,
-                sleepStartMinute, sleepEndHour, sleepEndMinute,
+        return Objects.hash(allowCalls, allowMessages, allowFrom, sleepMode,
+                sleepStartHour, sleepStartMinute, sleepEndHour, sleepEndMinute,
                 Arrays.hashCode(conditionComponents), Arrays.hashCode(conditionIds));
     }
 
@@ -191,6 +215,10 @@
                 if (ALLOW_TAG.equals(tag)) {
                     rt.allowCalls = safeBoolean(parser, ALLOW_ATT_CALLS, false);
                     rt.allowMessages = safeBoolean(parser, ALLOW_ATT_MESSAGES, false);
+                    rt.allowFrom = safeInt(parser, ALLOW_ATT_FROM, SOURCE_ANYONE);
+                    if (rt.allowFrom < SOURCE_ANYONE || rt.allowFrom > MAX_SOURCE) {
+                        throw new IndexOutOfBoundsException("bad source in config:" + rt.allowFrom);
+                    }
                 } else if (SLEEP_TAG.equals(tag)) {
                     final String mode = parser.getAttributeValue(null, SLEEP_ATT_MODE);
                     rt.sleepMode = (SLEEP_MODE_NIGHTS.equals(mode)
@@ -224,6 +252,7 @@
         out.startTag(null, ALLOW_TAG);
         out.attribute(null, ALLOW_ATT_CALLS, Boolean.toString(allowCalls));
         out.attribute(null, ALLOW_ATT_MESSAGES, Boolean.toString(allowMessages));
+        out.attribute(null, ALLOW_ATT_FROM, Integer.toString(allowFrom));
         out.endTag(null, ALLOW_TAG);
 
         out.startTag(null, SLEEP_TAG);
diff --git a/core/java/android/service/voice/VoiceInteractionSession.java b/core/java/android/service/voice/VoiceInteractionSession.java
index a83544d..2e9077a 100644
--- a/core/java/android/service/voice/VoiceInteractionSession.java
+++ b/core/java/android/service/voice/VoiceInteractionSession.java
@@ -20,8 +20,8 @@
 import android.app.Instrumentation;
 import android.content.Context;
 import android.content.Intent;
-import android.content.res.Resources;
 import android.content.res.TypedArray;
+import android.graphics.Rect;
 import android.graphics.Region;
 import android.inputmethodservice.SoftInputWindow;
 import android.os.Binder;
@@ -32,6 +32,7 @@
 import android.os.RemoteException;
 import android.util.ArrayMap;
 import android.util.Log;
+import android.view.Gravity;
 import android.view.KeyEvent;
 import android.view.LayoutInflater;
 import android.view.View;
@@ -46,9 +47,22 @@
 import com.android.internal.os.HandlerCaller;
 import com.android.internal.os.SomeArgs;
 
+import java.lang.ref.WeakReference;
+
 import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
 import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT;
 
+/**
+ * An active voice interaction session, providing a facility for the implementation
+ * to interact with the user in the voice interaction layer.  This interface is no shown
+ * by default, but you can request that it be shown with {@link #showWindow()}, which
+ * will result in a later call to {@link #onCreateContentView()} in which the UI can be
+ * built
+ *
+ * <p>A voice interaction session can be self-contained, ultimately calling {@link #finish}
+ * when done.  It can also initiate voice interactions with applications by calling
+ * {@link #startVoiceActivity}</p>.
+ */
 public abstract class VoiceInteractionSession implements KeyEvent.Callback {
     static final String TAG = "VoiceInteractionSession";
     static final boolean DEBUG = true;
@@ -79,11 +93,14 @@
     final Insets mTmpInsets = new Insets();
     final int[] mTmpLocation = new int[2];
 
+    final WeakReference<VoiceInteractionSession> mWeakRef
+            = new WeakReference<VoiceInteractionSession>(this);
+
     final IVoiceInteractor mInteractor = new IVoiceInteractor.Stub() {
         @Override
         public IVoiceInteractorRequest startConfirmation(String callingPackage,
-                IVoiceInteractorCallback callback, String prompt, Bundle extras) {
-            Request request = findRequest(callback, true);
+                IVoiceInteractorCallback callback, CharSequence prompt, Bundle extras) {
+            Request request = newRequest(callback);
             mHandlerCaller.sendMessage(mHandlerCaller.obtainMessageOOOO(MSG_START_CONFIRMATION,
                     new Caller(callingPackage, Binder.getCallingUid()), request,
                     prompt, extras));
@@ -91,9 +108,19 @@
         }
 
         @Override
+        public IVoiceInteractorRequest startAbortVoice(String callingPackage,
+                IVoiceInteractorCallback callback, CharSequence message, Bundle extras) {
+            Request request = newRequest(callback);
+            mHandlerCaller.sendMessage(mHandlerCaller.obtainMessageOOOO(MSG_START_ABORT_VOICE,
+                    new Caller(callingPackage, Binder.getCallingUid()), request,
+                    message, extras));
+            return request.mInterface;
+        }
+
+        @Override
         public IVoiceInteractorRequest startCommand(String callingPackage,
                 IVoiceInteractorCallback callback, String command, Bundle extras) {
-            Request request = findRequest(callback, true);
+            Request request = newRequest(callback);
             mHandlerCaller.sendMessage(mHandlerCaller.obtainMessageOOOO(MSG_START_COMMAND,
                     new Caller(callingPackage, Binder.getCallingUid()), request,
                     command, extras));
@@ -142,29 +169,60 @@
         final IVoiceInteractorRequest mInterface = new IVoiceInteractorRequest.Stub() {
             @Override
             public void cancel() throws RemoteException {
-                mHandlerCaller.sendMessage(mHandlerCaller.obtainMessageO(MSG_CANCEL, Request.this));
+                VoiceInteractionSession session = mSession.get();
+                if (session != null) {
+                    session.mHandlerCaller.sendMessage(
+                            session.mHandlerCaller.obtainMessageO(MSG_CANCEL, Request.this));
+                }
             }
         };
         final IVoiceInteractorCallback mCallback;
-        final HandlerCaller mHandlerCaller;
-        Request(IVoiceInteractorCallback callback, HandlerCaller handlerCaller) {
+        final WeakReference<VoiceInteractionSession> mSession;
+
+        Request(IVoiceInteractorCallback callback, VoiceInteractionSession session) {
             mCallback = callback;
-            mHandlerCaller = handlerCaller;
+            mSession = session.mWeakRef;
+        }
+
+        void finishRequest() {
+            VoiceInteractionSession session = mSession.get();
+            if (session == null) {
+                throw new IllegalStateException("VoiceInteractionSession has been destroyed");
+            }
+            Request req = session.removeRequest(mInterface.asBinder());
+            if (req == null) {
+                throw new IllegalStateException("Request not active: " + this);
+            } else if (req != this) {
+                throw new IllegalStateException("Current active request " + req
+                        + " not same as calling request " + this);
+            }
         }
 
         public void sendConfirmResult(boolean confirmed, Bundle result) {
             try {
                 if (DEBUG) Log.d(TAG, "sendConfirmResult: req=" + mInterface
                         + " confirmed=" + confirmed + " result=" + result);
+                finishRequest();
                 mCallback.deliverConfirmationResult(mInterface, confirmed, result);
             } catch (RemoteException e) {
             }
         }
 
+        public void sendAbortVoiceResult(Bundle result) {
+            try {
+                if (DEBUG) Log.d(TAG, "sendConfirmResult: req=" + mInterface
+                        + " result=" + result);
+                finishRequest();
+                mCallback.deliverAbortVoiceResult(mInterface, result);
+            } catch (RemoteException e) {
+            }
+        }
+
         public void sendCommandResult(boolean complete, Bundle result) {
             try {
                 if (DEBUG) Log.d(TAG, "sendCommandResult: req=" + mInterface
                         + " result=" + result);
+                finishRequest();
                 mCallback.deliverCommandResult(mInterface, complete, result);
             } catch (RemoteException e) {
             }
@@ -173,6 +231,7 @@
         public void sendCancelResult() {
             try {
                 if (DEBUG) Log.d(TAG, "sendCancelResult: req=" + mInterface);
+                finishRequest();
                 mCallback.deliverCancel(mInterface);
             } catch (RemoteException e) {
             }
@@ -190,9 +249,10 @@
     }
 
     static final int MSG_START_CONFIRMATION = 1;
-    static final int MSG_START_COMMAND = 2;
-    static final int MSG_SUPPORTS_COMMANDS = 3;
-    static final int MSG_CANCEL = 4;
+    static final int MSG_START_ABORT_VOICE = 2;
+    static final int MSG_START_COMMAND = 3;
+    static final int MSG_SUPPORTS_COMMANDS = 4;
+    static final int MSG_CANCEL = 5;
 
     static final int MSG_TASK_STARTED = 100;
     static final int MSG_TASK_FINISHED = 101;
@@ -208,9 +268,16 @@
                     args = (SomeArgs)msg.obj;
                     if (DEBUG) Log.d(TAG, "onConfirm: req=" + ((Request) args.arg2).mInterface
                             + " prompt=" + args.arg3 + " extras=" + args.arg4);
-                    onConfirm((Caller)args.arg1, (Request)args.arg2, (String)args.arg3,
+                    onConfirm((Caller)args.arg1, (Request)args.arg2, (CharSequence)args.arg3,
                             (Bundle)args.arg4);
                     break;
+                case MSG_START_ABORT_VOICE:
+                    args = (SomeArgs)msg.obj;
+                    if (DEBUG) Log.d(TAG, "onAbortVoice: req=" + ((Request) args.arg2).mInterface
+                            + " message=" + args.arg3 + " extras=" + args.arg4);
+                    onAbortVoice((Caller) args.arg1, (Request) args.arg2, (CharSequence) args.arg3,
+                            (Bundle) args.arg4);
+                    break;
                 case MSG_START_COMMAND:
                     args = (SomeArgs)msg.obj;
                     if (DEBUG) Log.d(TAG, "onCommand: req=" + ((Request) args.arg2).mInterface
@@ -262,14 +329,14 @@
      */
     public static final class Insets {
         /**
-         * This is the top part of the UI that is the main content.  It is
+         * This is the part of the UI that is the main content.  It is
          * used to determine the basic space needed, to resize/pan the
          * application behind.  It is assumed that this inset does not
          * change very much, since any change will cause a full resize/pan
          * of the application behind.  This value is relative to the top edge
          * of the input method window.
          */
-        public int contentTopInsets;
+        public final Rect contentInsets = new Rect();
 
         /**
          * This is the region of the UI that is touchable.  It is used when
@@ -311,7 +378,8 @@
             new ViewTreeObserver.OnComputeInternalInsetsListener() {
         public void onComputeInternalInsets(ViewTreeObserver.InternalInsetsInfo info) {
             onComputeInsets(mTmpInsets);
-            info.contentInsets.top = info.visibleInsets.top = mTmpInsets.contentTopInsets;
+            info.contentInsets.set(mTmpInsets.contentInsets);
+            info.visibleInsets.set(mTmpInsets.contentInsets);
             info.touchableRegion.set(mTmpInsets.touchableRegion);
             info.setTouchableInsets(mTmpInsets.touchableInsets);
         }
@@ -327,18 +395,20 @@
                 mCallbacks, true);
     }
 
-    Request findRequest(IVoiceInteractorCallback callback, boolean newRequest) {
+    Request newRequest(IVoiceInteractorCallback callback) {
         synchronized (this) {
-            Request req = mActiveRequests.get(callback.asBinder());
+            Request req = new Request(callback, this);
+            mActiveRequests.put(req.mInterface.asBinder(), req);
+            return req;
+        }
+    }
+
+    Request removeRequest(IBinder reqInterface) {
+        synchronized (this) {
+            Request req = mActiveRequests.get(reqInterface);
             if (req != null) {
-                if (newRequest) {
-                    throw new IllegalArgumentException("Given request callback " + callback
-                            + " is already active");
-                }
-                return req;
+                mActiveRequests.remove(req);
             }
-            req = new Request(callback, mHandlerCaller);
-            mActiveRequests.put(callback.asBinder(), req);
             return req;
         }
     }
@@ -423,11 +493,34 @@
         mTheme = theme;
     }
 
+    /**
+     * Ask that a new activity be started for voice interaction.  This will create a
+     * new dedicated task in the activity manager for this voice interaction session;
+     * this means that {@link Intent#FLAG_ACTIVITY_NEW_TASK Intent.FLAG_ACTIVITY_NEW_TASK}
+     * will be set for you to make it a new task.
+     *
+     * <p>The newly started activity will be displayed to the user in a special way, as
+     * a layer under the voice interaction UI.</p>
+     *
+     * <p>As the voice activity runs, it can retrieve a {@link android.app.VoiceInteractor}
+     * through which it can perform voice interactions through your session.  These requests
+     * for voice interactions will appear as callbacks on {@link #onGetSupportedCommands},
+     * {@link #onConfirm}, {@link #onCommand}, and {@link #onCancel}.
+     *
+     * <p>You will receive a call to {@link #onTaskStarted} when the task starts up
+     * and {@link #onTaskFinished} when the last activity has finished.
+     *
+     * @param intent The Intent to start this voice interaction.  The given Intent will
+     * always have {@link Intent#CATEGORY_VOICE Intent.CATEGORY_VOICE} added to it, since
+     * this is part of a voice interaction.
+     */
     public void startVoiceActivity(Intent intent) {
         if (mToken == null) {
             throw new IllegalStateException("Can't call before onCreate()");
         }
         try {
+            intent.migrateExtraStreamToClipData();
+            intent.prepareToLeaveProcess();
             int res = mSystemService.startVoiceActivity(mToken, intent,
                     intent.resolveType(mContext.getContentResolver()));
             Instrumentation.checkStartActivityResult(res, intent);
@@ -435,14 +528,23 @@
         }
     }
 
+    /**
+     * Convenience for inflating views.
+     */
     public LayoutInflater getLayoutInflater() {
         return mInflater;
     }
 
+    /**
+     * Retrieve the window being used to show the session's UI.
+     */
     public Dialog getWindow() {
         return mWindow;
     }
 
+    /**
+     * Finish the session.
+     */
     public void finish() {
         if (mToken == null) {
             throw new IllegalStateException("Can't call before onCreate()");
@@ -454,22 +556,35 @@
         }
     }
 
+    /**
+     * Initiatize a new session.
+     *
+     * @param args The arguments that were supplied to
+     * {@link VoiceInteractionService#startSession VoiceInteractionService.startSession}.
+     */
     public void onCreate(Bundle args) {
         mTheme = mTheme != 0 ? mTheme
                 : com.android.internal.R.style.Theme_DeviceDefault_VoiceInteractionSession;
         mInflater = (LayoutInflater)mContext.getSystemService(
                 Context.LAYOUT_INFLATER_SERVICE);
         mWindow = new SoftInputWindow(mContext, "VoiceInteractionSession", mTheme,
-                mCallbacks, this, mDispatcherState, true);
+                mCallbacks, this, mDispatcherState,
+                WindowManager.LayoutParams.TYPE_VOICE_INTERACTION, Gravity.TOP, true);
         mWindow.getWindow().addFlags(WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED);
         initViews();
         mWindow.getWindow().setLayout(MATCH_PARENT, WRAP_CONTENT);
         mWindow.setToken(mToken);
     }
 
+    /**
+     * Last callback to the session as it is being finished.
+     */
     public void onDestroy() {
     }
 
+    /**
+     * Hook in which to create the session's UI.
+     */
     public View onCreateContentView() {
         return null;
     }
@@ -502,6 +617,11 @@
         finish();
     }
 
+    /**
+     * Sessions automatically watch for requests that all system UI be closed (such as when
+     * the user presses HOME), which will appear here.  The default implementation always
+     * calls {@link #finish}.
+     */
     public void onCloseSystemDialogs() {
         finish();
     }
@@ -517,20 +637,106 @@
         int[] loc = mTmpLocation;
         View decor = getWindow().getWindow().getDecorView();
         decor.getLocationInWindow(loc);
-        outInsets.contentTopInsets = loc[1];
+        outInsets.contentInsets.top = 0;
+        outInsets.contentInsets.left = 0;
+        outInsets.contentInsets.right = 0;
+        outInsets.contentInsets.bottom = 0;
         outInsets.touchableInsets = Insets.TOUCHABLE_INSETS_FRAME;
         outInsets.touchableRegion.setEmpty();
     }
 
+    /**
+     * Called when a task initiated by {@link #startVoiceActivity(android.content.Intent)}
+     * has actually started.
+     *
+     * @param intent The original {@link Intent} supplied to
+     * {@link #startVoiceActivity(android.content.Intent)}.
+     * @param taskId Unique ID of the now running task.
+     */
     public void onTaskStarted(Intent intent, int taskId) {
     }
 
+    /**
+     * Called when the last activity of a task initiated by
+     * {@link #startVoiceActivity(android.content.Intent)} has finished.  The default
+     * implementation calls {@link #finish()} on the assumption that this represents
+     * the completion of a voice action.  You can override the implementation if you would
+     * like a different behavior.
+     *
+     * @param intent The original {@link Intent} supplied to
+     * {@link #startVoiceActivity(android.content.Intent)}.
+     * @param taskId Unique ID of the finished task.
+     */
     public void onTaskFinished(Intent intent, int taskId) {
         finish();
     }
 
-    public abstract boolean[] onGetSupportedCommands(Caller caller, String[] commands);
-    public abstract void onConfirm(Caller caller, Request request, String prompt, Bundle extras);
+    /**
+     * Request to query for what extended commands the session supports.
+     *
+     * @param caller Who is making the request.
+     * @param commands An array of commands that are being queried.
+     * @return Return an array of booleans indicating which of each entry in the
+     * command array is supported.  A true entry in the array indicates the command
+     * is supported; false indicates it is not.  The default implementation returns
+     * an array of all false entries.
+     */
+    public boolean[] onGetSupportedCommands(Caller caller, String[] commands) {
+        return new boolean[commands.length];
+    }
+
+    /**
+     * Request to confirm with the user before proceeding with an unrecoverable operation,
+     * corresponding to a {@link android.app.VoiceInteractor.ConfirmationRequest
+     * VoiceInteractor.ConfirmationRequest}.
+     *
+     * @param caller Who is making the request.
+     * @param request The active request.
+     * @param prompt The prompt informing the user of what will happen, as per
+     * {@link android.app.VoiceInteractor.ConfirmationRequest VoiceInteractor.ConfirmationRequest}.
+     * @param extras Any additional information, as per
+     * {@link android.app.VoiceInteractor.ConfirmationRequest VoiceInteractor.ConfirmationRequest}.
+     */
+    public abstract void onConfirm(Caller caller, Request request, CharSequence prompt,
+            Bundle extras);
+
+    /**
+     * Request to abort the voice interaction session because the voice activity can not
+     * complete its interaction using voice.  Corresponds to
+     * {@link android.app.VoiceInteractor.AbortVoiceRequest
+     * VoiceInteractor.AbortVoiceRequest}.  The default implementation just sends an empty
+     * confirmation back to allow the activity to exit.
+     *
+     * @param caller Who is making the request.
+     * @param request The active request.
+     * @param message The message informing the user of the problem, as per
+     * {@link android.app.VoiceInteractor.AbortVoiceRequest VoiceInteractor.AbortVoiceRequest}.
+     * @param extras Any additional information, as per
+     * {@link android.app.VoiceInteractor.AbortVoiceRequest VoiceInteractor.AbortVoiceRequest}.
+     */
+    public void onAbortVoice(Caller caller, Request request, CharSequence message, Bundle extras) {
+        request.sendAbortVoiceResult(null);
+    }
+
+    /**
+     * Process an arbitrary extended command from the caller,
+     * corresponding to a {@link android.app.VoiceInteractor.CommandRequest
+     * VoiceInteractor.CommandRequest}.
+     *
+     * @param caller Who is making the request.
+     * @param request The active request.
+     * @param command The command that is being executed, as per
+     * {@link android.app.VoiceInteractor.CommandRequest VoiceInteractor.CommandRequest}.
+     * @param extras Any additional information, as per
+     * {@link android.app.VoiceInteractor.CommandRequest VoiceInteractor.CommandRequest}.
+     */
     public abstract void onCommand(Caller caller, Request request, String command, Bundle extras);
+
+    /**
+     * Called when the {@link android.app.VoiceInteractor} has asked to cancel a {@link Request}
+     * that was previously delivered to {@link #onConfirm} or {@link #onCommand}.
+     *
+     * @param request The request that is being canceled.
+     */
     public abstract void onCancel(Request request);
 }
diff --git a/core/java/android/speech/tts/Markup.java b/core/java/android/speech/tts/Markup.java
new file mode 100644
index 0000000..c886e5d
--- /dev/null
+++ b/core/java/android/speech/tts/Markup.java
@@ -0,0 +1,537 @@
+package android.speech.tts;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.SortedMap;
+import java.util.TreeMap;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import android.os.Bundle;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/**
+ * A class that provides markup to a synthesis request to control aspects of speech.
+ * <p>
+ * Markup itself is a feature agnostic data format; the {@link Utterance} class defines the currently
+ * available set of features and should be used to construct instances of the Markup class.
+ * </p>
+ * <p>
+ * A marked up sentence is a tree. Each node has a type, an optional plain text, a set of
+ * parameters, and a list of children.
+ * The <b>type</b> defines what it contains, e.g. "text", "date", "measure", etc. A Markup node
+ * can be either a part of sentence (often a leaf node), or node altering some property of its
+ * children (node with children). The top level node has to be of type "utterance" and its children
+ * are synthesized in order.
+ * The <b>plain text</b> is optional except for the top level node. If the synthesis engine does not
+ * support Markup at all, it should use the plain text of the top level node. If an engine does not
+ * recognize or support a node type, it will try to use the plain text of that node if provided. If
+ * the plain text is null, it will synthesize its children in order.
+ * <b>Parameters</b> are key-value pairs specific to each node type. In case of a date node the
+ * parameters may be for example "month: 7" and "day: 10".
+ * The <b>nested markups</b> are children and can for example be used to nest semiotic classes (a
+ * measure may have a node of type "decimal" as its child) or to modify some property of its
+ * children. See "plain text" on how they are processed if the parent of the children is unknown to
+ * the engine.
+ * <p>
+ */
+public final class Markup implements Parcelable {
+
+    private String mType;
+    private String mPlainText;
+
+    private Bundle mParameters = new Bundle();
+    private List<Markup> mNestedMarkups = new ArrayList<Markup>();
+
+    private static final String TYPE = "type";
+    private static final String PLAIN_TEXT = "plain_text";
+    private static final String MARKUP = "markup";
+
+    private static final String IDENTIFIER_REGEX = "([0-9a-z_]+)";
+    private static final Pattern legalIdentifierPattern = Pattern.compile(IDENTIFIER_REGEX);
+
+    /**
+     * Constructs an empty markup.
+     */
+    public Markup() {}
+
+    /**
+     * Constructs a markup of the given type.
+     */
+    public Markup(String type) {
+        setType(type);
+    }
+
+    /**
+     * Returns the type of this node; can be null.
+     */
+    public String getType() {
+        return mType;
+    }
+
+    /**
+     * Sets the type of this node. can be null. May only contain [0-9a-z_].
+     */
+    public void setType(String type) {
+        if (type != null) {
+            Matcher matcher = legalIdentifierPattern.matcher(type);
+            if (!matcher.matches()) {
+                throw new IllegalArgumentException("Type cannot be empty and may only contain " +
+                                                   "0-9, a-z and underscores.");
+            }
+        }
+        mType = type;
+    }
+
+    /**
+     * Returns this node's plain text; can be null.
+     */
+    public String getPlainText() {
+        return mPlainText;
+    }
+
+    /**
+     * Sets this nodes's plain text; can be null.
+     */
+    public void setPlainText(String plainText) {
+        mPlainText = plainText;
+    }
+
+    /**
+     * Adds or modifies a parameter.
+     * @param key The key; may only contain [0-9a-z_] and cannot be "type" or "plain_text".
+     * @param value The value.
+     * @throws An {@link IllegalArgumentException} if the key is null or empty.
+     * @return this
+     */
+    public Markup setParameter(String key, String value) {
+        if (key == null || key.isEmpty()) {
+            throw new IllegalArgumentException("Key cannot be null or empty.");
+        }
+        if (key.equals("type")) {
+            throw new IllegalArgumentException("Key cannot be \"type\".");
+        }
+        if (key.equals("plain_text")) {
+            throw new IllegalArgumentException("Key cannot be \"plain_text\".");
+        }
+        Matcher matcher = legalIdentifierPattern.matcher(key);
+        if (!matcher.matches()) {
+            throw new IllegalArgumentException("Key may only contain 0-9, a-z and underscores.");
+        }
+
+        if (value != null) {
+            mParameters.putString(key, value);
+        } else {
+            removeParameter(key);
+        }
+        return this;
+    }
+
+    /**
+     * Removes the parameter with the given key
+     */
+    public void removeParameter(String key) {
+        mParameters.remove(key);
+    }
+
+    /**
+     * Returns the value of the parameter.
+     * @param key The parameter key.
+     * @return The value of the parameter or null if the parameter is not set.
+     */
+    public String getParameter(String key) {
+        return mParameters.getString(key);
+    }
+
+    /**
+     * Returns the number of parameters that have been set.
+     */
+    public int parametersSize() {
+        return mParameters.size();
+    }
+
+    /**
+     * Appends a child to the list of children
+     * @param markup The child.
+     * @return This instance.
+     * @throws {@link IllegalArgumentException} if markup is null.
+     */
+    public Markup addNestedMarkup(Markup markup) {
+        if (markup == null) {
+            throw new IllegalArgumentException("Nested markup cannot be null");
+        }
+        mNestedMarkups.add(markup);
+        return this;
+    }
+
+    /**
+     * Removes the given node from its children.
+     * @param markup The child to remove.
+     * @return True if this instance was modified by this operation, false otherwise.
+     */
+    public boolean removeNestedMarkup(Markup markup) {
+        return mNestedMarkups.remove(markup);
+    }
+
+    /**
+     * Returns the index'th child.
+     * @param i The index of the child.
+     * @return The child.
+     * @throws {@link IndexOutOfBoundsException} if i < 0 or i >= nestedMarkupSize()
+     */
+    public Markup getNestedMarkup(int i) {
+        return mNestedMarkups.get(i);
+    }
+
+
+    /**
+     * Returns the number of children.
+     */
+    public int nestedMarkupSize() {
+        return mNestedMarkups.size();
+    }
+
+    /**
+     * Returns a string representation of this Markup instance. Can be deserialized back to a Markup
+     * instance with markupFromString().
+     */
+    public String toString() {
+        StringBuilder out = new StringBuilder();
+        if (mType != null) {
+            out.append(TYPE + ": \"" + mType + "\"");
+        }
+        if (mPlainText != null) {
+            out.append(out.length() > 0 ? " " : "");
+            out.append(PLAIN_TEXT + ": \"" + escapeQuotedString(mPlainText) + "\"");
+        }
+        // Sort the parameters alphabetically by key so we have a stable output.
+        SortedMap<String, String> sortedMap = new TreeMap<String, String>();
+        for (String key : mParameters.keySet()) {
+            sortedMap.put(key, mParameters.getString(key));
+        }
+        for (Map.Entry<String, String> entry : sortedMap.entrySet()) {
+            out.append(out.length() > 0 ? " " : "");
+            out.append(entry.getKey() + ": \"" + escapeQuotedString(entry.getValue()) + "\"");
+        }
+        for (Markup m : mNestedMarkups) {
+            out.append(out.length() > 0 ? " " : "");
+            String nestedStr = m.toString();
+            if (nestedStr.isEmpty()) {
+                out.append(MARKUP + " {}");
+            } else {
+                out.append(MARKUP + " { " + m.toString() + " }");
+            }
+        }
+        return out.toString();
+    }
+
+    /**
+     * Escapes backslashes and double quotes in the plain text and parameter values before this
+     * instance is written to a string.
+     * @param str The string to escape.
+     * @return The escaped string.
+     */
+    private static String escapeQuotedString(String str) {
+        StringBuilder out = new StringBuilder();
+        for (int i = 0; i < str.length(); i++) {
+            char c = str.charAt(i);
+            if (c == '"') {
+                out.append("\\\"");
+            } else if (str.charAt(i) == '\\') {
+                out.append("\\\\");
+            } else {
+                out.append(c);
+            }
+        }
+        return out.toString();
+    }
+
+    /**
+     * The reverse of the escape method, returning plain text and parameter values to their original
+     * form.
+     * @param str An escaped string.
+     * @return The unescaped string.
+     */
+    private static String unescapeQuotedString(String str) {
+        StringBuilder out = new StringBuilder();
+        for (int i = 0; i < str.length(); i++) {
+            char c = str.charAt(i);
+            if (c == '\\') {
+                i++;
+                if (i >= str.length()) {
+                    throw new IllegalArgumentException("Unterminated escape sequence in string: " +
+                                                       str);
+                }
+                c = str.charAt(i);
+                if (c == '\\') {
+                    out.append("\\");
+                } else if (c == '"') {
+                    out.append("\"");
+                } else {
+                    throw new IllegalArgumentException("Unsupported escape sequence: \\" + c +
+                                                       " in string " + str);
+                }
+            } else {
+                out.append(c);
+            }
+        }
+        return out.toString();
+    }
+
+    /**
+     * Returns true if the given string consists only of whitespace.
+     * @param str The string to check.
+     * @return True if the given string consists only of whitespace.
+     */
+    private static boolean isWhitespace(String str) {
+        return Pattern.matches("\\s*", str);
+    }
+
+    /**
+     * Parses the given string, and overrides the values of this instance with those contained
+     * in the given string.
+     * @param str The string to parse; can have superfluous whitespace.
+     * @return An empty string on success, else the remainder of the string that could not be
+     *     parsed.
+     */
+    private String fromReadableString(String str) {
+        while (!isWhitespace(str)) {
+            String newStr = matchValue(str);
+            if (newStr == null) {
+                newStr = matchMarkup(str);
+
+                if (newStr == null) {
+                    return str;
+                }
+            }
+            str = newStr;
+        }
+        return "";
+    }
+
+    // Matches: key : "value"
+    // where key is an identifier and value can contain escaped quotes
+    // there may be superflouous whitespace
+    // The value string may contain quotes and backslashes.
+    private static final String OPTIONAL_WHITESPACE = "\\s*";
+    private static final String VALUE_REGEX = "((\\\\.|[^\\\"])*)";
+    private static final String KEY_VALUE_REGEX =
+            "\\A" + OPTIONAL_WHITESPACE +                                         // start of string
+            IDENTIFIER_REGEX + OPTIONAL_WHITESPACE + ":" + OPTIONAL_WHITESPACE +  // key:
+            "\"" + VALUE_REGEX + "\"";                                            // "value"
+    private static final Pattern KEY_VALUE_PATTERN = Pattern.compile(KEY_VALUE_REGEX);
+
+    /**
+     * Tries to match a key-value pair at the start of the string. If found, add that as a parameter
+     * of this instance.
+     * @param str The string to parse.
+     * @return The remainder of the string without the parsed key-value pair on success, else null.
+     */
+    private String matchValue(String str) {
+        // Matches: key: "value"
+        Matcher matcher = KEY_VALUE_PATTERN.matcher(str);
+        if (!matcher.find()) {
+            return null;
+        }
+        String key = matcher.group(1);
+        String value = matcher.group(2);
+
+        if (key == null || value == null) {
+            return null;
+        }
+        String unescapedValue = unescapeQuotedString(value);
+        if (key.equals(TYPE)) {
+            this.mType = unescapedValue;
+        } else if (key.equals(PLAIN_TEXT)) {
+            this.mPlainText = unescapedValue;
+        } else {
+            setParameter(key, unescapedValue);
+        }
+
+        return str.substring(matcher.group(0).length());
+    }
+
+    // matches 'markup {'
+    private static final Pattern OPEN_MARKUP_PATTERN =
+            Pattern.compile("\\A" + OPTIONAL_WHITESPACE + MARKUP + OPTIONAL_WHITESPACE + "\\{");
+    // matches '}'
+    private static final Pattern CLOSE_MARKUP_PATTERN =
+            Pattern.compile("\\A" + OPTIONAL_WHITESPACE + "\\}");
+
+    /**
+     * Tries to parse a Markup specification from the start of the string. If so, add that markup to
+     * the list of nested Markup's of this instance.
+     * @param str The string to parse.
+     * @return The remainder of the string without the parsed Markup on success, else null.
+     */
+    private String matchMarkup(String str) {
+        // find and strip "markup {"
+        Matcher matcher = OPEN_MARKUP_PATTERN.matcher(str);
+
+        if (!matcher.find()) {
+            return null;
+        }
+        String strRemainder = str.substring(matcher.group(0).length());
+        // parse and strip markup contents
+        Markup nestedMarkup = new Markup();
+        strRemainder = nestedMarkup.fromReadableString(strRemainder);
+
+        // find and strip "}"
+        Matcher matcherClose = CLOSE_MARKUP_PATTERN.matcher(strRemainder);
+        if (!matcherClose.find()) {
+            return null;
+        }
+        strRemainder = strRemainder.substring(matcherClose.group(0).length());
+
+        // Everything parsed, add markup
+        this.addNestedMarkup(nestedMarkup);
+
+        // Return remainder
+        return strRemainder;
+    }
+
+    /**
+     * Returns a Markup instance from the string representation generated by toString().
+     * @param string The string representation generated by toString().
+     * @return The new Markup instance.
+     * @throws {@link IllegalArgumentException} if the input cannot be correctly parsed.
+     */
+    public static Markup markupFromString(String string) throws IllegalArgumentException {
+        Markup m = new Markup();
+        if (m.fromReadableString(string).isEmpty()) {
+            return m;
+        } else {
+            throw new IllegalArgumentException("Cannot parse input to Markup");
+        }
+    }
+
+    /**
+     * Compares the specified object with this Markup for equality.
+     * @return True if the given object is a Markup instance with the same type, plain text,
+     * parameters and the nested markups are also equal to each other and in the same order.
+     */
+    @Override
+    public boolean equals(Object o) {
+        if ( this == o ) return true;
+        if ( !(o instanceof Markup) ) return false;
+        Markup m = (Markup) o;
+
+        if (nestedMarkupSize() != this.nestedMarkupSize()) {
+            return false;
+        }
+
+        if (!(mType == null ? m.mType == null : mType.equals(m.mType))) {
+            return false;
+        }
+        if (!(mPlainText == null ? m.mPlainText == null : mPlainText.equals(m.mPlainText))) {
+            return false;
+        }
+        if (!equalBundles(mParameters, m.mParameters)) {
+            return false;
+        }
+
+        for (int i = 0; i < this.nestedMarkupSize(); i++) {
+            if (!mNestedMarkups.get(i).equals(m.mNestedMarkups.get(i))) {
+                return false;
+            }
+        }
+
+        return true;
+    }
+
+    /**
+     * Checks if two bundles are equal to each other. Used by equals(o).
+     */
+    private boolean equalBundles(Bundle one, Bundle two) {
+        if (one == null || two == null) {
+            return false;
+        }
+
+        if(one.size() != two.size()) {
+            return false;
+        }
+
+        Set<String> valuesOne = one.keySet();
+        for(String key : valuesOne) {
+            Object valueOne = one.get(key);
+            Object valueTwo = two.get(key);
+            if (valueOne instanceof Bundle && valueTwo instanceof Bundle &&
+                !equalBundles((Bundle) valueOne, (Bundle) valueTwo)) {
+                return false;
+            } else if (valueOne == null) {
+                if (valueTwo != null || !two.containsKey(key)) {
+                    return false;
+                }
+            } else if(!valueOne.equals(valueTwo)) {
+                return false;
+            }
+        }
+        return true;
+    }
+
+    /**
+     * Returns an unmodifiable list of the children.
+     * @return An unmodifiable list of children that throws an {@link UnsupportedOperationException}
+     *     if an attempt is made to modify it
+     */
+    public List<Markup> getNestedMarkups() {
+        return Collections.unmodifiableList(mNestedMarkups);
+    }
+
+    /**
+     * @hide
+     */
+    public Markup(Parcel in) {
+        mType = in.readString();
+        mPlainText = in.readString();
+        mParameters = in.readBundle();
+        in.readList(mNestedMarkups, Markup.class.getClassLoader());
+    }
+
+    /**
+     * Creates a deep copy of the given markup.
+     */
+    public Markup(Markup markup) {
+        mType = markup.mType;
+        mPlainText = markup.mPlainText;
+        mParameters = markup.mParameters;
+        for (Markup nested : markup.getNestedMarkups()) {
+            addNestedMarkup(new Markup(nested));
+        }
+    }
+
+    /**
+     * @hide
+     */
+    public int describeContents() {
+        return 0;
+    }
+
+    /**
+     * @hide
+     */
+    public void writeToParcel(Parcel dest, int flags) {
+        dest.writeString(mType);
+        dest.writeString(mPlainText);
+        dest.writeBundle(mParameters);
+        dest.writeList(mNestedMarkups);
+    }
+
+    /**
+     * @hide
+     */
+    public static final Parcelable.Creator CREATOR = new Parcelable.Creator() {
+        public Markup createFromParcel(Parcel in) {
+            return new Markup(in);
+        }
+
+        public Markup[] newArray(int size) {
+            return new Markup[size];
+        }
+    };
+}
+
diff --git a/core/java/android/speech/tts/SynthesisRequestV2.java b/core/java/android/speech/tts/SynthesisRequestV2.java
index a1da49c..130e3f9 100644
--- a/core/java/android/speech/tts/SynthesisRequestV2.java
+++ b/core/java/android/speech/tts/SynthesisRequestV2.java
@@ -4,11 +4,12 @@
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.speech.tts.TextToSpeechClient.UtteranceId;
+import android.util.Log;
 
 /**
  * Service-side representation of a synthesis request from a V2 API client. Contains:
  * <ul>
- *   <li>The utterance to synthesize</li>
+ *   <li>The markup object to synthesize containing the utterance.</li>
  *   <li>The id of the utterance (String, result of {@link UtteranceId#toUniqueString()}</li>
  *   <li>The synthesis voice name (String, result of {@link VoiceInfo#getName()})</li>
  *   <li>Voice parameters (Bundle of parameters)</li>
@@ -16,8 +17,11 @@
  * </ul>
  */
 public final class SynthesisRequestV2 implements Parcelable {
-    /** Synthesis utterance. */
-    private final String mText;
+
+    private static final String TAG = "SynthesisRequestV2";
+
+    /** Synthesis markup */
+    private final Markup mMarkup;
 
     /** Synthesis id. */
     private final String mUtteranceId;
@@ -34,9 +38,9 @@
     /**
      * Constructor for test purposes.
      */
-    public SynthesisRequestV2(String text, String utteranceId, String voiceName,
+    public SynthesisRequestV2(Markup markup, String utteranceId, String voiceName,
             Bundle voiceParams, Bundle audioParams) {
-        this.mText = text;
+        this.mMarkup = markup;
         this.mUtteranceId = utteranceId;
         this.mVoiceName = voiceName;
         this.mVoiceParams = voiceParams;
@@ -49,15 +53,18 @@
      * @hide
      */
     public SynthesisRequestV2(Parcel in) {
-        this.mText = in.readString();
+        this.mMarkup = (Markup) in.readValue(Markup.class.getClassLoader());
         this.mUtteranceId = in.readString();
         this.mVoiceName = in.readString();
         this.mVoiceParams = in.readBundle();
         this.mAudioParams = in.readBundle();
     }
 
-    SynthesisRequestV2(String text, String utteranceId, RequestConfig rconfig) {
-        this.mText = text;
+    /**
+     * Constructor to request the synthesis of a sentence.
+     */
+    SynthesisRequestV2(Markup markup, String utteranceId, RequestConfig rconfig) {
+        this.mMarkup = markup;
         this.mUtteranceId = utteranceId;
         this.mVoiceName = rconfig.getVoice().getName();
         this.mVoiceParams = rconfig.getVoiceParams();
@@ -71,7 +78,7 @@
      */
     @Override
     public void writeToParcel(Parcel dest, int flags) {
-        dest.writeString(mText);
+        dest.writeValue(mMarkup);
         dest.writeString(mUtteranceId);
         dest.writeString(mVoiceName);
         dest.writeBundle(mVoiceParams);
@@ -82,7 +89,18 @@
      * @return the text which should be synthesized.
      */
     public String getText() {
-        return mText;
+        if (mMarkup.getPlainText() == null) {
+            Log.e(TAG, "Plaintext of markup is null.");
+            return "";
+        }
+        return mMarkup.getPlainText();
+    }
+
+    /**
+     * @return the markup which should be synthesized.
+     */
+    public Markup getMarkup() {
+        return mMarkup;
     }
 
     /**
diff --git a/core/java/android/speech/tts/TextToSpeechClient.java b/core/java/android/speech/tts/TextToSpeechClient.java
index 85f702b..0c0be83 100644
--- a/core/java/android/speech/tts/TextToSpeechClient.java
+++ b/core/java/android/speech/tts/TextToSpeechClient.java
@@ -512,7 +512,6 @@
         }
     }
 
-
     /**
      * Connects the client to TTS service. This method returns immediately, and connects to the
      * service in the background.
@@ -794,15 +793,14 @@
             return mService != null && mEstablished;
         }
 
-        boolean runAction(Action action) {
+        <T> ActionResult<T> runAction(Action<T> action) {
             synchronized (mLock) {
                 try {
-                    action.run(mService);
-                    return true;
+                    return new ActionResult<T>(true, action.run(mService));
                 } catch (Exception ex) {
                     Log.e(TAG, action.getName() + " failed", ex);
                     disconnect();
-                    return false;
+                    return new ActionResult<T>(false);
                 }
             }
         }
@@ -822,7 +820,7 @@
         }
     }
 
-    private abstract class Action {
+    private abstract class Action<T> {
         private final String mName;
 
         public Action(String name) {
@@ -830,7 +828,21 @@
         }
 
         public String getName() {return mName;}
-        abstract void run(ITextToSpeechService service) throws RemoteException;
+        abstract T run(ITextToSpeechService service) throws RemoteException;
+    }
+
+    private class ActionResult<T> {
+        boolean mSuccess;
+        T mResult;
+
+        ActionResult(boolean success) {
+            mSuccess = success;
+        }
+
+        ActionResult(boolean success, T result) {
+            mSuccess = success;
+            mResult = result;
+        }
     }
 
     private IBinder getCallerIdentity() {
@@ -840,18 +852,17 @@
         return null;
     }
 
-    private boolean runAction(Action action) {
+    private <T> ActionResult<T> runAction(Action<T> action) {
         synchronized (mLock) {
             if (mServiceConnection == null) {
                 Log.w(TAG, action.getName() + " failed: not bound to TTS engine");
-                return false;
+                return new ActionResult<T>(false);
             }
             if (!mServiceConnection.isEstablished()) {
                 Log.w(TAG, action.getName() + " failed: not fully bound to TTS engine");
-                return false;
+                return new ActionResult<T>(false);
             }
-            mServiceConnection.runAction(action);
-            return true;
+            return mServiceConnection.runAction(action);
         }
     }
 
@@ -862,13 +873,14 @@
      * other utterances in the queue.
      */
     public void stop() {
-        runAction(new Action(ACTION_STOP_NAME) {
+        runAction(new Action<Void>(ACTION_STOP_NAME) {
             @Override
-            public void run(ITextToSpeechService service) throws RemoteException {
+            public Void run(ITextToSpeechService service) throws RemoteException {
                if (service.stop(getCallerIdentity()) != Status.SUCCESS) {
                    Log.e(TAG, "Stop failed");
                }
                mCallbacks.clear();
+               return null;
             }
         });
     }
@@ -876,7 +888,7 @@
     private static final String ACTION_QUEUE_SPEAK_NAME = "queueSpeak";
 
     /**
-     * Speaks the string using the specified queuing strategy using current
+     * Speaks the string using the specified queuing strategy and the current
      * voice. This method is asynchronous, i.e. the method just adds the request
      * to the queue of TTS requests and then returns. The synthesis might not
      * have finished (or even started!) at the time when this method returns.
@@ -887,15 +899,38 @@
      *            in {@link RequestCallbacks}.
      * @param config Synthesis request configuration. Can't be null. Has to contain a
      *            voice.
-     * @param callbacks Synthesis request callbacks. If null, default request
+     * @param callbacks Synthesis request callbacks. If null, the default request
      *            callbacks object will be used.
      */
     public void queueSpeak(final String utterance, final UtteranceId utteranceId,
             final RequestConfig config,
             final RequestCallbacks callbacks) {
-        runAction(new Action(ACTION_QUEUE_SPEAK_NAME) {
+        queueSpeak(createMarkupFromString(utterance), utteranceId, config, callbacks);
+    }
+
+    /**
+     * Speaks the {@link Markup} (which can be constructed with {@link Utterance}) using
+     * the specified queuing strategy and the current voice. This method is
+     * asynchronous, i.e. the method just adds the request to the queue of TTS
+     * requests and then returns. The synthesis might not have finished (or even
+     * started!) at the time when this method returns.
+     *
+     * @param markup The Markup to be spoken. The written equivalent of the spoken
+     *            text should be no longer than 1000 characters.
+     * @param utteranceId Unique identificator used to track the synthesis progress
+     *            in {@link RequestCallbacks}.
+     * @param config Synthesis request configuration. Can't be null. Has to contain a
+     *            voice.
+     * @param callbacks Synthesis request callbacks. If null, the default request
+     *            callbacks object will be used.
+     */
+    public void queueSpeak(final Markup markup,
+            final UtteranceId utteranceId,
+            final RequestConfig config,
+            final RequestCallbacks callbacks) {
+        runAction(new Action<Void>(ACTION_QUEUE_SPEAK_NAME) {
             @Override
-            public void run(ITextToSpeechService service) throws RemoteException {
+            public Void run(ITextToSpeechService service) throws RemoteException {
                 RequestCallbacks c = mDefaultRequestCallbacks;
                 if (callbacks != null) {
                     c = callbacks;
@@ -903,15 +938,16 @@
                 int addCallbackStatus = addCallback(utteranceId, c);
                 if (addCallbackStatus != Status.SUCCESS) {
                     c.onSynthesisFailure(utteranceId, Status.ERROR_INVALID_REQUEST);
-                    return;
+                    return null;
                 }
 
                 int queueResult = service.speakV2(
                         getCallerIdentity(),
-                        new SynthesisRequestV2(utterance, utteranceId.toUniqueString(), config));
+                        new SynthesisRequestV2(markup, utteranceId.toUniqueString(), config));
                 if (queueResult != Status.SUCCESS) {
                     removeCallbackAndErr(utteranceId.toUniqueString(), queueResult);
                 }
+                return null;
             }
         });
     }
@@ -931,15 +967,40 @@
      * @param outputFile File to write the generated audio data to.
      * @param config Synthesis request configuration. Can't be null. Have to contain a
      *            voice.
-     * @param callbacks Synthesis request callbacks. If null, default request
+     * @param callbacks Synthesis request callbacks. If null, the default request
      *            callbacks object will be used.
      */
     public void queueSynthesizeToFile(final String utterance, final UtteranceId utteranceId,
             final File outputFile, final RequestConfig config,
             final RequestCallbacks callbacks) {
-        runAction(new Action(ACTION_QUEUE_SYNTHESIZE_TO_FILE) {
+        queueSynthesizeToFile(createMarkupFromString(utterance), utteranceId, outputFile, config, callbacks);
+    }
+
+    /**
+     * Synthesizes the given {@link Markup} (can be constructed with {@link Utterance})
+     * to a file using the specified parameters. This method is asynchronous, i.e. the
+     * method just adds the request to the queue of TTS requests and then returns. The
+     * synthesis might not have finished (or even started!) at the time when this method
+     * returns.
+     *
+     * @param markup The Markup that should be synthesized. The written equivalent of
+     *            the spoken text should be no longer than 1000 characters.
+     * @param utteranceId Unique identificator used to track the synthesis progress
+     *            in {@link RequestCallbacks}.
+     * @param outputFile File to write the generated audio data to.
+     * @param config Synthesis request configuration. Can't be null. Have to contain a
+     *            voice.
+     * @param callbacks Synthesis request callbacks. If null, the default request
+     *            callbacks object will be used.
+     */
+    public void queueSynthesizeToFile(
+            final Markup markup,
+            final UtteranceId utteranceId,
+            final File outputFile, final RequestConfig config,
+            final RequestCallbacks callbacks) {
+        runAction(new Action<Void>(ACTION_QUEUE_SYNTHESIZE_TO_FILE) {
             @Override
-            public void run(ITextToSpeechService service) throws RemoteException {
+            public Void run(ITextToSpeechService service) throws RemoteException {
                 RequestCallbacks c = mDefaultRequestCallbacks;
                 if (callbacks != null) {
                     c = callbacks;
@@ -947,7 +1008,7 @@
                 int addCallbackStatus = addCallback(utteranceId, c);
                 if (addCallbackStatus != Status.SUCCESS) {
                     c.onSynthesisFailure(utteranceId, Status.ERROR_INVALID_REQUEST);
-                    return;
+                    return null;
                 }
 
                 ParcelFileDescriptor fileDescriptor = null;
@@ -955,7 +1016,7 @@
                     if (outputFile.exists() && !outputFile.canWrite()) {
                         Log.e(TAG, "No permissions to write to " + outputFile);
                         removeCallbackAndErr(utteranceId.toUniqueString(), Status.ERROR_OUTPUT);
-                        return;
+                        return null;
                     }
                     fileDescriptor = ParcelFileDescriptor.open(outputFile,
                             ParcelFileDescriptor.MODE_WRITE_ONLY |
@@ -964,8 +1025,7 @@
 
                     int queueResult = service.synthesizeToFileDescriptorV2(getCallerIdentity(),
                             fileDescriptor,
-                            new SynthesisRequestV2(utterance, utteranceId.toUniqueString(),
-                                    config));
+                            new SynthesisRequestV2(markup, utteranceId.toUniqueString(), config));
                     fileDescriptor.close();
                     if (queueResult != Status.SUCCESS) {
                         removeCallbackAndErr(utteranceId.toUniqueString(), queueResult);
@@ -977,10 +1037,18 @@
                     Log.e(TAG, "Closing file " + outputFile + " failed", e);
                     removeCallbackAndErr(utteranceId.toUniqueString(), Status.ERROR_OUTPUT);
                 }
+                return null;
             }
         });
     }
 
+    private static Markup createMarkupFromString(String str) {
+        return new Utterance()
+            .append(new Utterance.TtsText(str))
+            .setNoWarningOnFallback(true)
+            .createMarkup();
+    }
+
     private static final String ACTION_QUEUE_SILENCE_NAME = "queueSilence";
 
     /**
@@ -997,9 +1065,9 @@
      */
     public void queueSilence(final long durationInMs, final UtteranceId utteranceId,
             final RequestCallbacks callbacks) {
-        runAction(new Action(ACTION_QUEUE_SILENCE_NAME) {
+        runAction(new Action<Void>(ACTION_QUEUE_SILENCE_NAME) {
             @Override
-            public void run(ITextToSpeechService service) throws RemoteException {
+            public Void run(ITextToSpeechService service) throws RemoteException {
                 RequestCallbacks c = mDefaultRequestCallbacks;
                 if (callbacks != null) {
                     c = callbacks;
@@ -1015,6 +1083,7 @@
                 if (queueResult != Status.SUCCESS) {
                     removeCallbackAndErr(utteranceId.toUniqueString(), queueResult);
                 }
+                return null;
             }
         });
     }
@@ -1038,9 +1107,9 @@
      */
     public void queueAudio(final Uri audioUrl, final UtteranceId utteranceId,
             final RequestConfig config, final RequestCallbacks callbacks) {
-        runAction(new Action(ACTION_QUEUE_AUDIO_NAME) {
+        runAction(new Action<Void>(ACTION_QUEUE_AUDIO_NAME) {
             @Override
-            public void run(ITextToSpeechService service) throws RemoteException {
+            public Void run(ITextToSpeechService service) throws RemoteException {
                 RequestCallbacks c = mDefaultRequestCallbacks;
                 if (callbacks != null) {
                     c = callbacks;
@@ -1056,10 +1125,35 @@
                 if (queueResult != Status.SUCCESS) {
                     removeCallbackAndErr(utteranceId.toUniqueString(), queueResult);
                 }
+                return null;
             }
         });
     }
 
+    private static final String ACTION_IS_SPEAKING_NAME = "isSpeaking";
+
+    /**
+     * Checks whether the TTS engine is busy speaking. Note that a speech item is
+     * considered complete once it's audio data has been sent to the audio mixer, or
+     * written to a file. There might be a finite lag between this point, and when
+     * the audio hardware completes playback.
+     *
+     * @return {@code true} if the TTS engine is speaking.
+     */
+    public boolean isSpeaking() {
+        ActionResult<Boolean> result = runAction(new Action<Boolean>(ACTION_IS_SPEAKING_NAME) {
+            @Override
+            public Boolean run(ITextToSpeechService service) throws RemoteException {
+                return service.isSpeaking();
+            }
+        });
+        if (!result.mSuccess) {
+            return false; // We can't really say, return false
+        }
+        return result.mResult;
+    }
+
+
     class InternalHandler extends Handler {
         final static int WHAT_ENGINE_STATUS_CHANGED = 1;
         final static int WHAT_SERVICE_DISCONNECTED = 2;
diff --git a/core/java/android/speech/tts/TextToSpeechService.java b/core/java/android/speech/tts/TextToSpeechService.java
index 6b899d9..14a4024 100644
--- a/core/java/android/speech/tts/TextToSpeechService.java
+++ b/core/java/android/speech/tts/TextToSpeechService.java
@@ -352,6 +352,12 @@
             params.putString(TextToSpeech.Engine.KEY_FEATURE_EMBEDDED_SYNTHESIS, "true");
         }
 
+        String noWarning = request.getMarkup().getParameter(Utterance.KEY_NO_WARNING_ON_FALLBACK);
+        if (noWarning == null || noWarning.equals("false")) {
+            Log.w("TextToSpeechService", "The synthesis engine does not support Markup, falling " +
+                                         "back to the given plain text.");
+        }
+
         // Build V1 request
         SynthesisRequest requestV1 = new SynthesisRequest(request.getText(), params);
         Locale locale = selectedVoice.getLocale();
@@ -856,14 +862,53 @@
             }
         }
 
+        /**
+         * Estimate of the character count equivalent of a Markup instance. Calculated
+         * by summing the characters of all Markups of type "text". Each other node
+         * is counted as a single character, as the character count of other nodes
+         * is non-trivial to calculate and we don't want to accept arbitrarily large
+         * requests.
+         */
+        private int estimateSynthesisLengthFromMarkup(Markup m) {
+            int size = 0;
+            if (m.getType() != null &&
+                m.getType().equals("text") &&
+                m.getParameter("text") != null) {
+                size += m.getParameter("text").length();
+            } else if (m.getType() == null ||
+                       !m.getType().equals("utterance")) {
+                size += 1;
+            }
+            for (Markup nested : m.getNestedMarkups()) {
+                size += estimateSynthesisLengthFromMarkup(nested);
+            }
+            return size;
+        }
+
         @Override
         public boolean isValid() {
-            if (mSynthesisRequest.getText() == null) {
-                Log.e(TAG, "null synthesis text");
+            if (mSynthesisRequest.getMarkup() == null) {
+                Log.e(TAG, "No markup in request.");
                 return false;
             }
-            if (mSynthesisRequest.getText().length() >= TextToSpeech.getMaxSpeechInputLength()) {
-                Log.w(TAG, "Text too long: " + mSynthesisRequest.getText().length() + " chars");
+            String type = mSynthesisRequest.getMarkup().getType();
+            if (type == null) {
+                Log.w(TAG, "Top level markup node should have type \"utterance\", not null");
+                return false;
+            } else if (!type.equals("utterance")) {
+                Log.w(TAG, "Top level markup node should have type \"utterance\" instead of " +
+                            "\"" + type + "\"");
+                return false;
+            }
+
+            int estimate = estimateSynthesisLengthFromMarkup(mSynthesisRequest.getMarkup());
+            if (estimate >= TextToSpeech.getMaxSpeechInputLength()) {
+                Log.w(TAG, "Text too long: estimated size of text was " + estimate + " chars.");
+                return false;
+            }
+
+            if (estimate <= 0) {
+                Log.e(TAG, "null synthesis text");
                 return false;
             }
 
diff --git a/core/java/android/speech/tts/Utterance.java b/core/java/android/speech/tts/Utterance.java
new file mode 100644
index 0000000..0a29283
--- /dev/null
+++ b/core/java/android/speech/tts/Utterance.java
@@ -0,0 +1,595 @@
+package android.speech.tts;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * This class acts as a builder for {@link Markup} instances.
+ * <p>
+ * Each Utterance consists of a list of the semiotic classes ({@link Utterance.TtsCardinal} and
+ * {@link Utterance.TtsText}).
+ * <p>Each semiotic class can be supplied with morphosyntactic features
+ * (gender, animacy, multiplicity and case), it is up to the synthesis engine to use this
+ * information during synthesis.
+ * Examples where morphosyntactic features matter:
+ * <ul>
+ * <li>In French, the number one is verbalized differently based on the gender of the noun
+ * it modifies. "un homme" (one man) versus "une femme" (one woman).
+ * <li>In German the grammatical case (accusative, locative, etc) needs to be included to be
+ * verbalize correctly. In German you'd have the sentence "Sie haben 1 kilometer vor Ihnen" (You
+ * have 1 kilometer ahead of you), "1" in this case needs to become inflected to the accusative
+ * form ("einen") instead of the nominative form "ein".
+ * </p>
+ * <p>
+ * Utterance usage example:
+ * Markup m1 = new Utterance().append("The Eiffel Tower is")
+ *                            .append(new TtsCardinal(324))
+ *                            .append("meters tall.");
+ * Markup m2 = new Utterance().append("Sie haben")
+ *                            .append(new TtsCardinal(1).setGender(Utterance.GENDER_MALE)
+ *                            .append("Tag frei.");
+ * </p>
+ */
+public class Utterance {
+
+    /***
+     * Toplevel type of markup representation.
+     */
+    public static final String TYPE_UTTERANCE = "utterance";
+    /***
+     * The no_warning_on_fallback parameter can be set to "false" or "true", true indicating that
+     * no warning will be given when the synthesizer does not support Markup. This is used when
+     * the user only provides a string to the API instead of a markup.
+     */
+    public static final String KEY_NO_WARNING_ON_FALLBACK = "no_warning_on_fallback";
+
+    // Gender.
+    public final static int GENDER_UNKNOWN = 0;
+    public final static int GENDER_NEUTRAL = 1;
+    public final static int GENDER_MALE = 2;
+    public final static int GENDER_FEMALE = 3;
+
+    // Animacy.
+    public final static int ANIMACY_UNKNOWN = 0;
+    public final static int ANIMACY_ANIMATE = 1;
+    public final static int ANIMACY_INANIMATE = 2;
+
+    // Multiplicity.
+    public final static int MULTIPLICITY_UNKNOWN = 0;
+    public final static int MULTIPLICITY_SINGLE = 1;
+    public final static int MULTIPLICITY_DUAL = 2;
+    public final static int MULTIPLICITY_PLURAL = 3;
+
+    // Case.
+    public final static int CASE_UNKNOWN = 0;
+    public final static int CASE_NOMINATIVE = 1;
+    public final static int CASE_ACCUSATIVE = 2;
+    public final static int CASE_DATIVE = 3;
+    public final static int CASE_ABLATIVE = 4;
+    public final static int CASE_GENITIVE = 5;
+    public final static int CASE_VOCATIVE = 6;
+    public final static int CASE_LOCATIVE = 7;
+    public final static int CASE_INSTRUMENTAL = 8;
+
+    private List<AbstractTts<? extends AbstractTts<?>>> says =
+            new ArrayList<AbstractTts<? extends AbstractTts<?>>>();
+    Boolean mNoWarningOnFallback = null;
+
+    /**
+     * Objects deriving from this class can be appended to a Utterance. This class uses generics
+     * so method from this class can return instances of its child classes, resulting in a better
+     * API (CRTP pattern).
+     */
+    public static abstract class AbstractTts<C extends AbstractTts<C>> {
+
+        protected Markup mMarkup = new Markup();
+
+        /**
+         * Empty constructor.
+         */
+        protected AbstractTts() {
+        }
+
+        /**
+         * Construct with Markup.
+         * @param markup
+         */
+        protected AbstractTts(Markup markup) {
+            mMarkup = markup;
+        }
+
+        /**
+         * Returns the type of this class, e.g. "cardinal" or "measure".
+         * @return The type.
+         */
+        public String getType() {
+            return mMarkup.getType();
+        }
+
+        /**
+         * A fallback plain text can be provided, in case the engine does not support this class
+         * type, or even Markup altogether.
+         * @param plainText A string with the plain text.
+         * @return This instance.
+         */
+        @SuppressWarnings("unchecked")
+        public C setPlainText(String plainText) {
+            mMarkup.setPlainText(plainText);
+            return (C) this;
+        }
+
+        /**
+         * Returns the plain text (fallback) string.
+         * @return Plain text string or null if not set.
+         */
+        public String getPlainText() {
+            return mMarkup.getPlainText();
+        }
+
+        /**
+         * Populates the plainText if not set and builds a Markup instance.
+         * @return The Markup object describing this instance.
+         */
+        public Markup getMarkup() {
+            return new Markup(mMarkup);
+        }
+
+        @SuppressWarnings("unchecked")
+        protected C setParameter(String key, String value) {
+            mMarkup.setParameter(key, value);
+            return (C) this;
+        }
+
+        protected String getParameter(String key) {
+            return mMarkup.getParameter(key);
+        }
+
+        @SuppressWarnings("unchecked")
+        protected C removeParameter(String key) {
+            mMarkup.removeParameter(key);
+            return (C) this;
+        }
+
+        /**
+         * Returns a string representation of this instance, can be deserialized to an equal
+         * Utterance instance.
+         */
+        public String toString() {
+            return mMarkup.toString();
+        }
+
+        /**
+         * Returns a generated plain text alternative for this instance if this instance isn't
+         * better representated by the list of it's children.
+         * @return Best effort plain text representation of this instance, can be null.
+         */
+        public String generatePlainText() {
+            return null;
+        }
+    }
+
+    public static abstract class AbstractTtsSemioticClass<C extends AbstractTtsSemioticClass<C>>
+            extends AbstractTts<C> {
+        // Keys.
+        private static final String KEY_GENDER = "gender";
+        private static final String KEY_ANIMACY = "animacy";
+        private static final String KEY_MULTIPLICITY = "multiplicity";
+        private static final String KEY_CASE = "case";
+
+        protected AbstractTtsSemioticClass() {
+            super();
+        }
+
+        protected AbstractTtsSemioticClass(Markup markup) {
+            super(markup);
+        }
+
+        @SuppressWarnings("unchecked")
+        public C setGender(int gender) {
+            if (gender < 0 || gender > 3) {
+                throw new IllegalArgumentException("Only four types of gender can be set: " +
+                                                   "unknown, neutral, maculine and female.");
+            }
+            if (gender != GENDER_UNKNOWN) {
+                setParameter(KEY_GENDER, String.valueOf(gender));
+            } else {
+                setParameter(KEY_GENDER, null);
+            }
+            return (C) this;
+        }
+
+        public int getGender() {
+            String gender = mMarkup.getParameter(KEY_GENDER);
+            return gender != null ? Integer.valueOf(gender) : GENDER_UNKNOWN;
+        }
+
+        @SuppressWarnings("unchecked")
+        public C setAnimacy(int animacy) {
+            if (animacy < 0 || animacy > 2) {
+                throw new IllegalArgumentException(
+                        "Only two types of animacy can be set: unknown, animate and inanimate");
+            }
+            if (animacy != ANIMACY_UNKNOWN) {
+                setParameter(KEY_ANIMACY, String.valueOf(animacy));
+            } else {
+                setParameter(KEY_ANIMACY, null);
+            }
+            return (C) this;
+        }
+
+        public int getAnimacy() {
+            String animacy = getParameter(KEY_ANIMACY);
+            return animacy != null ? Integer.valueOf(animacy) : ANIMACY_UNKNOWN;
+        }
+
+        @SuppressWarnings("unchecked")
+        public C setMultiplicity(int multiplicity) {
+            if (multiplicity < 0 || multiplicity > 3) {
+                throw new IllegalArgumentException(
+                        "Only four types of multiplicity can be set: unknown, single, dual and " +
+                        "plural.");
+            }
+            if (multiplicity != MULTIPLICITY_UNKNOWN) {
+                setParameter(KEY_MULTIPLICITY, String.valueOf(multiplicity));
+            } else {
+                setParameter(KEY_MULTIPLICITY, null);
+            }
+            return (C) this;
+        }
+
+        public int getMultiplicity() {
+            String multiplicity = mMarkup.getParameter(KEY_MULTIPLICITY);
+            return multiplicity != null ? Integer.valueOf(multiplicity) : MULTIPLICITY_UNKNOWN;
+        }
+
+        @SuppressWarnings("unchecked")
+        public C setCase(int grammaticalCase) {
+            if (grammaticalCase < 0 || grammaticalCase > 8) {
+                throw new IllegalArgumentException(
+                        "Only nine types of grammatical case can be set.");
+            }
+            if (grammaticalCase != CASE_UNKNOWN) {
+                setParameter(KEY_CASE, String.valueOf(grammaticalCase));
+            } else {
+                setParameter(KEY_CASE, null);
+            }
+            return (C) this;
+        }
+
+        public int getCase() {
+            String grammaticalCase = mMarkup.getParameter(KEY_CASE);
+            return grammaticalCase != null ? Integer.valueOf(grammaticalCase) : CASE_UNKNOWN;
+        }
+    }
+
+    /**
+     * Class that contains regular text, synthesis engine pronounces it using its regular pipeline.
+     * Parameters:
+     * <ul>
+     *   <li>Text: the text to synthesize</li>
+     * </ul>
+     */
+    public static class TtsText extends AbstractTtsSemioticClass<TtsText> {
+
+        // The type of this node.
+        protected static final String TYPE_TEXT = "text";
+        // The text parameter stores the text to be synthesized.
+        private static final String KEY_TEXT = "text";
+
+        /**
+         * Default constructor.
+         */
+        public TtsText() {
+            mMarkup.setType(TYPE_TEXT);
+        }
+
+        /**
+         * Constructor that sets the text to be synthesized.
+         * @param text The text to be synthesized.
+         */
+        public TtsText(String text) {
+            this();
+            setText(text);
+        }
+
+        /**
+         * Constructs a TtsText with the values of the Markup, does not check if the given Markup is
+         * of the right type.
+         */
+        private TtsText(Markup markup) {
+            super(markup);
+        }
+
+        /**
+         * Sets the text to be synthesized.
+         * @return This instance.
+         */
+        public TtsText setText(String text) {
+            setParameter(KEY_TEXT, text);
+            return this;
+        }
+
+        /**
+         * Returns the text to be synthesized.
+         * @return This instance.
+         */
+        public String getText() {
+            return getParameter(KEY_TEXT);
+        }
+
+        /**
+         * Generates a best effort plain text, in this case simply the text.
+         */
+        @Override
+        public String generatePlainText() {
+            return getText();
+        }
+    }
+
+    /**
+     * Contains a cardinal.
+     * Parameters:
+     * <ul>
+     *   <li>integer: the integer to synthesize</li>
+     * </ul>
+     */
+    public static class TtsCardinal extends AbstractTtsSemioticClass<TtsCardinal> {
+
+        // The type of this node.
+        protected static final String TYPE_CARDINAL = "cardinal";
+        // The parameter integer stores the integer to synthesize.
+        private static final String KEY_INTEGER = "integer";
+
+        /**
+         * Default constructor.
+         */
+        public TtsCardinal() {
+            mMarkup.setType(TYPE_CARDINAL);
+        }
+
+        /**
+         * Constructor that sets the integer to be synthesized.
+         */
+        public TtsCardinal(int integer) {
+            this();
+            setInteger(integer);
+        }
+
+        /**
+         * Constructor that sets the integer to be synthesized.
+         */
+        public TtsCardinal(String integer) {
+            this();
+            setInteger(integer);
+        }
+
+        /**
+         * Constructs a TtsText with the values of the Markup.
+         * Does not check if the given Markup is of the right type.
+         */
+        private TtsCardinal(Markup markup) {
+            super(markup);
+        }
+
+        /**
+         * Sets the integer.
+         * @return This instance.
+         */
+        public TtsCardinal setInteger(int integer) {
+            return setInteger(String.valueOf(integer));
+        }
+
+        /**
+         * Sets the integer.
+         * @param integer A non-empty string of digits with an optional '-' in front.
+         * @return This instance.
+         */
+        public TtsCardinal setInteger(String integer) {
+            if (!integer.matches("-?\\d+")) {
+                throw new IllegalArgumentException("Expected a cardinal: \"" + integer + "\"");
+            }
+            setParameter(KEY_INTEGER, integer);
+            return this;
+        }
+
+        /**
+         * Returns the integer parameter.
+         */
+        public String getInteger() {
+            return getParameter(KEY_INTEGER);
+        }
+
+        /**
+         * Generates a best effort plain text, in this case simply the integer.
+         */
+        @Override
+        public String generatePlainText() {
+            return getInteger();
+        }
+    }
+
+    /**
+     * Default constructor.
+     */
+    public Utterance() {}
+
+    /**
+     * Returns the plain text of a given Markup if it was set; if it's not set, recursively call the
+     * this same method on its children.
+     */
+    private String constructPlainText(Markup m) {
+        StringBuilder plainText = new StringBuilder();
+        if (m.getPlainText() != null) {
+            plainText.append(m.getPlainText());
+        } else {
+            for (Markup nestedMarkup : m.getNestedMarkups()) {
+                String nestedPlainText = constructPlainText(nestedMarkup);
+                if (!nestedPlainText.isEmpty()) {
+                    if (plainText.length() != 0) {
+                        plainText.append(" ");
+                    }
+                    plainText.append(nestedPlainText);
+                }
+            }
+        }
+        return plainText.toString();
+    }
+
+    /**
+     * Creates a Markup instance with auto generated plain texts for the relevant nodes, in case the
+     * user has not provided one already.
+     * @return A Markup instance representing this utterance.
+     */
+    public Markup createMarkup() {
+        Markup markup = new Markup(TYPE_UTTERANCE);
+        StringBuilder plainText = new StringBuilder();
+        for (AbstractTts<? extends AbstractTts<?>> say : says) {
+            // Get a copy of this markup, and generate a plaintext for it if is not set.
+            Markup sayMarkup = say.getMarkup();
+            if (sayMarkup.getPlainText() == null) {
+                sayMarkup.setPlainText(say.generatePlainText());
+            }
+            if (plainText.length() != 0) {
+                plainText.append(" ");
+            }
+            plainText.append(constructPlainText(sayMarkup));
+            markup.addNestedMarkup(sayMarkup);
+        }
+        if (mNoWarningOnFallback != null) {
+            markup.setParameter(KEY_NO_WARNING_ON_FALLBACK,
+                                mNoWarningOnFallback ? "true" : "false");
+        }
+        markup.setPlainText(plainText.toString());
+        return markup;
+    }
+
+    /**
+     * Appends an element to this Utterance instance.
+     * @return this instance
+     */
+    public Utterance append(AbstractTts<? extends AbstractTts<?>> say) {
+        says.add(say);
+        return this;
+    }
+
+    private Utterance append(Markup markup) {
+        if (markup.getType().equals(TtsText.TYPE_TEXT)) {
+            append(new TtsText(markup));
+        } else if (markup.getType().equals(TtsCardinal.TYPE_CARDINAL)) {
+            append(new TtsCardinal(markup));
+        } else {
+            // Unknown node, a class we don't know about.
+            if (markup.getPlainText() != null) {
+                append(new TtsText(markup.getPlainText()));
+            } else {
+                // No plainText specified; add its children
+                // seperately. In case of a new prosody node,
+                // we would still verbalize it correctly.
+                for (Markup nested : markup.getNestedMarkups()) {
+                    append(nested);
+                }
+            }
+        }
+        return this;
+    }
+
+    /**
+     * Returns a string representation of this Utterance instance. Can be deserialized back to an
+     * Utterance instance with utteranceFromString(). Can be used to store utterances to be used
+     * at a later time.
+     */
+    public String toString() {
+        String out = "type: \"" + TYPE_UTTERANCE + "\"";
+        if (mNoWarningOnFallback != null) {
+            out += " no_warning_on_fallback: \"" + (mNoWarningOnFallback ? "true" : "false") + "\"";
+        }
+        for (AbstractTts<? extends AbstractTts<?>> say : says) {
+            out += " markup { " + say.getMarkup().toString() + " }";
+        }
+        return out;
+    }
+
+    /**
+     * Returns an Utterance instance from the string representation generated by toString().
+     * @param string The string representation generated by toString().
+     * @return The new Utterance instance.
+     * @throws {@link IllegalArgumentException} if the input cannot be correctly parsed.
+     */
+    static public Utterance utteranceFromString(String string) throws IllegalArgumentException {
+        Utterance utterance = new Utterance();
+        Markup markup = Markup.markupFromString(string);
+        if (!markup.getType().equals(TYPE_UTTERANCE)) {
+            throw new IllegalArgumentException("Top level markup should be of type \"" +
+                                               TYPE_UTTERANCE + "\", but was of type \"" +
+                                               markup.getType() + "\".") ;
+        }
+        for (Markup nestedMarkup : markup.getNestedMarkups()) {
+            utterance.append(nestedMarkup);
+        }
+        return utterance;
+    }
+
+    /**
+     * Appends a new TtsText with the given text.
+     * @param text The text to synthesize.
+     * @return This instance.
+     */
+    public Utterance append(String text) {
+        return append(new TtsText(text));
+    }
+
+    /**
+     * Appends a TtsCardinal representing the given number.
+     * @param integer The integer to synthesize.
+     * @return this
+     */
+    public Utterance append(int integer) {
+        return append(new TtsCardinal(integer));
+    }
+
+    /**
+     * Returns the n'th element in this Utterance.
+     * @param i The index.
+     * @return The n'th element in this Utterance.
+     * @throws {@link IndexOutOfBoundsException} - if i < 0 || i >= size()
+     */
+    public AbstractTts<? extends AbstractTts<?>> get(int i) {
+        return says.get(i);
+    }
+
+    /**
+     * Returns the number of elements in this Utterance.
+     * @return The number of elements in this Utterance.
+     */
+    public int size() {
+        return says.size();
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if ( this == o ) return true;
+        if ( !(o instanceof Utterance) ) return false;
+        Utterance utt = (Utterance) o;
+
+        if (says.size() != utt.says.size()) {
+            return false;
+        }
+
+        for (int i = 0; i < says.size(); i++) {
+            if (!says.get(i).getMarkup().equals(utt.says.get(i).getMarkup())) {
+                return false;
+            }
+        }
+        return true;
+    }
+
+    /**
+     * Can be set to true or false, true indicating that the user provided only a string to the API,
+     * at which the system will not issue a warning if the synthesizer falls back onto the plain
+     * text when the synthesizer does not support Markup.
+     */
+    public Utterance setNoWarningOnFallback(boolean noWarning) {
+        mNoWarningOnFallback = noWarning;
+        return this;
+    }
+}
diff --git a/core/java/android/tv/TvInputInfo.java b/core/java/android/tv/TvInputInfo.java
deleted file mode 100644
index 50462cc..0000000
--- a/core/java/android/tv/TvInputInfo.java
+++ /dev/null
@@ -1,163 +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.tv;
-
-import android.content.ComponentName;
-import android.content.pm.PackageManager;
-import android.content.pm.ResolveInfo;
-import android.content.pm.ServiceInfo;
-import android.os.Parcel;
-import android.os.Parcelable;
-
-/**
- * This class is used to specify meta information of a TV input.
- */
-public final class TvInputInfo implements Parcelable {
-    private final ResolveInfo mService;
-    private final String mId;
-
-    /**
-     * Constructor.
-     *
-     * @param service The ResolveInfo returned from the package manager about this TV input service.
-     * @hide
-     */
-    public TvInputInfo(ResolveInfo service) {
-        mService = service;
-        ServiceInfo si = service.serviceInfo;
-        mId = generateInputIdForComponenetName(new ComponentName(si.packageName, si.name));
-    }
-
-    /**
-     * Returns a unique ID for this TV input. The ID is generated from the package and class name
-     * implementing the TV input service.
-     */
-    public String getId() {
-        return mId;
-    }
-
-    /**
-     * Returns the .apk package that implements this TV input service.
-     */
-    public String getPackageName() {
-        return mService.serviceInfo.packageName;
-    }
-
-    /**
-     * Returns the class name of the service component that implements this TV input service.
-     */
-    public String getServiceName() {
-        return mService.serviceInfo.name;
-    }
-
-    /**
-     * Returns the component of the service that implements this TV input.
-     */
-    public ComponentName getComponent() {
-        return new ComponentName(mService.serviceInfo.packageName, mService.serviceInfo.name);
-    }
-
-    /**
-     * Loads the user-displayed label for this TV input service.
-     *
-     * @param pm Supplies a PackageManager used to load the TV input's resources.
-     * @return a CharSequence containing the TV input's label. If the TV input does not have
-     *         a label, its name is returned.
-     */
-    public CharSequence loadLabel(PackageManager pm) {
-        return mService.loadLabel(pm);
-    }
-
-    @Override
-    public int describeContents() {
-        return 0;
-    }
-
-    @Override
-    public int hashCode() {
-        return mId.hashCode();
-    }
-
-    @Override
-    public boolean equals(Object o) {
-        if (o == this) {
-            return true;
-        }
-
-        if (!(o instanceof TvInputInfo)) {
-            return false;
-        }
-
-        TvInputInfo obj = (TvInputInfo) o;
-        return mId.equals(obj.mId)
-                && mService.serviceInfo.packageName.equals(obj.mService.serviceInfo.packageName)
-                && mService.serviceInfo.name.equals(obj.mService.serviceInfo.name);
-    }
-
-    @Override
-    public String toString() {
-        return "TvInputInfo{id=" + mId
-                + ", pkg=" + mService.serviceInfo.packageName
-                + ", service=" + mService.serviceInfo.name + "}";
-    }
-
-    /**
-     * Used to package this object into a {@link Parcel}.
-     *
-     * @param dest The {@link Parcel} to be written.
-     * @param flags The flags used for parceling.
-     */
-    @Override
-    public void writeToParcel(Parcel dest, int flags) {
-        dest.writeString(mId);
-        mService.writeToParcel(dest, flags);
-    }
-
-    /**
-     * Used to generate an input id from a ComponentName.
-     *
-     * @param name the component name for generating an input id.
-     * @return the generated input id for the given {@code name}.
-     * @hide
-     */
-    public static final String generateInputIdForComponenetName(ComponentName name) {
-        return name.flattenToShortString();
-    }
-
-    /**
-     * Used to make this class parcelable.
-     *
-     * @hide
-     */
-    public static final Parcelable.Creator<TvInputInfo> CREATOR =
-            new Parcelable.Creator<TvInputInfo>() {
-        @Override
-        public TvInputInfo createFromParcel(Parcel in) {
-            return new TvInputInfo(in);
-        }
-
-        @Override
-        public TvInputInfo[] newArray(int size) {
-            return new TvInputInfo[size];
-        }
-    };
-
-    private TvInputInfo(Parcel in) {
-        mId = in.readString();
-        mService = ResolveInfo.CREATOR.createFromParcel(in);
-    }
-}
diff --git a/core/java/android/view/GLES20Canvas.java b/core/java/android/view/GLES20Canvas.java
index 424d860..5056097 100644
--- a/core/java/android/view/GLES20Canvas.java
+++ b/core/java/android/view/GLES20Canvas.java
@@ -75,22 +75,10 @@
     // Constructors
     ///////////////////////////////////////////////////////////////////////////
 
-    /**
-     * Creates a canvas to render directly on screen.
-     */
-    GLES20Canvas(boolean translucent) {
-        this(false, translucent);
-    }
-    
-    protected GLES20Canvas(boolean record, boolean translucent) {
-        mOpaque = !translucent;
-
-        if (record) {
-            mRenderer = nCreateDisplayListRenderer();
-        } else {
-            mRenderer = nCreateRenderer();
-        }
-
+    // TODO: Merge with GLES20RecordingCanvas
+    protected GLES20Canvas() {
+        mOpaque = false;
+        mRenderer = nCreateDisplayListRenderer();
         setupFinalizer();
     }
 
@@ -102,7 +90,6 @@
         }
     }
 
-    private static native long nCreateRenderer();
     private static native long nCreateDisplayListRenderer();
     private static native void nResetDisplayListRenderer(long renderer);
     private static native void nDestroyRenderer(long renderer);
@@ -131,36 +118,6 @@
     private static native void nSetProperty(String name, String value);
 
     ///////////////////////////////////////////////////////////////////////////
-    // Hardware layers
-    ///////////////////////////////////////////////////////////////////////////
-
-    @Override
-    void pushLayerUpdate(HardwareLayer layer) {
-        nPushLayerUpdate(mRenderer, layer.getLayer());
-    }
-
-    @Override
-    void cancelLayerUpdate(HardwareLayer layer) {
-        nCancelLayerUpdate(mRenderer, layer.getLayer());
-    }
-
-    @Override
-    void flushLayerUpdates() {
-        nFlushLayerUpdates(mRenderer);
-    }
-
-    @Override
-    void clearLayerUpdates() {
-        nClearLayerUpdates(mRenderer);
-    }
-
-    static native boolean nCopyLayer(long layerId, long bitmap);
-    private static native void nClearLayerUpdates(long renderer);
-    private static native void nFlushLayerUpdates(long renderer);
-    private static native void nPushLayerUpdate(long renderer, long layer);
-    private static native void nCancelLayerUpdate(long renderer, long layer);
-
-    ///////////////////////////////////////////////////////////////////////////
     // Canvas management
     ///////////////////////////////////////////////////////////////////////////
 
@@ -234,20 +191,6 @@
 
     private static native void nFinish(long renderer);
 
-    /**
-     * Returns the size of the stencil buffer required by the underlying
-     * implementation.
-     * 
-     * @return The minimum number of bits the stencil buffer must. Always >= 0.
-     * 
-     * @hide
-     */
-    public static int getStencilSize() {
-        return nGetStencilSize();
-    }
-
-    private static native int nGetStencilSize();
-
     ///////////////////////////////////////////////////////////////////////////
     // Functor
     ///////////////////////////////////////////////////////////////////////////
@@ -284,49 +227,6 @@
      */
     static final int FLUSH_CACHES_FULL = 2;
 
-    /**
-     * Flush caches to reclaim as much memory as possible. The amount of memory
-     * to reclaim is indicate by the level parameter.
-     * 
-     * The level can be one of {@link #FLUSH_CACHES_MODERATE} or
-     * {@link #FLUSH_CACHES_FULL}.
-     * 
-     * @param level Hint about the amount of memory to reclaim
-     */
-    static void flushCaches(int level) {
-        nFlushCaches(level);
-    }
-
-    private static native void nFlushCaches(int level);
-
-    /**
-     * Release all resources associated with the underlying caches. This should
-     * only be called after a full flushCaches().
-     * 
-     * @hide
-     */
-    static void terminateCaches() {
-        nTerminateCaches();
-    }
-
-    private static native void nTerminateCaches();
-
-    static boolean initCaches() {
-        return nInitCaches();
-    }
-
-    private static native boolean nInitCaches();
-
-    ///////////////////////////////////////////////////////////////////////////
-    // Atlas
-    ///////////////////////////////////////////////////////////////////////////
-
-    static void initAtlas(GraphicBuffer buffer, long[] map) {
-        nInitAtlas(buffer, map, map.length);
-    }
-
-    private static native void nInitAtlas(GraphicBuffer buffer, long[] map, int count);
-
     ///////////////////////////////////////////////////////////////////////////
     // Display list
     ///////////////////////////////////////////////////////////////////////////
@@ -899,12 +799,6 @@
     private static native void nDrawPath(long renderer, long path, long paint);
     private static native void nDrawRects(long renderer, long region, long paint);
 
-    void drawRects(float[] rects, int count, Paint paint) {
-        nDrawRects(mRenderer, rects, count, paint.mNativePaint);
-    }
-
-    private static native void nDrawRects(long renderer, float[] rects, int count, long paint);
-
     @Override
     public void drawPicture(Picture picture) {
         if (picture.createdFromStream) {
diff --git a/core/java/android/view/GLES20RecordingCanvas.java b/core/java/android/view/GLES20RecordingCanvas.java
index a94ec3a..b2961e5 100644
--- a/core/java/android/view/GLES20RecordingCanvas.java
+++ b/core/java/android/view/GLES20RecordingCanvas.java
@@ -36,7 +36,7 @@
     RenderNode mNode;
 
     private GLES20RecordingCanvas() {
-        super(true, true);
+        super();
     }
 
     static GLES20RecordingCanvas obtain(@NonNull RenderNode node) {
diff --git a/core/java/android/view/GLRenderer.java b/core/java/android/view/GLRenderer.java
deleted file mode 100644
index 6dd7c00..0000000
--- a/core/java/android/view/GLRenderer.java
+++ /dev/null
@@ -1,1534 +0,0 @@
-/*
- * Copyright (C) 2013 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.view;
-
-import static javax.microedition.khronos.egl.EGL10.EGL_ALPHA_SIZE;
-import static javax.microedition.khronos.egl.EGL10.EGL_BAD_NATIVE_WINDOW;
-import static javax.microedition.khronos.egl.EGL10.EGL_BLUE_SIZE;
-import static javax.microedition.khronos.egl.EGL10.EGL_CONFIG_CAVEAT;
-import static javax.microedition.khronos.egl.EGL10.EGL_DEFAULT_DISPLAY;
-import static javax.microedition.khronos.egl.EGL10.EGL_DEPTH_SIZE;
-import static javax.microedition.khronos.egl.EGL10.EGL_DRAW;
-import static javax.microedition.khronos.egl.EGL10.EGL_GREEN_SIZE;
-import static javax.microedition.khronos.egl.EGL10.EGL_HEIGHT;
-import static javax.microedition.khronos.egl.EGL10.EGL_NONE;
-import static javax.microedition.khronos.egl.EGL10.EGL_NO_CONTEXT;
-import static javax.microedition.khronos.egl.EGL10.EGL_NO_DISPLAY;
-import static javax.microedition.khronos.egl.EGL10.EGL_NO_SURFACE;
-import static javax.microedition.khronos.egl.EGL10.EGL_RED_SIZE;
-import static javax.microedition.khronos.egl.EGL10.EGL_RENDERABLE_TYPE;
-import static javax.microedition.khronos.egl.EGL10.EGL_SAMPLES;
-import static javax.microedition.khronos.egl.EGL10.EGL_SAMPLE_BUFFERS;
-import static javax.microedition.khronos.egl.EGL10.EGL_STENCIL_SIZE;
-import static javax.microedition.khronos.egl.EGL10.EGL_SUCCESS;
-import static javax.microedition.khronos.egl.EGL10.EGL_SURFACE_TYPE;
-import static javax.microedition.khronos.egl.EGL10.EGL_WIDTH;
-import static javax.microedition.khronos.egl.EGL10.EGL_WINDOW_BIT;
-
-import android.content.ComponentCallbacks2;
-import android.graphics.Bitmap;
-import android.graphics.Paint;
-import android.graphics.Rect;
-import android.graphics.SurfaceTexture;
-import android.opengl.EGL14;
-import android.opengl.GLUtils;
-import android.opengl.ManagedEGLContext;
-import android.os.Handler;
-import android.os.IBinder;
-import android.os.Looper;
-import android.os.RemoteException;
-import android.os.ServiceManager;
-import android.os.SystemClock;
-import android.os.SystemProperties;
-import android.os.Trace;
-import android.util.DisplayMetrics;
-import android.util.Log;
-import android.view.Surface.OutOfResourcesException;
-
-import com.google.android.gles_jni.EGLImpl;
-
-import java.io.PrintWriter;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.concurrent.locks.ReentrantLock;
-
-import javax.microedition.khronos.egl.EGL10;
-import javax.microedition.khronos.egl.EGL11;
-import javax.microedition.khronos.egl.EGLConfig;
-import javax.microedition.khronos.egl.EGLContext;
-import javax.microedition.khronos.egl.EGLDisplay;
-import javax.microedition.khronos.egl.EGLSurface;
-import javax.microedition.khronos.opengles.GL;
-
-/**
- * Hardware renderer using OpenGL
- *
- * @hide
- */
-public class GLRenderer extends HardwareRenderer {
-    static final int SURFACE_STATE_ERROR = 0;
-    static final int SURFACE_STATE_SUCCESS = 1;
-    static final int SURFACE_STATE_UPDATED = 2;
-
-    static final int FUNCTOR_PROCESS_DELAY = 4;
-
-    /**
-     * Number of frames to profile.
-     */
-    private static final int PROFILE_MAX_FRAMES = 128;
-
-    /**
-     * Number of floats per profiled frame.
-     */
-    private static final int PROFILE_FRAME_DATA_COUNT = 3;
-
-    private static final int PROFILE_DRAW_MARGIN = 0;
-    private static final int PROFILE_DRAW_WIDTH = 3;
-    private static final int[] PROFILE_DRAW_COLORS = { 0xcf3e66cc, 0xcfdc3912, 0xcfe69800 };
-    private static final int PROFILE_DRAW_CURRENT_FRAME_COLOR = 0xcf5faa4d;
-    private static final int PROFILE_DRAW_THRESHOLD_COLOR = 0xff5faa4d;
-    private static final int PROFILE_DRAW_THRESHOLD_STROKE_WIDTH = 2;
-    private static final int PROFILE_DRAW_DP_PER_MS = 7;
-
-    private static final String[] VISUALIZERS = {
-            PROFILE_PROPERTY_VISUALIZE_BARS,
-            PROFILE_PROPERTY_VISUALIZE_LINES
-    };
-
-    private static final String[] OVERDRAW = {
-            OVERDRAW_PROPERTY_SHOW,
-    };
-    private static final int GL_VERSION = 2;
-
-    static EGL10 sEgl;
-    static EGLDisplay sEglDisplay;
-    static EGLConfig sEglConfig;
-    static final Object[] sEglLock = new Object[0];
-    int mWidth = -1, mHeight = -1;
-
-    static final ThreadLocal<ManagedEGLContext> sEglContextStorage
-            = new ThreadLocal<ManagedEGLContext>();
-
-    EGLContext mEglContext;
-    Thread mEglThread;
-
-    EGLSurface mEglSurface;
-
-    GL mGl;
-    HardwareCanvas mCanvas;
-
-    String mName;
-
-    long mFrameCount;
-    Paint mDebugPaint;
-
-    static boolean sDirtyRegions;
-    static final boolean sDirtyRegionsRequested;
-    static {
-        String dirtyProperty = SystemProperties.get(RENDER_DIRTY_REGIONS_PROPERTY, "true");
-        //noinspection PointlessBooleanExpression,ConstantConditions
-        sDirtyRegions = "true".equalsIgnoreCase(dirtyProperty);
-        sDirtyRegionsRequested = sDirtyRegions;
-    }
-
-    boolean mDirtyRegionsEnabled;
-    boolean mUpdateDirtyRegions;
-
-    boolean mProfileEnabled;
-    int mProfileVisualizerType = -1;
-    float[] mProfileData;
-    ReentrantLock mProfileLock;
-    int mProfileCurrentFrame = -PROFILE_FRAME_DATA_COUNT;
-
-    GraphDataProvider mDebugDataProvider;
-    float[][] mProfileShapes;
-    Paint mProfilePaint;
-
-    boolean mDebugDirtyRegions;
-    int mDebugOverdraw = -1;
-
-    final boolean mTranslucent;
-
-    private boolean mDestroyed;
-
-    private final Rect mRedrawClip = new Rect();
-
-    private final int[] mSurfaceSize = new int[2];
-
-    private long mDrawDelta = Long.MAX_VALUE;
-
-    private GLES20Canvas mGlCanvas;
-
-    private DisplayMetrics mDisplayMetrics;
-
-    private static EGLSurface sPbuffer;
-    private static final Object[] sPbufferLock = new Object[0];
-
-    private List<HardwareLayer> mAttachedLayers = new ArrayList<HardwareLayer>();
-
-    private static class GLRendererEglContext extends ManagedEGLContext {
-        final Handler mHandler = new Handler();
-
-        public GLRendererEglContext(EGLContext context) {
-            super(context);
-        }
-
-        @Override
-        public void onTerminate(final EGLContext eglContext) {
-            // Make sure we do this on the correct thread.
-            if (mHandler.getLooper() != Looper.myLooper()) {
-                mHandler.post(new Runnable() {
-                    @Override
-                    public void run() {
-                        onTerminate(eglContext);
-                    }
-                });
-                return;
-            }
-
-            synchronized (sEglLock) {
-                if (sEgl == null) return;
-
-                if (EGLImpl.getInitCount(sEglDisplay) == 1) {
-                    usePbufferSurface(eglContext);
-                    GLES20Canvas.terminateCaches();
-
-                    sEgl.eglDestroyContext(sEglDisplay, eglContext);
-                    sEglContextStorage.set(null);
-                    sEglContextStorage.remove();
-
-                    sEgl.eglDestroySurface(sEglDisplay, sPbuffer);
-                    sEgl.eglMakeCurrent(sEglDisplay, EGL_NO_SURFACE,
-                            EGL_NO_SURFACE, EGL_NO_CONTEXT);
-
-                    sEgl.eglReleaseThread();
-                    sEgl.eglTerminate(sEglDisplay);
-
-                    sEgl = null;
-                    sEglDisplay = null;
-                    sEglConfig = null;
-                    sPbuffer = null;
-                }
-            }
-        }
-    }
-
-    HardwareCanvas createCanvas() {
-        return mGlCanvas = new GLES20Canvas(mTranslucent);
-    }
-
-    ManagedEGLContext createManagedContext(EGLContext eglContext) {
-        return new GLRendererEglContext(mEglContext);
-    }
-
-    int[] getConfig(boolean dirtyRegions) {
-        //noinspection PointlessBooleanExpression,ConstantConditions
-        final int stencilSize = GLES20Canvas.getStencilSize();
-        final int swapBehavior = dirtyRegions ? EGL14.EGL_SWAP_BEHAVIOR_PRESERVED_BIT : 0;
-
-        return new int[] {
-                EGL_RENDERABLE_TYPE, EGL14.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, stencilSize,
-                EGL_SURFACE_TYPE, EGL_WINDOW_BIT | swapBehavior,
-                EGL_NONE
-        };
-    }
-
-    void initCaches() {
-        if (GLES20Canvas.initCaches()) {
-            // Caches were (re)initialized, rebind atlas
-            initAtlas();
-        }
-    }
-
-    void initAtlas() {
-        IBinder binder = ServiceManager.getService("assetatlas");
-        if (binder == null) return;
-
-        IAssetAtlas atlas = IAssetAtlas.Stub.asInterface(binder);
-        try {
-            if (atlas.isCompatible(android.os.Process.myPpid())) {
-                GraphicBuffer buffer = atlas.getBuffer();
-                if (buffer != null) {
-                    long[] map = atlas.getMap();
-                    if (map != null) {
-                        GLES20Canvas.initAtlas(buffer, map);
-                    }
-                    // If IAssetAtlas is not the same class as the IBinder
-                    // we are using a remote service and we can safely
-                    // destroy the graphic buffer
-                    if (atlas.getClass() != binder.getClass()) {
-                        buffer.destroy();
-                    }
-                }
-            }
-        } catch (RemoteException e) {
-            Log.w(LOG_TAG, "Could not acquire atlas", e);
-        }
-    }
-
-    boolean canDraw() {
-        return mGl != null && mCanvas != null && mGlCanvas != null;
-    }
-
-    int onPreDraw(Rect dirty) {
-        return mGlCanvas.onPreDraw(dirty);
-    }
-
-    void onPostDraw() {
-        mGlCanvas.onPostDraw();
-    }
-
-    void drawProfileData(View.AttachInfo attachInfo) {
-        if (mDebugDataProvider != null) {
-            final GraphDataProvider provider = mDebugDataProvider;
-            initProfileDrawData(attachInfo, provider);
-
-            final int height = provider.getVerticalUnitSize();
-            final int margin = provider.getHorizontaUnitMargin();
-            final int width = provider.getHorizontalUnitSize();
-
-            int x = 0;
-            int count = 0;
-            int current = 0;
-
-            final float[] data = provider.getData();
-            final int elementCount = provider.getElementCount();
-            final int graphType = provider.getGraphType();
-
-            int totalCount = provider.getFrameCount() * elementCount;
-            if (graphType == GraphDataProvider.GRAPH_TYPE_LINES) {
-                totalCount -= elementCount;
-            }
-
-            for (int i = 0; i < totalCount; i += elementCount) {
-                if (data[i] < 0.0f) break;
-
-                int index = count * 4;
-                if (i == provider.getCurrentFrame() * elementCount) current = index;
-
-                x += margin;
-                int x2 = x + width;
-
-                int y2 = mHeight;
-                int y1 = (int) (y2 - data[i] * height);
-
-                switch (graphType) {
-                    case GraphDataProvider.GRAPH_TYPE_BARS: {
-                        for (int j = 0; j < elementCount; j++) {
-                            //noinspection MismatchedReadAndWriteOfArray
-                            final float[] r = mProfileShapes[j];
-                            r[index] = x;
-                            r[index + 1] = y1;
-                            r[index + 2] = x2;
-                            r[index + 3] = y2;
-
-                            y2 = y1;
-                            if (j < elementCount - 1) {
-                                y1 = (int) (y2 - data[i + j + 1] * height);
-                            }
-                        }
-                    } break;
-                    case GraphDataProvider.GRAPH_TYPE_LINES: {
-                        for (int j = 0; j < elementCount; j++) {
-                            //noinspection MismatchedReadAndWriteOfArray
-                            final float[] r = mProfileShapes[j];
-                            r[index] = (x + x2) * 0.5f;
-                            r[index + 1] = index == 0 ? y1 : r[index - 1];
-                            r[index + 2] = r[index] + width;
-                            r[index + 3] = y1;
-
-                            y2 = y1;
-                            if (j < elementCount - 1) {
-                                y1 = (int) (y2 - data[i + j + 1] * height);
-                            }
-                        }
-                    } break;
-                }
-
-
-                x += width;
-                count++;
-            }
-
-            x += margin;
-
-            drawGraph(graphType, count);
-            drawCurrentFrame(graphType, current);
-            drawThreshold(x, height);
-        }
-    }
-
-    private void drawGraph(int graphType, int count) {
-        for (int i = 0; i < mProfileShapes.length; i++) {
-            mDebugDataProvider.setupGraphPaint(mProfilePaint, i);
-            switch (graphType) {
-                case GraphDataProvider.GRAPH_TYPE_BARS:
-                    mGlCanvas.drawRects(mProfileShapes[i], count * 4, mProfilePaint);
-                    break;
-                case GraphDataProvider.GRAPH_TYPE_LINES:
-                    mGlCanvas.drawLines(mProfileShapes[i], 0, count * 4, mProfilePaint);
-                    break;
-            }
-        }
-    }
-
-    private void drawCurrentFrame(int graphType, int index) {
-        if (index >= 0) {
-            mDebugDataProvider.setupCurrentFramePaint(mProfilePaint);
-            switch (graphType) {
-                case GraphDataProvider.GRAPH_TYPE_BARS:
-                    mGlCanvas.drawRect(mProfileShapes[2][index], mProfileShapes[2][index + 1],
-                            mProfileShapes[2][index + 2], mProfileShapes[0][index + 3],
-                            mProfilePaint);
-                    break;
-                case GraphDataProvider.GRAPH_TYPE_LINES:
-                    mGlCanvas.drawLine(mProfileShapes[2][index], mProfileShapes[2][index + 1],
-                            mProfileShapes[2][index], mHeight, mProfilePaint);
-                    break;
-            }
-        }
-    }
-
-    private void drawThreshold(int x, int height) {
-        float threshold = mDebugDataProvider.getThreshold();
-        if (threshold > 0.0f) {
-            mDebugDataProvider.setupThresholdPaint(mProfilePaint);
-            int y = (int) (mHeight - threshold * height);
-            mGlCanvas.drawLine(0.0f, y, x, y, mProfilePaint);
-        }
-    }
-
-    private void initProfileDrawData(View.AttachInfo attachInfo, GraphDataProvider provider) {
-        if (mProfileShapes == null) {
-            final int elementCount = provider.getElementCount();
-            final int frameCount = provider.getFrameCount();
-
-            mProfileShapes = new float[elementCount][];
-            for (int i = 0; i < elementCount; i++) {
-                mProfileShapes[i] = new float[frameCount * 4];
-            }
-
-            mProfilePaint = new Paint();
-        }
-
-        mProfilePaint.reset();
-        if (provider.getGraphType() == GraphDataProvider.GRAPH_TYPE_LINES) {
-            mProfilePaint.setAntiAlias(true);
-        }
-
-        if (mDisplayMetrics == null) {
-            mDisplayMetrics = new DisplayMetrics();
-        }
-
-        attachInfo.mDisplay.getMetrics(mDisplayMetrics);
-        provider.prepare(mDisplayMetrics);
-    }
-
-    @Override
-    void destroy(boolean full) {
-        try {
-            if (full && mCanvas != null) {
-                mCanvas = null;
-            }
-
-            if (!isEnabled() || mDestroyed) {
-                setEnabled(false);
-                return;
-            }
-
-            destroySurface();
-            setEnabled(false);
-
-            mDestroyed = true;
-            mGl = null;
-        } finally {
-            if (full && mGlCanvas != null) {
-                mGlCanvas = null;
-            }
-        }
-    }
-
-    @Override
-    void pushLayerUpdate(HardwareLayer layer) {
-        mGlCanvas.pushLayerUpdate(layer);
-    }
-
-    @Override
-    void flushLayerUpdates() {
-        if (validate()) {
-            flushLayerChanges();
-            mGlCanvas.flushLayerUpdates();
-        }
-    }
-
-    @Override
-    HardwareLayer createTextureLayer() {
-        validate();
-        return HardwareLayer.createTextureLayer(this);
-    }
-
-    @Override
-    public HardwareLayer createDisplayListLayer(int width, int height) {
-        validate();
-        return HardwareLayer.createDisplayListLayer(this, width, height);
-    }
-
-    @Override
-    void onLayerCreated(HardwareLayer hardwareLayer) {
-        mAttachedLayers.add(hardwareLayer);
-    }
-
-    boolean hasContext() {
-        return sEgl != null && mEglContext != null
-                && mEglContext.equals(sEgl.eglGetCurrentContext());
-    }
-
-    @Override
-    void onLayerDestroyed(HardwareLayer layer) {
-        if (mGlCanvas != null) {
-            mGlCanvas.cancelLayerUpdate(layer);
-        }
-        if (hasContext()) {
-            long backingLayer = layer.detachBackingLayer();
-            nDestroyLayer(backingLayer);
-        }
-        mAttachedLayers.remove(layer);
-    }
-
-    @Override
-    public SurfaceTexture createSurfaceTexture(HardwareLayer layer) {
-        return layer.createSurfaceTexture();
-    }
-
-    @Override
-    boolean copyLayerInto(HardwareLayer layer, Bitmap bitmap) {
-        if (!validate()) {
-            throw new IllegalStateException("Could not acquire hardware rendering context");
-        }
-        layer.flushChanges();
-        return GLES20Canvas.nCopyLayer(layer.getLayer(), bitmap.mNativeBitmap);
-    }
-
-    @Override
-    boolean safelyRun(Runnable action) {
-        boolean needsContext = !isEnabled() || checkRenderContext() == SURFACE_STATE_ERROR;
-
-        if (needsContext) {
-            GLRendererEglContext managedContext =
-                    (GLRendererEglContext) sEglContextStorage.get();
-            if (managedContext == null) return false;
-            usePbufferSurface(managedContext.getContext());
-        }
-
-        try {
-            action.run();
-        } finally {
-            if (needsContext) {
-                sEgl.eglMakeCurrent(sEglDisplay, EGL_NO_SURFACE,
-                        EGL_NO_SURFACE, EGL_NO_CONTEXT);
-            }
-        }
-
-        return true;
-    }
-
-    @Override
-    void invokeFunctor(long functor, boolean waitForCompletion) {
-        boolean needsContext = !isEnabled() || checkRenderContext() == SURFACE_STATE_ERROR;
-        boolean hasContext = !needsContext;
-
-        if (needsContext) {
-            GLRendererEglContext managedContext =
-                    (GLRendererEglContext) sEglContextStorage.get();
-            if (managedContext != null) {
-                usePbufferSurface(managedContext.getContext());
-                hasContext = true;
-            }
-        }
-
-        try {
-            nInvokeFunctor(functor, hasContext);
-        } finally {
-            if (needsContext) {
-                sEgl.eglMakeCurrent(sEglDisplay, EGL_NO_SURFACE,
-                        EGL_NO_SURFACE, EGL_NO_CONTEXT);
-            }
-        }
-    }
-
-    private static native void nInvokeFunctor(long functor, boolean hasContext);
-
-    @Override
-    void destroyHardwareResources(final View view) {
-        if (view != null) {
-            safelyRun(new Runnable() {
-                @Override
-                public void run() {
-                    if (mCanvas != null) {
-                        mCanvas.clearLayerUpdates();
-                    }
-                    destroyResources(view);
-                    GLES20Canvas.flushCaches(GLES20Canvas.FLUSH_CACHES_LAYERS);
-                }
-            });
-        }
-    }
-
-    private static void destroyResources(View view) {
-        view.destroyHardwareResources();
-
-        if (view instanceof ViewGroup) {
-            ViewGroup group = (ViewGroup) view;
-
-            int count = group.getChildCount();
-            for (int i = 0; i < count; i++) {
-                destroyResources(group.getChildAt(i));
-            }
-        }
-    }
-
-    static void startTrimMemory(int level) {
-        if (sEgl == null || sEglConfig == null) return;
-
-        GLRendererEglContext managedContext =
-                (GLRendererEglContext) sEglContextStorage.get();
-        // We do not have OpenGL objects
-        if (managedContext == null) {
-            return;
-        } else {
-            usePbufferSurface(managedContext.getContext());
-        }
-
-        if (level >= ComponentCallbacks2.TRIM_MEMORY_COMPLETE) {
-            GLES20Canvas.flushCaches(GLES20Canvas.FLUSH_CACHES_FULL);
-        } else if (level >= ComponentCallbacks2.TRIM_MEMORY_UI_HIDDEN) {
-            GLES20Canvas.flushCaches(GLES20Canvas.FLUSH_CACHES_MODERATE);
-        }
-    }
-
-    static void endTrimMemory() {
-        if (sEgl != null && sEglDisplay != null) {
-            sEgl.eglMakeCurrent(sEglDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
-        }
-    }
-
-    private static void usePbufferSurface(EGLContext eglContext) {
-        synchronized (sPbufferLock) {
-            // Create a temporary 1x1 pbuffer so we have a context
-            // to clear our OpenGL objects
-            if (sPbuffer == null) {
-                sPbuffer = sEgl.eglCreatePbufferSurface(sEglDisplay, sEglConfig, new int[] {
-                        EGL_WIDTH, 1, EGL_HEIGHT, 1, EGL_NONE
-                });
-            }
-        }
-        sEgl.eglMakeCurrent(sEglDisplay, sPbuffer, sPbuffer, eglContext);
-    }
-
-    GLRenderer(boolean translucent) {
-        mTranslucent = translucent;
-
-        loadSystemProperties();
-    }
-
-    @Override
-    void setOpaque(boolean opaque) {
-        // Not supported
-    }
-
-    @Override
-    boolean loadSystemProperties() {
-        boolean value;
-        boolean changed = false;
-
-        String profiling = SystemProperties.get(PROFILE_PROPERTY);
-        int graphType = search(VISUALIZERS, profiling);
-        value = graphType >= 0;
-
-        if (graphType != mProfileVisualizerType) {
-            changed = true;
-            mProfileVisualizerType = graphType;
-
-            mProfileShapes = null;
-            mProfilePaint = null;
-
-            if (value) {
-                mDebugDataProvider = new DrawPerformanceDataProvider(graphType);
-            } else {
-                mDebugDataProvider = null;
-            }
-        }
-
-        // If on-screen profiling is not enabled, we need to check whether
-        // console profiling only is enabled
-        if (!value) {
-            value = Boolean.parseBoolean(profiling);
-        }
-
-        if (value != mProfileEnabled) {
-            changed = true;
-            mProfileEnabled = value;
-
-            if (mProfileEnabled) {
-                Log.d(LOG_TAG, "Profiling hardware renderer");
-
-                int maxProfileFrames = SystemProperties.getInt(PROFILE_MAXFRAMES_PROPERTY,
-                        PROFILE_MAX_FRAMES);
-                mProfileData = new float[maxProfileFrames * PROFILE_FRAME_DATA_COUNT];
-                for (int i = 0; i < mProfileData.length; i += PROFILE_FRAME_DATA_COUNT) {
-                    mProfileData[i] = mProfileData[i + 1] = mProfileData[i + 2] = -1;
-                }
-
-                mProfileLock = new ReentrantLock();
-            } else {
-                mProfileData = null;
-                mProfileLock = null;
-                mProfileVisualizerType = -1;
-            }
-
-            mProfileCurrentFrame = -PROFILE_FRAME_DATA_COUNT;
-        }
-
-        value = SystemProperties.getBoolean(DEBUG_DIRTY_REGIONS_PROPERTY, false);
-        if (value != mDebugDirtyRegions) {
-            changed = true;
-            mDebugDirtyRegions = value;
-
-            if (mDebugDirtyRegions) {
-                Log.d(LOG_TAG, "Debugging dirty regions");
-            }
-        }
-
-        String overdraw = SystemProperties.get(HardwareRenderer.DEBUG_OVERDRAW_PROPERTY);
-        int debugOverdraw = search(OVERDRAW, overdraw);
-        if (debugOverdraw != mDebugOverdraw) {
-            changed = true;
-            mDebugOverdraw = debugOverdraw;
-        }
-
-        if (loadProperties()) {
-            changed = true;
-        }
-
-        return changed;
-    }
-
-    private static int search(String[] values, String value) {
-        for (int i = 0; i < values.length; i++) {
-            if (values[i].equals(value)) return i;
-        }
-        return -1;
-    }
-
-    @Override
-    void dumpGfxInfo(PrintWriter pw) {
-        if (mProfileEnabled) {
-            pw.printf("\n\tDraw\tProcess\tExecute\n");
-
-            mProfileLock.lock();
-            try {
-                for (int i = 0; i < mProfileData.length; i += PROFILE_FRAME_DATA_COUNT) {
-                    if (mProfileData[i] < 0) {
-                        break;
-                    }
-                    pw.printf("\t%3.2f\t%3.2f\t%3.2f\n", mProfileData[i], mProfileData[i + 1],
-                            mProfileData[i + 2]);
-                    mProfileData[i] = mProfileData[i + 1] = mProfileData[i + 2] = -1;
-                }
-                mProfileCurrentFrame = mProfileData.length;
-            } finally {
-                mProfileLock.unlock();
-            }
-        }
-    }
-
-    @Override
-    long getFrameCount() {
-        return mFrameCount;
-    }
-
-    /**
-     * Indicates whether this renderer instance can track and update dirty regions.
-     */
-    boolean hasDirtyRegions() {
-        return mDirtyRegionsEnabled;
-    }
-
-    /**
-     * Checks for OpenGL errors. If an error has occured, {@link #destroy(boolean)}
-     * is invoked and the requested flag is turned off. The error code is
-     * also logged as a warning.
-     */
-    void checkEglErrors() {
-        if (isEnabled()) {
-            checkEglErrorsForced();
-        }
-    }
-
-    private void checkEglErrorsForced() {
-        int error = sEgl.eglGetError();
-        if (error != EGL_SUCCESS) {
-            // something bad has happened revert to
-            // normal rendering.
-            Log.w(LOG_TAG, "EGL error: " + GLUtils.getEGLErrorString(error));
-            fallback(error != EGL11.EGL_CONTEXT_LOST);
-        }
-    }
-
-    private void fallback(boolean fallback) {
-        destroy(true);
-        if (fallback) {
-            // we'll try again if it was context lost
-            setRequested(false);
-            Log.w(LOG_TAG, "Mountain View, we've had a problem here. "
-                    + "Switching back to software rendering.");
-        }
-    }
-
-    @Override
-    boolean initialize(Surface surface) throws OutOfResourcesException {
-        if (isRequested() && !isEnabled()) {
-            boolean contextCreated = initializeEgl();
-            mGl = createEglSurface(surface);
-            mDestroyed = false;
-
-            if (mGl != null) {
-                int err = sEgl.eglGetError();
-                if (err != EGL_SUCCESS) {
-                    destroy(true);
-                    setRequested(false);
-                } else {
-                    if (mCanvas == null) {
-                        mCanvas = createCanvas();
-                    }
-                    setEnabled(true);
-
-                    if (contextCreated) {
-                        initAtlas();
-                    }
-                }
-
-                return mCanvas != null;
-            }
-        }
-        return false;
-    }
-
-    @Override
-    void updateSurface(Surface surface) throws OutOfResourcesException {
-        if (isRequested() && isEnabled()) {
-            createEglSurface(surface);
-        }
-    }
-
-    @Override
-    void pauseSurface(Surface surface) {
-        // No-op
-    }
-
-    boolean initializeEgl() {
-        synchronized (sEglLock) {
-            if (sEgl == null && sEglConfig == null) {
-                sEgl = (EGL10) EGLContext.getEGL();
-
-                // Get to the default display.
-                sEglDisplay = sEgl.eglGetDisplay(EGL_DEFAULT_DISPLAY);
-
-                if (sEglDisplay == EGL_NO_DISPLAY) {
-                    throw new RuntimeException("eglGetDisplay failed "
-                            + GLUtils.getEGLErrorString(sEgl.eglGetError()));
-                }
-
-                // We can now initialize EGL for that display
-                int[] version = new int[2];
-                if (!sEgl.eglInitialize(sEglDisplay, version)) {
-                    throw new RuntimeException("eglInitialize failed " +
-                            GLUtils.getEGLErrorString(sEgl.eglGetError()));
-                }
-
-                checkEglErrorsForced();
-
-                sEglConfig = loadEglConfig();
-            }
-        }
-
-        ManagedEGLContext managedContext = sEglContextStorage.get();
-        mEglContext = managedContext != null ? managedContext.getContext() : null;
-        mEglThread = Thread.currentThread();
-
-        if (mEglContext == null) {
-            mEglContext = createContext(sEgl, sEglDisplay, sEglConfig);
-            sEglContextStorage.set(createManagedContext(mEglContext));
-            return true;
-        }
-
-        return false;
-    }
-
-    private EGLConfig loadEglConfig() {
-        EGLConfig eglConfig = chooseEglConfig();
-        if (eglConfig == null) {
-            // We tried to use EGL_SWAP_BEHAVIOR_PRESERVED_BIT, try again without
-            if (sDirtyRegions) {
-                sDirtyRegions = false;
-                eglConfig = chooseEglConfig();
-                if (eglConfig == null) {
-                    throw new RuntimeException("eglConfig not initialized");
-                }
-            } else {
-                throw new RuntimeException("eglConfig not initialized");
-            }
-        }
-        return eglConfig;
-    }
-
-    private EGLConfig chooseEglConfig() {
-        EGLConfig[] configs = new EGLConfig[1];
-        int[] configsCount = new int[1];
-        int[] configSpec = getConfig(sDirtyRegions);
-
-        // Debug
-        final String debug = SystemProperties.get(PRINT_CONFIG_PROPERTY, "");
-        if ("all".equalsIgnoreCase(debug)) {
-            sEgl.eglChooseConfig(sEglDisplay, configSpec, null, 0, configsCount);
-
-            EGLConfig[] debugConfigs = new EGLConfig[configsCount[0]];
-            sEgl.eglChooseConfig(sEglDisplay, configSpec, debugConfigs,
-                    configsCount[0], configsCount);
-
-            for (EGLConfig config : debugConfigs) {
-                printConfig(config);
-            }
-        }
-
-        if (!sEgl.eglChooseConfig(sEglDisplay, configSpec, configs, 1, configsCount)) {
-            throw new IllegalArgumentException("eglChooseConfig failed " +
-                    GLUtils.getEGLErrorString(sEgl.eglGetError()));
-        } else if (configsCount[0] > 0) {
-            if ("choice".equalsIgnoreCase(debug)) {
-                printConfig(configs[0]);
-            }
-            return configs[0];
-        }
-
-        return null;
-    }
-
-    private static void printConfig(EGLConfig config) {
-        int[] value = new int[1];
-
-        Log.d(LOG_TAG, "EGL configuration " + config + ":");
-
-        sEgl.eglGetConfigAttrib(sEglDisplay, config, EGL_RED_SIZE, value);
-        Log.d(LOG_TAG, "  RED_SIZE = " + value[0]);
-
-        sEgl.eglGetConfigAttrib(sEglDisplay, config, EGL_GREEN_SIZE, value);
-        Log.d(LOG_TAG, "  GREEN_SIZE = " + value[0]);
-
-        sEgl.eglGetConfigAttrib(sEglDisplay, config, EGL_BLUE_SIZE, value);
-        Log.d(LOG_TAG, "  BLUE_SIZE = " + value[0]);
-
-        sEgl.eglGetConfigAttrib(sEglDisplay, config, EGL_ALPHA_SIZE, value);
-        Log.d(LOG_TAG, "  ALPHA_SIZE = " + value[0]);
-
-        sEgl.eglGetConfigAttrib(sEglDisplay, config, EGL_DEPTH_SIZE, value);
-        Log.d(LOG_TAG, "  DEPTH_SIZE = " + value[0]);
-
-        sEgl.eglGetConfigAttrib(sEglDisplay, config, EGL_STENCIL_SIZE, value);
-        Log.d(LOG_TAG, "  STENCIL_SIZE = " + value[0]);
-
-        sEgl.eglGetConfigAttrib(sEglDisplay, config, EGL_SAMPLE_BUFFERS, value);
-        Log.d(LOG_TAG, "  SAMPLE_BUFFERS = " + value[0]);
-
-        sEgl.eglGetConfigAttrib(sEglDisplay, config, EGL_SAMPLES, value);
-        Log.d(LOG_TAG, "  SAMPLES = " + value[0]);
-
-        sEgl.eglGetConfigAttrib(sEglDisplay, config, EGL_SURFACE_TYPE, value);
-        Log.d(LOG_TAG, "  SURFACE_TYPE = 0x" + Integer.toHexString(value[0]));
-
-        sEgl.eglGetConfigAttrib(sEglDisplay, config, EGL_CONFIG_CAVEAT, value);
-        Log.d(LOG_TAG, "  CONFIG_CAVEAT = 0x" + Integer.toHexString(value[0]));
-    }
-
-    GL createEglSurface(Surface surface) throws OutOfResourcesException {
-        // Check preconditions.
-        if (sEgl == null) {
-            throw new RuntimeException("egl not initialized");
-        }
-        if (sEglDisplay == null) {
-            throw new RuntimeException("eglDisplay not initialized");
-        }
-        if (sEglConfig == null) {
-            throw new RuntimeException("eglConfig not initialized");
-        }
-        if (Thread.currentThread() != mEglThread) {
-            throw new IllegalStateException("HardwareRenderer cannot be used "
-                    + "from multiple threads");
-        }
-
-        // In case we need to destroy an existing surface
-        destroySurface();
-
-        // Create an EGL surface we can render into.
-        if (!createSurface(surface)) {
-            return null;
-        }
-
-        initCaches();
-
-        return mEglContext.getGL();
-    }
-
-    private void enableDirtyRegions() {
-        // If mDirtyRegions is set, this means we have an EGL configuration
-        // with EGL_SWAP_BEHAVIOR_PRESERVED_BIT set
-        if (sDirtyRegions) {
-            if (!(mDirtyRegionsEnabled = preserveBackBuffer())) {
-                Log.w(LOG_TAG, "Backbuffer cannot be preserved");
-            }
-        } else if (sDirtyRegionsRequested) {
-            // If mDirtyRegions is not set, our EGL configuration does not
-            // have EGL_SWAP_BEHAVIOR_PRESERVED_BIT; however, the default
-            // swap behavior might be EGL_BUFFER_PRESERVED, which means we
-            // want to set mDirtyRegions. We try to do this only if dirty
-            // regions were initially requested as part of the device
-            // configuration (see RENDER_DIRTY_REGIONS)
-            mDirtyRegionsEnabled = isBackBufferPreserved();
-        }
-    }
-
-    EGLContext createContext(EGL10 egl, EGLDisplay eglDisplay, EGLConfig eglConfig) {
-        final int[] attribs = { EGL14.EGL_CONTEXT_CLIENT_VERSION, GL_VERSION, EGL_NONE };
-
-        EGLContext context = egl.eglCreateContext(eglDisplay, eglConfig, EGL_NO_CONTEXT,
-                attribs);
-        if (context == null || context == EGL_NO_CONTEXT) {
-            //noinspection ConstantConditions
-            throw new IllegalStateException(
-                    "Could not create an EGL context. eglCreateContext failed with error: " +
-                    GLUtils.getEGLErrorString(sEgl.eglGetError()));
-        }
-
-        return context;
-    }
-
-    void destroySurface() {
-        if (mEglSurface != null && mEglSurface != EGL_NO_SURFACE) {
-            if (mEglSurface.equals(sEgl.eglGetCurrentSurface(EGL_DRAW))) {
-                sEgl.eglMakeCurrent(sEglDisplay,
-                        EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
-            }
-            sEgl.eglDestroySurface(sEglDisplay, mEglSurface);
-            mEglSurface = null;
-        }
-    }
-
-    @Override
-    void invalidate(Surface surface) {
-        // Cancels any existing buffer to ensure we'll get a buffer
-        // of the right size before we call eglSwapBuffers
-        sEgl.eglMakeCurrent(sEglDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
-
-        if (mEglSurface != null && mEglSurface != EGL_NO_SURFACE) {
-            sEgl.eglDestroySurface(sEglDisplay, mEglSurface);
-            mEglSurface = null;
-            setEnabled(false);
-        }
-
-        if (surface.isValid()) {
-            if (!createSurface(surface)) {
-                return;
-            }
-
-            mUpdateDirtyRegions = true;
-
-            if (mCanvas != null) {
-                setEnabled(true);
-            }
-        }
-    }
-
-    private boolean createSurface(Surface surface) {
-        mEglSurface = sEgl.eglCreateWindowSurface(sEglDisplay, sEglConfig, surface, null);
-
-        if (mEglSurface == null || mEglSurface == EGL_NO_SURFACE) {
-            int error = sEgl.eglGetError();
-            if (error == EGL_BAD_NATIVE_WINDOW) {
-                Log.e(LOG_TAG, "createWindowSurface returned EGL_BAD_NATIVE_WINDOW.");
-                return false;
-            }
-            throw new RuntimeException("createWindowSurface failed "
-                    + GLUtils.getEGLErrorString(error));
-        }
-
-        if (!sEgl.eglMakeCurrent(sEglDisplay, mEglSurface, mEglSurface, mEglContext)) {
-            throw new IllegalStateException("eglMakeCurrent failed " +
-                    GLUtils.getEGLErrorString(sEgl.eglGetError()));
-        }
-
-        enableDirtyRegions();
-
-        return true;
-    }
-
-    boolean validate() {
-        return checkRenderContext() != SURFACE_STATE_ERROR;
-    }
-
-    @Override
-    void setup(int width, int height, float lightX, float lightY, float lightZ, float lightRadius) {
-        if (validate()) {
-            mCanvas.setViewport(width, height);
-            mCanvas.initializeLight(lightX, lightY, lightZ, lightRadius);
-            mWidth = width;
-            mHeight = height;
-        }
-    }
-
-    @Override
-    int getWidth() {
-        return mWidth;
-    }
-
-    @Override
-    int getHeight() {
-        return mHeight;
-    }
-
-    @Override
-    void setName(String name) {
-        mName = name;
-    }
-
-    @Override
-    void draw(View view, View.AttachInfo attachInfo, HardwareDrawCallbacks callbacks,
-            Rect dirty) {
-        if (canDraw()) {
-            if (!hasDirtyRegions()) {
-                dirty = null;
-            }
-            attachInfo.mIgnoreDirtyState = true;
-            attachInfo.mDrawingTime = SystemClock.uptimeMillis();
-
-            view.mPrivateFlags |= View.PFLAG_DRAWN;
-
-            // We are already on the correct thread
-            final int surfaceState = checkRenderContextUnsafe();
-            if (surfaceState != SURFACE_STATE_ERROR) {
-                HardwareCanvas canvas = mCanvas;
-
-                if (mProfileEnabled) {
-                    mProfileLock.lock();
-                }
-
-                dirty = beginFrame(canvas, dirty, surfaceState);
-
-                RenderNode displayList = buildDisplayList(view, canvas);
-
-                flushLayerChanges();
-
-                // buildDisplayList() calls into user code which can cause
-                // an eglMakeCurrent to happen with a different surface/context.
-                // We must therefore check again here.
-                if (checkRenderContextUnsafe() == SURFACE_STATE_ERROR) {
-                    return;
-                }
-
-                int saveCount = 0;
-                int status = RenderNode.STATUS_DONE;
-
-                long start = getSystemTime();
-                try {
-                    status = prepareFrame(dirty);
-
-                    saveCount = canvas.save();
-                    callbacks.onHardwarePreDraw(canvas);
-
-                    if (displayList != null) {
-                        status |= drawDisplayList(canvas, displayList, status);
-                    } else {
-                        // Shouldn't reach here
-                        view.draw(canvas);
-                    }
-                } catch (Exception e) {
-                    Log.e(LOG_TAG, "An error has occurred while drawing:", e);
-                } finally {
-                    callbacks.onHardwarePostDraw(canvas);
-                    canvas.restoreToCount(saveCount);
-                    view.mRecreateDisplayList = false;
-
-                    mDrawDelta = getSystemTime() - start;
-
-                    if (mDrawDelta > 0) {
-                        mFrameCount++;
-
-                        debugDirtyRegions(dirty, canvas);
-                        drawProfileData(attachInfo);
-                    }
-                }
-
-                onPostDraw();
-
-                swapBuffers(status);
-
-                if (mProfileEnabled) {
-                    mProfileLock.unlock();
-                }
-
-                attachInfo.mIgnoreDirtyState = false;
-            }
-        }
-    }
-
-    private void flushLayerChanges() {
-        // Loop through and apply any pending layer changes
-        for (int i = 0; i < mAttachedLayers.size(); i++) {
-            HardwareLayer layer = mAttachedLayers.get(i);
-            layer.flushChanges();
-            if (!layer.isValid()) {
-                // The layer was removed from mAttachedLayers, rewind i by 1
-                // Note that this shouldn't actually happen as View.getHardwareLayer()
-                // is already flushing for error checking reasons
-                i--;
-            }
-        }
-    }
-
-    @Override
-    void fence() {
-        // Everything is immediate, so this is a no-op
-    }
-
-    private RenderNode buildDisplayList(View view, HardwareCanvas canvas) {
-        view.mRecreateDisplayList = (view.mPrivateFlags & View.PFLAG_INVALIDATED)
-                == View.PFLAG_INVALIDATED;
-        view.mPrivateFlags &= ~View.PFLAG_INVALIDATED;
-
-        long buildDisplayListStartTime = startBuildDisplayListProfiling();
-        canvas.clearLayerUpdates();
-
-        Trace.traceBegin(Trace.TRACE_TAG_VIEW, "getDisplayList");
-        RenderNode renderNode = view.getDisplayList();
-        Trace.traceEnd(Trace.TRACE_TAG_VIEW);
-
-        endBuildDisplayListProfiling(buildDisplayListStartTime);
-
-        return renderNode;
-    }
-
-    private Rect beginFrame(HardwareCanvas canvas, Rect dirty, int surfaceState) {
-        // We had to change the current surface and/or context, redraw everything
-        if (surfaceState == SURFACE_STATE_UPDATED) {
-            dirty = null;
-            beginFrame(null);
-        } else {
-            int[] size = mSurfaceSize;
-            beginFrame(size);
-
-            if (size[1] != mHeight || size[0] != mWidth) {
-                mWidth = size[0];
-                mHeight = size[1];
-
-                canvas.setViewport(mWidth, mHeight);
-
-                dirty = null;
-            }
-        }
-
-        if (mDebugDataProvider != null) dirty = null;
-
-        return dirty;
-    }
-
-    private long startBuildDisplayListProfiling() {
-        if (mProfileEnabled) {
-            mProfileCurrentFrame += PROFILE_FRAME_DATA_COUNT;
-            if (mProfileCurrentFrame >= mProfileData.length) {
-                mProfileCurrentFrame = 0;
-            }
-
-            return System.nanoTime();
-        }
-        return 0;
-    }
-
-    private void endBuildDisplayListProfiling(long getDisplayListStartTime) {
-        if (mProfileEnabled) {
-            long now = System.nanoTime();
-            float total = (now - getDisplayListStartTime) * 0.000001f;
-            //noinspection PointlessArithmeticExpression
-            mProfileData[mProfileCurrentFrame] = total;
-        }
-    }
-
-    private int prepareFrame(Rect dirty) {
-        int status;
-        Trace.traceBegin(Trace.TRACE_TAG_VIEW, "prepareFrame");
-        try {
-            status = onPreDraw(dirty);
-        } finally {
-            Trace.traceEnd(Trace.TRACE_TAG_VIEW);
-        }
-        return status;
-    }
-
-    private int drawDisplayList(HardwareCanvas canvas, RenderNode displayList,
-            int status) {
-
-        long drawDisplayListStartTime = 0;
-        if (mProfileEnabled) {
-            drawDisplayListStartTime = System.nanoTime();
-        }
-
-        Trace.traceBegin(Trace.TRACE_TAG_VIEW, "drawDisplayList");
-        nPrepareTree(displayList.getNativeDisplayList());
-        try {
-            status |= canvas.drawDisplayList(displayList, mRedrawClip,
-                    RenderNode.FLAG_CLIP_CHILDREN);
-        } finally {
-            Trace.traceEnd(Trace.TRACE_TAG_VIEW);
-        }
-
-        if (mProfileEnabled) {
-            long now = System.nanoTime();
-            float total = (now - drawDisplayListStartTime) * 0.000001f;
-            mProfileData[mProfileCurrentFrame + 1] = total;
-        }
-
-        return status;
-    }
-
-    private void swapBuffers(int status) {
-        if ((status & RenderNode.STATUS_DREW) == RenderNode.STATUS_DREW) {
-            long eglSwapBuffersStartTime = 0;
-            if (mProfileEnabled) {
-                eglSwapBuffersStartTime = System.nanoTime();
-            }
-
-            sEgl.eglSwapBuffers(sEglDisplay, mEglSurface);
-
-            if (mProfileEnabled) {
-                long now = System.nanoTime();
-                float total = (now - eglSwapBuffersStartTime) * 0.000001f;
-                mProfileData[mProfileCurrentFrame + 2] = total;
-            }
-
-            checkEglErrors();
-        }
-    }
-
-    private void debugDirtyRegions(Rect dirty, HardwareCanvas canvas) {
-        if (mDebugDirtyRegions) {
-            if (mDebugPaint == null) {
-                mDebugPaint = new Paint();
-                mDebugPaint.setColor(0x7fff0000);
-            }
-
-            if (dirty != null && (mFrameCount & 1) == 0) {
-                canvas.drawRect(dirty, mDebugPaint);
-            }
-        }
-    }
-
-    /**
-     * Ensures the current EGL context and surface are the ones we expect.
-     * This method throws an IllegalStateException if invoked from a thread
-     * that did not initialize EGL.
-     *
-     * @return {@link #SURFACE_STATE_ERROR} if the correct EGL context cannot be made current,
-     *         {@link #SURFACE_STATE_UPDATED} if the EGL context was changed or
-     *         {@link #SURFACE_STATE_SUCCESS} if the EGL context was the correct one
-     *
-     * @see #checkRenderContextUnsafe()
-     */
-    int checkRenderContext() {
-        if (mEglThread != Thread.currentThread()) {
-            throw new IllegalStateException("Hardware acceleration can only be used with a " +
-                    "single UI thread.\nOriginal thread: " + mEglThread + "\n" +
-                    "Current thread: " + Thread.currentThread());
-        }
-
-        return checkRenderContextUnsafe();
-    }
-
-    /**
-     * Ensures the current EGL context and surface are the ones we expect.
-     * This method does not check the current thread.
-     *
-     * @return {@link #SURFACE_STATE_ERROR} if the correct EGL context cannot be made current,
-     *         {@link #SURFACE_STATE_UPDATED} if the EGL context was changed or
-     *         {@link #SURFACE_STATE_SUCCESS} if the EGL context was the correct one
-     *
-     * @see #checkRenderContext()
-     */
-    private int checkRenderContextUnsafe() {
-        if (!mEglSurface.equals(sEgl.eglGetCurrentSurface(EGL_DRAW)) ||
-                !mEglContext.equals(sEgl.eglGetCurrentContext())) {
-            if (!sEgl.eglMakeCurrent(sEglDisplay, mEglSurface, mEglSurface, mEglContext)) {
-                Log.e(LOG_TAG, "eglMakeCurrent failed " +
-                        GLUtils.getEGLErrorString(sEgl.eglGetError()));
-                fallback(true);
-                return SURFACE_STATE_ERROR;
-            } else {
-                if (mUpdateDirtyRegions) {
-                    enableDirtyRegions();
-                    mUpdateDirtyRegions = false;
-                }
-                return SURFACE_STATE_UPDATED;
-            }
-        }
-        return SURFACE_STATE_SUCCESS;
-    }
-
-    private static int dpToPx(int dp, float density) {
-        return (int) (dp * density + 0.5f);
-    }
-
-    static native boolean loadProperties();
-
-    static native void setupShadersDiskCache(String cacheFile);
-
-    /**
-     * Notifies EGL that the frame is about to be rendered.
-     * @param size
-     */
-    static native void beginFrame(int[] size);
-
-    /**
-     * Returns the current system time according to the renderer.
-     * This method is used for debugging only and should not be used
-     * as a clock.
-     */
-    static native long getSystemTime();
-
-    /**
-     * Preserves the back buffer of the current surface after a buffer swap.
-     * Calling this method sets the EGL_SWAP_BEHAVIOR attribute of the current
-     * surface to EGL_BUFFER_PRESERVED. Calling this method requires an EGL
-     * config that supports EGL_SWAP_BEHAVIOR_PRESERVED_BIT.
-     *
-     * @return True if the swap behavior was successfully changed,
-     *         false otherwise.
-     */
-    static native boolean preserveBackBuffer();
-
-    /**
-     * Indicates whether the current surface preserves its back buffer
-     * after a buffer swap.
-     *
-     * @return True, if the surface's EGL_SWAP_BEHAVIOR is EGL_BUFFER_PRESERVED,
-     *         false otherwise
-     */
-    static native boolean isBackBufferPreserved();
-
-    static native void nDestroyLayer(long layerPtr);
-
-    private static native void nPrepareTree(long displayListPtr);
-
-    class DrawPerformanceDataProvider extends GraphDataProvider {
-        private final int mGraphType;
-
-        private int mVerticalUnit;
-        private int mHorizontalUnit;
-        private int mHorizontalMargin;
-        private int mThresholdStroke;
-
-        DrawPerformanceDataProvider(int graphType) {
-            mGraphType = graphType;
-        }
-
-        @Override
-        void prepare(DisplayMetrics metrics) {
-            final float density = metrics.density;
-
-            mVerticalUnit = dpToPx(PROFILE_DRAW_DP_PER_MS, density);
-            mHorizontalUnit = dpToPx(PROFILE_DRAW_WIDTH, density);
-            mHorizontalMargin = dpToPx(PROFILE_DRAW_MARGIN, density);
-            mThresholdStroke = dpToPx(PROFILE_DRAW_THRESHOLD_STROKE_WIDTH, density);
-        }
-
-        @Override
-        int getGraphType() {
-            return mGraphType;
-        }
-
-        @Override
-        int getVerticalUnitSize() {
-            return mVerticalUnit;
-        }
-
-        @Override
-        int getHorizontalUnitSize() {
-            return mHorizontalUnit;
-        }
-
-        @Override
-        int getHorizontaUnitMargin() {
-            return mHorizontalMargin;
-        }
-
-        @Override
-        float[] getData() {
-            return mProfileData;
-        }
-
-        @Override
-        float getThreshold() {
-            return 16;
-        }
-
-        @Override
-        int getFrameCount() {
-            return mProfileData.length / PROFILE_FRAME_DATA_COUNT;
-        }
-
-        @Override
-        int getElementCount() {
-            return PROFILE_FRAME_DATA_COUNT;
-        }
-
-        @Override
-        int getCurrentFrame() {
-            return mProfileCurrentFrame / PROFILE_FRAME_DATA_COUNT;
-        }
-
-        @Override
-        void setupGraphPaint(Paint paint, int elementIndex) {
-            paint.setColor(PROFILE_DRAW_COLORS[elementIndex]);
-            if (mGraphType == GRAPH_TYPE_LINES) paint.setStrokeWidth(mThresholdStroke);
-        }
-
-        @Override
-        void setupThresholdPaint(Paint paint) {
-            paint.setColor(PROFILE_DRAW_THRESHOLD_COLOR);
-            paint.setStrokeWidth(mThresholdStroke);
-        }
-
-        @Override
-        void setupCurrentFramePaint(Paint paint) {
-            paint.setColor(PROFILE_DRAW_CURRENT_FRAME_COLOR);
-            if (mGraphType == GRAPH_TYPE_LINES) paint.setStrokeWidth(mThresholdStroke);
-        }
-    }
-}
diff --git a/core/java/android/view/HardwareCanvas.java b/core/java/android/view/HardwareCanvas.java
index 9568760..b8e7d8c 100644
--- a/core/java/android/view/HardwareCanvas.java
+++ b/core/java/android/view/HardwareCanvas.java
@@ -110,48 +110,6 @@
         return RenderNode.STATUS_DONE;
     }
 
-    /**
-     * Indicates that the specified layer must be updated as soon as possible.
-     *
-     * @param layer The layer to update
-     *
-     * @see #clearLayerUpdates()
-     *
-     * @hide
-     */
-    abstract void pushLayerUpdate(HardwareLayer layer);
-
-    /**
-     * Cancels a queued layer update. If the specified layer was not
-     * queued for update, this method has no effect.
-     *
-     * @param layer The layer whose update to cancel
-     *
-     * @see #pushLayerUpdate(HardwareLayer)
-     * @see #clearLayerUpdates()
-     *
-     * @hide
-     */
-    abstract void cancelLayerUpdate(HardwareLayer layer);
-
-    /**
-     * Immediately executes all enqueued layer updates.
-     *
-     * @see #pushLayerUpdate(HardwareLayer)
-     *
-     * @hide
-     */
-    abstract void flushLayerUpdates();
-
-    /**
-     * Removes all enqueued layer updates.
-     *
-     * @see #pushLayerUpdate(HardwareLayer)
-     *
-     * @hide
-     */
-    abstract void clearLayerUpdates();
-
     public abstract void drawCircle(CanvasProperty<Float> cx, CanvasProperty<Float> cy,
             CanvasProperty<Float> radius, CanvasProperty<Paint> paint);
 }
diff --git a/core/java/android/view/HardwareLayer.java b/core/java/android/view/HardwareLayer.java
index 4d78733..6acb134 100644
--- a/core/java/android/view/HardwareLayer.java
+++ b/core/java/android/view/HardwareLayer.java
@@ -22,6 +22,8 @@
 import android.graphics.Rect;
 import android.graphics.SurfaceTexture;
 
+import com.android.internal.util.VirtualRefBasePtr;
+
 /**
  * A hardware layer can be used to render graphics operations into a hardware
  * friendly buffer. For instance, with an OpenGL backend a hardware layer
@@ -36,7 +38,7 @@
     private static final int LAYER_TYPE_DISPLAY_LIST = 2;
 
     private HardwareRenderer mRenderer;
-    private Finalizer mFinalizer;
+    private VirtualRefBasePtr mFinalizer;
     private RenderNode mDisplayList;
     private final int mLayerType;
 
@@ -47,10 +49,7 @@
         }
         mRenderer = renderer;
         mLayerType = type;
-        mFinalizer = new Finalizer(deferredUpdater);
-
-        // Layer is considered initialized at this point, notify the HardwareRenderer
-        mRenderer.onLayerCreated(this);
+        mFinalizer = new VirtualRefBasePtr(deferredUpdater);
     }
 
     private void assertType(int type) {
@@ -59,6 +58,10 @@
         }
     }
 
+    boolean hasDisplayList() {
+        return mDisplayList != null;
+    }
+
     /**
      * Update the paint used when drawing this layer.
      *
@@ -66,7 +69,8 @@
      * @see View#setLayerPaint(android.graphics.Paint)
      */
     public void setLayerPaint(Paint paint) {
-        nSetLayerPaint(mFinalizer.mDeferredUpdater, paint.mNativePaint);
+        nSetLayerPaint(mFinalizer.get(), paint.mNativePaint);
+        mRenderer.pushLayerUpdate(this);
     }
 
     /**
@@ -75,7 +79,7 @@
      * @return True if the layer can be rendered into, false otherwise
      */
     public boolean isValid() {
-        return mFinalizer != null && mFinalizer.mDeferredUpdater != 0;
+        return mFinalizer != null && mFinalizer.get() != 0;
     }
 
     /**
@@ -91,35 +95,14 @@
             mDisplayList.destroyDisplayListData();
             mDisplayList = null;
         }
-        if (mRenderer != null) {
-            mRenderer.onLayerDestroyed(this);
-            mRenderer = null;
-        }
-        doDestroyLayerUpdater();
+        mRenderer.onLayerDestroyed(this);
+        mRenderer = null;
+        mFinalizer.release();
+        mFinalizer = null;
     }
 
     public long getDeferredLayerUpdater() {
-        return mFinalizer.mDeferredUpdater;
-    }
-
-    /**
-     * Destroys the deferred layer updater but not the backing layer. The
-     * backing layer is instead returned and is the caller's responsibility
-     * to destroy/recycle as appropriate.
-     *
-     * It is safe to call this in onLayerDestroyed only
-     */
-    public long detachBackingLayer() {
-        long backingLayer = nDetachBackingLayer(mFinalizer.mDeferredUpdater);
-        doDestroyLayerUpdater();
-        return backingLayer;
-    }
-
-    private void doDestroyLayerUpdater() {
-        if (mFinalizer != null) {
-            mFinalizer.destroy();
-            mFinalizer = null;
-        }
+        return mFinalizer.get();
     }
 
     public RenderNode startRecording() {
@@ -132,7 +115,7 @@
     }
 
     public void endRecording(Rect dirtyRect) {
-        nUpdateRenderLayer(mFinalizer.mDeferredUpdater, mDisplayList.getNativeDisplayList(),
+        nUpdateRenderLayer(mFinalizer.get(), mDisplayList.getNativeDisplayList(),
                 dirtyRect.left, dirtyRect.top, dirtyRect.right, dirtyRect.bottom);
         mRenderer.pushLayerUpdate(this);
     }
@@ -160,7 +143,7 @@
      *         match the desired values.
      */
     public boolean prepare(int width, int height, boolean isOpaque) {
-        return nPrepare(mFinalizer.mDeferredUpdater, width, height, isOpaque);
+        return nPrepare(mFinalizer.get(), width, height, isOpaque);
     }
 
     /**
@@ -169,7 +152,8 @@
      * @param matrix The transform to apply to the layer.
      */
     public void setTransform(Matrix matrix) {
-        nSetTransform(mFinalizer.mDeferredUpdater, matrix.native_instance);
+        nSetTransform(mFinalizer.get(), matrix.native_instance);
+        mRenderer.pushLayerUpdate(this);
     }
 
     /**
@@ -183,41 +167,25 @@
                 surface.detachFromGLContext();
                 // SurfaceTexture owns the texture name and detachFromGLContext
                 // should have deleted it
-                nOnTextureDestroyed(mFinalizer.mDeferredUpdater);
+                nOnTextureDestroyed(mFinalizer.get());
             }
         });
     }
 
-    /**
-     * This exists to minimize impact into the current HardwareLayer paths as
-     * some of the specifics of how to handle error cases in the fully
-     * deferred model will work
-     */
-    @Deprecated
-    public void flushChanges() {
-        if (HardwareRenderer.sUseRenderThread) {
-            // Not supported, don't try.
-            return;
-        }
-
-        boolean success = nFlushChanges(mFinalizer.mDeferredUpdater);
-        if (!success) {
-            destroy();
-        }
-    }
-
     public long getLayer() {
-        return nGetLayer(mFinalizer.mDeferredUpdater);
+        return nGetLayer(mFinalizer.get());
     }
 
     public void setSurfaceTexture(SurfaceTexture surface) {
         assertType(LAYER_TYPE_TEXTURE);
-        nSetSurfaceTexture(mFinalizer.mDeferredUpdater, surface, false);
+        nSetSurfaceTexture(mFinalizer.get(), surface, false);
+        mRenderer.pushLayerUpdate(this);
     }
 
     public void updateSurfaceTexture() {
         assertType(LAYER_TYPE_TEXTURE);
-        nUpdateSurfaceTexture(mFinalizer.mDeferredUpdater);
+        nUpdateSurfaceTexture(mFinalizer.get());
+        mRenderer.pushLayerUpdate(this);
     }
 
     /**
@@ -225,48 +193,20 @@
      */
     SurfaceTexture createSurfaceTexture() {
         assertType(LAYER_TYPE_TEXTURE);
-        SurfaceTexture st = new SurfaceTexture(nGetTexName(mFinalizer.mDeferredUpdater));
-        nSetSurfaceTexture(mFinalizer.mDeferredUpdater, st, true);
+        SurfaceTexture st = new SurfaceTexture(nGetTexName(mFinalizer.get()));
+        nSetSurfaceTexture(mFinalizer.get(), st, true);
         return st;
     }
 
-    /**
-     * This should only be used by HardwareRenderer! Do not call directly
-     */
-    static HardwareLayer createTextureLayer(HardwareRenderer renderer) {
-        return new HardwareLayer(renderer, nCreateTextureLayer(), LAYER_TYPE_TEXTURE);
-    }
-
     static HardwareLayer adoptTextureLayer(HardwareRenderer renderer, long layer) {
         return new HardwareLayer(renderer, layer, LAYER_TYPE_TEXTURE);
     }
 
-    /**
-     * This should only be used by HardwareRenderer! Do not call directly
-     */
-    static HardwareLayer createDisplayListLayer(HardwareRenderer renderer,
-            int width, int height) {
-        return new HardwareLayer(renderer, nCreateRenderLayer(width, height), LAYER_TYPE_DISPLAY_LIST);
-    }
-
     static HardwareLayer adoptDisplayListLayer(HardwareRenderer renderer, long layer) {
         return new HardwareLayer(renderer, layer, LAYER_TYPE_DISPLAY_LIST);
     }
 
-    /** This also creates the underlying layer */
-    private static native long nCreateTextureLayer();
-    private static native long nCreateRenderLayer(int width, int height);
-
     private static native void nOnTextureDestroyed(long layerUpdater);
-    private static native long nDetachBackingLayer(long layerUpdater);
-
-    /** This also destroys the underlying layer if it is still attached.
-     *  Note it does not recycle the underlying layer, but instead queues it
-     *  for deferred deletion.
-     *  The HardwareRenderer should use detachBackingLayer() in the
-     *  onLayerDestroyed() callback to do recycling if desired.
-     */
-    private static native void nDestroyLayerUpdater(long layerUpdater);
 
     private static native boolean nPrepare(long layerUpdater, int width, int height, boolean isOpaque);
     private static native void nSetLayerPaint(long layerUpdater, long paint);
@@ -281,28 +221,4 @@
 
     private static native long nGetLayer(long layerUpdater);
     private static native int nGetTexName(long layerUpdater);
-
-    private static class Finalizer {
-        private long mDeferredUpdater;
-
-        public Finalizer(long deferredUpdater) {
-            mDeferredUpdater = deferredUpdater;
-        }
-
-        @Override
-        protected void finalize() throws Throwable {
-            try {
-                destroy();
-            } finally {
-                super.finalize();
-            }
-        }
-
-        void destroy() {
-            if (mDeferredUpdater != 0) {
-                nDestroyLayerUpdater(mDeferredUpdater);
-                mDeferredUpdater = 0;
-            }
-        }
-    }
 }
diff --git a/core/java/android/view/HardwareRenderer.java b/core/java/android/view/HardwareRenderer.java
index 3c4d83f..d67c974 100644
--- a/core/java/android/view/HardwareRenderer.java
+++ b/core/java/android/view/HardwareRenderer.java
@@ -17,13 +17,13 @@
 package android.view;
 
 import android.graphics.Bitmap;
-import android.graphics.Paint;
 import android.graphics.Rect;
 import android.graphics.SurfaceTexture;
 import android.util.DisplayMetrics;
 import android.view.Surface.OutOfResourcesException;
 
 import java.io.File;
+import java.io.FileDescriptor;
 import java.io.PrintWriter;
 
 /**
@@ -61,11 +61,9 @@
      * Possible values:
      * "true", to enable profiling
      * "visual_bars", to enable profiling and visualize the results on screen
-     * "visual_lines", to enable profiling and visualize the results on screen
      * "false", to disable profiling
      *
      * @see #PROFILE_PROPERTY_VISUALIZE_BARS
-     * @see #PROFILE_PROPERTY_VISUALIZE_LINES
      *
      * @hide
      */
@@ -80,14 +78,6 @@
     public static final String PROFILE_PROPERTY_VISUALIZE_BARS = "visual_bars";
 
     /**
-     * Value for {@link #PROFILE_PROPERTY}. When the property is set to this
-     * value, profiling data will be visualized on screen as a line chart.
-     *
-     * @hide
-     */
-    public static final String PROFILE_PROPERTY_VISUALIZE_LINES = "visual_lines";
-
-    /**
      * System property used to specify the number of frames to be used
      * when doing hardware rendering profiling.
      * The default value of this property is #PROFILE_MAX_FRAMES.
@@ -181,9 +171,6 @@
      */
     public static boolean sSystemRendererDisabled = false;
 
-    /** @hide */
-    public static boolean sUseRenderThread = true;
-
     private boolean mEnabled;
     private boolean mRequested = true;
 
@@ -298,16 +285,8 @@
 
     /**
      * Outputs extra debugging information in the specified file descriptor.
-     * @param pw
      */
-    abstract void dumpGfxInfo(PrintWriter pw);
-
-    /**
-     * Outputs the total number of frames rendered (used for fps calculations)
-     *
-     * @return the number of frames rendered
-     */
-    abstract long getFrameCount();
+    abstract void dumpGfxInfo(PrintWriter pw, FileDescriptor fd);
 
     /**
      * Loads system properties used by the renderer. This method is invoked
@@ -327,7 +306,7 @@
      * @hide
      */
     public static void setupDiskCache(File cacheDir) {
-        GLRenderer.setupShadersDiskCache(new File(cacheDir, CACHE_PATH_SHADERS).getAbsolutePath());
+        ThreadedRenderer.setupShadersDiskCache(new File(cacheDir, CACHE_PATH_SHADERS).getAbsolutePath());
     }
 
     /**
@@ -341,12 +320,6 @@
     abstract void pushLayerUpdate(HardwareLayer layer);
 
     /**
-     * Tells the HardwareRenderer that a layer was created. The renderer should
-     * make sure to apply any pending layer changes at the start of a new frame
-     */
-    abstract void onLayerCreated(HardwareLayer hardwareLayer);
-
-    /**
      * Tells the HardwareRenderer that the layer is destroyed. The renderer
      * should remove the layer from any update queues.
      */
@@ -493,11 +466,7 @@
     static HardwareRenderer create(boolean translucent) {
         HardwareRenderer renderer = null;
         if (GLES20Canvas.isAvailable()) {
-            if (sUseRenderThread) {
-                renderer = new ThreadedRenderer(translucent);
-            } else {
-                renderer = new GLRenderer(translucent);
-            }
+            renderer = new ThreadedRenderer(translucent);
         }
         return renderer;
     }
@@ -524,7 +493,7 @@
      *              see {@link android.content.ComponentCallbacks}
      */
     static void startTrimMemory(int level) {
-        GLRenderer.startTrimMemory(level);
+        ThreadedRenderer.startTrimMemory(level);
     }
 
     /**
@@ -532,7 +501,7 @@
      * cleanup special resources used by the memory trimming process.
      */
     static void endTrimMemory() {
-        GLRenderer.endTrimMemory();
+        ThreadedRenderer.endTrimMemory();
     }
 
     /**
@@ -583,98 +552,4 @@
      */
     public void notifyFramePending() {
     }
-
-    /**
-     * Describes a series of frames that should be drawn on screen as a graph.
-     * Each frame is composed of 1 or more elements.
-     */
-    abstract class GraphDataProvider {
-        /**
-         * Draws the graph as bars. Frame elements are stacked on top of
-         * each other.
-         */
-        public static final int GRAPH_TYPE_BARS = 0;
-        /**
-         * Draws the graph as lines. The number of series drawn corresponds
-         * to the number of elements.
-         */
-        public static final int GRAPH_TYPE_LINES = 1;
-
-        /**
-         * Returns the type of graph to render.
-         *
-         * @return {@link #GRAPH_TYPE_BARS} or {@link #GRAPH_TYPE_LINES}
-         */
-        abstract int getGraphType();
-
-        /**
-         * This method is invoked before the graph is drawn. This method
-         * can be used to compute sizes, etc.
-         *
-         * @param metrics The display metrics
-         */
-        abstract void prepare(DisplayMetrics metrics);
-
-        /**
-         * @return The size in pixels of a vertical unit.
-         */
-        abstract int getVerticalUnitSize();
-
-        /**
-         * @return The size in pixels of a horizontal unit.
-         */
-        abstract int getHorizontalUnitSize();
-
-        /**
-         * @return The size in pixels of the margin between horizontal units.
-         */
-        abstract int getHorizontaUnitMargin();
-
-        /**
-         * An optional threshold value.
-         *
-         * @return A value >= 0 to draw the threshold, a negative value
-         *         to ignore it.
-         */
-        abstract float getThreshold();
-
-        /**
-         * The data to draw in the graph. The number of elements in the
-         * array must be at least {@link #getFrameCount()} * {@link #getElementCount()}.
-         * If a value is negative the following values will be ignored.
-         */
-        abstract float[] getData();
-
-        /**
-         * Returns the number of frames to render in the graph.
-         */
-        abstract int getFrameCount();
-
-        /**
-         * Returns the number of elements in each frame. This directly affects
-         * the number of series drawn in the graph.
-         */
-        abstract int getElementCount();
-
-        /**
-         * Returns the current frame, if any. If the returned value is negative
-         * the current frame is ignored.
-         */
-        abstract int getCurrentFrame();
-
-        /**
-         * Prepares the paint to draw the specified element (or series.)
-         */
-        abstract void setupGraphPaint(Paint paint, int elementIndex);
-
-        /**
-         * Prepares the paint to draw the threshold.
-         */
-        abstract void setupThresholdPaint(Paint paint);
-
-        /**
-         * Prepares the paint to draw the current frame indicator.
-         */
-        abstract void setupCurrentFramePaint(Paint paint);
-    }
 }
diff --git a/core/java/android/view/IWindowManager.aidl b/core/java/android/view/IWindowManager.aidl
index 7d13399..af16185 100644
--- a/core/java/android/view/IWindowManager.aidl
+++ b/core/java/android/view/IWindowManager.aidl
@@ -79,7 +79,7 @@
     void removeWindowToken(IBinder token);
     void addAppToken(int addPos, IApplicationToken token, int groupId, int stackId,
             int requestedOrientation, boolean fullscreen, boolean showWhenLocked, int userId,
-            int configChanges);
+            int configChanges, boolean voiceInteraction);
     void setAppGroupId(IBinder token, int groupId);
     void setAppOrientation(IApplicationToken token, int requestedOrientation);
     int getAppOrientation(IApplicationToken token);
@@ -120,6 +120,7 @@
     boolean isKeyguardSecure();
     boolean inKeyguardRestrictedInputMode();
     void dismissKeyguard();
+    void keyguardGoingAway();
 
     void closeSystemDialogs(String reason);
 
diff --git a/core/java/android/view/RenderNode.java b/core/java/android/view/RenderNode.java
index cf125bc..e63829e 100644
--- a/core/java/android/view/RenderNode.java
+++ b/core/java/android/view/RenderNode.java
@@ -850,6 +850,13 @@
         nOutput(mNativeRenderNode);
     }
 
+    /**
+     * Gets the size of the DisplayList for debug purposes.
+     */
+    public int getDebugSize() {
+        return nGetDebugSize(mNativeRenderNode);
+    }
+
     ///////////////////////////////////////////////////////////////////////////
     // Animations
     ///////////////////////////////////////////////////////////////////////////
@@ -941,6 +948,7 @@
     private static native float nGetPivotX(long renderNode);
     private static native float nGetPivotY(long renderNode);
     private static native void nOutput(long renderNode);
+    private static native int nGetDebugSize(long renderNode);
 
     ///////////////////////////////////////////////////////////////////////////
     // Animations
diff --git a/core/java/android/view/RenderNodeAnimator.java b/core/java/android/view/RenderNodeAnimator.java
index e918119..4979059 100644
--- a/core/java/android/view/RenderNodeAnimator.java
+++ b/core/java/android/view/RenderNodeAnimator.java
@@ -219,6 +219,15 @@
         return mTarget;
     }
 
+    /**
+     * WARNING: May only be called once!!!
+     * TODO: Fix above -_-
+     */
+    public void setStartValue(float startValue) {
+        checkMutable();
+        nSetStartValue(mNativePtr.get(), startValue);
+    }
+
     @Override
     public void setStartDelay(long startDelay) {
         checkMutable();
@@ -282,11 +291,12 @@
     }
 
     private static native long nCreateAnimator(WeakReference<RenderNodeAnimator> weakThis,
-            int property, float deltaValue);
+            int property, float finalValue);
     private static native long nCreateCanvasPropertyFloatAnimator(WeakReference<RenderNodeAnimator> weakThis,
-            long canvasProperty, float deltaValue);
+            long canvasProperty, float finalValue);
     private static native long nCreateCanvasPropertyPaintAnimator(WeakReference<RenderNodeAnimator> weakThis,
-            long canvasProperty, int paintField, float deltaValue);
+            long canvasProperty, int paintField, float finalValue);
+    private static native void nSetStartValue(long nativePtr, float startValue);
     private static native void nSetDuration(long nativePtr, long duration);
     private static native long nGetDuration(long nativePtr);
     private static native void nSetStartDelay(long nativePtr, long startDelay);
diff --git a/core/java/android/view/SurfaceControl.java b/core/java/android/view/SurfaceControl.java
index c15ce44..5cd3d62 100644
--- a/core/java/android/view/SurfaceControl.java
+++ b/core/java/android/view/SurfaceControl.java
@@ -38,11 +38,11 @@
     private static native void nativeDestroy(long nativeObject);
 
     private static native Bitmap nativeScreenshot(IBinder displayToken,
-            int width, int height, int minLayer, int maxLayer, boolean allLayers,
-            boolean useIdentityTransform);
+            Rect sourceCrop, int width, int height, int minLayer, int maxLayer,
+            boolean allLayers, boolean useIdentityTransform);
     private static native void nativeScreenshot(IBinder displayToken, Surface consumer,
-            int width, int height, int minLayer, int maxLayer, boolean allLayers,
-            boolean useIdentityTransform);
+            Rect sourceCrop, int width, int height, int minLayer, int maxLayer,
+            boolean allLayers, boolean useIdentityTransform);
 
     private static native void nativeOpenTransaction();
     private static native void nativeCloseTransaction();
@@ -597,8 +597,8 @@
     public static void screenshot(IBinder display, Surface consumer,
             int width, int height, int minLayer, int maxLayer,
             boolean useIdentityTransform) {
-        screenshot(display, consumer, width, height, minLayer, maxLayer, false,
-                useIdentityTransform);
+        screenshot(display, consumer, new Rect(), width, height, minLayer, maxLayer,
+                false, useIdentityTransform);
     }
 
     /**
@@ -613,7 +613,7 @@
      */
     public static void screenshot(IBinder display, Surface consumer,
             int width, int height) {
-        screenshot(display, consumer, width, height, 0, 0, true, false);
+        screenshot(display, consumer, new Rect(), width, height, 0, 0, true, false);
     }
 
     /**
@@ -623,7 +623,7 @@
      * @param consumer The {@link Surface} to take the screenshot into.
      */
     public static void screenshot(IBinder display, Surface consumer) {
-        screenshot(display, consumer, 0, 0, 0, 0, true, false);
+        screenshot(display, consumer, new Rect(), 0, 0, 0, 0, true, false);
     }
 
     /**
@@ -634,6 +634,8 @@
      * the versions that use a {@link Surface} instead, such as
      * {@link SurfaceControl#screenshot(IBinder, Surface)}.
      *
+     * @param sourceCrop The portion of the screen to capture into the Bitmap;
+     * caller may pass in 'new Rect()' if no cropping is desired.
      * @param width The desired width of the returned bitmap; the raw
      * screen will be scaled down to this size.
      * @param height The desired height of the returned bitmap; the raw
@@ -649,13 +651,13 @@
      * if an error occurs. Make sure to call Bitmap.recycle() as soon as
      * possible, once its content is not needed anymore.
      */
-    public static Bitmap screenshot(int width, int height, int minLayer, int maxLayer,
-            boolean useIdentityTransform) {
+    public static Bitmap screenshot(Rect sourceCrop, int width, int height,
+            int minLayer, int maxLayer, boolean useIdentityTransform) {
         // TODO: should take the display as a parameter
         IBinder displayToken = SurfaceControl.getBuiltInDisplay(
                 SurfaceControl.BUILT_IN_DISPLAY_ID_MAIN);
-        return nativeScreenshot(displayToken, width, height, minLayer, maxLayer, false,
-                useIdentityTransform);
+        return nativeScreenshot(displayToken, sourceCrop, width, height,
+                minLayer, maxLayer, false, useIdentityTransform);
     }
 
     /**
@@ -674,10 +676,10 @@
         // TODO: should take the display as a parameter
         IBinder displayToken = SurfaceControl.getBuiltInDisplay(
                 SurfaceControl.BUILT_IN_DISPLAY_ID_MAIN);
-        return nativeScreenshot(displayToken, width, height, 0, 0, true, false);
+        return nativeScreenshot(displayToken, new Rect(), width, height, 0, 0, true, false);
     }
 
-    private static void screenshot(IBinder display, Surface consumer,
+    private static void screenshot(IBinder display, Surface consumer, Rect sourceCrop,
             int width, int height, int minLayer, int maxLayer, boolean allLayers,
             boolean useIdentityTransform) {
         if (display == null) {
@@ -686,7 +688,7 @@
         if (consumer == null) {
             throw new IllegalArgumentException("consumer must not be null");
         }
-        nativeScreenshot(display, consumer, width, height, minLayer, maxLayer, allLayers,
-                useIdentityTransform);
+        nativeScreenshot(display, consumer, sourceCrop, width, height,
+                minLayer, maxLayer, allLayers, useIdentityTransform);
     }
 }
diff --git a/core/java/android/view/TextureView.java b/core/java/android/view/TextureView.java
index 1765c43..2a9f7d5 100644
--- a/core/java/android/view/TextureView.java
+++ b/core/java/android/view/TextureView.java
@@ -405,7 +405,9 @@
             // To cancel updates, the easiest thing to do is simply to remove the
             // updates listener
             if (visibility == VISIBLE) {
-                mSurface.setOnFrameAvailableListener(mUpdateListener, mAttachInfo.mHandler);
+                if (mLayer != null) {
+                    mSurface.setOnFrameAvailableListener(mUpdateListener, mAttachInfo.mHandler);
+                }
                 updateLayerAndInvalidate();
             } else {
                 mSurface.setOnFrameAvailableListener(null);
diff --git a/core/java/android/view/ThreadedRenderer.java b/core/java/android/view/ThreadedRenderer.java
index 8417887..9b3ef7f 100644
--- a/core/java/android/view/ThreadedRenderer.java
+++ b/core/java/android/view/ThreadedRenderer.java
@@ -22,19 +22,19 @@
 import android.os.IBinder;
 import android.os.RemoteException;
 import android.os.ServiceManager;
+import android.os.SystemProperties;
 import android.os.Trace;
 import android.util.Log;
 import android.util.TimeUtils;
 import android.view.Surface.OutOfResourcesException;
 import android.view.View.AttachInfo;
 
+import java.io.FileDescriptor;
 import java.io.PrintWriter;
 
 /**
  * Hardware renderer that proxies the rendering to a render thread. Most calls
  * are currently synchronous.
- * TODO: Make draw() async.
- * TODO: Figure out how to share the DisplayList between two threads (global lock?)
  *
  * The UI thread can block on the RenderThread, but RenderThread must never
  * block on the UI thread.
@@ -62,11 +62,16 @@
     // Needs a ViewRoot invalidate
     private static final int SYNC_INVALIDATE_REQUIRED = 0x1;
 
+    private static final String[] VISUALIZERS = {
+        PROFILE_PROPERTY_VISUALIZE_BARS,
+    };
+
     private int mWidth, mHeight;
     private long mNativeProxy;
     private boolean mInitialized = false;
     private RenderNode mRootNode;
     private Choreographer mChoreographer;
+    private boolean mProfilingEnabled;
 
     ThreadedRenderer(boolean translucent) {
         AtlasInitializer.sInstance.init();
@@ -79,6 +84,8 @@
         // Setup timing
         mChoreographer = Choreographer.getInstance();
         nSetFrameInterval(mNativeProxy, mChoreographer.getFrameIntervalNanos());
+
+        loadSystemProperties();
     }
 
     @Override
@@ -117,7 +124,7 @@
     @Override
     void destroyHardwareResources(View view) {
         destroyResources(view);
-        // TODO: GLES20Canvas.flushCaches(GLES20Canvas.FLUSH_CACHES_LAYERS);
+        nFlushCaches(mNativeProxy, GLES20Canvas.FLUSH_CACHES_LAYERS);
     }
 
     private static void destroyResources(View view) {
@@ -168,19 +175,33 @@
     }
 
     @Override
-    void dumpGfxInfo(PrintWriter pw) {
-        // TODO Auto-generated method stub
+    void dumpGfxInfo(PrintWriter pw, FileDescriptor fd) {
+        pw.flush();
+        nDumpProfileInfo(mNativeProxy, fd);
     }
 
-    @Override
-    long getFrameCount() {
-        // TODO Auto-generated method stub
-        return 0;
+    private static int search(String[] values, String value) {
+        for (int i = 0; i < values.length; i++) {
+            if (values[i].equals(value)) return i;
+        }
+        return -1;
+    }
+
+    private static boolean checkIfProfilingRequested() {
+        String profiling = SystemProperties.get(HardwareRenderer.PROFILE_PROPERTY);
+        int graphType = search(VISUALIZERS, profiling);
+        return (graphType >= 0) || Boolean.parseBoolean(profiling);
     }
 
     @Override
     boolean loadSystemProperties() {
-        return nLoadSystemProperties(mNativeProxy);
+        boolean changed = nLoadSystemProperties(mNativeProxy);
+        boolean wantProfiling = checkIfProfilingRequested();
+        if (wantProfiling != mProfilingEnabled) {
+            mProfilingEnabled = wantProfiling;
+            changed = true;
+        }
+        return changed;
     }
 
     private void updateRootDisplayList(View view, HardwareDrawCallbacks callbacks) {
@@ -212,14 +233,24 @@
         long frameTimeNanos = mChoreographer.getFrameTimeNanos();
         attachInfo.mDrawingTime = frameTimeNanos / TimeUtils.NANOS_PER_MS;
 
+        long recordDuration = 0;
+        if (mProfilingEnabled) {
+            recordDuration = System.nanoTime();
+        }
+
         updateRootDisplayList(view, callbacks);
 
+        if (mProfilingEnabled) {
+            recordDuration = System.nanoTime() - recordDuration;
+        }
+
         attachInfo.mIgnoreDirtyState = false;
 
         if (dirty == null) {
             dirty = NULL_RECT;
         }
         int syncResult = nSyncAndDrawFrame(mNativeProxy, frameTimeNanos,
+                recordDuration, view.getResources().getDisplayMetrics().density,
                 dirty.left, dirty.top, dirty.right, dirty.bottom);
         if ((syncResult & SYNC_INVALIDATE_REQUIRED) != 0) {
             attachInfo.mViewRootImpl.invalidate();
@@ -263,12 +294,7 @@
 
     @Override
     void pushLayerUpdate(HardwareLayer layer) {
-        // TODO: Remove this, it's not needed outside of GLRenderer
-    }
-
-    @Override
-    void onLayerCreated(HardwareLayer layer) {
-        // TODO: Is this actually useful?
+        nPushLayerUpdate(mNativeProxy, layer.getDeferredLayerUpdater());
     }
 
     @Override
@@ -278,7 +304,7 @@
 
     @Override
     void onLayerDestroyed(HardwareLayer layer) {
-        nDestroyLayer(mNativeProxy, layer.getDeferredLayerUpdater());
+        nCancelLayerUpdate(mNativeProxy, layer.getDeferredLayerUpdater());
     }
 
     @Override
@@ -305,6 +331,14 @@
         }
     }
 
+    static void startTrimMemory(int level) {
+        // TODO
+    }
+
+    static void endTrimMemory() {
+        // TODO
+    }
+
     private static class AtlasInitializer {
         static AtlasInitializer sInstance = new AtlasInitializer();
 
@@ -341,6 +375,8 @@
         }
     }
 
+    static native void setupShadersDiskCache(String cacheFile);
+
     private static native void nSetAtlas(GraphicBuffer buffer, long[] map);
 
     private static native long nCreateRootRenderNode();
@@ -356,7 +392,8 @@
     private static native void nSetup(long nativeProxy, int width, int height,
             float lightX, float lightY, float lightZ, float lightRadius);
     private static native void nSetOpaque(long nativeProxy, boolean opaque);
-    private static native int nSyncAndDrawFrame(long nativeProxy, long frameTimeNanos,
+    private static native int nSyncAndDrawFrame(long nativeProxy,
+            long frameTimeNanos, long recordDuration, float density,
             int dirtyLeft, int dirtyTop, int dirtyRight, int dirtyBottom);
     private static native void nRunWithGlContext(long nativeProxy, Runnable runnable);
     private static native void nDestroyCanvasAndSurface(long nativeProxy);
@@ -366,8 +403,13 @@
     private static native long nCreateDisplayListLayer(long nativeProxy, int width, int height);
     private static native long nCreateTextureLayer(long nativeProxy);
     private static native boolean nCopyLayerInto(long nativeProxy, long layer, long bitmap);
-    private static native void nDestroyLayer(long nativeProxy, long layer);
+    private static native void nPushLayerUpdate(long nativeProxy, long layer);
+    private static native void nCancelLayerUpdate(long nativeProxy, long layer);
+
+    private static native void nFlushCaches(long nativeProxy, int flushMode);
 
     private static native void nFence(long nativeProxy);
     private static native void nNotifyFramePending(long nativeProxy);
+
+    private static native void nDumpProfileInfo(long nativeProxy, FileDescriptor fd);
 }
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index 6396781..622fa8c 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -10711,8 +10711,8 @@
      * {@link Drawable#getOutline(Outline)}. Manually setting the Outline with this method allows
      * this behavior to be overridden.
      * <p>
-     * If the outline is empty or is null, shadows will be cast from the
-     * bounds of the View.
+     * If the outline is {@link Outline#isEmpty()} or is <code>null</code>,
+     * shadows will not be cast.
      * <p>
      * Only outlines that return true from {@link Outline#canClip()} may be used for clipping.
      *
@@ -13593,12 +13593,6 @@
                 }
             }
 
-            // The layer is not valid if the underlying GPU resources cannot be allocated
-            mHardwareLayer.flushChanges();
-            if (!mHardwareLayer.isValid()) {
-                return null;
-            }
-
             mHardwareLayer.setLayerPaint(mLayerPaint);
             RenderNode displayList = mHardwareLayer.startRecording();
             updateDisplayListIfDirty(displayList, true);
@@ -16197,6 +16191,20 @@
     }
 
     /**
+     * Set this view's optical insets.
+     *
+     * <p>This method should be treated similarly to setMeasuredDimension and not as a general
+     * property. Views that compute their own optical insets should call it as part of measurement.
+     * This method does not request layout. If you are setting optical insets outside of
+     * measure/layout itself you will want to call requestLayout() yourself.
+     * </p>
+     * @hide
+     */
+    public void setOpticalInsets(Insets insets) {
+        mLayoutInsets = insets;
+    }
+
+    /**
      * Changes the selection state of this view. A view can be selected or not.
      * Note that selection is not the same as focus. Views are typically
      * selected in the context of an AdapterView like ListView or GridView;
diff --git a/core/java/android/view/ViewGroup.java b/core/java/android/view/ViewGroup.java
index b821a3e..0f40ee7 100644
--- a/core/java/android/view/ViewGroup.java
+++ b/core/java/android/view/ViewGroup.java
@@ -456,6 +456,10 @@
     // views during a transition when they otherwise would have become gone/invisible
     private ArrayList<View> mVisibilityChangingChildren;
 
+    // Temporary holder of presorted children, only used for
+    // input/software draw dispatch for correctly Z ordering.
+    private ArrayList<View> mPreSortedChildren;
+
     // Indicates how many of this container's child subtrees contain transient state
     @ViewDebug.ExportedProperty(category = "layout")
     private int mChildCountWithTransientState = 0;
@@ -1499,13 +1503,15 @@
             final float y = event.getY();
             final int childrenCount = mChildrenCount;
             if (childrenCount != 0) {
-                final boolean customChildOrder = isChildrenDrawingOrderEnabled();
+                final ArrayList<View> preorderedList = buildOrderedChildList();
+                final boolean customOrder = preorderedList == null
+                        && isChildrenDrawingOrderEnabled();
                 final View[] children = mChildren;
                 HoverTarget lastHoverTarget = null;
                 for (int i = childrenCount - 1; i >= 0; i--) {
-                    final int childIndex = customChildOrder
-                            ? getChildDrawingOrder(childrenCount, i) : i;
-                    final View child = children[childIndex];
+                    int childIndex = customOrder ? getChildDrawingOrder(childrenCount, i) : i;
+                    final View child = (preorderedList == null)
+                            ? children[childIndex] : preorderedList.get(childIndex);
                     if (!canViewReceivePointerEvents(child)
                             || !isTransformedTouchPointInView(x, y, child, null)) {
                         continue;
@@ -1572,6 +1578,7 @@
                         break;
                     }
                 }
+                if (preorderedList != null) preorderedList.clear();
             }
         }
 
@@ -1778,23 +1785,28 @@
         // Send the event to the child under the pointer.
         final int childrenCount = mChildrenCount;
         if (childrenCount != 0) {
-            final View[] children = mChildren;
             final float x = event.getX();
             final float y = event.getY();
 
-            final boolean customOrder = isChildrenDrawingOrderEnabled();
+            final ArrayList<View> preorderedList = buildOrderedChildList();
+            final boolean customOrder = preorderedList == null
+                    && isChildrenDrawingOrderEnabled();
+            final View[] children = mChildren;
             for (int i = childrenCount - 1; i >= 0; i--) {
-                final int childIndex = customOrder ? getChildDrawingOrder(childrenCount, i) : i;
-                final View child = children[childIndex];
+                int childIndex = customOrder ? getChildDrawingOrder(childrenCount, i) : i;
+                final View child = (preorderedList == null)
+                        ? children[childIndex] : preorderedList.get(childIndex);
                 if (!canViewReceivePointerEvents(child)
                         || !isTransformedTouchPointInView(x, y, child, null)) {
                     continue;
                 }
 
                 if (dispatchTransformedGenericPointerEvent(event, child)) {
+                    if (preorderedList != null) preorderedList.clear();
                     return true;
                 }
             }
+            if (preorderedList != null) preorderedList.clear();
         }
 
         // No child handled the event.  Send it to this view group.
@@ -1910,13 +1922,15 @@
                         final float y = ev.getY(actionIndex);
                         // Find a child that can receive the event.
                         // Scan children from front to back.
+                        final ArrayList<View> preorderedList = buildOrderedChildList();
+                        final boolean customOrder = preorderedList == null
+                                && isChildrenDrawingOrderEnabled();
                         final View[] children = mChildren;
-
-                        final boolean customOrder = isChildrenDrawingOrderEnabled();
                         for (int i = childrenCount - 1; i >= 0; i--) {
-                            final int childIndex = customOrder ?
-                                    getChildDrawingOrder(childrenCount, i) : i;
-                            final View child = children[childIndex];
+                            final int childIndex = customOrder
+                                    ? getChildDrawingOrder(childrenCount, i) : i;
+                            final View child = (preorderedList == null)
+                                    ? children[childIndex] : preorderedList.get(childIndex);
                             if (!canViewReceivePointerEvents(child)
                                     || !isTransformedTouchPointInView(x, y, child, null)) {
                                 continue;
@@ -1934,7 +1948,17 @@
                             if (dispatchTransformedTouchEvent(ev, false, child, idBitsToAssign)) {
                                 // Child wants to receive touch within its bounds.
                                 mLastTouchDownTime = ev.getDownTime();
-                                mLastTouchDownIndex = childIndex;
+                                if (preorderedList != null) {
+                                    // childIndex points into presorted list, find original index
+                                    for (int j = 0; j < childrenCount; j++) {
+                                        if (children[childIndex] == mChildren[j]) {
+                                            mLastTouchDownIndex = j;
+                                            break;
+                                        }
+                                    }
+                                } else {
+                                    mLastTouchDownIndex = childIndex;
+                                }
                                 mLastTouchDownX = ev.getX();
                                 mLastTouchDownY = ev.getY();
                                 newTouchTarget = addTouchTarget(child, idBitsToAssign);
@@ -1942,6 +1966,7 @@
                                 break;
                             }
                         }
+                        if (preorderedList != null) preorderedList.clear();
                     }
 
                     if (newTouchTarget == null && mFirstTouchTarget != null) {
@@ -2928,7 +2953,7 @@
      */
     @Override
     protected void dispatchDraw(Canvas canvas) {
-        final int count = mChildrenCount;
+        final int childrenCount = mChildrenCount;
         final View[] children = mChildren;
         int flags = mGroupFlags;
 
@@ -2936,15 +2961,15 @@
             final boolean cache = (mGroupFlags & FLAG_ANIMATION_CACHE) == FLAG_ANIMATION_CACHE;
 
             final boolean buildCache = !isHardwareAccelerated();
-            for (int i = 0; i < count; i++) {
+            for (int i = 0; i < childrenCount; i++) {
                 final View child = children[i];
                 if ((child.mViewFlags & VISIBILITY_MASK) == VISIBLE) {
                     final LayoutParams params = child.getLayoutParams();
-                    attachLayoutAnimationParameters(child, params, i, count);
+                    attachLayoutAnimationParameters(child, params, i, childrenCount);
                     bindLayoutAnimation(child);
                     if (cache) {
                         child.setDrawingCacheEnabled(true);
-                        if (buildCache) {                        
+                        if (buildCache) {
                             child.buildDrawingCache(true);
                         }
                     }
@@ -2997,21 +3022,22 @@
         boolean more = false;
         final long drawingTime = getDrawingTime();
 
-        if ((flags & FLAG_USE_CHILD_DRAWING_ORDER) == 0) {
-            for (int i = 0; i < count; i++) {
-                final View child = children[i];
-                if ((child.mViewFlags & VISIBILITY_MASK) == VISIBLE || child.getAnimation() != null) {
-                    more |= drawChild(canvas, child, drawingTime);
-                }
-            }
-        } else {
-            for (int i = 0; i < count; i++) {
-                final View child = children[getChildDrawingOrder(count, i)];
-                if ((child.mViewFlags & VISIBILITY_MASK) == VISIBLE || child.getAnimation() != null) {
-                    more |= drawChild(canvas, child, drawingTime);
-                }
+
+        // Only use the preordered list if not HW accelerated, since the HW pipeline will do the
+        // draw reordering internally
+        final ArrayList<View> preorderedList = canvas.isHardwareAccelerated()
+                ? null : buildOrderedChildList();
+        final boolean customOrder = preorderedList == null
+                && isChildrenDrawingOrderEnabled();
+        for (int i = 0; i < childrenCount; i++) {
+            int childIndex = customOrder ? getChildDrawingOrder(childrenCount, i) : i;
+            final View child = (preorderedList == null)
+                    ? children[childIndex] : preorderedList.get(childIndex);
+            if ((child.mViewFlags & VISIBILITY_MASK) == VISIBLE || child.getAnimation() != null) {
+                more |= drawChild(canvas, child, drawingTime);
             }
         }
+        if (preorderedList != null) preorderedList.clear();
 
         // Draw any disappearing views that have animations
         if (mDisappearingChildren != null) {
@@ -3096,6 +3122,47 @@
         return i;
     }
 
+    private boolean hasChildWithZ() {
+        for (int i = 0; i < mChildrenCount; i++) {
+            if (mChildren[i].getZ() != 0) return true;
+        }
+        return false;
+    }
+
+    /**
+     * Populates (and returns) mPreSortedChildren with a pre-ordered list of the View's children,
+     * sorted first by Z, then by child drawing order (if applicable).
+     *
+     * Uses a stable, insertion sort which is commonly O(n) for ViewGroups with very few elevated
+     * children.
+     */
+    private ArrayList<View> buildOrderedChildList() {
+        final int count = mChildrenCount;
+        if (count <= 1 || !hasChildWithZ()) return null;
+
+        if (mPreSortedChildren == null) {
+            mPreSortedChildren = new ArrayList<View>(count);
+        } else {
+            mPreSortedChildren.ensureCapacity(count);
+        }
+
+        final boolean useCustomOrder = isChildrenDrawingOrderEnabled();
+        for (int i = 0; i < mChildrenCount; i++) {
+            // add next child (in child order) to end of list
+            int childIndex = useCustomOrder ? getChildDrawingOrder(mChildrenCount, i) : i;
+            View nextChild = mChildren[childIndex];
+            float currentZ = nextChild.getZ();
+
+            // insert ahead of any Views with greater Z
+            int insertIndex = i;
+            while (insertIndex > 0 && mPreSortedChildren.get(insertIndex - 1).getZ() > currentZ) {
+                insertIndex--;
+            }
+            mPreSortedChildren.add(insertIndex, nextChild);
+        }
+        return mPreSortedChildren;
+    }
+
     private void notifyAnimationListener() {
         mGroupFlags &= ~FLAG_NOTIFY_ANIMATION_LISTENER;
         mGroupFlags |= FLAG_ANIMATION_DONE;
diff --git a/core/java/android/view/ViewPropertyAnimator.java b/core/java/android/view/ViewPropertyAnimator.java
index 3104862..af1de78 100644
--- a/core/java/android/view/ViewPropertyAnimator.java
+++ b/core/java/android/view/ViewPropertyAnimator.java
@@ -253,9 +253,10 @@
     ViewPropertyAnimator(View view) {
         mView = view;
         view.ensureTransformationInfo();
-        if (view.getContext().getApplicationInfo().targetSdkVersion >= Build.VERSION_CODES.L) {
-            mRTBackend = new ViewPropertyAnimatorRT(view);
-        }
+        // TODO: Disabled because of b/15287046
+        //if (view.getContext().getApplicationInfo().targetSdkVersion >= Build.VERSION_CODES.L) {
+        //    mRTBackend = new ViewPropertyAnimatorRT(view);
+        //}
     }
 
     /**
@@ -1142,7 +1143,8 @@
                 // Shouldn't happen, but just to play it safe
                 return;
             }
-            boolean useRenderNodeProperties = mView.mRenderNode != null;
+
+            boolean hardwareAccelerated = mView.isHardwareAccelerated();
 
             // alpha requires slightly different treatment than the other (transform) properties.
             // The logic in setAlpha() is not simply setting mAlpha, plus the invalidation
@@ -1150,13 +1152,13 @@
             // We track what kinds of properties are set, and how alpha is handled when it is
             // set, and perform the invalidation steps appropriately.
             boolean alphaHandled = false;
-            if (!useRenderNodeProperties) {
+            if (!hardwareAccelerated) {
                 mView.invalidateParentCaches();
             }
             float fraction = animation.getAnimatedFraction();
             int propertyMask = propertyBundle.mPropertyMask;
             if ((propertyMask & TRANSFORM_MASK) != 0) {
-                mView.invalidateViewProperty(false, false);
+                mView.invalidateViewProperty(hardwareAccelerated, false);
             }
             ArrayList<NameValuesHolder> valueList = propertyBundle.mNameValuesHolder;
             if (valueList != null) {
@@ -1172,7 +1174,7 @@
                 }
             }
             if ((propertyMask & TRANSFORM_MASK) != 0) {
-                if (!useRenderNodeProperties) {
+                if (!hardwareAccelerated) {
                     mView.mPrivateFlags |= View.PFLAG_DRAWN; // force another invalidation
                 }
             }
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index c3bf295..f3d1e3c 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -716,17 +716,6 @@
 
             if (!HardwareRenderer.sRendererDisabled || (HardwareRenderer.sSystemRendererDisabled
                     && forceHwAccelerated)) {
-                if (!HardwareRenderer.sUseRenderThread) {
-                    // TODO: Delete
-                    // Don't enable hardware acceleration when we're not on the main thread
-                    if (!HardwareRenderer.sSystemRendererDisabled &&
-                            Looper.getMainLooper() != Looper.myLooper()) {
-                        Log.w(HardwareRenderer.LOG_TAG, "Attempting to initialize hardware "
-                                + "acceleration outside of the main thread, aborting");
-                        return;
-                    }
-                }
-
                 if (mAttachInfo.mHardwareRenderer != null) {
                     mAttachInfo.mHardwareRenderer.destroy(true);
                 }
@@ -5351,7 +5340,7 @@
         RenderNode renderNode = view.mRenderNode;
         info[0]++;
         if (renderNode != null) {
-            info[1] += 0; /* TODO: Memory used by RenderNodes (properties + DisplayLists) */
+            info[1] += renderNode.getDebugSize();
         }
 
         if (view instanceof ViewGroup) {
diff --git a/core/java/android/view/WindowManager.java b/core/java/android/view/WindowManager.java
index 031ad80..4eecc6a 100644
--- a/core/java/android/view/WindowManager.java
+++ b/core/java/android/view/WindowManager.java
@@ -218,7 +218,8 @@
             @ViewDebug.IntToString(from = TYPE_NAVIGATION_BAR_PANEL, to = "TYPE_NAVIGATION_BAR_PANEL"),
             @ViewDebug.IntToString(from = TYPE_DISPLAY_OVERLAY, to = "TYPE_DISPLAY_OVERLAY"),
             @ViewDebug.IntToString(from = TYPE_MAGNIFICATION_OVERLAY, to = "TYPE_MAGNIFICATION_OVERLAY"),
-            @ViewDebug.IntToString(from = TYPE_PRIVATE_PRESENTATION, to = "TYPE_PRIVATE_PRESENTATION")
+            @ViewDebug.IntToString(from = TYPE_PRIVATE_PRESENTATION, to = "TYPE_PRIVATE_PRESENTATION"),
+            @ViewDebug.IntToString(from = TYPE_VOICE_INTERACTION, to = "TYPE_VOICE_INTERACTION"),
         })
         public int type;
     
@@ -541,6 +542,12 @@
         public static final int TYPE_PRIVATE_PRESENTATION = FIRST_SYSTEM_WINDOW+30;
 
         /**
+         * Window type: Windows in the voice interaction layer.
+         * @hide
+         */
+        public static final int TYPE_VOICE_INTERACTION = FIRST_SYSTEM_WINDOW+31;
+
+        /**
          * End of types of system windows.
          */
         public static final int LAST_SYSTEM_WINDOW      = 2999;
diff --git a/core/java/android/view/WindowManagerGlobal.java b/core/java/android/view/WindowManagerGlobal.java
index 96c0ed2..b4779f4 100644
--- a/core/java/android/view/WindowManagerGlobal.java
+++ b/core/java/android/view/WindowManagerGlobal.java
@@ -430,7 +430,7 @@
                     HardwareRenderer renderer =
                             root.getView().mAttachInfo.mHardwareRenderer;
                     if (renderer != null) {
-                        renderer.dumpGfxInfo(pw);
+                        renderer.dumpGfxInfo(pw, fd);
                     }
                 }
 
@@ -447,11 +447,6 @@
                     String name = getWindowName(root);
                     pw.printf("  %s\n  %d views, %.2f kB of display lists",
                             name, info[0], info[1] / 1024.0f);
-                    HardwareRenderer renderer =
-                            root.getView().mAttachInfo.mHardwareRenderer;
-                    if (renderer != null) {
-                        pw.printf(", %d frames rendered", renderer.getFrameCount());
-                    }
                     pw.printf("\n\n");
 
                     viewsCount += info[0];
diff --git a/core/java/android/view/WindowManagerPolicy.java b/core/java/android/view/WindowManagerPolicy.java
index 1bb20c9..2b4677c 100644
--- a/core/java/android/view/WindowManagerPolicy.java
+++ b/core/java/android/view/WindowManagerPolicy.java
@@ -274,6 +274,11 @@
         public IApplicationToken getAppToken();
 
         /**
+         * Return true if this window is participating in voice interaction.
+         */
+        public boolean isVoiceInteraction();
+
+        /**
          * Return true if, at any point, the application token associated with 
          * this window has actually displayed any windows.  This is most useful 
          * with the "starting up" window to determine if any windows were 
@@ -603,8 +608,15 @@
      * Return whether the given window should forcibly hide everything
      * behind it.  Typically returns true for the keyguard.
      */
-    public boolean doesForceHide(WindowState win, WindowManager.LayoutParams attrs);
-    
+    public boolean doesForceHide(WindowManager.LayoutParams attrs);
+
+
+    /**
+     * Return whether the given window can become one that passes doesForceHide() test.
+     * Typically returns true for the StatusBar.
+     */
+    public boolean isKeyguardHostWindow(WindowManager.LayoutParams attrs);
+
     /**
      * Determine if a window that is behind one that is force hiding
      * (as determined by {@link #doesForceHide}) should actually be hidden.
@@ -613,7 +625,7 @@
      * will conflict with what you set.
      */
     public boolean canBeForceHidden(WindowState win, WindowManager.LayoutParams attrs);
-    
+
     /**
      * Called when the system would like to show a UI to indicate that an
      * application is starting.  You can use this to add a
@@ -1144,12 +1156,6 @@
     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();
@@ -1190,4 +1196,12 @@
      * @return True if the window is a top level one.
      */
     public boolean isTopLevelWindow(int windowType);
+
+    /**
+     * Notifies the keyguard to start fading out.
+     *
+     * @param startTime the start time of the animation in uptime milliseconds
+     * @param fadeoutDuration the duration of the exit animation, in milliseconds
+     */
+    public void startKeyguardExitAnimation(long startTime, long fadeoutDuration);
 }
diff --git a/core/java/android/view/textservice/SpellCheckerSession.java b/core/java/android/view/textservice/SpellCheckerSession.java
index 628da3c..84f395a 100644
--- a/core/java/android/view/textservice/SpellCheckerSession.java
+++ b/core/java/android/view/textservice/SpellCheckerSession.java
@@ -427,8 +427,12 @@
 
         @Override
         public void onGetSentenceSuggestions(SentenceSuggestionsInfo[] results) {
-            mHandler.sendMessage(
-                    Message.obtain(mHandler, MSG_ON_GET_SUGGESTION_MULTIPLE_FOR_SENTENCE, results));
+            synchronized (this) {
+                if (mHandler != null) {
+                    mHandler.sendMessage(Message.obtain(mHandler,
+                            MSG_ON_GET_SUGGESTION_MULTIPLE_FOR_SENTENCE, results));
+                }
+            }
         }
     }
 
diff --git a/core/java/android/webkit/WebSettings.java b/core/java/android/webkit/WebSettings.java
index 7c32c5b..d14c19b 100644
--- a/core/java/android/webkit/WebSettings.java
+++ b/core/java/android/webkit/WebSettings.java
@@ -1460,4 +1460,36 @@
      *     {@link #MIXED_CONTENT_NEVER_ALLOW} or {@link #MIXED_CONTENT_COMPATIBILITY_MODE}.
      */
     public abstract int getMixedContentMode();
+
+    /**
+     * Sets whether to use a video overlay for embedded encrypted video.
+     * In API levels prior to {@link android.os.Build.VERSION_CODES#L}, encrypted video can
+     * only be rendered directly on a secure video surface, so it had been a hard problem to play
+     * encrypted video in HTML.  When this flag is on, WebView can play encrypted video (MSE/EME)
+     * by using a video overlay (aka hole-punching) for videos embedded using HTML &lt;video&gt;
+     * tag.<br>
+     * Caution: This setting is intended for use only in a narrow set of circumstances and apps
+     * should only enable it if they require playback of encrypted video content. It will impose
+     * the following limitations on the WebView:
+     * <ul>
+     * <li> Only one video overlay can be played at a time.
+     * <li> Changes made to position or dimensions of a video element may be propagated to the
+     * corresponding video overlay with a noticeable delay.
+     * <li> The video overlay is not visible to web APIs and as such may not interact with
+     * script or styling. For example, CSS styles applied to the &lt;video&gt; tag may be ignored.
+     * </ul>
+     * This is not an exhaustive set of constraints and it may vary with new versions of the
+     * WebView.
+     * @hide
+     */
+    public abstract void setVideoOverlayForEmbeddedEncryptedVideoEnabled(boolean flag);
+
+    /**
+     * Gets whether a video overlay will be used for embedded encrypted video.
+     *
+     * @return true if WebView uses a video overlay for embedded encrypted video.
+     * @see #setVideoOverlayForEmbeddedEncryptedVideoEnabled
+     * @hide
+     */
+    public abstract boolean getVideoOverlayForEmbeddedEncryptedVideoEnabled();
 }
diff --git a/core/java/android/widget/AbsListView.java b/core/java/android/widget/AbsListView.java
index c9eb130..9a46052 100644
--- a/core/java/android/widget/AbsListView.java
+++ b/core/java/android/widget/AbsListView.java
@@ -2495,17 +2495,25 @@
     }
 
     /**
-     * Positions the selector in a way that mimics keyboard focus. If the
-     * selector drawable supports hotspots, this manages the focus hotspot.
+     * Positions the selector in a way that mimics keyboard focus.
      */
     void positionSelectorLikeFocus(int position, View sel) {
+        // If we're changing position, update the visibility since the selector
+        // is technically being detached from the previous selection.
+        final Drawable selector = mSelector;
+        final boolean manageState = selector != null && mSelectorPosition != position
+                && position != INVALID_POSITION;
+        if (manageState) {
+            selector.setVisible(false, false);
+        }
+
         positionSelector(position, sel);
 
-        final Drawable selector = mSelector;
-        if (selector != null && position != INVALID_POSITION) {
+        if (manageState) {
             final Rect bounds = mSelectorRect;
             final float x = bounds.exactCenterX();
             final float y = bounds.exactCenterY();
+            selector.setVisible(getVisibility() == VISIBLE, false);
             selector.setHotspot(x, y);
         }
     }
@@ -2520,8 +2528,18 @@
         if (sel instanceof SelectionBoundsAdjuster) {
             ((SelectionBoundsAdjuster)sel).adjustListItemSelectionBounds(selectorRect);
         }
-        positionSelector(selectorRect.left, selectorRect.top, selectorRect.right,
-                selectorRect.bottom);
+
+        // Adjust for selection padding.
+        selectorRect.left -= mSelectionLeftPadding;
+        selectorRect.top -= mSelectionTopPadding;
+        selectorRect.right += mSelectionRightPadding;
+        selectorRect.bottom += mSelectionBottomPadding;
+
+        // Update the selector drawable.
+        final Drawable selector = mSelector;
+        if (selector != null) {
+            selector.setBounds(selectorRect);
+        }
 
         final boolean isChildViewEnabled = mIsChildViewEnabled;
         if (sel.isEnabled() != isChildViewEnabled) {
@@ -2532,11 +2550,6 @@
         }
     }
 
-    private void positionSelector(int l, int t, int r, int b) {
-        mSelectorRect.set(l - mSelectionLeftPadding, t - mSelectionTopPadding, r
-                + mSelectionRightPadding, b + mSelectionBottomPadding);
-    }
-
     @Override
     protected void dispatchDraw(Canvas canvas) {
         int saveCount = 0;
diff --git a/core/java/android/widget/ActionMenuPresenter.java b/core/java/android/widget/ActionMenuPresenter.java
index 51759c5..1fddf3e 100644
--- a/core/java/android/widget/ActionMenuPresenter.java
+++ b/core/java/android/widget/ActionMenuPresenter.java
@@ -544,6 +544,7 @@
 
     public void setMenuView(ActionMenuView menuView) {
         mMenuView = menuView;
+        menuView.initialize(mMenu);
     }
 
     private static class SavedState implements Parcelable {
diff --git a/core/java/android/widget/ActionMenuView.java b/core/java/android/widget/ActionMenuView.java
index 3975edf..acee592 100644
--- a/core/java/android/widget/ActionMenuView.java
+++ b/core/java/android/widget/ActionMenuView.java
@@ -69,6 +69,7 @@
     /** @hide */
     public void setPresenter(ActionMenuPresenter presenter) {
         mPresenter = presenter;
+        mPresenter.setMenuView(this);
     }
 
     @Override
@@ -488,7 +489,7 @@
     @Override
     public void onDetachedFromWindow() {
         super.onDetachedFromWindow();
-        mPresenter.dismissPopupMenus();
+        dismissPopupMenus();
     }
 
     /** @hide */
@@ -569,15 +570,65 @@
             mMenu = new MenuBuilder(context);
             mMenu.setCallback(new MenuBuilderCallback());
             mPresenter = new ActionMenuPresenter(context);
-            mPresenter.setMenuView(this);
             mPresenter.setCallback(new ActionMenuPresenterCallback());
             mMenu.addMenuPresenter(mPresenter);
+            mPresenter.setMenuView(this);
         }
 
         return mMenu;
     }
 
     /**
+     * Returns the current menu or null if one has not yet been configured.
+     * @hide Internal use only for action bar integration
+     */
+    public MenuBuilder peekMenu() {
+        return mMenu;
+    }
+
+    /**
+     * Show the overflow items from the associated menu.
+     *
+     * @return true if the menu was able to be shown, false otherwise
+     */
+    public boolean showOverflowMenu() {
+        return mPresenter != null && mPresenter.showOverflowMenu();
+    }
+
+    /**
+     * Hide the overflow items from the associated menu.
+     *
+     * @return true if the menu was able to be hidden, false otherwise
+     */
+    public boolean hideOverflowMenu() {
+        return mPresenter != null && mPresenter.hideOverflowMenu();
+    }
+
+    /**
+     * Check whether the overflow menu is currently showing. This may not reflect
+     * a pending show operation in progress.
+     *
+     * @return true if the overflow menu is currently showing
+     */
+    public boolean isOverflowMenuShowing() {
+        return mPresenter != null && mPresenter.isOverflowMenuShowing();
+    }
+
+    /** @hide */
+    public boolean isOverflowMenuShowPending() {
+        return mPresenter != null && mPresenter.isOverflowMenuShowPending();
+    }
+
+    /**
+     * Dismiss any popups associated with this menu view.
+     */
+    public void dismissPopupMenus() {
+        if (mPresenter != null) {
+            mPresenter.dismissPopupMenus();
+        }
+    }
+
+    /**
      * @hide Private LinearLayout (superclass) API. Un-hide if LinearLayout API is made public.
      */
     @Override
@@ -601,6 +652,11 @@
         return false;
     }
 
+    /** @hide */
+    public void setExpandedActionViewsExclusive(boolean exclusive) {
+        mPresenter.setExpandedActionViewsExclusive(exclusive);
+    }
+
     /**
      * Interface responsible for receiving menu item click events if the items themselves
      * do not have individual item click listeners.
diff --git a/core/java/android/widget/DatePicker.java b/core/java/android/widget/DatePicker.java
index 265dbcd..2c1a77c 100644
--- a/core/java/android/widget/DatePicker.java
+++ b/core/java/android/widget/DatePicker.java
@@ -24,6 +24,7 @@
 import android.os.Parcelable;
 import android.text.TextUtils;
 import android.text.InputType;
+import android.text.format.DateFormat;
 import android.text.format.DateUtils;
 import android.util.AttributeSet;
 import android.util.Log;
@@ -814,8 +815,7 @@
             mSpinners.removeAllViews();
             // We use numeric spinners for year and day, but textual months. Ask icu4c what
             // order the user's locale uses for that combination. http://b/7207103.
-            String pattern = ICU.getBestDateTimePattern("yyyyMMMdd",
-                    Locale.getDefault().toString());
+            String pattern = DateFormat.getBestDateTimePattern(Locale.getDefault(), "yyyyMMMdd");
             char[] order = ICU.getDateFormatOrder(pattern);
             final int spinnerCount = order.length;
             for (int i = 0; i < spinnerCount; i++) {
diff --git a/core/java/android/widget/ImageView.java b/core/java/android/widget/ImageView.java
index eedacb5..572302a 100644
--- a/core/java/android/widget/ImageView.java
+++ b/core/java/android/widget/ImageView.java
@@ -664,7 +664,7 @@
                 InputStream stream = null;
                 try {
                     stream = mContext.getContentResolver().openInputStream(mUri);
-                    d = Drawable.createFromStreamThemed(stream, null, mContext.getTheme());
+                    d = Drawable.createFromStream(stream, null);
                 } catch (Exception e) {
                     Log.w("ImageView", "Unable to open content: " + mUri, e);
                 } finally {
diff --git a/core/java/android/widget/ProgressBar.java b/core/java/android/widget/ProgressBar.java
index 0c3715d..b49938c 100644
--- a/core/java/android/widget/ProgressBar.java
+++ b/core/java/android/widget/ProgressBar.java
@@ -357,9 +357,8 @@
                     Shader.TileMode.REPEAT, Shader.TileMode.CLAMP);
             shapeDrawable.getPaint().setShader(bitmapShader);
 
-            // Ensure the color filter and tint are propagated.
-            shapeDrawable.setTint(bitmap.getTint());
-            shapeDrawable.setTintMode(bitmap.getTintMode());
+            // Ensure the tint and filter are propagated in the correct order.
+            shapeDrawable.setTint(bitmap.getTint(), bitmap.getTintMode());
             shapeDrawable.setColorFilter(bitmap.getColorFilter());
 
             return clip ? new ClipDrawable(
diff --git a/core/java/android/widget/SuggestionsAdapter.java b/core/java/android/widget/SuggestionsAdapter.java
index 0203301..c8917e0 100644
--- a/core/java/android/widget/SuggestionsAdapter.java
+++ b/core/java/android/widget/SuggestionsAdapter.java
@@ -574,7 +574,7 @@
                     throw new FileNotFoundException("Failed to open " + uri);
                 }
                 try {
-                    return Drawable.createFromStreamThemed(stream, null, mContext.getTheme());
+                    return Drawable.createFromStream(stream, null);
                 } finally {
                     try {
                         stream.close();
diff --git a/core/java/android/widget/Toolbar.java b/core/java/android/widget/Toolbar.java
index f903346..419c582 100644
--- a/core/java/android/widget/Toolbar.java
+++ b/core/java/android/widget/Toolbar.java
@@ -18,13 +18,17 @@
 package android.widget;
 
 import android.annotation.NonNull;
+import android.app.ActionBar;
 import android.content.Context;
 import android.content.res.TypedArray;
 import android.graphics.drawable.Drawable;
 import android.os.Parcel;
 import android.os.Parcelable;
+import android.text.Layout;
 import android.text.TextUtils;
 import android.util.AttributeSet;
+import android.util.Log;
+import android.view.CollapsibleActionView;
 import android.view.Gravity;
 import android.view.Menu;
 import android.view.MenuInflater;
@@ -32,7 +36,15 @@
 import android.view.View;
 import android.view.ViewDebug;
 import android.view.ViewGroup;
+import android.view.Window;
 import com.android.internal.R;
+import com.android.internal.view.menu.MenuBuilder;
+import com.android.internal.view.menu.MenuItemImpl;
+import com.android.internal.view.menu.MenuPresenter;
+import com.android.internal.view.menu.MenuView;
+import com.android.internal.view.menu.SubMenuBuilder;
+import com.android.internal.widget.DecorToolbar;
+import com.android.internal.widget.ToolbarWidgetWrapper;
 
 import java.util.ArrayList;
 import java.util.List;
@@ -80,14 +92,25 @@
  * layout is discouraged on API 21 devices and newer.</p>
  */
 public class Toolbar extends ViewGroup {
+    private static final String TAG = "Toolbar";
+
     private ActionMenuView mMenuView;
     private TextView mTitleTextView;
     private TextView mSubtitleTextView;
     private ImageButton mNavButtonView;
     private ImageView mLogoView;
 
+    private Drawable mCollapseIcon;
+    private ImageButton mCollapseButtonView;
+    View mExpandedActionView;
+
     private int mTitleTextAppearance;
     private int mSubtitleTextAppearance;
+    private int mNavButtonStyle;
+
+    private int mButtonGravity;
+
+    private int mMaxButtonHeight;
 
     private int mTitleMarginStart;
     private int mTitleMarginEnd;
@@ -104,6 +127,8 @@
     // Clear me after use.
     private final ArrayList<View> mTempViews = new ArrayList<View>();
 
+    private final int[] mTempMargins = new int[2];
+
     private OnMenuItemClickListener mOnMenuItemClickListener;
 
     private final ActionMenuView.OnMenuItemClickListener mMenuViewItemClickListener =
@@ -117,6 +142,10 @@
                 }
             };
 
+    private ToolbarWidgetWrapper mWrapper;
+    private ActionMenuPresenter mOuterActionMenuPresenter;
+    private ExpandedActionViewMenuPresenter mExpandedMenuPresenter;
+
     public Toolbar(Context context) {
         this(context, null);
     }
@@ -137,7 +166,9 @@
 
         mTitleTextAppearance = a.getResourceId(R.styleable.Toolbar_titleTextAppearance, 0);
         mSubtitleTextAppearance = a.getResourceId(R.styleable.Toolbar_subtitleTextAppearance, 0);
+        mNavButtonStyle = a.getResourceId(R.styleable.Toolbar_navigationButtonStyle, 0);
         mGravity = a.getInteger(R.styleable.Toolbar_gravity, mGravity);
+        mButtonGravity = a.getInteger(R.styleable.Toolbar_buttonGravity, Gravity.TOP);
         mTitleMarginStart = mTitleMarginEnd = mTitleMarginTop = mTitleMarginBottom =
                 a.getDimensionPixelOffset(R.styleable.Toolbar_titleMargins, 0);
 
@@ -162,6 +193,8 @@
             mTitleMarginBottom = marginBottom;
         }
 
+        mMaxButtonHeight = a.getDimensionPixelSize(R.styleable.Toolbar_maxButtonHeight, -1);
+
         final int contentInsetStart =
                 a.getDimensionPixelOffset(R.styleable.Toolbar_contentInsetStart,
                         RtlSpacingHelper.UNDEFINED);
@@ -180,6 +213,8 @@
             mContentInsets.setRelative(contentInsetStart, contentInsetEnd);
         }
 
+        mCollapseIcon = a.getDrawable(R.styleable.Toolbar_collapseIcon);
+
         final CharSequence title = a.getText(R.styleable.Toolbar_title);
         if (!TextUtils.isEmpty(title)) {
             setTitle(title);
@@ -187,7 +222,7 @@
 
         final CharSequence subtitle = a.getText(R.styleable.Toolbar_subtitle);
         if (!TextUtils.isEmpty(subtitle)) {
-            setSubtitle(title);
+            setSubtitle(subtitle);
         }
         a.recycle();
     }
@@ -211,6 +246,110 @@
         setLogo(getContext().getDrawable(resId));
     }
 
+    /** @hide */
+    public boolean canShowOverflowMenu() {
+        return getVisibility() == VISIBLE && mMenuView != null && mMenuView.isOverflowReserved();
+    }
+
+    /**
+     * Check whether the overflow menu is currently showing. This may not reflect
+     * a pending show operation in progress.
+     *
+     * @return true if the overflow menu is currently showing
+     */
+    public boolean isOverflowMenuShowing() {
+        return mMenuView != null && mMenuView.isOverflowMenuShowing();
+    }
+
+    /** @hide */
+    public boolean isOverflowMenuShowPending() {
+        return mMenuView != null && mMenuView.isOverflowMenuShowPending();
+    }
+
+    /**
+     * Show the overflow items from the associated menu.
+     *
+     * @return true if the menu was able to be shown, false otherwise
+     */
+    public boolean showOverflowMenu() {
+        return mMenuView != null && mMenuView.showOverflowMenu();
+    }
+
+    /**
+     * Hide the overflow items from the associated menu.
+     *
+     * @return true if the menu was able to be hidden, false otherwise
+     */
+    public boolean hideOverflowMenu() {
+        return mMenuView != null && mMenuView.hideOverflowMenu();
+    }
+
+    /** @hide */
+    public void setMenu(MenuBuilder menu, ActionMenuPresenter outerPresenter) {
+        if (menu == null && mMenuView == null) {
+            return;
+        }
+
+        ensureMenuView();
+        final MenuBuilder oldMenu = mMenuView.peekMenu();
+        if (oldMenu == menu) {
+            return;
+        }
+
+        if (oldMenu != null) {
+            oldMenu.removeMenuPresenter(mOuterActionMenuPresenter);
+            oldMenu.removeMenuPresenter(mExpandedMenuPresenter);
+        }
+
+        final Context context = getContext();
+
+        if (mExpandedMenuPresenter == null) {
+            mExpandedMenuPresenter = new ExpandedActionViewMenuPresenter();
+        }
+
+        outerPresenter.setExpandedActionViewsExclusive(true);
+        if (menu != null) {
+            menu.addMenuPresenter(outerPresenter);
+            menu.addMenuPresenter(mExpandedMenuPresenter);
+        } else {
+            outerPresenter.initForMenu(context, null);
+            mExpandedMenuPresenter.initForMenu(context, null);
+            outerPresenter.updateMenuView(true);
+            mExpandedMenuPresenter.updateMenuView(true);
+        }
+        mMenuView.setPresenter(outerPresenter);
+        mOuterActionMenuPresenter = outerPresenter;
+    }
+
+    /**
+     * Dismiss all currently showing popup menus, including overflow or submenus.
+     */
+    public void dismissPopupMenus() {
+        if (mMenuView != null) {
+            mMenuView.dismissPopupMenus();
+        }
+    }
+
+    /** @hide */
+    public boolean isTitleTruncated() {
+        if (mTitleTextView == null) {
+            return false;
+        }
+
+        final Layout titleLayout = mTitleTextView.getLayout();
+        if (titleLayout == null) {
+            return false;
+        }
+
+        final int lineCount = titleLayout.getLineCount();
+        for (int i = 0; i < lineCount; i++) {
+            if (titleLayout.getEllipsisCount(i) > 0) {
+                return true;
+            }
+        }
+        return false;
+    }
+
     /**
      * Set a logo drawable.
      *
@@ -222,9 +361,7 @@
      */
     public void setLogo(Drawable drawable) {
         if (drawable != null) {
-            if (mLogoView == null) {
-                mLogoView = new ImageView(getContext());
-            }
+            ensureLogoView();
             if (mLogoView.getParent() == null) {
                 addSystemView(mLogoView);
             }
@@ -268,8 +405,8 @@
      * @param description Description to set
      */
     public void setLogoDescription(CharSequence description) {
-        if (!TextUtils.isEmpty(description) && mLogoView == null) {
-            mLogoView = new ImageView(getContext());
+        if (!TextUtils.isEmpty(description)) {
+            ensureLogoView();
         }
         if (mLogoView != null) {
             mLogoView.setContentDescription(description);
@@ -285,10 +422,48 @@
         return mLogoView != null ? mLogoView.getContentDescription() : null;
     }
 
+    private void ensureLogoView() {
+        if (mLogoView == null) {
+            mLogoView = new ImageView(getContext());
+        }
+    }
+
     /**
-     * Return the current title displayed in the toolbar.
+     * Check whether this Toolbar is currently hosting an expanded action view.
      *
-     * @return The current title
+     * <p>An action view may be expanded either directly from the
+     * {@link android.view.MenuItem MenuItem} it belongs to or by user action. If the Toolbar
+     * has an expanded action view it can be collapsed using the {@link #collapseActionView()}
+     * method.</p>
+     *
+     * @return true if the Toolbar has an expanded action view
+     */
+    public boolean hasExpandedActionView() {
+        return mExpandedMenuPresenter != null &&
+                mExpandedMenuPresenter.mCurrentExpandedItem != null;
+    }
+
+    /**
+     * Collapse a currently expanded action view. If this Toolbar does not have an
+     * expanded action view this method has no effect.
+     *
+     * <p>An action view may be expanded either directly from the
+     * {@link android.view.MenuItem MenuItem} it belongs to or by user action.</p>
+     *
+     * @see #hasExpandedActionView()
+     */
+    public void collapseActionView() {
+        final MenuItemImpl item = mExpandedMenuPresenter == null ? null :
+                mExpandedMenuPresenter.mCurrentExpandedItem;
+        if (item != null) {
+            item.collapseActionView();
+        }
+    }
+
+    /**
+     * Returns the title of this toolbar.
+     *
+     * @return The current title.
      */
     public CharSequence getTitle() {
         return mTitleText;
@@ -319,6 +494,8 @@
             if (mTitleTextView == null) {
                 final Context context = getContext();
                 mTitleTextView = new TextView(context);
+                mTitleTextView.setSingleLine();
+                mTitleTextView.setEllipsize(TextUtils.TruncateAt.END);
                 mTitleTextView.setTextAppearance(context, mTitleTextAppearance);
             }
             if (mTitleTextView.getParent() == null) {
@@ -365,6 +542,8 @@
             if (mSubtitleTextView == null) {
                 final Context context = getContext();
                 mSubtitleTextView = new TextView(context);
+                mSubtitleTextView.setSingleLine();
+                mSubtitleTextView.setEllipsize(TextUtils.TruncateAt.END);
                 mSubtitleTextView.setTextAppearance(context, mSubtitleTextAppearance);
             }
             if (mSubtitleTextView.getParent() == null) {
@@ -380,6 +559,28 @@
     }
 
     /**
+     * Sets the text color, size, style, hint color, and highlight color
+     * from the specified TextAppearance resource.
+     */
+    public void setTitleTextAppearance(Context context, int resId) {
+        mTitleTextAppearance = resId;
+        if (mTitleTextView != null) {
+            mTitleTextView.setTextAppearance(context, resId);
+        }
+    }
+
+    /**
+     * Sets the text color, size, style, hint color, and highlight color
+     * from the specified TextAppearance resource.
+     */
+    public void setSubtitleTextAppearance(Context context, int resId) {
+        mSubtitleTextAppearance = resId;
+        if (mSubtitleTextView != null) {
+            mSubtitleTextView.setTextAppearance(context, resId);
+        }
+    }
+
+    /**
      * 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
@@ -395,6 +596,30 @@
     }
 
     /**
+     * Set a content description for the navigation button if one is present. The content
+     * description will be read via screen readers or other accessibility systems to explain
+     * the action of the navigation button.
+     *
+     * @param description Content description to set
+     */
+    public void setNavigationContentDescription(CharSequence description) {
+        ensureNavButtonView();
+        mNavButtonView.setContentDescription(description);
+    }
+
+    /**
+     * Set a content description for the navigation button if one is present. The content
+     * description will be read via screen readers or other accessibility systems to explain
+     * the action of the navigation button.
+     *
+     * @param resId Resource ID of a content description string to set
+     */
+    public void setNavigationContentDescription(int resId) {
+        ensureNavButtonView();
+        mNavButtonView.setContentDescription(resId != 0 ? getContext().getText(resId) : null);
+    }
+
+    /**
      * 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
@@ -480,12 +705,32 @@
      * @return The toolbar's Menu
      */
     public Menu getMenu() {
+        ensureMenu();
+        return mMenuView.getMenu();
+    }
+
+    private void ensureMenu() {
+        ensureMenuView();
+        if (mMenuView.peekMenu() == null) {
+            // Initialize a new menu for the first time.
+            final MenuBuilder menu = (MenuBuilder) mMenuView.getMenu();
+            if (mExpandedMenuPresenter == null) {
+                mExpandedMenuPresenter = new ExpandedActionViewMenuPresenter();
+            }
+            mMenuView.setExpandedActionViewsExclusive(true);
+            menu.addMenuPresenter(mExpandedMenuPresenter);
+        }
+    }
+
+    private void ensureMenuView() {
         if (mMenuView == null) {
             mMenuView = new ActionMenuView(getContext());
             mMenuView.setOnMenuItemClickListener(mMenuViewItemClickListener);
+            final LayoutParams lp = generateDefaultLayoutParams();
+            lp.gravity = Gravity.END | (mButtonGravity & Gravity.VERTICAL_GRAVITY_MASK);
+            mMenuView.setLayoutParams(lp);
             addSystemView(mMenuView);
         }
-        return mMenuView.getMenu();
     }
 
     private MenuInflater getMenuInflater() {
@@ -634,7 +879,27 @@
 
     private void ensureNavButtonView() {
         if (mNavButtonView == null) {
-            mNavButtonView = new ImageButton(getContext(), null, R.attr.borderlessButtonStyle);
+            mNavButtonView = new ImageButton(getContext(), null, 0, mNavButtonStyle);
+            final LayoutParams lp = generateDefaultLayoutParams();
+            lp.gravity = Gravity.START | (mButtonGravity & Gravity.VERTICAL_GRAVITY_MASK);
+            mNavButtonView.setLayoutParams(lp);
+        }
+    }
+
+    private void ensureCollapseButtonView() {
+        if (mCollapseButtonView == null) {
+            mCollapseButtonView = new ImageButton(getContext(), null, 0, mNavButtonStyle);
+            mCollapseButtonView.setImageDrawable(mCollapseIcon);
+            final LayoutParams lp = generateDefaultLayoutParams();
+            lp.gravity = Gravity.START | (mButtonGravity & Gravity.VERTICAL_GRAVITY_MASK);
+            lp.mViewType = LayoutParams.EXPANDED;
+            mCollapseButtonView.setLayoutParams(lp);
+            mCollapseButtonView.setOnClickListener(new OnClickListener() {
+                @Override
+                public void onClick(View v) {
+                    collapseActionView();
+                }
+            });
         }
     }
 
@@ -657,39 +922,121 @@
         super.onRestoreInstanceState(ss.getSuperState());
     }
 
+    private void measureChildConstrained(View child, int parentWidthSpec, int widthUsed,
+            int parentHeightSpec, int heightUsed, int heightConstraint) {
+        final MarginLayoutParams lp = (MarginLayoutParams) child.getLayoutParams();
+
+        int childWidthSpec = getChildMeasureSpec(parentWidthSpec,
+                mPaddingLeft + mPaddingRight + lp.leftMargin + lp.rightMargin
+                        + widthUsed, lp.width);
+        int childHeightSpec = getChildMeasureSpec(parentHeightSpec,
+                mPaddingTop + mPaddingBottom + lp.topMargin + lp.bottomMargin
+                        + heightUsed, lp.height);
+
+        final int childHeightMode = MeasureSpec.getMode(childHeightSpec);
+        if (childHeightMode != MeasureSpec.EXACTLY && heightConstraint >= 0) {
+            final int size = childHeightMode != MeasureSpec.UNSPECIFIED ?
+                    Math.min(MeasureSpec.getSize(childHeightSpec), heightConstraint) :
+                    heightConstraint;
+            childHeightSpec = MeasureSpec.makeMeasureSpec(size, MeasureSpec.EXACTLY);
+        }
+        child.measure(childWidthSpec, childHeightSpec);
+    }
+
+    /**
+     * Returns the width + uncollapsed margins
+     */
+    private int measureChildCollapseMargins(View child,
+            int parentWidthMeasureSpec, int widthUsed,
+            int parentHeightMeasureSpec, int heightUsed, int[] collapsingMargins) {
+        final MarginLayoutParams lp = (MarginLayoutParams) child.getLayoutParams();
+
+        final int leftDiff = lp.leftMargin - collapsingMargins[0];
+        final int rightDiff = lp.rightMargin - collapsingMargins[1];
+        final int leftMargin = Math.max(0, leftDiff);
+        final int rightMargin = Math.max(0, rightDiff);
+        final int hMargins = leftMargin + rightMargin;
+        collapsingMargins[0] = Math.max(0, -leftDiff);
+        collapsingMargins[1] = Math.max(0, -rightDiff);
+
+        final int childWidthMeasureSpec = getChildMeasureSpec(parentWidthMeasureSpec,
+                mPaddingLeft + mPaddingRight + hMargins + widthUsed, lp.width);
+        final int childHeightMeasureSpec = getChildMeasureSpec(parentHeightMeasureSpec,
+                mPaddingTop + mPaddingBottom + lp.topMargin + lp.bottomMargin
+                        + heightUsed, lp.height);
+
+        child.measure(childWidthMeasureSpec, childHeightMeasureSpec);
+        return child.getMeasuredWidth() + hMargins;
+    }
+
     @Override
     protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
         int width = 0;
         int height = 0;
         int childState = 0;
 
+        final int[] collapsingMargins = mTempMargins;
+        final int marginStartIndex;
+        final int marginEndIndex;
+        if (isLayoutRtl()) {
+            marginStartIndex = 1;
+            marginEndIndex = 0;
+        } else {
+            marginStartIndex = 0;
+            marginEndIndex = 1;
+        }
+
         // System views measure first.
 
         int navWidth = 0;
         if (shouldLayout(mNavButtonView)) {
-            measureChildWithMargins(mNavButtonView, widthMeasureSpec, width, heightMeasureSpec, 0);
+            measureChildConstrained(mNavButtonView, widthMeasureSpec, width, heightMeasureSpec, 0,
+                    mMaxButtonHeight);
             navWidth = mNavButtonView.getMeasuredWidth() + getHorizontalMargins(mNavButtonView);
             height = Math.max(height, mNavButtonView.getMeasuredHeight() +
                     getVerticalMargins(mNavButtonView));
             childState = combineMeasuredStates(childState, mNavButtonView.getMeasuredState());
         }
 
-        width += Math.max(getContentInsetStart(), navWidth);
+        if (shouldLayout(mCollapseButtonView)) {
+            measureChildConstrained(mCollapseButtonView, widthMeasureSpec, width,
+                    heightMeasureSpec, 0, mMaxButtonHeight);
+            navWidth = mCollapseButtonView.getMeasuredWidth() +
+                    getHorizontalMargins(mCollapseButtonView);
+            height = Math.max(height, mCollapseButtonView.getMeasuredHeight() +
+                    getVerticalMargins(mCollapseButtonView));
+            childState = combineMeasuredStates(childState, mCollapseButtonView.getMeasuredState());
+        }
+
+        final int contentInsetStart = getContentInsetStart();
+        width += Math.max(contentInsetStart, navWidth);
+        collapsingMargins[marginStartIndex] = Math.max(0, contentInsetStart - navWidth);
 
         int menuWidth = 0;
         if (shouldLayout(mMenuView)) {
-            measureChildWithMargins(mMenuView, widthMeasureSpec, width, heightMeasureSpec, 0);
+            measureChildConstrained(mMenuView, widthMeasureSpec, width, heightMeasureSpec, 0,
+                    mMaxButtonHeight);
             menuWidth = mMenuView.getMeasuredWidth() + getHorizontalMargins(mMenuView);
             height = Math.max(height, mMenuView.getMeasuredHeight() +
                     getVerticalMargins(mMenuView));
             childState = combineMeasuredStates(childState, mMenuView.getMeasuredState());
         }
 
-        width += Math.max(getContentInsetEnd(), menuWidth);
+        final int contentInsetEnd = getContentInsetEnd();
+        width += Math.max(contentInsetEnd, menuWidth);
+        collapsingMargins[marginEndIndex] = Math.max(0, contentInsetEnd - menuWidth);
+
+        if (shouldLayout(mExpandedActionView)) {
+            width += measureChildCollapseMargins(mExpandedActionView, widthMeasureSpec, width,
+                    heightMeasureSpec, 0, collapsingMargins);
+            height = Math.max(height, mExpandedActionView.getMeasuredHeight() +
+                    getVerticalMargins(mExpandedActionView));
+            childState = combineMeasuredStates(childState, mExpandedActionView.getMeasuredState());
+        }
 
         if (shouldLayout(mLogoView)) {
-            measureChildWithMargins(mLogoView, widthMeasureSpec, width, heightMeasureSpec, 0);
-            width += mLogoView.getMeasuredWidth() + getHorizontalMargins(mLogoView);
+            width += measureChildCollapseMargins(mLogoView, widthMeasureSpec, width,
+                    heightMeasureSpec, 0, collapsingMargins);
             height = Math.max(height, mLogoView.getMeasuredHeight() +
                     getVerticalMargins(mLogoView));
             childState = combineMeasuredStates(childState, mLogoView.getMeasuredState());
@@ -700,17 +1047,18 @@
         final int titleVertMargins = mTitleMarginTop + mTitleMarginBottom;
         final int titleHorizMargins = mTitleMarginStart + mTitleMarginEnd;
         if (shouldLayout(mTitleTextView)) {
-            measureChildWithMargins(mTitleTextView, widthMeasureSpec, width + titleHorizMargins,
-                    heightMeasureSpec, titleVertMargins);
+            titleWidth = measureChildCollapseMargins(mTitleTextView, widthMeasureSpec,
+                    width + titleHorizMargins, heightMeasureSpec, titleVertMargins,
+                    collapsingMargins);
             titleWidth = mTitleTextView.getMeasuredWidth() + getHorizontalMargins(mTitleTextView);
             titleHeight = mTitleTextView.getMeasuredHeight() + getVerticalMargins(mTitleTextView);
             childState = combineMeasuredStates(childState, mTitleTextView.getMeasuredState());
         }
         if (shouldLayout(mSubtitleTextView)) {
-            measureChildWithMargins(mSubtitleTextView, widthMeasureSpec, width + titleHorizMargins,
-                    heightMeasureSpec, titleHeight + titleVertMargins);
-            titleWidth = Math.max(titleWidth, mSubtitleTextView.getMeasuredWidth() +
-                    getHorizontalMargins(mSubtitleTextView));
+            titleWidth = Math.max(titleWidth, measureChildCollapseMargins(mSubtitleTextView,
+                    widthMeasureSpec, width + titleHorizMargins,
+                    heightMeasureSpec, titleHeight + titleVertMargins,
+                    collapsingMargins));
             titleHeight += mSubtitleTextView.getMeasuredHeight() +
                     getVerticalMargins(mSubtitleTextView);
             childState = combineMeasuredStates(childState, mSubtitleTextView.getMeasuredState());
@@ -723,13 +1071,13 @@
         for (int i = 0; i < childCount; i++) {
             final View child = getChildAt(i);
             final LayoutParams lp = (LayoutParams) child.getLayoutParams();
-            if (lp.mViewType == LayoutParams.SYSTEM || !shouldLayout(child)) {
+            if (lp.mViewType != LayoutParams.CUSTOM || !shouldLayout(child)) {
                 // We already got all system views above. Skip them and GONE views.
                 continue;
             }
 
-            measureChildWithMargins(child, widthMeasureSpec, width, heightMeasureSpec, 0);
-            width += child.getMeasuredWidth() + getHorizontalMargins(child);
+            width += measureChildCollapseMargins(child, widthMeasureSpec, width,
+                    heightMeasureSpec, 0, collapsingMargins);
             height = Math.max(height, child.getMeasuredHeight() + getVerticalMargins(child));
             childState = combineMeasuredStates(childState, child.getMeasuredState());
         }
@@ -760,30 +1108,51 @@
         int left = paddingLeft;
         int right = width - paddingRight;
 
+        final int[] collapsingMargins = mTempMargins;
+        collapsingMargins[0] = collapsingMargins[1] = 0;
+
         if (shouldLayout(mNavButtonView)) {
             if (isRtl) {
-                right = layoutChildRight(mNavButtonView, right);
+                right = layoutChildRight(mNavButtonView, right, collapsingMargins);
             } else {
-                left = layoutChildLeft(mNavButtonView, left);
+                left = layoutChildLeft(mNavButtonView, left, collapsingMargins);
+            }
+        }
+
+        if (shouldLayout(mCollapseButtonView)) {
+            if (isRtl) {
+                right = layoutChildRight(mCollapseButtonView, right, collapsingMargins);
+            } else {
+                left = layoutChildLeft(mCollapseButtonView, left, collapsingMargins);
             }
         }
 
         if (shouldLayout(mMenuView)) {
             if (isRtl) {
-                left = layoutChildLeft(mMenuView, left);
+                left = layoutChildLeft(mMenuView, left, collapsingMargins);
             } else {
-                right = layoutChildRight(mMenuView, right);
+                right = layoutChildRight(mMenuView, right, collapsingMargins);
             }
         }
 
+        collapsingMargins[0] = Math.max(0, getContentInsetLeft() - left);
+        collapsingMargins[1] = Math.max(0, getContentInsetRight() - (width - paddingRight - right));
         left = Math.max(left, getContentInsetLeft());
         right = Math.min(right, width - paddingRight - getContentInsetRight());
 
+        if (shouldLayout(mExpandedActionView)) {
+            if (isRtl) {
+                right = layoutChildRight(mExpandedActionView, right, collapsingMargins);
+            } else {
+                left = layoutChildLeft(mExpandedActionView, left, collapsingMargins);
+            }
+        }
+
         if (shouldLayout(mLogoView)) {
             if (isRtl) {
-                right = layoutChildRight(mLogoView, right);
+                right = layoutChildRight(mLogoView, right, collapsingMargins);
             } else {
-                left = layoutChildLeft(mLogoView, left);
+                left = layoutChildLeft(mLogoView, left, collapsingMargins);
             }
         }
 
@@ -801,79 +1170,83 @@
 
         if (layoutTitle || layoutSubtitle) {
             int titleTop;
+            final View topChild = layoutTitle ? mTitleTextView : mSubtitleTextView;
+            final View bottomChild = layoutSubtitle ? mSubtitleTextView : mTitleTextView;
+            final LayoutParams toplp = (LayoutParams) topChild.getLayoutParams();
+            final LayoutParams bottomlp = (LayoutParams) bottomChild.getLayoutParams();
+
             switch (mGravity & Gravity.VERTICAL_GRAVITY_MASK) {
                 case Gravity.TOP:
-                    titleTop = getPaddingTop();
+                    titleTop = getPaddingTop() + toplp.topMargin + mTitleMarginTop;
                     break;
                 default:
                 case Gravity.CENTER_VERTICAL:
-                    final View child = layoutTitle ? mTitleTextView : mSubtitleTextView;
-                    final LayoutParams lp = (LayoutParams) child.getLayoutParams();
                     final int space = height - paddingTop - paddingBottom;
                     int spaceAbove = (space - titleHeight) / 2;
-                    if (spaceAbove < lp.topMargin + mTitleMarginTop) {
-                        spaceAbove = lp.topMargin + mTitleMarginTop;
+                    if (spaceAbove < toplp.topMargin + mTitleMarginTop) {
+                        spaceAbove = toplp.topMargin + mTitleMarginTop;
                     } else {
                         final int spaceBelow = height - paddingBottom - titleHeight -
                                 spaceAbove - paddingTop;
-                        if (spaceBelow < lp.bottomMargin + mTitleMarginBottom) {
+                        if (spaceBelow < toplp.bottomMargin + mTitleMarginBottom) {
                             spaceAbove = Math.max(0, spaceAbove -
-                                    (lp.bottomMargin + mTitleMarginBottom - spaceBelow));
+                                    (bottomlp.bottomMargin + mTitleMarginBottom - spaceBelow));
                         }
                     }
                     titleTop = paddingTop + spaceAbove;
                     break;
                 case Gravity.BOTTOM:
-                    titleTop = height - paddingBottom - titleHeight;
+                    titleTop = height - paddingBottom - bottomlp.bottomMargin - mTitleMarginBottom -
+                            titleHeight;
                     break;
             }
             if (isRtl) {
+                final int rd = mTitleMarginStart - collapsingMargins[1];
+                right -= Math.max(0, rd);
+                collapsingMargins[1] = Math.max(0, -rd);
                 int titleRight = right;
                 int subtitleRight = right;
-                titleTop += mTitleMarginTop;
+
                 if (layoutTitle) {
                     final LayoutParams lp = (LayoutParams) mTitleTextView.getLayoutParams();
-                    titleRight -= lp.rightMargin + mTitleMarginStart;
-                    titleTop += lp.topMargin;
                     final int titleLeft = titleRight - mTitleTextView.getMeasuredWidth();
                     final int titleBottom = titleTop + mTitleTextView.getMeasuredHeight();
                     mTitleTextView.layout(titleLeft, titleTop, titleRight, titleBottom);
-                    titleRight = titleLeft - lp.leftMargin - mTitleMarginEnd;
+                    titleRight = titleLeft - mTitleMarginEnd;
                     titleTop = titleBottom + lp.bottomMargin;
                 }
                 if (layoutSubtitle) {
                     final LayoutParams lp = (LayoutParams) mSubtitleTextView.getLayoutParams();
-                    subtitleRight -= lp.rightMargin + mTitleMarginStart;
                     titleTop += lp.topMargin;
                     final int subtitleLeft = subtitleRight - mSubtitleTextView.getMeasuredWidth();
                     final int subtitleBottom = titleTop + mSubtitleTextView.getMeasuredHeight();
                     mSubtitleTextView.layout(subtitleLeft, titleTop, subtitleRight, subtitleBottom);
-                    subtitleRight = subtitleRight - lp.leftMargin - mTitleMarginEnd;
+                    subtitleRight = subtitleRight - mTitleMarginEnd;
                     titleTop = subtitleBottom + lp.bottomMargin;
                 }
                 right = Math.max(titleRight, subtitleRight);
             } else {
+                final int ld = mTitleMarginStart - collapsingMargins[0];
+                left += Math.max(0, ld);
+                collapsingMargins[0] = Math.max(0, -ld);
                 int titleLeft = left;
                 int subtitleLeft = left;
-                titleTop += mTitleMarginTop;
+
                 if (layoutTitle) {
                     final LayoutParams lp = (LayoutParams) mTitleTextView.getLayoutParams();
-                    titleLeft += lp.leftMargin + mTitleMarginStart;
-                    titleTop += lp.topMargin;
                     final int titleRight = titleLeft + mTitleTextView.getMeasuredWidth();
                     final int titleBottom = titleTop + mTitleTextView.getMeasuredHeight();
                     mTitleTextView.layout(titleLeft, titleTop, titleRight, titleBottom);
-                    titleLeft = titleRight + lp.rightMargin + mTitleMarginEnd;
+                    titleLeft = titleRight + mTitleMarginEnd;
                     titleTop = titleBottom + lp.bottomMargin;
                 }
                 if (layoutSubtitle) {
                     final LayoutParams lp = (LayoutParams) mSubtitleTextView.getLayoutParams();
-                    subtitleLeft += lp.leftMargin + mTitleMarginStart;
                     titleTop += lp.topMargin;
                     final int subtitleRight = subtitleLeft + mSubtitleTextView.getMeasuredWidth();
                     final int subtitleBottom = titleTop + mSubtitleTextView.getMeasuredHeight();
                     mSubtitleTextView.layout(subtitleLeft, titleTop, subtitleRight, subtitleBottom);
-                    subtitleLeft = subtitleRight + lp.rightMargin + mTitleMarginEnd;
+                    subtitleLeft = subtitleRight + mTitleMarginEnd;
                     titleTop = subtitleBottom + lp.bottomMargin;
                 }
                 left = Math.max(titleLeft, subtitleLeft);
@@ -886,19 +1259,19 @@
         addCustomViewsWithGravity(mTempViews, Gravity.LEFT);
         final int leftViewsCount = mTempViews.size();
         for (int i = 0; i < leftViewsCount; i++) {
-            left = layoutChildLeft(mTempViews.get(i), left);
+            left = layoutChildLeft(mTempViews.get(i), left, collapsingMargins);
         }
 
         addCustomViewsWithGravity(mTempViews, Gravity.RIGHT);
         final int rightViewsCount = mTempViews.size();
         for (int i = 0; i < rightViewsCount; i++) {
-            right = layoutChildRight(mTempViews.get(i), right);
+            right = layoutChildRight(mTempViews.get(i), right, collapsingMargins);
         }
 
         // Centered views try to center with respect to the whole bar, but views pinned
         // to the left or right can push the mass of centered views to one side or the other.
-        addCustomViewsWithGravity(mTempViews, Gravity.CENTER);
-        final int centerViewsWidth = getViewListMeasuredWidth(mTempViews);
+        addCustomViewsWithGravity(mTempViews, Gravity.CENTER_HORIZONTAL);
+        final int centerViewsWidth = getViewListMeasuredWidth(mTempViews, collapsingMargins);
         final int parentCenter = paddingLeft + (width - paddingLeft - paddingRight) / 2;
         final int halfCenterViewsWidth = centerViewsWidth / 2;
         int centerLeft = parentCenter - halfCenterViewsWidth;
@@ -911,25 +1284,35 @@
 
         final int centerViewsCount = mTempViews.size();
         for (int i = 0; i < centerViewsCount; i++) {
-            centerLeft = layoutChildLeft(mTempViews.get(i), centerLeft);
+            centerLeft = layoutChildLeft(mTempViews.get(i), centerLeft, collapsingMargins);
         }
         mTempViews.clear();
     }
 
-    private int getViewListMeasuredWidth(List<View> views) {
+    private int getViewListMeasuredWidth(List<View> views, int[] collapsingMargins) {
+        int collapseLeft = collapsingMargins[0];
+        int collapseRight = collapsingMargins[1];
         int width = 0;
         final int count = views.size();
         for (int i = 0; i < count; i++) {
             final View v = views.get(i);
             final LayoutParams lp = (LayoutParams) v.getLayoutParams();
-            width += lp.leftMargin + v.getMeasuredWidth() + lp.rightMargin;
+            final int l = lp.leftMargin - collapseLeft;
+            final int r = lp.rightMargin - collapseRight;
+            final int leftMargin = Math.max(0, l);
+            final int rightMargin = Math.max(0, r);
+            collapseLeft = Math.max(0, -l);
+            collapseRight = Math.max(0, -r);
+            width += leftMargin + v.getMeasuredWidth() + rightMargin;
         }
         return width;
     }
 
-    private int layoutChildLeft(View child, int left) {
+    private int layoutChildLeft(View child, int left, int[] collapsingMargins) {
         final LayoutParams lp = (LayoutParams) child.getLayoutParams();
-        left += lp.leftMargin;
+        final int l = lp.leftMargin - collapsingMargins[0];
+        left += Math.max(0, l);
+        collapsingMargins[0] = Math.max(0, -l);
         final int top = getChildTop(child);
         final int childWidth = child.getMeasuredWidth();
         child.layout(left, top, left + childWidth, top + child.getMeasuredHeight());
@@ -937,9 +1320,11 @@
         return left;
     }
 
-    private int layoutChildRight(View child, int right) {
+    private int layoutChildRight(View child, int right, int[] collapsingMargins) {
         final LayoutParams lp = (LayoutParams) child.getLayoutParams();
-        right -= lp.rightMargin;
+        final int r = lp.rightMargin - collapsingMargins[1];
+        right -= Math.max(0, r);
+        collapsingMargins[1] = Math.max(0, -r);
         final int top = getChildTop(child);
         final int childWidth = child.getMeasuredWidth();
         child.layout(right - childWidth, top, right, top + child.getMeasuredHeight());
@@ -1007,17 +1392,16 @@
             for (int i = childCount - 1; i >= 0; i--) {
                 final View child = getChildAt(i);
                 final LayoutParams lp = (LayoutParams) child.getLayoutParams();
-                if (lp.mViewType != LayoutParams.SYSTEM && shouldLayout(child) &&
+                if (lp.mViewType == LayoutParams.CUSTOM && shouldLayout(child) &&
                         getChildHorizontalGravity(lp.gravity) == absGrav) {
                     views.add(child);
                 }
-
             }
         } else {
             for (int i = 0; i < childCount; i++) {
                 final View child = getChildAt(i);
                 final LayoutParams lp = (LayoutParams) child.getLayoutParams();
-                if (lp.mViewType != LayoutParams.SYSTEM && shouldLayout(child) &&
+                if (lp.mViewType == LayoutParams.CUSTOM && shouldLayout(child) &&
                         getChildHorizontalGravity(lp.gravity) == absGrav) {
                     views.add(child);
                 }
@@ -1054,14 +1438,16 @@
     }
 
     @Override
-    public ViewGroup.LayoutParams generateLayoutParams(AttributeSet attrs) {
-        return super.generateLayoutParams(attrs);
+    public LayoutParams generateLayoutParams(AttributeSet attrs) {
+        return new LayoutParams(getContext(), attrs);
     }
 
     @Override
-    protected ViewGroup.LayoutParams generateLayoutParams(ViewGroup.LayoutParams p) {
+    protected LayoutParams generateLayoutParams(ViewGroup.LayoutParams p) {
         if (p instanceof LayoutParams) {
             return new LayoutParams((LayoutParams) p);
+        } else if (p instanceof ActionBar.LayoutParams) {
+            return new LayoutParams((ActionBar.LayoutParams) p);
         } else if (p instanceof MarginLayoutParams) {
             return new LayoutParams((MarginLayoutParams) p);
         } else {
@@ -1070,7 +1456,7 @@
     }
 
     @Override
-    protected ViewGroup.LayoutParams generateDefaultLayoutParams() {
+    protected LayoutParams generateDefaultLayoutParams() {
         return new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
     }
 
@@ -1083,6 +1469,25 @@
         return ((LayoutParams) child.getLayoutParams()).mViewType == LayoutParams.CUSTOM;
     }
 
+    /** @hide */
+    public DecorToolbar getWrapper() {
+        if (mWrapper == null) {
+            mWrapper = new ToolbarWidgetWrapper(this);
+        }
+        return mWrapper;
+    }
+
+    private void setChildVisibilityForExpandedActionView(boolean expand) {
+        final int childCount = getChildCount();
+        for (int i = 0; i < childCount; i++) {
+            final View child = getChildAt(i);
+            final LayoutParams lp = (LayoutParams) child.getLayoutParams();
+            if (lp.mViewType != LayoutParams.EXPANDED && child != mMenuView) {
+                child.setVisibility(expand ? GONE : VISIBLE);
+            }
+        }
+    }
+
     /**
      * Interface responsible for receiving menu item click events if the items themselves
      * do not have individual item click listeners.
@@ -1103,44 +1508,15 @@
      *
      * @attr ref android.R.styleable#Toolbar_LayoutParams_layout_gravity
      */
-    public static class LayoutParams extends MarginLayoutParams {
-        /**
-         * Gravity for the view associated with these LayoutParams.
-         *
-         * @see android.view.Gravity
-         */
-        @ViewDebug.ExportedProperty(category = "layout", mapping = {
-                @ViewDebug.IntToString(from =  -1,                       to = "NONE"),
-                @ViewDebug.IntToString(from = Gravity.NO_GRAVITY,        to = "NONE"),
-                @ViewDebug.IntToString(from = Gravity.TOP,               to = "TOP"),
-                @ViewDebug.IntToString(from = Gravity.BOTTOM,            to = "BOTTOM"),
-                @ViewDebug.IntToString(from = Gravity.LEFT,              to = "LEFT"),
-                @ViewDebug.IntToString(from = Gravity.RIGHT,             to = "RIGHT"),
-                @ViewDebug.IntToString(from = Gravity.START,             to = "START"),
-                @ViewDebug.IntToString(from = Gravity.END,               to = "END"),
-                @ViewDebug.IntToString(from = Gravity.CENTER_VERTICAL,   to = "CENTER_VERTICAL"),
-                @ViewDebug.IntToString(from = Gravity.FILL_VERTICAL,     to = "FILL_VERTICAL"),
-                @ViewDebug.IntToString(from = Gravity.CENTER_HORIZONTAL, to = "CENTER_HORIZONTAL"),
-                @ViewDebug.IntToString(from = Gravity.FILL_HORIZONTAL,   to = "FILL_HORIZONTAL"),
-                @ViewDebug.IntToString(from = Gravity.CENTER,            to = "CENTER"),
-                @ViewDebug.IntToString(from = Gravity.FILL,              to = "FILL")
-        })
-        public int gravity = Gravity.NO_GRAVITY;
-
+    public static class LayoutParams extends ActionBar.LayoutParams {
         static final int CUSTOM = 0;
         static final int SYSTEM = 1;
+        static final int EXPANDED = 2;
 
         int mViewType = CUSTOM;
 
         public LayoutParams(@NonNull Context c, AttributeSet attrs) {
             super(c, attrs);
-
-            TypedArray a = c.obtainStyledAttributes(attrs,
-                    com.android.internal.R.styleable.Toolbar_LayoutParams);
-            gravity = a.getInt(
-                    com.android.internal.R.styleable.Toolbar_LayoutParams_layout_gravity,
-                    Gravity.NO_GRAVITY);
-            a.recycle();
         }
 
         public LayoutParams(int width, int height) {
@@ -1160,7 +1536,11 @@
         public LayoutParams(LayoutParams source) {
             super(source);
 
-            this.gravity = source.gravity;
+            mViewType = source.mViewType;
+        }
+
+        public LayoutParams(ActionBar.LayoutParams source) {
+            super(source);
         }
 
         public LayoutParams(MarginLayoutParams source) {
@@ -1199,4 +1579,126 @@
             }
         };
     }
+
+    private class ExpandedActionViewMenuPresenter implements MenuPresenter {
+        MenuBuilder mMenu;
+        MenuItemImpl mCurrentExpandedItem;
+
+        @Override
+        public void initForMenu(Context context, MenuBuilder menu) {
+            // Clear the expanded action view when menus change.
+            if (mMenu != null && mCurrentExpandedItem != null) {
+                mMenu.collapseItemActionView(mCurrentExpandedItem);
+            }
+            mMenu = menu;
+        }
+
+        @Override
+        public MenuView getMenuView(ViewGroup root) {
+            return null;
+        }
+
+        @Override
+        public void updateMenuView(boolean cleared) {
+            // Make sure the expanded item we have is still there.
+            if (mCurrentExpandedItem != null) {
+                boolean found = false;
+
+                if (mMenu != null) {
+                    final int count = mMenu.size();
+                    for (int i = 0; i < count; i++) {
+                        final MenuItem item = mMenu.getItem(i);
+                        if (item == mCurrentExpandedItem) {
+                            found = true;
+                            break;
+                        }
+                    }
+                }
+
+                if (!found) {
+                    // The item we had expanded disappeared. Collapse.
+                    collapseItemActionView(mMenu, mCurrentExpandedItem);
+                }
+            }
+        }
+
+        @Override
+        public void setCallback(Callback cb) {
+        }
+
+        @Override
+        public boolean onSubMenuSelected(SubMenuBuilder subMenu) {
+            return false;
+        }
+
+        @Override
+        public void onCloseMenu(MenuBuilder menu, boolean allMenusAreClosing) {
+        }
+
+        @Override
+        public boolean flagActionItems() {
+            return false;
+        }
+
+        @Override
+        public boolean expandItemActionView(MenuBuilder menu, MenuItemImpl item) {
+            ensureCollapseButtonView();
+            if (mCollapseButtonView.getParent() != Toolbar.this) {
+                addView(mCollapseButtonView);
+            }
+            mExpandedActionView = item.getActionView();
+            mCurrentExpandedItem = item;
+            if (mExpandedActionView.getParent() != Toolbar.this) {
+                final LayoutParams lp = generateDefaultLayoutParams();
+                lp.gravity = Gravity.START | (mButtonGravity & Gravity.VERTICAL_GRAVITY_MASK);
+                lp.mViewType = LayoutParams.EXPANDED;
+                mExpandedActionView.setLayoutParams(lp);
+                addView(mExpandedActionView);
+            }
+
+            setChildVisibilityForExpandedActionView(true);
+            requestLayout();
+            item.setActionViewExpanded(true);
+
+            if (mExpandedActionView instanceof CollapsibleActionView) {
+                ((CollapsibleActionView) mExpandedActionView).onActionViewExpanded();
+            }
+
+            return true;
+        }
+
+        @Override
+        public boolean collapseItemActionView(MenuBuilder menu, MenuItemImpl item) {
+            // Do this before detaching the actionview from the hierarchy, in case
+            // it needs to dismiss the soft keyboard, etc.
+            if (mExpandedActionView instanceof CollapsibleActionView) {
+                ((CollapsibleActionView) mExpandedActionView).onActionViewCollapsed();
+            }
+
+            removeView(mExpandedActionView);
+            removeView(mCollapseButtonView);
+            mExpandedActionView = null;
+
+            setChildVisibilityForExpandedActionView(false);
+            mCurrentExpandedItem = null;
+            requestLayout();
+            item.setActionViewExpanded(false);
+
+            return true;
+        }
+
+        @Override
+        public int getId() {
+            return 0;
+        }
+
+        @Override
+        public Parcelable onSaveInstanceState() {
+            return null;
+        }
+
+        @Override
+        public void onRestoreInstanceState(Parcelable state) {
+        }
+    }
 }
diff --git a/core/java/com/android/internal/app/IMediaContainerService.aidl b/core/java/com/android/internal/app/IMediaContainerService.aidl
index 03d3b22..77f0dec 100644
--- a/core/java/com/android/internal/app/IMediaContainerService.aidl
+++ b/core/java/com/android/internal/app/IMediaContainerService.aidl
@@ -25,16 +25,18 @@
 interface IMediaContainerService {
     String copyResourceToContainer(in Uri packageURI, String containerId, String key,
             String resFileName, String publicResFileName, boolean isExternal,
-            boolean isForwardLocked);
+            boolean isForwardLocked, in String abiOverride);
     int copyResource(in Uri packageURI, in ContainerEncryptionParams encryptionParams,
             in ParcelFileDescriptor outStream);
-    PackageInfoLite getMinimalPackageInfo(in String packagePath, in int flags, in long threshold);
+    PackageInfoLite getMinimalPackageInfo(in String packagePath, in int flags, in long threshold,
+            in String abiOverride);
     boolean checkInternalFreeStorage(in Uri fileUri, boolean isForwardLocked, in long threshold);
-    boolean checkExternalFreeStorage(in Uri fileUri, boolean isForwardLocked);
+    boolean checkExternalFreeStorage(in Uri fileUri, boolean isForwardLocked, in String abiOverride);
     ObbInfo getObbInfo(in String filename);
     long calculateDirectorySize(in String directory);
     /** Return file system stats: [0] is total bytes, [1] is available bytes */
     long[] getFileSystemStats(in String path);
     void clearDirectory(in String directory);
-    long calculateInstalledSize(in String packagePath, boolean isForwardLocked);
+    long calculateInstalledSize(in String packagePath, boolean isForwardLocked,
+            in String abiOverride);
 }
diff --git a/core/java/com/android/internal/app/IVoiceInteractor.aidl b/core/java/com/android/internal/app/IVoiceInteractor.aidl
index 737906a..2900595 100644
--- a/core/java/com/android/internal/app/IVoiceInteractor.aidl
+++ b/core/java/com/android/internal/app/IVoiceInteractor.aidl
@@ -26,7 +26,9 @@
  */
 interface IVoiceInteractor {
     IVoiceInteractorRequest startConfirmation(String callingPackage,
-            IVoiceInteractorCallback callback, String prompt, in Bundle extras);
+            IVoiceInteractorCallback callback, CharSequence prompt, in Bundle extras);
+    IVoiceInteractorRequest startAbortVoice(String callingPackage,
+            IVoiceInteractorCallback callback, CharSequence message, in Bundle extras);
     IVoiceInteractorRequest startCommand(String callingPackage,
             IVoiceInteractorCallback callback, String command, in Bundle extras);
     boolean[] supportsCommands(String callingPackage, in String[] commands);
diff --git a/core/java/com/android/internal/app/IVoiceInteractorCallback.aidl b/core/java/com/android/internal/app/IVoiceInteractorCallback.aidl
index c6f93e1..8dbf9d4 100644
--- a/core/java/com/android/internal/app/IVoiceInteractorCallback.aidl
+++ b/core/java/com/android/internal/app/IVoiceInteractorCallback.aidl
@@ -26,6 +26,7 @@
 oneway interface IVoiceInteractorCallback {
     void deliverConfirmationResult(IVoiceInteractorRequest request, boolean confirmed,
             in Bundle result);
+    void deliverAbortVoiceResult(IVoiceInteractorRequest request, in Bundle result);
     void deliverCommandResult(IVoiceInteractorRequest request, boolean complete, in Bundle result);
     void deliverCancel(IVoiceInteractorRequest request);
 }
diff --git a/core/java/com/android/internal/app/ProcessStats.java b/core/java/com/android/internal/app/ProcessStats.java
index 41f3337..7e11850 100644
--- a/core/java/com/android/internal/app/ProcessStats.java
+++ b/core/java/com/android/internal/app/ProcessStats.java
@@ -1100,7 +1100,7 @@
 
     public boolean evaluateSystemProperties(boolean update) {
         boolean changed = false;
-        String runtime = SystemProperties.get("persist.sys.dalvik.vm.lib.1",
+        String runtime = SystemProperties.get("persist.sys.dalvik.vm.lib.2",
                 VMRuntime.getRuntime().vmLibrary());
         if (!Objects.equals(runtime, mRuntime)) {
             changed = true;
diff --git a/core/java/com/android/internal/app/ToolbarActionBar.java b/core/java/com/android/internal/app/ToolbarActionBar.java
index afb6f7c..6056bf2 100644
--- a/core/java/com/android/internal/app/ToolbarActionBar.java
+++ b/core/java/com/android/internal/app/ToolbarActionBar.java
@@ -23,37 +23,50 @@
 import android.content.res.Configuration;
 import android.graphics.drawable.Drawable;
 import android.view.ActionMode;
+import android.view.KeyEvent;
 import android.view.LayoutInflater;
+import android.view.Menu;
+import android.view.MenuItem;
 import android.view.View;
+import android.view.Window;
 import android.widget.SpinnerAdapter;
 import android.widget.Toolbar;
+import com.android.internal.view.menu.MenuBuilder;
+import com.android.internal.widget.DecorToolbar;
+import com.android.internal.widget.ToolbarWidgetWrapper;
 
 import java.util.ArrayList;
-import java.util.Map;
 
 public class ToolbarActionBar extends ActionBar {
     private Toolbar mToolbar;
-    private View mCustomView;
-
-    private int mDisplayOptions;
-
-    private int mNavResId;
-    private int mIconResId;
-    private int mLogoResId;
-    private Drawable mNavDrawable;
-    private Drawable mIconDrawable;
-    private Drawable mLogoDrawable;
-    private int mTitleResId;
-    private int mSubtitleResId;
-    private CharSequence mTitle;
-    private CharSequence mSubtitle;
+    private DecorToolbar mDecorToolbar;
+    private Window.Callback mWindowCallback;
 
     private boolean mLastMenuVisibility;
     private ArrayList<OnMenuVisibilityListener> mMenuVisibilityListeners =
             new ArrayList<OnMenuVisibilityListener>();
 
-    public ToolbarActionBar(Toolbar toolbar) {
+    private final Runnable mMenuInvalidator = new Runnable() {
+        @Override
+        public void run() {
+            populateOptionsMenu();
+        }
+    };
+
+    private final Toolbar.OnMenuItemClickListener mMenuClicker =
+            new Toolbar.OnMenuItemClickListener() {
+        @Override
+        public boolean onMenuItemClick(MenuItem item) {
+            return mWindowCallback.onMenuItemSelected(Window.FEATURE_OPTIONS_PANEL, item);
+        }
+    };
+
+    public ToolbarActionBar(Toolbar toolbar, CharSequence title, Window.Callback windowCallback) {
         mToolbar = toolbar;
+        mDecorToolbar = new ToolbarWidgetWrapper(toolbar);
+        mWindowCallback = windowCallback;
+        toolbar.setOnMenuItemClickListener(mMenuClicker);
+        mDecorToolbar.setWindowTitle(title);
     }
 
     @Override
@@ -63,19 +76,8 @@
 
     @Override
     public void setCustomView(View view, LayoutParams layoutParams) {
-        if (mCustomView != null) {
-            mToolbar.removeView(mCustomView);
-        }
-        mCustomView = view;
-        if (view != null) {
-            mToolbar.addView(view, generateLayoutParams(layoutParams));
-        }
-    }
-
-    private Toolbar.LayoutParams generateLayoutParams(LayoutParams lp) {
-        final Toolbar.LayoutParams result = new Toolbar.LayoutParams(lp);
-        result.gravity = lp.gravity;
-        return result;
+        view.setLayoutParams(layoutParams);
+        mDecorToolbar.setCustomView(view);
     }
 
     @Override
@@ -86,48 +88,22 @@
 
     @Override
     public void setIcon(int resId) {
-        mIconResId = resId;
-        mIconDrawable = null;
-        updateToolbarLogo();
+        mDecorToolbar.setIcon(resId);
     }
 
     @Override
     public void setIcon(Drawable icon) {
-        mIconResId = 0;
-        mIconDrawable = icon;
-        updateToolbarLogo();
+        mDecorToolbar.setIcon(icon);
     }
 
     @Override
     public void setLogo(int resId) {
-        mLogoResId = resId;
-        mLogoDrawable = null;
-        updateToolbarLogo();
+        mDecorToolbar.setLogo(resId);
     }
 
     @Override
     public void setLogo(Drawable logo) {
-        mLogoResId = 0;
-        mLogoDrawable = logo;
-        updateToolbarLogo();
-    }
-
-    private void updateToolbarLogo() {
-        Drawable drawable = null;
-        if ((mDisplayOptions & ActionBar.DISPLAY_SHOW_HOME) != 0) {
-            final int resId;
-            if ((mDisplayOptions & ActionBar.DISPLAY_USE_LOGO) != 0) {
-                resId = mLogoResId;
-                drawable = mLogoDrawable;
-            } else {
-                resId = mIconResId;
-                drawable = mIconDrawable;
-            }
-            if (resId != 0) {
-                drawable = mToolbar.getContext().getDrawable(resId);
-            }
-        }
-        mToolbar.setLogo(drawable);
+        mDecorToolbar.setLogo(logo);
     }
 
     @Override
@@ -219,42 +195,22 @@
 
     @Override
     public void setTitle(CharSequence title) {
-        mTitle = title;
-        mTitleResId = 0;
-        updateToolbarTitle();
+        mDecorToolbar.setTitle(title);
     }
 
     @Override
     public void setTitle(int resId) {
-        mTitleResId = resId;
-        mTitle = null;
-        updateToolbarTitle();
+        mDecorToolbar.setTitle(resId != 0 ? mDecorToolbar.getContext().getText(resId) : null);
     }
 
     @Override
     public void setSubtitle(CharSequence subtitle) {
-        mSubtitle = subtitle;
-        mSubtitleResId = 0;
-        updateToolbarTitle();
+        mDecorToolbar.setSubtitle(subtitle);
     }
 
     @Override
     public void setSubtitle(int resId) {
-        mSubtitleResId = resId;
-        mSubtitle = null;
-        updateToolbarTitle();
-    }
-
-    private void updateToolbarTitle() {
-        final Context context = mToolbar.getContext();
-        CharSequence title = null;
-        CharSequence subtitle = null;
-        if ((mDisplayOptions & ActionBar.DISPLAY_SHOW_TITLE) != 0) {
-            title = mTitleResId != 0 ? context.getText(mTitleResId) : mTitle;
-            subtitle = mSubtitleResId != 0 ? context.getText(mSubtitleResId) : mSubtitle;
-        }
-        mToolbar.setTitle(title);
-        mToolbar.setSubtitle(subtitle);
+        mDecorToolbar.setSubtitle(resId != 0 ? mDecorToolbar.getContext().getText(resId) : null);
     }
 
     @Override
@@ -264,9 +220,8 @@
 
     @Override
     public void setDisplayOptions(@DisplayOptions int options, @DisplayOptions int mask) {
-        final int oldOptions = mDisplayOptions;
-        mDisplayOptions = (options & mask) | (mDisplayOptions & ~mask);
-        final int optionsChanged = oldOptions ^ mDisplayOptions;
+        mDecorToolbar.setDisplayOptions((options & mask) |
+                mDecorToolbar.getDisplayOptions() & ~mask);
     }
 
     @Override
@@ -301,7 +256,7 @@
 
     @Override
     public View getCustomView() {
-        return mCustomView;
+        return mDecorToolbar.getCustomView();
     }
 
     @Override
@@ -327,7 +282,7 @@
 
     @Override
     public int getDisplayOptions() {
-        return mDisplayOptions;
+        return mDecorToolbar.getDisplayOptions();
     }
 
     @Override
@@ -425,6 +380,54 @@
         return mToolbar.getVisibility() == View.VISIBLE;
     }
 
+    @Override
+    public boolean openOptionsMenu() {
+        return mToolbar.showOverflowMenu();
+    }
+
+    @Override
+    public boolean invalidateOptionsMenu() {
+        mToolbar.removeCallbacks(mMenuInvalidator);
+        mToolbar.postOnAnimation(mMenuInvalidator);
+        return true;
+    }
+
+    @Override
+    public boolean collapseActionView() {
+        if (mToolbar.hasExpandedActionView()) {
+            mToolbar.collapseActionView();
+            return true;
+        }
+        return false;
+    }
+
+    void populateOptionsMenu() {
+        final Menu menu = mToolbar.getMenu();
+        final MenuBuilder mb = menu instanceof MenuBuilder ? (MenuBuilder) menu : null;
+        if (mb != null) {
+            mb.stopDispatchingItemsChanged();
+        }
+        try {
+            menu.clear();
+            if (!mWindowCallback.onCreatePanelMenu(Window.FEATURE_OPTIONS_PANEL, menu) ||
+                    !mWindowCallback.onPreparePanel(Window.FEATURE_OPTIONS_PANEL, null, menu)) {
+                menu.clear();
+            }
+        } finally {
+            if (mb != null) {
+                mb.startDispatchingItemsChanged();
+            }
+        }
+    }
+
+    @Override
+    public boolean onMenuKeyEvent(KeyEvent event) {
+        if (event.getAction() == KeyEvent.ACTION_UP) {
+            openOptionsMenu();
+        }
+        return true;
+    }
+
     public void addOnMenuVisibilityListener(OnMenuVisibilityListener listener) {
         mMenuVisibilityListeners.add(listener);
     }
diff --git a/core/java/com/android/internal/app/WindowDecorActionBar.java b/core/java/com/android/internal/app/WindowDecorActionBar.java
index a238ae3..c0b5b97 100644
--- a/core/java/com/android/internal/app/WindowDecorActionBar.java
+++ b/core/java/com/android/internal/app/WindowDecorActionBar.java
@@ -18,7 +18,10 @@
 
 import android.animation.ValueAnimator;
 import android.content.res.TypedArray;
+import android.view.ViewGroup;
 import android.view.ViewParent;
+import android.widget.AdapterView;
+import android.widget.Toolbar;
 import com.android.internal.R;
 import com.android.internal.view.ActionBarPolicy;
 import com.android.internal.view.menu.MenuBuilder;
@@ -28,6 +31,7 @@
 import com.android.internal.widget.ActionBarContextView;
 import com.android.internal.widget.ActionBarOverlayLayout;
 import com.android.internal.widget.ActionBarView;
+import com.android.internal.widget.DecorToolbar;
 import com.android.internal.widget.ScrollingTabContainerView;
 
 import android.animation.Animator;
@@ -55,6 +59,7 @@
 import android.view.accessibility.AccessibilityEvent;
 import android.view.animation.AnimationUtils;
 import android.widget.SpinnerAdapter;
+import com.android.internal.widget.ToolbarWidgetWrapper;
 
 import java.lang.ref.WeakReference;
 import java.util.ArrayList;
@@ -77,7 +82,7 @@
 
     private ActionBarOverlayLayout mOverlayLayout;
     private ActionBarContainer mContainerView;
-    private ActionBarView mActionView;
+    private DecorToolbar mDecorToolbar;
     private ActionBarContextView mContextView;
     private ActionBarContainer mSplitView;
     private View mContentView;
@@ -187,7 +192,7 @@
         if (mOverlayLayout != null) {
             mOverlayLayout.setActionBarVisibilityCallback(this);
         }
-        mActionView = (ActionBarView) decor.findViewById(com.android.internal.R.id.action_bar);
+        mDecorToolbar = getDecorToolbar(decor.findViewById(com.android.internal.R.id.action_bar));
         mContextView = (ActionBarContextView) decor.findViewById(
                 com.android.internal.R.id.action_context_bar);
         mContainerView = (ActionBarContainer) decor.findViewById(
@@ -195,18 +200,17 @@
         mSplitView = (ActionBarContainer) decor.findViewById(
                 com.android.internal.R.id.split_action_bar);
 
-        if (mActionView == null || mContextView == null || mContainerView == null) {
+        if (mDecorToolbar == null || mContextView == null || mContainerView == null) {
             throw new IllegalStateException(getClass().getSimpleName() + " can only be used " +
                     "with a compatible window decor layout");
         }
 
-        mContext = mActionView.getContext();
-        mActionView.setContextView(mContextView);
-        mContextDisplayMode = mActionView.isSplitActionBar() ?
+        mContext = mDecorToolbar.getContext();
+        mContextDisplayMode = mDecorToolbar.isSplit() ?
                 CONTEXT_DISPLAY_SPLIT : CONTEXT_DISPLAY_NORMAL;
 
         // This was initially read from the action bar style
-        final int current = mActionView.getDisplayOptions();
+        final int current = mDecorToolbar.getDisplayOptions();
         final boolean homeAsUp = (current & DISPLAY_HOME_AS_UP) != 0;
         if (homeAsUp) {
             mDisplayHomeAsUpSet = true;
@@ -225,6 +229,17 @@
         a.recycle();
     }
 
+    private DecorToolbar getDecorToolbar(View view) {
+        if (view instanceof DecorToolbar) {
+            return (DecorToolbar) view;
+        } else if (view instanceof Toolbar) {
+            return ((Toolbar) view).getWrapper();
+        } else {
+            throw new IllegalStateException("Can't make a decor toolbar out of " +
+                    view.getClass().getSimpleName());
+        }
+    }
+
     public void onConfigurationChanged(Configuration newConfig) {
         setHasEmbeddedTabs(ActionBarPolicy.get(mContext).hasEmbeddedTabs());
     }
@@ -233,11 +248,11 @@
         mHasEmbeddedTabs = hasEmbeddedTabs;
         // Switch tab layout configuration if needed
         if (!mHasEmbeddedTabs) {
-            mActionView.setEmbeddedTabView(null);
+            mDecorToolbar.setEmbeddedTabView(null);
             mContainerView.setTabContainer(mTabScrollView);
         } else {
             mContainerView.setTabContainer(null);
-            mActionView.setEmbeddedTabView(mTabScrollView);
+            mDecorToolbar.setEmbeddedTabView(mTabScrollView);
         }
         final boolean isInTabMode = getNavigationMode() == NAVIGATION_MODE_TABS;
         if (mTabScrollView != null) {
@@ -250,7 +265,7 @@
                 mTabScrollView.setVisibility(View.GONE);
             }
         }
-        mActionView.setCollapsable(!mHasEmbeddedTabs && isInTabMode);
+        mDecorToolbar.setCollapsible(!mHasEmbeddedTabs && isInTabMode);
         mOverlayLayout.setHasNonEmbeddedTabs(!mHasEmbeddedTabs && isInTabMode);
     }
 
@@ -263,7 +278,7 @@
 
         if (mHasEmbeddedTabs) {
             tabScroller.setVisibility(View.VISIBLE);
-            mActionView.setEmbeddedTabView(tabScroller);
+            mDecorToolbar.setEmbeddedTabView(tabScroller);
         } else {
             if (getNavigationMode() == NAVIGATION_MODE_TABS) {
                 tabScroller.setVisibility(View.VISIBLE);
@@ -326,7 +341,8 @@
 
     @Override
     public void setCustomView(int resId) {
-        setCustomView(LayoutInflater.from(getThemedContext()).inflate(resId, mActionView, false));
+        setCustomView(LayoutInflater.from(getThemedContext()).inflate(resId,
+                mDecorToolbar.getViewGroup(), false));
     }
 
     @Override
@@ -356,7 +372,7 @@
 
     @Override
     public void setHomeButtonEnabled(boolean enable) {
-        mActionView.setHomeButtonEnabled(enable);
+        mDecorToolbar.setHomeButtonEnabled(enable);
     }
 
     @Override
@@ -370,12 +386,12 @@
     }
 
     public void setSelectedNavigationItem(int position) {
-        switch (mActionView.getNavigationMode()) {
+        switch (mDecorToolbar.getNavigationMode()) {
         case NAVIGATION_MODE_TABS:
             selectTab(mTabs.get(position));
             break;
         case NAVIGATION_MODE_LIST:
-            mActionView.setDropdownSelectedPosition(position);
+            mDecorToolbar.setDropdownSelectedPosition(position);
             break;
         default:
             throw new IllegalStateException(
@@ -399,26 +415,26 @@
     }
 
     public void setTitle(CharSequence title) {
-        mActionView.setTitle(title);
+        mDecorToolbar.setTitle(title);
     }
 
     public void setSubtitle(CharSequence subtitle) {
-        mActionView.setSubtitle(subtitle);
+        mDecorToolbar.setSubtitle(subtitle);
     }
 
     public void setDisplayOptions(int options) {
         if ((options & DISPLAY_HOME_AS_UP) != 0) {
             mDisplayHomeAsUpSet = true;
         }
-        mActionView.setDisplayOptions(options);
+        mDecorToolbar.setDisplayOptions(options);
     }
 
     public void setDisplayOptions(int options, int mask) {
-        final int current = mActionView.getDisplayOptions(); 
+        final int current = mDecorToolbar.getDisplayOptions();
         if ((mask & DISPLAY_HOME_AS_UP) != 0) {
             mDisplayHomeAsUpSet = true;
         }
-        mActionView.setDisplayOptions((options & mask) | (current & ~mask));
+        mDecorToolbar.setDisplayOptions((options & mask) | (current & ~mask));
     }
 
     public void setBackgroundDrawable(Drawable d) {
@@ -436,23 +452,23 @@
     }
 
     public View getCustomView() {
-        return mActionView.getCustomNavigationView();
+        return mDecorToolbar.getCustomView();
     }
 
     public CharSequence getTitle() {
-        return mActionView.getTitle();
+        return mDecorToolbar.getTitle();
     }
 
     public CharSequence getSubtitle() {
-        return mActionView.getSubtitle();
+        return mDecorToolbar.getSubtitle();
     }
 
     public int getNavigationMode() {
-        return mActionView.getNavigationMode();
+        return mDecorToolbar.getNavigationMode();
     }
 
     public int getDisplayOptions() {
-        return mActionView.getDisplayOptions();
+        return mDecorToolbar.getDisplayOptions();
     }
 
     public ActionMode startActionMode(ActionMode.Callback callback) {
@@ -572,7 +588,7 @@
             return;
         }
 
-        final FragmentTransaction trans = mActionView.isInEditMode() ? null :
+        final FragmentTransaction trans = mDecorToolbar.getViewGroup().isInEditMode() ? null :
                 mActivity.getFragmentManager().beginTransaction().disallowAddToBackStack();
 
         if (mSelectedTab == tab) {
@@ -828,13 +844,18 @@
             hideForActionMode();
         }
 
-        mActionView.animateToVisibility(toActionMode ? View.GONE : View.VISIBLE);
+        mDecorToolbar.animateToVisibility(toActionMode ? View.GONE : View.VISIBLE);
         mContextView.animateToVisibility(toActionMode ? View.VISIBLE : View.GONE);
-        if (mTabScrollView != null && !mActionView.hasEmbeddedTabs() && mActionView.isCollapsed()) {
+        if (mTabScrollView != null && !mDecorToolbar.hasEmbeddedTabs() &&
+                isCollapsed(mDecorToolbar.getViewGroup())) {
             mTabScrollView.animateToVisibility(toActionMode ? View.GONE : View.VISIBLE);
         }
     }
 
+    private boolean isCollapsed(View view) {
+        return view == null || view.getVisibility() == View.GONE || view.getMeasuredHeight() == 0;
+    }
+
     public Context getThemedContext() {
         if (mThemedContext == null) {
             TypedValue outValue = new TypedValue();
@@ -854,27 +875,27 @@
     
     @Override
     public boolean isTitleTruncated() {
-        return mActionView != null && mActionView.isTitleTruncated();
+        return mDecorToolbar != null && mDecorToolbar.isTitleTruncated();
     }
 
     @Override
     public void setHomeAsUpIndicator(Drawable indicator) {
-        mActionView.setHomeAsUpIndicator(indicator);
+        mDecorToolbar.setNavigationIcon(indicator);
     }
 
     @Override
     public void setHomeAsUpIndicator(int resId) {
-        mActionView.setHomeAsUpIndicator(resId);
+        mDecorToolbar.setNavigationIcon(resId);
     }
 
     @Override
     public void setHomeActionContentDescription(CharSequence description) {
-        mActionView.setHomeActionContentDescription(description);
+        mDecorToolbar.setNavigationContentDescription(description);
     }
 
     @Override
     public void setHomeActionContentDescription(int resId) {
-        mActionView.setHomeActionContentDescription(resId);
+        mDecorToolbar.setNavigationContentDescription(resId);
     }
 
     @Override
@@ -938,7 +959,8 @@
 
             // Clear out the context mode views after the animation finishes
             mContextView.closeMode();
-            mActionView.sendAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED);
+            mDecorToolbar.getViewGroup().sendAccessibilityEvent(
+                    AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED);
             mOverlayLayout.setHideOnContentScrollEnabled(mHideOnContentScroll);
 
             mActionMode = null;
@@ -1178,28 +1200,27 @@
 
     @Override
     public void setCustomView(View view) {
-        mActionView.setCustomNavigationView(view);
+        mDecorToolbar.setCustomView(view);
     }
 
     @Override
     public void setCustomView(View view, LayoutParams layoutParams) {
         view.setLayoutParams(layoutParams);
-        mActionView.setCustomNavigationView(view);
+        mDecorToolbar.setCustomView(view);
     }
 
     @Override
     public void setListNavigationCallbacks(SpinnerAdapter adapter, OnNavigationListener callback) {
-        mActionView.setDropdownAdapter(adapter);
-        mActionView.setCallback(callback);
+        mDecorToolbar.setDropdownParams(adapter, new NavItemSelectedListener(callback));
     }
 
     @Override
     public int getSelectedNavigationIndex() {
-        switch (mActionView.getNavigationMode()) {
+        switch (mDecorToolbar.getNavigationMode()) {
             case NAVIGATION_MODE_TABS:
                 return mSelectedTab != null ? mSelectedTab.getPosition() : -1;
             case NAVIGATION_MODE_LIST:
-                return mActionView.getDropdownSelectedPosition();
+                return mDecorToolbar.getDropdownSelectedPosition();
             default:
                 return -1;
         }
@@ -1207,12 +1228,11 @@
 
     @Override
     public int getNavigationItemCount() {
-        switch (mActionView.getNavigationMode()) {
+        switch (mDecorToolbar.getNavigationMode()) {
             case NAVIGATION_MODE_TABS:
                 return mTabs.size();
             case NAVIGATION_MODE_LIST:
-                SpinnerAdapter adapter = mActionView.getDropdownAdapter();
-                return adapter != null ? adapter.getCount() : 0;
+                return mDecorToolbar.getDropdownItemCount();
             default:
                 return 0;
         }
@@ -1225,7 +1245,7 @@
 
     @Override
     public void setNavigationMode(int mode) {
-        final int oldMode = mActionView.getNavigationMode();
+        final int oldMode = mDecorToolbar.getNavigationMode();
         switch (oldMode) {
             case NAVIGATION_MODE_TABS:
                 mSavedTabPosition = getSelectedNavigationIndex();
@@ -1238,7 +1258,7 @@
                 mOverlayLayout.requestFitSystemWindows();
             }
         }
-        mActionView.setNavigationMode(mode);
+        mDecorToolbar.setNavigationMode(mode);
         switch (mode) {
             case NAVIGATION_MODE_TABS:
                 ensureTabsExist();
@@ -1249,7 +1269,7 @@
                 }
                 break;
         }
-        mActionView.setCollapsable(mode == NAVIGATION_MODE_TABS && !mHasEmbeddedTabs);
+        mDecorToolbar.setCollapsible(mode == NAVIGATION_MODE_TABS && !mHasEmbeddedTabs);
         mOverlayLayout.setHasNonEmbeddedTabs(mode == NAVIGATION_MODE_TABS && !mHasEmbeddedTabs);
     }
 
@@ -1261,30 +1281,30 @@
 
     @Override
     public void setIcon(int resId) {
-        mActionView.setIcon(resId);
+        mDecorToolbar.setIcon(resId);
     }
 
     @Override
     public void setIcon(Drawable icon) {
-        mActionView.setIcon(icon);
+        mDecorToolbar.setIcon(icon);
     }
 
     public boolean hasIcon() {
-        return mActionView.hasIcon();
+        return mDecorToolbar.hasIcon();
     }
 
     @Override
     public void setLogo(int resId) {
-        mActionView.setLogo(resId);
+        mDecorToolbar.setLogo(resId);
     }
 
     @Override
     public void setLogo(Drawable logo) {
-        mActionView.setLogo(logo);
+        mDecorToolbar.setLogo(logo);
     }
 
     public boolean hasLogo() {
-        return mActionView.hasLogo();
+        return mDecorToolbar.hasLogo();
     }
 
     public void setDefaultDisplayHomeAsUpEnabled(boolean enable) {
@@ -1292,4 +1312,24 @@
             setDisplayHomeAsUpEnabled(enable);
         }
     }
+
+    static class NavItemSelectedListener implements AdapterView.OnItemSelectedListener {
+        private final OnNavigationListener mListener;
+
+        public NavItemSelectedListener(OnNavigationListener listener) {
+            mListener = listener;
+        }
+
+        @Override
+        public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
+            if (mListener != null) {
+                mListener.onNavigationItemSelected(position, id);
+            }
+        }
+
+        @Override
+        public void onNothingSelected(AdapterView<?> parent) {
+            // Do nothing
+        }
+    }
 }
diff --git a/core/java/com/android/internal/backup/BackupConstants.java b/core/java/com/android/internal/backup/BackupConstants.java
deleted file mode 100644
index 4c276b7..0000000
--- a/core/java/com/android/internal/backup/BackupConstants.java
+++ /dev/null
@@ -1,28 +0,0 @@
-/*
- * Copyright (C) 2009 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.backup;
-
-/**
- * Constants used internally between the backup manager and its transports
- */
-public class BackupConstants {
-    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 AGENT_ERROR = 3;
-    public static final int AGENT_UNKNOWN = 4;
-}
diff --git a/core/java/com/android/internal/backup/IBackupTransport.aidl b/core/java/com/android/internal/backup/IBackupTransport.aidl
index 1e37fd9..d10451b 100644
--- a/core/java/com/android/internal/backup/IBackupTransport.aidl
+++ b/core/java/com/android/internal/backup/IBackupTransport.aidl
@@ -178,7 +178,7 @@
     /**
      * Get the data for the application returned by {@link #nextRestorePackage}.
      * @param data An open, writable file into which the backup data should be stored.
-     * @return the same error codes as {@link #nextRestorePackage}.
+     * @return the same error codes as {@link #startRestore}.
      */
     int getRestoreData(in ParcelFileDescriptor outFd);
 
diff --git a/core/java/com/android/internal/backup/LocalTransport.java b/core/java/com/android/internal/backup/LocalTransport.java
index 446ef55..7292116 100644
--- a/core/java/com/android/internal/backup/LocalTransport.java
+++ b/core/java/com/android/internal/backup/LocalTransport.java
@@ -18,6 +18,7 @@
 
 import android.app.backup.BackupDataInput;
 import android.app.backup.BackupDataOutput;
+import android.app.backup.BackupTransport;
 import android.app.backup.RestoreSet;
 import android.content.ComponentName;
 import android.content.Context;
@@ -47,7 +48,7 @@
  * later restoring from there.  For testing only.
  */
 
-public class LocalTransport extends IBackupTransport.Stub {
+public class LocalTransport extends BackupTransport {
     private static final String TAG = "LocalTransport";
     private static final boolean DEBUG = true;
 
@@ -103,7 +104,7 @@
     public int initializeDevice() {
         if (DEBUG) Log.v(TAG, "wiping all data");
         deleteContents(mCurrentSetDir);
-        return BackupConstants.TRANSPORT_OK;
+        return BackupTransport.TRANSPORT_OK;
     }
 
     public int performBackup(PackageInfo packageInfo, ParcelFileDescriptor data) {
@@ -165,7 +166,7 @@
                         entity.write(buf, 0, dataSize);
                     } catch (IOException e) {
                         Log.e(TAG, "Unable to update key file " + entityFile.getAbsolutePath());
-                        return BackupConstants.TRANSPORT_ERROR;
+                        return BackupTransport.TRANSPORT_ERROR;
                     } finally {
                         entity.close();
                     }
@@ -173,11 +174,11 @@
                     entityFile.delete();
                 }
             }
-            return BackupConstants.TRANSPORT_OK;
+            return BackupTransport.TRANSPORT_OK;
         } catch (IOException e) {
             // oops, something went wrong.  abort the operation and return error.
             Log.v(TAG, "Exception reading backup input:", e);
-            return BackupConstants.TRANSPORT_ERROR;
+            return BackupTransport.TRANSPORT_ERROR;
         }
     }
 
@@ -207,17 +208,17 @@
             }
             packageDir.delete();
         }
-        return BackupConstants.TRANSPORT_OK;
+        return BackupTransport.TRANSPORT_OK;
     }
 
     public int finishBackup() {
         if (DEBUG) Log.v(TAG, "finishBackup()");
-        return BackupConstants.TRANSPORT_OK;
+        return BackupTransport.TRANSPORT_OK;
     }
 
     // Restore handling
     static final long[] POSSIBLE_SETS = { 2, 3, 4, 5, 6, 7, 8, 9 }; 
-    public RestoreSet[] getAvailableRestoreSets() throws android.os.RemoteException {
+    public RestoreSet[] getAvailableRestoreSets() {
         long[] existing = new long[POSSIBLE_SETS.length + 1];
         int num = 0;
 
@@ -248,7 +249,7 @@
         mRestorePackage = -1;
         mRestoreToken = token;
         mRestoreDataDir = new File(mDataDir, Long.toString(token));
-        return BackupConstants.TRANSPORT_OK;
+        return BackupTransport.TRANSPORT_OK;
     }
 
     public String nextRestorePackage() {
@@ -280,7 +281,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 BackupConstants.TRANSPORT_ERROR;
+            return BackupTransport.TRANSPORT_ERROR;
         }
 
         // We expect at least some data if the directory exists in the first place
@@ -301,10 +302,10 @@
                     in.close();
                 }
             }
-            return BackupConstants.TRANSPORT_OK;
+            return BackupTransport.TRANSPORT_OK;
         } catch (IOException e) {
             Log.e(TAG, "Unable to read backup records", e);
-            return BackupConstants.TRANSPORT_ERROR;
+            return BackupTransport.TRANSPORT_ERROR;
         }
     }
 
diff --git a/core/java/com/android/internal/backup/LocalTransportService.java b/core/java/com/android/internal/backup/LocalTransportService.java
index d05699a..77ac313 100644
--- a/core/java/com/android/internal/backup/LocalTransportService.java
+++ b/core/java/com/android/internal/backup/LocalTransportService.java
@@ -32,6 +32,6 @@
 
     @Override
     public IBinder onBind(Intent intent) {
-        return sTransport;
+        return sTransport.getBinder();
     }
 }
diff --git a/core/java/com/android/internal/content/NativeLibraryHelper.java b/core/java/com/android/internal/content/NativeLibraryHelper.java
index ba419f9..dab3aff 100644
--- a/core/java/com/android/internal/content/NativeLibraryHelper.java
+++ b/core/java/com/android/internal/content/NativeLibraryHelper.java
@@ -20,6 +20,7 @@
 import android.util.Slog;
 
 import java.io.File;
+import java.io.IOException;
 
 /**
  * Native libraries helper.
@@ -141,4 +142,18 @@
 
         return deletedFiles;
     }
+
+    // We don't care about the other return values for now.
+    private static final int BITCODE_PRESENT = 1;
+
+    public static boolean hasRenderscriptBitcode(ApkHandle handle) throws IOException {
+        final int returnVal = hasRenderscriptBitcode(handle.apkHandle);
+        if (returnVal < 0) {
+            throw new IOException("Error scanning APK, code: " + returnVal);
+        }
+
+        return (returnVal == BITCODE_PRESENT);
+    }
+
+    private static native int hasRenderscriptBitcode(long apkHandle);
 }
diff --git a/core/java/com/android/internal/inputmethod/InputMethodSubtypeSwitchingController.java b/core/java/com/android/internal/inputmethod/InputMethodSubtypeSwitchingController.java
index bfa5c22..7dbde69 100644
--- a/core/java/com/android/internal/inputmethod/InputMethodSubtypeSwitchingController.java
+++ b/core/java/com/android/internal/inputmethod/InputMethodSubtypeSwitchingController.java
@@ -34,6 +34,7 @@
 import java.util.HashSet;
 import java.util.List;
 import java.util.Locale;
+import java.util.Objects;
 import java.util.TreeMap;
 
 /**
@@ -117,6 +118,24 @@
                     + " mIsSystemLanguage=" + mIsSystemLanguage
                     + "}";
         }
+
+        @Override
+        public boolean equals(Object o) {
+            if (o == this) {
+                return true;
+            }
+            if (o instanceof ImeSubtypeListItem) {
+                final ImeSubtypeListItem that = (ImeSubtypeListItem)o;
+                if (!Objects.equals(this.mImi, that.mImi)) {
+                    return false;
+                }
+                if (this.mSubtypeId != that.mSubtypeId) {
+                    return false;
+                }
+                return true;
+            }
+            return false;
+        }
     }
 
     private static class InputMethodAndSubtypeList {
@@ -276,7 +295,7 @@
         private final List<ImeSubtypeListItem> mImeSubtypeList;
         private final int[] mUsageHistoryOfSubtypeListItemIndex;
 
-        public DynamicRotationList(final List<ImeSubtypeListItem> imeSubtypeListItems) {
+        private DynamicRotationList(final List<ImeSubtypeListItem> imeSubtypeListItems) {
             mImeSubtypeList = imeSubtypeListItems;
             mUsageHistoryOfSubtypeListItemIndex = new int[mImeSubtypeList.size()];
             final int N = mImeSubtypeList.size();
@@ -347,14 +366,53 @@
 
     @VisibleForTesting
     public static class ControllerImpl {
-        private final DynamicRotationList mSwitchingAwareSubtypeList;
-        private final StaticRotationList mSwitchingUnawareSubtypeList;
+        private final DynamicRotationList mSwitchingAwareRotationList;
+        private final StaticRotationList mSwitchingUnawareRotationList;
 
-        public ControllerImpl(final List<ImeSubtypeListItem> sortedItems) {
-            mSwitchingAwareSubtypeList = new DynamicRotationList(filterImeSubtypeList(sortedItems,
-                    true /* supportsSwitchingToNextInputMethod */));
-            mSwitchingUnawareSubtypeList = new StaticRotationList(filterImeSubtypeList(sortedItems,
-                    false /* supportsSwitchingToNextInputMethod */));
+        public static ControllerImpl createFrom(final ControllerImpl currentInstance,
+                final List<ImeSubtypeListItem> sortedEnabledItems) {
+            DynamicRotationList switchingAwareRotationList = null;
+            {
+                final List<ImeSubtypeListItem> switchingAwareImeSubtypes =
+                        filterImeSubtypeList(sortedEnabledItems,
+                                true /* supportsSwitchingToNextInputMethod */);
+                if (currentInstance != null &&
+                        currentInstance.mSwitchingAwareRotationList != null &&
+                        Objects.equals(currentInstance.mSwitchingAwareRotationList.mImeSubtypeList,
+                                switchingAwareImeSubtypes)) {
+                    // Can reuse the current instance.
+                    switchingAwareRotationList = currentInstance.mSwitchingAwareRotationList;
+                }
+                if (switchingAwareRotationList == null) {
+                    switchingAwareRotationList = new DynamicRotationList(switchingAwareImeSubtypes);
+                }
+            }
+
+            StaticRotationList switchingUnawareRotationList = null;
+            {
+                final List<ImeSubtypeListItem> switchingUnawareImeSubtypes = filterImeSubtypeList(
+                        sortedEnabledItems, false /* supportsSwitchingToNextInputMethod */);
+                if (currentInstance != null &&
+                        currentInstance.mSwitchingUnawareRotationList != null &&
+                        Objects.equals(
+                                currentInstance.mSwitchingUnawareRotationList.mImeSubtypeList,
+                                switchingUnawareImeSubtypes)) {
+                    // Can reuse the current instance.
+                    switchingUnawareRotationList = currentInstance.mSwitchingUnawareRotationList;
+                }
+                if (switchingUnawareRotationList == null) {
+                    switchingUnawareRotationList =
+                            new StaticRotationList(switchingUnawareImeSubtypes);
+                }
+            }
+
+            return new ControllerImpl(switchingAwareRotationList, switchingUnawareRotationList);
+        }
+
+        private ControllerImpl(final DynamicRotationList switchingAwareRotationList,
+                final StaticRotationList switchingUnawareRotationList) {
+            mSwitchingAwareRotationList = switchingAwareRotationList;
+            mSwitchingUnawareRotationList = switchingUnawareRotationList;
         }
 
         public ImeSubtypeListItem getNextInputMethod(boolean onlyCurrentIme, InputMethodInfo imi,
@@ -363,10 +421,10 @@
                 return null;
             }
             if (imi.supportsSwitchingToNextInputMethod()) {
-                return mSwitchingAwareSubtypeList.getNextInputMethodLocked(onlyCurrentIme, imi,
+                return mSwitchingAwareRotationList.getNextInputMethodLocked(onlyCurrentIme, imi,
                         subtype);
             } else {
-                return mSwitchingUnawareSubtypeList.getNextInputMethodLocked(onlyCurrentIme, imi,
+                return mSwitchingUnawareRotationList.getNextInputMethodLocked(onlyCurrentIme, imi,
                         subtype);
             }
         }
@@ -376,7 +434,7 @@
                 return;
             }
             if (imi.supportsSwitchingToNextInputMethod()) {
-                mSwitchingAwareSubtypeList.onUserAction(imi, subtype);
+                mSwitchingAwareRotationList.onUserAction(imi, subtype);
             }
         }
 
@@ -422,7 +480,8 @@
 
     public void resetCircularListLocked(Context context) {
         mSubtypeList = new InputMethodAndSubtypeList(context, mSettings);
-        mController = new ControllerImpl(mSubtypeList.getSortedInputMethodAndSubtypeList());
+        mController = ControllerImpl.createFrom(mController,
+                mSubtypeList.getSortedInputMethodAndSubtypeList());
     }
 
     public ImeSubtypeListItem getNextInputMethodLocked(boolean onlyCurrentIme, InputMethodInfo imi,
diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java
index ed9f9bc..240d520 100644
--- a/core/java/com/android/internal/os/BatteryStatsImpl.java
+++ b/core/java/com/android/internal/os/BatteryStatsImpl.java
@@ -2006,6 +2006,11 @@
         }
     }
 
+    @Override
+    public void commitCurrentHistoryBatchLocked() {
+        mHistoryLastWritten.cmd = HistoryItem.CMD_NULL;
+    }
+
     void addHistoryBufferLocked(long elapsedRealtimeMs, long uptimeMs, HistoryItem cur) {
         if (!mHaveBatteryLevel || !mRecordingHistory) {
             return;
@@ -2342,13 +2347,16 @@
             // Only care about partial wake locks, since full wake locks
             // will be canceled when the user puts the screen to sleep.
             aggregateLastWakeupUptimeLocked(uptime);
+            if (historyName == null) {
+                historyName = name;
+            }
             if (mRecordAllWakeLocks) {
-                if (mActiveEvents.updateState(HistoryItem.EVENT_WAKE_LOCK_START, name, uid, 0)) {
+                if (mActiveEvents.updateState(HistoryItem.EVENT_WAKE_LOCK_START, historyName,
+                        uid, 0)) {
                     addHistoryEventLocked(elapsedRealtime, uptime,
-                            HistoryItem.EVENT_WAKE_LOCK_START, name, uid);
+                            HistoryItem.EVENT_WAKE_LOCK_START, historyName, uid);
                 }
             }
-            historyName = historyName == null ? name : historyName;
             if (mWakeLockNesting == 0) {
                 mHistoryCur.states |= HistoryItem.STATE_WAKE_LOCK_FLAG;
                 if (DEBUG_HISTORY) Slog.v(TAG, "Start wake lock to: "
@@ -2358,7 +2366,8 @@
                 mHistoryCur.wakelockTag.uid = mInitialAcquireWakeUid = uid;
                 mWakeLockImportant = !unimportantForLogging;
                 addHistoryRecordLocked(elapsedRealtime, uptime);
-            } else if (!mWakeLockImportant && !unimportantForLogging) {
+            } else if (!mWakeLockImportant && !unimportantForLogging
+                    && mHistoryLastWritten.cmd == HistoryItem.CMD_UPDATE) {
                 if (mHistoryLastWritten.wakelockTag != null) {
                     // We'll try to update the last tag.
                     mHistoryLastWritten.wakelockTag = null;
@@ -2386,9 +2395,13 @@
         if (type == WAKE_TYPE_PARTIAL) {
             mWakeLockNesting--;
             if (mRecordAllWakeLocks) {
-                if (mActiveEvents.updateState(HistoryItem.EVENT_WAKE_LOCK_FINISH, name, uid, 0)) {
+                if (historyName == null) {
+                    historyName = name;
+                }
+                if (mActiveEvents.updateState(HistoryItem.EVENT_WAKE_LOCK_FINISH, historyName,
+                        uid, 0)) {
                     addHistoryEventLocked(elapsedRealtime, uptime,
-                            HistoryItem.EVENT_WAKE_LOCK_FINISH, name, uid);
+                            HistoryItem.EVENT_WAKE_LOCK_FINISH, historyName, uid);
                 }
             }
             if (mWakeLockNesting == 0) {
diff --git a/core/java/com/android/internal/os/HandlerCaller.java b/core/java/com/android/internal/os/HandlerCaller.java
index 40834ba..17685fd 100644
--- a/core/java/com/android/internal/os/HandlerCaller.java
+++ b/core/java/com/android/internal/os/HandlerCaller.java
@@ -22,9 +22,6 @@
 import android.os.Message;
 
 public class HandlerCaller {
-
-    public final Context mContext;
-
     final Looper mMainLooper;
     final Handler mH;
 
@@ -47,7 +44,6 @@
 
     public HandlerCaller(Context context, Looper looper, Callback callback,
             boolean asyncHandler) {
-        mContext = context;
         mMainLooper = looper != null ? looper : context.getMainLooper();
         mH = new MyHandler(mMainLooper, asyncHandler);
         mCallback = callback;
diff --git a/core/java/com/android/internal/policy/IKeyguardService.aidl b/core/java/com/android/internal/policy/IKeyguardService.aidl
index b78c70f..a5421f5 100644
--- a/core/java/com/android/internal/policy/IKeyguardService.aidl
+++ b/core/java/com/android/internal/policy/IKeyguardService.aidl
@@ -56,4 +56,13 @@
     oneway void dispatch(in MotionEvent event);
     oneway void launchCamera();
     oneway void onBootCompleted();
+
+    /**
+     * Notifies that the activity behind has now been drawn and it's safe to remove the wallpaper
+     * and keyguard flag.
+     *
+     * @param startTime the start time of the animation in uptime milliseconds
+     * @param fadeoutDuration the duration of the exit animation, in milliseconds
+     */
+    oneway void startKeyguardExitAnimation(long startTime, long fadeoutDuration);
 }
diff --git a/core/java/com/android/internal/util/ArrayUtils.java b/core/java/com/android/internal/util/ArrayUtils.java
index a56fa36..d66ef83 100644
--- a/core/java/com/android/internal/util/ArrayUtils.java
+++ b/core/java/com/android/internal/util/ArrayUtils.java
@@ -169,6 +169,15 @@
         return false;
     }
 
+    public static boolean contains(long[] array, long value) {
+        for (long element : array) {
+            if (element == value) {
+                return true;
+            }
+        }
+        return false;
+    }
+
     public static long total(long[] array) {
         long total = 0;
         for (long value : array) {
@@ -229,6 +238,14 @@
         return array;
     }
 
+    /**
+     * Appends a new value to a copy of the array and returns the copy.  If
+     * the value is already present, the original array is returned
+     * @param cur The original array, or null to represent an empty array.
+     * @param val The value to add.
+     * @return A new array that contains all of the values of the original array
+     * with the new value added, or the original array.
+     */
     public static int[] appendInt(int[] cur, int val) {
         if (cur == null) {
             return new int[] { val };
@@ -264,4 +281,48 @@
         }
         return cur;
     }
+
+    /**
+     * Appends a new value to a copy of the array and returns the copy.  If
+     * the value is already present, the original array is returned
+     * @param cur The original array, or null to represent an empty array.
+     * @param val The value to add.
+     * @return A new array that contains all of the values of the original array
+     * with the new value added, or the original array.
+     */
+    public static long[] appendLong(long[] cur, long val) {
+        if (cur == null) {
+            return new long[] { val };
+        }
+        final int N = cur.length;
+        for (int i = 0; i < N; i++) {
+            if (cur[i] == val) {
+                return cur;
+            }
+        }
+        long[] ret = new long[N + 1];
+        System.arraycopy(cur, 0, ret, 0, N);
+        ret[N] = val;
+        return ret;
+    }
+
+    public static long[] removeLong(long[] cur, long val) {
+        if (cur == null) {
+            return null;
+        }
+        final int N = cur.length;
+        for (int i = 0; i < N; i++) {
+            if (cur[i] == val) {
+                long[] ret = new long[N - 1];
+                if (i > 0) {
+                    System.arraycopy(cur, 0, ret, 0, i);
+                }
+                if (i < (N - 1)) {
+                    System.arraycopy(cur, i + 1, ret, i, N - i - 1);
+                }
+                return ret;
+            }
+        }
+        return cur;
+    }
 }
diff --git a/core/java/com/android/internal/util/Preconditions.java b/core/java/com/android/internal/util/Preconditions.java
index f6722a6..c0d1e88 100644
--- a/core/java/com/android/internal/util/Preconditions.java
+++ b/core/java/com/android/internal/util/Preconditions.java
@@ -183,6 +183,33 @@
     }
 
     /**
+     * Ensures that the argument int value is within the inclusive range.
+     *
+     * @param value a int value
+     * @param lower the lower endpoint of the inclusive range
+     * @param upper the upper endpoint of the inclusive range
+     * @param valueName the name of the argument to use if the check fails
+     *
+     * @return the validated int value
+     *
+     * @throws IllegalArgumentException if {@code value} was not within the range
+     */
+    public static int checkArgumentInRange(int value, int lower, int upper,
+            String valueName) {
+        if (value < lower) {
+            throw new IllegalArgumentException(
+                    String.format(
+                            "%s is out of range of [%d, %d] (too low)", valueName, lower, upper));
+        } else if (value > upper) {
+            throw new IllegalArgumentException(
+                    String.format(
+                            "%s is out of range of [%d, %d] (too high)", valueName, lower, upper));
+        }
+
+        return value;
+    }
+
+    /**
      * Ensures that the array is not {@code null}, and none if its elements are {@code null}.
      *
      * @param value an array of boxed objects
diff --git a/core/java/com/android/internal/util/Protocol.java b/core/java/com/android/internal/util/Protocol.java
index 81e67d8..af966b1 100644
--- a/core/java/com/android/internal/util/Protocol.java
+++ b/core/java/com/android/internal/util/Protocol.java
@@ -57,9 +57,9 @@
     public static final int BASE_DNS_PINGER                                         = 0x00050000;
     public static final int BASE_NSD_MANAGER                                        = 0x00060000;
     public static final int BASE_NETWORK_STATE_TRACKER                              = 0x00070000;
-    public static final int BASE_CONNECTIVITY_SERVICE                               = 0x00080000;
+    public static final int BASE_CONNECTIVITY_MANAGER                               = 0x00080000;
     public static final int BASE_NETWORK_AGENT                                      = 0x00081000;
     public static final int BASE_NETWORK_MONITOR                                    = 0x00082000;
-    public static final int BASE_CONNECTIVITY_MANAGER                               = 0x00083000;
+    public static final int BASE_NETWORK_FACTORY                                    = 0x00083000;
     //TODO: define all used protocols
 }
diff --git a/core/java/com/android/internal/util/VirtualRefBasePtr.java b/core/java/com/android/internal/util/VirtualRefBasePtr.java
index 0bd4d3a..52306f1 100644
--- a/core/java/com/android/internal/util/VirtualRefBasePtr.java
+++ b/core/java/com/android/internal/util/VirtualRefBasePtr.java
@@ -32,11 +32,17 @@
         return mNativePtr;
     }
 
+    public void release() {
+        if (mNativePtr != 0) {
+            nDecStrong(mNativePtr);
+            mNativePtr = 0;
+        }
+    }
+
     @Override
     protected void finalize() throws Throwable {
         try {
-            nDecStrong(mNativePtr);
-            mNativePtr = 0;
+            release();
         } finally {
             super.finalize();
         }
diff --git a/core/java/com/android/internal/widget/AbsActionBarView.java b/core/java/com/android/internal/widget/AbsActionBarView.java
index 183478f..9e7ff93 100644
--- a/core/java/com/android/internal/widget/AbsActionBarView.java
+++ b/core/java/com/android/internal/widget/AbsActionBarView.java
@@ -34,7 +34,7 @@
 public abstract class AbsActionBarView extends ViewGroup {
     protected ActionMenuView mMenuView;
     protected ActionMenuPresenter mActionMenuPresenter;
-    protected ActionBarContainer mSplitView;
+    protected ViewGroup mSplitView;
     protected boolean mSplitActionBar;
     protected boolean mSplitWhenNarrow;
     protected int mContentHeight;
@@ -74,7 +74,7 @@
         setContentHeight(a.getLayoutDimension(R.styleable.ActionBar_height, 0));
         a.recycle();
         if (mSplitWhenNarrow) {
-            setSplitActionBar(getContext().getResources().getBoolean(
+            setSplitToolbar(getContext().getResources().getBoolean(
                     com.android.internal.R.bool.split_action_bar_is_narrow));
         }
         if (mActionMenuPresenter != null) {
@@ -86,7 +86,7 @@
      * Sets whether the bar should be split right now, no questions asked.
      * @param split true if the bar should split
      */
-    public void setSplitActionBar(boolean split) {
+    public void setSplitToolbar(boolean split) {
         mSplitActionBar = split;
     }
 
@@ -107,7 +107,7 @@
         return mContentHeight;
     }
 
-    public void setSplitView(ActionBarContainer splitView) {
+    public void setSplitView(ViewGroup splitView) {
         mSplitView = splitView;
     }
 
@@ -214,6 +214,10 @@
         return mActionMenuPresenter != null && mActionMenuPresenter.isOverflowReserved();
     }
 
+    public boolean canShowOverflowMenu() {
+        return isOverflowReserved() && getVisibility() == VISIBLE;
+    }
+
     public void dismissPopupMenus() {
         if (mActionMenuPresenter != null) {
             mActionMenuPresenter.dismissPopupMenus();
diff --git a/core/java/com/android/internal/widget/ActionBarContainer.java b/core/java/com/android/internal/widget/ActionBarContainer.java
index ed07514..790b611 100644
--- a/core/java/com/android/internal/widget/ActionBarContainer.java
+++ b/core/java/com/android/internal/widget/ActionBarContainer.java
@@ -36,7 +36,7 @@
 public class ActionBarContainer extends FrameLayout {
     private boolean mIsTransitioning;
     private View mTabContainer;
-    private ActionBarView mActionBarView;
+    private View mActionBarView;
 
     private Drawable mBackground;
     private Drawable mStackedBackground;
@@ -76,7 +76,7 @@
     @Override
     public void onFinishInflate() {
         super.onFinishInflate();
-        mActionBarView = (ActionBarView) findViewById(com.android.internal.R.id.action_bar);
+        mActionBarView = findViewById(com.android.internal.R.id.action_bar);
     }
 
     public void setPrimaryBackground(Drawable bg) {
@@ -251,6 +251,10 @@
         return null;
     }
 
+    private boolean isCollapsed(View view) {
+        return view == null || view.getVisibility() == GONE || view.getMeasuredHeight() == 0;
+    }
+
     @Override
     public void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
         if (mActionBarView == null &&
@@ -263,7 +267,7 @@
         if (mActionBarView == null) return;
 
         final LayoutParams lp = (LayoutParams) mActionBarView.getLayoutParams();
-        final int actionBarViewHeight = mActionBarView.isCollapsed() ? 0 :
+        final int actionBarViewHeight = isCollapsed(mActionBarView) ? 0 :
                 mActionBarView.getMeasuredHeight() + lp.topMargin + lp.bottomMargin;
 
         if (mTabContainer != null && mTabContainer.getVisibility() != GONE) {
@@ -298,9 +302,8 @@
             }
         } else {
             if (mBackground != null) {
-                final ActionBarView actionBarView = mActionBarView;
-                mBackground.setBounds(actionBarView.getLeft(), actionBarView.getTop(),
-                        actionBarView.getRight(), actionBarView.getBottom());
+                mBackground.setBounds(mActionBarView.getLeft(), mActionBarView.getTop(),
+                        mActionBarView.getRight(), mActionBarView.getBottom());
                 needsInvalidate = true;
             }
             mIsStacked = hasTabs;
diff --git a/core/java/com/android/internal/widget/ActionBarContextView.java b/core/java/com/android/internal/widget/ActionBarContextView.java
index e10070f..6ff77a0 100644
--- a/core/java/com/android/internal/widget/ActionBarContextView.java
+++ b/core/java/com/android/internal/widget/ActionBarContextView.java
@@ -83,7 +83,7 @@
 
         final TypedArray a = context.obtainStyledAttributes(
                 attrs, R.styleable.ActionMode, defStyleAttr, defStyleRes);
-        setBackgroundDrawable(a.getDrawable(
+        setBackground(a.getDrawable(
                 com.android.internal.R.styleable.ActionMode_background));
         mTitleStyleRes = a.getResourceId(
                 com.android.internal.R.styleable.ActionMode_titleTextStyle, 0);
@@ -109,7 +109,7 @@
     }
 
     @Override
-    public void setSplitActionBar(boolean split) {
+    public void setSplitToolbar(boolean split) {
         if (mSplitActionBar != split) {
             if (mActionMenuPresenter != null) {
                 // Mode is already active; move everything over and adjust the menu itself.
@@ -137,7 +137,7 @@
                     mSplitView.addView(mMenuView, layoutParams);
                 }
             }
-            super.setSplitActionBar(split);
+            super.setSplitToolbar(split);
         }
     }
 
diff --git a/core/java/com/android/internal/widget/ActionBarOverlayLayout.java b/core/java/com/android/internal/widget/ActionBarOverlayLayout.java
index 7ab4bed..8a9cb22 100644
--- a/core/java/com/android/internal/widget/ActionBarOverlayLayout.java
+++ b/core/java/com/android/internal/widget/ActionBarOverlayLayout.java
@@ -39,6 +39,7 @@
 import android.view.Window;
 import android.view.WindowInsets;
 import android.widget.OverScroller;
+import android.widget.Toolbar;
 import com.android.internal.view.menu.MenuPresenter;
 
 /**
@@ -59,7 +60,7 @@
     private ActionBarContainer mActionBarTop;
 
     // Some interior UI elements.
-    private ActionBarView mActionBarView;
+    private DecorToolbar mDecorToolbar;
 
     // Content overlay drawable - generally the action bar's shadow
     private Drawable mWindowContentOverlay;
@@ -401,7 +402,7 @@
             topInset = mActionBarTop.getMeasuredHeight();
         }
 
-        if (mActionBarView.isSplitActionBar()) {
+        if (mDecorToolbar.isSplit()) {
             // If action bar is split, adjust bottom insets for it.
             if (mActionBarBottom != null) {
                 if (stable) {
@@ -563,12 +564,23 @@
             mContent = findViewById(com.android.internal.R.id.content);
             mActionBarTop = (ActionBarContainer) findViewById(
                     com.android.internal.R.id.action_bar_container);
-            mActionBarView = (ActionBarView) findViewById(com.android.internal.R.id.action_bar);
+            mDecorToolbar = getDecorToolbar(findViewById(com.android.internal.R.id.action_bar));
             mActionBarBottom = (ActionBarContainer) findViewById(
                     com.android.internal.R.id.split_action_bar);
         }
     }
 
+    private DecorToolbar getDecorToolbar(View view) {
+        if (view instanceof DecorToolbar) {
+            return (DecorToolbar) view;
+        } else if (view instanceof Toolbar) {
+            return ((Toolbar) view).getWrapper();
+        } else {
+            throw new IllegalStateException("Can't make a decor toolbar out of " +
+                    view.getClass().getSimpleName());
+        }
+    }
+
     public void setHideOnContentScrollEnabled(boolean hideOnContentScroll) {
         if (hideOnContentScroll != mHideOnContentScroll) {
             mHideOnContentScroll = hideOnContentScroll;
@@ -648,9 +660,9 @@
             final int action = event.getAction();
 
             // Collapse any expanded action views.
-            if (mActionBarView != null && mActionBarView.hasExpandedActionView()) {
+            if (mDecorToolbar != null && mDecorToolbar.hasExpandedActionView()) {
                 if (action == KeyEvent.ACTION_UP) {
-                    mActionBarView.collapseActionView();
+                    mDecorToolbar.collapseActionView();
                 }
                 return true;
             }
@@ -662,19 +674,19 @@
     @Override
     public void setWindowCallback(Window.Callback cb) {
         pullChildren();
-        mActionBarView.setWindowCallback(cb);
+        mDecorToolbar.setWindowCallback(cb);
     }
 
     @Override
     public void setWindowTitle(CharSequence title) {
         pullChildren();
-        mActionBarView.setWindowTitle(title);
+        mDecorToolbar.setWindowTitle(title);
     }
 
     @Override
     public CharSequence getTitle() {
         pullChildren();
-        return mActionBarView.getTitle();
+        return mDecorToolbar.getTitle();
     }
 
     @Override
@@ -682,10 +694,10 @@
         pullChildren();
         switch (windowFeature) {
             case Window.FEATURE_PROGRESS:
-                mActionBarView.initProgress();
+                mDecorToolbar.initProgress();
                 break;
             case Window.FEATURE_INDETERMINATE_PROGRESS:
-                mActionBarView.initIndeterminateProgress();
+                mDecorToolbar.initIndeterminateProgress();
                 break;
             case Window.FEATURE_ACTION_BAR_OVERLAY:
                 setOverlayMode(true);
@@ -704,15 +716,15 @@
         }
         if (splitActionBar) {
             pullChildren();
-            if (mActionBarBottom != null) {
-                mActionBarView.setSplitView(mActionBarBottom);
-                mActionBarView.setSplitActionBar(splitActionBar);
-                mActionBarView.setSplitWhenNarrow(splitWhenNarrow);
+            if (mActionBarBottom != null && mDecorToolbar.canSplit()) {
+                mDecorToolbar.setSplitView(mActionBarBottom);
+                mDecorToolbar.setSplitToolbar(splitActionBar);
+                mDecorToolbar.setSplitWhenNarrow(splitWhenNarrow);
 
                 final ActionBarContextView cab = (ActionBarContextView) findViewById(
                         com.android.internal.R.id.action_context_bar);
                 cab.setSplitView(mActionBarBottom);
-                cab.setSplitActionBar(splitActionBar);
+                cab.setSplitToolbar(splitActionBar);
                 cab.setSplitWhenNarrow(splitWhenNarrow);
             } else if (splitActionBar) {
                 Log.e(TAG, "Requested split action bar with " +
@@ -724,91 +736,91 @@
     @Override
     public boolean hasIcon() {
         pullChildren();
-        return mActionBarView.hasIcon();
+        return mDecorToolbar.hasIcon();
     }
 
     @Override
     public boolean hasLogo() {
         pullChildren();
-        return mActionBarView.hasLogo();
+        return mDecorToolbar.hasLogo();
     }
 
     @Override
     public void setIcon(int resId) {
         pullChildren();
-        mActionBarView.setIcon(resId);
+        mDecorToolbar.setIcon(resId);
     }
 
     @Override
     public void setIcon(Drawable d) {
         pullChildren();
-        mActionBarView.setIcon(d);
+        mDecorToolbar.setIcon(d);
     }
 
     @Override
     public void setLogo(int resId) {
         pullChildren();
-        mActionBarView.setLogo(resId);
+        mDecorToolbar.setLogo(resId);
     }
 
     @Override
     public boolean canShowOverflowMenu() {
         pullChildren();
-        return mActionBarView.isOverflowReserved() && mActionBarView.getVisibility() == VISIBLE;
+        return mDecorToolbar.canShowOverflowMenu();
     }
 
     @Override
     public boolean isOverflowMenuShowing() {
         pullChildren();
-        return mActionBarView.isOverflowMenuShowing();
+        return mDecorToolbar.isOverflowMenuShowing();
     }
 
     @Override
     public boolean isOverflowMenuShowPending() {
         pullChildren();
-        return mActionBarView.isOverflowMenuShowPending();
+        return mDecorToolbar.isOverflowMenuShowPending();
     }
 
     @Override
     public boolean showOverflowMenu() {
         pullChildren();
-        return mActionBarView.showOverflowMenu();
+        return mDecorToolbar.showOverflowMenu();
     }
 
     @Override
     public boolean hideOverflowMenu() {
         pullChildren();
-        return mActionBarView.hideOverflowMenu();
+        return mDecorToolbar.hideOverflowMenu();
     }
 
     @Override
     public void setMenuPrepared() {
         pullChildren();
-        mActionBarView.setMenuPrepared();
+        mDecorToolbar.setMenuPrepared();
     }
 
     @Override
     public void setMenu(Menu menu, MenuPresenter.Callback cb) {
         pullChildren();
-        mActionBarView.setMenu(menu, cb);
+        mDecorToolbar.setMenu(menu, cb);
     }
 
     @Override
     public void saveToolbarHierarchyState(SparseArray<Parcelable> toolbarStates) {
         pullChildren();
-        mActionBarView.saveHierarchyState(toolbarStates);
+        mDecorToolbar.saveHierarchyState(toolbarStates);
     }
 
     @Override
     public void restoreToolbarHierarchyState(SparseArray<Parcelable> toolbarStates) {
         pullChildren();
-        mActionBarView.restoreHierarchyState(toolbarStates);
+        mDecorToolbar.restoreHierarchyState(toolbarStates);
     }
 
     @Override
     public void dismissPopups() {
         pullChildren();
-        mActionBarView.dismissPopupMenus();
+        mDecorToolbar.dismissPopupMenus();
     }
 
     public static class LayoutParams extends MarginLayoutParams {
diff --git a/core/java/com/android/internal/widget/ActionBarView.java b/core/java/com/android/internal/widget/ActionBarView.java
index 60631b9..af82778 100644
--- a/core/java/com/android/internal/widget/ActionBarView.java
+++ b/core/java/com/android/internal/widget/ActionBarView.java
@@ -18,7 +18,6 @@
 
 import android.animation.LayoutTransition;
 import android.app.ActionBar;
-import android.app.ActionBar.OnNavigationListener;
 import android.content.Context;
 import android.content.pm.ApplicationInfo;
 import android.content.pm.PackageManager;
@@ -63,7 +62,7 @@
 /**
  * @hide
  */
-public class ActionBarView extends AbsActionBarView {
+public class ActionBarView extends AbsActionBarView implements DecorToolbar {
     private static final String TAG = "ActionBarView";
 
     /**
@@ -117,8 +116,7 @@
 
     private boolean mUserTitle;
     private boolean mIncludeTabs;
-    private boolean mIsCollapsable;
-    private boolean mIsCollapsed;
+    private boolean mIsCollapsible;
     private boolean mWasHomeEnabled; // Was it enabled before action view expansion?
 
     private MenuBuilder mOptionsMenu;
@@ -129,7 +127,7 @@
     private ActionMenuItem mLogoNavItem;
 
     private SpinnerAdapter mSpinnerAdapter;
-    private OnNavigationListener mCallback;
+    private AdapterView.OnItemSelectedListener mNavItemSelectedListener;
 
     private Runnable mTabSelector;
 
@@ -138,18 +136,6 @@
 
     Window.Callback mWindowCallback;
 
-    private final AdapterView.OnItemSelectedListener mNavItemSelectedListener =
-            new AdapterView.OnItemSelectedListener() {
-        public void onItemSelected(AdapterView parent, View view, int position, long id) {
-            if (mCallback != null) {
-                mCallback.onNavigationItemSelected(position, id);
-            }
-        }
-        public void onNothingSelected(AdapterView parent) {
-            // Do nothing
-        }
-    };
-
     private final OnClickListener mExpandedActionViewUpListener = new OnClickListener() {
         @Override
         public void onClick(View v) {
@@ -178,8 +164,6 @@
         TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.ActionBar,
                 com.android.internal.R.attr.actionBarStyle, 0);
 
-        ApplicationInfo appInfo = context.getApplicationInfo();
-        PackageManager pm = context.getPackageManager();
         mNavigationMode = a.getInt(R.styleable.ActionBar_navigationMode,
                 ActionBar.NAVIGATION_MODE_STANDARD);
         mTitle = a.getText(R.styleable.ActionBar_title);
@@ -260,7 +244,7 @@
         }
 
         if (mHomeDescriptionRes != 0) {
-            setHomeActionContentDescription(mHomeDescriptionRes);
+            setNavigationContentDescription(mHomeDescriptionRes);
         }
 
         if (mTabScrollView != null && mIncludeTabs) {
@@ -313,7 +297,7 @@
     }
 
     @Override
-    public void setSplitActionBar(boolean splitActionBar) {
+    public void setSplitToolbar(boolean splitActionBar) {
         if (mSplitActionBar != splitActionBar) {
             if (mMenuView != null) {
                 final ViewGroup oldParent = (ViewGroup) mMenuView.getParent();
@@ -349,18 +333,26 @@
                     mActionMenuPresenter.setItemLimit(Integer.MAX_VALUE);
                 }
             }
-            super.setSplitActionBar(splitActionBar);
+            super.setSplitToolbar(splitActionBar);
         }
     }
 
-    public boolean isSplitActionBar() {
+    public boolean isSplit() {
         return mSplitActionBar;
     }
 
+    public boolean canSplit() {
+        return true;
+    }
+
     public boolean hasEmbeddedTabs() {
         return mIncludeTabs;
     }
 
+    public void setEmbeddedTabView(View view) {
+        setEmbeddedTabView((ScrollingTabContainerView) view);
+    }
+
     public void setEmbeddedTabView(ScrollingTabContainerView tabs) {
         if (mTabScrollView != null) {
             removeView(mTabScrollView);
@@ -376,10 +368,6 @@
         }
     }
 
-    public void setCallback(OnNavigationListener callback) {
-        mCallback = callback;
-    }
-
     public void setMenuPrepared() {
         mMenuPrepared = true;
     }
@@ -473,7 +461,7 @@
         }
     }
 
-    public void setCustomNavigationView(View view) {
+    public void setCustomView(View view) {
         final boolean showCustom = (mDisplayOptions & ActionBar.DISPLAY_SHOW_CUSTOM) != 0;
         if (showCustom) {
             ActionBarTransition.beginDelayedTransition(this);
@@ -765,15 +753,16 @@
         }
     }
 
-    public void setDropdownAdapter(SpinnerAdapter adapter) {
+    public void setDropdownParams(SpinnerAdapter adapter, AdapterView.OnItemSelectedListener l) {
         mSpinnerAdapter = adapter;
+        mNavItemSelectedListener = l;
         if (mSpinner != null) {
             mSpinner.setAdapter(adapter);
         }
     }
 
-    public SpinnerAdapter getDropdownAdapter() {
-        return mSpinnerAdapter;
+    public int getDropdownItemCount() {
+        return mSpinnerAdapter != null ? mSpinnerAdapter.getCount() : 0;
     }
 
     public void setDropdownSelectedPosition(int position) {
@@ -784,7 +773,7 @@
         return mSpinner.getSelectedItemPosition();
     }
 
-    public View getCustomNavigationView() {
+    public View getCustomView() {
         return mCustomNavView;
     }
 
@@ -797,6 +786,11 @@
     }
 
     @Override
+    public ViewGroup getViewGroup() {
+        return this;
+    }
+
+    @Override
     protected ViewGroup.LayoutParams generateDefaultLayoutParams() {
         // Used by custom nav views if they don't supply layout params. Everything else
         // added to an ActionBarView should have them already.
@@ -860,12 +854,8 @@
         mContextView = view;
     }
 
-    public void setCollapsable(boolean collapsable) {
-        mIsCollapsable = collapsable;
-    }
-
-    public boolean isCollapsed() {
-        return mIsCollapsed;
+    public void setCollapsible(boolean collapsible) {
+        mIsCollapsible = collapsible;
     }
 
     /**
@@ -893,7 +883,7 @@
     @Override
     protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
         final int childCount = getChildCount();
-        if (mIsCollapsable) {
+        if (mIsCollapsible) {
             int visibleChildren = 0;
             for (int i = 0; i < childCount; i++) {
                 final View child = getChildAt(i);
@@ -915,11 +905,9 @@
             if (visibleChildren == 0) {
                 // No size for an empty action bar when collapsable.
                 setMeasuredDimension(0, 0);
-                mIsCollapsed = true;
                 return;
             }
         }
-        mIsCollapsed = false;
 
         int widthMode = MeasureSpec.getMode(widthMeasureSpec);
         if (widthMode != MeasureSpec.EXACTLY) {
@@ -1323,20 +1311,20 @@
         }
     }
 
-    public void setHomeAsUpIndicator(Drawable indicator) {
+    public void setNavigationIcon(Drawable indicator) {
         mHomeLayout.setUpIndicator(indicator);
     }
 
-    public void setHomeAsUpIndicator(int resId) {
+    public void setNavigationIcon(int resId) {
         mHomeLayout.setUpIndicator(resId);
     }
 
-    public void setHomeActionContentDescription(CharSequence description) {
+    public void setNavigationContentDescription(CharSequence description) {
         mHomeDescription = description;
         updateHomeAccessibility(mUpGoerFive.isEnabled());
     }
 
-    public void setHomeActionContentDescription(int resId) {
+    public void setNavigationContentDescription(int resId) {
         mHomeDescriptionRes = resId;
         mHomeDescription = resId != 0 ? getResources().getText(resId) : null;
         updateHomeAccessibility(mUpGoerFive.isEnabled());
diff --git a/core/java/com/android/internal/widget/DecorToolbar.java b/core/java/com/android/internal/widget/DecorToolbar.java
new file mode 100644
index 0000000..ee6988e
--- /dev/null
+++ b/core/java/com/android/internal/widget/DecorToolbar.java
@@ -0,0 +1,94 @@
+/*
+ * 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.widget;
+
+import android.content.Context;
+import android.graphics.drawable.Drawable;
+import android.os.Parcelable;
+import android.util.SparseArray;
+import android.view.Menu;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.Window;
+import android.widget.AdapterView;
+import android.widget.SpinnerAdapter;
+import com.android.internal.view.menu.MenuPresenter;
+
+/**
+ * Common interface for a toolbar that sits as part of the window decor.
+ * Layouts that control window decor use this as a point of interaction with different
+ * bar implementations.
+ *
+ * @hide
+ */
+public interface DecorToolbar {
+    ViewGroup getViewGroup();
+    Context getContext();
+    boolean isSplit();
+    boolean hasExpandedActionView();
+    void collapseActionView();
+    void setWindowCallback(Window.Callback cb);
+    void setWindowTitle(CharSequence title);
+    CharSequence getTitle();
+    void setTitle(CharSequence title);
+    CharSequence getSubtitle();
+    void setSubtitle(CharSequence subtitle);
+    void initProgress();
+    void initIndeterminateProgress();
+    boolean canSplit();
+    void setSplitView(ViewGroup splitView);
+    void setSplitToolbar(boolean split);
+    void setSplitWhenNarrow(boolean splitWhenNarrow);
+    boolean hasIcon();
+    boolean hasLogo();
+    void setIcon(int resId);
+    void setIcon(Drawable d);
+    void setLogo(int resId);
+    void setLogo(Drawable d);
+    boolean canShowOverflowMenu();
+    boolean isOverflowMenuShowing();
+    boolean isOverflowMenuShowPending();
+    boolean showOverflowMenu();
+    boolean hideOverflowMenu();
+    void setMenuPrepared();
+    void setMenu(Menu menu, MenuPresenter.Callback cb);
+    void dismissPopupMenus();
+
+    int getDisplayOptions();
+    void setDisplayOptions(int opts);
+    void setEmbeddedTabView(View tabView);
+    boolean hasEmbeddedTabs();
+    boolean isTitleTruncated();
+    void setCollapsible(boolean collapsible);
+    void setHomeButtonEnabled(boolean enable);
+    int getNavigationMode();
+    void setNavigationMode(int mode);
+    void setDropdownParams(SpinnerAdapter adapter, AdapterView.OnItemSelectedListener listener);
+    void setDropdownSelectedPosition(int position);
+    int getDropdownSelectedPosition();
+    int getDropdownItemCount();
+    void setCustomView(View view);
+    View getCustomView();
+    void animateToVisibility(int visibility);
+    void setNavigationIcon(Drawable icon);
+    void setNavigationIcon(int resId);
+    void setNavigationContentDescription(CharSequence description);
+    void setNavigationContentDescription(int resId);
+    void saveHierarchyState(SparseArray<Parcelable> toolbarStates);
+    void restoreHierarchyState(SparseArray<Parcelable> toolbarStates);
+}
diff --git a/core/java/com/android/internal/widget/LockPatternUtils.java b/core/java/com/android/internal/widget/LockPatternUtils.java
index 25e3463..d31c5cc 100644
--- a/core/java/com/android/internal/widget/LockPatternUtils.java
+++ b/core/java/com/android/internal/widget/LockPatternUtils.java
@@ -43,7 +43,6 @@
 import android.widget.Button;
 
 import com.android.internal.R;
-import com.android.internal.telephony.ITelephony;
 import com.google.android.collect.Lists;
 
 import java.security.MessageDigest;
@@ -1360,19 +1359,11 @@
     /**
      * Resumes a call in progress. Typically launched from the EmergencyCall button
      * on various lockscreens.
-     *
-     * @return true if we were able to tell InCallScreen to show.
      */
-    public boolean resumeCall() {
-        ITelephony phone = ITelephony.Stub.asInterface(ServiceManager.checkService("phone"));
-        try {
-            if (phone != null && phone.showCallScreen()) {
-                return true;
-            }
-        } catch (RemoteException e) {
-            // What can we do?
-        }
-        return false;
+    public void resumeCall() {
+        TelephonyManager telephonyManager =
+                (TelephonyManager) mContext.getSystemService(Context.TELEPHONY_SERVICE);
+        telephonyManager.showCallScreen();
     }
 
     private void finishBiometricWeak() {
diff --git a/core/java/com/android/internal/widget/LockPatternUtilsCache.java b/core/java/com/android/internal/widget/LockPatternUtilsCache.java
index 550aa6d..624f67c 100644
--- a/core/java/com/android/internal/widget/LockPatternUtilsCache.java
+++ b/core/java/com/android/internal/widget/LockPatternUtilsCache.java
@@ -28,6 +28,11 @@
  */
 public class LockPatternUtilsCache implements ILockSettings {
 
+    private static final String HAS_LOCK_PATTERN_CACHE_KEY
+            = "LockPatternUtils.Cache.HasLockPatternCacheKey";
+    private static final String HAS_LOCK_PASSWORD_CACHE_KEY
+            = "LockPatternUtils.Cache.HasLockPasswordCacheKey";
+
     private static LockPatternUtilsCache sInstance;
 
     private final ILockSettings mService;
@@ -109,7 +114,9 @@
 
     @Override
     public void setLockPattern(String pattern, int userId) throws RemoteException {
+        invalidateCache(HAS_LOCK_PATTERN_CACHE_KEY, userId);
         mService.setLockPattern(pattern, userId);
+        putCache(HAS_LOCK_PATTERN_CACHE_KEY, userId, pattern != null);
     }
 
     @Override
@@ -119,7 +126,9 @@
 
     @Override
     public void setLockPassword(String password, int userId) throws RemoteException {
+        invalidateCache(HAS_LOCK_PASSWORD_CACHE_KEY, userId);
         mService.setLockPassword(password, userId);
+        putCache(HAS_LOCK_PASSWORD_CACHE_KEY, userId, password != null);
     }
 
     @Override
@@ -134,12 +143,24 @@
 
     @Override
     public boolean havePattern(int userId) throws RemoteException {
-        return mService.havePattern(userId);
+        Object value = peekCache(HAS_LOCK_PATTERN_CACHE_KEY, userId);
+        if (value instanceof Boolean) {
+            return (boolean) value;
+        }
+        boolean result = mService.havePattern(userId);
+        putCache(HAS_LOCK_PATTERN_CACHE_KEY, userId, result);
+        return result;
     }
 
     @Override
     public boolean havePassword(int userId) throws RemoteException {
-        return mService.havePassword(userId);
+        Object value = peekCache(HAS_LOCK_PASSWORD_CACHE_KEY, userId);
+        if (value instanceof Boolean) {
+            return (boolean) value;
+        }
+        boolean result = mService.havePassword(userId);
+        putCache(HAS_LOCK_PASSWORD_CACHE_KEY, userId, result);
+        return result;
     }
 
     @Override
diff --git a/core/java/com/android/internal/widget/LockPatternView.java b/core/java/com/android/internal/widget/LockPatternView.java
index 36ed344..d841d53 100644
--- a/core/java/com/android/internal/widget/LockPatternView.java
+++ b/core/java/com/android/internal/widget/LockPatternView.java
@@ -57,6 +57,7 @@
     private static final int ASPECT_LOCK_HEIGHT = 2; // Fixed height; width will be minimum of (w,h)
 
     private static final boolean PROFILE_DRAWING = false;
+    private final CellState[][] mCellStates;
     private boolean mDrawingProfilingStarted = false;
 
     private Paint mPaint = new Paint();
@@ -187,6 +188,12 @@
         }
     }
 
+    public static class CellState {
+        public float scale = 1.0f;
+        public float translateY = 0.0f;
+        public float alpha = 1.0f;
+     }
+
     /**
      * How to display the current pattern.
      */
@@ -296,6 +303,18 @@
             mBitmapHeight = Math.max(mBitmapHeight, bitmap.getHeight());
         }
 
+        mPaint.setFilterBitmap(true);
+
+        mCellStates = new CellState[3][3];
+        for (int i = 0; i < 3; i++) {
+            for (int j = 0; j < 3; j++) {
+                mCellStates[i][j] = new CellState();
+            }
+        }
+    }
+
+    public CellState[][] getCellStates() {
+        return mCellStates;
     }
 
     private Bitmap getBitmapFor(int resId) {
@@ -873,18 +892,22 @@
             //float centerY = mPaddingTop + i * mSquareHeight + (mSquareHeight / 2);
             for (int j = 0; j < 3; j++) {
                 float leftX = paddingLeft + j * squareWidth;
-                drawCircle(canvas, (int) leftX, (int) topY, drawLookup[i][j]);
+                float scale = mCellStates[i][j].scale;
+                mPaint.setAlpha((int) (mCellStates[i][j].alpha * 255));
+                float translationY = mCellStates[i][j].translateY;
+                drawCircle(canvas, (int) leftX, (int) topY + translationY, scale, drawLookup[i][j]);
             }
         }
 
+        // Reset the alpha to draw normally
+        mPaint.setAlpha(255);
+
         // TODO: the path should be created and cached every time we hit-detect a cell
         // only the last segment of the path should be computed here
         // draw the path of the pattern (unless we are in stealth mode)
         final boolean drawPath = !mInStealthMode;
 
         // draw the arrows associated with the path (unless we are in stealth mode)
-        boolean oldFlag = (mPaint.getFlags() & Paint.FILTER_BITMAP_FLAG) != 0;
-        mPaint.setFilterBitmap(true); // draw with higher quality since we render with transforms
         if (drawPath) {
             for (int i = 0; i < count - 1; i++) {
                 Cell cell = pattern.get(i);
@@ -898,7 +921,8 @@
                 }
 
                 float leftX = paddingLeft + cell.column * squareWidth;
-                float topY = paddingTop + cell.row * squareHeight;
+                float topY = paddingTop + cell.row * squareHeight
+                        + mCellStates[cell.row][cell.column].translateY;
 
                 drawArrow(canvas, leftX, topY, cell, next);
             }
@@ -919,6 +943,9 @@
 
                 float centerX = getCenterXForColumn(cell.column);
                 float centerY = getCenterYForRow(cell.row);
+
+                // Respect translation in animation
+                centerY += mCellStates[cell.row][cell.column].translateY;
                 if (i == 0) {
                     currentPath.moveTo(centerX, centerY);
                 } else {
@@ -933,8 +960,6 @@
             }
             canvas.drawPath(currentPath, mPathPaint);
         }
-
-        mPaint.setFilterBitmap(oldFlag); // restore default flag
     }
 
     private void drawArrow(Canvas canvas, float leftX, float topY, Cell start, Cell end) {
@@ -979,7 +1004,8 @@
      * @param topY
      * @param partOfPattern Whether this circle is part of the pattern.
      */
-    private void drawCircle(Canvas canvas, int leftX, int topY, boolean partOfPattern) {
+    private void drawCircle(Canvas canvas, float leftX, float topY, float scale,
+            boolean partOfPattern) {
         Bitmap outerCircle;
         Bitmap innerCircle;
 
@@ -1019,7 +1045,7 @@
 
         mCircleMatrix.setTranslate(leftX + offsetX, topY + offsetY);
         mCircleMatrix.preTranslate(mBitmapWidth/2, mBitmapHeight/2);
-        mCircleMatrix.preScale(sx, sy);
+        mCircleMatrix.preScale(sx * scale, sy * scale);
         mCircleMatrix.preTranslate(-mBitmapWidth/2, -mBitmapHeight/2);
 
         canvas.drawBitmap(outerCircle, mCircleMatrix, mPaint);
diff --git a/core/java/com/android/internal/widget/ToolbarWidgetWrapper.java b/core/java/com/android/internal/widget/ToolbarWidgetWrapper.java
new file mode 100644
index 0000000..3e15c32
--- /dev/null
+++ b/core/java/com/android/internal/widget/ToolbarWidgetWrapper.java
@@ -0,0 +1,541 @@
+/*
+ * 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.widget;
+
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.app.ActionBar;
+import android.content.Context;
+import android.content.res.TypedArray;
+import android.graphics.drawable.Drawable;
+import android.os.Parcelable;
+import android.text.TextUtils;
+import android.util.Log;
+import android.util.SparseArray;
+import android.view.LayoutInflater;
+import android.view.Menu;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.Window;
+import android.widget.ActionMenuPresenter;
+import android.widget.AdapterView;
+import android.widget.Spinner;
+import android.widget.SpinnerAdapter;
+import android.widget.Toolbar;
+import com.android.internal.R;
+import com.android.internal.view.menu.ActionMenuItem;
+import com.android.internal.view.menu.MenuBuilder;
+import com.android.internal.view.menu.MenuPresenter;
+
+/**
+ * Internal class used to interact with the Toolbar widget without
+ * exposing interface methods to the public API.
+ *
+ * <p>ToolbarWidgetWrapper manages the differences between Toolbar and ActionBarView
+ * so that either variant acting as a
+ * {@link com.android.internal.app.WindowDecorActionBar WindowDecorActionBar} can behave
+ * in the same way.</p>
+ *
+ * @hide
+ */
+public class ToolbarWidgetWrapper implements DecorToolbar {
+    private static final String TAG = "ToolbarWidgetWrapper";
+
+    private static final int AFFECTS_LOGO_MASK =
+            ActionBar.DISPLAY_SHOW_HOME | ActionBar.DISPLAY_USE_LOGO;
+
+    private Toolbar mToolbar;
+
+    private int mDisplayOpts;
+    private View mTabView;
+    private Spinner mSpinner;
+    private View mCustomView;
+
+    private Drawable mIcon;
+    private Drawable mLogo;
+    private Drawable mNavIcon;
+
+    private boolean mTitleSet;
+    private CharSequence mTitle;
+    private CharSequence mSubtitle;
+
+    private Window.Callback mWindowCallback;
+    private boolean mMenuPrepared;
+    private ActionMenuPresenter mActionMenuPresenter;
+
+    public ToolbarWidgetWrapper(Toolbar toolbar) {
+        mToolbar = toolbar;
+
+        mTitle = toolbar.getTitle();
+        mSubtitle = toolbar.getSubtitle();
+        mTitleSet = !TextUtils.isEmpty(mTitle);
+
+        final TypedArray a = toolbar.getContext().obtainStyledAttributes(null,
+                R.styleable.ActionBar, R.attr.actionBarStyle, 0);
+
+        final CharSequence title = a.getText(R.styleable.ActionBar_title);
+        if (!TextUtils.isEmpty(title)) {
+            setTitle(title);
+        }
+
+        final CharSequence subtitle = a.getText(R.styleable.ActionBar_subtitle);
+        if (!TextUtils.isEmpty(subtitle)) {
+            setSubtitle(subtitle);
+        }
+
+        final Drawable logo = a.getDrawable(R.styleable.ActionBar_logo);
+        if (logo != null) {
+            setLogo(logo);
+        }
+
+        final Drawable icon = a.getDrawable(R.styleable.ActionBar_icon);
+        if (icon != null) {
+            setIcon(icon);
+        }
+
+        final Drawable navIcon = a.getDrawable(R.styleable.ActionBar_homeAsUpIndicator);
+        if (navIcon != null) {
+            setNavigationIcon(navIcon);
+        }
+
+        setDisplayOptions(a.getInt(R.styleable.ActionBar_displayOptions, 0));
+
+        final int customNavId = a.getResourceId(R.styleable.ActionBar_customNavigationLayout, 0);
+        if (customNavId != 0) {
+            setCustomView(LayoutInflater.from(mToolbar.getContext()).inflate(customNavId,
+                    mToolbar, false));
+            setDisplayOptions(mDisplayOpts | ActionBar.DISPLAY_SHOW_CUSTOM);
+        }
+
+        final int height = a.getLayoutDimension(R.styleable.ActionBar_height, 0);
+        if (height > 0) {
+            final ViewGroup.LayoutParams lp = mToolbar.getLayoutParams();
+            lp.height = height;
+            mToolbar.setLayoutParams(lp);
+        }
+
+        final int contentInsetStart = a.getDimensionPixelOffset(
+                R.styleable.ActionBar_contentInsetStart, 0);
+        final int contentInsetEnd = a.getDimensionPixelOffset(
+                R.styleable.ActionBar_contentInsetEnd, 0);
+        if (contentInsetStart > 0 || contentInsetEnd > 0) {
+            mToolbar.setContentInsetsRelative(contentInsetStart, contentInsetEnd);
+        }
+
+        final int titleTextStyle = a.getResourceId(R.styleable.ActionBar_titleTextStyle, 0);
+        if (titleTextStyle != 0) {
+            mToolbar.setTitleTextAppearance(mToolbar.getContext(), titleTextStyle);
+        }
+
+        final int subtitleTextStyle = a.getResourceId(R.styleable.ActionBar_subtitleTextStyle, 0);
+        if (subtitleTextStyle != 0) {
+            mToolbar.setSubtitleTextAppearance(mToolbar.getContext(), subtitleTextStyle);
+        }
+
+        a.recycle();
+
+        mToolbar.setNavigationOnClickListener(new View.OnClickListener() {
+            final ActionMenuItem mNavItem = new ActionMenuItem(mToolbar.getContext(),
+                    0, android.R.id.home, 0, 0, mTitle);
+            @Override
+            public void onClick(View v) {
+                if (mWindowCallback != null && mMenuPrepared) {
+                    mWindowCallback.onMenuItemSelected(Window.FEATURE_OPTIONS_PANEL, mNavItem);
+                }
+            }
+        });
+    }
+
+    @Override
+    public ViewGroup getViewGroup() {
+        return mToolbar;
+    }
+
+    @Override
+    public Context getContext() {
+        return mToolbar.getContext();
+    }
+
+    @Override
+    public boolean isSplit() {
+        return false;
+    }
+
+    @Override
+    public boolean hasExpandedActionView() {
+        return mToolbar.hasExpandedActionView();
+    }
+
+    @Override
+    public void collapseActionView() {
+        mToolbar.collapseActionView();
+    }
+
+    @Override
+    public void setWindowCallback(Window.Callback cb) {
+        mWindowCallback = cb;
+    }
+
+    @Override
+    public void setWindowTitle(CharSequence title) {
+        // "Real" title always trumps window title.
+        if (!mTitleSet) {
+            setTitleInt(title);
+        }
+    }
+
+    @Override
+    public CharSequence getTitle() {
+        return mToolbar.getTitle();
+    }
+
+    @Override
+    public void setTitle(CharSequence title) {
+        mTitleSet = true;
+        setTitleInt(title);
+    }
+
+    private void setTitleInt(CharSequence title) {
+        mTitle = title;
+        if ((mDisplayOpts & ActionBar.DISPLAY_SHOW_TITLE) != 0) {
+            mToolbar.setTitle(title);
+        }
+    }
+
+    @Override
+    public CharSequence getSubtitle() {
+        return mToolbar.getSubtitle();
+    }
+
+    @Override
+    public void setSubtitle(CharSequence subtitle) {
+        mSubtitle = subtitle;
+        if ((mDisplayOpts & ActionBar.DISPLAY_SHOW_TITLE) != 0) {
+            mToolbar.setSubtitle(subtitle);
+        }
+    }
+
+    @Override
+    public void initProgress() {
+        Log.i(TAG, "Progress display unsupported");
+    }
+
+    @Override
+    public void initIndeterminateProgress() {
+        Log.i(TAG, "Progress display unsupported");
+    }
+
+    @Override
+    public boolean canSplit() {
+        return false;
+    }
+
+    @Override
+    public void setSplitView(ViewGroup splitView) {
+    }
+
+    @Override
+    public void setSplitToolbar(boolean split) {
+        if (split) {
+            throw new UnsupportedOperationException("Cannot split an android.widget.Toolbar");
+        }
+    }
+
+    @Override
+    public void setSplitWhenNarrow(boolean splitWhenNarrow) {
+        // Ignore.
+    }
+
+    @Override
+    public boolean hasIcon() {
+        return mIcon != null;
+    }
+
+    @Override
+    public boolean hasLogo() {
+        return mLogo != null;
+    }
+
+    @Override
+    public void setIcon(int resId) {
+        setIcon(resId != 0 ? getContext().getDrawable(resId) : null);
+    }
+
+    @Override
+    public void setIcon(Drawable d) {
+        mIcon = d;
+        updateToolbarLogo();
+    }
+
+    @Override
+    public void setLogo(int resId) {
+        setLogo(resId != 0 ? getContext().getDrawable(resId) : null);
+    }
+
+    @Override
+    public void setLogo(Drawable d) {
+        mLogo = d;
+        updateToolbarLogo();
+    }
+
+    private void updateToolbarLogo() {
+        Drawable logo = null;
+        if ((mDisplayOpts & ActionBar.DISPLAY_SHOW_HOME) != 0) {
+            if ((mDisplayOpts & ActionBar.DISPLAY_USE_LOGO) != 0) {
+                logo = mLogo != null ? mLogo : mIcon;
+            } else {
+                logo = mIcon;
+            }
+        }
+        mToolbar.setLogo(logo);
+    }
+
+    @Override
+    public boolean canShowOverflowMenu() {
+        return mToolbar.canShowOverflowMenu();
+    }
+
+    @Override
+    public boolean isOverflowMenuShowing() {
+        return mToolbar.isOverflowMenuShowing();
+    }
+
+    @Override
+    public boolean isOverflowMenuShowPending() {
+        return mToolbar.isOverflowMenuShowPending();
+    }
+
+    @Override
+    public boolean showOverflowMenu() {
+        return mToolbar.showOverflowMenu();
+    }
+
+    @Override
+    public boolean hideOverflowMenu() {
+        return mToolbar.hideOverflowMenu();
+    }
+
+    @Override
+    public void setMenuPrepared() {
+        mMenuPrepared = true;
+    }
+
+    @Override
+    public void setMenu(Menu menu, MenuPresenter.Callback cb) {
+        if (mActionMenuPresenter == null) {
+            mActionMenuPresenter = new ActionMenuPresenter(mToolbar.getContext());
+            mActionMenuPresenter.setId(com.android.internal.R.id.action_menu_presenter);
+        }
+        mActionMenuPresenter.setCallback(cb);
+        mToolbar.setMenu((MenuBuilder) menu, mActionMenuPresenter);
+    }
+
+    @Override
+    public void dismissPopupMenus() {
+        mToolbar.dismissPopupMenus();
+    }
+
+    @Override
+    public int getDisplayOptions() {
+        return mDisplayOpts;
+    }
+
+    @Override
+    public void setDisplayOptions(int newOpts) {
+        final int oldOpts = mDisplayOpts;
+        final int changed = oldOpts ^ newOpts;
+        mDisplayOpts = newOpts;
+        if (changed != 0) {
+            if ((changed & ActionBar.DISPLAY_HOME_AS_UP) != 0) {
+                if ((newOpts & ActionBar.DISPLAY_HOME_AS_UP) != 0) {
+                    mToolbar.setNavigationIcon(mNavIcon);
+                } else {
+                    mToolbar.setNavigationIcon(null);
+                }
+            }
+
+            if ((changed & AFFECTS_LOGO_MASK) != 0) {
+                updateToolbarLogo();
+            }
+
+            if ((changed & ActionBar.DISPLAY_SHOW_TITLE) != 0) {
+                if ((newOpts & ActionBar.DISPLAY_SHOW_TITLE) != 0) {
+                    mToolbar.setTitle(mTitle);
+                    mToolbar.setSubtitle(mSubtitle);
+                } else {
+                    mToolbar.setTitle(null);
+                    mToolbar.setSubtitle(null);
+                }
+            }
+
+            if ((changed & ActionBar.DISPLAY_SHOW_CUSTOM) != 0 && mCustomView != null) {
+                if ((newOpts & ActionBar.DISPLAY_SHOW_CUSTOM) != 0) {
+                    mToolbar.addView(mCustomView);
+                } else {
+                    mToolbar.removeView(mCustomView);
+                }
+            }
+        }
+    }
+
+    @Override
+    public void setEmbeddedTabView(View tabView) {
+        mTabView = tabView;
+    }
+
+    @Override
+    public boolean hasEmbeddedTabs() {
+        return mTabView != null;
+    }
+
+    @Override
+    public boolean isTitleTruncated() {
+        return mToolbar.isTitleTruncated();
+    }
+
+    @Override
+    public void setCollapsible(boolean collapsible) {
+        // Ignore
+    }
+
+    @Override
+    public void setHomeButtonEnabled(boolean enable) {
+        // Ignore
+    }
+
+    @Override
+    public int getNavigationMode() {
+        return 0;
+    }
+
+    @Override
+    public void setNavigationMode(int mode) {
+        if (mode != ActionBar.NAVIGATION_MODE_STANDARD) {
+            throw new IllegalArgumentException(
+                    "Navigation modes not supported in this configuration");
+        }
+    }
+
+    @Override
+    public void setDropdownParams(SpinnerAdapter adapter,
+            AdapterView.OnItemSelectedListener listener) {
+        if (mSpinner == null) {
+            mSpinner = new Spinner(getContext());
+        }
+        mSpinner.setAdapter(adapter);
+        mSpinner.setOnItemSelectedListener(listener);
+    }
+
+    @Override
+    public void setDropdownSelectedPosition(int position) {
+        if (mSpinner == null) {
+            throw new IllegalStateException(
+                    "Can't set dropdown selected position without an adapter");
+        }
+        mSpinner.setSelection(position);
+    }
+
+    @Override
+    public int getDropdownSelectedPosition() {
+        return mSpinner != null ? mSpinner.getSelectedItemPosition() : 0;
+    }
+
+    @Override
+    public int getDropdownItemCount() {
+        return mSpinner != null ? mSpinner.getCount() : 0;
+    }
+
+    @Override
+    public void setCustomView(View view) {
+        if (mCustomView != null && (mDisplayOpts & ActionBar.DISPLAY_SHOW_CUSTOM) != 0) {
+            mToolbar.removeView(mCustomView);
+        }
+        mCustomView = view;
+        if (view != null && (mDisplayOpts & ActionBar.DISPLAY_SHOW_CUSTOM) != 0) {
+            mToolbar.addView(mCustomView);
+        }
+    }
+
+    @Override
+    public View getCustomView() {
+        return mCustomView;
+    }
+
+    @Override
+    public void animateToVisibility(int visibility) {
+        if (visibility == View.GONE) {
+            mToolbar.animate().translationY(mToolbar.getHeight()).alpha(0)
+                    .setListener(new AnimatorListenerAdapter() {
+                        private boolean mCanceled = false;
+                        @Override
+                        public void onAnimationEnd(Animator animation) {
+                            if (!mCanceled) {
+                                mToolbar.setVisibility(View.GONE);
+                            }
+                        }
+
+                        @Override
+                        public void onAnimationCancel(Animator animation) {
+                            mCanceled = true;
+                        }
+                    });
+        } else if (visibility == View.VISIBLE) {
+            mToolbar.animate().translationY(0).alpha(1)
+                    .setListener(new AnimatorListenerAdapter() {
+                        @Override
+                        public void onAnimationStart(Animator animation) {
+                            mToolbar.setVisibility(View.VISIBLE);
+                        }
+                    });
+        }
+    }
+
+    @Override
+    public void setNavigationIcon(Drawable icon) {
+        mNavIcon = icon;
+        if ((mDisplayOpts & ActionBar.DISPLAY_HOME_AS_UP) != 0) {
+            mToolbar.setNavigationIcon(icon);
+        }
+    }
+
+    @Override
+    public void setNavigationIcon(int resId) {
+        setNavigationIcon(mToolbar.getContext().getDrawable(resId));
+    }
+
+    @Override
+    public void setNavigationContentDescription(CharSequence description) {
+        mToolbar.setNavigationContentDescription(description);
+    }
+
+    @Override
+    public void setNavigationContentDescription(int resId) {
+        mToolbar.setNavigationContentDescription(resId);
+    }
+
+    @Override
+    public void saveHierarchyState(SparseArray<Parcelable> toolbarStates) {
+        mToolbar.saveHierarchyState(toolbarStates);
+    }
+
+    @Override
+    public void restoreHierarchyState(SparseArray<Parcelable> toolbarStates) {
+        mToolbar.restoreHierarchyState(toolbarStates);
+    }
+
+}
diff --git a/core/jni/Android.mk b/core/jni/Android.mk
index f446c3a..a1cd7f7 100644
--- a/core/jni/Android.mk
+++ b/core/jni/Android.mk
@@ -57,7 +57,6 @@
 	android_view_KeyEvent.cpp \
 	android_view_KeyCharacterMap.cpp \
 	android_view_GraphicBuffer.cpp \
-	android_view_GLRenderer.cpp \
 	android_view_GLES20Canvas.cpp \
 	android_view_HardwareLayer.cpp \
 	android_view_ThreadedRenderer.cpp \
diff --git a/core/jni/AndroidRuntime.cpp b/core/jni/AndroidRuntime.cpp
index 0c7eefa..e0c5e96 100644
--- a/core/jni/AndroidRuntime.cpp
+++ b/core/jni/AndroidRuntime.cpp
@@ -130,7 +130,6 @@
 extern int register_android_view_RenderNodeAnimator(JNIEnv* env);
 extern int register_android_view_GraphicBuffer(JNIEnv* env);
 extern int register_android_view_GLES20Canvas(JNIEnv* env);
-extern int register_android_view_GLRenderer(JNIEnv* env);
 extern int register_android_view_HardwareLayer(JNIEnv* env);
 extern int register_android_view_ThreadedRenderer(JNIEnv* env);
 extern int register_android_view_Surface(JNIEnv* env);
@@ -789,7 +788,7 @@
     }
 
     // libart tolerates libdvm flags, but not vice versa, so only pass some options if libart.
-    property_get("persist.sys.dalvik.vm.lib.1", dalvikVmLibBuf, "libdvm.so");
+    property_get("persist.sys.dalvik.vm.lib.2", dalvikVmLibBuf, "libart.so");
     bool libart = (strncmp(dalvikVmLibBuf, "libart", 6) == 0);
 
     if (libart) {
@@ -1214,7 +1213,6 @@
     REG_JNI(register_android_view_RenderNodeAnimator),
     REG_JNI(register_android_view_GraphicBuffer),
     REG_JNI(register_android_view_GLES20Canvas),
-    REG_JNI(register_android_view_GLRenderer),
     REG_JNI(register_android_view_HardwareLayer),
     REG_JNI(register_android_view_ThreadedRenderer),
     REG_JNI(register_android_view_Surface),
diff --git a/core/jni/android/graphics/BitmapFactory.cpp b/core/jni/android/graphics/BitmapFactory.cpp
index 7aa241a..928a7f8 100644
--- a/core/jni/android/graphics/BitmapFactory.cpp
+++ b/core/jni/android/graphics/BitmapFactory.cpp
@@ -128,8 +128,7 @@
 static SkPixelRef* installPixelRef(SkBitmap* bitmap, SkStreamRewindable* stream,
         int sampleSize, bool ditherImage) {
 
-    SkImageInfo bitmapInfo;
-    if (!bitmap->asImageInfo(&bitmapInfo)) {
+    if (kUnknown_SkColorType == bitmap->colorType()) {
         ALOGW("bitmap has unknown configuration so no memory has been allocated");
         return NULL;
     }
@@ -137,9 +136,9 @@
     SkImageRef* pr;
     // only use ashmem for large images, since mmaps come at a price
     if (bitmap->getSize() >= 32 * 1024) {
-        pr = new SkImageRef_ashmem(bitmapInfo, stream, sampleSize);
+        pr = new SkImageRef_ashmem(bitmap->info(), stream, sampleSize);
     } else {
-        pr = new SkImageRef_GlobalPool(bitmapInfo, stream, sampleSize);
+        pr = new SkImageRef_GlobalPool(bitmap->info(), stream, sampleSize);
     }
     pr->setDitherImage(ditherImage);
     bitmap->setPixelRef(pr)->unref();
diff --git a/core/jni/android/graphics/Camera.cpp b/core/jni/android/graphics/Camera.cpp
index ef57e3d..d17f46c 100644
--- a/core/jni/android/graphics/Camera.cpp
+++ b/core/jni/android/graphics/Camera.cpp
@@ -3,6 +3,8 @@
 
 #include "SkCamera.h"
 
+#include "GraphicsJNI.h"
+
 static jfieldID gNativeInstanceFieldID;
 
 static void Camera_constructor(JNIEnv* env, jobject obj) {
@@ -93,7 +95,7 @@
 }
 
 static void Camera_applyToCanvas(JNIEnv* env, jobject obj, jlong canvasHandle) {
-    SkCanvas* native_canvas = reinterpret_cast<SkCanvas*>(canvasHandle);
+    SkCanvas* native_canvas = GraphicsJNI::getNativeCanvas(canvasHandle);
     jlong viewHandle = env->GetLongField(obj, gNativeInstanceFieldID);
     Sk3DView* v = reinterpret_cast<Sk3DView*>(viewHandle);
     v->applyToCanvas((SkCanvas*)native_canvas);
diff --git a/core/jni/android/graphics/Canvas.cpp b/core/jni/android/graphics/Canvas.cpp
index f7acbd7..5fca582 100644
--- a/core/jni/android/graphics/Canvas.cpp
+++ b/core/jni/android/graphics/Canvas.cpp
@@ -42,23 +42,27 @@
 
 #include <utils/Log.h>
 
-static uint32_t get_thread_msec() {
-#if defined(HAVE_POSIX_CLOCKS)
-    struct timespec tm;
-
-    clock_gettime(CLOCK_THREAD_CPUTIME_ID, &tm);
-
-    return tm.tv_sec * 1000LL + tm.tv_nsec / 1000000;
-#else
-    struct timeval tv;
-
-    gettimeofday(&tv, NULL);
-    return tv.tv_sec * 1000LL + tv.tv_usec / 1000;
-#endif
-}
-
 namespace android {
 
+// Holds an SkCanvas reference plus additional native data.
+class NativeCanvasWrapper {
+public:
+    NativeCanvasWrapper(SkCanvas* canvas)
+        : mCanvas(canvas) { }
+
+    SkCanvas* getCanvas() const {
+        return mCanvas.get();
+    }
+
+    void setCanvas(SkCanvas* canvas) {
+        SkASSERT(canvas);
+        mCanvas.reset(canvas);
+    }
+
+private:
+    SkAutoTUnref<SkCanvas> mCanvas;
+};
+
 class ClipCopier : public SkCanvas::ClipVisitor {
 public:
     ClipCopier(SkCanvas* dstCanvas) : m_dstCanvas(dstCanvas) {}
@@ -86,27 +90,30 @@
 class SkCanvasGlue {
 public:
 
-    static void finalizer(JNIEnv* env, jobject clazz, jlong canvasHandle) {
-        SkCanvas* canvas = reinterpret_cast<SkCanvas*>(canvasHandle);
-        canvas->unref();
+    // Get the SkCanvas for a given native handle.
+    static inline SkCanvas* getNativeCanvas(jlong nativeHandle) {
+        SkASSERT(nativeHandle);
+        NativeCanvasWrapper* wrapper = reinterpret_cast<NativeCanvasWrapper*>(nativeHandle);
+        SkCanvas* canvas = wrapper->getCanvas();
+        SkASSERT(canvas);
+
+        return canvas;
     }
 
-    static jlong initRaster(JNIEnv* env, jobject, jlong bitmapHandle) {
-        SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(bitmapHandle);
+    // Construct an SkCanvas from the bitmap.
+    static SkCanvas* createCanvas(SkBitmap* bitmap) {
         if (bitmap) {
-            return reinterpret_cast<jlong>(new SkCanvas(*bitmap));
-        } else {
-            // Create an empty bitmap device to prevent callers from crashing
-            // if they attempt to draw into this canvas.
-            SkBitmap emptyBitmap;
-            return reinterpret_cast<jlong>(new SkCanvas(emptyBitmap));
+            return SkNEW_ARGS(SkCanvas, (*bitmap));
         }
+
+        // Create an empty bitmap device to prevent callers from crashing
+        // if they attempt to draw into this canvas.
+        SkBitmap emptyBitmap;
+        return new SkCanvas(emptyBitmap);
     }
 
-    static void copyCanvasState(JNIEnv* env, jobject clazz,
-                                jlong srcCanvasHandle, jlong dstCanvasHandle) {
-        SkCanvas* srcCanvas = reinterpret_cast<SkCanvas*>(srcCanvasHandle);
-        SkCanvas* dstCanvas = reinterpret_cast<SkCanvas*>(dstCanvasHandle);
+    // Copy the canvas matrix & clip state.
+    static void copyCanvasState(SkCanvas* srcCanvas, SkCanvas* dstCanvas) {
         if (srcCanvas && dstCanvas) {
             dstCanvas->setMatrix(srcCanvas->getTotalMatrix());
             if (NULL != srcCanvas->getDevice() && NULL != dstCanvas->getDevice()) {
@@ -116,6 +123,42 @@
         }
     }
 
+    // Native JNI handlers
+    static void finalizer(JNIEnv* env, jobject clazz, jlong nativeHandle) {
+        NativeCanvasWrapper* wrapper = reinterpret_cast<NativeCanvasWrapper*>(nativeHandle);
+        delete wrapper;
+    }
+
+    // Native wrapper constructor used by Canvas(Bitmap)
+    static jlong initRaster(JNIEnv* env, jobject, jlong bitmapHandle) {
+        // No check - 0 is a valid bitmapHandle.
+        SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(bitmapHandle);
+        SkCanvas* canvas = createCanvas(bitmap);
+
+        return reinterpret_cast<jlong>(new NativeCanvasWrapper(canvas));
+    }
+
+    // Native wrapper constructor used by Canvas(native_canvas)
+    static jlong initCanvas(JNIEnv* env, jobject, jlong canvasHandle) {
+        SkCanvas* canvas = reinterpret_cast<SkCanvas*>(canvasHandle);
+        return reinterpret_cast<jlong>(new NativeCanvasWrapper(canvas));
+    }
+
+    // Set the given bitmap as the new draw target (wrapped in a new SkCanvas),
+    // optionally copying canvas matrix & clip state.
+    static void setBitmap(JNIEnv* env, jobject, jlong canvasHandle, jlong bitmapHandle,
+                          jboolean copyState) {
+        NativeCanvasWrapper* wrapper = reinterpret_cast<NativeCanvasWrapper*>(canvasHandle);
+        SkCanvas* newCanvas = createCanvas(reinterpret_cast<SkBitmap*>(bitmapHandle));
+        NPE_CHECK_RETURN_VOID(env, newCanvas);
+
+        if (copyState == JNI_TRUE) {
+            copyCanvasState(wrapper->getCanvas(), newCanvas);
+        }
+
+        // setCanvas() unrefs the old canvas.
+        wrapper->setCanvas(newCanvas);
+    }
 
     static void freeCaches(JNIEnv* env, jobject) {
         // these are called in no particular order
@@ -163,7 +206,7 @@
 
     static jint saveLayer(JNIEnv* env, jobject, jlong canvasHandle, jobject bounds,
                          jlong paintHandle, jint flags) {
-        SkCanvas* canvas = reinterpret_cast<SkCanvas*>(canvasHandle);
+        SkCanvas* canvas = getNativeCanvas(canvasHandle);
         SkPaint* paint  = reinterpret_cast<SkPaint*>(paintHandle);
         SkRect* bounds_ = NULL;
         SkRect  storage;
@@ -177,7 +220,7 @@
     static jint saveLayer4F(JNIEnv* env, jobject, jlong canvasHandle,
                            jfloat l, jfloat t, jfloat r, jfloat b,
                            jlong paintHandle, jint flags) {
-        SkCanvas* canvas = reinterpret_cast<SkCanvas*>(canvasHandle);
+        SkCanvas* canvas = getNativeCanvas(canvasHandle);
         SkPaint* paint  = reinterpret_cast<SkPaint*>(paintHandle);
         SkRect bounds;
         bounds.set(l, t, r, b);
@@ -188,7 +231,7 @@
 
     static jint saveLayerAlpha(JNIEnv* env, jobject, jlong canvasHandle,
                               jobject bounds, jint alpha, jint flags) {
-        SkCanvas* canvas = reinterpret_cast<SkCanvas*>(canvasHandle);
+        SkCanvas* canvas = getNativeCanvas(canvasHandle);
         SkRect* bounds_ = NULL;
         SkRect  storage;
         if (bounds != NULL) {
@@ -203,7 +246,7 @@
     static jint saveLayerAlpha4F(JNIEnv* env, jobject, jlong canvasHandle,
                                 jfloat l, jfloat t, jfloat r, jfloat b,
                                 jint alpha, jint flags) {
-        SkCanvas* canvas = reinterpret_cast<SkCanvas*>(canvasHandle);
+        SkCanvas* canvas = getNativeCanvas(canvasHandle);
         SkRect  bounds;
         bounds.set(l, t, r, b);
         int result = canvas->saveLayerAlpha(&bounds, alpha,
@@ -259,14 +302,14 @@
 
     static void concat(JNIEnv* env, jobject, jlong canvasHandle,
                        jlong matrixHandle) {
-        SkCanvas* canvas = reinterpret_cast<SkCanvas*>(canvasHandle);
+        SkCanvas* canvas = getNativeCanvas(canvasHandle);
         const SkMatrix* matrix = reinterpret_cast<SkMatrix*>(matrixHandle);
         canvas->concat(*matrix);
     }
 
     static void setMatrix(JNIEnv* env, jobject, jlong canvasHandle,
                           jlong matrixHandle) {
-        SkCanvas* canvas = reinterpret_cast<SkCanvas*>(canvasHandle);
+        SkCanvas* canvas = getNativeCanvas(canvasHandle);
         const SkMatrix* matrix = reinterpret_cast<SkMatrix*>(matrixHandle);
         if (NULL == matrix) {
             canvas->resetMatrix();
@@ -318,8 +361,8 @@
     static jboolean clipRect(JNIEnv* env, jobject, jlong canvasHandle,
                              jfloat left, jfloat top, jfloat right, jfloat bottom,
                              jint op) {
+        SkCanvas* canvas = getNativeCanvas(canvasHandle);
         SkRect rect;
-        SkCanvas* canvas = reinterpret_cast<SkCanvas*>(canvasHandle);
         rect.set(left, top, right, bottom);
         canvas->clipRect(rect, static_cast<SkRegion::Op>(op));
         return hasNonEmptyClip(*canvas);
@@ -327,7 +370,7 @@
 
     static jboolean clipPath(JNIEnv* env, jobject, jlong canvasHandle,
                              jlong pathHandle, jint op) {
-        SkCanvas* canvas = reinterpret_cast<SkCanvas*>(canvasHandle);
+        SkCanvas* canvas = getNativeCanvas(canvasHandle);
         canvas->clipPath(*reinterpret_cast<SkPath*>(pathHandle),
                 static_cast<SkRegion::Op>(op));
         return hasNonEmptyClip(*canvas);
@@ -335,7 +378,7 @@
 
     static jboolean clipRegion(JNIEnv* env, jobject, jlong canvasHandle,
                                jlong deviceRgnHandle, jint op) {
-        SkCanvas* canvas = reinterpret_cast<SkCanvas*>(canvasHandle);
+        SkCanvas* canvas = getNativeCanvas(canvasHandle);
         SkRegion* deviceRgn = reinterpret_cast<SkRegion*>(deviceRgnHandle);
         canvas->clipRegion(*deviceRgn, static_cast<SkRegion::Op>(op));
         return hasNonEmptyClip(*canvas);
@@ -343,13 +386,13 @@
 
     static void setDrawFilter(JNIEnv* env, jobject, jlong canvasHandle,
                               jlong filterHandle) {
-        SkCanvas* canvas = reinterpret_cast<SkCanvas*>(canvasHandle);
+        SkCanvas* canvas = getNativeCanvas(canvasHandle);
         canvas->setDrawFilter(reinterpret_cast<SkDrawFilter*>(filterHandle));
     }
 
     static jboolean quickReject__RectF(JNIEnv* env, jobject, jlong canvasHandle,
                                         jobject rect) {
-        SkCanvas* canvas = reinterpret_cast<SkCanvas*>(canvasHandle);
+        SkCanvas* canvas = getNativeCanvas(canvasHandle);
         SkRect rect_;
         GraphicsJNI::jrectf_to_rect(env, rect, &rect_);
         bool result = canvas->quickReject(rect_);
@@ -358,7 +401,7 @@
 
     static jboolean quickReject__Path(JNIEnv* env, jobject, jlong canvasHandle,
                                        jlong pathHandle) {
-        SkCanvas* canvas = reinterpret_cast<SkCanvas*>(canvasHandle);
+        SkCanvas* canvas = getNativeCanvas(canvasHandle);
         bool result = canvas->quickReject(*reinterpret_cast<SkPath*>(pathHandle));
         return result ? JNI_TRUE : JNI_FALSE;
     }
@@ -366,7 +409,7 @@
     static jboolean quickReject__FFFF(JNIEnv* env, jobject, jlong canvasHandle,
                                        jfloat left, jfloat top, jfloat right,
                                        jfloat bottom) {
-        SkCanvas* canvas = reinterpret_cast<SkCanvas*>(canvasHandle);
+        SkCanvas* canvas = getNativeCanvas(canvasHandle);
         SkRect r;
         r.set(left, top, right, bottom);
         bool result = canvas->quickReject(r);
@@ -375,32 +418,32 @@
 
     static void drawRGB(JNIEnv* env, jobject, jlong canvasHandle,
                         jint r, jint g, jint b) {
-        SkCanvas* canvas = reinterpret_cast<SkCanvas*>(canvasHandle);
+        SkCanvas* canvas = getNativeCanvas(canvasHandle);
         canvas->drawARGB(0xFF, r, g, b);
     }
 
     static void drawARGB(JNIEnv* env, jobject, jlong canvasHandle,
                          jint a, jint r, jint g, jint b) {
-        SkCanvas* canvas = reinterpret_cast<SkCanvas*>(canvasHandle);
+        SkCanvas* canvas = getNativeCanvas(canvasHandle);
         canvas->drawARGB(a, r, g, b);
     }
 
     static void drawColor__I(JNIEnv* env, jobject, jlong canvasHandle,
                              jint color) {
-        SkCanvas* canvas = reinterpret_cast<SkCanvas*>(canvasHandle);
+        SkCanvas* canvas = getNativeCanvas(canvasHandle);
         canvas->drawColor(color);
     }
 
     static void drawColor__II(JNIEnv* env, jobject, jlong canvasHandle,
                               jint color, jint modeHandle) {
-        SkCanvas* canvas = reinterpret_cast<SkCanvas*>(canvasHandle);
+        SkCanvas* canvas = getNativeCanvas(canvasHandle);
         SkPorterDuff::Mode mode = static_cast<SkPorterDuff::Mode>(modeHandle);
         canvas->drawColor(color, SkPorterDuff::ToXfermodeMode(mode));
     }
 
     static void drawPaint(JNIEnv* env, jobject, jlong canvasHandle,
                           jlong paintHandle) {
-        SkCanvas* canvas = reinterpret_cast<SkCanvas*>(canvasHandle);
+        SkCanvas* canvas = getNativeCanvas(canvasHandle);
         SkPaint* paint = reinterpret_cast<SkPaint*>(paintHandle);
         canvas->drawPaint(*paint);
     }
@@ -461,14 +504,14 @@
     static void drawLine__FFFFPaint(JNIEnv* env, jobject, jlong canvasHandle,
                                     jfloat startX, jfloat startY, jfloat stopX,
                                     jfloat stopY, jlong paintHandle) {
-        SkCanvas* canvas = reinterpret_cast<SkCanvas*>(canvasHandle);
+        SkCanvas* canvas = getNativeCanvas(canvasHandle);
         SkPaint* paint = reinterpret_cast<SkPaint*>(paintHandle);
         canvas->drawLine(startX, startY, stopX, stopY, *paint);
     }
 
     static void drawRect__RectFPaint(JNIEnv* env, jobject, jlong canvasHandle,
                                      jobject rect, jlong paintHandle) {
-        SkCanvas* canvas = reinterpret_cast<SkCanvas*>(canvasHandle);
+        SkCanvas* canvas = getNativeCanvas(canvasHandle);
         SkPaint* paint = reinterpret_cast<SkPaint*>(paintHandle);
         SkRect rect_;
         GraphicsJNI::jrectf_to_rect(env, rect, &rect_);
@@ -478,14 +521,14 @@
     static void drawRect__FFFFPaint(JNIEnv* env, jobject, jlong canvasHandle,
                                     jfloat left, jfloat top, jfloat right,
                                     jfloat bottom, jlong paintHandle) {
-        SkCanvas* canvas = reinterpret_cast<SkCanvas*>(canvasHandle);
+        SkCanvas* canvas = getNativeCanvas(canvasHandle);
         SkPaint* paint = reinterpret_cast<SkPaint*>(paintHandle);
         canvas->drawRectCoords(left, top, right, bottom, *paint);
     }
 
     static void drawOval(JNIEnv* env, jobject, jlong canvasHandle, jobject joval,
                          jlong paintHandle) {
-        SkCanvas* canvas = reinterpret_cast<SkCanvas*>(canvasHandle);
+        SkCanvas* canvas = getNativeCanvas(canvasHandle);
         SkPaint* paint = reinterpret_cast<SkPaint*>(paintHandle);
         SkRect oval;
         GraphicsJNI::jrectf_to_rect(env, joval, &oval);
@@ -494,7 +537,7 @@
 
     static void drawCircle(JNIEnv* env, jobject, jlong canvasHandle, jfloat cx,
                            jfloat cy, jfloat radius, jlong paintHandle) {
-        SkCanvas* canvas = reinterpret_cast<SkCanvas*>(canvasHandle);
+        SkCanvas* canvas = getNativeCanvas(canvasHandle);
         SkPaint* paint = reinterpret_cast<SkPaint*>(paintHandle);
         canvas->drawCircle(cx, cy, radius, *paint);
     }
@@ -502,7 +545,7 @@
     static void drawArc(JNIEnv* env, jobject, jlong canvasHandle, jobject joval,
                         jfloat startAngle, jfloat sweepAngle,
                         jboolean useCenter, jlong paintHandle) {
-        SkCanvas* canvas = reinterpret_cast<SkCanvas*>(canvasHandle);
+        SkCanvas* canvas = getNativeCanvas(canvasHandle);
         SkPaint* paint = reinterpret_cast<SkPaint*>(paintHandle);
         SkRect oval;
         GraphicsJNI::jrectf_to_rect(env, joval, &oval);
@@ -512,7 +555,7 @@
     static void drawRoundRect(JNIEnv* env, jobject, jlong canvasHandle,
             jfloat left, jfloat top, jfloat right, jfloat bottom, jfloat rx, jfloat ry,
             jlong paintHandle) {
-        SkCanvas* canvas = reinterpret_cast<SkCanvas*>(canvasHandle);
+        SkCanvas* canvas = getNativeCanvas(canvasHandle);
         SkPaint* paint = reinterpret_cast<SkPaint*>(paintHandle);
         SkRect rect = SkRect::MakeLTRB(left, top, right, bottom);
         canvas->drawRoundRect(rect, rx, ry, *paint);
@@ -520,7 +563,7 @@
 
     static void drawPath(JNIEnv* env, jobject, jlong canvasHandle, jlong pathHandle,
                          jlong paintHandle) {
-        SkCanvas* canvas = reinterpret_cast<SkCanvas*>(canvasHandle);
+        SkCanvas* canvas = getNativeCanvas(canvasHandle);
         SkPath* path = reinterpret_cast<SkPath*>(pathHandle);
         SkPaint* paint = reinterpret_cast<SkPaint*>(paintHandle);
         canvas->drawPath(*path, *paint);
@@ -531,7 +574,7 @@
                                           jfloat left, jfloat top,
                                           jlong paintHandle, jint canvasDensity,
                                           jint screenDensity, jint bitmapDensity) {
-        SkCanvas* canvas = reinterpret_cast<SkCanvas*>(canvasHandle);
+        SkCanvas* canvas = getNativeCanvas(canvasHandle);
         SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(bitmapHandle);
         SkPaint* paint = reinterpret_cast<SkPaint*>(paintHandle);
 
@@ -591,7 +634,7 @@
                              jlong bitmapHandle, jobject srcIRect,
                              jobject dstRectF, jlong paintHandle,
                              jint screenDensity, jint bitmapDensity) {
-        SkCanvas* canvas = reinterpret_cast<SkCanvas*>(canvasHandle);
+        SkCanvas* canvas = getNativeCanvas(canvasHandle);
         SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(bitmapHandle);
         SkPaint* paint = reinterpret_cast<SkPaint*>(paintHandle);
         SkRect      dst;
@@ -604,7 +647,7 @@
                              jlong bitmapHandle, jobject srcIRect,
                              jobject dstRect, jlong paintHandle,
                              jint screenDensity, jint bitmapDensity) {
-        SkCanvas* canvas = reinterpret_cast<SkCanvas*>(canvasHandle);
+        SkCanvas* canvas = getNativeCanvas(canvasHandle);
         SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(bitmapHandle);
         SkPaint* paint = reinterpret_cast<SkPaint*>(paintHandle);
         SkRect      dst;
@@ -616,9 +659,8 @@
     static void drawBitmapArray(JNIEnv* env, jobject, jlong canvasHandle,
                                 jintArray jcolors, jint offset, jint stride,
                                 jfloat x, jfloat y, jint width, jint height,
-                                jboolean hasAlpha, jlong paintHandle)
-    {
-        SkCanvas* canvas = reinterpret_cast<SkCanvas*>(canvasHandle);
+                                jboolean hasAlpha, jlong paintHandle) {
+        SkCanvas* canvas = getNativeCanvas(canvasHandle);
         SkPaint* paint = reinterpret_cast<SkPaint*>(paintHandle);
         SkBitmap    bitmap;
         bitmap.setConfig(hasAlpha ? SkBitmap::kARGB_8888_Config :
@@ -638,7 +680,7 @@
     static void drawBitmapMatrix(JNIEnv* env, jobject, jlong canvasHandle,
                                  jlong bitmapHandle, jlong matrixHandle,
                                  jlong paintHandle) {
-        SkCanvas* canvas = reinterpret_cast<SkCanvas*>(canvasHandle);
+        SkCanvas* canvas = getNativeCanvas(canvasHandle);
         const SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(bitmapHandle);
         const SkMatrix* matrix = reinterpret_cast<SkMatrix*>(matrixHandle);
         const SkPaint* paint = reinterpret_cast<SkPaint*>(paintHandle);
@@ -649,7 +691,7 @@
                           jlong bitmapHandle, jint meshWidth, jint meshHeight,
                           jfloatArray jverts, jint vertIndex, jintArray jcolors,
                           jint colorIndex, jlong paintHandle) {
-        SkCanvas* canvas = reinterpret_cast<SkCanvas*>(canvasHandle);
+        SkCanvas* canvas = getNativeCanvas(canvasHandle);
         const SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(bitmapHandle);
         const SkPaint* paint = reinterpret_cast<SkPaint*>(paintHandle);
 
@@ -759,7 +801,7 @@
                              jintArray jcolors, jint colorIndex,
                              jshortArray jindices, jint indexIndex,
                              jint indexCount, jlong paintHandle) {
-        SkCanvas* canvas = reinterpret_cast<SkCanvas*>(canvasHandle);
+        SkCanvas* canvas = getNativeCanvas(canvasHandle);
         SkCanvas::VertexMode mode = static_cast<SkCanvas::VertexMode>(modeHandle);
         const SkPaint* paint = reinterpret_cast<SkPaint*>(paintHandle);
 
@@ -799,7 +841,7 @@
                                                jcharArray text, jint index, jint count,
                                                jfloat x, jfloat y, jint flags,
                                                jlong paintHandle, jlong typefaceHandle) {
-        SkCanvas* canvas = reinterpret_cast<SkCanvas*>(canvasHandle);
+        SkCanvas* canvas = getNativeCanvas(canvasHandle);
         SkPaint* paint = reinterpret_cast<SkPaint*>(paintHandle);
         TypefaceImpl* typeface = reinterpret_cast<TypefaceImpl*>(typefaceHandle);
         jchar* textArray = env->GetCharArrayElements(text, NULL);
@@ -812,7 +854,7 @@
                                                    jint start, jint end,
                                                    jfloat x, jfloat y, jint flags,
                                                    jlong paintHandle, jlong typefaceHandle) {
-        SkCanvas* canvas = reinterpret_cast<SkCanvas*>(canvasHandle);
+        SkCanvas* canvas = getNativeCanvas(canvasHandle);
         SkPaint* paint = reinterpret_cast<SkPaint*>(paintHandle);
         TypefaceImpl* typeface = reinterpret_cast<TypefaceImpl*>(typefaceHandle);
         const jchar* textArray = env->GetStringChars(text, NULL);
@@ -939,10 +981,10 @@
     }
 
     static void drawTextRun___CIIIIFFIPaintTypeface(
-        JNIEnv* env, jobject, jlong canvasHandle, jcharArray text, jint index,
-        jint count, jint contextIndex, jint contextCount,
-        jfloat x, jfloat y, jint dirFlags, jlong paintHandle, jlong typefaceHandle) {
-        SkCanvas* canvas = reinterpret_cast<SkCanvas*>(canvasHandle);
+            JNIEnv* env, jobject, jlong canvasHandle, jcharArray text, jint index,
+            jint count, jint contextIndex, jint contextCount,
+            jfloat x, jfloat y, jint dirFlags, jlong paintHandle, jlong typefaceHandle) {
+        SkCanvas* canvas = getNativeCanvas(canvasHandle);
         SkPaint* paint = reinterpret_cast<SkPaint*>(paintHandle);
         TypefaceImpl* typeface = reinterpret_cast<TypefaceImpl*>(typefaceHandle);
 
@@ -953,10 +995,10 @@
     }
 
     static void drawTextRun__StringIIIIFFIPaintTypeface(
-        JNIEnv* env, jobject obj, jlong canvasHandle, jstring text, jint start,
-        jint end, jint contextStart, jint contextEnd,
-        jfloat x, jfloat y, jint dirFlags, jlong paintHandle, jlong typefaceHandle) {
-        SkCanvas* canvas = reinterpret_cast<SkCanvas*>(canvasHandle);
+            JNIEnv* env, jobject obj, jlong canvasHandle, jstring text, jint start,
+            jint end, jint contextStart, jint contextEnd,
+            jfloat x, jfloat y, jint dirFlags, jlong paintHandle, jlong typefaceHandle) {
+        SkCanvas* canvas = getNativeCanvas(canvasHandle);
         SkPaint* paint = reinterpret_cast<SkPaint*>(paintHandle);
         TypefaceImpl* typeface = reinterpret_cast<TypefaceImpl*>(typefaceHandle);
 
@@ -971,7 +1013,7 @@
     static void drawPosText___CII_FPaint(JNIEnv* env, jobject, jlong canvasHandle,
                                          jcharArray text, jint index, jint count,
                                          jfloatArray pos, jlong paintHandle) {
-        SkCanvas* canvas = reinterpret_cast<SkCanvas*>(canvasHandle);
+        SkCanvas* canvas = getNativeCanvas(canvasHandle);
         SkPaint* paint = reinterpret_cast<SkPaint*>(paintHandle);
         jchar* textArray = text ? env->GetCharArrayElements(text, NULL) : NULL;
         jsize textCount = text ? env->GetArrayLength(text) : NULL;
@@ -1002,7 +1044,7 @@
                                            jlong canvasHandle, jstring text,
                                            jfloatArray pos,
                                            jlong paintHandle) {
-        SkCanvas* canvas = reinterpret_cast<SkCanvas*>(canvasHandle);
+        SkCanvas* canvas = getNativeCanvas(canvasHandle);
         SkPaint* paint = reinterpret_cast<SkPaint*>(paintHandle);
         const void* text_ = text ? env->GetStringChars(text, NULL) : NULL;
         int byteLength = text ? env->GetStringLength(text) : 0;
@@ -1032,7 +1074,7 @@
     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) {
-        SkCanvas* canvas = reinterpret_cast<SkCanvas*>(canvasHandle);
+        SkCanvas* canvas = getNativeCanvas(canvasHandle);
         SkPath* path = reinterpret_cast<SkPath*>(pathHandle);
         SkPaint* paint = reinterpret_cast<SkPaint*>(paintHandle);
 
@@ -1045,7 +1087,7 @@
     static void drawTextOnPath__StringPathFFPaint(JNIEnv* env, jobject,
             jlong canvasHandle, jstring text, jlong pathHandle,
             jfloat hOffset, jfloat vOffset, jint bidiFlags, jlong paintHandle) {
-        SkCanvas* canvas = reinterpret_cast<SkCanvas*>(canvasHandle);
+        SkCanvas* canvas = getNativeCanvas(canvasHandle);
         SkPath* path = reinterpret_cast<SkPath*>(pathHandle);
         SkPaint* paint = reinterpret_cast<SkPaint*>(paintHandle);
         const jchar* text_ = env->GetStringChars(text, NULL);
@@ -1084,7 +1126,7 @@
 
     static jboolean getClipBounds(JNIEnv* env, jobject, jlong canvasHandle,
                                   jobject bounds) {
-        SkCanvas* canvas = reinterpret_cast<SkCanvas*>(canvasHandle);
+        SkCanvas* canvas = getNativeCanvas(canvasHandle);
         SkRect   r;
         SkIRect ir;
         bool result = getHardClipBounds(canvas, &r);
@@ -1100,7 +1142,7 @@
 
     static void getCTM(JNIEnv* env, jobject, jlong canvasHandle,
                        jlong matrixHandle) {
-        SkCanvas* canvas = reinterpret_cast<SkCanvas*>(canvasHandle);
+        SkCanvas* canvas = getNativeCanvas(canvasHandle);
         SkMatrix* matrix = reinterpret_cast<SkMatrix*>(matrixHandle);
         *matrix = canvas->getTotalMatrix();
     }
@@ -1108,8 +1150,9 @@
 
 static JNINativeMethod gCanvasMethods[] = {
     {"finalizer", "(J)V", (void*) SkCanvasGlue::finalizer},
-    {"initRaster","(J)J", (void*) SkCanvasGlue::initRaster},
-    {"copyNativeCanvasState","(JJ)V", (void*) SkCanvasGlue::copyCanvasState},
+    {"initRaster", "(J)J", (void*) SkCanvasGlue::initRaster},
+    {"initCanvas", "(J)J", (void*) SkCanvasGlue::initCanvas},
+    {"native_setBitmap", "(JJZ)V", (void*) SkCanvasGlue::setBitmap},
     {"isOpaque","()Z", (void*) SkCanvasGlue::isOpaque},
     {"getWidth","()I", (void*) SkCanvasGlue::getWidth},
     {"getHeight","()I", (void*) SkCanvasGlue::getHeight},
@@ -1224,4 +1267,11 @@
     return result;
 }
 
+} // namespace android
+
+// GraphicsJNI helper for external clients.
+// We keep the implementation here to avoid exposing NativeCanvasWrapper
+// externally.
+SkCanvas* GraphicsJNI::getNativeCanvas(jlong nativeHandle) {
+    return android::SkCanvasGlue::getNativeCanvas(nativeHandle);
 }
diff --git a/core/jni/android/graphics/DrawFilter.cpp b/core/jni/android/graphics/DrawFilter.cpp
index fbfa2ec..3275875 100644
--- a/core/jni/android/graphics/DrawFilter.cpp
+++ b/core/jni/android/graphics/DrawFilter.cpp
@@ -30,6 +30,35 @@
 
 namespace android {
 
+// Custom version of SkPaintFlagsDrawFilter that also calls setFilterLevel.
+class CompatFlagsDrawFilter : public SkPaintFlagsDrawFilter {
+public:
+    CompatFlagsDrawFilter(uint32_t clearFlags, uint32_t setFlags,
+            SkPaint::FilterLevel desiredLevel)
+    : SkPaintFlagsDrawFilter(clearFlags, setFlags)
+    , fDesiredLevel(desiredLevel) {
+    }
+
+    virtual bool filter(SkPaint* paint, Type type) {
+        SkPaintFlagsDrawFilter::filter(paint, type);
+        paint->setFilterLevel(fDesiredLevel);
+        return true;
+    }
+
+private:
+    const SkPaint::FilterLevel fDesiredLevel;
+};
+
+// Returns whether flags contains FILTER_BITMAP_FLAG. If flags does, remove it.
+static inline bool hadFiltering(jint& flags) {
+    // Equivalent to the Java Paint's FILTER_BITMAP_FLAG.
+    static const uint32_t sFilterBitmapFlag = 0x02;
+
+    const bool result = (flags & sFilterBitmapFlag) != 0;
+    flags &= ~sFilterBitmapFlag;
+    return result;
+}
+
 class SkDrawFilterGlue {
 public:
 
@@ -40,12 +69,25 @@
 
     static jlong CreatePaintFlagsDF(JNIEnv* env, jobject clazz,
                                     jint clearFlags, jint setFlags) {
-        // trim off any out-of-range bits
-        clearFlags &= SkPaint::kAllFlags;
-        setFlags &= SkPaint::kAllFlags;
-
         if (clearFlags | setFlags) {
-            SkDrawFilter* filter = new SkPaintFlagsDrawFilter(clearFlags, setFlags);
+            // Mask both groups of flags to remove FILTER_BITMAP_FLAG, which no
+            // longer has a Skia equivalent flag (instead it corresponds to
+            // calling setFilterLevel), and keep track of which group(s), if
+            // any, had the flag set.
+            const bool turnFilteringOn = hadFiltering(setFlags);
+            const bool turnFilteringOff = hadFiltering(clearFlags);
+
+            SkDrawFilter* filter;
+            if (turnFilteringOn) {
+                // Turning filtering on overrides turning it off.
+                filter = new CompatFlagsDrawFilter(clearFlags, setFlags,
+                        SkPaint::kLow_FilterLevel);
+            } else if (turnFilteringOff) {
+                filter = new CompatFlagsDrawFilter(clearFlags, setFlags,
+                        SkPaint::kNone_FilterLevel);
+            } else {
+                filter = new SkPaintFlagsDrawFilter(clearFlags, setFlags);
+            }
             return reinterpret_cast<jlong>(filter);
         } else {
             return NULL;
diff --git a/core/jni/android/graphics/FontFamily.cpp b/core/jni/android/graphics/FontFamily.cpp
index 041790f..dd6b36f 100644
--- a/core/jni/android/graphics/FontFamily.cpp
+++ b/core/jni/android/graphics/FontFamily.cpp
@@ -23,6 +23,9 @@
 #include "GraphicsJNI.h"
 #include <ScopedPrimitiveArray.h>
 #include <ScopedUtfChars.h>
+#include <android_runtime/android_util_AssetManager.h>
+#include <androidfw/AssetManager.h>
+#include "Utils.h"
 
 #ifdef USE_MINIKIN
 #include <minikin/FontFamily.h>
@@ -31,9 +34,14 @@
 
 namespace android {
 
-static jlong FontFamily_create(JNIEnv* env, jobject clazz) {
+static jlong FontFamily_create(JNIEnv* env, jobject clazz, jstring lang, jint variant) {
 #ifdef USE_MINIKIN
-    return (jlong)new FontFamily();
+    FontLanguage fontLanguage;
+    if (lang != NULL) {
+        ScopedUtfChars str(env, lang);
+        fontLanguage = FontLanguage(str.c_str(), str.size());
+    }
+    return (jlong)new FontFamily(fontLanguage, variant);
 #else
     return 0;
 #endif
@@ -46,19 +54,60 @@
 #endif
 }
 
+#ifdef USE_MINIKIN
+static jboolean addSkTypeface(FontFamily* family, SkTypeface* face) {
+    MinikinFont* minikinFont = new MinikinFontSkia(face);
+    bool result = family->addFont(minikinFont);
+    minikinFont->Unref();
+    return result;
+}
+#endif
+
 static jboolean FontFamily_addFont(JNIEnv* env, jobject clazz, jlong familyPtr, jstring path) {
 #ifdef USE_MINIKIN
     NPE_CHECK_RETURN_ZERO(env, path);
     ScopedUtfChars str(env, path);
-    ALOGD("addFont %s", str.c_str());
     SkTypeface* face = SkTypeface::CreateFromFile(str.c_str());
     if (face == NULL) {
         ALOGE("addFont failed to create font %s", str.c_str());
         return false;
     }
-    MinikinFont* minikinFont = new MinikinFontSkia(face);
     FontFamily* fontFamily = (FontFamily*)familyPtr;
-    return fontFamily->addFont(minikinFont);
+    return addSkTypeface(fontFamily, face);
+#else
+    return false;
+#endif
+}
+
+static jboolean FontFamily_addFontFromAsset(JNIEnv* env, jobject, jlong familyPtr,
+        jobject jassetMgr, jstring jpath) {
+#ifdef USE_MINIKIN
+    NPE_CHECK_RETURN_ZERO(env, jassetMgr);
+    NPE_CHECK_RETURN_ZERO(env, jpath);
+
+    AssetManager* mgr = assetManagerForJavaObject(env, jassetMgr);
+    if (NULL == mgr) {
+        return false;
+    }
+
+    ScopedUtfChars str(env, jpath);
+    Asset* asset = mgr->open(str.c_str(), Asset::ACCESS_BUFFER);
+    if (NULL == asset) {
+        return false;
+    }
+
+    SkStream* stream = new AssetStreamAdaptor(asset,
+                                              AssetStreamAdaptor::kYes_OwnAsset,
+                                              AssetStreamAdaptor::kYes_HasMemoryBase);
+    SkTypeface* face = SkTypeface::CreateFromStream(stream);
+    // Note: SkTypeface::CreateFromStream holds its own reference to the stream
+    stream->unref();
+    if (face == NULL) {
+        ALOGE("addFontFromAsset failed to create font %s", str.c_str());
+        return false;
+    }
+    FontFamily* fontFamily = (FontFamily*)familyPtr;
+    return addSkTypeface(fontFamily, face);
 #else
     return false;
 #endif
@@ -67,9 +116,11 @@
 ///////////////////////////////////////////////////////////////////////////////
 
 static JNINativeMethod gFontFamilyMethods[] = {
-    { "nCreateFamily",            "()J", (void*)FontFamily_create },
+    { "nCreateFamily",            "(Ljava/lang/String;I)J", (void*)FontFamily_create },
     { "nUnrefFamily",             "(J)V", (void*)FontFamily_unref },
     { "nAddFont",                 "(JLjava/lang/String;)Z", (void*)FontFamily_addFont },
+    { "nAddFontFromAsset",        "(JLandroid/content/res/AssetManager;Ljava/lang/String;)Z",
+                                           (void*)FontFamily_addFontFromAsset },
 };
 
 int register_android_graphics_FontFamily(JNIEnv* env)
diff --git a/core/jni/android/graphics/Graphics.cpp b/core/jni/android/graphics/Graphics.cpp
index dce185d..64ad223 100644
--- a/core/jni/android/graphics/Graphics.cpp
+++ b/core/jni/android/graphics/Graphics.cpp
@@ -320,7 +320,7 @@
     SkASSERT(canvas);
     SkASSERT(env->IsInstanceOf(canvas, gCanvas_class));
     jlong canvasHandle = env->GetLongField(canvas, gCanvas_nativeInstanceID);
-    SkCanvas* c = reinterpret_cast<SkCanvas*>(canvasHandle);
+    SkCanvas* c = getNativeCanvas(canvasHandle);
     SkASSERT(c);
     return c;
 }
@@ -698,7 +698,7 @@
                                                      "nativeInt", "I");
 
     gCanvas_class = make_globalref(env, "android/graphics/Canvas");
-    gCanvas_nativeInstanceID = getFieldIDCheck(env, gCanvas_class, "mNativeCanvas", "J");
+    gCanvas_nativeInstanceID = getFieldIDCheck(env, gCanvas_class, "mNativeCanvasWrapper", "J");
 
     gPaint_class = make_globalref(env, "android/graphics/Paint");
     gPaint_nativeInstanceID = getFieldIDCheck(env, gPaint_class, "mNativePaint", "J");
diff --git a/core/jni/android/graphics/GraphicsJNI.h b/core/jni/android/graphics/GraphicsJNI.h
index db7b6d9..73dd11b 100644
--- a/core/jni/android/graphics/GraphicsJNI.h
+++ b/core/jni/android/graphics/GraphicsJNI.h
@@ -45,6 +45,7 @@
     static SkPoint* jpointf_to_point(JNIEnv*, jobject jpointf, SkPoint* point);
     static void point_to_jpointf(const SkPoint& point, JNIEnv*, jobject jpointf);
 
+    static SkCanvas* getNativeCanvas(jlong nativeHandle);
     static SkCanvas* getNativeCanvas(JNIEnv*, jobject canvas);
     static SkPaint*  getNativePaint(JNIEnv*, jobject paint);
     static android::TypefaceImpl* getNativeTypeface(JNIEnv*, jobject paint);
diff --git a/core/jni/android/graphics/MaskFilter.cpp b/core/jni/android/graphics/MaskFilter.cpp
index 2113330..b394905 100644
--- a/core/jni/android/graphics/MaskFilter.cpp
+++ b/core/jni/android/graphics/MaskFilter.cpp
@@ -21,8 +21,7 @@
 
     static jlong createBlur(JNIEnv* env, jobject, jfloat radius, jint blurStyle) {
         SkScalar sigma = SkBlurMask::ConvertRadiusToSigma(radius);
-        SkMaskFilter* filter = SkBlurMaskFilter::Create(
-                (SkBlurMaskFilter::BlurStyle)blurStyle, sigma);
+        SkMaskFilter* filter = SkBlurMaskFilter::Create((SkBlurStyle)blurStyle, sigma);
         ThrowIAE_IfNull(env, filter);
         return reinterpret_cast<jlong>(filter);
     }
diff --git a/core/jni/android/graphics/MinikinSkia.h b/core/jni/android/graphics/MinikinSkia.h
index 7a8954d..1cc2c51 100644
--- a/core/jni/android/graphics/MinikinSkia.h
+++ b/core/jni/android/graphics/MinikinSkia.h
@@ -18,6 +18,7 @@
 
 class MinikinFontSkia : public MinikinFont {
 public:
+    // Note: this takes ownership of the reference (will unref on dtor)
     explicit MinikinFontSkia(SkTypeface *typeface);
 
     ~MinikinFontSkia();
diff --git a/core/jni/android/graphics/MinikinUtils.cpp b/core/jni/android/graphics/MinikinUtils.cpp
index ee04d6f..79381ad 100644
--- a/core/jni/android/graphics/MinikinUtils.cpp
+++ b/core/jni/android/graphics/MinikinUtils.cpp
@@ -28,11 +28,17 @@
     layout->setFontCollection(resolvedFace->fFontCollection);
     FontStyle style = resolvedFace->fStyle;
     char css[256];
-    sprintf(css, "font-size: %d; font-weight: %d; font-style: %s; -minikin-bidi: %d",
+    int off = snprintf(css, sizeof(css),
+        "font-size: %d; font-weight: %d; font-style: %s; -minikin-bidi: %d;",
         (int)paint->getTextSize(),
         style.getWeight() * 100,
         style.getItalic() ? "italic" : "normal",
         flags);
+    SkString langString = paint->getPaintOptionsAndroid().getLanguage().getTag();
+    off += snprintf(css + off, sizeof(css) - off, " lang: %s;", langString.c_str());
+    SkPaintOptionsAndroid::FontVariant var = paint->getPaintOptionsAndroid().getFontVariant();
+    const char* varstr = var == SkPaintOptionsAndroid::kElegant_Variant ? "elegant" : "compact";
+    off += snprintf(css + off, sizeof(css) - off, " -minikin-variant: %s;", varstr);
     layout->setProperties(css);
 }
 
diff --git a/core/jni/android/graphics/NinePatch.cpp b/core/jni/android/graphics/NinePatch.cpp
index 855d267..ab5bdb0 100644
--- a/core/jni/android/graphics/NinePatch.cpp
+++ b/core/jni/android/graphics/NinePatch.cpp
@@ -119,7 +119,7 @@
     static void drawF(JNIEnv* env, jobject, jlong canvasHandle, jobject boundsRectF,
             jlong bitmapHandle, jlong chunkHandle, jlong paintHandle,
             jint destDensity, jint srcDensity) {
-        SkCanvas* canvas       = reinterpret_cast<SkCanvas*>(canvasHandle);
+        SkCanvas* canvas       = GraphicsJNI::getNativeCanvas(canvasHandle);
         const SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(bitmapHandle);
         Res_png_9patch* chunk  = reinterpret_cast<Res_png_9patch*>(chunkHandle);
         const SkPaint* paint   = reinterpret_cast<SkPaint*>(paintHandle);
@@ -138,7 +138,7 @@
     static void drawI(JNIEnv* env, jobject, jlong canvasHandle, jobject boundsRect,
             jlong bitmapHandle, jlong chunkHandle, jlong paintHandle,
             jint destDensity, jint srcDensity) {
-        SkCanvas* canvas       = reinterpret_cast<SkCanvas*>(canvasHandle);
+        SkCanvas* canvas       = GraphicsJNI::getNativeCanvas(canvasHandle);
         const SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(bitmapHandle);
         Res_png_9patch* chunk  = reinterpret_cast<Res_png_9patch*>(chunkHandle);
         const SkPaint* paint   = reinterpret_cast<SkPaint*>(paintHandle);
diff --git a/core/jni/android/graphics/Picture.cpp b/core/jni/android/graphics/Picture.cpp
index bac8ef7..a8a3dae 100644
--- a/core/jni/android/graphics/Picture.cpp
+++ b/core/jni/android/graphics/Picture.cpp
@@ -56,7 +56,7 @@
 
     static void draw(JNIEnv* env, jobject, jlong canvasHandle,
                             jlong pictureHandle) {
-        SkCanvas* canvas = reinterpret_cast<SkCanvas*>(canvasHandle);
+        SkCanvas* canvas = GraphicsJNI::getNativeCanvas(canvasHandle);
         SkPicture* picture = reinterpret_cast<SkPicture*>(pictureHandle);
         SkASSERT(canvas);
         SkASSERT(picture);
diff --git a/core/jni/android/graphics/TypefaceImpl.cpp b/core/jni/android/graphics/TypefaceImpl.cpp
index 958cd85..ff52b07 100644
--- a/core/jni/android/graphics/TypefaceImpl.cpp
+++ b/core/jni/android/graphics/TypefaceImpl.cpp
@@ -171,7 +171,9 @@
 }
 
 void TypefaceImpl_unref(TypefaceImpl* face) {
-    face->fFontCollection->Unref();
+    if (face != NULL) {
+        face->fFontCollection->Unref();
+    }
     delete face;
 }
 
diff --git a/core/jni/android_hardware_SensorManager.cpp b/core/jni/android_hardware_SensorManager.cpp
index fcf8f83..ddcc396 100644
--- a/core/jni/android_hardware_SensorManager.cpp
+++ b/core/jni/android_hardware_SensorManager.cpp
@@ -51,6 +51,8 @@
     jfieldID    fifoMaxEventCount;
     jfieldID    stringType;
     jfieldID    requiredPermission;
+    jfieldID    maxDelay;
+    jfieldID    isWakeUpSensor;
 } gSensorOffsets;
 
 
@@ -78,6 +80,8 @@
     sensorOffsets.stringType = _env->GetFieldID(sensorClass, "mStringType", "Ljava/lang/String;");
     sensorOffsets.requiredPermission = _env->GetFieldID(sensorClass, "mRequiredPermission",
                                                         "Ljava/lang/String;");
+    sensorOffsets.maxDelay    = _env->GetFieldID(sensorClass, "mMaxDelay",  "I");
+    sensorOffsets.isWakeUpSensor = _env->GetFieldID(sensorClass, "mWakeUpSensor",  "Z");
 }
 
 static jint
@@ -112,6 +116,8 @@
     env->SetObjectField(sensor, sensorOffsets.stringType, stringType);
     env->SetObjectField(sensor, sensorOffsets.requiredPermission,
                         requiredPermission);
+    env->SetIntField(sensor, sensorOffsets.maxDelay, list->getMaxDelay());
+    env->SetBooleanField(sensor, sensorOffsets.isWakeUpSensor, list->isWakeUpSensor());
     next++;
     return size_t(next) < count ? next : 0;
 }
@@ -160,7 +166,8 @@
         ASensorEvent buffer[16];
         while ((n = q->read(buffer, 16)) > 0) {
             for (int i=0 ; i<n ; i++) {
-                if (buffer[i].type == SENSOR_TYPE_STEP_COUNTER) {
+                if (buffer[i].type == SENSOR_TYPE_STEP_COUNTER ||
+                    buffer[i].type == SENSOR_TYPE_WAKE_UP_STEP_COUNTER) {
                     // step-counter returns a uint64, but the java API only deals with floats
                     float value = float(buffer[i].u64.step_counter);
                     env->SetFloatArrayRegion(mScratch, 0, 1, &value);
@@ -175,11 +182,26 @@
                                         gBaseEventQueueClassInfo.dispatchFlushCompleteEvent,
                                         buffer[i].meta_data.sensor);
                 } else {
+                    int8_t status;
+                    switch (buffer[i].type) {
+                    case SENSOR_TYPE_ORIENTATION:
+                    case SENSOR_TYPE_MAGNETIC_FIELD:
+                    case SENSOR_TYPE_ACCELEROMETER:
+                    case SENSOR_TYPE_GYROSCOPE:
+                        status = buffer[i].vector.status;
+                        break;
+                    case SENSOR_TYPE_HEART_RATE:
+                        status = buffer[i].heart_rate.status;
+                        break;
+                    default:
+                        status = SENSOR_STATUS_ACCURACY_HIGH;
+                        break;
+                    }
                     env->CallVoidMethod(mReceiverObject,
                                         gBaseEventQueueClassInfo.dispatchSensorEvent,
                                         buffer[i].sensor,
                                         mScratch,
-                                        buffer[i].vector.status,
+                                        status,
                                         buffer[i].timestamp);
                 }
                 if (env->ExceptionCheck()) {
diff --git a/core/jni/android_media_AudioFormat.h b/core/jni/android_media_AudioFormat.h
index fedb1b2..a2b1ed9 100644
--- a/core/jni/android_media_AudioFormat.h
+++ b/core/jni/android_media_AudioFormat.h
@@ -23,6 +23,11 @@
 #define ENCODING_PCM_16BIT 2
 #define ENCODING_PCM_8BIT  3
 #define ENCODING_PCM_FLOAT 4
+#define ENCODING_INVALID 0
+#define ENCODING_DEFAULT 1
+
+#define CHANNEL_INVALID 0
+#define CHANNEL_OUT_DEFAULT 1
 
 static inline audio_format_t audioFormatToNative(int audioFormat)
 {
@@ -33,9 +38,58 @@
         return AUDIO_FORMAT_PCM_8_BIT;
     case ENCODING_PCM_FLOAT:
         return AUDIO_FORMAT_PCM_FLOAT;
+    case ENCODING_DEFAULT:
+        return AUDIO_FORMAT_DEFAULT;
     default:
         return AUDIO_FORMAT_INVALID;
     }
 }
 
+static inline int audioFormatFromNative(audio_format_t nativeFormat)
+{
+    switch (nativeFormat) {
+    case AUDIO_FORMAT_PCM_16_BIT:
+        return ENCODING_PCM_16BIT;
+    case AUDIO_FORMAT_PCM_8_BIT:
+        return ENCODING_PCM_8BIT;
+    case AUDIO_FORMAT_PCM_FLOAT:
+        return ENCODING_PCM_FLOAT;
+    case AUDIO_FORMAT_DEFAULT:
+        return ENCODING_DEFAULT;
+    default:
+        return ENCODING_INVALID;
+    }
+}
+
+static inline audio_channel_mask_t outChannelMaskToNative(int channelMask)
+{
+    switch (channelMask) {
+    case CHANNEL_OUT_DEFAULT:
+    case CHANNEL_INVALID:
+        return AUDIO_CHANNEL_NONE;
+    default:
+        return (audio_channel_mask_t)(channelMask>>2);
+    }
+}
+
+static inline int outChannelMaskFromNative(audio_channel_mask_t nativeMask)
+{
+    switch (nativeMask) {
+    case AUDIO_CHANNEL_NONE:
+        return CHANNEL_OUT_DEFAULT;
+    default:
+        return (int)nativeMask<<2;
+    }
+}
+
+static inline audio_channel_mask_t inChannelMaskToNative(int channelMask)
+{
+    return (audio_channel_mask_t)channelMask;
+}
+
+static inline int inChannelMaskFromNative(audio_channel_mask_t nativeMask)
+{
+    return (int)nativeMask;
+}
+
 #endif // ANDROID_MEDIA_AUDIOFORMAT_H
diff --git a/core/jni/android_media_AudioSystem.cpp b/core/jni/android_media_AudioSystem.cpp
index a9a62f8..0f7e140 100644
--- a/core/jni/android_media_AudioSystem.cpp
+++ b/core/jni/android_media_AudioSystem.cpp
@@ -15,7 +15,9 @@
 ** limitations under the License.
 */
 
-#define LOG_TAG "AudioSystem"
+//#define LOG_NDEBUG 0
+
+#define LOG_TAG "AudioSystem-JNI"
 #include <utils/Log.h>
 
 #include <jni.h>
@@ -26,6 +28,8 @@
 
 #include <system/audio.h>
 #include <system/audio_policy.h>
+#include "android_media_AudioFormat.h"
+#include "android_media_AudioErrors.h"
 
 // ----------------------------------------------------------------------------
 
@@ -33,12 +37,160 @@
 
 static const char* const kClassPathName = "android/media/AudioSystem";
 
+static jclass gArrayListClass;
+static struct {
+    jmethodID    add;
+} gArrayListMethods;
+
+static jclass gAudioHandleClass;
+static jmethodID gAudioHandleCstor;
+static struct {
+    jfieldID    mId;
+} gAudioHandleFields;
+
+static jclass gAudioPortClass;
+static jmethodID gAudioPortCstor;
+static struct {
+    jfieldID    mHandle;
+    jfieldID    mRole;
+    jfieldID    mGains;
+    jfieldID    mActiveConfig;
+    // other fields unused by JNI
+} gAudioPortFields;
+
+static jclass gAudioPortConfigClass;
+static jmethodID gAudioPortConfigCstor;
+static struct {
+    jfieldID    mPort;
+    jfieldID    mSamplingRate;
+    jfieldID    mChannelMask;
+    jfieldID    mFormat;
+    jfieldID    mGain;
+    jfieldID    mConfigMask;
+} gAudioPortConfigFields;
+
+static jclass gAudioDevicePortClass;
+static jmethodID gAudioDevicePortCstor;
+
+static jclass gAudioDevicePortConfigClass;
+static jmethodID gAudioDevicePortConfigCstor;
+
+static jclass gAudioMixPortClass;
+static jmethodID gAudioMixPortCstor;
+
+static jclass gAudioMixPortConfigClass;
+static jmethodID gAudioMixPortConfigCstor;
+
+static jclass gAudioGainClass;
+static jmethodID gAudioGainCstor;
+
+static jclass gAudioGainConfigClass;
+static jmethodID gAudioGainConfigCstor;
+static struct {
+    jfieldID mIndex;
+    jfieldID mMode;
+    jfieldID mChannelMask;
+    jfieldID mValues;
+    jfieldID mRampDurationMs;
+    // other fields unused by JNI
+} gAudioGainConfigFields;
+
+static jclass gAudioPatchClass;
+static jmethodID gAudioPatchCstor;
+static struct {
+    jfieldID    mHandle;
+    // other fields unused by JNI
+} gAudioPatchFields;
+
+static const char* const kEventHandlerClassPathName =
+        "android/media/AudioPortEventHandler";
+static jmethodID gPostEventFromNative;
+
 enum AudioError {
     kAudioStatusOk = 0,
     kAudioStatusError = 1,
     kAudioStatusMediaServerDied = 100
 };
 
+enum  {
+    AUDIOPORT_EVENT_PORT_LIST_UPDATED = 1,
+    AUDIOPORT_EVENT_PATCH_LIST_UPDATED = 2,
+    AUDIOPORT_EVENT_SERVICE_DIED = 3,
+};
+
+#define MAX_PORT_GENERATION_SYNC_ATTEMPTS 5
+
+// ----------------------------------------------------------------------------
+// ref-counted object for callbacks
+class JNIAudioPortCallback: public AudioSystem::AudioPortCallback
+{
+public:
+    JNIAudioPortCallback(JNIEnv* env, jobject thiz, jobject weak_thiz);
+    ~JNIAudioPortCallback();
+
+    virtual void onAudioPortListUpdate();
+    virtual void onAudioPatchListUpdate();
+    virtual void onServiceDied();
+
+private:
+    void sendEvent(int event);
+
+    jclass      mClass;     // Reference to AudioPortEventHandlerDelegate class
+    jobject     mObject;    // Weak ref to AudioPortEventHandlerDelegate Java object to call on
+};
+
+JNIAudioPortCallback::JNIAudioPortCallback(JNIEnv* env, jobject thiz, jobject weak_thiz)
+{
+
+    // Hold onto the SoundTriggerModule class for use in calling the static method
+    // that posts events to the application thread.
+    jclass clazz = env->GetObjectClass(thiz);
+    if (clazz == NULL) {
+        ALOGE("Can't find class %s", kEventHandlerClassPathName);
+        return;
+    }
+    mClass = (jclass)env->NewGlobalRef(clazz);
+
+    // We use a weak reference so the SoundTriggerModule object can be garbage collected.
+    // The reference is only used as a proxy for callbacks.
+    mObject  = env->NewGlobalRef(weak_thiz);
+}
+
+JNIAudioPortCallback::~JNIAudioPortCallback()
+{
+    // remove global references
+    JNIEnv *env = AndroidRuntime::getJNIEnv();
+    env->DeleteGlobalRef(mObject);
+    env->DeleteGlobalRef(mClass);
+}
+
+void JNIAudioPortCallback::sendEvent(int event)
+{
+    JNIEnv *env = AndroidRuntime::getJNIEnv();
+
+    env->CallStaticVoidMethod(mClass, gPostEventFromNative, mObject,
+                              event, 0, 0, NULL);
+    if (env->ExceptionCheck()) {
+        ALOGW("An exception occurred while notifying an event.");
+        env->ExceptionClear();
+    }
+}
+
+void JNIAudioPortCallback::onAudioPortListUpdate()
+{
+    sendEvent(AUDIOPORT_EVENT_PORT_LIST_UPDATED);
+}
+
+void JNIAudioPortCallback::onAudioPatchListUpdate()
+{
+    sendEvent(AUDIOPORT_EVENT_PATCH_LIST_UPDATED);
+}
+
+void JNIAudioPortCallback::onServiceDied()
+{
+    sendEvent(AUDIOPORT_EVENT_SERVICE_DIED);
+}
+
 static int check_AudioSystem_Command(status_t status)
 {
     switch (status) {
@@ -281,6 +433,854 @@
     return (jint) check_AudioSystem_Command(AudioSystem::checkAudioFlinger());
 }
 
+
+static bool useInChannelMask(audio_port_type_t type, audio_port_role_t role)
+{
+    return ((type == AUDIO_PORT_TYPE_DEVICE) && (role == AUDIO_PORT_ROLE_SOURCE)) ||
+                ((type == AUDIO_PORT_TYPE_MIX) && (role == AUDIO_PORT_ROLE_SINK));
+}
+
+static void convertAudioGainConfigToNative(JNIEnv *env,
+                                               struct audio_gain_config *nAudioGainConfig,
+                                               const jobject jAudioGainConfig,
+                                               bool useInMask)
+{
+    nAudioGainConfig->index = env->GetIntField(jAudioGainConfig, gAudioGainConfigFields.mIndex);
+    nAudioGainConfig->mode = env->GetIntField(jAudioGainConfig, gAudioGainConfigFields.mMode);
+    ALOGV("convertAudioGainConfigToNative got gain index %d", nAudioGainConfig->index);
+    jint jMask = env->GetIntField(jAudioGainConfig, gAudioGainConfigFields.mChannelMask);
+    audio_channel_mask_t nMask;
+    if (useInMask) {
+        nMask = inChannelMaskToNative(jMask);
+        ALOGV("convertAudioGainConfigToNative IN mask java %x native %x", jMask, nMask);
+    } else {
+        nMask = outChannelMaskToNative(jMask);
+        ALOGV("convertAudioGainConfigToNative OUT mask java %x native %x", jMask, nMask);
+    }
+    nAudioGainConfig->channel_mask = nMask;
+    nAudioGainConfig->ramp_duration_ms = env->GetIntField(jAudioGainConfig,
+                                                       gAudioGainConfigFields.mRampDurationMs);
+    jintArray jValues = (jintArray)env->GetObjectField(jAudioGainConfig,
+                                                       gAudioGainConfigFields.mValues);
+    int *nValues = env->GetIntArrayElements(jValues, NULL);
+    size_t size = env->GetArrayLength(jValues);
+    memcpy(nAudioGainConfig->values, nValues, size * sizeof(int));
+    env->DeleteLocalRef(jValues);
+}
+
+
+static jint convertAudioPortConfigToNative(JNIEnv *env,
+                                               struct audio_port_config *nAudioPortConfig,
+                                               const jobject jAudioPortConfig)
+{
+    jobject jAudioPort = env->GetObjectField(jAudioPortConfig, gAudioPortConfigFields.mPort);
+    jobject jHandle = env->GetObjectField(jAudioPort, gAudioPortFields.mHandle);
+    nAudioPortConfig->id = env->GetIntField(jHandle, gAudioHandleFields.mId);
+    nAudioPortConfig->role = (audio_port_role_t)env->GetIntField(jAudioPort,
+                                                                 gAudioPortFields.mRole);
+    if (env->IsInstanceOf(jAudioPort, gAudioDevicePortClass)) {
+        nAudioPortConfig->type = AUDIO_PORT_TYPE_DEVICE;
+    } else if (env->IsInstanceOf(jAudioPort, gAudioMixPortClass)) {
+        nAudioPortConfig->type = AUDIO_PORT_TYPE_MIX;
+    } else {
+        env->DeleteLocalRef(jAudioPort);
+        env->DeleteLocalRef(jHandle);
+        return (jint)AUDIO_JAVA_ERROR;
+    }
+    ALOGV("convertAudioPortConfigToNative handle %d role %d type %d",
+          nAudioPortConfig->id, nAudioPortConfig->role, nAudioPortConfig->type);
+
+    nAudioPortConfig->sample_rate = env->GetIntField(jAudioPortConfig,
+                                                     gAudioPortConfigFields.mSamplingRate);
+
+    bool useInMask = useInChannelMask(nAudioPortConfig->type, nAudioPortConfig->role);
+    audio_channel_mask_t nMask;
+    jint jMask = env->GetIntField(jAudioPortConfig,
+                                   gAudioPortConfigFields.mChannelMask);
+    if (useInMask) {
+        nMask = inChannelMaskToNative(jMask);
+        ALOGV("convertAudioPortConfigToNative IN mask java %x native %x", jMask, nMask);
+    } else {
+        nMask = outChannelMaskToNative(jMask);
+        ALOGV("convertAudioPortConfigToNative OUT mask java %x native %x", jMask, nMask);
+    }
+    nAudioPortConfig->channel_mask = nMask;
+
+    jint jFormat = env->GetIntField(jAudioPortConfig, gAudioPortConfigFields.mFormat);
+    audio_format_t nFormat = audioFormatToNative(jFormat);
+    ALOGV("convertAudioPortConfigToNative format %d native %d", jFormat, nFormat);
+    nAudioPortConfig->format = nFormat;
+    jobject jGain = env->GetObjectField(jAudioPortConfig, gAudioPortConfigFields.mGain);
+    if (jGain != NULL) {
+        convertAudioGainConfigToNative(env, &nAudioPortConfig->gain, jGain, useInMask);
+        env->DeleteLocalRef(jGain);
+    } else {
+        ALOGV("convertAudioPortConfigToNative no gain");
+        nAudioPortConfig->gain.index = -1;
+    }
+    nAudioPortConfig->config_mask = env->GetIntField(jAudioPortConfig,
+                                                     gAudioPortConfigFields.mConfigMask);
+
+    env->DeleteLocalRef(jAudioPort);
+    env->DeleteLocalRef(jHandle);
+    return (jint)AUDIO_JAVA_SUCCESS;
+}
+
+static jint convertAudioPortConfigFromNative(JNIEnv *env,
+                                                 jobject jAudioPort,
+                                                 jobject *jAudioPortConfig,
+                                                 const struct audio_port_config *nAudioPortConfig)
+{
+    jint jStatus = AUDIO_JAVA_SUCCESS;
+    jobject jAudioGainConfig = NULL;
+    jobject jAudioGain = NULL;
+    jintArray jGainValues;
+    bool audioportCreated = false;
+
+    ALOGV("convertAudioPortConfigFromNative jAudioPort %p", jAudioPort);
+
+    if (jAudioPort == NULL) {
+        jobject jHandle = env->NewObject(gAudioHandleClass, gAudioHandleCstor,
+                                                 nAudioPortConfig->id);
+
+        ALOGV("convertAudioPortConfigFromNative handle %d is a %s", nAudioPortConfig->id,
+              nAudioPortConfig->type == AUDIO_PORT_TYPE_DEVICE ? "device" : "mix");
+
+        if (jHandle == NULL) {
+            return (jint)AUDIO_JAVA_ERROR;
+        }
+        // create dummy port and port config objects with just the correct handle
+        // and configuration data. The actual AudioPortConfig objects will be
+        // constructed by java code with correct class type (device, mix etc...)
+        // and reference to AudioPort instance in this client
+        jAudioPort = env->NewObject(gAudioPortClass, gAudioPortCstor,
+                                           jHandle,
+                                           0,
+                                           NULL,
+                                           NULL,
+                                           NULL,
+                                           NULL);
+        env->DeleteLocalRef(jHandle);
+        if (jAudioPort == NULL) {
+            return (jint)AUDIO_JAVA_ERROR;
+        }
+        ALOGV("convertAudioPortConfigFromNative jAudioPort created for handle %d",
+              nAudioPortConfig->id);
+
+        audioportCreated = true;
+    }
+
+    bool useInMask = useInChannelMask(nAudioPortConfig->type, nAudioPortConfig->role);
+
+    audio_channel_mask_t nMask;
+    jint jMask;
+
+    int gainIndex = nAudioPortConfig->gain.index;
+    if (gainIndex >= 0) {
+        ALOGV("convertAudioPortConfigFromNative gain found with index %d mode %x",
+              gainIndex, nAudioPortConfig->gain.mode);
+        if (audioportCreated) {
+            ALOGV("convertAudioPortConfigFromNative creating gain");
+            jAudioGain = env->NewObject(gAudioGainClass, gAudioGainCstor,
+                                               gainIndex,
+                                               0,
+                                               0,
+                                               0,
+                                               0,
+                                               0,
+                                               0,
+                                               0,
+                                               0);
+            if (jAudioGain == NULL) {
+                ALOGV("convertAudioPortConfigFromNative creating gain FAILED");
+                jStatus = (jint)AUDIO_JAVA_ERROR;
+                goto exit;
+            }
+        } else {
+            ALOGV("convertAudioPortConfigFromNative reading gain from port");
+            jobjectArray jGains = (jobjectArray)env->GetObjectField(jAudioPort,
+                                                                      gAudioPortFields.mGains);
+            if (jGains == NULL) {
+                ALOGV("convertAudioPortConfigFromNative could not get gains from port");
+                jStatus = (jint)AUDIO_JAVA_ERROR;
+                goto exit;
+            }
+            jAudioGain = env->GetObjectArrayElement(jGains, gainIndex);
+            env->DeleteLocalRef(jGains);
+            if (jAudioGain == NULL) {
+                ALOGV("convertAudioPortConfigFromNative could not get gain at index %d", gainIndex);
+                jStatus = (jint)AUDIO_JAVA_ERROR;
+                goto exit;
+            }
+        }
+        //TODO: replace popcount by audio utils function mask to count
+        int numValues = popcount(nAudioPortConfig->gain.channel_mask);
+        jGainValues = env->NewIntArray(numValues);
+        if (jGainValues == NULL) {
+            ALOGV("convertAudioPortConfigFromNative could not create gain values %d", numValues);
+            jStatus = (jint)AUDIO_JAVA_ERROR;
+            goto exit;
+        }
+        env->SetIntArrayRegion(jGainValues, 0, numValues,
+                               nAudioPortConfig->gain.values);
+
+        nMask = nAudioPortConfig->gain.channel_mask;
+        if (useInMask) {
+            jMask = inChannelMaskFromNative(nMask);
+            ALOGV("convertAudioPortConfigFromNative IN mask java %x native %x", jMask, nMask);
+        } else {
+            jMask = outChannelMaskFromNative(nMask);
+            ALOGV("convertAudioPortConfigFromNative OUT mask java %x native %x", jMask, nMask);
+        }
+
+        jAudioGainConfig = env->NewObject(gAudioGainConfigClass,
+                                        gAudioGainConfigCstor,
+                                        gainIndex,
+                                        jAudioGain,
+                                        nAudioPortConfig->gain.mode,
+                                        jMask,
+                                        jGainValues,
+                                        nAudioPortConfig->gain.ramp_duration_ms);
+        env->DeleteLocalRef(jGainValues);
+        if (jAudioGainConfig == NULL) {
+            ALOGV("convertAudioPortConfigFromNative could not create gain config");
+            jStatus = (jint)AUDIO_JAVA_ERROR;
+            goto exit;
+        }
+    }
+    jclass clazz;
+    jmethodID methodID;
+    if (audioportCreated) {
+        clazz = gAudioPortConfigClass;
+        methodID = gAudioPortConfigCstor;
+        ALOGV("convertAudioPortConfigFromNative building a generic port config");
+    } else {
+        if (env->IsInstanceOf(jAudioPort, gAudioDevicePortClass)) {
+            clazz = gAudioDevicePortConfigClass;
+            methodID = gAudioDevicePortConfigCstor;
+            ALOGV("convertAudioPortConfigFromNative building a device config");
+        } else if (env->IsInstanceOf(jAudioPort, gAudioMixPortClass)) {
+            clazz = gAudioMixPortConfigClass;
+            methodID = gAudioMixPortConfigCstor;
+            ALOGV("convertAudioPortConfigFromNative building a mix config");
+        } else {
+            jStatus = (jint)AUDIO_JAVA_ERROR;
+            goto exit;
+        }
+    }
+    nMask = nAudioPortConfig->channel_mask;
+    if (useInMask) {
+        jMask = inChannelMaskFromNative(nMask);
+        ALOGV("convertAudioPortConfigFromNative IN mask java %x native %x", jMask, nMask);
+    } else {
+        jMask = outChannelMaskFromNative(nMask);
+        ALOGV("convertAudioPortConfigFromNative OUT mask java %x native %x", jMask, nMask);
+    }
+
+    *jAudioPortConfig = env->NewObject(clazz, methodID,
+                                       jAudioPort,
+                                       nAudioPortConfig->sample_rate,
+                                       jMask,
+                                       audioFormatFromNative(nAudioPortConfig->format),
+                                       jAudioGainConfig);
+    if (*jAudioPortConfig == NULL) {
+        ALOGV("convertAudioPortConfigFromNative could not create new port config");
+        jStatus = (jint)AUDIO_JAVA_ERROR;
+    } else {
+        ALOGV("convertAudioPortConfigFromNative OK");
+    }
+
+exit:
+    if (audioportCreated) {
+        env->DeleteLocalRef(jAudioPort);
+        if (jAudioGain != NULL) {
+            env->DeleteLocalRef(jAudioGain);
+        }
+    }
+    if (jAudioGainConfig != NULL) {
+        env->DeleteLocalRef(jAudioGainConfig);
+    }
+    return jStatus;
+}
+
+static jint convertAudioPortFromNative(JNIEnv *env,
+                                           jobject *jAudioPort, const struct audio_port *nAudioPort)
+{
+    jint jStatus = (jint)AUDIO_JAVA_SUCCESS;
+    jintArray jSamplingRates = NULL;
+    jintArray jChannelMasks = NULL;
+    jintArray jFormats = NULL;
+    jobjectArray jGains = NULL;
+    jobject jHandle = NULL;
+    bool useInMask;
+
+    ALOGV("convertAudioPortFromNative id %d role %d type %d",
+                                  nAudioPort->id, nAudioPort->role, nAudioPort->type);
+
+    jSamplingRates = env->NewIntArray(nAudioPort->num_sample_rates);
+    if (jSamplingRates == NULL) {
+        jStatus = (jint)AUDIO_JAVA_ERROR;
+        goto exit;
+    }
+    if (nAudioPort->num_sample_rates) {
+        env->SetIntArrayRegion(jSamplingRates, 0, nAudioPort->num_sample_rates,
+                               (jint *)nAudioPort->sample_rates);
+    }
+
+    jChannelMasks = env->NewIntArray(nAudioPort->num_channel_masks);
+    if (jChannelMasks == NULL) {
+        jStatus = (jint)AUDIO_JAVA_ERROR;
+        goto exit;
+    }
+    useInMask = useInChannelMask(nAudioPort->type, nAudioPort->role);
+
+    jint jMask;
+    for (size_t j = 0; j < nAudioPort->num_channel_masks; j++) {
+        if (useInMask) {
+            jMask = inChannelMaskFromNative(nAudioPort->channel_masks[j]);
+        } else {
+            jMask = outChannelMaskFromNative(nAudioPort->channel_masks[j]);
+        }
+        env->SetIntArrayRegion(jChannelMasks, j, 1, &jMask);
+    }
+
+    jFormats = env->NewIntArray(nAudioPort->num_formats);
+    if (jFormats == NULL) {
+        jStatus = (jint)AUDIO_JAVA_ERROR;
+        goto exit;
+    }
+    for (size_t j = 0; j < nAudioPort->num_formats; j++) {
+        jint jFormat = audioFormatFromNative(nAudioPort->formats[j]);
+        env->SetIntArrayRegion(jFormats, j, 1, &jFormat);
+    }
+
+    jGains = env->NewObjectArray(nAudioPort->num_gains,
+                                          gAudioGainClass, NULL);
+    if (jGains == NULL) {
+        jStatus = (jint)AUDIO_JAVA_ERROR;
+        goto exit;
+    }
+    for (size_t j = 0; j < nAudioPort->num_gains; j++) {
+        audio_channel_mask_t nMask = nAudioPort->gains[j].channel_mask;
+        if (useInMask) {
+            jMask = inChannelMaskFromNative(nMask);
+            ALOGV("convertAudioPortConfigFromNative IN mask java %x native %x", jMask, nMask);
+        } else {
+            jMask = outChannelMaskFromNative(nMask);
+            ALOGV("convertAudioPortConfigFromNative OUT mask java %x native %x", jMask, nMask);
+        }
+
+        jobject jGain = env->NewObject(gAudioGainClass, gAudioGainCstor,
+                                                 j,
+                                                 nAudioPort->gains[j].mode,
+                                                 jMask,
+                                                 nAudioPort->gains[j].min_value,
+                                                 nAudioPort->gains[j].max_value,
+                                                 nAudioPort->gains[j].default_value,
+                                                 nAudioPort->gains[j].step_value,
+                                                 nAudioPort->gains[j].min_ramp_ms,
+                                                 nAudioPort->gains[j].max_ramp_ms);
+        if (jGain == NULL) {
+            jStatus = (jint)AUDIO_JAVA_ERROR;
+            goto exit;
+        }
+        env->SetObjectArrayElement(jGains, j, jGain);
+        env->DeleteLocalRef(jGain);
+    }
+
+    jHandle = env->NewObject(gAudioHandleClass, gAudioHandleCstor,
+                                             nAudioPort->id);
+    if (jHandle == NULL) {
+        jStatus = (jint)AUDIO_JAVA_ERROR;
+        goto exit;
+    }
+
+    if (nAudioPort->type == AUDIO_PORT_TYPE_DEVICE) {
+        ALOGV("convertAudioPortFromNative is a device %08x", nAudioPort->ext.device.type);
+        jstring jAddress = env->NewStringUTF(nAudioPort->ext.device.address);
+        *jAudioPort = env->NewObject(gAudioDevicePortClass, gAudioDevicePortCstor,
+                                     jHandle, jSamplingRates, jChannelMasks, jFormats, jGains,
+                                     nAudioPort->ext.device.type, jAddress);
+        env->DeleteLocalRef(jAddress);
+    } else if (nAudioPort->type == AUDIO_PORT_TYPE_MIX) {
+        ALOGV("convertAudioPortFromNative is a mix");
+        *jAudioPort = env->NewObject(gAudioMixPortClass, gAudioMixPortCstor,
+                                     jHandle, nAudioPort->role, jSamplingRates, jChannelMasks,
+                                     jFormats, jGains);
+    } else {
+        ALOGE("convertAudioPortFromNative unknown nAudioPort type %d", nAudioPort->type);
+        jStatus = (jint)AUDIO_JAVA_ERROR;
+        goto exit;
+    }
+    if (*jAudioPort == NULL) {
+        jStatus = (jint)AUDIO_JAVA_ERROR;
+        goto exit;
+    }
+
+    jobject jAudioPortConfig;
+    jStatus = convertAudioPortConfigFromNative(env,
+                                                       *jAudioPort,
+                                                       &jAudioPortConfig,
+                                                       &nAudioPort->active_config);
+    if (jStatus != AUDIO_JAVA_SUCCESS) {
+        return jStatus;
+    }
+
+    env->SetObjectField(*jAudioPort, gAudioPortFields.mActiveConfig, jAudioPortConfig);
+
+exit:
+    if (jSamplingRates != NULL) {
+        env->DeleteLocalRef(jSamplingRates);
+    }
+    if (jChannelMasks != NULL) {
+        env->DeleteLocalRef(jChannelMasks);
+    }
+    if (jFormats != NULL) {
+        env->DeleteLocalRef(jFormats);
+    }
+    if (jGains != NULL) {
+        env->DeleteLocalRef(jGains);
+    }
+    if (jHandle != NULL) {
+        env->DeleteLocalRef(jHandle);
+    }
+
+    return jStatus;
+}
+
+
+static jint
+android_media_AudioSystem_listAudioPorts(JNIEnv *env, jobject clazz,
+                                         jobject jPorts, jintArray jGeneration)
+{
+    ALOGV("listAudioPorts");
+
+    if (jPorts == NULL) {
+        ALOGE("listAudioPorts NULL AudioPort ArrayList");
+        return (jint)AUDIO_JAVA_BAD_VALUE;
+    }
+    if (!env->IsInstanceOf(jPorts, gArrayListClass)) {
+        ALOGE("listAudioPorts not an arraylist");
+        return (jint)AUDIO_JAVA_BAD_VALUE;
+    }
+
+    if (jGeneration == NULL || env->GetArrayLength(jGeneration) != 1) {
+        return (jint)AUDIO_JAVA_BAD_VALUE;
+    }
+
+    status_t status;
+    unsigned int generation1;
+    unsigned int generation;
+    unsigned int numPorts;
+    jint *nGeneration;
+    struct audio_port *nPorts = NULL;
+    int attempts = MAX_PORT_GENERATION_SYNC_ATTEMPTS;
+
+    // get the port count and all the ports until they both return the same generation
+    do {
+        if (attempts-- < 0) {
+            status = TIMED_OUT;
+            break;
+        }
+
+        numPorts = 0;
+        status = AudioSystem::listAudioPorts(AUDIO_PORT_ROLE_NONE,
+                                             AUDIO_PORT_TYPE_NONE,
+                                                      &numPorts,
+                                                      NULL,
+                                                      &generation1);
+        if (status != NO_ERROR || numPorts == 0) {
+            ALOGE_IF(status != NO_ERROR, "AudioSystem::listAudioPorts error %d", status);
+            break;
+        }
+        nPorts = (struct audio_port *)realloc(nPorts, numPorts * sizeof(struct audio_port));
+
+        status = AudioSystem::listAudioPorts(AUDIO_PORT_ROLE_NONE,
+                                             AUDIO_PORT_TYPE_NONE,
+                                                      &numPorts,
+                                                      nPorts,
+                                                      &generation);
+        ALOGV("listAudioPorts AudioSystem::listAudioPorts numPorts %d generation %d generation1 %d",
+              numPorts, generation, generation1);
+    } while (generation1 != generation && status == NO_ERROR);
+
+    jint jStatus = nativeToJavaStatus(status);
+    if (jStatus != AUDIO_JAVA_SUCCESS) {
+        goto exit;
+    }
+
+    nGeneration = env->GetIntArrayElements(jGeneration, NULL);
+    if (nGeneration == NULL) {
+        jStatus = (jint)AUDIO_JAVA_ERROR;
+        goto exit;
+    }
+    nGeneration[0] = generation1;
+    env->ReleaseIntArrayElements(jGeneration, nGeneration, 0);
+
+    for (size_t i = 0; i < numPorts; i++) {
+        jobject jAudioPort;
+        jStatus = convertAudioPortFromNative(env, &jAudioPort, &nPorts[i]);
+        if (jStatus != AUDIO_JAVA_SUCCESS) {
+            goto exit;
+        }
+        env->CallBooleanMethod(jPorts, gArrayListMethods.add, jAudioPort);
+    }
+
+exit:
+    free(nPorts);
+    return jStatus;
+}
+
+static int
+android_media_AudioSystem_createAudioPatch(JNIEnv *env, jobject clazz,
+                                 jobjectArray jPatches, jobjectArray jSources, jobjectArray jSinks)
+{
+    status_t status;
+    jint jStatus;
+
+    ALOGV("createAudioPatch");
+    if (jPatches == NULL || jSources == NULL || jSinks == NULL) {
+        return (jint)AUDIO_JAVA_BAD_VALUE;
+    }
+
+    if (env->GetArrayLength(jPatches) != 1) {
+        return (jint)AUDIO_JAVA_BAD_VALUE;
+    }
+    jint numSources = env->GetArrayLength(jSources);
+    if (numSources == 0 || numSources > AUDIO_PATCH_PORTS_MAX) {
+        return (jint)AUDIO_JAVA_BAD_VALUE;
+    }
+
+    jint numSinks = env->GetArrayLength(jSinks);
+    if (numSinks == 0 || numSinks > AUDIO_PATCH_PORTS_MAX) {
+        return (jint)AUDIO_JAVA_BAD_VALUE;
+    }
+
+    audio_patch_handle_t handle = (audio_patch_handle_t)0;
+    jobject jPatch = env->GetObjectArrayElement(jPatches, 0);
+    jobject jPatchHandle = NULL;
+    if (jPatch != NULL) {
+        if (!env->IsInstanceOf(jPatch, gAudioPatchClass)) {
+            return (jint)AUDIO_JAVA_BAD_VALUE;
+        }
+        jPatchHandle = env->GetObjectField(jPatch, gAudioPatchFields.mHandle);
+        handle = (audio_patch_handle_t)env->GetIntField(jPatchHandle, gAudioHandleFields.mId);
+    }
+
+    struct audio_patch nPatch;
+
+    nPatch.id = handle;
+    nPatch.num_sources = 0;
+    nPatch.num_sinks = 0;
+    jobject jSource = NULL;
+    jobject jSink = NULL;
+
+    for (jint i = 0; i < numSources; i++) {
+        jSource = env->GetObjectArrayElement(jSources, i);
+        if (!env->IsInstanceOf(jSource, gAudioPortConfigClass)) {
+            jStatus = (jint)AUDIO_JAVA_BAD_VALUE;
+            goto exit;
+        }
+        jStatus = convertAudioPortConfigToNative(env, &nPatch.sources[i], jSource);
+        env->DeleteLocalRef(jSource);
+        jSource = NULL;
+        if (jStatus != AUDIO_JAVA_SUCCESS) {
+            goto exit;
+        }
+        nPatch.num_sources++;
+    }
+
+    for (jint i = 0; i < numSinks; i++) {
+        jSink = env->GetObjectArrayElement(jSinks, i);
+        if (!env->IsInstanceOf(jSink, gAudioPortConfigClass)) {
+            jStatus = (jint)AUDIO_JAVA_BAD_VALUE;
+            goto exit;
+        }
+        jStatus = convertAudioPortConfigToNative(env, &nPatch.sinks[i], jSink);
+        env->DeleteLocalRef(jSink);
+        jSink = NULL;
+        if (jStatus != AUDIO_JAVA_SUCCESS) {
+            goto exit;
+        }
+        nPatch.num_sinks++;
+    }
+
+    ALOGV("AudioSystem::createAudioPatch");
+    status = AudioSystem::createAudioPatch(&nPatch, &handle);
+    ALOGV("AudioSystem::createAudioPatch() returned %d hande %d", status, handle);
+
+    jStatus = nativeToJavaStatus(status);
+    if (jStatus != AUDIO_JAVA_SUCCESS) {
+        goto exit;
+    }
+
+    if (jPatchHandle == NULL) {
+        jPatchHandle = env->NewObject(gAudioHandleClass, gAudioHandleCstor,
+                                           handle);
+        if (jPatchHandle == NULL) {
+            jStatus = (jint)AUDIO_JAVA_ERROR;
+            goto exit;
+        }
+        jPatch = env->NewObject(gAudioPatchClass, gAudioPatchCstor, jPatchHandle, jSources, jSinks);
+        if (jPatch == NULL) {
+            jStatus = (jint)AUDIO_JAVA_ERROR;
+            goto exit;
+        }
+        env->SetObjectArrayElement(jPatches, 0, jPatch);
+    } else {
+        env->SetIntField(jPatchHandle, gAudioHandleFields.mId, handle);
+    }
+
+exit:
+    if (jPatchHandle != NULL) {
+        env->DeleteLocalRef(jPatchHandle);
+    }
+    if (jPatch != NULL) {
+        env->DeleteLocalRef(jPatch);
+    }
+    if (jSource != NULL) {
+        env->DeleteLocalRef(jSource);
+    }
+    if (jSink != NULL) {
+        env->DeleteLocalRef(jSink);
+    }
+    return jStatus;
+}
+
+static int
+android_media_AudioSystem_releaseAudioPatch(JNIEnv *env, jobject clazz,
+                                               jobject jPatch)
+{
+    ALOGV("releaseAudioPatch");
+    if (jPatch == NULL) {
+        return (jint)AUDIO_JAVA_BAD_VALUE;
+    }
+
+    audio_patch_handle_t handle = (audio_patch_handle_t)0;
+    jobject jPatchHandle = NULL;
+    if (!env->IsInstanceOf(jPatch, gAudioPatchClass)) {
+        return (jint)AUDIO_JAVA_BAD_VALUE;
+    }
+    jPatchHandle = env->GetObjectField(jPatch, gAudioPatchFields.mHandle);
+    handle = (audio_patch_handle_t)env->GetIntField(jPatchHandle, gAudioHandleFields.mId);
+    env->DeleteLocalRef(jPatchHandle);
+
+    ALOGV("AudioSystem::releaseAudioPatch");
+    status_t status = AudioSystem::releaseAudioPatch(handle);
+    ALOGV("AudioSystem::releaseAudioPatch() returned %d", status);
+    jint jStatus = nativeToJavaStatus(status);
+    return status;
+}
+
+static jint
+android_media_AudioSystem_listAudioPatches(JNIEnv *env, jobject clazz,
+                                           jobject jPatches, jintArray jGeneration)
+{
+    ALOGV("listAudioPatches");
+    if (jPatches == NULL) {
+        ALOGE("listAudioPatches NULL AudioPatch ArrayList");
+        return (jint)AUDIO_JAVA_BAD_VALUE;
+    }
+    if (!env->IsInstanceOf(jPatches, gArrayListClass)) {
+        ALOGE("listAudioPatches not an arraylist");
+        return (jint)AUDIO_JAVA_BAD_VALUE;
+    }
+
+    if (jGeneration == NULL || env->GetArrayLength(jGeneration) != 1) {
+        return (jint)AUDIO_JAVA_BAD_VALUE;
+    }
+
+    status_t status;
+    unsigned int generation1;
+    unsigned int generation;
+    unsigned int numPatches;
+    jint *nGeneration;
+    struct audio_patch *nPatches = NULL;
+    jobjectArray jSources = NULL;
+    jobject jSource = NULL;
+    jobjectArray jSinks = NULL;
+    jobject jSink = NULL;
+    jobject jPatch = NULL;
+    int attempts = MAX_PORT_GENERATION_SYNC_ATTEMPTS;
+
+    // get the patch count and all the patches until they both return the same generation
+    do {
+        if (attempts-- < 0) {
+            status = TIMED_OUT;
+            break;
+        }
+
+        numPatches = 0;
+        status = AudioSystem::listAudioPatches(&numPatches,
+                                               NULL,
+                                               &generation1);
+        if (status != NO_ERROR || numPatches == 0) {
+            ALOGE_IF(status != NO_ERROR, "listAudioPatches AudioSystem::listAudioPatches error %d",
+                                      status);
+            break;
+        }
+        nPatches = (struct audio_patch *)realloc(nPatches, numPatches * sizeof(struct audio_patch));
+
+        status = AudioSystem::listAudioPatches(&numPatches,
+                                               nPatches,
+                                               &generation);
+        ALOGV("listAudioPatches AudioSystem::listAudioPatches numPatches %d generation %d generation1 %d",
+              numPatches, generation, generation1);
+
+    } while (generation1 != generation && status == NO_ERROR);
+
+    jint jStatus = nativeToJavaStatus(status);
+    if (jStatus != AUDIO_JAVA_SUCCESS) {
+        goto exit;
+    }
+
+    nGeneration = env->GetIntArrayElements(jGeneration, NULL);
+    if (nGeneration == NULL) {
+        jStatus = AUDIO_JAVA_ERROR;
+        goto exit;
+    }
+    nGeneration[0] = generation1;
+    env->ReleaseIntArrayElements(jGeneration, nGeneration, 0);
+
+    for (size_t i = 0; i < numPatches; i++) {
+        jobject patchHandle = env->NewObject(gAudioHandleClass, gAudioHandleCstor,
+                                                 nPatches[i].id);
+        if (patchHandle == NULL) {
+            jStatus = AUDIO_JAVA_ERROR;
+            goto exit;
+        }
+        ALOGV("listAudioPatches patch %d num_sources %d num_sinks %d",
+              i, nPatches[i].num_sources, nPatches[i].num_sinks);
+
+        env->SetIntField(patchHandle, gAudioHandleFields.mId, nPatches[i].id);
+
+        // load sources
+        jSources = env->NewObjectArray(nPatches[i].num_sources,
+                                       gAudioPortConfigClass, NULL);
+        if (jSources == NULL) {
+            jStatus = AUDIO_JAVA_ERROR;
+            goto exit;
+        }
+
+        for (size_t j = 0; j < nPatches[i].num_sources; j++) {
+            jStatus = convertAudioPortConfigFromNative(env,
+                                                      NULL,
+                                                      &jSource,
+                                                      &nPatches[i].sources[j]);
+            if (jStatus != AUDIO_JAVA_SUCCESS) {
+                goto exit;
+            }
+            env->SetObjectArrayElement(jSources, j, jSource);
+            env->DeleteLocalRef(jSource);
+            jSource = NULL;
+            ALOGV("listAudioPatches patch %d source %d is a %s handle %d",
+                  i, j,
+                  nPatches[i].sources[j].type == AUDIO_PORT_TYPE_DEVICE ? "device" : "mix",
+                  nPatches[i].sources[j].id);
+        }
+        // load sinks
+        jSinks = env->NewObjectArray(nPatches[i].num_sinks,
+                                     gAudioPortConfigClass, NULL);
+        if (jSinks == NULL) {
+            jStatus = AUDIO_JAVA_ERROR;
+            goto exit;
+        }
+
+        for (size_t j = 0; j < nPatches[i].num_sinks; j++) {
+            jStatus = convertAudioPortConfigFromNative(env,
+                                                      NULL,
+                                                      &jSink,
+                                                      &nPatches[i].sinks[j]);
+
+            if (jStatus != AUDIO_JAVA_SUCCESS) {
+                goto exit;
+            }
+            env->SetObjectArrayElement(jSinks, j, jSink);
+            env->DeleteLocalRef(jSink);
+            jSink = NULL;
+            ALOGV("listAudioPatches patch %d sink %d is a %s handle %d",
+                  i, j,
+                  nPatches[i].sinks[j].type == AUDIO_PORT_TYPE_DEVICE ? "device" : "mix",
+                  nPatches[i].sinks[j].id);
+        }
+
+        jPatch = env->NewObject(gAudioPatchClass, gAudioPatchCstor,
+                                       patchHandle, jSources, jSinks);
+        env->DeleteLocalRef(jSources);
+        jSources = NULL;
+        env->DeleteLocalRef(jSinks);
+        jSinks = NULL;
+        if (jPatch == NULL) {
+            jStatus = AUDIO_JAVA_ERROR;
+            goto exit;
+        }
+        env->CallBooleanMethod(jPatches, gArrayListMethods.add, jPatch);
+        env->DeleteLocalRef(jPatch);
+        jPatch = NULL;
+    }
+
+exit:
+    if (jSources != NULL) {
+        env->DeleteLocalRef(jSources);
+    }
+    if (jSource != NULL) {
+        env->DeleteLocalRef(jSource);
+    }
+    if (jSinks != NULL) {
+        env->DeleteLocalRef(jSinks);
+    }
+    if (jSink != NULL) {
+        env->DeleteLocalRef(jSink);
+    }
+    if (jPatch != NULL) {
+        env->DeleteLocalRef(jPatch);
+    }
+    free(nPatches);
+    return jStatus;
+}
+
+static jint
+android_media_AudioSystem_setAudioPortConfig(JNIEnv *env, jobject clazz,
+                                 jobject jAudioPortConfig)
+{
+    ALOGV("setAudioPortConfig");
+    if (jAudioPortConfig == NULL) {
+        return AUDIO_JAVA_BAD_VALUE;
+    }
+    if (!env->IsInstanceOf(jAudioPortConfig, gAudioPortConfigClass)) {
+        return AUDIO_JAVA_BAD_VALUE;
+    }
+    struct audio_port_config nAudioPortConfig;
+    jint jStatus = convertAudioPortConfigToNative(env, &nAudioPortConfig, jAudioPortConfig);
+    if (jStatus != AUDIO_JAVA_SUCCESS) {
+        return jStatus;
+    }
+    status_t status = AudioSystem::setAudioPortConfig(&nAudioPortConfig);
+    ALOGV("AudioSystem::setAudioPortConfig() returned %d", status);
+    jStatus = nativeToJavaStatus(status);
+    return jStatus;
+}
+
+static void
+android_media_AudioSystem_eventHandlerSetup(JNIEnv *env, jobject thiz, jobject weak_this)
+{
+    ALOGV("eventHandlerSetup");
+
+    sp<JNIAudioPortCallback> callback = new JNIAudioPortCallback(env, thiz, weak_this);
+
+    AudioSystem::setAudioPortCallback(callback);
+}
+
+static void
+android_media_AudioSystem_eventHandlerFinalize(JNIEnv *env, jobject thiz)
+{
+    ALOGV("eventHandlerFinalize");
+
+    sp<JNIAudioPortCallback> callback;
+
+    AudioSystem::setAudioPortCallback(callback);
+}
+
 // ----------------------------------------------------------------------------
 
 static JNINativeMethod gMethods[] = {
@@ -309,12 +1309,123 @@
     {"getOutputLatency",    "(I)I",     (void *)android_media_AudioSystem_getOutputLatency},
     {"setLowRamDevice",     "(Z)I",     (void *)android_media_AudioSystem_setLowRamDevice},
     {"checkAudioFlinger",    "()I",     (void *)android_media_AudioSystem_checkAudioFlinger},
+    {"listAudioPorts",      "(Ljava/util/ArrayList;[I)I",
+                                                (void *)android_media_AudioSystem_listAudioPorts},
+    {"createAudioPatch",    "([Landroid/media/AudioPatch;[Landroid/media/AudioPortConfig;[Landroid/media/AudioPortConfig;)I",
+                                            (void *)android_media_AudioSystem_createAudioPatch},
+    {"releaseAudioPatch",   "(Landroid/media/AudioPatch;)I",
+                                            (void *)android_media_AudioSystem_releaseAudioPatch},
+    {"listAudioPatches",    "(Ljava/util/ArrayList;[I)I",
+                                                (void *)android_media_AudioSystem_listAudioPatches},
+    {"setAudioPortConfig",   "(Landroid/media/AudioPortConfig;)I",
+                                            (void *)android_media_AudioSystem_setAudioPortConfig},
+};
+
+
+static JNINativeMethod gEventHandlerMethods[] = {
+    {"native_setup",
+        "(Ljava/lang/Object;)V",
+        (void *)android_media_AudioSystem_eventHandlerSetup},
+    {"native_finalize",
+        "()V",
+        (void *)android_media_AudioSystem_eventHandlerFinalize},
 };
 
 int register_android_media_AudioSystem(JNIEnv *env)
 {
+
+    jclass arrayListClass = env->FindClass("java/util/ArrayList");
+    gArrayListClass = (jclass) env->NewGlobalRef(arrayListClass);
+    gArrayListMethods.add = env->GetMethodID(arrayListClass, "add", "(Ljava/lang/Object;)Z");
+
+    jclass audioHandleClass = env->FindClass("android/media/AudioHandle");
+    gAudioHandleClass = (jclass) env->NewGlobalRef(audioHandleClass);
+    gAudioHandleCstor = env->GetMethodID(audioHandleClass, "<init>", "(I)V");
+    gAudioHandleFields.mId = env->GetFieldID(audioHandleClass, "mId", "I");
+
+    jclass audioPortClass = env->FindClass("android/media/AudioPort");
+    gAudioPortClass = (jclass) env->NewGlobalRef(audioPortClass);
+    gAudioPortCstor = env->GetMethodID(audioPortClass, "<init>",
+                               "(Landroid/media/AudioHandle;I[I[I[I[Landroid/media/AudioGain;)V");
+    gAudioPortFields.mHandle = env->GetFieldID(audioPortClass, "mHandle",
+                                               "Landroid/media/AudioHandle;");
+    gAudioPortFields.mRole = env->GetFieldID(audioPortClass, "mRole", "I");
+    gAudioPortFields.mGains = env->GetFieldID(audioPortClass, "mGains",
+                                              "[Landroid/media/AudioGain;");
+    gAudioPortFields.mActiveConfig = env->GetFieldID(audioPortClass, "mActiveConfig",
+                                              "Landroid/media/AudioPortConfig;");
+
+    jclass audioPortConfigClass = env->FindClass("android/media/AudioPortConfig");
+    gAudioPortConfigClass = (jclass) env->NewGlobalRef(audioPortConfigClass);
+    gAudioPortConfigCstor = env->GetMethodID(audioPortConfigClass, "<init>",
+                                 "(Landroid/media/AudioPort;IIILandroid/media/AudioGainConfig;)V");
+    gAudioPortConfigFields.mPort = env->GetFieldID(audioPortConfigClass, "mPort",
+                                                   "Landroid/media/AudioPort;");
+    gAudioPortConfigFields.mSamplingRate = env->GetFieldID(audioPortConfigClass,
+                                                           "mSamplingRate", "I");
+    gAudioPortConfigFields.mChannelMask = env->GetFieldID(audioPortConfigClass,
+                                                          "mChannelMask", "I");
+    gAudioPortConfigFields.mFormat = env->GetFieldID(audioPortConfigClass, "mFormat", "I");
+    gAudioPortConfigFields.mGain = env->GetFieldID(audioPortConfigClass, "mGain",
+                                                   "Landroid/media/AudioGainConfig;");
+    gAudioPortConfigFields.mConfigMask = env->GetFieldID(audioPortConfigClass, "mConfigMask", "I");
+
+    jclass audioDevicePortConfigClass = env->FindClass("android/media/AudioDevicePortConfig");
+    gAudioDevicePortConfigClass = (jclass) env->NewGlobalRef(audioDevicePortConfigClass);
+    gAudioDevicePortConfigCstor = env->GetMethodID(audioDevicePortConfigClass, "<init>",
+                         "(Landroid/media/AudioDevicePort;IIILandroid/media/AudioGainConfig;)V");
+
+    jclass audioMixPortConfigClass = env->FindClass("android/media/AudioMixPortConfig");
+    gAudioMixPortConfigClass = (jclass) env->NewGlobalRef(audioMixPortConfigClass);
+    gAudioMixPortConfigCstor = env->GetMethodID(audioMixPortConfigClass, "<init>",
+                         "(Landroid/media/AudioMixPort;IIILandroid/media/AudioGainConfig;)V");
+
+    jclass audioDevicePortClass = env->FindClass("android/media/AudioDevicePort");
+    gAudioDevicePortClass = (jclass) env->NewGlobalRef(audioDevicePortClass);
+    gAudioDevicePortCstor = env->GetMethodID(audioDevicePortClass, "<init>",
+             "(Landroid/media/AudioHandle;[I[I[I[Landroid/media/AudioGain;ILjava/lang/String;)V");
+
+    jclass audioMixPortClass = env->FindClass("android/media/AudioMixPort");
+    gAudioMixPortClass = (jclass) env->NewGlobalRef(audioMixPortClass);
+    gAudioMixPortCstor = env->GetMethodID(audioMixPortClass, "<init>",
+                              "(Landroid/media/AudioHandle;I[I[I[I[Landroid/media/AudioGain;)V");
+
+    jclass audioGainClass = env->FindClass("android/media/AudioGain");
+    gAudioGainClass = (jclass) env->NewGlobalRef(audioGainClass);
+    gAudioGainCstor = env->GetMethodID(audioGainClass, "<init>", "(IIIIIIIII)V");
+
+    jclass audioGainConfigClass = env->FindClass("android/media/AudioGainConfig");
+    gAudioGainConfigClass = (jclass) env->NewGlobalRef(audioGainConfigClass);
+    gAudioGainConfigCstor = env->GetMethodID(audioGainConfigClass, "<init>",
+                                             "(ILandroid/media/AudioGain;II[II)V");
+    gAudioGainConfigFields.mIndex = env->GetFieldID(gAudioGainConfigClass, "mIndex", "I");
+    gAudioGainConfigFields.mMode = env->GetFieldID(audioGainConfigClass, "mMode", "I");
+    gAudioGainConfigFields.mChannelMask = env->GetFieldID(audioGainConfigClass, "mChannelMask",
+                                                          "I");
+    gAudioGainConfigFields.mValues = env->GetFieldID(audioGainConfigClass, "mValues", "[I");
+    gAudioGainConfigFields.mRampDurationMs = env->GetFieldID(audioGainConfigClass,
+                                                             "mRampDurationMs", "I");
+
+    jclass audioPatchClass = env->FindClass("android/media/AudioPatch");
+    gAudioPatchClass = (jclass) env->NewGlobalRef(audioPatchClass);
+    gAudioPatchCstor = env->GetMethodID(audioPatchClass, "<init>",
+"(Landroid/media/AudioHandle;[Landroid/media/AudioPortConfig;[Landroid/media/AudioPortConfig;)V");
+    gAudioPatchFields.mHandle = env->GetFieldID(audioPatchClass, "mHandle",
+                                                "Landroid/media/AudioHandle;");
+
+    jclass eventHandlerClass = env->FindClass(kEventHandlerClassPathName);
+    gPostEventFromNative = env->GetStaticMethodID(eventHandlerClass, "postEventFromNative",
+                                            "(Ljava/lang/Object;IIILjava/lang/Object;)V");
+
+
     AudioSystem::setErrorCallback(android_media_AudioSystem_error_callback);
 
-    return AndroidRuntime::registerNativeMethods(env,
+    int status = AndroidRuntime::registerNativeMethods(env,
                 kClassPathName, gMethods, NELEM(gMethods));
+
+    if (status == 0) {
+        status = AndroidRuntime::registerNativeMethods(env,
+                kEventHandlerClassPathName, gEventHandlerMethods, NELEM(gEventHandlerMethods));
+    }
+    return status;
 }
diff --git a/core/jni/android_view_GLES20Canvas.cpp b/core/jni/android_view_GLES20Canvas.cpp
index c5dd06f..ecdfeb2 100644
--- a/core/jni/android_view_GLES20Canvas.cpp
+++ b/core/jni/android_view_GLES20Canvas.cpp
@@ -91,53 +91,9 @@
 } gRectClassInfo;
 
 // ----------------------------------------------------------------------------
-// Caching
-// ----------------------------------------------------------------------------
-
-static void android_view_GLES20Canvas_flushCaches(JNIEnv* env, jobject clazz,
-        jint mode) {
-    if (Caches::hasInstance()) {
-        Caches::getInstance().flush(static_cast<Caches::FlushMode>(mode));
-    }
-}
-
-static jboolean android_view_GLES20Canvas_initCaches(JNIEnv* env, jobject clazz) {
-    if (Caches::hasInstance()) {
-        return Caches::getInstance().init() ? JNI_TRUE : JNI_FALSE;
-    }
-    return JNI_FALSE;
-}
-
-static void android_view_GLES20Canvas_terminateCaches(JNIEnv* env, jobject clazz) {
-    if (Caches::hasInstance()) {
-        Caches::getInstance().terminate();
-    }
-}
-
-// ----------------------------------------------------------------------------
-// Caching
-// ----------------------------------------------------------------------------
-
-static void android_view_GLES20Canvas_initAtlas(JNIEnv* env, jobject clazz,
-        jobject graphicBuffer, jlongArray atlasMapArray, jint count) {
-
-    sp<GraphicBuffer> buffer = graphicBufferForJavaObject(env, graphicBuffer);
-    jlong* jAtlasMap = env->GetLongArrayElements(atlasMapArray, NULL);
-    Caches::getInstance().assetAtlas.init(buffer, jAtlasMap, count);
-    env->ReleaseLongArrayElements(atlasMapArray, jAtlasMap, 0);
-}
-
-// ----------------------------------------------------------------------------
 // Constructors
 // ----------------------------------------------------------------------------
 
-static jlong android_view_GLES20Canvas_createRenderer(JNIEnv* env, jobject clazz) {
-    RENDERER_LOGD("Create OpenGLRenderer");
-    OpenGLRenderer* renderer = new OpenGLRenderer();
-    renderer->initProperties();
-    return reinterpret_cast<jlong>(renderer);
-}
-
 static void android_view_GLES20Canvas_destroyRenderer(JNIEnv* env, jobject clazz,
         jlong rendererPtr) {
     OpenGLRenderer* renderer = reinterpret_cast<OpenGLRenderer*>(rendererPtr);
@@ -174,10 +130,6 @@
     renderer->finish();
 }
 
-static jint android_view_GLES20Canvas_getStencilSize(JNIEnv* env, jobject clazz) {
-    return Stencil::getStencilSize();
-}
-
 static void android_view_GLES20Canvas_setProperty(JNIEnv* env,
         jobject clazz, jstring name, jstring value) {
     if (!Caches::hasInstance()) {
@@ -373,7 +325,7 @@
         jlong rendererPtr, jlong matrixPtr) {
     OpenGLRenderer* renderer = reinterpret_cast<OpenGLRenderer*>(rendererPtr);
     SkMatrix* matrix = reinterpret_cast<SkMatrix*>(matrixPtr);
-    renderer->setMatrix(matrix);
+    renderer->setMatrix(matrix ? *matrix : SkMatrix::I());
 }
 
 static void android_view_GLES20Canvas_getMatrix(JNIEnv* env, jobject clazz,
@@ -387,7 +339,7 @@
         jlong rendererPtr, jlong matrixPtr) {
     OpenGLRenderer* renderer = reinterpret_cast<OpenGLRenderer*>(rendererPtr);
     SkMatrix* matrix = reinterpret_cast<SkMatrix*>(matrixPtr);
-    renderer->concatMatrix(matrix);
+    renderer->concatMatrix(*matrix);
 }
 
 // ----------------------------------------------------------------------------
@@ -430,7 +382,7 @@
     OpenGLRenderer* renderer = reinterpret_cast<OpenGLRenderer*>(rendererPtr);
     SkMatrix* matrix = reinterpret_cast<SkMatrix*>(matrixPtr);
     SkPaint* paint = reinterpret_cast<SkPaint*>(paintPtr);
-    renderer->drawBitmap(bitmap, matrix, paint);
+    renderer->drawBitmap(bitmap, *matrix, paint);
 }
 
 static void android_view_GLES20Canvas_drawBitmapData(JNIEnv* env, jobject clazz,
@@ -577,15 +529,6 @@
     }
 }
 
-static void android_view_GLES20Canvas_drawRects(JNIEnv* env, jobject clazz,
-        jlong rendererPtr, jfloatArray rects, jint count, jlong paintPtr) {
-    OpenGLRenderer* renderer = reinterpret_cast<OpenGLRenderer*>(rendererPtr);
-    jfloat* storage = env->GetFloatArrayElements(rects, NULL);
-    SkPaint* paint = reinterpret_cast<SkPaint*>(paintPtr);
-    renderer->drawRects(storage, count, paint);
-    env->ReleaseFloatArrayElements(rects, storage, 0);
-}
-
 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);
@@ -924,39 +867,6 @@
     renderer->drawLayer(layer, x, y);
 }
 
-static jboolean android_view_GLES20Canvas_copyLayer(JNIEnv* env, jobject clazz,
-        jlong layerPtr, jlong bitmapPtr) {
-    Layer* layer = reinterpret_cast<Layer*>(layerPtr);
-    SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(bitmapPtr);
-    return LayerRenderer::copyLayer(layer, bitmap);
-}
-
-static void android_view_GLES20Canvas_pushLayerUpdate(JNIEnv* env, jobject clazz,
-        jlong rendererPtr, jlong layerPtr) {
-    OpenGLRenderer* renderer = reinterpret_cast<OpenGLRenderer*>(rendererPtr);
-    Layer* layer = reinterpret_cast<Layer*>(layerPtr);
-    renderer->pushLayerUpdate(layer);
-}
-
-static void android_view_GLES20Canvas_cancelLayerUpdate(JNIEnv* env, jobject clazz,
-        jlong rendererPtr, jlong layerPtr) {
-    OpenGLRenderer* renderer = reinterpret_cast<OpenGLRenderer*>(rendererPtr);
-    Layer* layer = reinterpret_cast<Layer*>(layerPtr);
-    renderer->cancelLayerUpdate(layer);
-}
-
-static void android_view_GLES20Canvas_clearLayerUpdates(JNIEnv* env, jobject clazz,
-        jlong rendererPtr) {
-    OpenGLRenderer* renderer = reinterpret_cast<OpenGLRenderer*>(rendererPtr);
-    renderer->clearLayerUpdates();
-}
-
-static void android_view_GLES20Canvas_flushLayerUpdates(JNIEnv* env, jobject clazz,
-        jlong rendererPtr) {
-    OpenGLRenderer* renderer = reinterpret_cast<OpenGLRenderer*>(rendererPtr);
-    renderer->flushLayerUpdates();
-}
-
 #endif // USE_OPENGL_RENDERER
 
 // ----------------------------------------------------------------------------
@@ -1001,14 +911,7 @@
     { "nIsAvailable",       "()Z",             (void*) android_view_GLES20Canvas_isAvailable },
 
 #ifdef USE_OPENGL_RENDERER
-    { "nFlushCaches",       "(I)V",            (void*) android_view_GLES20Canvas_flushCaches },
-    { "nInitCaches",        "()Z",             (void*) android_view_GLES20Canvas_initCaches },
-    { "nTerminateCaches",   "()V",             (void*) android_view_GLES20Canvas_terminateCaches },
 
-    { "nInitAtlas",         "(Landroid/view/GraphicBuffer;[JI)V",
-            (void*) android_view_GLES20Canvas_initAtlas },
-
-    { "nCreateRenderer",    "()J",             (void*) android_view_GLES20Canvas_createRenderer },
     { "nDestroyRenderer",   "(J)V",            (void*) android_view_GLES20Canvas_destroyRenderer },
     { "nSetViewport",       "(JII)V",          (void*) android_view_GLES20Canvas_setViewport },
     { "nPrepare",           "(JZ)I",           (void*) android_view_GLES20Canvas_prepare },
@@ -1017,9 +920,6 @@
     { "nSetProperty",           "(Ljava/lang/String;Ljava/lang/String;)V",
             (void*) android_view_GLES20Canvas_setProperty },
 
-
-    { "nGetStencilSize",    "()I",             (void*) android_view_GLES20Canvas_getStencilSize },
-
     { "nCallDrawGLFunction", "(JJ)I",          (void*) android_view_GLES20Canvas_callDrawGLFunction },
 
     { "nSave",              "(JI)I",           (void*) android_view_GLES20Canvas_save },
@@ -1059,7 +959,6 @@
     { "nDrawColor",         "(JII)V",          (void*) android_view_GLES20Canvas_drawColor },
     { "nDrawRect",          "(JFFFFJ)V",       (void*) android_view_GLES20Canvas_drawRect },
     { "nDrawRects",         "(JJJ)V",          (void*) android_view_GLES20Canvas_drawRegionAsRects },
-    { "nDrawRects",         "(J[FIJ)V",        (void*) android_view_GLES20Canvas_drawRects },
     { "nDrawRoundRect",     "(JFFFFFFJ)V",     (void*) android_view_GLES20Canvas_drawRoundRect },
     { "nDrawCircle",        "(JFFFJ)V",        (void*) android_view_GLES20Canvas_drawCircle },
     { "nDrawCircle",        "(JJJJJ)V",        (void*) android_view_GLES20Canvas_drawCircleProps },
@@ -1099,11 +998,6 @@
     { "nCreateDisplayListRenderer", "()J",     (void*) android_view_GLES20Canvas_createDisplayListRenderer },
 
     { "nDrawLayer",              "(JJFF)V",    (void*) android_view_GLES20Canvas_drawLayer },
-    { "nCopyLayer",              "(JJ)Z",      (void*) android_view_GLES20Canvas_copyLayer },
-    { "nClearLayerUpdates",      "(J)V",       (void*) android_view_GLES20Canvas_clearLayerUpdates },
-    { "nFlushLayerUpdates",      "(J)V",       (void*) android_view_GLES20Canvas_flushLayerUpdates },
-    { "nPushLayerUpdate",        "(JJ)V",      (void*) android_view_GLES20Canvas_pushLayerUpdate },
-    { "nCancelLayerUpdate",      "(JJ)V",      (void*) android_view_GLES20Canvas_cancelLayerUpdate },
 
     { "nGetMaximumTextureWidth",  "()I",       (void*) android_view_GLES20Canvas_getMaxTextureWidth },
     { "nGetMaximumTextureHeight", "()I",       (void*) android_view_GLES20Canvas_getMaxTextureHeight },
diff --git a/core/jni/android_view_GLRenderer.cpp b/core/jni/android_view_GLRenderer.cpp
deleted file mode 100644
index d0269a3..0000000
--- a/core/jni/android_view_GLRenderer.cpp
+++ /dev/null
@@ -1,203 +0,0 @@
-/*
- * Copyright (C) 2010 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 "GLRenderer"
-
-#include "jni.h"
-#include <nativehelper/JNIHelp.h>
-#include <android_runtime/AndroidRuntime.h>
-
-#include <EGL/egl.h>
-#include <EGL/eglext.h>
-#include <EGL/egl_cache.h>
-
-#include <utils/Timers.h>
-
-#include <private/hwui/DrawGlInfo.h>
-
-#include <Caches.h>
-#include <Extensions.h>
-#include <LayerRenderer.h>
-#include <RenderNode.h>
-
-#ifdef USE_OPENGL_RENDERER
-    EGLAPI void EGLAPIENTRY eglBeginFrame(EGLDisplay dpy, EGLSurface surface);
-#endif
-
-namespace android {
-
-/**
- * Note: OpenGLRenderer 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.
- */
-#ifdef USE_OPENGL_RENDERER
-
-// ----------------------------------------------------------------------------
-// Defines
-// ----------------------------------------------------------------------------
-
-// Debug
-#define DEBUG_RENDERER 0
-
-// Debug
-#if DEBUG_RENDERER
-    #define RENDERER_LOGD(...) ALOGD(__VA_ARGS__)
-#else
-    #define RENDERER_LOGD(...)
-#endif
-
-// ----------------------------------------------------------------------------
-// Surface and display management
-// ----------------------------------------------------------------------------
-
-static jboolean android_view_GLRenderer_preserveBackBuffer(JNIEnv* env, jobject clazz) {
-    EGLDisplay display = eglGetCurrentDisplay();
-    EGLSurface surface = eglGetCurrentSurface(EGL_DRAW);
-
-    eglGetError();
-    eglSurfaceAttrib(display, surface, EGL_SWAP_BEHAVIOR, EGL_BUFFER_PRESERVED);
-
-    EGLint error = eglGetError();
-    if (error != EGL_SUCCESS) {
-        RENDERER_LOGD("Could not enable buffer preserved swap behavior (%x)", error);
-    }
-
-    return error == EGL_SUCCESS;
-}
-
-static jboolean android_view_GLRenderer_isBackBufferPreserved(JNIEnv* env, jobject clazz) {
-    EGLDisplay display = eglGetCurrentDisplay();
-    EGLSurface surface = eglGetCurrentSurface(EGL_DRAW);
-    EGLint value;
-
-    eglGetError();
-    eglQuerySurface(display, surface, EGL_SWAP_BEHAVIOR, &value);
-
-    EGLint error = eglGetError();
-    if (error != EGL_SUCCESS) {
-        RENDERER_LOGD("Could not query buffer preserved swap behavior (%x)", error);
-    }
-
-    return error == EGL_SUCCESS && value == EGL_BUFFER_PRESERVED;
-}
-
-// ----------------------------------------------------------------------------
-// Tracing and debugging
-// ----------------------------------------------------------------------------
-
-static bool android_view_GLRenderer_loadProperties(JNIEnv* env, jobject clazz) {
-    if (uirenderer::Caches::hasInstance()) {
-        return uirenderer::Caches::getInstance().initProperties();
-    }
-    return false;
-}
-
-static void android_view_GLRenderer_beginFrame(JNIEnv* env, jobject clazz,
-        jintArray size) {
-
-    EGLDisplay display = eglGetCurrentDisplay();
-    EGLSurface surface = eglGetCurrentSurface(EGL_DRAW);
-
-    if (size) {
-        EGLint value;
-        jint* storage = env->GetIntArrayElements(size, NULL);
-
-        eglQuerySurface(display, surface, EGL_WIDTH, &value);
-        storage[0] = value;
-
-        eglQuerySurface(display, surface, EGL_HEIGHT, &value);
-        storage[1] = value;
-
-        env->ReleaseIntArrayElements(size, storage, 0);
-    }
-
-    eglBeginFrame(display, surface);
-}
-
-static jlong android_view_GLRenderer_getSystemTime(JNIEnv* env, jobject clazz) {
-    if (uirenderer::Extensions::getInstance().hasNvSystemTime()) {
-        return eglGetSystemTimeNV();
-    }
-    return systemTime(SYSTEM_TIME_MONOTONIC);
-}
-
-static void android_view_GLRenderer_destroyLayer(JNIEnv* env, jobject clazz,
-        jlong layerPtr) {
-    using namespace android::uirenderer;
-    Layer* layer = reinterpret_cast<Layer*>(layerPtr);
-    LayerRenderer::destroyLayer(layer);
-}
-
-static void android_view_GLRenderer_prepareTree(JNIEnv* env, jobject clazz,
-        jlong renderNodePtr) {
-    using namespace android::uirenderer;
-    RenderNode* renderNode = reinterpret_cast<RenderNode*>(renderNodePtr);
-    TreeInfo ignoredInfo;
-    renderNode->prepareTree(ignoredInfo);
-}
-
-static void android_view_GLRenderer_invokeFunctor(JNIEnv* env, jobject clazz,
-        jlong functorPtr, jboolean hasContext) {
-    using namespace android::uirenderer;
-    Functor* functor = reinterpret_cast<Functor*>(functorPtr);
-    DrawGlInfo::Mode mode = hasContext ? DrawGlInfo::kModeProcess : DrawGlInfo::kModeProcessNoContext;
-    (*functor)(mode, NULL);
-}
-
-#endif // USE_OPENGL_RENDERER
-
-// ----------------------------------------------------------------------------
-// Shaders
-// ----------------------------------------------------------------------------
-
-static void android_view_GLRenderer_setupShadersDiskCache(JNIEnv* env, jobject clazz,
-        jstring diskCachePath) {
-
-    const char* cacheArray = env->GetStringUTFChars(diskCachePath, NULL);
-    egl_cache_t::get()->setCacheFilename(cacheArray);
-    env->ReleaseStringUTFChars(diskCachePath, cacheArray);
-}
-
-// ----------------------------------------------------------------------------
-// JNI Glue
-// ----------------------------------------------------------------------------
-
-const char* const kClassPathName = "android/view/GLRenderer";
-
-static JNINativeMethod gMethods[] = {
-#ifdef USE_OPENGL_RENDERER
-    { "isBackBufferPreserved", "()Z",   (void*) android_view_GLRenderer_isBackBufferPreserved },
-    { "preserveBackBuffer",    "()Z",   (void*) android_view_GLRenderer_preserveBackBuffer },
-    { "loadProperties",        "()Z",   (void*) android_view_GLRenderer_loadProperties },
-
-    { "beginFrame",            "([I)V", (void*) android_view_GLRenderer_beginFrame },
-
-    { "getSystemTime",         "()J",   (void*) android_view_GLRenderer_getSystemTime },
-    { "nDestroyLayer",         "(J)V",  (void*) android_view_GLRenderer_destroyLayer },
-    { "nPrepareTree", "(J)V", (void*) android_view_GLRenderer_prepareTree },
-    { "nInvokeFunctor",        "(JZ)V", (void*) android_view_GLRenderer_invokeFunctor },
-#endif
-
-    { "setupShadersDiskCache", "(Ljava/lang/String;)V",
-            (void*) android_view_GLRenderer_setupShadersDiskCache },
-};
-
-int register_android_view_GLRenderer(JNIEnv* env) {
-    return AndroidRuntime::registerNativeMethods(env, kClassPathName, gMethods, NELEM(gMethods));
-}
-
-};
diff --git a/core/jni/android_view_GraphicBuffer.cpp b/core/jni/android_view_GraphicBuffer.cpp
index 8a426ac..0210bd9 100644
--- a/core/jni/android_view_GraphicBuffer.cpp
+++ b/core/jni/android_view_GraphicBuffer.cpp
@@ -21,6 +21,7 @@
 
 #include "android_os_Parcel.h"
 #include "android_view_GraphicBuffer.h"
+#include "android/graphics/GraphicsJNI.h"
 
 #include <android_runtime/AndroidRuntime.h>
 
@@ -75,7 +76,7 @@
 
 static struct {
     jfieldID mSurfaceFormat;
-    jmethodID safeCanvasSwap;
+    jmethodID setNativeBitmap;
 } gCanvasClassInfo;
 
 #define GET_INT(object, field) \
@@ -197,12 +198,11 @@
     }
 
     SET_INT(canvas, gCanvasClassInfo.mSurfaceFormat, buffer->getPixelFormat());
-
-    SkCanvas* nativeCanvas = SkNEW_ARGS(SkCanvas, (bitmap));
-    INVOKEV(canvas, gCanvasClassInfo.safeCanvasSwap, (jlong)nativeCanvas, false);
+    INVOKEV(canvas, gCanvasClassInfo.setNativeBitmap, reinterpret_cast<jlong>(&bitmap));
 
     SkRect clipRect;
     clipRect.set(rect.left, rect.top, rect.right, rect.bottom);
+    SkCanvas* nativeCanvas = GraphicsJNI::getNativeCanvas(env, canvas);
     nativeCanvas->clipRect(clipRect);
 
     if (dirtyRect) {
@@ -218,8 +218,7 @@
 
     GraphicBufferWrapper* wrapper =
                 reinterpret_cast<GraphicBufferWrapper*>(wrapperHandle);
-    SkCanvas* nativeCanvas = SkNEW(SkCanvas);
-    INVOKEV(canvas, gCanvasClassInfo.safeCanvasSwap, (jlong)nativeCanvas, false);
+    INVOKEV(canvas, gCanvasClassInfo.setNativeBitmap, (jlong)0);
 
     if (wrapper) {
         status_t status = wrapper->buffer->unlock();
@@ -319,7 +318,7 @@
 
     FIND_CLASS(clazz, "android/graphics/Canvas");
     GET_FIELD_ID(gCanvasClassInfo.mSurfaceFormat, clazz, "mSurfaceFormat", "I");
-    GET_METHOD_ID(gCanvasClassInfo.safeCanvasSwap, clazz, "safeCanvasSwap", "(JZ)V");
+    GET_METHOD_ID(gCanvasClassInfo.setNativeBitmap, clazz, "setNativeBitmap", "(J)V");
 
     return AndroidRuntime::registerNativeMethods(env, kClassPathName, gMethods, NELEM(gMethods));
 }
diff --git a/core/jni/android_view_HardwareLayer.cpp b/core/jni/android_view_HardwareLayer.cpp
index b2f17de..879836d 100644
--- a/core/jni/android_view_HardwareLayer.cpp
+++ b/core/jni/android_view_HardwareLayer.cpp
@@ -43,41 +43,12 @@
 
 #ifdef USE_OPENGL_RENDERER
 
-static jlong android_view_HardwareLayer_createTextureLayer(JNIEnv* env, jobject clazz) {
-    Layer* layer = LayerRenderer::createTextureLayer();
-    if (!layer) return 0;
-
-    return reinterpret_cast<jlong>( new DeferredLayerUpdater(layer) );
-}
-
-static jlong android_view_HardwareLayer_createRenderLayer(JNIEnv* env, jobject clazz,
-        jint width, jint height) {
-    Layer* layer = LayerRenderer::createRenderLayer(width, height);
-    if (!layer) return 0;
-
-    OpenGLRenderer* renderer = new LayerRenderer(layer);
-    renderer->initProperties();
-    return reinterpret_cast<jlong>( new DeferredLayerUpdater(layer, renderer) );
-}
-
 static void android_view_HardwareLayer_onTextureDestroyed(JNIEnv* env, jobject clazz,
         jlong layerUpdaterPtr) {
     DeferredLayerUpdater* layer = reinterpret_cast<DeferredLayerUpdater*>(layerUpdaterPtr);
     layer->backingLayer()->clearTexture();
 }
 
-static jlong android_view_HardwareLayer_detachBackingLayer(JNIEnv* env, jobject clazz,
-        jlong layerUpdaterPtr) {
-    DeferredLayerUpdater* layer = reinterpret_cast<DeferredLayerUpdater*>(layerUpdaterPtr);
-    return reinterpret_cast<jlong>( layer->detachBackingLayer() );
-}
-
-static void android_view_HardwareLayer_destroyLayerUpdater(JNIEnv* env, jobject clazz,
-        jlong layerUpdaterPtr) {
-    DeferredLayerUpdater* layer = reinterpret_cast<DeferredLayerUpdater*>(layerUpdaterPtr);
-    delete layer;
-}
-
 static jboolean android_view_HardwareLayer_prepare(JNIEnv* env, jobject clazz,
         jlong layerUpdaterPtr, jint width, jint height, jboolean isOpaque) {
     DeferredLayerUpdater* layer = reinterpret_cast<DeferredLayerUpdater*>(layerUpdaterPtr);
@@ -154,11 +125,7 @@
 static JNINativeMethod gMethods[] = {
 #ifdef USE_OPENGL_RENDERER
 
-    { "nCreateTextureLayer",     "()J",        (void*) android_view_HardwareLayer_createTextureLayer },
-    { "nCreateRenderLayer",      "(II)J",      (void*) android_view_HardwareLayer_createRenderLayer },
     { "nOnTextureDestroyed",     "(J)V",       (void*) android_view_HardwareLayer_onTextureDestroyed },
-    { "nDetachBackingLayer",     "(J)J",       (void*) android_view_HardwareLayer_detachBackingLayer },
-    { "nDestroyLayerUpdater",    "(J)V",       (void*) android_view_HardwareLayer_destroyLayerUpdater },
 
     { "nPrepare",                "(JIIZ)Z",    (void*) android_view_HardwareLayer_prepare },
     { "nSetLayerPaint",          "(JJ)V",      (void*) android_view_HardwareLayer_setLayerPaint },
diff --git a/core/jni/android_view_RenderNode.cpp b/core/jni/android_view_RenderNode.cpp
index 867c1b1..26022e0 100644
--- a/core/jni/android_view_RenderNode.cpp
+++ b/core/jni/android_view_RenderNode.cpp
@@ -48,6 +48,12 @@
     renderNode->output();
 }
 
+static jint android_view_RenderNode_getDebugSize(JNIEnv* env,
+        jobject clazz, jlong renderNodePtr) {
+    RenderNode* renderNode = reinterpret_cast<RenderNode*>(renderNodePtr);
+    return renderNode->getDebugSize();
+}
+
 static jlong android_view_RenderNode_create(JNIEnv* env, jobject clazz, jstring name) {
     RenderNode* renderNode = new RenderNode();
     renderNode->incStrong(0);
@@ -505,6 +511,7 @@
     { "nDestroyRenderNode",   "(J)V",   (void*) android_view_RenderNode_destroyRenderNode },
     { "nSetDisplayListData",   "(JJ)V", (void*) android_view_RenderNode_setDisplayListData },
     { "nOutput",               "(J)V",  (void*) android_view_RenderNode_output },
+    { "nGetDebugSize",         "(J)I",  (void*) android_view_RenderNode_getDebugSize },
 
     { "nSetCaching",           "(JZ)V",  (void*) android_view_RenderNode_setCaching },
     { "nSetStaticMatrix",      "(JJ)V",  (void*) android_view_RenderNode_setStaticMatrix },
diff --git a/core/jni/android_view_RenderNodeAnimator.cpp b/core/jni/android_view_RenderNodeAnimator.cpp
index e19ce36..d689864 100644
--- a/core/jni/android_view_RenderNodeAnimator.cpp
+++ b/core/jni/android_view_RenderNodeAnimator.cpp
@@ -116,6 +116,11 @@
     return reinterpret_cast<jlong>( animator );
 }
 
+static void setStartValue(JNIEnv* env, jobject clazz, jlong animatorPtr, jfloat startValue) {
+    BaseRenderNodeAnimator* animator = reinterpret_cast<BaseRenderNodeAnimator*>(animatorPtr);
+    animator->setStartValue(startValue);
+}
+
 static void setDuration(JNIEnv* env, jobject clazz, jlong animatorPtr, jlong duration) {
     LOG_ALWAYS_FATAL_IF(duration < 0, "Duration cannot be negative");
     BaseRenderNodeAnimator* animator = reinterpret_cast<BaseRenderNodeAnimator*>(animatorPtr);
@@ -157,6 +162,7 @@
     { "nCreateAnimator", "(Ljava/lang/ref/WeakReference;IF)J", (void*) createAnimator },
     { "nCreateCanvasPropertyFloatAnimator", "(Ljava/lang/ref/WeakReference;JF)J", (void*) createCanvasPropertyFloatAnimator },
     { "nCreateCanvasPropertyPaintAnimator", "(Ljava/lang/ref/WeakReference;JIF)J", (void*) createCanvasPropertyPaintAnimator },
+    { "nSetStartValue", "(JF)V", (void*) setStartValue },
     { "nSetDuration", "(JJ)V", (void*) setDuration },
     { "nGetDuration", "(J)J", (void*) getDuration },
     { "nSetStartDelay", "(JJ)V", (void*) setStartDelay },
diff --git a/core/jni/android_view_Surface.cpp b/core/jni/android_view_Surface.cpp
index 6c9d060..11f87cc 100644
--- a/core/jni/android_view_Surface.cpp
+++ b/core/jni/android_view_Surface.cpp
@@ -70,7 +70,7 @@
 
 static struct {
     jfieldID mSurfaceFormat;
-    jmethodID safeCanvasSwap;
+    jmethodID setNativeBitmap;
 } gCanvasClassInfo;
 
 // ----------------------------------------------------------------------------
@@ -232,10 +232,11 @@
         bitmap.setPixels(NULL);
     }
 
-    SkCanvas* nativeCanvas = SkNEW_ARGS(SkCanvas, (bitmap));
-    env->CallVoidMethod(canvasObj, gCanvasClassInfo.safeCanvasSwap, (jlong)nativeCanvas, false);
+    env->CallVoidMethod(canvasObj, gCanvasClassInfo.setNativeBitmap,
+                        reinterpret_cast<jlong>(&bitmap));
 
     if (dirtyRectPtr) {
+        SkCanvas* nativeCanvas = GraphicsJNI::getNativeCanvas(env, canvasObj);
         nativeCanvas->clipRect( SkRect::Make(reinterpret_cast<const SkIRect&>(dirtyRect)) );
     }
 
@@ -262,8 +263,7 @@
     }
 
     // detach the canvas from the surface
-    SkCanvas* nativeCanvas = SkNEW(SkCanvas);
-    env->CallVoidMethod(canvasObj, gCanvasClassInfo.safeCanvasSwap, (jlong)nativeCanvas, false);
+    env->CallVoidMethod(canvasObj, gCanvasClassInfo.setNativeBitmap, (jlong)0);
 
     // unlock surface
     status_t err = surface->unlockAndPost();
@@ -375,7 +375,7 @@
 
     clazz = env->FindClass("android/graphics/Canvas");
     gCanvasClassInfo.mSurfaceFormat = env->GetFieldID(clazz, "mSurfaceFormat", "I");
-    gCanvasClassInfo.safeCanvasSwap = env->GetMethodID(clazz, "safeCanvasSwap", "(JZ)V");
+    gCanvasClassInfo.setNativeBitmap = env->GetMethodID(clazz, "setNativeBitmap", "(J)V");
 
     clazz = env->FindClass("android/graphics/Rect");
     gRectClassInfo.left = env->GetFieldID(clazz, "left", "I");
diff --git a/core/jni/android_view_SurfaceControl.cpp b/core/jni/android_view_SurfaceControl.cpp
index 5a935a9..4594cc3 100644
--- a/core/jni/android_view_SurfaceControl.cpp
+++ b/core/jni/android_view_SurfaceControl.cpp
@@ -61,6 +61,13 @@
     jfieldID secure;
 } gPhysicalDisplayInfoClassInfo;
 
+static struct {
+    jfieldID bottom;
+    jfieldID left;
+    jfieldID right;
+    jfieldID top;
+} gRectClassInfo;
+
 // Implements SkMallocPixelRef::ReleaseProc, to delete the screenshot on unref.
 void DeleteScreenshot(void* addr, void* context) {
     SkASSERT(addr == ((ScreenshotClient*) context)->getPixels());
@@ -104,25 +111,32 @@
     ctrl->decStrong((void *)nativeCreate);
 }
 
-static jobject nativeScreenshotBitmap(JNIEnv* env, jclass clazz, jobject displayTokenObj,
-        jint width, jint height, jint minLayer, jint maxLayer, bool allLayers,
-        bool useIdentityTransform) {
+static jobject nativeScreenshotBitmap(JNIEnv* env, jclass clazz,
+        jobject displayTokenObj, jobject sourceCropObj, jint width, jint height,
+        jint minLayer, jint maxLayer, bool allLayers, bool useIdentityTransform) {
     sp<IBinder> displayToken = ibinderForJavaObject(env, displayTokenObj);
     if (displayToken == NULL) {
         return NULL;
     }
 
+    int left = env->GetIntField(sourceCropObj, gRectClassInfo.left);
+    int top = env->GetIntField(sourceCropObj, gRectClassInfo.top);
+    int right = env->GetIntField(sourceCropObj, gRectClassInfo.right);
+    int bottom = env->GetIntField(sourceCropObj, gRectClassInfo.bottom);
+    Rect sourceCrop(left, top, right, bottom);
+
     ScreenshotClient* screenshot = new ScreenshotClient();
     status_t res;
     if (width > 0 && height > 0) {
         if (allLayers) {
-            res = screenshot->update(displayToken, width, height, useIdentityTransform);
-        } else {
-            res = screenshot->update(displayToken, width, height, minLayer, maxLayer,
+            res = screenshot->update(displayToken, sourceCrop, width, height,
                     useIdentityTransform);
+        } else {
+            res = screenshot->update(displayToken, sourceCrop, width, height,
+                    minLayer, maxLayer, useIdentityTransform);
         }
     } else {
-        res = screenshot->update(displayToken, useIdentityTransform);
+        res = screenshot->update(displayToken, sourceCrop, useIdentityTransform);
     }
     if (res != NO_ERROR) {
         delete screenshot;
@@ -174,20 +188,25 @@
             GraphicsJNI::kBitmapCreateFlag_Premultiplied, NULL);
 }
 
-static void nativeScreenshot(JNIEnv* env, jclass clazz,
-        jobject displayTokenObj, jobject surfaceObj,
-        jint width, jint height, jint minLayer, jint maxLayer, bool allLayers,
-        bool useIdentityTransform) {
+static void nativeScreenshot(JNIEnv* env, jclass clazz, jobject displayTokenObj,
+        jobject surfaceObj, jobject sourceCropObj, jint width, jint height,
+        jint minLayer, jint maxLayer, bool allLayers, bool useIdentityTransform) {
     sp<IBinder> displayToken = ibinderForJavaObject(env, displayTokenObj);
     if (displayToken != NULL) {
         sp<Surface> consumer = android_view_Surface_getSurface(env, surfaceObj);
         if (consumer != NULL) {
+            int left = env->GetIntField(sourceCropObj, gRectClassInfo.left);
+            int top = env->GetIntField(sourceCropObj, gRectClassInfo.top);
+            int right = env->GetIntField(sourceCropObj, gRectClassInfo.right);
+            int bottom = env->GetIntField(sourceCropObj, gRectClassInfo.bottom);
+            Rect sourceCrop(left, top, right, bottom);
+
             if (allLayers) {
                 minLayer = 0;
                 maxLayer = -1;
             }
-            ScreenshotClient::capture(
-                    displayToken, consumer->getIGraphicBufferProducer(),
+            ScreenshotClient::capture(displayToken,
+                    consumer->getIGraphicBufferProducer(), sourceCrop,
                     width, height, uint32_t(minLayer), uint32_t(maxLayer),
                     useIdentityTransform);
         }
@@ -563,9 +582,9 @@
             (void*)nativeRelease },
     {"nativeDestroy", "(J)V",
             (void*)nativeDestroy },
-    {"nativeScreenshot", "(Landroid/os/IBinder;IIIIZZ)Landroid/graphics/Bitmap;",
+    {"nativeScreenshot", "(Landroid/os/IBinder;Landroid/graphics/Rect;IIIIZZ)Landroid/graphics/Bitmap;",
             (void*)nativeScreenshotBitmap },
-    {"nativeScreenshot", "(Landroid/os/IBinder;Landroid/view/Surface;IIIIZZ)V",
+    {"nativeScreenshot", "(Landroid/os/IBinder;Landroid/view/Surface;Landroid/graphics/Rect;IIIIZZ)V",
             (void*)nativeScreenshot },
     {"nativeOpenTransaction", "()V",
             (void*)nativeOpenTransaction },
@@ -640,6 +659,12 @@
     gPhysicalDisplayInfoClassInfo.yDpi = env->GetFieldID(clazz, "yDpi", "F");
     gPhysicalDisplayInfoClassInfo.secure = env->GetFieldID(clazz, "secure", "Z");
 
+    jclass rectClazz = env->FindClass("android/graphics/Rect");
+    gRectClassInfo.bottom = env->GetFieldID(rectClazz, "bottom", "I");
+    gRectClassInfo.left = env->GetFieldID(rectClazz, "left", "I");
+    gRectClassInfo.right = env->GetFieldID(rectClazz, "right", "I");
+    gRectClassInfo.top = env->GetFieldID(rectClazz, "top", "I");
+
     jclass frameStatsClazz = env->FindClass("android/view/FrameStats");
     jfieldID undefined_time_nano_field =  env->GetStaticFieldID(frameStatsClazz, "UNDEFINED_TIME_NANO", "J");
     nsecs_t undefined_time_nano = env->GetStaticLongField(frameStatsClazz, undefined_time_nano_field);
diff --git a/core/jni/android_view_TextureView.cpp b/core/jni/android_view_TextureView.cpp
index 9258543..c1ab515 100644
--- a/core/jni/android_view_TextureView.cpp
+++ b/core/jni/android_view_TextureView.cpp
@@ -29,6 +29,8 @@
 #include <SkCanvas.h>
 #include <SkImage.h>
 
+#include "android/graphics/GraphicsJNI.h"
+
 namespace android {
 
 // ----------------------------------------------------------------------------
@@ -45,7 +47,7 @@
 
 static struct {
     jfieldID mSurfaceFormat;
-    jmethodID safeCanvasSwap;
+    jmethodID setNativeBitmap;
 } gCanvasClassInfo;
 
 static struct {
@@ -159,12 +161,11 @@
     }
 
     SET_INT(canvas, gCanvasClassInfo.mSurfaceFormat, buffer.format);
-
-    SkCanvas* nativeCanvas = SkNEW_ARGS(SkCanvas, (bitmap));
-    INVOKEV(canvas, gCanvasClassInfo.safeCanvasSwap, (jlong)nativeCanvas, false);
+    INVOKEV(canvas, gCanvasClassInfo.setNativeBitmap, reinterpret_cast<jlong>(&bitmap));
 
     SkRect clipRect;
     clipRect.set(rect.left, rect.top, rect.right, rect.bottom);
+    SkCanvas* nativeCanvas = GraphicsJNI::getNativeCanvas(env, canvas);
     nativeCanvas->clipRect(clipRect);
 
     if (dirtyRect) {
@@ -178,8 +179,7 @@
 static void android_view_TextureView_unlockCanvasAndPost(JNIEnv* env, jobject,
         jlong nativeWindow, jobject canvas) {
 
-    SkCanvas* nativeCanvas = SkNEW(SkCanvas);
-    INVOKEV(canvas, gCanvasClassInfo.safeCanvasSwap, (jlong)nativeCanvas, false);
+    INVOKEV(canvas, gCanvasClassInfo.setNativeBitmap, (jlong)0);
 
     if (nativeWindow) {
         sp<ANativeWindow> window((ANativeWindow*) nativeWindow);
@@ -228,7 +228,7 @@
 
     FIND_CLASS(clazz, "android/graphics/Canvas");
     GET_FIELD_ID(gCanvasClassInfo.mSurfaceFormat, clazz, "mSurfaceFormat", "I");
-    GET_METHOD_ID(gCanvasClassInfo.safeCanvasSwap, clazz, "safeCanvasSwap", "(JZ)V");
+    GET_METHOD_ID(gCanvasClassInfo.setNativeBitmap, clazz, "setNativeBitmap", "(J)V");
 
     FIND_CLASS(clazz, "android/view/TextureView");
     GET_FIELD_ID(gTextureViewClassInfo.nativeWindow, clazz, "mNativeWindow", "J");
diff --git a/core/jni/android_view_ThreadedRenderer.cpp b/core/jni/android_view_ThreadedRenderer.cpp
index 5bc0f62..a550649 100644
--- a/core/jni/android_view_ThreadedRenderer.cpp
+++ b/core/jni/android_view_ThreadedRenderer.cpp
@@ -22,6 +22,10 @@
 #include <nativehelper/JNIHelp.h>
 #include <android_runtime/AndroidRuntime.h>
 
+#include <EGL/egl.h>
+#include <EGL/eglext.h>
+#include <EGL/egl_cache.h>
+
 #include <utils/StrongPointer.h>
 #include <android_runtime/android_view_Surface.h>
 #include <system/window.h>
@@ -238,10 +242,11 @@
 }
 
 static int android_view_ThreadedRenderer_syncAndDrawFrame(JNIEnv* env, jobject clazz,
-        jlong proxyPtr, jlong frameTimeNanos, jint dirtyLeft, jint dirtyTop,
-        jint dirtyRight, jint dirtyBottom) {
+        jlong proxyPtr, jlong frameTimeNanos, jlong recordDuration, jfloat density,
+        jint dirtyLeft, jint dirtyTop, jint dirtyRight, jint dirtyBottom) {
     RenderProxy* proxy = reinterpret_cast<RenderProxy*>(proxyPtr);
-    return proxy->syncAndDrawFrame(frameTimeNanos, dirtyLeft, dirtyTop, dirtyRight, dirtyBottom);
+    return proxy->syncAndDrawFrame(frameTimeNanos, recordDuration, density,
+            dirtyLeft, dirtyTop, dirtyRight, dirtyBottom);
 }
 
 static void android_view_ThreadedRenderer_destroyCanvasAndSurface(JNIEnv* env, jobject clazz,
@@ -286,11 +291,24 @@
     return proxy->copyLayerInto(layer, bitmap);
 }
 
-static void android_view_ThreadedRenderer_destroyLayer(JNIEnv* env, jobject clazz,
+static void android_view_ThreadedRenderer_pushLayerUpdate(JNIEnv* env, jobject clazz,
         jlong proxyPtr, jlong layerPtr) {
     RenderProxy* proxy = reinterpret_cast<RenderProxy*>(proxyPtr);
     DeferredLayerUpdater* layer = reinterpret_cast<DeferredLayerUpdater*>(layerPtr);
-    proxy->destroyLayer(layer);
+    proxy->pushLayerUpdate(layer);
+}
+
+static void android_view_ThreadedRenderer_cancelLayerUpdate(JNIEnv* env, jobject clazz,
+        jlong proxyPtr, jlong layerPtr) {
+    RenderProxy* proxy = reinterpret_cast<RenderProxy*>(proxyPtr);
+    DeferredLayerUpdater* layer = reinterpret_cast<DeferredLayerUpdater*>(layerPtr);
+    proxy->cancelLayerUpdate(layer);
+}
+
+static void android_view_ThreadedRenderer_flushCaches(JNIEnv* env, jobject clazz,
+        jlong proxyPtr, jint flushMode) {
+    RenderProxy* proxy = reinterpret_cast<RenderProxy*>(proxyPtr);
+    proxy->flushCaches(static_cast<Caches::FlushMode>(flushMode));
 }
 
 static void android_view_ThreadedRenderer_fence(JNIEnv* env, jobject clazz,
@@ -305,9 +323,28 @@
     proxy->notifyFramePending();
 }
 
+static void android_view_ThreadedRenderer_dumpProfileInfo(JNIEnv* env, jobject clazz,
+        jlong proxyPtr, jobject javaFileDescriptor) {
+    RenderProxy* proxy = reinterpret_cast<RenderProxy*>(proxyPtr);
+    int fd = jniGetFDFromFileDescriptor(env, javaFileDescriptor);
+    proxy->dumpProfileInfo(fd);
+}
+
 #endif
 
 // ----------------------------------------------------------------------------
+// Shaders
+// ----------------------------------------------------------------------------
+
+static void android_view_ThreadedRenderer_setupShadersDiskCache(JNIEnv* env, jobject clazz,
+        jstring diskCachePath) {
+
+    const char* cacheArray = env->GetStringUTFChars(diskCachePath, NULL);
+    egl_cache_t::get()->setCacheFilename(cacheArray);
+    env->ReleaseStringUTFChars(diskCachePath, cacheArray);
+}
+
+// ----------------------------------------------------------------------------
 // JNI Glue
 // ----------------------------------------------------------------------------
 
@@ -326,17 +363,22 @@
     { "nPauseSurface", "(JLandroid/view/Surface;)V", (void*) android_view_ThreadedRenderer_pauseSurface },
     { "nSetup", "(JIIFFFF)V", (void*) android_view_ThreadedRenderer_setup },
     { "nSetOpaque", "(JZ)V", (void*) android_view_ThreadedRenderer_setOpaque },
-    { "nSyncAndDrawFrame", "(JJIIII)I", (void*) android_view_ThreadedRenderer_syncAndDrawFrame },
+    { "nSyncAndDrawFrame", "(JJJFIIII)I", (void*) android_view_ThreadedRenderer_syncAndDrawFrame },
     { "nDestroyCanvasAndSurface", "(J)V", (void*) android_view_ThreadedRenderer_destroyCanvasAndSurface },
     { "nInvokeFunctor", "(JJZ)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 },
     { "nCopyLayerInto", "(JJJ)Z", (void*) android_view_ThreadedRenderer_copyLayerInto },
-    { "nDestroyLayer", "(JJ)V", (void*) android_view_ThreadedRenderer_destroyLayer },
+    { "nPushLayerUpdate", "(JJ)V", (void*) android_view_ThreadedRenderer_pushLayerUpdate },
+    { "nCancelLayerUpdate", "(JJ)V", (void*) android_view_ThreadedRenderer_cancelLayerUpdate },
+    { "nFlushCaches", "(JI)V", (void*) android_view_ThreadedRenderer_flushCaches },
     { "nFence", "(J)V", (void*) android_view_ThreadedRenderer_fence },
     { "nNotifyFramePending", "(J)V", (void*) android_view_ThreadedRenderer_notifyFramePending },
+    { "nDumpProfileInfo", "(JLjava/io/FileDescriptor;)V", (void*) android_view_ThreadedRenderer_dumpProfileInfo },
 #endif
+    { "setupShadersDiskCache", "(Ljava/lang/String;)V",
+                (void*) android_view_ThreadedRenderer_setupShadersDiskCache },
 };
 
 int register_android_view_ThreadedRenderer(JNIEnv* env) {
diff --git a/core/jni/com_android_internal_content_NativeLibraryHelper.cpp b/core/jni/com_android_internal_content_NativeLibraryHelper.cpp
index 230658f..e55e4ea 100644
--- a/core/jni/com_android_internal_content_NativeLibraryHelper.cpp
+++ b/core/jni/com_android_internal_content_NativeLibraryHelper.cpp
@@ -46,6 +46,9 @@
 #define LIB_SUFFIX ".so"
 #define LIB_SUFFIX_LEN (sizeof(LIB_SUFFIX) - 1)
 
+#define RS_BITCODE_SUFFIX ".bc"
+#define RS_BITCODE_SUFFIX_LEN (sizeof(RS_BITCODE_SUFFIX) -1)
+
 #define GDBSERVER "gdbserver"
 #define GDBSERVER_LEN (sizeof(GDBSERVER) - 1)
 
@@ -486,6 +489,42 @@
     return (jint) findSupportedAbi(env, apkHandle, javaCpuAbisToSearch);
 }
 
+enum bitcode_scan_result_t {
+  APK_SCAN_ERROR = -1,
+  NO_BITCODE_PRESENT = 0,
+  BITCODE_PRESENT = 1,
+};
+
+static jint
+com_android_internal_content_NativeLibraryHelper_hasRenderscriptBitcode(JNIEnv *env, jclass clazz,
+        jlong apkHandle) {
+    ZipFileRO* zipFile = reinterpret_cast<ZipFileRO*>(apkHandle);
+    void* cookie = NULL;
+    if (!zipFile->startIteration(&cookie)) {
+        return APK_SCAN_ERROR;
+    }
+
+    char fileName[PATH_MAX];
+    ZipEntryRO next = NULL;
+    while ((next = zipFile->nextEntry(cookie)) != NULL) {
+        if (zipFile->getEntryFileName(next, fileName, sizeof(fileName))) {
+            continue;
+        }
+
+        const size_t fileNameLen = strlen(fileName);
+        const char* lastSlash = strrchr(fileName, '/');
+        const char* baseName = (lastSlash == NULL) ? fileName : fileName + 1;
+        if (!strncmp(fileName + fileNameLen - RS_BITCODE_SUFFIX_LEN, RS_BITCODE_SUFFIX,
+                     RS_BITCODE_SUFFIX_LEN) && isFilenameSafe(baseName)) {
+            zipFile->endIteration(cookie);
+            return BITCODE_PRESENT;
+        }
+    }
+
+    zipFile->endIteration(cookie);
+    return NO_BITCODE_PRESENT;
+}
+
 static jlong
 com_android_internal_content_NativeLibraryHelper_openApk(JNIEnv *env, jclass, jstring apkPath)
 {
@@ -517,6 +556,8 @@
     {"nativeFindSupportedAbi",
             "(J[Ljava/lang/String;)I",
             (void *)com_android_internal_content_NativeLibraryHelper_findSupportedAbi},
+    {"hasRenderscriptBitcode", "(J)I",
+            (void *)com_android_internal_content_NativeLibraryHelper_hasRenderscriptBitcode},
 };
 
 
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 14141d7..fe703b2 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -258,6 +258,12 @@
     <protected-broadcast
         android:name="com.android.server.connectivityservice.CONNECTED_TO_PROVISIONING_NETWORK_ACTION" />
 
+    <!-- Defined in RestrictionsManager -->
+    <protected-broadcast
+        android:name="android.intent.action.PERMISSION_RESPONSE_RECEIVED" />
+    <!-- Defined in RestrictionsManager -->
+    <protected-broadcast android:name="android.intent.action.REQUEST_PERMISSION" />
+
     <!-- ====================================== -->
     <!-- Permissions for things that cost money -->
     <!-- ====================================== -->
@@ -2070,7 +2076,7 @@
         android:description="@string/permdesc_bindRemoteDisplay"
         android:protectionLevel="signature" />
 
-    <!-- Must be required by a {@link android.tv.TvInputService}
+    <!-- Must be required by a {@link android.media.tv.TvInputService}
          to ensure that only the system can bind to it. -->
     <permission android:name="android.permission.BIND_TV_INPUT"
         android:label="@string/permlab_bindTvInput"
diff --git a/core/res/res/anim/input_method_exit.xml b/core/res/res/anim/input_method_exit.xml
index e87352f..117774a 100644
--- a/core/res/res/anim/input_method_exit.xml
+++ b/core/res/res/anim/input_method_exit.xml
@@ -1,8 +1,6 @@
 <?xml version="1.0" encoding="utf-8"?>
 <!--
-/* //device/apps/common/res/anim/fade_out.xml
-**
-** Copyright 2007, The Android Open Source Project
+/* Copyright 2007, 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. 
diff --git a/core/res/res/anim/lock_screen_behind_enter.xml b/core/res/res/anim/lock_screen_behind_enter.xml
index cb47b3c..7e212be 100644
--- a/core/res/res/anim/lock_screen_behind_enter.xml
+++ b/core/res/res/anim/lock_screen_behind_enter.xml
@@ -18,11 +18,17 @@
 -->
 
 <set xmlns:android="http://schemas.android.com/apk/res/android"
-    android:background="#ff000000" android:shareInterpolator="false">
+    android:detachWallpaper="true" android:shareInterpolator="false" android:startOffset="60">
     <alpha
         android:fromAlpha="0.0" android:toAlpha="1.0"
         android:fillEnabled="true" android:fillBefore="true"
-        android:interpolator="@interpolator/decelerate_quint"
-        android:startOffset="@android:integer/config_shortAnimTime"
-        android:duration="@android:integer/config_shortAnimTime"/>
+        android:interpolator="@interpolator/linear_out_slow_in"
+        android:duration="@integer/config_shortAnimTime"/>
+    <scale
+        android:fromXScale="0.95" android:toXScale="1.0"
+        android:fromYScale="0.95" android:toYScale="1.0"
+        android:pivotX="50%" android:pivotY="50%"
+        android:fillEnabled="true" android:fillBefore="true"
+        android:interpolator="@interpolator/linear_out_slow_in"
+        android:duration="@integer/config_shortAnimTime" />
 </set>
\ No newline at end of file
diff --git a/core/res/res/anim/lock_screen_wallpaper_behind_enter.xml b/core/res/res/anim/voice_activity_close_enter.xml
similarity index 60%
copy from core/res/res/anim/lock_screen_wallpaper_behind_enter.xml
copy to core/res/res/anim/voice_activity_close_enter.xml
index c29fd1a..4f3d3d5 100644
--- a/core/res/res/anim/lock_screen_wallpaper_behind_enter.xml
+++ b/core/res/res/anim/voice_activity_close_enter.xml
@@ -1,7 +1,6 @@
 <?xml version="1.0" encoding="utf-8"?>
 <!--
-/*
-** Copyright 2007, The Android Open Source Project
+/* 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.
@@ -16,13 +15,9 @@
 ** limitations under the License.
 */
 -->
-
 <set xmlns:android="http://schemas.android.com/apk/res/android"
-    android:detachWallpaper="true" android:shareInterpolator="false">
-    <alpha
-        android:fromAlpha="0.0" android:toAlpha="1.0"
-        android:fillEnabled="true" android:fillBefore="true"
-        android:interpolator="@interpolator/decelerate_quad"
-        android:startOffset="@android:integer/config_shortAnimTime"
-        android:duration="@android:integer/config_shortAnimTime"/>
+	android:shareInterpolator="false">
+    <alpha android:fromAlpha="1.0" android:toAlpha="1.0"
+			android:interpolator="@interpolator/accelerate_cubic"
+            android:duration="@android:integer/config_shortAnimTime"/>
 </set>
diff --git a/core/res/res/anim/voice_activity_close_exit.xml b/core/res/res/anim/voice_activity_close_exit.xml
new file mode 100644
index 0000000..023b012
--- /dev/null
+++ b/core/res/res/anim/voice_activity_close_exit.xml
@@ -0,0 +1,26 @@
+<?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.
+*/
+-->
+<set xmlns:android="http://schemas.android.com/apk/res/android"
+	android:shareInterpolator="false">
+    <translate android:fromYDelta="0" android:toYDelta="-20%"
+			android:interpolator="@interpolator/accelerate_quint"
+            android:duration="@android:integer/config_shortAnimTime"/>
+    <alpha android:fromAlpha="1.0" android:toAlpha="0.0"
+			android:interpolator="@interpolator/accelerate_cubic"
+            android:duration="@android:integer/config_shortAnimTime"/>
+</set>
diff --git a/core/res/res/anim/lock_screen_wallpaper_behind_enter.xml b/core/res/res/anim/voice_activity_open_enter.xml
similarity index 63%
rename from core/res/res/anim/lock_screen_wallpaper_behind_enter.xml
rename to core/res/res/anim/voice_activity_open_enter.xml
index c29fd1a..ce7a4f9 100644
--- a/core/res/res/anim/lock_screen_wallpaper_behind_enter.xml
+++ b/core/res/res/anim/voice_activity_open_enter.xml
@@ -1,6 +1,6 @@
 <?xml version="1.0" encoding="utf-8"?>
 <!--
-/*
+**
 ** Copyright 2007, The Android Open Source Project
 **
 ** Licensed under the Apache License, Version 2.0 (the "License");
@@ -18,11 +18,11 @@
 -->
 
 <set xmlns:android="http://schemas.android.com/apk/res/android"
-    android:detachWallpaper="true" android:shareInterpolator="false">
-    <alpha
-        android:fromAlpha="0.0" android:toAlpha="1.0"
-        android:fillEnabled="true" android:fillBefore="true"
-        android:interpolator="@interpolator/decelerate_quad"
-        android:startOffset="@android:integer/config_shortAnimTime"
-        android:duration="@android:integer/config_shortAnimTime"/>
+		android:shareInterpolator="false">
+    <translate android:fromYDelta="-20%" android:toYDelta="0"
+	        android:interpolator="@interpolator/decelerate_quint"
+            android:duration="@android:integer/config_shortAnimTime"/>
+    <alpha android:fromAlpha="0.5" android:toAlpha="1.0"
+			android:interpolator="@interpolator/decelerate_cubic"
+            android:duration="@android:integer/config_shortAnimTime" />
 </set>
diff --git a/core/res/res/anim/lock_screen_wallpaper_behind_enter.xml b/core/res/res/anim/voice_activity_open_exit.xml
similarity index 60%
copy from core/res/res/anim/lock_screen_wallpaper_behind_enter.xml
copy to core/res/res/anim/voice_activity_open_exit.xml
index c29fd1a..4f3d3d5 100644
--- a/core/res/res/anim/lock_screen_wallpaper_behind_enter.xml
+++ b/core/res/res/anim/voice_activity_open_exit.xml
@@ -1,7 +1,6 @@
 <?xml version="1.0" encoding="utf-8"?>
 <!--
-/*
-** Copyright 2007, The Android Open Source Project
+/* 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.
@@ -16,13 +15,9 @@
 ** limitations under the License.
 */
 -->
-
 <set xmlns:android="http://schemas.android.com/apk/res/android"
-    android:detachWallpaper="true" android:shareInterpolator="false">
-    <alpha
-        android:fromAlpha="0.0" android:toAlpha="1.0"
-        android:fillEnabled="true" android:fillBefore="true"
-        android:interpolator="@interpolator/decelerate_quad"
-        android:startOffset="@android:integer/config_shortAnimTime"
-        android:duration="@android:integer/config_shortAnimTime"/>
+	android:shareInterpolator="false">
+    <alpha android:fromAlpha="1.0" android:toAlpha="1.0"
+			android:interpolator="@interpolator/accelerate_cubic"
+            android:duration="@android:integer/config_shortAnimTime"/>
 </set>
diff --git a/core/res/res/anim/lock_screen_wallpaper_behind_enter.xml b/core/res/res/anim/voice_layer_enter.xml
similarity index 63%
copy from core/res/res/anim/lock_screen_wallpaper_behind_enter.xml
copy to core/res/res/anim/voice_layer_enter.xml
index c29fd1a..ce7a4f9 100644
--- a/core/res/res/anim/lock_screen_wallpaper_behind_enter.xml
+++ b/core/res/res/anim/voice_layer_enter.xml
@@ -1,6 +1,6 @@
 <?xml version="1.0" encoding="utf-8"?>
 <!--
-/*
+**
 ** Copyright 2007, The Android Open Source Project
 **
 ** Licensed under the Apache License, Version 2.0 (the "License");
@@ -18,11 +18,11 @@
 -->
 
 <set xmlns:android="http://schemas.android.com/apk/res/android"
-    android:detachWallpaper="true" android:shareInterpolator="false">
-    <alpha
-        android:fromAlpha="0.0" android:toAlpha="1.0"
-        android:fillEnabled="true" android:fillBefore="true"
-        android:interpolator="@interpolator/decelerate_quad"
-        android:startOffset="@android:integer/config_shortAnimTime"
-        android:duration="@android:integer/config_shortAnimTime"/>
+		android:shareInterpolator="false">
+    <translate android:fromYDelta="-20%" android:toYDelta="0"
+	        android:interpolator="@interpolator/decelerate_quint"
+            android:duration="@android:integer/config_shortAnimTime"/>
+    <alpha android:fromAlpha="0.5" android:toAlpha="1.0"
+			android:interpolator="@interpolator/decelerate_cubic"
+            android:duration="@android:integer/config_shortAnimTime" />
 </set>
diff --git a/core/res/res/anim/voice_layer_exit.xml b/core/res/res/anim/voice_layer_exit.xml
new file mode 100644
index 0000000..023b012
--- /dev/null
+++ b/core/res/res/anim/voice_layer_exit.xml
@@ -0,0 +1,26 @@
+<?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.
+*/
+-->
+<set xmlns:android="http://schemas.android.com/apk/res/android"
+	android:shareInterpolator="false">
+    <translate android:fromYDelta="0" android:toYDelta="-20%"
+			android:interpolator="@interpolator/accelerate_quint"
+            android:duration="@android:integer/config_shortAnimTime"/>
+    <alpha android:fromAlpha="1.0" android:toAlpha="0.0"
+			android:interpolator="@interpolator/accelerate_cubic"
+            android:duration="@android:integer/config_shortAnimTime"/>
+</set>
diff --git a/core/res/res/color/btn_default_quantum_dark.xml b/core/res/res/color/btn_default_quantum_dark.xml
index f2e772d..ec0f140 100644
--- a/core/res/res/color/btn_default_quantum_dark.xml
+++ b/core/res/res/color/btn_default_quantum_dark.xml
@@ -15,6 +15,6 @@
 -->
 
 <selector xmlns:android="http://schemas.android.com/apk/res/android">
-    <item android:state_enabled="false" android:alpha="0.5" android:color="@color/quantum_grey_700"/>
-    <item android:color="@color/quantum_grey_700"/>
+    <item android:state_enabled="false" android:alpha="0.5" android:color="@color/button_quantum_dark"/>
+    <item android:color="@color/button_quantum_dark"/>
 </selector>
diff --git a/core/res/res/color/btn_default_quantum_light.xml b/core/res/res/color/btn_default_quantum_light.xml
index de1bd2c..9536d24 100644
--- a/core/res/res/color/btn_default_quantum_light.xml
+++ b/core/res/res/color/btn_default_quantum_light.xml
@@ -15,6 +15,6 @@
 -->
 
 <selector xmlns:android="http://schemas.android.com/apk/res/android">
-    <item android:state_enabled="false" android:alpha="0.5" android:color="@color/quantum_grey_300"/>
-    <item android:color="@color/quantum_grey_300"/>
+    <item android:state_enabled="false" android:alpha="0.5" android:color="@color/button_quantum_light"/>
+    <item android:color="@color/button_quantum_light"/>
 </selector>
diff --git a/core/res/res/drawable-hdpi/ic_lock_bugreport_alpha.png b/core/res/res/drawable-hdpi/ic_lock_bugreport_alpha.png
deleted file mode 100644
index ba5bd01..0000000
--- a/core/res/res/drawable-hdpi/ic_lock_bugreport_alpha.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-hdpi/work_icon.png b/core/res/res/drawable-hdpi/work_icon.png
index e90866b..be8a36e 100644
--- a/core/res/res/drawable-hdpi/work_icon.png
+++ b/core/res/res/drawable-hdpi/work_icon.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/ic_lock_bugreport_alpha.png b/core/res/res/drawable-mdpi/ic_lock_bugreport_alpha.png
deleted file mode 100644
index 4e2612d..0000000
--- a/core/res/res/drawable-mdpi/ic_lock_bugreport_alpha.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/ic_lock_bugreport_alpha.png b/core/res/res/drawable-xhdpi/ic_lock_bugreport_alpha.png
deleted file mode 100644
index e6ca1ea..0000000
--- a/core/res/res/drawable-xhdpi/ic_lock_bugreport_alpha.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/ic_lock_bugreport_alpha.png b/core/res/res/drawable-xxhdpi/ic_lock_bugreport_alpha.png
deleted file mode 100644
index d6018dd..0000000
--- a/core/res/res/drawable-xxhdpi/ic_lock_bugreport_alpha.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable/ic_audio_ring_notif.xml b/core/res/res/drawable/ic_audio_ring_notif.xml
index 247d1b4..b52db5c 100644
--- a/core/res/res/drawable/ic_audio_ring_notif.xml
+++ b/core/res/res/drawable/ic_audio_ring_notif.xml
@@ -1,23 +1,28 @@
-<?xml version="1.0" encoding="utf-8"?>
 <!--
-/*
- * Copyright 2013, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
--->
+Copyright (C) 2014 The Android Open Source Project
 
-<bitmap xmlns:android="http://schemas.android.com/apk/res/android"
-    android:src="@drawable/ic_audio_ring_notif_am_alpha"
-    android:autoMirrored="true"
-    android:tint="?attr/colorControlNormal" />
+   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="32dp"
+        android:height="32dp"/>
+
+    <viewport
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0"/>
+
+    <path
+        android:fill="#8A000000"
+        android:pathData="M11.5,22.0c1.1,0.0 2.0,-0.9 2.0,-2.0l-4.0,0.0C9.5,21.1 10.4,22.0 11.5,22.0zM18.0,16.0l0.0,-5.5c0.0,-3.1 -2.1,-5.6 -5.0,-6.3L13.0,3.5C13.0,2.7 12.3,2.0 11.5,2.0C10.7,2.0 10.0,2.7 10.0,3.5l0.0,0.7c-2.9,0.7 -5.0,3.2 -5.0,6.3L5.0,16.0l-2.0,2.0l0.0,1.0l17.0,0.0l0.0,-1.0L18.0,16.0z"/>
+</vector>
diff --git a/core/res/res/drawable/ic_audio_ring_notif_mute.xml b/core/res/res/drawable/ic_audio_ring_notif_mute.xml
index 72aaa9d..8d7d6cb 100644
--- a/core/res/res/drawable/ic_audio_ring_notif_mute.xml
+++ b/core/res/res/drawable/ic_audio_ring_notif_mute.xml
@@ -1,23 +1,28 @@
-<?xml version="1.0" encoding="utf-8"?>
 <!--
-/*
- * Copyright 2013, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
--->
+Copyright (C) 2014 The Android Open Source Project
 
-<bitmap xmlns:android="http://schemas.android.com/apk/res/android"
-    android:src="@drawable/ic_audio_ring_notif_mute_am_alpha"
-    android:autoMirrored="true"
-    android:tint="?attr/colorControlNormal" />
+   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="32dp"
+        android:height="32dp"/>
+
+    <viewport
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0"/>
+
+    <path
+        android:fill="#8A000000"
+        android:pathData="M11.5,22.0c1.1,0.0 2.0,-0.9 2.0,-2.0l-4.0,0.0C9.5,21.1 10.4,22.0 11.5,22.0zM18.0,10.5c0.0,-3.1 -2.1,-5.6 -5.0,-6.3L13.0,3.5C13.0,2.7 12.3,2.0 11.5,2.0C10.7,2.0 10.0,2.7 10.0,3.5l0.0,0.7C9.5,4.3 9.0,4.5 8.6,4.7l9.4,9.4L18.0,10.5zM17.7,19.0l2.0,2.0l1.3,-1.3L4.3,3.0L3.0,4.3l2.9,2.9C5.3,8.2 5.0,9.3 5.0,10.5L5.0,16.0l-2.0,2.0l0.0,1.0L17.7,19.0z" />
+</vector>
diff --git a/core/res/res/drawable/ic_audio_ring_notif_vibrate.xml b/core/res/res/drawable/ic_audio_ring_notif_vibrate.xml
index 9e31aba..2f1d940 100644
--- a/core/res/res/drawable/ic_audio_ring_notif_vibrate.xml
+++ b/core/res/res/drawable/ic_audio_ring_notif_vibrate.xml
@@ -1,23 +1,28 @@
-<?xml version="1.0" encoding="utf-8"?>
 <!--
-/*
- * Copyright 2013, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
--->
+Copyright (C) 2014 The Android Open Source Project
 
-<bitmap xmlns:android="http://schemas.android.com/apk/res/android"
-    android:src="@drawable/ic_audio_ring_notif_vibrate_am_alpha"
-    android:autoMirrored="true"
-    android:tint="?attr/colorControlNormal" />
+   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="32dp"
+        android:height="32dp"/>
+
+    <viewport
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0"/>
+
+    <path
+        android:fill="#8A000000"
+        android:pathData="M0.0,15.0l2.0,0.0L2.0,9.0L0.0,9.0L0.0,15.0zM3.0,17.0l2.0,0.0L5.0,7.0L3.0,7.0L3.0,17.0zM22.0,9.0l0.0,6.0l2.0,0.0L24.0,9.0L22.0,9.0zM19.0,17.0l2.0,0.0L21.0,7.0l-2.0,0.0L19.0,17.0zM16.5,3.0l-9.0,0.0C6.7,3.0 6.0,3.7 6.0,4.5l0.0,15.0C6.0,20.3 6.7,21.0 7.5,21.0l9.0,0.0c0.8,0.0 1.5,-0.7 1.5,-1.5l0.0,-15.0C18.0,3.7 17.3,3.0 16.5,3.0zM16.0,19.0L8.0,19.0L8.0,5.0l8.0,0.0L16.0,19.0z"/>
+</vector>
diff --git a/core/res/res/drawable/ic_lock_bugreport.xml b/core/res/res/drawable/ic_lock_bugreport.xml
index a3f82ce..b93a09a 100644
--- a/core/res/res/drawable/ic_lock_bugreport.xml
+++ b/core/res/res/drawable/ic_lock_bugreport.xml
@@ -1,19 +1,28 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2014 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.
-     You may obtain a copy of the License at
+   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
+         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.
+    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="32dp"
+        android:height="32dp"/>
 
-<bitmap xmlns:android="http://schemas.android.com/apk/res/android"
-    android:src="@drawable/ic_lock_bugreport_alpha"
-    android:tint="?attr/colorControlNormal" />
+    <viewport
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0"/>
+
+    <path
+        android:fill="?attr/colorControlNormal"
+        android:pathData="M20.0,8.0l-2.8,0.0c-0.5,-0.8 -1.1,-1.5 -1.8,-2.0L17.0,4.4L15.6,3.0l-2.2,2.2C13.0,5.1 12.5,5.0 12.0,5.0s-1.0,0.1 -1.4,0.2L8.4,3.0L7.0,4.4L8.6,6.0C7.9,6.5 7.3,7.2 6.8,8.0L4.0,8.0l0.0,2.0l2.1,0.0C6.0,10.3 6.0,10.7 6.0,11.0l0.0,1.0L4.0,12.0l0.0,2.0l2.0,0.0l0.0,1.0c0.0,0.3 0.0,0.7 0.1,1.0L4.0,16.0l0.0,2.0l2.8,0.0c1.0,1.8 3.0,3.0 5.2,3.0s4.2,-1.2 5.2,-3.0L20.0,18.0l0.0,-2.0l-2.1,0.0c0.1,-0.3 0.1,-0.7 0.1,-1.0l0.0,-1.0l2.0,0.0l0.0,-2.0l-2.0,0.0l0.0,-1.0c0.0,-0.3 0.0,-0.7 -0.1,-1.0L20.0,10.0L20.0,8.0zM14.0,16.0l-4.0,0.0l0.0,-2.0l4.0,0.0L14.0,16.0zM14.0,12.0l-4.0,0.0l0.0,-2.0l4.0,0.0L14.0,12.0z"/>
+</vector>
diff --git a/core/res/res/drawable/list_selector_quantum.xml b/core/res/res/drawable/item_background_borderless_quantum.xml
similarity index 82%
rename from core/res/res/drawable/list_selector_quantum.xml
rename to core/res/res/drawable/item_background_borderless_quantum.xml
index 6cd59e5..c2a1c127 100644
--- a/core/res/res/drawable/list_selector_quantum.xml
+++ b/core/res/res/drawable/item_background_borderless_quantum.xml
@@ -15,8 +15,5 @@
 -->
 
 <ripple xmlns:android="http://schemas.android.com/apk/res/android"
-    android:tint="?attr/colorControlHighlight">
-    <item android:id="@id/mask">
-        <color android:color="@color/white" />
-    </item>
-</ripple>
+    android:tint="?attr/colorControlHighlight"
+    android:pinned="true" />
diff --git a/core/res/res/drawable/item_background_quantum.xml b/core/res/res/drawable/item_background_quantum.xml
index c2a1c127..039ca51 100644
--- a/core/res/res/drawable/item_background_quantum.xml
+++ b/core/res/res/drawable/item_background_quantum.xml
@@ -15,5 +15,8 @@
 -->
 
 <ripple xmlns:android="http://schemas.android.com/apk/res/android"
-    android:tint="?attr/colorControlHighlight"
-    android:pinned="true" />
+    android:tint="?attr/colorControlHighlight">
+    <item android:id="@id/mask">
+        <color android:color="@color/white" />
+    </item>
+</ripple>
\ No newline at end of file
diff --git a/core/res/res/layout/alert_dialog_quantum.xml b/core/res/res/layout/alert_dialog_quantum.xml
index e109425..7fd22ad 100644
--- a/core/res/res/layout/alert_dialog_quantum.xml
+++ b/core/res/res/layout/alert_dialog_quantum.xml
@@ -23,7 +23,10 @@
     android:orientation="vertical"
     android:background="@drawable/dialog_background_quantum"
     android:translationZ="@dimen/floating_window_z"
-    android:layout_margin="@dimen/floating_window_margin">
+    android:layout_marginLeft="@dimen/floating_window_margin_left"
+    android:layout_marginTop="@dimen/floating_window_margin_top"
+    android:layout_marginRight="@dimen/floating_window_margin_right"
+    android:layout_marginBottom="@dimen/floating_window_margin_bottom">
 
     <LinearLayout android:id="@+id/topPanel"
         android:layout_width="match_parent"
@@ -44,7 +47,7 @@
                 android:scaleType="fitCenter"
                 android:src="@null" />
             <TextView android:id="@+id/alertTitle"
-                style="?android:attr/windowTitleStyle"
+                style="?attr/windowTitleStyle"
                 android:singleLine="true"
                 android:ellipsize="end"
                 android:layout_width="match_parent"
@@ -65,7 +68,7 @@
             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"
@@ -92,26 +95,24 @@
         android:gravity="end"
         android:padding="16dip">
         <LinearLayout
-            style="?android:attr/buttonBarStyle"
+            style="?attr/buttonBarStyle"
             android:layout_width="wrap_content"
             android:layout_height="wrap_content"
             android:layoutDirection="locale">
             <Button android:id="@+id/button3"
-                style="?android:attr/buttonBarButtonStyle"
+                style="?attr/buttonBarButtonStyle"
                 android:layout_width="wrap_content"
                 android:layout_height="wrap_content"
-                android:layout_marginRight="8dip"
                 android:maxLines="2"
                 android:minHeight="@dimen/alert_dialog_button_bar_height" />
             <Button android:id="@+id/button2"
-                style="?android:attr/buttonBarButtonStyle"
+                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="?android:attr/buttonBarButtonStyle"
-                android:layout_marginLeft="8dip"
+                style="?attr/buttonBarButtonStyle"
                 android:layout_width="wrap_content"
                 android:layout_height="wrap_content"
                 android:maxLines="2"
diff --git a/core/res/res/layout/global_actions_silent_mode.xml b/core/res/res/layout/global_actions_silent_mode.xml
index 79401af..a358623 100644
--- a/core/res/res/layout/global_actions_silent_mode.xml
+++ b/core/res/res/layout/global_actions_silent_mode.xml
@@ -37,7 +37,7 @@
             android:layout_marginEnd="8dp"
             android:layout_marginTop="6dp"
             android:layout_marginBottom="6dp"
-            android:src="@drawable/ic_audio_vol_mute"
+            android:src="@drawable/ic_audio_ring_notif_mute"
             android:scaleType="center"
             android:duplicateParentState="true"
             android:background="@drawable/silent_mode_indicator"
@@ -94,7 +94,7 @@
             android:layout_marginEnd="8dp"
             android:layout_marginTop="6dp"
             android:layout_marginBottom="6dp"
-            android:src="@drawable/ic_audio_vol"
+            android:src="@drawable/ic_audio_ring_notif"
             android:scaleType="center"
             android:duplicateParentState="true"
             android:background="@drawable/silent_mode_indicator"
diff --git a/core/res/res/layout/screen_toolbar.xml b/core/res/res/layout/screen_toolbar.xml
new file mode 100644
index 0000000..290c7da
--- /dev/null
+++ b/core/res/res/layout/screen_toolbar.xml
@@ -0,0 +1,51 @@
+<?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.
+-->
+
+<!--
+This is an optimized layout for a screen with a toolbar enabled.
+-->
+
+<com.android.internal.widget.ActionBarOverlayLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@+id/decor_content_parent"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:splitMotionEvents="false"
+    android:theme="?attr/actionBarTheme">
+    <FrameLayout android:id="@android:id/content"
+                 android:layout_width="match_parent"
+                 android:layout_height="match_parent" />
+    <com.android.internal.widget.ActionBarContainer
+        android:id="@+id/action_bar_container"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:layout_alignParentTop="true"
+        style="?attr/actionBarStyle"
+        android:viewName="android:action_bar"
+        android:gravity="top">
+        <Toolbar
+            android:id="@+id/action_bar"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            style="?attr/toolbarStyle" />
+        <com.android.internal.widget.ActionBarContextView
+            android:id="@+id/action_context_bar"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:visibility="gone"
+            style="?attr/actionModeStyle" />
+    </com.android.internal.widget.ActionBarContainer>
+</com.android.internal.widget.ActionBarOverlayLayout>
diff --git a/core/res/res/layout/volume_adjust.xml b/core/res/res/layout/volume_adjust.xml
deleted file mode 100644
index 3ad1f23..0000000
--- a/core/res/res/layout/volume_adjust.xml
+++ /dev/null
@@ -1,51 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2007 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/visible_panel"
-    android:orientation="horizontal"
-    android:layout_width="300dp"
-    android:layout_height="wrap_content">
-
-    <LinearLayout
-        android:id="@+id/slider_group"
-        android:layout_width="0dp"
-        android:layout_height="wrap_content"
-        android:layout_weight="1"
-        android:orientation="vertical">
-        <!-- Sliders go here -->
-    </LinearLayout>
-
-    <ImageView
-        android:id="@+id/expand_button_divider"
-        android:src="?attr/dividerVertical"
-        android:layout_width="wrap_content"
-        android:layout_height="32dip"
-        android:scaleType="fitXY"
-        android:layout_gravity="top"
-        android:layout_marginTop="16dip"
-        android:layout_marginBottom="16dip" />
-
-    <ImageView
-        android:id="@+id/expand_button"
-        android:layout_width="wrap_content"
-        android:layout_height="wrap_content"
-        android:layout_gravity="top"
-        android:padding="16dip"
-        android:background="?attr/selectableItemBackground"
-        android:src="@drawable/ic_sysbar_quicksettings" />
-
-</LinearLayout>
diff --git a/core/res/res/layout/volume_adjust_item.xml b/core/res/res/layout/volume_adjust_item.xml
deleted file mode 100644
index 57cecf4..0000000
--- a/core/res/res/layout/volume_adjust_item.xml
+++ /dev/null
@@ -1,42 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2011 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="80dip"
-    android:orientation="horizontal"
-    android:layout_marginTop="8dip"
-    android:layout_marginBottom="8dip"
-    android:gravity="start|center_vertical">
-
-    <ImageView
-        android:id="@+id/stream_icon"
-        android:layout_width="wrap_content"
-        android:layout_height="wrap_content"
-        android:paddingLeft="16dip"
-        android:background="?attr/selectableItemBackground"
-        android:contentDescription="@null" />
-
-    <SeekBar
-        style="?android:attr/seekBarStyle"
-        android:id="@+id/seekbar"
-        android:layout_width="0dp"
-        android:layout_height="wrap_content"
-        android:layout_weight="1"
-        android:padding="16dip"
-        android:layout_marginEnd="16dip" />
-
-</LinearLayout>
diff --git a/core/res/res/values-da/strings.xml b/core/res/res/values-da/strings.xml
index df59abe..b17b8c6 100644
--- a/core/res/res/values-da/strings.xml
+++ b/core/res/res/values-da/strings.xml
@@ -267,7 +267,7 @@
     <string name="permdesc_statusBarService" msgid="716113660795976060">"Tillader, at appen er statusbjælken."</string>
     <string name="permlab_expandStatusBar" msgid="1148198785937489264">"udvid/skjul statuslinje"</string>
     <string name="permdesc_expandStatusBar" msgid="6917549437129401132">"Tillader, at appen kan udvide og skjule statusbjælken."</string>
-    <string name="permlab_install_shortcut" msgid="4279070216371564234">"installer genveje"</string>
+    <string name="permlab_install_shortcut" msgid="4279070216371564234">"installere genveje"</string>
     <string name="permdesc_install_shortcut" msgid="8341295916286736996">"Tillader, at en applikation føjer genveje til startskærmen uden brugerindgriben."</string>
     <string name="permlab_uninstall_shortcut" msgid="4729634524044003699">"afinstaller genveje"</string>
     <string name="permdesc_uninstall_shortcut" msgid="6745743474265057975">"Tillader, at applikationen fjerner genveje på startskærmen uden brugerindgriben."</string>
@@ -281,7 +281,7 @@
     <string name="permdesc_receiveEmergencyBroadcast" msgid="848524070262431974">"Tillader, at appen kan modtage og behandle nødtransmissioner. Denne tilladelse er kun tilgængelig for systemapps."</string>
     <string name="permlab_readCellBroadcasts" msgid="1598328843619646166">"læse Cell Broadcast-beskeder"</string>
     <string name="permdesc_readCellBroadcasts" msgid="6361972776080458979">"Tillader, at appen læser Cell Broadcast-beskeder, der modtages af din enhed. I nogle områder sendes der Cell Broadcast-beskeder for at advare om nødsituationer. Ondsindede apps kan forstyrre ydelsen eller driften af ​din ​enhed, når der modtages en Cell Broadcast-besked om en nødsituation."</string>
-    <string name="permlab_sendSms" msgid="5600830612147671529">"send sms-beskeder"</string>
+    <string name="permlab_sendSms" msgid="5600830612147671529">"sende sms-beskeder"</string>
     <string name="permdesc_sendSms" msgid="7094729298204937667">"Tillader, at appen kan sende sms-beskeder. Dette kan resultere i uventede opkrævninger. Skadelige apps kan koste dig penge ved at sende beskeder uden din bekræftelse."</string>
     <string name="permlab_sendRespondViaMessageRequest" msgid="8713889105305943200">"send hændelser, hvor der skal svares pr. besked"</string>
     <string name="permdesc_sendRespondViaMessageRequest" msgid="7107648548468778734">"Tillader, at appen kan sende anmodninger til andre apps til beskeder for at håndtere hændelser, hvor der skal svares pr. besked."</string>
@@ -295,7 +295,7 @@
     <string name="permdesc_receiveWapPush" msgid="748232190220583385">"Tillader, at appen kan modtage og behandle WAP-beskeder. Denne tilladelse omfatter muligheden for at overvåge eller slette de beskeder, der sendes til dig, uden at vise dem til dig."</string>
     <string name="permlab_getTasks" msgid="6466095396623933906">"hente kørende apps"</string>
     <string name="permdesc_getTasks" msgid="7454215995847658102">"Tillader, at appen kan hente oplysninger om nuværende og seneste opgaver. Med denne tilladelse kan appen finde oplysninger om, hvilke applikationer der bruges på enheden."</string>
-    <string name="permlab_interactAcrossUsers" msgid="7114255281944211682">"kommuniker på tværs af brugere"</string>
+    <string name="permlab_interactAcrossUsers" msgid="7114255281944211682">"kommunikere på tværs af brugere"</string>
     <string name="permdesc_interactAcrossUsers" msgid="364670963623385786">"Tillader, at appen udfører handlinger på tværs af forskellige brugere på enheden. Ondsindede apps kan bruge dette til at krænke beskyttelsen mellem brugere."</string>
     <string name="permlab_interactAcrossUsersFull" msgid="2567734285545074105">"fuld licens til at kommunikere på tværs af brugere"</string>
     <string name="permdesc_interactAcrossUsersFull" msgid="376841368395502366">"Tillader alle mulige former for kommunikation på tværs af brugere."</string>
@@ -325,7 +325,7 @@
     <string name="permdesc_forceStopPackages" msgid="5253157296183940812">"Tillader, at appen kan tvinge andre apps til at stoppe."</string>
     <string name="permlab_forceBack" msgid="652935204072584616">"tvinge appen til at lukke"</string>
     <string name="permdesc_forceBack" msgid="3892295830419513623">"Tillader, at appen kan tvinge enhver aktivitet i forgrunden til at lukke og gå tilbage. Bør aldrig være nødvendigt til almindelige apps."</string>
-    <string name="permlab_dump" msgid="1681799862438954752">"hent intern systemtilstand"</string>
+    <string name="permlab_dump" msgid="1681799862438954752">"hente intern systemtilstand"</string>
     <string name="permdesc_dump" msgid="1778299088692290329">"Tillader, at appen kan hente systemets interne tilstand. Ondsindede apps kan hente en lang række fortrolige og beskyttede oplysninger, som de normalt aldrig ville have brug for."</string>
     <string name="permlab_retrieve_window_content" msgid="8022588608994589938">"hente skærmindhold"</string>
     <string name="permdesc_retrieve_window_content" msgid="3193269069469700265">"Tillader, at appen kan hente indholdet i det aktive vindue. Ondsindede apps kan hente al indholdet i vinduet og undersøge al dens tekst med undtagelse af adgangskoder."</string>
@@ -359,7 +359,7 @@
     <string name="permdesc_batteryStats" msgid="5897346582882915114">"Tillader, at en applikation læser de aktuelle data for batteriforbruget. Kan tillade, at applikationen henter detaljerede oplysninger om, hvilke apps du bruger."</string>
     <string name="permlab_updateBatteryStats" msgid="3719689764536379557">"rediger batteristatistikker"</string>
     <string name="permdesc_updateBatteryStats" msgid="6862817857178025002">"Tillader, at appen kan ændre indsamlede batteristatistikker. Anvendes ikke af normale apps."</string>
-    <string name="permlab_getAppOpsStats" msgid="1508779687436585744">"hent statistikker for handlinger i appen"</string>
+    <string name="permlab_getAppOpsStats" msgid="1508779687436585744">"hente statistikker om handlinger i appen"</string>
     <string name="permdesc_getAppOpsStats" msgid="6243887041577912877">"Tillader, at appen indhenter statistikker for handlinger i applikationen. Denne handling bruges ikke i almindelige apps."</string>
     <string name="permlab_updateAppOpsStats" msgid="8829097373851521505">"lav ændringer i statistik for handlinger i appen"</string>
     <string name="permdesc_updateAppOpsStats" msgid="50784596594403483">"Tillader, at appen kan ændre indsamlede statistikker for handlinger i applikationen. Dette kan ikke bruges af almindelige apps."</string>
@@ -474,7 +474,7 @@
     <string name="permlab_writeContacts" msgid="5107492086416793544">"ændre dine kontaktpersoner"</string>
     <string name="permdesc_writeContacts" product="tablet" msgid="897243932521953602">"Tillader, at appen kan ændre data om de kontaktpersoner, der er gemt på din tablet, f.eks. hvor ofte du har ringet til dem, sendt dem en e-mail eller på anden måde kommunikeret med bestemte kontaktpersoner. Med denne tilladelse kan apps slette kontaktoplysninger."</string>
     <string name="permdesc_writeContacts" product="default" msgid="589869224625163558">"Tillader, at appen kan ændre data om de kontaktpersoner, der er gemt på din telefon, f.eks. hvor ofte du har ringet til dem, sendt en e-mail til dem eller på anden måde kommunikeret med bestemte kontaktpersoner. Med denne tilladelse kan apps slette kontaktoplysninger."</string>
-    <string name="permlab_readCallLog" msgid="3478133184624102739">"læs opkaldsliste"</string>
+    <string name="permlab_readCallLog" msgid="3478133184624102739">"læse opkaldsliste"</string>
     <string name="permdesc_readCallLog" product="tablet" msgid="3700645184870760285">"Tillader, at appen kan læse din tablets opkaldsliste, f.eks. data om indgående og udgående opkald. Med denne tilladelse kan apps gemme dine opkaldslistedata, og skadelige apps kan dele opkaldslistedata uden din viden."</string>
     <string name="permdesc_readCallLog" product="default" msgid="5777725796813217244">"Tillader, at appen kan læse telefonens opkaldsliste, f.eks. data om indgående og udgående opkald. Med denne tilladelse kan apps gemme dine opkaldslistedata, og skadelige apps kan dele disse opkaldslistedata uden din viden."</string>
     <string name="permlab_writeCallLog" msgid="8552045664743499354">"skriv opkaldsliste"</string>
@@ -486,9 +486,9 @@
     <string name="permdesc_writeProfile" product="default" msgid="5552084294598465899">"Tillader, at appen kan ændre eller tilføje oplysninger i din personlige profil, der er gemt på din enhed, f.eks. dit navn eller dine kontaktoplysninger. Dette betyder, at andre apps kan identificere dig og sende profiloplysninger til andre."</string>
     <string name="permlab_bodySensors" msgid="4871091374767171066">"kropssensorer (f.eks. pulsmålere)"</string>
     <string name="permdesc_bodySensors" product="default" msgid="2998865085124153531">"Tillader, at appen får adgang til data fra sensorer, du bruger til at måle, hvad der sker inde i din krop, f.eks. din puls."</string>
-    <string name="permlab_readSocialStream" product="default" msgid="1268920956152419170">"læs din sociale strøm"</string>
+    <string name="permlab_readSocialStream" product="default" msgid="1268920956152419170">"læse din sociale strøm"</string>
     <string name="permdesc_readSocialStream" product="default" msgid="4255706027172050872">"Tillader, at appen kan få adgang til og synkronisere sociale opdateringer fra dig og dine venner. Vær forsigtig, når du deler oplysninger – med denne tilladelse kan appen læse kommunikation mellem dig og dine venner på sociale netværk, uanset fortrolighed. Bemærk! Denne tilladelse håndhæves muligvis ikke på alle sociale netværk."</string>
-    <string name="permlab_writeSocialStream" product="default" msgid="3504179222493235645">"skriv i din sociale strøm"</string>
+    <string name="permlab_writeSocialStream" product="default" msgid="3504179222493235645">"skrive i din sociale strøm"</string>
     <string name="permdesc_writeSocialStream" product="default" msgid="3086557552204114849">"Tillader, at appen kan vise sociale opdateringer fra dine venner. Vær forsigtig, når du deler oplysninger – med denne tilladelse kan appen producere meddelelser, der kan synes at komme fra en ven. Bemærk! Denne tilladelse håndhæves muligvis ikke på alle sociale netværk."</string>
     <string name="permlab_readCalendar" msgid="5972727560257612398">"læse kalenderbegivenheder og fortrolige oplysninger"</string>
     <string name="permdesc_readCalendar" product="tablet" msgid="4216462049057658723">"Tillader, at appen kan læse alle de kalenderbegivenheder, der er gemt på din tablet, f.eks. venners eller kollegers. Med denne tilladelse kan appen dele eller gemme dine kalenderdata, uanset fortrolighed eller følsomhed."</string>
@@ -526,7 +526,7 @@
     <string name="permdesc_captureSecureVideoOutput" msgid="2779793064709350289">"Tillader, at appen opfanger og omdirigerer et sikkert videooutput."</string>
     <string name="permlab_mediaContentControl" msgid="8749790560720562511">"kontrollér medieafspilning og metadataadgang"</string>
     <string name="permdesc_mediaContentControl" msgid="1637478200272062">"Tillader, at appen styrer medieafspilning og får adgang til medieoplysninger (titel, forfatter...)."</string>
-    <string name="permlab_modifyAudioSettings" msgid="6095859937069146086">"skift dine lydindstillinger"</string>
+    <string name="permlab_modifyAudioSettings" msgid="6095859937069146086">"skifte dine lydindstillinger"</string>
     <string name="permdesc_modifyAudioSettings" msgid="3522565366806248517">"Tillader, at appen kan ændre globale lydindstillinger, som f.eks. lydstyrke og hvilken højttaler der bruges til output."</string>
     <string name="permlab_recordAudio" msgid="3876049771427466323">"optage lyd"</string>
     <string name="permdesc_recordAudio" msgid="4906839301087980680">"Tillader, at appen kan optage lyd med mikrofonen. Med denne tilladelse kan appen til enhver tid optage lyd uden din bekræftelse."</string>
@@ -632,7 +632,7 @@
     <string name="permdesc_createNetworkSockets" msgid="3403062187779724185">"Tillader, at appen kan oprette netværkssockets og bruge tilpassede netværksprotokoller. Browseren og andre applikationer indeholder midler til at sende data til internettet, så med denne tilladelse er der ingen forpligtelse til at sende data til internettet."</string>
     <string name="permlab_writeApnSettings" msgid="505660159675751896">"ændre/opfange netværksindstillinger og trafik"</string>
     <string name="permdesc_writeApnSettings" msgid="5333798886412714193">"Tillader, at appen kan ændre netværksindstillinger og opsnappe og inspicere al netværkstrafik, f.eks. for at ændre proxy og port for et adgangspunkt. Ondsindede apps kan overvåge, omdirigere eller ændre netværkspakker uden din viden."</string>
-    <string name="permlab_changeNetworkState" msgid="958884291454327309">"skift netværksforbindelse"</string>
+    <string name="permlab_changeNetworkState" msgid="958884291454327309">"skifte netværksforbindelse"</string>
     <string name="permdesc_changeNetworkState" msgid="6789123912476416214">"Tillader, at appen kan ændre netværksforbindelsens tilstand."</string>
     <string name="permlab_changeTetherState" msgid="5952584964373017960">"skifte forbindelse til netdeling"</string>
     <string name="permdesc_changeTetherState" msgid="1524441344412319780">"Tillader, at appen kan ændre tilstand for en netværksforbindelse via netdeling."</string>
@@ -672,9 +672,9 @@
     <string name="permdesc_writeSyncSettings" msgid="8956262591306369868">"Tillader, at en app kan ændre synkroniseringsindstillingerne for en konto. Denne tilladelse kan f.eks. anvendes til at aktivere synkronisering af appen Personer med en konto."</string>
     <string name="permlab_readSyncStats" msgid="7396577451360202448">"læse synkroniseringsstatistikker"</string>
     <string name="permdesc_readSyncStats" msgid="1510143761757606156">"Tillader, at en app kan læse synkroniseringsstatistikkerne for en konto, f.eks. historikken for synkroniserede begivenheder og hvor meget data der synkroniseres."</string>
-    <string name="permlab_subscribedFeedsRead" msgid="4756609637053353318">"læs abonnerede feeds"</string>
+    <string name="permlab_subscribedFeedsRead" msgid="4756609637053353318">"læse feeds, jeg abonnerer på"</string>
     <string name="permdesc_subscribedFeedsRead" msgid="5557058907906144505">"Tillader, at appen kan hente oplysninger om de feeds, der synkroniseres."</string>
-    <string name="permlab_subscribedFeedsWrite" msgid="9015246325408209296">"skriv abonnerede feeds"</string>
+    <string name="permlab_subscribedFeedsWrite" msgid="9015246325408209296">"skrive feeds, som jeg abonnerer på"</string>
     <string name="permdesc_subscribedFeedsWrite" msgid="6928930188826089413">"Tillader, at appen kan ændre dine synkroniserede feeds. Ondsindede apps kan ændre dine synkroniserede feeds."</string>
     <string name="permlab_readDictionary" msgid="4107101525746035718">"læse termer, som du har føjet til ordbogen"</string>
     <string name="permdesc_readDictionary" msgid="659614600338904243">"Tillader, at appen kan læse alle ord, navne og sætninger, som brugeren har gemt i brugerordbogen."</string>
@@ -995,7 +995,7 @@
     <string name="permdesc_writeHistoryBookmarks" product="default" msgid="8497389531014185509">"Tillader, at appen kan ændre browserens historik eller de bogmærker, der er gemt på din telefon. Dette kan give appen tilladelse til at slette eller ændre browserdata. Bemærk! Denne tilladelse håndhæves muligvis ikke af tredjepartsbrowsere eller andre applikationer med websøgningsfunktioner."</string>
     <string name="permlab_setAlarm" msgid="1379294556362091814">"indstille en alarm"</string>
     <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øj telefonsvarer"</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_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>
@@ -1275,7 +1275,7 @@
     <string name="date_picker_dialog_title" msgid="5879450659453782278">"Angiv dato"</string>
     <string name="date_time_set" msgid="5777075614321087758">"Angiv"</string>
     <string name="date_time_done" msgid="2507683751759308828">"Udført"</string>
-    <string name="perms_new_perm_prefix" msgid="8257740710754301407"><font size="12" fgcolor="#ff33b5e5">"NYHED! "</font></string>
+    <string name="perms_new_perm_prefix" msgid="8257740710754301407"><font size="12" fgcolor="#ff33b5e5">"NY: "</font></string>
     <string name="perms_description_app" msgid="5139836143293299417">"Leveret af <xliff:g id="APP_NAME">%1$s</xliff:g>."</string>
     <string name="no_permissions" msgid="7283357728219338112">"Der kræves ingen tilladelser"</string>
     <string name="perm_costs_money" msgid="4902470324142151116">"dette kan koste dig penge"</string>
diff --git a/core/res/res/values-el/strings.xml b/core/res/res/values-el/strings.xml
index 989eec6..d0561b9 100644
--- a/core/res/res/values-el/strings.xml
+++ b/core/res/res/values-el/strings.xml
@@ -1471,8 +1471,8 @@
     <string name="keyboardview_keycode_enter" msgid="2985864015076059467">"Enter"</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_application" msgid="5627411384638389738">"Κοινοποίηση με <xliff:g id="APPLICATION_NAME">%s</xliff:g>"</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">"Στοιχείο χειρισμού με δυνατότητα ολίσθησης. Αγγίξτε και πατήστε παρατεταμένα."</string>
     <string name="description_target_unlock_tablet" msgid="3833195335629795055">"Σύρετε για ξεκλείδωμα."</string>
     <string name="keyboard_headset_required_to_hear_password" msgid="7011927352267668657">"Συνδέστε ακουστικά για να ακούσετε τα πλήκτρα του κωδικού πρόσβασης να εκφωνούνται."</string>
@@ -1516,7 +1516,7 @@
     <string name="sha1_fingerprint" msgid="7930330235269404581">"Αποτύπωμα SHA-1"</string>
     <string name="activity_chooser_view_see_all" msgid="4292569383976636200">"Εμφάνιση όλων"</string>
     <string name="activity_chooser_view_dialog_title_default" msgid="4710013864974040615">"Επιλογή δραστηριότητας"</string>
-    <string name="share_action_provider_share_with" msgid="5247684435979149216">"Κοινοποίηση με"</string>
+    <string name="share_action_provider_share_with" msgid="5247684435979149216">"Κοινή χρήση με"</string>
     <string name="list_delimeter" msgid="3975117572185494152">", "</string>
     <string name="sending" msgid="3245653681008218030">"Γίνεται αποστολή…"</string>
     <string name="launchBrowserDefault" msgid="2057951947297614725">"Εκκίνηση προγράμματος περιήγησης;"</string>
diff --git a/core/res/res/values-fr/strings.xml b/core/res/res/values-fr/strings.xml
index c8eca25..3d043c4 100644
--- a/core/res/res/values-fr/strings.xml
+++ b/core/res/res/values-fr/strings.xml
@@ -294,7 +294,7 @@
     <string name="permlab_receiveWapPush" msgid="5991398711936590410">"recevoir des messages texte (WAP)"</string>
     <string name="permdesc_receiveWapPush" msgid="748232190220583385">"Permet à l\'application de recevoir et de traiter les messages WAP. Cette autorisation lui donne la possibilité de surveiller ou supprimer les messages envoyés à votre appareil sans vous les montrer."</string>
     <string name="permlab_getTasks" msgid="6466095396623933906">"récupérer les applications en cours d\'exécution"</string>
-    <string name="permdesc_getTasks" msgid="7454215995847658102">"Permet à l\'application de récupérer des informations sur des tâches en cours d\'exécution et récemment exécutées. L\'application est ainsi susceptible de d\'obtenir des informations sur les applications utilisées sur l\'appareil."</string>
+    <string name="permdesc_getTasks" msgid="7454215995847658102">"Permet à l\'application de récupérer des informations sur des tâches en cours d\'exécution et récemment exécutées. L\'application est ainsi susceptible d\'obtenir des informations sur les applications utilisées sur l\'appareil."</string>
     <string name="permlab_interactAcrossUsers" msgid="7114255281944211682">"interagir entre les utilisateurs"</string>
     <string name="permdesc_interactAcrossUsers" msgid="364670963623385786">"Permet à l\'application d\'effectuer des actions entre les différents utilisateurs de l\'appareil. Les applications malveillantes peuvent utiliser cette autorisation pour passer outre la protection entre les utilisateurs."</string>
     <string name="permlab_interactAcrossUsersFull" msgid="2567734285545074105">"autorisation totale d\'interagir entre les utilisateurs"</string>
@@ -503,7 +503,7 @@
     <string name="permlab_installLocationProvider" msgid="6578101199825193873">"autoriser l\'installation d\'un fournisseur de services de localisation"</string>
     <string name="permdesc_installLocationProvider" msgid="9066146120470591509">"Permet de créer des sources de localisation fictives à des fins de tests ou pour installer un nouveau fournisseur de position. L\'application peut ainsi modifier la position et/ou l\'état renvoyé par d\'autres sources de localisation telles que le GPS ou les fournisseurs de position."</string>
     <string name="permlab_accessFineLocation" msgid="1191898061965273372">"position précise (GPS et réseau)"</string>
-    <string name="permdesc_accessFineLocation" msgid="5295047563564981250">"Permet à l\'application d\'obtenir votre position exacte à l\'aide du récepteur satellite GPS ou des sources de localisation de réseau tels que les points d\'accès Wi-Fi et les antennes-relais. Ces services de localisation doivent être activés et disponibles sur votre appareil pour que l\'application puissent déterminer où vous vous trouvez, le cas échéant. Cette autorisation peut entraîner une utilisation accrue de la batterie."</string>
+    <string name="permdesc_accessFineLocation" msgid="5295047563564981250">"Permet à l\'application d\'obtenir votre position exacte à l\'aide du récepteur satellite GPS ou des sources de localisation de réseau tels que les points d\'accès Wi-Fi et les antennes-relais. Ces services de localisation doivent être activés et disponibles sur votre appareil pour que l\'application puisse déterminer où vous vous trouvez, le cas échéant. Cette autorisation peut entraîner une utilisation accrue de la batterie."</string>
     <string name="permlab_accessCoarseLocation" msgid="4887895362354239628">"position approximative (réseau)"</string>
     <string name="permdesc_accessCoarseLocation" msgid="2538200184373302295">"Permet à l\'application d\'obtenir votre position approximative. Celle-ci est fournie par des services de localisation sur la base des sources de localisation de réseau tels que les points d\'accès Wi-Fi et les antennes-relais. Ces services de localisation doivent être activés et disponibles sur votre appareil pour que l\'application puisse déterminer où vous vous trouvez de façon approximative, le cas échéant."</string>
     <string name="permlab_accessSurfaceFlinger" msgid="2363969641792388947">"Accès à SurfaceFlinger"</string>
diff --git a/core/res/res/values-hr/strings.xml b/core/res/res/values-hr/strings.xml
index 000fb3a..2adaaf1 100644
--- a/core/res/res/values-hr/strings.xml
+++ b/core/res/res/values-hr/strings.xml
@@ -496,7 +496,7 @@
     <string name="permlab_writeCalendar" msgid="8438874755193825647">"dodajte ili izmijenite kalendarske događaje i pošaljite e-poštu gostima bez znanja vlasnika"</string>
     <string name="permdesc_writeCalendar" product="tablet" msgid="6679035520113668528">"Aplikaciji omogućuje dodavanje, uklanjanje i promjenu događaja koje možete izmijeniti na tabletnom računalu, uključujući one od vaših prijatelja ili suradnika. To aplikaciji može omogućiti slanje poruka koje izgledaju kao da dolaze od vlasnika kalendara ili izmjenu događaja bez znanja vlasnika."</string>
     <string name="permdesc_writeCalendar" product="default" msgid="2324469496327249376">"Aplikaciji omogućuje dodavanje, uklanjanje i promjenu događaja koje možete izmijeniti na telefonu, uključujući one od vaših prijatelja ili suradnika. To aplikaciji može omogućiti slanje poruka koje izgledaju kao da dolaze od vlasnika kalendara ili izmjenu događaja bez znanja vlasnika."</string>
-    <string name="permlab_accessMockLocation" msgid="8688334974036823330">"omogući testiranje izvora lokacije"</string>
+    <string name="permlab_accessMockLocation" msgid="8688334974036823330">"omogućeno testiranje izvora lokacije"</string>
     <string name="permdesc_accessMockLocation" msgid="5808711039482051824">"Stvaranje lažnih izvora lokacije radi testiranja ili za instaliranje novog pružatelja usluga lokacije. To aplikaciji omogućuje zaobilaženje lokacije i/ili statusa koji vraćaju drugi izvori lokacije, primjerice GPS ili pružatelji usluga lokacije."</string>
     <string name="permlab_accessLocationExtraCommands" msgid="2836308076720553837">"pristup dodatnim naredbama davatelja lokacije"</string>
     <string name="permdesc_accessLocationExtraCommands" msgid="5945166642335800763">"Aplikaciji omogućuje pristup dodatnim naredbama za pružatelja usluga lokacije. To aplikaciji može omogućiti ometanje rada GPS-a ili drugih izvora lokacije."</string>
diff --git a/core/res/res/values-th/strings.xml b/core/res/res/values-th/strings.xml
index 4640f2a..d235eaa 100644
--- a/core/res/res/values-th/strings.xml
+++ b/core/res/res/values-th/strings.xml
@@ -187,7 +187,7 @@
     <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>
-    <string name="android_system_label" msgid="6577375335728551336">"ระบบ Android"</string>
+    <string name="android_system_label" msgid="6577375335728551336">"ระบบแอนดรอยด์"</string>
     <string name="user_owner_label" msgid="2804351898001038951">"ส่วนตัว"</string>
     <string name="managed_profile_label" msgid="6260850669674791528">"ที่ทำงาน"</string>
     <string name="permgrouplab_costMoney" msgid="5429808217861460401">"บริการที่ต้องเสียค่าใช้จ่าย"</string>
@@ -424,7 +424,7 @@
     <string name="permdesc_persistentActivity" product="tablet" msgid="8525189272329086137">"อนุญาตให้แอปพลิเคชันทำให้ส่วนหนึ่งของตัวเองคงอยู่ถาวรในหน่วยความจำ ซึ่งจะจำกัดพื้นที่หน่วยความจำที่ใช้งานได้ของแอปพลิเคชันอื่นๆ และทำให้แท็บเล็ตทำงานช้าลง"</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">"อนุญาตให้แอปพลิเคชันลบแพ็กเกจแอนดรอยด์แอปพลิเคชันที่เป็นอันตรายอาจใช้การอนุญาตนี้ลบแอปพลิเคชันที่สำคัญ"</string>
     <string name="permlab_clearAppUserData" msgid="274109191845842756">"ลบข้อมูลของแอปพลิเคชันอื่น"</string>
     <string name="permdesc_clearAppUserData" msgid="4625323684125459488">"อนุญาตให้แอปพลิเคชันล้างข้อมูลผู้ใช้"</string>
     <string name="permlab_deleteCacheFiles" msgid="3128665571837408675">"ลบแคชของแอปพลิเคชันอื่น"</string>
@@ -432,7 +432,7 @@
     <string name="permlab_getPackageSize" msgid="7472921768357981986">"วัดพื้นที่เก็บข้อมูลของแอปพลิเคชัน"</string>
     <string name="permdesc_getPackageSize" msgid="3921068154420738296">"อนุญาตให้แอปพลิเคชันเรียกดูรหัส ข้อมูล และขนาดแคชของตน"</string>
     <string name="permlab_installPackages" msgid="2199128482820306924">"ติดตั้งแอปพลิเคชันโดยตรง"</string>
-    <string name="permdesc_installPackages" msgid="5628530972548071284">"อนุญาตให้แอปพลิเคชันติดตั้งแพ็กเกจ Android ใหม่หรือที่อัปเดต แอปพลิเคชันที่เป็นอันตรายอาจใช้การอนุญาตนี้ในการเพิ่มแอปพลิเคชันใหม่ๆ ด้วยสิทธิ์ที่สูงนี้ได้ตามต้องการ"</string>
+    <string name="permdesc_installPackages" msgid="5628530972548071284">"อนุญาตให้แอปพลิเคชันติดตั้งแพ็กเกจแอนดรอยด์ใหม่หรือที่อัปเดต แอปพลิเคชันที่เป็นอันตรายอาจใช้การอนุญาตนี้ในการเพิ่มแอปพลิเคชันใหม่ๆ ด้วยสิทธิ์ที่สูงนี้ได้ตามต้องการ"</string>
     <string name="permlab_clearAppCache" msgid="7487279391723526815">"ลบข้อมูลแคชของแอปพลิเคชันทั้งหมด"</string>
     <string name="permdesc_clearAppCache" product="tablet" msgid="8974640871945434565">"อนุญาตให้แอปพลิเคชันสร้างพื้นที่ว่างในที่จัดเก็บข้อมูลของแท็บเล็ต โดยลบไฟล์ในไดเรกทอรีแคชของแอปพลิเคชันอื่นๆ ซึ่งอาจทำให้แอปพลิเคชันอื่นเริ่มทำงานช้ากว่าเดิมเนื่องจากต้องดึงข้อมูลของตนซ้ำ"</string>
     <string name="permdesc_clearAppCache" product="default" msgid="2459441021956436779">"อนุญาตให้แอปพลิเคชันสร้างพื้นที่ว่างในที่จัดเก็บข้อมูลของโทรศัพท์ โดยลบไฟล์ในไดเรกทอรีแคชของแอปพลิเคชันอื่นๆ ซึ่งอาจทำให้แอปพลิเคชันอื่นเริ่มทำงานช้ากว่าเดิมเนื่องจากต้องดึงข้อมูลของตนซ้ำ"</string>
@@ -1190,7 +1190,7 @@
     <string name="screen_compat_mode_hint" msgid="1064524084543304459">"เปิดใช้งานอีกครั้งในการตั้งค่าระบบ &gt; แอปพลิเคชัน &gt; ดาวน์โหลด"</string>
     <string name="smv_application" msgid="3307209192155442829">"แอปพลิเคชัน <xliff:g id="APPLICATION">%1$s</xliff:g> (กระบวนการ <xliff:g id="PROCESS">%2$s</xliff:g>) ละเมิดนโยบาย StrictMode ที่บังคับใช้ด้วยตัวเอง"</string>
     <string name="smv_process" msgid="5120397012047462446">"กระบวนการ <xliff:g id="PROCESS">%1$s</xliff:g> ละเมิดนโยบาย StrictMode ที่บังคับใช้ด้วยตัวเอง"</string>
-    <string name="android_upgrading_title" msgid="1584192285441405746">"กำลังอัปเกรด Android..."</string>
+    <string name="android_upgrading_title" msgid="1584192285441405746">"กำลังอัปเกรดแอนดรอยด์..."</string>
     <string name="android_upgrading_apk" msgid="7904042682111526169">"กำลังเพิ่มประสิทธิภาพแอปพลิเคชัน <xliff:g id="NUMBER_0">%1$d</xliff:g> จาก <xliff:g id="NUMBER_1">%2$d</xliff:g> รายการ"</string>
     <string name="android_upgrading_starting_apps" msgid="451464516346926713">"กำลังเริ่มต้นแอปพลิเคชัน"</string>
     <string name="android_upgrading_complete" msgid="1405954754112999229">"เสร็จสิ้นการบูต"</string>
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index 9eb6f74..b8086be 100644
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -493,6 +493,9 @@
              {@link android.view.Window#setAllowExitTransitionOverlap(boolean)}. -->
         <attr name="windowAllowExitTransitionOverlap" format="boolean"/>
 
+        <!-- Internal layout used internally for window decor -->
+        <attr name="windowActionBarFullscreenDecorLayout" format="reference" />
+
         <!-- ============ -->
         <!-- Alert Dialog styles -->
         <!-- ============ -->
@@ -685,6 +688,7 @@
         <!-- Default ActivityChooserView style. -->
         <attr name="activityChooserViewStyle" format="reference" />
 
+        <!-- Default Toolbar style. -->
         <attr name="toolbarStyle" format="reference" />
 
         <!-- Fast scroller styles -->
@@ -886,9 +890,12 @@
              with the appearance of a singel button broken into segments. -->
         <attr name="segmentedButtonStyle" format="reference" />
 
-        <!-- Background drawable for standalone items that need focus/pressed states. -->
+        <!-- Background drawable for bordered standalone items that need focus/pressed states. -->
         <attr name="selectableItemBackground" format="reference" />
 
+        <!-- Background drawable for borderless standalone items that need focus/pressed states. -->
+        <attr name="selectableItemBackgroundBorderless" format="reference" />
+
         <!-- Style for buttons without an explicit border, often used in groups. -->
         <attr name="borderlessButtonStyle" format="reference" />
 
@@ -1713,6 +1720,7 @@
         <attr name="windowSwipeToDismiss" />
         <attr name="windowContentTransitions" />
         <attr name="windowContentTransitionManager" />
+        <attr name="windowActionBarFullscreenDecorLayout" />
 
         <!-- The minimum width the window is allowed to be, along the major
              axis of the screen.  That is, when in landscape.  Can be either
@@ -4659,11 +4667,11 @@
 
     <!-- Drawable used to show animated touch feedback. -->
     <declare-styleable name="RippleDrawable">
-        <!-- The tint to use for feedback ripples. This attribute is required. -->
+        <!-- The tint to use for ripple effects. This attribute is required. -->
         <attr name="tint" />
-        <!-- Specifies the Porter-Duff blending mode used to apply the tint. The default vlaue is src_atop, which draws over the opaque parts of the drawable. -->
+        <!-- Specifies the Porter-Duff blending mode used to apply the tint. The default value is src_atop, which draws over the opaque parts of the drawable. -->
         <attr name="tintMode" />
-        <!-- Whether to pin feedback ripples to the center of the drawable. Default value is false. -->
+        <!-- Whether to pin ripple effects to the center of the drawable. Default value is false. -->
         <attr name="pinned" format="boolean" />
     </declare-styleable>
 
@@ -4780,6 +4788,28 @@
         <attr name="height" />
     </declare-styleable>
 
+    <!-- Defines the group used in Vector Drawables. -->
+    <declare-styleable name="VectorDrawableGroup">
+        <!-- The Name of this group -->
+        <attr name="name" />
+        <!-- The amount to rotate the group -->
+        <attr name="rotation" />
+        <!-- The X coordinate of the center of rotation of a group -->
+        <attr name="pivotX" />
+        <!-- The Y coordinate of the center of rotation of a group -->
+        <attr name="pivotY" />
+        <!-- The amount to translate the group on X coordinate -->
+        <attr name="translateX" format="float"/>
+        <!-- The amount to translate the group on Y coordinate -->
+        <attr name="translateY" format="float"/>
+        <!-- The amount to scale the group on X coordinate -->
+        <attr name="scaleX" />
+        <!-- The amount to scale the group on X coordinate -->
+        <attr name="scaleY" />
+        <!-- The alpha of the group (0 is transparent and 1 is opaque) -->
+        <attr name="alpha" />
+    </declare-styleable>
+
     <!-- Defines the path used in Vector Drawables. -->
     <declare-styleable name="VectorDrawablePath">
         <!-- The Name of this path -->
@@ -4788,12 +4818,6 @@
         <attr name="strokeWidth" format="float" />
         <!-- The opacity of a path stroke -->
         <attr name="strokeOpacity" format="float" />
-        <!-- The amount to rotate the path stroke -->
-        <attr name="rotation" />
-        <!-- The X coordinate of the center of rotation of a path -->
-        <attr name="pivotX" />
-        <!-- The Y coordinate of the center of rotation of a path -->
-        <attr name="pivotY" />
         <!-- The color to stroke the path if not defined implies no stroke-->
         <attr name="stroke" format="color" />
         <!-- The color to fill the path if not defined implies no fill-->
@@ -6125,6 +6149,11 @@
         <!-- Component name of an activity that allows the user to modify
              the settings for this trust agent. -->
         <attr name="settingsActivity" />
+        <!-- Title for a preference that allows that user to launch the
+             activity to modify trust agent settings. -->
+        <attr name="title" />
+        <!-- Summary for the same preference as the title. -->
+        <attr name="summary" />
     </declare-styleable>
 
     <!-- =============================== -->
@@ -6404,11 +6433,17 @@
         <attr name="indeterminateProgressStyle" format="reference" />
         <!-- Specifies the horizontal padding on either end for an embedded progress bar. -->
         <attr name="progressBarPadding" format="dimension" />
+        <!-- Up navigation glyph -->
+        <attr name="homeAsUpIndicator" />
         <!-- Specifies padding that should be applied to the left and right sides of
              system-provided items in the bar. -->
         <attr name="itemPadding" format="dimension" />
         <!-- Set true to hide the action bar on a vertical nested scroll of content. -->
         <attr name="hideOnContentScroll" format="boolean" />
+        <attr name="contentInsetStart" format="dimension" />
+        <attr name="contentInsetEnd" format="dimension" />
+        <attr name="contentInsetLeft" format="dimension" />
+        <attr name="contentInsetRight" format="dimension" />
     </declare-styleable>
 
     <declare-styleable name="ActionMode">
@@ -6659,10 +6694,19 @@
         <attr name="titleMarginEnd" format="dimension" />
         <attr name="titleMarginTop" format="dimension" />
         <attr name="titleMarginBottom" format="dimension" />
-        <attr name="contentInsetStart" format="dimension" />
-        <attr name="contentInsetEnd" format="dimension" />
-        <attr name="contentInsetLeft" format="dimension" />
-        <attr name="contentInsetRight" format="dimension" />
+        <attr name="contentInsetStart" />
+        <attr name="contentInsetEnd" />
+        <attr name="contentInsetLeft" />
+        <attr name="contentInsetRight" />
+        <attr name="maxButtonHeight" format="dimension" />
+        <attr name="navigationButtonStyle" format="reference" />
+        <attr name="buttonGravity">
+            <!-- Push object to the top of its container, not changing its size. -->
+            <flag name="top" value="0x30" />
+            <!-- Push object to the bottom of its container, not changing its size. -->
+            <flag name="bottom" value="0x50" />
+        </attr>
+        <attr name="collapseIcon" format="reference" />
     </declare-styleable>
 
     <declare-styleable name="Toolbar_LayoutParams">
@@ -6677,4 +6721,17 @@
     <declare-styleable name="EdgeEffect">
         <attr name="colorPrimaryLight" />
     </declare-styleable>
+
+    <!-- Use <code>tv-input</code> as the root tag of the XML resource that describes an
+         {@link android.media.tv.TvInputService}, which is referenced from its
+         {@link android.media.tv.TvInputService#SERVICE_META_DATA} meta-data entry.
+         Described here are the attributes that can be included in that tag. -->
+    <declare-styleable name="TvInputService">
+        <!-- Component name of an activity for setup of this service.
+             The setup includes scanning channels and registering EPG data. -->
+        <attr name="setupActivity" format="string" />
+        <!-- Component name of an activity that allows the user to modify
+             the settings for this service. -->
+        <attr name="settingsActivity" />
+    </declare-styleable>
 </resources>
diff --git a/core/res/res/values/colors_quantum.xml b/core/res/res/values/colors_quantum.xml
index 556463e..976930c 100644
--- a/core/res/res/values/colors_quantum.xml
+++ b/core/res/res/values/colors_quantum.xml
@@ -16,8 +16,14 @@
 
 <!-- Colors specific to Quantum themes. -->
 <resources>
-    <color name="background_quantum_dark">#ff303030</color>
-    <color name="background_quantum_light">@color/white</color>
+    <color name="background_quantum_dark">#ff414042</color>
+    <color name="background_quantum_light">#fff1f2f2</color>
+
+    <color name="ripple_quantum_dark">#30ffffff</color>
+    <color name="ripple_quantum_light">#30000000</color>
+
+    <color name="button_quantum_dark">#ff5a595b</color>
+    <color name="button_quantum_light">#ffd6d7d7</color>
 
     <color name="bright_foreground_quantum_dark">@color/white</color>
     <color name="bright_foreground_quantum_light">@color/black</color>
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 5375c14..f6732d3 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -1538,9 +1538,7 @@
          -->
     <string-array translatable="false" name="config_globalActionsList">
         <item>power</item>
-        <item>airplane</item>
         <item>bugreport</item>
-        <item>silent</item>
         <item>users</item>
     </string-array>
 
diff --git a/core/res/res/values/dimens.xml b/core/res/res/values/dimens.xml
index 38f00fe..657f614 100644
--- a/core/res/res/values/dimens.xml
+++ b/core/res/res/values/dimens.xml
@@ -202,9 +202,6 @@
     <!-- Default width for a textview error popup -->
     <dimen name="textview_error_popup_default_width">240dip</dimen>
 
-    <!-- Volume panel y offset -->
-    <dimen name="volume_panel_top">16dp</dimen>
-
     <!-- Default padding to apply to AppWidgetHostViews containing widgets targeting API level 14 and up. -->
     <dimen name="default_app_widget_padding_left">8dp</dimen>
     <dimen name="default_app_widget_padding_top">8dp</dimen>
diff --git a/core/res/res/values/dimens_quantum.xml b/core/res/res/values/dimens_quantum.xml
index 2defee2..b5ba1ca 100644
--- a/core/res/res/values/dimens_quantum.xml
+++ b/core/res/res/values/dimens_quantum.xml
@@ -52,7 +52,10 @@
     <dimen name="text_size_small_quantum">14sp</dimen>
 
     <dimen name="floating_window_z">16dp</dimen>
-    <dimen name="floating_window_margin">32dp</dimen>
+    <dimen name="floating_window_margin_left">16dp</dimen>
+    <dimen name="floating_window_margin_top">8dp</dimen>
+    <dimen name="floating_window_margin_right">16dp</dimen>
+    <dimen name="floating_window_margin_bottom">32dp</dimen>
 
     <!-- the amount of elevation for pressed button state-->
     <dimen name="button_pressed_z">2dp</dimen>
diff --git a/core/res/res/values/donottranslate_quantum.xml b/core/res/res/values/donottranslate_quantum.xml
index 83cc4e5..e53c40e 100644
--- a/core/res/res/values/donottranslate_quantum.xml
+++ b/core/res/res/values/donottranslate_quantum.xml
@@ -27,6 +27,6 @@
     <string name="font_family_body_1_quantum">sans-serif</string>
     <string name="font_family_caption_quantum">sans-serif</string>
     <string name="font_family_menu_quantum">sans-serif-medium</string>
-    <string name="font_family_button_quantum">sans-serif</string>
+    <string name="font_family_button_quantum">sans-serif-medium</string>
 
 </resources>
diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml
index 42ed318..f6ffd15 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -2096,6 +2096,7 @@
   <public type="attr" name="windowSwipeToDismiss" id="0x10103f3" />
   <public type="attr" name="isGame" id="0x10103f4" />
   <public type="attr" name="allowEmbedded" id="0x10103f5" />
+  <public type="attr" name="setupActivity" id="0x10103f6"/>
 
 <!-- ===============================================================
      Resources added in version 21 of the platform
@@ -2180,6 +2181,9 @@
   <public type="attr" name="paddingMode" />
   <public type="attr" name="layout_rowWeight" />
   <public type="attr" name="layout_columnWeight" />
+  <public type="attr" name="translateX" />
+  <public type="attr" name="translateY" />
+  <public type="attr" name="selectableItemBackgroundBorderless" />
 
   <public-padding type="dimen" name="l_resource_pad" end="0x01050010" />
 
@@ -2254,6 +2258,7 @@
   <public type="style" name="Theme.Quantum.NoActionBar.Overscan" />
   <public type="style" name="Theme.Quantum.NoActionBar.TranslucentDecor" />
   <public type="style" name="Theme.Quantum.Panel" />
+  <public type="style" name="Theme.Quantum.Voice" />
   <public type="style" name="Theme.Quantum.Wallpaper" />
   <public type="style" name="Theme.Quantum.Wallpaper.NoTitleBar" />
 
@@ -2270,6 +2275,7 @@
   <public type="style" name="Theme.Quantum.Light.NoActionBar.Overscan" />
   <public type="style" name="Theme.Quantum.Light.NoActionBar.TranslucentDecor" />
   <public type="style" name="Theme.Quantum.Light.Panel" />
+  <public type="style" name="Theme.Quantum.Light.Voice" />
 
   <public type="style" name="ThemeOverlay" />
   <public type="style" name="ThemeOverlay.Quantum" />
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index 8286ef9..9ff67b4 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -419,6 +419,8 @@
         current device state, to send as an e-mail message.  It will take a little
         time from starting the bug report until it is ready to be sent; please be
         patient.</string>
+    <!-- Format for build summary info [CHAR LIMIT=NONE] -->
+    <string name="bugreport_status" translatable="false">%s (%s)</string>
 
     <!-- label for item that enables silent mode in phone options dialog -->
     <string name="global_action_toggle_silent_mode">Silent mode</string>
@@ -455,10 +457,10 @@
     <string name="android_system_label">Android System</string>
 
     <!-- Label for the user owner in the intent forwarding app. -->
-    <string name="user_owner_label">Personal</string>
+    <string name="user_owner_label">Personal apps</string>
 
     <!-- Label for a corporate profile in the intent forwarding app. -->
-    <string name="managed_profile_label">Work</string>
+    <string name="managed_profile_label">Android for Work</string>
 
     <!-- Title of a category of application permissions, listed so the user can choose whether they want to allow the application to do this. -->
     <string name="permgrouplab_costMoney">Services that cost you money</string>
diff --git a/core/res/res/values/styles.xml b/core/res/res/values/styles.xml
index 1f4c88d..59bd667 100644
--- a/core/res/res/values/styles.xml
+++ b/core/res/res/values/styles.xml
@@ -169,10 +169,16 @@
         <item name="windowExitAnimation">@anim/input_method_exit</item>
     </style>
 
+    <!-- Window animations that are applied to voice activity windows. -->
+    <style name="Animation.VoiceActivity">
+        <item name="windowEnterAnimation">@anim/voice_activity_open_enter</item>
+        <item name="windowExitAnimation">@anim/voice_activity_close_exit</item>
+    </style>
+
     <!-- Window animations that are applied to voice interaction overlay windows. -->
     <style name="Animation.VoiceInteractionSession">
-        <item name="windowEnterAnimation">@anim/input_method_enter</item>
-        <item name="windowExitAnimation">@anim/input_method_exit</item>
+        <item name="windowEnterAnimation">@anim/voice_layer_enter</item>
+        <item name="windowExitAnimation">@anim/voice_layer_exit</item>
     </style>
 
     <!-- Special optional fancy IM animations. @hide -->
@@ -1189,6 +1195,16 @@
         <item name="android:subtitleTextAppearance">@android:style/TextAppearance.Widget.Toolbar.Subtitle</item>
         <item name="android:minHeight">?android:attr/actionBarSize</item>
         <item name="android:titleMargins">4dp</item>
+        <item name="android:maxButtonHeight">56dp</item>
+        <item name="android:buttonGravity">top</item>
+        <item name="android:navigationButtonStyle">@android:style/Widget.Toolbar.Button.Navigation</item>
+        <item name="android:collapseIcon">?android:attr/homeAsUpIndicator</item>
+    </style>
+
+    <style name="Widget.Toolbar.Button.Navigation" parent="@android:style/Widget">
+        <item name="android:background">?android:attr/selectableItemBackground</item>
+        <item name="android:minWidth">56dp</item>
+        <item name="android:scaleType">center</item>
     </style>
 
     <style name="TextAppearance.Widget.ActionBar.Title"
@@ -2386,7 +2402,6 @@
         <item name="android:background">@android:drawable/ab_transparent_light_holo</item>
         <item name="android:backgroundStacked">@android:drawable/ab_stacked_transparent_light_holo</item>
         <item name="android:backgroundSplit">@android:drawable/ab_bottom_transparent_light_holo</item>
-        <item name="android:homeAsUpIndicator">@android:drawable/ic_ab_back_holo_light</item>
         <item name="android:progressBarStyle">@android:style/Widget.Holo.Light.ProgressBar.Horizontal</item>
         <item name="android:indeterminateProgressStyle">@android:style/Widget.Holo.Light.ProgressBar</item>
     </style>
diff --git a/core/res/res/values/styles_quantum.xml b/core/res/res/values/styles_quantum.xml
index 2e7a5b1..108334f 100644
--- a/core/res/res/values/styles_quantum.xml
+++ b/core/res/res/values/styles_quantum.xml
@@ -404,8 +404,10 @@
         <item name="textAppearance">?attr/textAppearanceButton</item>
         <item name="textColor">?attr/textColorPrimary</item>
         <item name="minHeight">48dip</item>
-        <item name="minWidth">96dip</item>
-        <item name="stateListAnimator">@anim/button_state_list_anim_quantum</item>
+        <item name="minWidth">88dip</item>
+
+        <!-- TODO: Turn this back on when we support inset drawable outlines. -->
+        <!-- <item name="stateListAnimator">@anim/button_state_list_anim_quantum</item> -->
     </style>
 
     <!-- Small bordered ink button -->
@@ -434,7 +436,6 @@
         <item name="background">@drawable/btn_toggle_quantum</item>
         <item name="textOn">@string/capital_on</item>
         <item name="textOff">@string/capital_off</item>
-        <item name="textAppearance">?attr/textAppearanceSmall</item>
         <item name="minHeight">48dip</item>
     </style>
 
@@ -468,34 +469,29 @@
         <item name="paddingEnd">8dp</item>
     </style>
 
-    <style name="Widget.Quantum.CheckedTextView" parent="Widget.CheckedTextView">
-        <item name="drawablePadding">4dip</item>
-    </style>
-
+    <style name="Widget.Quantum.CheckedTextView" parent="Widget.CheckedTextView" />
     <style name="Widget.Quantum.TextSelectHandle" parent="Widget.TextSelectHandle"/>
     <style name="Widget.Quantum.TextSuggestionsPopupWindow" parent="Widget.TextSuggestionsPopupWindow"/>
     <style name="Widget.Quantum.AbsListView" parent="Widget.AbsListView"/>
 
     <style name="Widget.Quantum.AutoCompleteTextView" parent="Widget.AutoCompleteTextView">
-        <item name="dropDownSelector">@drawable/list_selector_quantum</item>
+        <item name="dropDownSelector">?attr/listChoiceBackgroundIndicator</item>
         <item name="popupBackground">@drawable/popup_background_quantum</item>
     </style>
 
     <style name="Widget.Quantum.CompoundButton" parent="Widget.CompoundButton"/>
 
     <style name="Widget.Quantum.CompoundButton.CheckBox" parent="Widget.CompoundButton.CheckBox">
-        <item name="background">?attr/selectableItemBackground</item>
-        <item name="drawablePadding">4dip</item>
+        <item name="background">?attr/selectableItemBackgroundBorderless</item>
     </style>
 
     <style name="Widget.Quantum.CompoundButton.RadioButton" parent="Widget.CompoundButton.RadioButton">
-        <item name="background">?attr/selectableItemBackground</item>
-        <item name="drawablePadding">4dip</item>
+        <item name="background">?attr/selectableItemBackgroundBorderless</item>
     </style>
 
     <style name="Widget.Quantum.CompoundButton.Star" parent="Widget.CompoundButton.Star">
         <item name="button">@drawable/btn_star_quantum</item>
-        <item name="background">?attr/selectableItemBackground</item>
+        <item name="background">?attr/selectableItemBackgroundBorderless</item>
     </style>
 
     <style name="Widget.Quantum.CompoundButton.Switch">
@@ -507,7 +503,7 @@
         <item name="textOff"></item>
         <item name="switchMinWidth">4dip</item>
         <item name="switchPadding">4dip</item>
-        <item name="background">?attr/selectableItemBackground</item>
+        <item name="background">?attr/selectableItemBackgroundBorderless</item>
     </style>
 
     <style name="Widget.Quantum.EditText" parent="Widget.EditText"/>
@@ -522,7 +518,10 @@
     <style name="Widget.Quantum.ExpandableListView.White"/>
     <style name="Widget.Quantum.Gallery" parent="Widget.Gallery"/>
     <style name="Widget.Quantum.GestureOverlayView" parent="Widget.GestureOverlayView"/>
-    <style name="Widget.Quantum.GridView" parent="Widget.GridView"/>
+
+    <style name="Widget.Quantum.GridView" parent="Widget.GridView">
+        <item name="android:listSelector">?attr/selectableItemBackground</item>
+    </style>
 
     <style name="Widget.Quantum.CalendarView" parent="Widget.CalendarView">
         <item name="selectedWeekBackgroundColor">#330099FF</item>
@@ -586,7 +585,7 @@
     <style name="Widget.Quantum.PopupWindow" parent="Widget.PopupWindow"/>
 
     <style name="Widget.Quantum.PopupWindow.ActionMode">
-        <item name="popupBackground">@color/black</item>
+        <item name="popupBackground">@drawable/popup_background_quantum</item>
         <item name="popupAnimationStyle">@style/Animation.PopupWindow.ActionMode</item>
     </style>
 
@@ -626,7 +625,7 @@
         <item name="paddingStart">16dip</item>
         <item name="paddingEnd">16dip</item>
         <item name="mirrorForRtl">true</item>
-        <item name="background">?attr/selectableItemBackground</item>
+        <item name="background">?attr/selectableItemBackgroundBorderless</item>
     </style>
 
     <style name="Widget.Quantum.RatingBar" parent="Widget.RatingBar">
@@ -653,7 +652,7 @@
 
     <style name="Widget.Quantum.Spinner" parent="Widget.Spinner.DropDown">
         <item name="background">@drawable/spinner_background_quantum</item>
-        <item name="dropDownSelector">@drawable/list_selector_quantum</item>
+        <item name="dropDownSelector">?attr/listChoiceBackgroundIndicator</item>
         <item name="popupBackground">@drawable/popup_background_quantum</item>
         <item name="dropDownVerticalOffset">0dip</item>
         <item name="dropDownHorizontalOffset">0dip</item>
@@ -712,7 +711,7 @@
     <style name="Widget.Quantum.QuickContactBadgeSmall.WindowLarge" parent="Widget.QuickContactBadgeSmall.WindowLarge"/>
 
     <style name="Widget.Quantum.ListPopupWindow" parent="Widget.ListPopupWindow">
-        <item name="dropDownSelector">@drawable/list_selector_quantum</item>
+        <item name="dropDownSelector">?attr/listChoiceBackgroundIndicator</item>
         <item name="popupBackground">@drawable/popup_background_quantum</item>
         <item name="popupAnimationStyle">@style/Animation.Quantum.Popup</item>
         <item name="dropDownVerticalOffset">0dip</item>
@@ -773,7 +772,7 @@
         <item name="background">@null</item>
         <item name="backgroundStacked">@null</item>
         <item name="backgroundSplit">@null</item>
-        <item name="displayOptions">useLogo|showHome|showTitle</item>
+        <item name="displayOptions">showTitle</item>
         <item name="divider">?attr/dividerVertical</item>
         <item name="titleTextStyle">@style/TextAppearance.Quantum.Widget.ActionBar.Title</item>
         <item name="subtitleTextStyle">@style/TextAppearance.Quantum.Widget.ActionBar.Subtitle</item>
@@ -809,7 +808,7 @@
     </style>
 
     <style name="Widget.Quantum.MediaRouteButton">
-        <item name="background">?attr/selectableItemBackground</item>
+        <item name="background">?attr/selectableItemBackgroundBorderless</item>
         <item name="externalRouteEnabledDrawable">@drawable/ic_media_route_quantum</item>
         <item name="minWidth">56dp</item>
         <item name="minHeight">48dp</item>
@@ -879,12 +878,7 @@
     <style name="Widget.Quantum.Light.ListView" parent="Widget.Quantum.ListView"/>
     <style name="Widget.Quantum.Light.ListView.White" parent="Widget.Quantum.ListView.White"/>
     <style name="Widget.Quantum.Light.PopupWindow" parent="Widget.Quantum.PopupWindow"/>
-
-    <style name="Widget.Quantum.Light.PopupWindow.ActionMode">
-        <item name="popupBackground">@color/white</item>
-        <item name="popupAnimationStyle">@style/Animation.PopupWindow.ActionMode</item>
-    </style>
-
+    <style name="Widget.Quantum.Light.PopupWindow.ActionMode" parent="Widget.Quantum.PopupWindow.ActionMode"/>
     <style name="Widget.Quantum.Light.ProgressBar" parent="Widget.Quantum.ProgressBar"/>
     <style name="Widget.Quantum.Light.ProgressBar.Horizontal" parent="Widget.Quantum.ProgressBar.Horizontal"/>
     <style name="Widget.Quantum.Light.ProgressBar.Small" parent="Widget.Quantum.ProgressBar.Small"/>
@@ -894,7 +888,6 @@
     <style name="Widget.Quantum.Light.ProgressBar.Small.Inverse" parent="Widget.Quantum.ProgressBar.Small.Inverse"/>
     <style name="Widget.Quantum.Light.ProgressBar.Large.Inverse" parent="Widget.Quantum.ProgressBar.Large.Inverse"/>
     <style name="Widget.Quantum.Light.SeekBar" parent="Widget.Quantum.SeekBar"/>
-
     <style name="Widget.Quantum.Light.RatingBar" parent="Widget.Quantum.RatingBar" />
 
     <style name="Widget.Quantum.Light.RatingBar.Indicator" parent="Widget.RatingBar.Indicator">
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index dcff978..8b1ca31 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -69,8 +69,6 @@
   <java-symbol type="id" name="edittext_container" />
   <java-symbol type="id" name="enter_pin_section" />
   <java-symbol type="id" name="expand_activities_button" />
-  <java-symbol type="id" name="expand_button" />
-  <java-symbol type="id" name="expand_button_divider" />
   <java-symbol type="id" name="expires_on" />
   <java-symbol type="id" name="find_next" />
   <java-symbol type="id" name="find_prev" />
@@ -161,9 +159,7 @@
   <java-symbol type="id" name="share" />
   <java-symbol type="id" name="shortcut" />
   <java-symbol type="id" name="skip_button" />
-  <java-symbol type="id" name="slider_group" />
   <java-symbol type="id" name="split_action_bar" />
-  <java-symbol type="id" name="stream_icon" />
   <java-symbol type="id" name="submit_area" />
   <java-symbol type="id" name="switch_new" />
   <java-symbol type="id" name="switch_old" />
@@ -181,7 +177,6 @@
   <java-symbol type="id" name="topPanel" />
   <java-symbol type="id" name="up" />
   <java-symbol type="id" name="value" />
-  <java-symbol type="id" name="visible_panel" />
   <java-symbol type="id" name="websearch" />
   <java-symbol type="id" name="wifi_p2p_wps_pin" />
   <java-symbol type="id" name="year" />
@@ -343,7 +338,6 @@
   <java-symbol type="dimen" name="search_view_preferred_width" />
   <java-symbol type="dimen" name="textview_error_popup_default_width" />
   <java-symbol type="dimen" name="toast_y_offset" />
-  <java-symbol type="dimen" name="volume_panel_top" />
   <java-symbol type="dimen" name="action_bar_stacked_max_height" />
   <java-symbol type="dimen" name="action_bar_stacked_tab_max_width" />
   <java-symbol type="dimen" name="notification_text_size" />
@@ -1199,8 +1193,6 @@
   <java-symbol type="layout" name="time_picker_legacy" />
   <java-symbol type="layout" name="time_picker_dialog" />
   <java-symbol type="layout" name="transient_notification" />
-  <java-symbol type="layout" name="volume_adjust" />
-  <java-symbol type="layout" name="volume_adjust_item" />
   <java-symbol type="layout" name="voice_interaction_session" />
   <java-symbol type="layout" name="web_text_view_dropdown" />
   <java-symbol type="layout" name="webview_find" />
@@ -1294,6 +1286,10 @@
   <java-symbol type="anim" name="dock_left_exit" />
   <java-symbol type="anim" name="dock_right_enter" />
   <java-symbol type="anim" name="dock_right_exit" />
+  <java-symbol type="anim" name="voice_activity_close_exit" />
+  <java-symbol type="anim" name="voice_activity_close_enter" />
+  <java-symbol type="anim" name="voice_activity_open_exit" />
+  <java-symbol type="anim" name="voice_activity_open_enter" />
 
   <java-symbol type="array" name="config_hdmiCecLogicalDeviceType" />
   <java-symbol type="array" name="config_keyboardTapVibePattern" />
@@ -1392,6 +1388,7 @@
   <java-symbol type="string" name="android_upgrading_title" />
   <java-symbol type="string" name="bugreport_title" />
   <java-symbol type="string" name="bugreport_message" />
+  <java-symbol type="string" name="bugreport_status" />
   <java-symbol type="string" name="faceunlock_multiple_failures" />
   <java-symbol type="string" name="global_action_power_off" />
   <java-symbol type="string" name="global_actions_airplane_mode_off_status" />
@@ -1683,7 +1680,6 @@
   <java-symbol type="anim" name="push_down_out" />
   <java-symbol type="anim" name="push_up_in" />
   <java-symbol type="anim" name="push_up_out" />
-  <java-symbol type="anim" name="lock_screen_wallpaper_behind_enter" />
   <java-symbol type="anim" name="lock_screen_behind_enter" />
 
   <java-symbol type="bool" name="config_alwaysUseCdmaRssi" />
@@ -1870,6 +1866,7 @@
   <java-symbol type="attr" name="toolbarStyle" />
   <java-symbol type="attr" name="titleTextAppearance" />
   <java-symbol type="attr" name="subtitleTextAppearance" />
+  <java-symbol type="attr" name="windowActionBarFullscreenDecorLayout" />
   <java-symbol type="drawable" name="ic_lock_bugreport" />
   <java-symbol type="id" name="icon_frame" />
   <java-symbol type="style" name="Animation.VolumePanel" />
diff --git a/core/res/res/values/themes.xml b/core/res/res/values/themes.xml
index aaab949..648660b 100644
--- a/core/res/res/values/themes.xml
+++ b/core/res/res/values/themes.xml
@@ -124,6 +124,7 @@
         <item name="buttonStyleToggle">@android:style/Widget.Button.Toggle</item>
 
         <item name="selectableItemBackground">@android:drawable/item_background</item>
+        <item name="selectableItemBackgroundBorderless">?android:attr/selectableItemBackground</item>
         <item name="borderlessButtonStyle">?android:attr/buttonStyle</item>
         <item name="homeAsUpIndicator">@android:drawable/ic_ab_back_holo_dark</item>
 
@@ -192,6 +193,7 @@
         <item name="windowDrawsSystemBarBackgrounds">false</item>
         <item name="statusBarColor">@android:color/black</item>
         <item name="navigationBarColor">@android:color/black</item>
+        <item name="windowActionBarFullscreenDecorLayout">@layout/screen_action_bar</item>
 
         <!-- Define these here; ContextThemeWrappers around themes that define them should
              always clear these values. -->
@@ -1031,6 +1033,7 @@
         <item name="mediaRouteButtonStyle">@android:style/Widget.Holo.MediaRouteButton</item>
 
         <item name="selectableItemBackground">@android:drawable/item_background_holo_dark</item>
+        <item name="selectableItemBackgroundBorderless">?android:attr/selectableItemBackground</item>
         <item name="borderlessButtonStyle">@android:style/Widget.Holo.Button.Borderless</item>
         <item name="homeAsUpIndicator">@android:drawable/ic_ab_back_holo_dark</item>
 
@@ -1371,6 +1374,7 @@
         <item name="mediaRouteButtonStyle">@android:style/Widget.Holo.Light.MediaRouteButton</item>
 
         <item name="selectableItemBackground">@android:drawable/item_background_holo_light</item>
+        <item name="selectableItemBackgroundBorderless">?android:attr/selectableItemBackground</item>
         <item name="borderlessButtonStyle">@android:style/Widget.Holo.Light.Button.Borderless</item>
         <item name="homeAsUpIndicator">@android:drawable/ic_ab_back_holo_light</item>
 
diff --git a/core/res/res/values/themes_quantum.xml b/core/res/res/values/themes_quantum.xml
index bb787bb..cdbd771 100644
--- a/core/res/res/values/themes_quantum.xml
+++ b/core/res/res/values/themes_quantum.xml
@@ -101,6 +101,7 @@
         <item name="mediaRouteButtonStyle">@style/Widget.Quantum.MediaRouteButton</item>
 
         <item name="selectableItemBackground">@drawable/item_background_quantum</item>
+        <item name="selectableItemBackgroundBorderless">@drawable/item_background_borderless_quantum</item>
         <item name="borderlessButtonStyle">@style/Widget.Quantum.Button.Borderless</item>
         <item name="homeAsUpIndicator">@drawable/ic_ab_back_quantum</item>
 
@@ -125,8 +126,7 @@
         <item name="listChoiceIndicatorSingle">@drawable/btn_radio_quantum_anim</item>
         <item name="listChoiceIndicatorMultiple">@drawable/btn_check_quantum_anim</item>
 
-        <item name="listChoiceBackgroundIndicator">@drawable/list_selector_quantum</item>
-
+        <item name="listChoiceBackgroundIndicator">?attr/selectableItemBackground</item>
         <item name="activatedBackgroundIndicator">@drawable/activated_background_quantum</item>
 
         <item name="listDividerAlertDialog">@drawable/list_divider_quantum</item>
@@ -162,6 +162,7 @@
         <item name="windowActionBar">true</item>
         <item name="windowActionModeOverlay">false</item>
         <item name="windowDrawsSystemBarBackgrounds">true</item>
+        <item name="windowActionBarFullscreenDecorLayout">@layout/screen_toolbar</item>
         <item name="statusBarColor">?attr/colorPrimaryDark</item>
         <item name="navigationBarColor">?attr/colorPrimaryDark</item>
 
@@ -307,7 +308,7 @@
         <item name="actionModePopupWindowStyle">@style/Widget.Quantum.PopupWindow.ActionMode</item>
         <item name="actionBarWidgetTheme">@style/ThemeOverlay.Quantum.ActionBarWidget</item>
         <item name="actionBarTheme">@null</item>
-        <item name="actionBarItemBackground">@drawable/item_background_quantum</item>
+        <item name="actionBarItemBackground">?attr/selectableItemBackgroundBorderless</item>
 
         <item name="actionModeCutDrawable">@drawable/ic_menu_cut_quantum</item>
         <item name="actionModeCopyDrawable">@drawable/ic_menu_copy_quantum</item>
@@ -377,8 +378,8 @@
 
         <item name="colorControlNormal">?attr/textColorSecondary</item>
         <item name="colorControlActivated">?attr/colorPrimary</item>
-        <item name="colorControlHighlight">#30ffffff</item>
 
+        <item name="colorControlHighlight">@color/ripple_quantum_dark</item>
         <item name="colorButtonNormal">@color/btn_default_quantum_dark</item>
     </style>
 
@@ -445,6 +446,7 @@
         <item name="mediaRouteButtonStyle">@style/Widget.Quantum.Light.MediaRouteButton</item>
 
         <item name="selectableItemBackground">@drawable/item_background_quantum</item>
+        <item name="selectableItemBackgroundBorderless">@drawable/item_background_borderless_quantum</item>
         <item name="borderlessButtonStyle">@style/Widget.Quantum.Light.Button.Borderless</item>
         <item name="homeAsUpIndicator">@drawable/ic_ab_back_quantum</item>
 
@@ -469,8 +471,7 @@
         <item name="listChoiceIndicatorSingle">@drawable/btn_radio_quantum_anim</item>
         <item name="listChoiceIndicatorMultiple">@drawable/btn_check_quantum_anim</item>
 
-        <item name="listChoiceBackgroundIndicator">@drawable/list_selector_quantum</item>
-
+        <item name="listChoiceBackgroundIndicator">?attr/selectableItemBackground</item>
         <item name="activatedBackgroundIndicator">@drawable/activated_background_quantum</item>
 
         <item name="expandableListPreferredItemPaddingLeft">40dip</item>
@@ -505,6 +506,7 @@
         <item name="windowActionBar">true</item>
         <item name="windowActionModeOverlay">false</item>
         <item name="windowDrawsSystemBarBackgrounds">true</item>
+        <item name="windowActionBarFullscreenDecorLayout">@layout/screen_toolbar</item>
         <item name="statusBarColor">?attr/colorPrimaryDark</item>
         <item name="navigationBarColor">?attr/colorPrimaryDark</item>
 
@@ -653,7 +655,7 @@
         <item name="actionModePopupWindowStyle">@style/Widget.Quantum.Light.PopupWindow.ActionMode</item>
         <item name="actionBarWidgetTheme">@style/ThemeOverlay.Quantum.ActionBarWidget</item>
         <item name="actionBarTheme">@null</item>
-        <item name="actionBarItemBackground">@drawable/item_background_quantum</item>
+        <item name="actionBarItemBackground">?attr/selectableItemBackgroundBorderless</item>
 
         <item name="actionModeCutDrawable">@drawable/ic_menu_cut_quantum</item>
         <item name="actionModeCopyDrawable">@drawable/ic_menu_copy_quantum</item>
@@ -666,7 +668,7 @@
         <item name="dividerVertical">?attr/listDivider</item>
         <item name="dividerHorizontal">?attr/listDivider</item>
         <item name="buttonBarStyle">@style/Widget.Quantum.Light.ButtonBar</item>
-        <item name="buttonBarButtonStyle">@style/Widget.Quantum.Light.Button.Borderless.Small</item>
+        <item name="buttonBarButtonStyle">@style/Widget.Quantum.Light.Button.Borderless</item>
         <item name="segmentedButtonStyle">@style/Widget.Quantum.Light.SegmentedButton</item>
 
         <!-- SearchView attributes -->
@@ -719,8 +721,8 @@
 
         <item name="colorControlNormal">?attr/textColorSecondary</item>
         <item name="colorControlActivated">?attr/colorPrimary</item>
-        <item name="colorControlHighlight">#30000000</item>
 
+        <item name="colorControlHighlight">@color/ripple_quantum_light</item>
         <item name="colorButtonNormal">@color/btn_default_quantum_light</item>
     </style>
 
@@ -768,7 +770,8 @@
         <item name="fastScrollPreviewBackgroundLeft">@drawable/fastscroll_label_left_holo_light</item>
         <item name="fastScrollPreviewBackgroundRight">@drawable/fastscroll_label_right_holo_light</item>
 
-        <item name="colorButtonNormal">@color/quantum_grey_100</item>
+        <item name="colorControlHighlight">@color/ripple_quantum_light</item>
+        <item name="colorButtonNormal">@color/btn_default_quantum_light</item>
     </style>
 
     <!-- Theme overlay that replaces colors with their dark versions but preserves
@@ -803,7 +806,8 @@
         <item name="fastScrollPreviewBackgroundLeft">@drawable/fastscroll_label_left_holo_dark</item>
         <item name="fastScrollPreviewBackgroundRight">@drawable/fastscroll_label_right_holo_dark</item>
 
-        <item name="colorButtonNormal">@color/quantum_grey_700</item>
+        <item name="colorControlHighlight">@color/ripple_quantum_dark</item>
+        <item name="colorButtonNormal">@color/btn_default_quantum_dark</item>
     </style>
 
     <!-- Theme overlay that replaces the activated control color (which by default
@@ -911,6 +915,22 @@
         <item name="windowNoTitle">true</item>
     </style>
 
+    <!-- Quantum theme for an activity that is to be used for voice interaction.
+         This gives the activity a floating dialog style, to incorporate with the
+         system voice experience. -->
+    <style name="Theme.Quantum.Voice" parent="@style/Theme.Quantum.Dialog">
+        <item name="windowAnimationStyle">@style/Animation.VoiceActivity</item>
+        <item name="backgroundDimEnabled">false</item>
+    </style>
+
+    <!-- Quantum light theme for an activity that is to be used for voice interaction.
+         This gives the activity a floating dialog style, to incorporate with the
+         system voice experience. -->
+    <style name="Theme.Quantum.Light.Voice" parent="@style/Theme.Quantum.Light.Dialog">
+        <item name="windowAnimationStyle">@style/Animation.VoiceActivity</item>
+        <item name="backgroundDimEnabled">false</item>
+    </style>
+
     <!-- Default theme for quantum style input methods, which is used by the
          {@link android.inputmethodservice.InputMethodService} class.
          this inherits from Theme.Panel, but sets up IME appropriate animations
@@ -927,7 +947,7 @@
          this inherits from Theme.Panel, but sets up appropriate animations
          and a few custom attributes. -->
     <style name="Theme.Quantum.VoiceInteractionSession" parent="Theme.Quantum.Light.Panel">
-        <item name="android:windowAnimationStyle">@android:style/Animation.VoiceInteractionSession</item>
+        <item name="windowAnimationStyle">@style/Animation.VoiceInteractionSession</item>
     </style>
 
     <!-- Theme for the search input bar. -->
@@ -984,7 +1004,7 @@
         <item name="colorBackgroundCacheHint">@null</item>
 
         <item name="buttonBarStyle">@style/Widget.Quantum.ButtonBar.AlertDialog</item>
-        <item name="borderlessButtonStyle">@style/Widget.Quantum.Button.Borderless.Small</item>
+        <item name="borderlessButtonStyle">@style/Widget.Quantum.Button.Borderless</item>
 
         <item name="textAppearance">@style/TextAppearance.Quantum</item>
         <item name="textAppearanceInverse">@style/TextAppearance.Quantum.Inverse</item>
@@ -1103,7 +1123,7 @@
         <item name="colorBackgroundCacheHint">@null</item>
 
         <item name="buttonBarStyle">@style/Widget.Quantum.Light.ButtonBar.AlertDialog</item>
-        <item name="borderlessButtonStyle">@style/Widget.Quantum.Light.Button.Borderless.Small</item>
+        <item name="borderlessButtonStyle">@style/Widget.Quantum.Light.Button.Borderless</item>
 
         <item name="textAppearance">@style/TextAppearance.Quantum</item>
         <item name="textAppearanceInverse">@style/TextAppearance.Quantum.Inverse</item>
diff --git a/core/tests/bluetoothtests/src/android/bluetooth/BluetoothLeScanFilterTest.java b/core/tests/bluetoothtests/src/android/bluetooth/BluetoothLeScanFilterTest.java
deleted file mode 100644
index ec35d85..0000000
--- a/core/tests/bluetoothtests/src/android/bluetooth/BluetoothLeScanFilterTest.java
+++ /dev/null
@@ -1,185 +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.bluetooth;
-
-import android.bluetooth.BluetoothLeScanner.ScanResult;
-import android.os.Parcel;
-import android.os.ParcelUuid;
-import android.test.suitebuilder.annotation.SmallTest;
-
-import junit.framework.TestCase;
-
-/**
- * Unit test cases for Bluetooth LE scan filters.
- * <p>
- * To run this test, use adb shell am instrument -e class
- * 'android.bluetooth.BluetoothLeScanFilterTest' -w
- * 'com.android.bluetooth.tests/android.bluetooth.BluetoothTestRunner'
- */
-public class BluetoothLeScanFilterTest extends TestCase {
-
-    private static final String DEVICE_MAC = "01:02:03:04:05:AB";
-    private ScanResult mScanResult;
-    private BluetoothLeScanFilter.Builder mFilterBuilder;
-
-    @Override
-    protected void setUp() throws Exception {
-        byte[] scanRecord = new byte[] {
-                0x02, 0x01, 0x1a, // advertising flags
-                0x05, 0x02, 0x0b, 0x11, 0x0a, 0x11, // 16 bit service uuids
-                0x04, 0x09, 0x50, 0x65, 0x64, // name
-                0x02, 0x0A, (byte) 0xec, // tx power level
-                0x05, 0x16, 0x0b, 0x11, 0x50, 0x64, // service data
-                0x05, (byte) 0xff, (byte) 0xe0, 0x00, 0x02, 0x15, // manufacturer specific data
-                0x03, 0x50, 0x01, 0x02, // an unknown data type won't cause trouble
-        };
-
-        BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
-        BluetoothDevice device = adapter.getRemoteDevice(DEVICE_MAC);
-        mScanResult = new ScanResult(device, scanRecord, -10, 1397545200000000L);
-        mFilterBuilder = BluetoothLeScanFilter.newBuilder();
-    }
-
-    @SmallTest
-    public void testNameFilter() {
-        BluetoothLeScanFilter filter = mFilterBuilder.name("Ped").build();
-        assertTrue("name filter fails", filter.matches(mScanResult));
-
-        filter = mFilterBuilder.name("Pem").build();
-        assertFalse("name filter fails", filter.matches(mScanResult));
-
-    }
-
-    @SmallTest
-    public void testDeviceFilter() {
-        BluetoothLeScanFilter filter = mFilterBuilder.macAddress(DEVICE_MAC).build();
-        assertTrue("device filter fails", filter.matches(mScanResult));
-
-        filter = mFilterBuilder.macAddress("11:22:33:44:55:66").build();
-        assertFalse("device filter fails", filter.matches(mScanResult));
-    }
-
-    @SmallTest
-    public void testServiceUuidFilter() {
-        BluetoothLeScanFilter filter = mFilterBuilder.serviceUuid(
-                ParcelUuid.fromString("0000110A-0000-1000-8000-00805F9B34FB")).build();
-        assertTrue("uuid filter fails", filter.matches(mScanResult));
-
-        filter = mFilterBuilder.serviceUuid(
-                ParcelUuid.fromString("0000110C-0000-1000-8000-00805F9B34FB")).build();
-        assertFalse("uuid filter fails", filter.matches(mScanResult));
-
-        filter = mFilterBuilder
-                .serviceUuid(ParcelUuid.fromString("0000110C-0000-1000-8000-00805F9B34FB"))
-                .serviceUuidMask(ParcelUuid.fromString("FFFFFFF0-FFFF-FFFF-FFFF-FFFFFFFFFFFF"))
-                .build();
-        assertTrue("uuid filter fails", filter.matches(mScanResult));
-    }
-
-    @SmallTest
-    public void testServiceDataFilter() {
-        byte[] serviceData = new byte[] {
-                0x0b, 0x11, 0x50, 0x64 };
-        BluetoothLeScanFilter filter = mFilterBuilder.serviceData(serviceData).build();
-        assertTrue("service data filter fails", filter.matches(mScanResult));
-
-        byte[] nonMatchData = new byte[] {
-                0x0b, 0x01, 0x50, 0x64 };
-        filter = mFilterBuilder.serviceData(nonMatchData).build();
-        assertFalse("service data filter fails", filter.matches(mScanResult));
-
-        byte[] mask = new byte[] {
-                (byte) 0xFF, (byte) 0x00, (byte) 0xFF, (byte) 0xFF };
-        filter = mFilterBuilder.serviceData(nonMatchData).serviceDataMask(mask).build();
-        assertTrue("partial service data filter fails", filter.matches(mScanResult));
-    }
-
-    @SmallTest
-    public void testManufacturerSpecificData() {
-        byte[] manufacturerData = new byte[] {
-                (byte) 0xE0, 0x00, 0x02, 0x15 };
-        int manufacturerId = 224;
-        BluetoothLeScanFilter filter =
-                mFilterBuilder.manufacturerData(manufacturerId, manufacturerData).build();
-        assertTrue("manufacturerData filter fails", filter.matches(mScanResult));
-
-        byte[] nonMatchData = new byte[] {
-                (byte) 0xF0, 0x00, 0x02, 0x15 };
-        filter = mFilterBuilder.manufacturerData(manufacturerId, nonMatchData).build();
-        assertFalse("manufacturerData filter fails", filter.matches(mScanResult));
-
-        byte[] mask = new byte[] {
-                (byte) 0x00, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF
-        };
-        filter = mFilterBuilder.manufacturerData(manufacturerId, nonMatchData)
-                .manufacturerDataMask(mask).build();
-        assertTrue("partial manufacturerData filter fails", filter.matches(mScanResult));
-    }
-
-    @SmallTest
-    public void testReadWriteParcel() {
-        BluetoothLeScanFilter filter = mFilterBuilder.build();
-        testReadWriteParcelForFilter(filter);
-
-        filter = mFilterBuilder.name("Ped").build();
-        testReadWriteParcelForFilter(filter);
-
-        filter = mFilterBuilder.macAddress("11:22:33:44:55:66").build();
-        testReadWriteParcelForFilter(filter);
-
-        filter = mFilterBuilder.serviceUuid(
-                ParcelUuid.fromString("0000110C-0000-1000-8000-00805F9B34FB")).build();
-        testReadWriteParcelForFilter(filter);
-
-        filter = mFilterBuilder.serviceUuidMask(
-                ParcelUuid.fromString("FFFFFFF0-FFFF-FFFF-FFFF-FFFFFFFFFFFF")).build();
-        testReadWriteParcelForFilter(filter);
-
-        byte[] serviceData = new byte[] {
-                0x0b, 0x11, 0x50, 0x64 };
-
-        filter = mFilterBuilder.serviceData(serviceData).build();
-        testReadWriteParcelForFilter(filter);
-
-        byte[] serviceDataMask = new byte[] {
-                (byte) 0xFF, (byte) 0x00, (byte) 0xFF, (byte) 0xFF };
-        filter = mFilterBuilder.serviceDataMask(serviceDataMask).build();
-        testReadWriteParcelForFilter(filter);
-
-        byte[] manufacturerData = new byte[] {
-                (byte) 0xE0, 0x00, 0x02, 0x15 };
-        int manufacturerId = 224;
-        filter = mFilterBuilder.manufacturerData(manufacturerId, manufacturerData).build();
-        testReadWriteParcelForFilter(filter);
-
-        byte[] manufacturerDataMask = new byte[] {
-                (byte) 0x00, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF
-        };
-        filter = mFilterBuilder.manufacturerDataMask(manufacturerDataMask).build();
-        testReadWriteParcelForFilter(filter);
-    }
-
-    private void testReadWriteParcelForFilter(BluetoothLeScanFilter filter) {
-        Parcel parcel = Parcel.obtain();
-        filter.writeToParcel(parcel, 0);
-        parcel.setDataPosition(0);
-        BluetoothLeScanFilter filterFromParcel =
-                BluetoothLeScanFilter.CREATOR.createFromParcel(parcel);
-        System.out.println(filterFromParcel);
-        assertEquals(filter, filterFromParcel);
-    }
-}
diff --git a/core/tests/bluetoothtests/src/android/bluetooth/le/ScanFilterTest.java b/core/tests/bluetoothtests/src/android/bluetooth/le/ScanFilterTest.java
new file mode 100644
index 0000000..bf34f1d
--- /dev/null
+++ b/core/tests/bluetoothtests/src/android/bluetooth/le/ScanFilterTest.java
@@ -0,0 +1,186 @@
+/*
+ * 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.bluetooth.le;
+
+import android.bluetooth.BluetoothAdapter;
+import android.bluetooth.BluetoothDevice;
+import android.os.Parcel;
+import android.os.ParcelUuid;
+import android.test.suitebuilder.annotation.SmallTest;
+
+import junit.framework.TestCase;
+
+/**
+ * Unit test cases for Bluetooth LE scan filters.
+ * <p>
+ * To run this test, use adb shell am instrument -e class 'android.bluetooth.ScanFilterTest' -w
+ * 'com.android.bluetooth.tests/android.bluetooth.BluetoothTestRunner'
+ */
+public class ScanFilterTest extends TestCase {
+
+    private static final String DEVICE_MAC = "01:02:03:04:05:AB";
+    private ScanResult mScanResult;
+    private ScanFilter.Builder mFilterBuilder;
+
+    @Override
+    protected void setUp() throws Exception {
+        byte[] scanRecord = new byte[] {
+                0x02, 0x01, 0x1a, // advertising flags
+                0x05, 0x02, 0x0b, 0x11, 0x0a, 0x11, // 16 bit service uuids
+                0x04, 0x09, 0x50, 0x65, 0x64, // setName
+                0x02, 0x0A, (byte) 0xec, // tx power level
+                0x05, 0x16, 0x0b, 0x11, 0x50, 0x64, // service data
+                0x05, (byte) 0xff, (byte) 0xe0, 0x00, 0x02, 0x15, // manufacturer specific data
+                0x03, 0x50, 0x01, 0x02, // an unknown data type won't cause trouble
+        };
+
+        BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
+        BluetoothDevice device = adapter.getRemoteDevice(DEVICE_MAC);
+        mScanResult = new ScanResult(device, scanRecord, -10, 1397545200000000L);
+        mFilterBuilder = new ScanFilter.Builder();
+    }
+
+    @SmallTest
+    public void testsetNameFilter() {
+        ScanFilter filter = mFilterBuilder.setName("Ped").build();
+        assertTrue("setName filter fails", filter.matches(mScanResult));
+
+        filter = mFilterBuilder.setName("Pem").build();
+        assertFalse("setName filter fails", filter.matches(mScanResult));
+
+    }
+
+    @SmallTest
+    public void testDeviceFilter() {
+        ScanFilter filter = mFilterBuilder.setMacAddress(DEVICE_MAC).build();
+        assertTrue("device filter fails", filter.matches(mScanResult));
+
+        filter = mFilterBuilder.setMacAddress("11:22:33:44:55:66").build();
+        assertFalse("device filter fails", filter.matches(mScanResult));
+    }
+
+    @SmallTest
+    public void testsetServiceUuidFilter() {
+        ScanFilter filter = mFilterBuilder.setServiceUuid(
+                ParcelUuid.fromString("0000110A-0000-1000-8000-00805F9B34FB")).build();
+        assertTrue("uuid filter fails", filter.matches(mScanResult));
+
+        filter = mFilterBuilder.setServiceUuid(
+                ParcelUuid.fromString("0000110C-0000-1000-8000-00805F9B34FB")).build();
+        assertFalse("uuid filter fails", filter.matches(mScanResult));
+
+        filter = mFilterBuilder
+                .setServiceUuid(ParcelUuid.fromString("0000110C-0000-1000-8000-00805F9B34FB"),
+                        ParcelUuid.fromString("FFFFFFF0-FFFF-FFFF-FFFF-FFFFFFFFFFFF"))
+                .build();
+        assertTrue("uuid filter fails", filter.matches(mScanResult));
+    }
+
+    @SmallTest
+    public void testsetServiceDataFilter() {
+        byte[] setServiceData = new byte[] {
+                0x0b, 0x11, 0x50, 0x64 };
+        ScanFilter filter = mFilterBuilder.setServiceData(setServiceData).build();
+        assertTrue("service data filter fails", filter.matches(mScanResult));
+
+        byte[] nonMatchData = new byte[] {
+                0x0b, 0x01, 0x50, 0x64 };
+        filter = mFilterBuilder.setServiceData(nonMatchData).build();
+        assertFalse("service data filter fails", filter.matches(mScanResult));
+
+        byte[] mask = new byte[] {
+                (byte) 0xFF, (byte) 0x00, (byte) 0xFF, (byte) 0xFF };
+        filter = mFilterBuilder.setServiceData(nonMatchData, mask).build();
+        assertTrue("partial service data filter fails", filter.matches(mScanResult));
+    }
+
+    @SmallTest
+    public void testManufacturerSpecificData() {
+        byte[] setManufacturerData = new byte[] {
+                (byte) 0xE0, 0x00, 0x02, 0x15 };
+        int manufacturerId = 224;
+        ScanFilter filter =
+                mFilterBuilder.setManufacturerData(manufacturerId, setManufacturerData).build();
+        assertTrue("setManufacturerData filter fails", filter.matches(mScanResult));
+
+        byte[] nonMatchData = new byte[] {
+                (byte) 0xF0, 0x00, 0x02, 0x15 };
+        filter = mFilterBuilder.setManufacturerData(manufacturerId, nonMatchData).build();
+        assertFalse("setManufacturerData filter fails", filter.matches(mScanResult));
+
+        byte[] mask = new byte[] {
+                (byte) 0x00, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF
+        };
+        filter = mFilterBuilder.setManufacturerData(manufacturerId, nonMatchData, mask).build();
+        assertTrue("partial setManufacturerData filter fails", filter.matches(mScanResult));
+    }
+
+    @SmallTest
+    public void testReadWriteParcel() {
+        ScanFilter filter = mFilterBuilder.build();
+        testReadWriteParcelForFilter(filter);
+
+        filter = mFilterBuilder.setName("Ped").build();
+        testReadWriteParcelForFilter(filter);
+
+        filter = mFilterBuilder.setMacAddress("11:22:33:44:55:66").build();
+        testReadWriteParcelForFilter(filter);
+
+        filter = mFilterBuilder.setServiceUuid(
+                ParcelUuid.fromString("0000110C-0000-1000-8000-00805F9B34FB")).build();
+        testReadWriteParcelForFilter(filter);
+
+        filter = mFilterBuilder.setServiceUuid(
+                ParcelUuid.fromString("0000110C-0000-1000-8000-00805F9B34FB"),
+                ParcelUuid.fromString("FFFFFFF0-FFFF-FFFF-FFFF-FFFFFFFFFFFF")).build();
+        testReadWriteParcelForFilter(filter);
+
+        byte[] setServiceData = new byte[] {
+                0x0b, 0x11, 0x50, 0x64 };
+
+        filter = mFilterBuilder.setServiceData(setServiceData).build();
+        testReadWriteParcelForFilter(filter);
+
+        byte[] serviceDataMask = new byte[] {
+                (byte) 0xFF, (byte) 0x00, (byte) 0xFF, (byte) 0xFF };
+        filter = mFilterBuilder.setServiceData(setServiceData, serviceDataMask).build();
+        testReadWriteParcelForFilter(filter);
+
+        byte[] manufacturerData = new byte[] {
+                (byte) 0xE0, 0x00, 0x02, 0x15 };
+        int manufacturerId = 224;
+        filter = mFilterBuilder.setManufacturerData(manufacturerId, manufacturerData).build();
+        testReadWriteParcelForFilter(filter);
+
+        byte[] manufacturerDataMask = new byte[] {
+                (byte) 0x00, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF
+        };
+        filter = mFilterBuilder.setManufacturerData(manufacturerId, manufacturerData,
+                manufacturerDataMask).build();
+        testReadWriteParcelForFilter(filter);
+    }
+
+    private void testReadWriteParcelForFilter(ScanFilter filter) {
+        Parcel parcel = Parcel.obtain();
+        filter.writeToParcel(parcel, 0);
+        parcel.setDataPosition(0);
+        ScanFilter filterFromParcel =
+                ScanFilter.CREATOR.createFromParcel(parcel);
+        System.out.println(filterFromParcel);
+        assertEquals(filter, filterFromParcel);
+    }
+}
diff --git a/core/tests/bluetoothtests/src/android/bluetooth/BluetoothLeAdvertiseScanDataTest.java b/core/tests/bluetoothtests/src/android/bluetooth/le/ScanRecordTest.java
similarity index 87%
rename from core/tests/bluetoothtests/src/android/bluetooth/BluetoothLeAdvertiseScanDataTest.java
rename to core/tests/bluetoothtests/src/android/bluetooth/le/ScanRecordTest.java
index eb6c419..cece96b 100644
--- a/core/tests/bluetoothtests/src/android/bluetooth/BluetoothLeAdvertiseScanDataTest.java
+++ b/core/tests/bluetoothtests/src/android/bluetooth/le/ScanRecordTest.java
@@ -14,8 +14,9 @@
  * limitations under the License.
  */
 
-package android.bluetooth;
+package android.bluetooth.le;
 
+import android.bluetooth.le.ScanRecord;
 import android.os.ParcelUuid;
 import android.test.suitebuilder.annotation.SmallTest;
 
@@ -24,13 +25,13 @@
 import java.util.Arrays;
 
 /**
- * Unit test cases for {@link BluetoothLeAdvertiseScanData}.
+ * Unit test cases for {@link ScanRecord}.
  * <p>
  * To run this test, use adb shell am instrument -e class
- * 'android.bluetooth.BluetoothLeAdvertiseScanDataTest' -w
+ * 'android.bluetooth.ScanRecordTest' -w
  * 'com.android.bluetooth.tests/android.bluetooth.BluetoothTestRunner'
  */
-public class BluetoothLeAdvertiseScanDataTest extends TestCase {
+public class ScanRecordTest extends TestCase {
 
     @SmallTest
     public void testParser() {
@@ -43,8 +44,7 @@
                 0x05, (byte) 0xff, (byte) 0xe0, 0x00, 0x02, 0x15, // manufacturer specific data
                 0x03, 0x50, 0x01, 0x02, // an unknown data type won't cause trouble
         };
-        BluetoothLeAdvertiseScanData.ScanRecord data = BluetoothLeAdvertiseScanData.ScanRecord
-                .getParser().parseFromScanRecord(scanRecord);
+        ScanRecord data = ScanRecord.parseFromBytes(scanRecord);
         assertEquals(0x1a, data.getAdvertiseFlags());
         ParcelUuid uuid1 = ParcelUuid.fromString("0000110A-0000-1000-8000-00805F9B34FB");
         ParcelUuid uuid2 = ParcelUuid.fromString("0000110B-0000-1000-8000-00805F9B34FB");
diff --git a/core/tests/bluetoothtests/src/android/bluetooth/BluetoothLeScannerTest.java b/core/tests/bluetoothtests/src/android/bluetooth/le/ScanResultTest.java
similarity index 79%
rename from core/tests/bluetoothtests/src/android/bluetooth/BluetoothLeScannerTest.java
rename to core/tests/bluetoothtests/src/android/bluetooth/le/ScanResultTest.java
index 8064ba8..241e88f 100644
--- a/core/tests/bluetoothtests/src/android/bluetooth/BluetoothLeScannerTest.java
+++ b/core/tests/bluetoothtests/src/android/bluetooth/le/ScanResultTest.java
@@ -14,9 +14,10 @@
  * limitations under the License.
  */
 
-package android.bluetooth;
+package android.bluetooth.le;
 
-import android.bluetooth.BluetoothLeScanner.ScanResult;
+import android.bluetooth.BluetoothAdapter;
+import android.bluetooth.BluetoothDevice;
 import android.os.Parcel;
 import android.test.suitebuilder.annotation.SmallTest;
 
@@ -25,17 +26,18 @@
 /**
  * Unit test cases for Bluetooth LE scans.
  * <p>
- * To run this test, use adb shell am instrument -e class 'android.bluetooth.BluetoothLeScannerTest'
- * -w 'com.android.bluetooth.tests/android.bluetooth.BluetoothTestRunner'
+ * To run this test, use adb shell am instrument -e class 'android.bluetooth.ScanResultTest' -w
+ * 'com.android.bluetooth.tests/android.bluetooth.BluetoothTestRunner'
  */
-public class BluetoothLeScannerTest extends TestCase {
+public class ScanResultTest extends TestCase {
 
     /**
      * Test read and write parcel of ScanResult
      */
     @SmallTest
     public void testScanResultParceling() {
-        BluetoothDevice device = new BluetoothDevice("01:02:03:04:05:06");
+        BluetoothDevice device = BluetoothAdapter.getDefaultAdapter().getRemoteDevice(
+                "01:02:03:04:05:06");
         byte[] scanRecord = new byte[] {
                 1, 2, 3 };
         int rssi = -10;
diff --git a/core/tests/coretests/src/com/android/internal/util/ArrayUtilsTest.java b/core/tests/coretests/src/com/android/internal/util/ArrayUtilsTest.java
index 5dc9ef8..433d4d2 100644
--- a/core/tests/coretests/src/com/android/internal/util/ArrayUtilsTest.java
+++ b/core/tests/coretests/src/com/android/internal/util/ArrayUtilsTest.java
@@ -16,6 +16,9 @@
 
 package com.android.internal.util;
 
+import android.test.MoreAsserts;
+
+import java.util.Arrays;
 import junit.framework.TestCase;
 
 /**
@@ -77,4 +80,79 @@
         assertFalse(ArrayUtils.containsAll(new Object[] { }, new Object[] { null }));
         assertFalse(ArrayUtils.containsAll(new Object[] { A }, new Object[] { null }));
     }
+
+    public void testContainsInt() throws Exception {
+        assertTrue(ArrayUtils.contains(new int[] { 1, 2, 3 }, 1));
+        assertTrue(ArrayUtils.contains(new int[] { 1, 2, 3 }, 2));
+        assertTrue(ArrayUtils.contains(new int[] { 1, 2, 3 }, 3));
+
+        assertFalse(ArrayUtils.contains(new int[] { 1, 2, 3 }, 0));
+        assertFalse(ArrayUtils.contains(new int[] { 1, 2, 3 }, 4));
+        assertFalse(ArrayUtils.contains(new int[] { }, 2));
+    }
+
+    public void testAppendInt() throws Exception {
+        MoreAsserts.assertEquals(new int[] { 1 },
+                ArrayUtils.appendInt(null, 1));
+        MoreAsserts.assertEquals(new int[] { 1 },
+                ArrayUtils.appendInt(new int[] { }, 1));
+        MoreAsserts.assertEquals(new int[] { 1, 2 },
+                ArrayUtils.appendInt(new int[] { 1 }, 2));
+        MoreAsserts.assertEquals(new int[] { 1, 2 },
+                ArrayUtils.appendInt(new int[] { 1, 2 }, 1));
+    }
+
+    public void testRemoveInt() throws Exception {
+        assertNull(ArrayUtils.removeInt(null, 1));
+        MoreAsserts.assertEquals(new int[] { },
+                ArrayUtils.removeInt(new int[] { }, 1));
+        MoreAsserts.assertEquals(new int[] { 1, 2, 3, },
+                ArrayUtils.removeInt(new int[] { 1, 2, 3}, 4));
+        MoreAsserts.assertEquals(new int[] { 2, 3, },
+                ArrayUtils.removeInt(new int[] { 1, 2, 3}, 1));
+        MoreAsserts.assertEquals(new int[] { 1, 3, },
+                ArrayUtils.removeInt(new int[] { 1, 2, 3}, 2));
+        MoreAsserts.assertEquals(new int[] { 1, 2, },
+                ArrayUtils.removeInt(new int[] { 1, 2, 3}, 3));
+        MoreAsserts.assertEquals(new int[] { 2, 3, 1 },
+                ArrayUtils.removeInt(new int[] { 1, 2, 3, 1 }, 1));
+    }
+
+    public void testContainsLong() throws Exception {
+        assertTrue(ArrayUtils.contains(new long[] { 1, 2, 3 }, 1));
+        assertTrue(ArrayUtils.contains(new long[] { 1, 2, 3 }, 2));
+        assertTrue(ArrayUtils.contains(new long[] { 1, 2, 3 }, 3));
+
+        assertFalse(ArrayUtils.contains(new long[] { 1, 2, 3 }, 0));
+        assertFalse(ArrayUtils.contains(new long[] { 1, 2, 3 }, 4));
+        assertFalse(ArrayUtils.contains(new long[] { }, 2));
+    }
+
+    public void testAppendLong() throws Exception {
+        MoreAsserts.assertEquals(new long[] { 1 },
+                ArrayUtils.appendLong(null, 1));
+        MoreAsserts.assertEquals(new long[] { 1 },
+                ArrayUtils.appendLong(new long[] { }, 1));
+        MoreAsserts.assertEquals(new long[] { 1, 2 },
+                ArrayUtils.appendLong(new long[] { 1 }, 2));
+        MoreAsserts.assertEquals(new long[] { 1, 2 },
+                ArrayUtils.appendLong(new long[] { 1, 2 }, 1));
+    }
+
+    public void testRemoveLong() throws Exception {
+        assertNull(ArrayUtils.removeLong(null, 1));
+        MoreAsserts.assertEquals(new long[] { },
+                ArrayUtils.removeLong(new long[] { }, 1));
+        MoreAsserts.assertEquals(new long[] { 1, 2, 3, },
+                ArrayUtils.removeLong(new long[] { 1, 2, 3}, 4));
+        MoreAsserts.assertEquals(new long[] { 2, 3, },
+                ArrayUtils.removeLong(new long[] { 1, 2, 3}, 1));
+        MoreAsserts.assertEquals(new long[] { 1, 3, },
+                ArrayUtils.removeLong(new long[] { 1, 2, 3}, 2));
+        MoreAsserts.assertEquals(new long[] { 1, 2, },
+                ArrayUtils.removeLong(new long[] { 1, 2, 3}, 3));
+        MoreAsserts.assertEquals(new long[] { 2, 3, 1 },
+                ArrayUtils.removeLong(new long[] { 1, 2, 3, 1 }, 1));
+    }
+
 }
diff --git a/core/tests/inputmethodtests/src/android/os/InputMethodSubtypeSwitchingControllerTest.java b/core/tests/inputmethodtests/src/android/os/InputMethodSubtypeSwitchingControllerTest.java
index 04cfe05..ca68e93 100644
--- a/core/tests/inputmethodtests/src/android/os/InputMethodSubtypeSwitchingControllerTest.java
+++ b/core/tests/inputmethodtests/src/android/os/InputMethodSubtypeSwitchingControllerTest.java
@@ -172,7 +172,8 @@
         final ImeSubtypeListItem japaneseIme_ja_JP = enabledItems.get(5);
         final ImeSubtypeListItem switchUnawareJapaneseIme_ja_JP = enabledItems.get(6);
 
-        final ControllerImpl controller = new ControllerImpl(enabledItems);
+        final ControllerImpl controller = ControllerImpl.createFrom(
+                null /* currentInstance */, enabledItems);
 
         // switching-aware loop
         assertRotationOrder(controller, false /* onlyCurrentIme */,
@@ -225,7 +226,8 @@
         final ImeSubtypeListItem japaneseIme_ja_JP = enabledItems.get(5);
         final ImeSubtypeListItem switchUnawareJapaneseIme_ja_JP = enabledItems.get(6);
 
-        final ControllerImpl controller = new ControllerImpl(enabledItems);
+        final ControllerImpl controller = ControllerImpl.createFrom(
+                null /* currentInstance */, enabledItems);
 
         // === switching-aware loop ===
         assertRotationOrder(controller, false /* onlyCurrentIme */,
@@ -271,5 +273,26 @@
                 subtypeUnawareIme, null);
         assertNextInputMethod(controller, true /* onlyCurrentIme */,
                 switchUnawareJapaneseIme_ja_JP, null);
+
+        // Rotation order should be preserved when created with the same subtype list.
+        final List<ImeSubtypeListItem> sameEnabledItems = createEnabledImeSubtypes();
+        final ControllerImpl newController = ControllerImpl.createFrom(controller,
+                sameEnabledItems);
+        assertRotationOrder(newController, false /* onlyCurrentIme */,
+                japaneseIme_ja_JP, latinIme_fr, latinIme_en_US);
+        assertRotationOrder(newController, false /* onlyCurrentIme */,
+                switchingUnawarelatinIme_en_UK, switchingUnawarelatinIme_hi, subtypeUnawareIme,
+                switchUnawareJapaneseIme_ja_JP);
+
+        // Rotation order should be initialized when created with a different subtype list.
+        final List<ImeSubtypeListItem> differentEnabledItems = Arrays.asList(
+                latinIme_en_US, latinIme_fr, switchingUnawarelatinIme_en_UK,
+                switchUnawareJapaneseIme_ja_JP);
+        final ControllerImpl anotherController = ControllerImpl.createFrom(controller,
+                differentEnabledItems);
+        assertRotationOrder(anotherController, false /* onlyCurrentIme */,
+                latinIme_en_US, latinIme_fr);
+        assertRotationOrder(anotherController, false /* onlyCurrentIme */,
+                switchingUnawarelatinIme_en_UK, switchUnawareJapaneseIme_ja_JP);
     }
 }
diff --git a/data/fonts/Roboto-Black.ttf b/data/fonts/Roboto-Black.ttf
index 2cdbe43..cb905bc 100644
--- a/data/fonts/Roboto-Black.ttf
+++ b/data/fonts/Roboto-Black.ttf
Binary files differ
diff --git a/data/fonts/Roboto-Bold.ttf b/data/fonts/Roboto-Bold.ttf
index 15c9b4e..68822ca 100644
--- a/data/fonts/Roboto-Bold.ttf
+++ b/data/fonts/Roboto-Bold.ttf
Binary files differ
diff --git a/data/fonts/Roboto-BoldItalic.ttf b/data/fonts/Roboto-BoldItalic.ttf
index a0abf30..aebf8eb 100644
--- a/data/fonts/Roboto-BoldItalic.ttf
+++ b/data/fonts/Roboto-BoldItalic.ttf
Binary files differ
diff --git a/data/fonts/Roboto-Italic.ttf b/data/fonts/Roboto-Italic.ttf
index 67b5394..2041cbc 100644
--- a/data/fonts/Roboto-Italic.ttf
+++ b/data/fonts/Roboto-Italic.ttf
Binary files differ
diff --git a/data/fonts/Roboto-Light.ttf b/data/fonts/Roboto-Light.ttf
index d9fb64a..aa45340 100644
--- a/data/fonts/Roboto-Light.ttf
+++ b/data/fonts/Roboto-Light.ttf
Binary files differ
diff --git a/data/fonts/Roboto-LightItalic.ttf b/data/fonts/Roboto-LightItalic.ttf
index 1fd1d31..a85444f 100644
--- a/data/fonts/Roboto-LightItalic.ttf
+++ b/data/fonts/Roboto-LightItalic.ttf
Binary files differ
diff --git a/data/fonts/Roboto-Medium.ttf b/data/fonts/Roboto-Medium.ttf
index c63c115..a3c1a1f 100644
--- a/data/fonts/Roboto-Medium.ttf
+++ b/data/fonts/Roboto-Medium.ttf
Binary files differ
diff --git a/data/fonts/Roboto-MediumItalic.ttf b/data/fonts/Roboto-MediumItalic.ttf
index cd7c835..a30aa0c 100644
--- a/data/fonts/Roboto-MediumItalic.ttf
+++ b/data/fonts/Roboto-MediumItalic.ttf
Binary files differ
diff --git a/data/fonts/Roboto-Regular.ttf b/data/fonts/Roboto-Regular.ttf
index 9cb4a5a..0e58508 100644
--- a/data/fonts/Roboto-Regular.ttf
+++ b/data/fonts/Roboto-Regular.ttf
Binary files differ
diff --git a/data/fonts/Roboto-Thin.ttf b/data/fonts/Roboto-Thin.ttf
index f02f100..8779333 100644
--- a/data/fonts/Roboto-Thin.ttf
+++ b/data/fonts/Roboto-Thin.ttf
Binary files differ
diff --git a/data/fonts/Roboto-ThinItalic.ttf b/data/fonts/Roboto-ThinItalic.ttf
index 12a2ce0..b79cb26 100644
--- a/data/fonts/Roboto-ThinItalic.ttf
+++ b/data/fonts/Roboto-ThinItalic.ttf
Binary files differ
diff --git a/data/fonts/RobotoCondensed-Bold.ttf b/data/fonts/RobotoCondensed-Bold.ttf
index 1079af6..3e06c7c 100644
--- a/data/fonts/RobotoCondensed-Bold.ttf
+++ b/data/fonts/RobotoCondensed-Bold.ttf
Binary files differ
diff --git a/data/fonts/RobotoCondensed-BoldItalic.ttf b/data/fonts/RobotoCondensed-BoldItalic.ttf
index e7f13c2..aaf9fe0 100644
--- a/data/fonts/RobotoCondensed-BoldItalic.ttf
+++ b/data/fonts/RobotoCondensed-BoldItalic.ttf
Binary files differ
diff --git a/data/fonts/RobotoCondensed-Italic.ttf b/data/fonts/RobotoCondensed-Italic.ttf
index 7fa0448..d2b611f 100644
--- a/data/fonts/RobotoCondensed-Italic.ttf
+++ b/data/fonts/RobotoCondensed-Italic.ttf
Binary files differ
diff --git a/data/fonts/RobotoCondensed-Light.ttf b/data/fonts/RobotoCondensed-Light.ttf
index 96b75dd..d4eb198 100644
--- a/data/fonts/RobotoCondensed-Light.ttf
+++ b/data/fonts/RobotoCondensed-Light.ttf
Binary files differ
diff --git a/data/fonts/RobotoCondensed-LightItalic.ttf b/data/fonts/RobotoCondensed-LightItalic.ttf
index 7a2c164..a08f3f4 100644
--- a/data/fonts/RobotoCondensed-LightItalic.ttf
+++ b/data/fonts/RobotoCondensed-LightItalic.ttf
Binary files differ
diff --git a/data/fonts/RobotoCondensed-Regular.ttf b/data/fonts/RobotoCondensed-Regular.ttf
index 734cc40..b9fc49c 100644
--- a/data/fonts/RobotoCondensed-Regular.ttf
+++ b/data/fonts/RobotoCondensed-Regular.ttf
Binary files differ
diff --git a/data/keyboards/Vendor_2378_Product_1008.kl b/data/keyboards/Vendor_2378_Product_1008.kl
new file mode 100644
index 0000000..478da03
--- /dev/null
+++ b/data/keyboards/Vendor_2378_Product_1008.kl
@@ -0,0 +1,35 @@
+# 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.
+
+# OnLive, Inc. OnLive Wireless Controller, USB adapter
+
+key 304 BUTTON_A
+key 305 BUTTON_B
+key 307 BUTTON_X
+key 308 BUTTON_Y
+key 310 BUTTON_L1
+key 311 BUTTON_R1
+key 315 BUTTON_START
+key 314 BUTTON_SELECT
+key 317 BUTTON_THUMBL
+key 318 BUTTON_THUMBR
+
+axis 0x00 X
+axis 0x01 Y
+axis 0x03 Z
+axis 0x04 RZ
+axis 0x05 RTRIGGER
+axis 0x02 LTRIGGER
+axis 0x10 HAT_X
+axis 0x11 HAT_Y
diff --git a/docs/html/distribute/engage/app-updates.jd b/docs/html/distribute/engage/app-updates.jd
index 6b751b9..2b7cd2c 100644
--- a/docs/html/distribute/engage/app-updates.jd
+++ b/docs/html/distribute/engage/app-updates.jd
@@ -36,12 +36,12 @@
 "18x6," data-maxresults="6">
 </div>
 
-<div class="headerLine clearfloat">
-  <h1 id="related-resources">
+<div class="headerLine">
+  <h2 id="related-resources">
     Related Resources
-  </h1>
+  </h2>
 
-  <hr>
+
 </div>
 
 <div class="resource-widget resource-flow-layout col-13" data-query=
diff --git a/docs/html/distribute/engage/community.jd b/docs/html/distribute/engage/community.jd
index 035058a..e202d54 100644
--- a/docs/html/distribute/engage/community.jd
+++ b/docs/html/distribute/engage/community.jd
@@ -29,12 +29,14 @@
   Learn more about how to <a href="{@docRoot}distribute/users/build-community.html">build and manage a community</a>.
 </p>
 
-<div class="headerLine clearfloat">
-  <h1 id="related-resources">
+<p style="clear:both">
+</p>
+<div class="headerLine">
+  <h2 id="related-resources">
     Related Resources
-  </h1>
+  </h2>
 
-  <hr>
+
 </div>
 
 <div class="resource-widget resource-flow-layout col-13" data-query=
diff --git a/docs/html/distribute/engage/deep-linking.jd b/docs/html/distribute/engage/deep-linking.jd
index cd62f9d..0417ba1 100644
--- a/docs/html/distribute/engage/deep-linking.jd
+++ b/docs/html/distribute/engage/deep-linking.jd
@@ -11,7 +11,7 @@
 </p>
 
 <div class="headerLine">
-<h1>Deep Linking from Google+ Posts</h1><hr>
+<h2>Deep Linking from Google+ Posts</h2>
 </div>
 
 <p>
@@ -43,8 +43,8 @@
 </div>
 
 
-<div class="headerLine clearfloat">
-<h1>Deep Linking from Google Search &mdash; App Indexing</h1><hr>
+<div class="headerLine">
+<h2>Deep Linking from Google Search &mdash; App Indexing</h2>
 </div>
 
 <p>
@@ -64,12 +64,10 @@
   <img src="{@docRoot}images/gp-listing-4.jpg" style="padding-top:1em;">
 </div>
 
-<div class="headerLine clearfloat">
-  <h1 id="related-resources">
+<div class="headerLine">
+  <h2 id="related-resources">
     Related Resources
-  </h1>
-
-  <hr>
+  </h2>
 </div>
 
 <div class="resource-widget resource-flow-layout col-13" data-query=
diff --git a/docs/html/distribute/engage/easy-signin.jd b/docs/html/distribute/engage/easy-signin.jd
index 92c3ffc..d066181 100644
--- a/docs/html/distribute/engage/easy-signin.jd
+++ b/docs/html/distribute/engage/easy-signin.jd
@@ -37,11 +37,11 @@
 </p>
 
 <div class="headerLine">
-  <h1>
+  <h2>
     And Spreading the Word a Snap
-  </h1>
+  </h2>
 
-  <hr>
+
 </div>
 
 
@@ -85,12 +85,14 @@
   </li>
 </ul>
 
-  <div class="headerLine clearfloat">
-    <h1 id="related-resources">
+<p style="clear:both">
+</p>
+  <div class="headerLine">
+    <h2 id="related-resources">
       Related Resources
-    </h1>
+    </h2>
 
-    <hr>
+
   </div>
 
   <div class="resource-widget resource-flow-layout col-13"
diff --git a/docs/html/distribute/engage/game-services.jd b/docs/html/distribute/engage/game-services.jd
index 5153435..1c77d2d 100644
--- a/docs/html/distribute/engage/game-services.jd
+++ b/docs/html/distribute/engage/game-services.jd
@@ -75,12 +75,12 @@
   Game Developer Best Practices</a>.
 </p>
 
-<div class="headerLine clearfloat">
-  <h1 id="related-resources">
+<div class="headerLine">
+  <h2 id="related-resources">
     Related Resources
-  </h1>
+  </h2>
 
-  <hr>
+
 </div>
 
 <div class="resource-widget resource-flow-layout col-13" data-query=
diff --git a/docs/html/distribute/engage/gcm.jd b/docs/html/distribute/engage/gcm.jd
index d793124e..7d9b6bb 100644
--- a/docs/html/distribute/engage/gcm.jd
+++ b/docs/html/distribute/engage/gcm.jd
@@ -35,12 +35,12 @@
   free and there are no quotas.
 </p>
 
-<div class="headerLine clearfloat">
-  <h1 id="related-resources">
+<div class="headerLine">
+  <h2 id="related-resources">
     Related Resources
-  </h1>
+  </h2>
 
-  <hr>
+
 </div>
 
 <div class="resource-widget resource-flow-layout col-13" 
diff --git a/docs/html/distribute/engage/notifications.jd b/docs/html/distribute/engage/notifications.jd
index fecfb45..1aa0637 100644
--- a/docs/html/distribute/engage/notifications.jd
+++ b/docs/html/distribute/engage/notifications.jd
@@ -40,17 +40,15 @@
 </p>
 
 
-  <div class="sidebox" style="width:326px;float:left;margin-left:0">
           <p><strong>Tip:</strong>
        Use notifications sparingly &mdash; be sure any information presented is
       useful. Give users the option to turn notifications off.
     </p>
-  </div>
 
-  <div class="headerLine clearfloat">
-  <h1 id="related-resources">
+  <div class="headerLine">
+  <h2 id="related-resources">
     Related Resources
-  </h1><hr>
+  </h2>
 </div>
 
 <div class="resource-widget resource-flow-layout col-13" 
diff --git a/docs/html/distribute/engage/video.jd b/docs/html/distribute/engage/video.jd
index 1a30f3a..c5a4997 100644
--- a/docs/html/distribute/engage/video.jd
+++ b/docs/html/distribute/engage/video.jd
@@ -23,12 +23,12 @@
   <img src="{@docRoot}images/gp-engage-smule.jpg">
 </div>
 
-<div class="headerLine clearfloat">
-  <h1 id="related-resources">
+<div class="headerLine">
+  <h2 id="related-resources">
     Related Resources
-  </h1>
+  </h2>
 
-  <hr>
+
 </div>
 
 <div class="resource-widget resource-flow-layout col-13" data-query=
diff --git a/docs/html/distribute/engage/widgets.jd b/docs/html/distribute/engage/widgets.jd
index b17af08..286adea 100644
--- a/docs/html/distribute/engage/widgets.jd
+++ b/docs/html/distribute/engage/widgets.jd
@@ -28,10 +28,13 @@
   or upcoming deadlines. Widgets should serve as more than a launcher icon.</p>
   </div>
 
-<div class="headerLine clearfloat">
-  <h1 id="related-resources">
+<p style="clear:both">
+</p>
+
+<div class="headerLine">
+  <h2 id="related-resources">
     Related Resources
-  </h1><hr>
+  </h2>
 </div>
 
 <div class="resource-widget resource-flow-layout col-13" 
diff --git a/docs/html/distribute/essentials/best-practices/apps.jd b/docs/html/distribute/essentials/best-practices/apps.jd
index 055a349..bbac727 100644
--- a/docs/html/distribute/essentials/best-practices/apps.jd
+++ b/docs/html/distribute/essentials/best-practices/apps.jd
@@ -17,7 +17,7 @@
 <p>The following best practices have enabled developers worldwide to build great, successful apps for Google Play.</p>
 
 <div class="headerLine">
-<h1 id="essentials">Get the Essentials Right</h1><hr>
+<h2 id="essentials">Get the Essentials Right</h2>
 </div>
 
 <h3>1. Make it Android</h3>
@@ -82,11 +82,11 @@
 </ul>
 
 <div class="headerLine">
-  <h1 id="users">
+  <h2 id="users">
   Get Users
-  </h1>
+  </h2>
 
-  <hr>
+
 </div>
 
 <h3>
@@ -150,11 +150,11 @@
 </ul>
 
 <div class="headerLine">
-  <h1 id="engage">
+  <h2 id="engage">
   Engage and Retain
-  </h1>
+  </h2>
 
-  <hr>
+
 </div>
 
 <h3>
@@ -211,11 +211,11 @@
 </ul>
 
 <div class="headerLine">
-  <h1 id="beyond">
+  <h2 id="beyond">
   Beyond the Basics
-  </h1>
+  </h2>
 
-  <hr>
+
 </div>
 
 <ul>
@@ -249,7 +249,7 @@
 </ul>
 
 <div class="headerLine">
-<h1 id="related-resources">Related Resources</h1><hr>
+<h2 id="related-resources">Related Resources</h2>
 </div>
 
 <div class="resource-widget resource-flow-layout col-13"
diff --git a/docs/html/distribute/essentials/best-practices/games.jd b/docs/html/distribute/essentials/best-practices/games.jd
index ac1df44..c4ce66e 100644
--- a/docs/html/distribute/essentials/best-practices/games.jd
+++ b/docs/html/distribute/essentials/best-practices/games.jd
@@ -20,11 +20,11 @@
 </p>
 
 <div class="headerLine">
-  <h1 id="users">
+  <h2 id="users">
   Get Users
-  </h1>
+  </h2>
 
-  <hr>
+
 </div>
 
 <h3>
@@ -111,11 +111,11 @@
 </ul>
 
 <div class="headerLine">
-  <h1 id="engage">
+  <h2 id="engage">
   Engage and Retain
-  </h1>
+  </h2>
 
-  <hr>
+
 </div>
 
 <h3>
@@ -213,11 +213,11 @@
 </ul>
 
 <div class="headerLine">
-  <h1 id="beyond">
+  <h2 id="beyond">
   Beyond the Basics
-  </h1>
+  </h2>
 
-  <hr>
+
 </div>
 
 <ul>
@@ -249,7 +249,7 @@
 </ul>
 
 <div class="headerLine">
-<h1 id="related-resources">Related Resources</h1><hr>
+<h2 id="related-resources">Related Resources</h2>
 </div>
 
 <div class="resource-widget resource-flow-layout col-13"
diff --git a/docs/html/distribute/essentials/gpfe-guidelines.jd b/docs/html/distribute/essentials/gpfe-guidelines.jd
index 799009f..734bddc 100644
--- a/docs/html/distribute/essentials/gpfe-guidelines.jd
+++ b/docs/html/distribute/essentials/gpfe-guidelines.jd
@@ -50,12 +50,12 @@
   Distribution Agreement</a>.
 </p>
 
-<div class="headerLine clearfloat">
-  <h1 id="basic-reqts">
+<div class="headerLine">
+  <h2 id="basic-reqts">
     Basic Requirements
-  </h1>
+  </h2>
 
-  <hr>
+
 </div>
 
 <p>
@@ -108,11 +108,11 @@
 </ul>
 
 <div class="headerLine">
-  <h1 id="monetizing-ads">
+  <h2 id="monetizing-ads">
     Monetizing and Ads
-  </h1>
+  </h2>
 
-  <hr>
+
 </div>
 
 <p>
@@ -195,11 +195,11 @@
 </ul>
 
 <div class="headerLine">
-  <h1 id="e-value">
+  <h2 id="e-value">
     Educational Value
-  </h1>
+  </h2>
 
-  <hr>
+
 </div>
 
 <div class="figure">
@@ -299,11 +299,11 @@
 </p>
 
 <div class="headerLine">
-  <h1 id="quality">
+  <h2 id="quality">
     App Quality
-  </h1>
+  </h2>
 
-  <hr>
+
 </div>
 
 <div class="figure">
@@ -410,11 +410,11 @@
 </ul>
 
 <div class="headerLine">
-  <h1 id="test-environment">
+  <h2 id="test-environment">
     Test Environment
-  </h1>
+  </h2>
 
-  <hr>
+
 </div>
 
 <p>
@@ -487,7 +487,7 @@
 </ul>
 
 <div class="headerLine">
-<h1>Related Resources</h1><hr>
+<h2>Related Resources</h2>
 </div>
 
 <div class="dynamic-grid">
diff --git a/docs/html/distribute/essentials/optimizing-your-app.jd b/docs/html/distribute/essentials/optimizing-your-app.jd
index 3fe91b28..696ef53 100644
--- a/docs/html/distribute/essentials/optimizing-your-app.jd
+++ b/docs/html/distribute/essentials/optimizing-your-app.jd
@@ -53,11 +53,11 @@
 </p>
 
 <div class="headerLine">
-  <h1 id="listen-to-your-users">
+  <h2 id="listen-to-your-users">
     Listen to Your Users
-  </h1>
+  </h2>
 
-  <hr>
+
 </div>
 
 <div class="figure">
@@ -147,11 +147,11 @@
 </p>
 
 <div class="headerLine" id="measuring-analyzing-responding">
-  <h1>
+  <h2>
     Measuring, Analyzing, and Responding to User Behavior
-  </h1>
+  </h2>
 
-  <hr>
+
 </div>
 
 <div class="figure">
@@ -260,11 +260,11 @@
 </p>
 
 <div class="headerLine">
-  <h1 id="improve-stability">
+  <h2 id="improve-stability">
     Improve Stability and Eliminate Bugs
-  </h1>
+  </h2>
 
-  <hr>
+
 </div>
 
 <p>
@@ -298,11 +298,11 @@
 </p>
 
 <div class="headerLine">
-  <h1 id="improve-ui">
+  <h2 id="improve-ui">
     Improve UI Responsiveness
-  </h1>
+  </h2>
 
-  <hr>
+
 </div>
 
 <div class="figure">
@@ -352,11 +352,11 @@
 </p>
 
 <div class="headerLine">
-  <h1 id="improve-usability">
+  <h2 id="improve-usability">
     Improve Usability
-  </h1>
+  </h2>
 
-  <hr>
+
 </div>
 
 <div class="sidebox-wrapper" style="float:right;">
@@ -403,11 +403,11 @@
 </p>
 
 <div class="headerLine">
-  <h1 id="professional-appearance">
+  <h2 id="professional-appearance">
     Professional Appearance and Aesthetics
-  </h1>
+  </h2>
 
-  <hr>
+
 </div>
 
 <p>
@@ -439,11 +439,11 @@
 </p>
 
 <div class="headerLine">
-  <h1 id="deliver-features">
+  <h2 id="deliver-features">
     Deliver the Right Set of Features
-  </h1>
+  </h2>
 
-  <hr>
+
 </div>
 
 <p>
@@ -464,11 +464,11 @@
 </p>
 
 <div class="headerLine">
-  <h1 id="integrate">
+  <h2 id="integrate">
     Integrate with the System and Third-Party apps
-  </h1>
+  </h2>
 
-  <hr>
+
 </div>
 
 <p>
@@ -502,7 +502,7 @@
 </p>
 
 <div class="headerLine">
-<h1 id="related-resources">Related Resources</h1><hr>
+<h2 id="related-resources">Related Resources</h2>
 </div>
 <div class="resource-widget resource-flow-layout col-13"
   data-query="collection:distribute/essentials/optimizing, tag:addia"
diff --git a/docs/html/distribute/essentials/quality/core.jd b/docs/html/distribute/essentials/quality/core.jd
index 558b030..cfe1a2a 100644
--- a/docs/html/distribute/essentials/quality/core.jd
+++ b/docs/html/distribute/essentials/quality/core.jd
@@ -63,12 +63,12 @@
   Guidelines</a>.
 </p>
 
-<div class="headerLine clearfloat">
-  <h1 id="ux">
+<div class="headerLine">
+  <h2 id="ux">
   Visual Design and User Interaction
-  </h1>
+  </h2>
 
-  <hr>
+
 </div>
 
 <p>
@@ -234,9 +234,7 @@
   </tr>
 </table>
 
-<h3>
-  Related Resources
-</h3>
+<h3 class="rel-resources clearfloat">Related resources</h3>
 
 <div class="resource-widget resource-flow-layout col-13" data-query=
 "collection:distribute/essentials/corequalityguidelines/visualdesign"
@@ -244,12 +242,12 @@
 data-maxresults="6">
 </div>
 
-<div class="headerLine clearfloat">
-  <h1 id="fn">
+<div class="headerLine">
+  <h2 id="fn">
   Functionality
-  </h1>
+  </h2>
 
-  <hr>
+
 </div>
 
 <p>
@@ -509,21 +507,19 @@
   </tr>
 </table>
 
-<h3>
-  Related Resources
-</h3>
+<h3 class="rel-resources clearfloat">Related resources</h3>
 
 <div class="resource-widget resource-flow-layout col-13" data-query=
 "collection:distribute/essentials/corequalityguidelines/functionality"
 data-sortorder="-timestamp" data-cardsizes="6x3" data-maxresults="6">
 </div>
 
-<div class="headerLine clearfloat">
-  <h1 id="ps">
+<div class="headerLine">
+  <h2 id="ps">
   Performance and Stability
-  </h1>
+  </h2>
 
-  <hr>
+
 </div>
 
 <p>
@@ -670,21 +666,19 @@
   </tr>
 </table>
 
-<h3>
-  Related Resources
-</h3>
+<h3 class="rel-resources clearfloat">Related resources</h3>
 
 <div class="resource-widget resource-flow-layout col-13" data-query=
 "collection:distribute/essentials/core/performance" data-sortorder="-timestamp"
 data-cardsizes="6x3" data-maxresults="6">
 </div>
 
-<div class="headerLine clearfloat">
-  <h1 id="listing">
+<div class="headerLine">
+  <h2 id="listing">
   Google Play
-  </h1>
+  </h2>
 
-  <hr>
+
 </div>
 
 <p>
@@ -819,21 +813,19 @@
   </tr>
 </table>
 
-<h3>
-  Related Resources
-</h3>
+<h3 class="rel-resources clearfloat">Related resources</h3>
 
 <div class="resource-widget resource-flow-layout col-13" data-query=
 "collection:distribute/essentials/core/play" data-sortorder="-timestamp"
 data-cardsizes="6x3,6x3,6x3,6x3,6x3,6x3" data-maxresults="6">
 </div>
 
-<div class="headerLine clearfloat">
-  <h1 id="test-environment">
+<div class="headerLine">
+  <h2 id="test-environment">
   Setting Up a Test Environment
-  </h1>
+  </h2>
 
-  <hr>
+
 </div>
 
 <p>
@@ -863,12 +855,12 @@
   increase the number or complexity of tests and quality criteria.
 </p>
 
-<div class="headerLine clearfloat">
-  <h1 id="tests">
+<div class="headerLine">
+  <h2 id="tests">
   Test Procedures
-  </h1>
+  </h2>
 
-  <hr>
+
 </div>
 
 <p>
diff --git a/docs/html/distribute/essentials/quality/tablets.jd b/docs/html/distribute/essentials/quality/tablets.jd
index 7dfab48..2b2a5ae 100644
--- a/docs/html/distribute/essentials/quality/tablets.jd
+++ b/docs/html/distribute/essentials/quality/tablets.jd
@@ -52,7 +52,7 @@
   help you address each recommendation included.
 </p>
 
-<div class="headerLine"><h1 id="core-app-quality">1. Test for Basic Tablet App Quality</h1><hr></div>
+<div class="headerLine"><h2 id="core-app-quality">1. Test for Basic Tablet App Quality</h2></div>
 
 <p>The first step in delivering a great tablet app experience is making sure
 that it meets the <em>core app quality criteria</em> for all of the devices
@@ -78,8 +78,8 @@
   Tips page</a>.</p>
 
 
-<div class="headerLine clearfloat">
-<h1 id="optimize-layouts">2. Optimize Layouts for Larger Screens</h1><hr></div>
+<div class="headerLine">
+<h2 id="optimize-layouts">2. Optimize Layouts for Larger Screens</h2></div>
 
 <p>
   Android makes it easy to develop an app that runs well on a wide range of
@@ -158,7 +158,7 @@
 multi-pane UI for tablets (see next section).</li>
 </ul>
 
-<h3>Related resources</h3>
+<h3 class="rel-resources clearfloat">Related resources</h3>
 
 <div class="resource-widget resource-flow-layout col-13"
   data-query="collection:distribute/essentials/tabletguidelines/optimize"
@@ -167,7 +167,7 @@
   data-maxResults="6"></div>
 
 
-<div class="headerLine clearfloat"><h1 id="use-extra-space">3. Take Advantage of Extra Screen Area</h1><hr></div>
+<div class="headerLine"><h2 id="use-extra-space">3. Take Advantage of Extra Screen Area</h2></div>
 
 <div style="width:340px;float:right;margin:1.5em;margin-bottom:0;margin-top:0;">
 <img src="{@docRoot}images/training/app-navigation-multiple-sizes-multipane-good.png"
@@ -219,7 +219,7 @@
 <code>sw600dp</code>/<code>sw720</code>).</li>
 </ul>
 
-<h3>Related resources</h3>
+<h3 class="rel-resources clearfloat">Related resources</h3>
 
 <div class="resource-widget resource-flow-layout col-13"
   data-query="collection:distribute/essentials/tabletguidelines/extrascreen"
@@ -227,7 +227,7 @@
   data-cardSizes="6x3,6x3,6x3"
   data-maxResults="6"></div>
 
-<div class="headerLine clearfloat"><h1 id="use-tablet-icons">4. Use Assets Designed for Tablet Screens</h1><hr></div>
+<div class="headerLine"><h2 id="use-tablet-icons">4. Use Assets Designed for Tablet Screens</h2></div>
 
 <div><img src="{@docRoot}design/media/devices_displays_density@2x.png"></div>
 
@@ -308,7 +308,7 @@
 it will request the {@code xxhdpi} version of the launcher icon.</li>
 </ul>
 
-<h3>Related resources</h3>
+<h3 class="rel-resources clearfloat">Related resources</h3>
 
 <div class="resource-widget resource-flow-layout col-13"
   data-query="collection:distribute/essentials/tabletguidelines/assets"
@@ -316,8 +316,8 @@
   data-cardSizes="9x3"
   data-maxResults="6"></div>
 
-<div class="headerLine clearfloat"><h1 id="adjust-font-sizes">5.
-Adjust Font Sizes and Touch Targets</h1><hr></div>
+<div class="headerLine"><h2 id="adjust-font-sizes">5.
+Adjust Font Sizes and Touch Targets</h2></div>
 
 <p>To make sure your app is easy to use on tablets, take some time to adjust the
 font sizes and touch targets in your tablet UI, for all of the screen
@@ -345,7 +345,7 @@
 or just centering the icon within the transparent button.</li>
 </ul>
 
-<h3>Related resources</h3>
+<h3 class="rel-resources clearfloat">Related resources</h3>
 
 <div class="resource-widget resource-flow-layout col-13"
   data-query="collection:distribute/essentials/tabletguidelines/fonts"
@@ -353,7 +353,7 @@
   data-cardSizes="9x3,9x3,6x3,6x3,6x3"
   data-maxResults="6"></div>
 
-<div class="headerLine clearfloat"><h1 id="adjust-widgets">6. Adjust Sizes of Home Screen Widgets</h1><hr></div>
+<div class="headerLine"><h2 id="adjust-widgets">6. Adjust Sizes of Home Screen Widgets</h2></div>
 
 <p>If your app includes a home screen widget, here are a few points to consider
 to ensure a great user experience on tablet screens: </p>
@@ -371,7 +371,7 @@
 possible.</li>
 </ul>
 
-<h3>Related resources</h3>
+<h3 class="rel-resources clearfloat">Related resources</h3>
 
 <div class="resource-widget resource-flow-layout col-13"
   data-query="collection:distribute/essentials/tabletguidelines/widgets"
@@ -380,7 +380,7 @@
   data-maxResults="6"></div>
 
 
-<div class="headerLine clearfloat"><h1 id="offer-full-feature-set">7. Full Feature Set for Tablet Users</h1><hr></div>
+<div class="headerLine"><h2 id="offer-full-feature-set">7. Full Feature Set for Tablet Users</h2></div>
 
 <div class="centered-full-image" style="width:600px;margin:1.5em"><img src="{@docRoot}images/gp-tablets-full-feature-set.png" alt="Tablet feature sets"></div>
 
@@ -415,7 +415,7 @@
   </li>
 </ul>
 
-<div class="headerLine clearfloat"><h1 id="android-versions">8. Target Android Versions Properly</h1><hr></div>
+<div class="headerLine"><h2 id="android-versions">8. Target Android Versions Properly</h2></div>
 
 <p>
   To ensure the broadest possible distribution to tablets, make sure that your
@@ -458,7 +458,7 @@
   </li>
 </ol>
 
-<h3>Related resources</h3>
+<h3 class="rel-resources clearfloat">Related resources</h3>
 
 <div class="resource-widget resource-flow-layout col-13"
   data-query="collection:distribute/essentials/tabletguidelines/versions"
@@ -466,7 +466,7 @@
   data-cardSizes="6x3"
   data-maxResults="6"></div>
 
-<div class="headerLine clearfloat"><h1 id="hardware-requirements">9. Declare Hardware Feature Dependencies Properly</h1><hr></div>
+<div class="headerLine"><h2 id="hardware-requirements">9. Declare Hardware Feature Dependencies Properly</h2></div>
 
 <p>
   Handsets and tablets typically offer slightly different hardware support for
@@ -528,7 +528,7 @@
   as needed.
 </p>
 
-<h3>Related resources</h3>
+<h3 class="rel-resources clearfloat">Related resources</h3>
 
 <div class="resource-widget resource-flow-layout col-13"
   data-query="collection:distribute/essentials/tabletguidelines/hardware"
@@ -536,7 +536,7 @@
   data-cardSizes="9x3"
   data-maxResults="6"></div>
 
-<div class="headerLine clearfloat"><h1 id="support-screens">10. Declare Support for Tablet Screens</h1><hr></div>
+<div class="headerLine"><h2 id="support-screens">10. Declare Support for Tablet Screens</h2></div>
 
 <p>To ensure that you can distribute your app to a broad range of tablets, your app should
 declare support for tablet screen sizes in its manifest file, as follows:</p>
@@ -560,7 +560,7 @@
 <a href="{@docRoot}guide/topics/manifest/compatible-screens-element.html"><code>&lt;compatible-screens&gt;</code></a>
 element in your app.</p>
 
-<h3>Related resources</h3>
+<h3 class="rel-resources clearfloat">Related resources</h3>
 
 <div class="resource-widget resource-flow-layout col-13"
   data-query="collection:distribute/essentials/tabletguidelines/tabletscreens"
@@ -569,7 +569,7 @@
   data-maxResults="6"></div>
 
 
-<div class="headerLine clearfloat"><h1 id="google-play">11. Showcase Your Tablet UI in Google Play</h1><hr></div>
+<div class="headerLine"><h2 id="google-play">11. Showcase Your Tablet UI in Google Play</h2></div>
 
 <p>
   After you've done the work to create an rich, optimized UI for your tablet
@@ -689,7 +689,7 @@
   </li>
 </ul>
 
-<h3>Related resources</h3>
+<h3 class="rel-resources clearfloat">Related resources</h3>
 
 <div class="resource-widget resource-flow-layout col-13"
   data-query="collection:distribute/essentials/tabletguidelines/showcase"
@@ -697,12 +697,12 @@
   data-cardSizes="9x3,9x3,9x3,9x3"
   data-maxResults="6"></div>
 
-<div class="headerLine clearfloat">
-  <h1 id="google-play-best-practices">
+<div class="headerLine">
+  <h2 id="google-play-best-practices">
     12. Follow Best Practices for Publishing in Google Play
-  </h1>
+  </h2>
 
-  <hr>
+
 </div>
 
 <p>
@@ -799,7 +799,7 @@
   recommended.
 </p>
 
-<h3 class="clearfloat">Related resources</h3>
+<h3 class="rel-resources clearfloat">Related resources</h3>
 <div class="resource-widget resource-flow-layout col-13"
   data-query="collection:distribute/essentials/tabletguidelines/googleplay"
   data-sortOrder="-timestamp"
@@ -807,12 +807,12 @@
   data-maxResults="6"></div>
 
 
-<div class="headerLine clearfloat">
-  <h1 id="test-environment">
+<div class="headerLine">
+  <h2 id="test-environment">
     Setting Up a Test Environment for Tablets
-  </h1>
+  </h2>
 
-  <hr>
+
 </div>
 
 <p>
@@ -858,7 +858,7 @@
 </tr>
 </table>
 
-<div class="headerLine clearfloat"><h1 id="related-resources">Related Resources</h1><hr></div>
+<div class="headerLine"><h2 id="related-resources">Related Resources</h2></div>
 
 <div class="resource-widget resource-flow-layout col-13"
   data-query="collection:distribute/essentials/tabletguidelines"
diff --git a/docs/html/distribute/googleplay/about.jd b/docs/html/distribute/googleplay/about.jd
index cf0c6d2..c7c91ac 100644
--- a/docs/html/distribute/googleplay/about.jd
+++ b/docs/html/distribute/googleplay/about.jd
@@ -58,11 +58,11 @@
 </p>
 
 <div class="headerLine">
-  <h1 id="ratings-reviews">
+  <h2 id="ratings-reviews">
     User Ratings and Reviews
-  </h1>
+  </h2>
 
-  <hr>
+
 </div>
 
 <p>
@@ -83,11 +83,11 @@
 </div>
 
 <div class="headerLine">
-  <h1 id="category-browsing">
+  <h2 id="category-browsing">
     Category Browsing
-  </h1>
+  </h2>
 
-  <hr>
+
 </div>
 
 <p>
@@ -98,11 +98,11 @@
 </p>
 
 <div class="headerLine">
-  <h1 id="search">
+  <h2 id="search">
     Search
-  </h1>
+  </h2>
 
-  <hr>
+
 </div>
 
 <p>
@@ -113,11 +113,11 @@
 </p>
 
 <div class="headerLine">
-  <h1 id="top-charts-and-lists">
+  <h2 id="top-charts-and-lists">
     Top Charts and Lists
-  </h1>
+  </h2>
 
-  <hr>
+
 </div>
 
 <div class="figure">
@@ -195,11 +195,11 @@
 </table>
 
 <div class="headerLine">
-  <h1 id="featured-staff-picks">
+  <h2 id="featured-staff-picks">
     Featured, Staff Picks, Collections, and Badges
-  </h1>
+  </h2>
 
-  <hr>
+
 </div>
 
 <p>
@@ -313,11 +313,11 @@
 </p>
 
 <div class="headerLine">
-  <h1 id="product-detail-pages">
+  <h2 id="product-detail-pages">
     Store Listing Pages
-  </h1>
+  </h2>
 
-  <hr>
+
 </div>
 
 <div class="figure">
@@ -357,8 +357,11 @@
   Products</a> to find out how.
 </p>
 
-<div class="headerLine clearfloat">
-<h1>Related Resources</h1><hr>
+<p style="clear:both">
+</p>
+
+<div class="headerLine">
+<h2>Related Resources</h2>
 </div>
 
 <div class="resource-widget resource-flow-layout col-13"
diff --git a/docs/html/distribute/googleplay/developer-console.jd b/docs/html/distribute/googleplay/developer-console.jd
index 6263431..f5b3ac6 100644
--- a/docs/html/distribute/googleplay/developer-console.jd
+++ b/docs/html/distribute/googleplay/developer-console.jd
@@ -44,12 +44,12 @@
   verification by email, you can sign in to your Google Play Developer Console.
 </p>
 
-<div class="headerLine clearfloat">
-  <h1 id="allapps">
+<div class="headerLine">
+  <h2 id="allapps">
     All Applications
-  </h1>
+  </h2>
 
-  <hr>
+
 </div>
 
 <p>
@@ -61,12 +61,12 @@
   <img src="{@docRoot}images/gp-dc-home.png" class="border-img">
 </div>
 
-<div class="headerLine clearfloat" style="margin-top:-6px">
-  <h1 id="account-details">
+<div class="headerLine" style="margin-top:-6px">
+  <h2 id="account-details">
     Your Account Details
-  </h1>
+  </h2>
 
-  <hr>
+
 </div>
 
 <p>
@@ -112,12 +112,12 @@
   Google Play licensing.
 </p>
 
-<div class="headerLine clearfloat">
-  <h1 id="merchant-account">
+<div class="headerLine">
+  <h2 id="merchant-account">
     Linking Your Merchant Account
-  </h1>
+  </h2>
 
-  <hr>
+
 </div>
 
 <p>
@@ -127,12 +127,12 @@
   from sales.
 </p>
 
-<div class="headerLine clearfloat">
-  <h1 id="multiple-user-accounts">
+<div class="headerLine">
+  <h2 id="multiple-user-accounts">
     Multiple User Accounts
-  </h1>
+  </h2>
 
-  <hr>
+
 </div>
 
 <p>
@@ -157,12 +157,12 @@
   up multiple accounts</a> now.
 </p>
 
-<div class="headerLine clearfloat">
-  <h1 id="store-listing-details">
+<div class="headerLine">
+  <h2 id="store-listing-details">
     Store Listing Details
-  </h1>
+  </h2>
 
-  <hr>
+
 </div>
 
 <p>
@@ -181,12 +181,12 @@
   <img src="{@docRoot}images/gp-dc-details.png" class="frame">
 </div>
 
-<div class="headerLine clearfloat">
-  <h1 id="upload-instantly-publish">
+<div class="headerLine">
+  <h2 id="upload-instantly-publish">
     Upload and Instantly Publish
-  </h1>
+  </h2>
 
-  <hr>
+
 </div>
 
 <p>
@@ -212,12 +212,12 @@
   time.
 </p>
 
-<div class="headerLine clearfloat">
-  <h1 id="alpha-beta">
+<div class="headerLine">
+  <h2 id="alpha-beta">
     Alpha and Beta Testing
-  </h1>
+  </h2>
 
-  <hr>
+
 </div>
 
 <p>
@@ -270,12 +270,12 @@
   Checklist</a>.
 </p>
 
-<div class="headerLine clearfloat">
-  <h1 id="staged-rollouts">
+<div class="headerLine">
+  <h2 id="staged-rollouts">
     Staged Rollouts
-  </h1>
+  </h2>
 
-  <hr>
+
 </div>
 
 <p>
@@ -303,12 +303,12 @@
   updates.
 </p>
 
-<div class="headerLine clearfloat">
-  <h1 id="multiple-apk">
+<div class="headerLine">
+  <h2 id="multiple-apk">
     Multiple APK Support
-  </h1>
+  </h2>
 
-  <hr>
+
 </div>
 
 <p>
@@ -335,12 +335,12 @@
   of the normal app installation.
 </p>
 
-<div class="headerLine clearfloat">
-  <h1 id="selling-pricing-your-products">
+<div class="headerLine">
+  <h2 id="selling-pricing-your-products">
     Selling and Pricing Your Products
-  </h1>
+  </h2>
 
-  <hr>
+
 </div>
 
 <div class="figure-right">
@@ -407,12 +407,12 @@
   Expand into New Markets</a>.
 </p>
 
-<div class="headerLine clearfloat">
-  <h1 id="in-app-products">
+<div class="headerLine">
+  <h2 id="in-app-products">
     In-app Products
-  </h1>
+  </h2>
 
-  <hr>
+
 </div>
 
 <p>
@@ -448,12 +448,12 @@
   monetization models
 </p>
 
-<div class="headerLine clearfloat">
-  <h1 id="distribution-controls">
+<div class="headerLine">
+  <h2 id="distribution-controls">
     Distribution Controls
-  </h1>
+  </h2>
 
-  <hr>
+
 </div>
 
 <p>
@@ -518,12 +518,12 @@
   exclude specific devices if needed.
 </p>
 
-<div class="headerLine clearfloat">
-  <h1 id="reviews-reports">
+<div class="headerLine">
+  <h2 id="reviews-reports">
     User Reviews and Crash Reports
-  </h1>
+  </h2>
 
-  <hr>
+
 </div>
 
 <div class="figure-right" style="width:500px;">
@@ -548,12 +548,12 @@
   devices.
 </p>
 
-<div class="headerLine clearfloat">
-  <h1 id="app-stats">
+<div class="headerLine">
+  <h2 id="app-stats">
     App Statistics
-  </h1>
+  </h2>
 
-  <hr>
+
 </div>
 
 <div class="figure" style="width:500px">
@@ -587,9 +587,12 @@
   on data inside a dimension by adding specific points to the timeline.
 </p>
 
+<p style="clear:both">
+</p>
+
 <div class="dynamic-grid">
-<div class="headerLine clearfloat">
-<h1 id="related-resources">Related Resources</h1><hr/>
+<div class="headerLine">
+<h2 id="related-resources">Related Resources</h2>
 </div>
 
 <div class="resource-widget resource-flow-layout col-13"
diff --git a/docs/html/distribute/googleplay/edu/about.jd b/docs/html/distribute/googleplay/edu/about.jd
index 3944909..e73356e 100644
--- a/docs/html/distribute/googleplay/edu/about.jd
+++ b/docs/html/distribute/googleplay/edu/about.jd
@@ -106,8 +106,10 @@
   </div>
 </div>
 
-<div class="headerLine clearfloat">
-<h1>Related Resources</h1><hr>
+<p style="clear:both">
+</p>
+<div class="headerLine">
+<h2 id="related-resources">Related Resources</h2>
 </div>
 
 <div class="dynamic-grid">
diff --git a/docs/html/distribute/googleplay/edu/faq.jd b/docs/html/distribute/googleplay/edu/faq.jd
index 0866da5..36e2064 100644
--- a/docs/html/distribute/googleplay/edu/faq.jd
+++ b/docs/html/distribute/googleplay/edu/faq.jd
@@ -58,11 +58,11 @@
 </p>
 
 <div class="headerLine">
-  <h1 id="business-model-and-monetization">
+  <h2 id="business-model-and-monetization">
   Business Model and Monetization
-  </h1>
+  </h2>
 
-  <hr>
+
 </div>
 
 <p>
@@ -143,11 +143,11 @@
 </p>
 
 <div class="headerLine">
-  <h1 id="free-trials">
+  <h2 id="free-trials">
   Free Trials
-  </h1>
+  </h2>
 
-  <hr>
+
 </div>
 
 <p>
@@ -172,11 +172,11 @@
 </p>
 
 <div class="headerLine">
-  <h1 id="discovery">
+  <h2 id="discovery">
   Discovery
-  </h1>
+  </h2>
 
-  <hr>
+
 </div>
 
 <p>
@@ -211,11 +211,11 @@
 </p>
 
 <div class="headerLine">
-  <h1 id="app-review-process">
+  <h2 id="app-review-process">
   App Review Process
-  </h1>
+  </h2>
 
-  <hr>
+
 </div>
 
 <p>
@@ -255,11 +255,11 @@
 </p>
 
 <div class="headerLine">
-  <h1 id="app-features">
+  <h2 id="app-features">
   App Features
-  </h1>
+  </h2>
 
-  <hr>
+
 </div>
 
 <p>
@@ -310,11 +310,11 @@
 </p>
 
 <div class="headerLine">
-  <h1 id="marketing-and-roi">
+  <h2 id="marketing-and-roi">
   Marketing and ROI
-  </h1>
+  </h2>
 
-  <hr>
+
 </div>
 
 <p>
@@ -364,11 +364,11 @@
 </p>
 
 <div class="headerLine">
-  <h1 id="devices">
+  <h2 id="devices">
   Devices
-  </h1>
+  </h2>
 
-  <hr>
+
 </div>
 
 <p>
@@ -393,11 +393,11 @@
 </p>
 
 <div class="headerLine">
-  <h1 id="accounts">
+  <h2 id="accounts">
   Accounts
-  </h1>
+  </h2>
 
-  <hr>
+
 </div>
 
 <p>
@@ -423,7 +423,7 @@
   schools in the program use Google Accounts and Google Apps for Education,
   offering Google Single Sign-on is ideal.
 </p>
-<div class="headerLine"><h1 id="related-resources">Related Resources</h1><hr></div>
+<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"
diff --git a/docs/html/distribute/googleplay/edu/start.jd b/docs/html/distribute/googleplay/edu/start.jd
index 260ae85..4886b5a 100644
--- a/docs/html/distribute/googleplay/edu/start.jd
+++ b/docs/html/distribute/googleplay/edu/start.jd
@@ -32,12 +32,12 @@
   "border:1px solid #ddd;padding:0px;width:100%;">
 </div>
 
-<div class="headerLine clearfloat">
-  <h1 id="register">
+<div class="headerLine">
+  <h2 id="register">
     Register for a Publisher Account
-  </h1>
+  </h2>
 
-  <hr>
+
 </div>
 
 <p>
@@ -49,11 +49,11 @@
 </p>
 
 <div class="headerLine">
-  <h1 id="prepare">
+  <h2 id="prepare">
     Prepare Your Apps
-  </h1>
+  </h2>
 
-  <hr>
+
 </div>
 
 <div class="figure-right">
@@ -138,11 +138,11 @@
 </p>
 
 <div class="headerLine">
-  <h1 id="publish">
+  <h2 id="publish">
     Publish Your Apps
-  </h1>
+  </h2>
 
-  <hr>
+
 </div>
 
 <p>
@@ -298,7 +298,7 @@
   </li>
 </ul>
 <div class="headerLine">
-<h1 id="related-resources">Related Resources</h1><hr>
+<h2 id="related-resources">Related Resources</h2>
 </div>
 
 <div class="dynamic-grid">
diff --git a/docs/html/distribute/googleplay/start.jd b/docs/html/distribute/googleplay/start.jd
index 6dc397b..2ba5880 100644
--- a/docs/html/distribute/googleplay/start.jd
+++ b/docs/html/distribute/googleplay/start.jd
@@ -33,11 +33,11 @@
 </p>
 
 <div class="headerLine">
-  <h1>
+  <h2>
     Register for a Publisher Account
-  </h1>
+  </h2>
 
-  <hr>
+
 </div>
 
 <div class="sidebox-wrapper" style="float:right;">
@@ -92,11 +92,11 @@
 </ol>
 
 <div class="headerLine">
-  <h1 id="merchant-account">
+  <h2 id="merchant-account">
     Set Up a Google Wallet Merchant Account
-  </h1>
+  </h2>
 
-  <hr>
+
 </div>
 
 <div class="figure" style="width:200px;">
@@ -135,11 +135,11 @@
 </p>
 
 <div class="headerLine">
-  <h1>
+  <h2>
     Explore the Developer Console
-  </h1>
+  </h2>
 
-  <hr>
+
 </div>
 
 <p>
@@ -153,7 +153,7 @@
 </div>
 
 <div class="headerLine">
-<h1 id="related-resources">Related Resources</h1><hr />
+<h2 id="related-resources">Related Resources</h2><hr />
 </div>
 
 <div class="resource-widget resource-flow-layout col-13"
diff --git a/docs/html/distribute/monetize/ads.jd b/docs/html/distribute/monetize/ads.jd
index 40120c3..bcb1e52 100644
--- a/docs/html/distribute/monetize/ads.jd
+++ b/docs/html/distribute/monetize/ads.jd
@@ -104,7 +104,7 @@
   DoubleClick for Publishers Small Business</a>.
 </p>
 
-<div class="headerLine"><h1 id="related-resources">Related resources</h1><hr></div>
+<div class="headerLine"><h2 id="related-resources">Related resources</h2></div>
 
 <div class="resource-widget resource-flow-layout col-13"
   data-query="collection:distribute/monetize/advertising"
diff --git a/docs/html/distribute/monetize/ecommerce.jd b/docs/html/distribute/monetize/ecommerce.jd
index b3f790d..65e2b20 100644
--- a/docs/html/distribute/monetize/ecommerce.jd
+++ b/docs/html/distribute/monetize/ecommerce.jd
@@ -44,12 +44,14 @@
   Account</a>.
 </p>
 
-<div class="headerLine clearfloat">
-  <h1 id="related-resources">
+<p style="clear:both">
+</p>
+<div class="headerLine">
+  <h2 id="related-resources">
     Related Resources
-  </h1>
+  </h2>
 
-  <hr>
+
 </div>
 <div class="resource-widget resource-flow-layout col-13"
   data-query="collection:distribute/monetize/ecommerce"
diff --git a/docs/html/distribute/monetize/freemium.jd b/docs/html/distribute/monetize/freemium.jd
index ec86d19..0d33054 100644
--- a/docs/html/distribute/monetize/freemium.jd
+++ b/docs/html/distribute/monetize/freemium.jd
@@ -66,11 +66,11 @@
 </p>
 
 <div class="headerLine">
-  <h1 id="related-resources">
+  <h2 id="related-resources">
   Related Resources
-  </h1>
+  </h2>
 
-  <hr>
+
 </div>
 
 <div class="resource-widget resource-flow-layout col-13"
diff --git a/docs/html/distribute/monetize/payments.jd b/docs/html/distribute/monetize/payments.jd
index 37b4d44..55c289f 100644
--- a/docs/html/distribute/monetize/payments.jd
+++ b/docs/html/distribute/monetize/payments.jd
@@ -17,11 +17,11 @@
 </p>
 
 <div class="headerLine">
-  <h1 id="dcb">
+  <h2 id="dcb">
   Direct Carrier Billing
-  </h1>
+  </h2>
 
-  <hr>
+
 </div>
 
 <p>
@@ -33,10 +33,10 @@
 </p>
 
 <div class="headerLine">
-  <h1 id="credit">
+  <h2 id="credit">
   Credit Cards
-  </h1>
-  <hr>
+  </h2>
+
 </div>
 
 <p>
@@ -47,12 +47,12 @@
   setup process.
 </p>
 
-<div class="headerLine clearfloat">
-  <h1 id="gift-cards">
+<div class="headerLine">
+  <h2 id="gift-cards">
   Google Play Gift Cards
-  </h1>
+  </h2>
 
-  <hr>
+
 </div>
 
 <div class="figure">
@@ -67,12 +67,14 @@
   "_android">here</a>.
 </p>
 
-<div class="headerLine clearfloat">
-  <h1 id="balance">
+<p style="clear:both">
+</p>
+<div class="headerLine">
+  <h2 id="balance">
   Google Play Balance
-  </h1>
+  </h2>
 
-  <hr>
+
 </div>
 
 <div class="figure">
@@ -94,7 +96,9 @@
   network, and other factors.
 </p>
  
-<div class="headerLine clearfloat"><h1 id="related-resources">Related Resources</h1><hr></div>
+<p style="clear:both">
+</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/monetize/paymentmethods"
diff --git a/docs/html/distribute/monetize/premium.jd b/docs/html/distribute/monetize/premium.jd
index b66cd03..c8823d1 100644
--- a/docs/html/distribute/monetize/premium.jd
+++ b/docs/html/distribute/monetize/premium.jd
@@ -35,11 +35,13 @@
   <a href="{@docRoot}distribute/monetize/ads.html">advertising</a> models.
 </p>
 
-<div class="headerLine clearfloat">
-  <h1>
+<p style="clear:both">
+</p>
+<div class="headerLine">
+  <h2>
   Related Resources
-  </h1>
-  <hr>
+  </h2>
+
 </div>
 
 <div class="resource-widget resource-flow-layout col-13"
diff --git a/docs/html/distribute/monetize/subscriptions.jd b/docs/html/distribute/monetize/subscriptions.jd
index a838e30..6a4d9b1 100644
--- a/docs/html/distribute/monetize/subscriptions.jd
+++ b/docs/html/distribute/monetize/subscriptions.jd
@@ -61,7 +61,9 @@
   </p>
 </div>
 
-<div class="headerLine clearfloat"><h1 id="related-resources">Related Resources</h1><hr></div>
+<p style="clear:both">
+</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/monetize/subscriptions"
diff --git a/docs/html/distribute/tools/launch-checklist.jd b/docs/html/distribute/tools/launch-checklist.jd
index 3b0dd55..f310800 100644
--- a/docs/html/distribute/tools/launch-checklist.jd
+++ b/docs/html/distribute/tools/launch-checklist.jd
@@ -62,11 +62,11 @@
 </p>
 
 <div class="headerLine">
-  <h1 id="understand-publishing">
+  <h2 id="understand-publishing">
     1. Understand the Publishing Process
-  </h1>
+  </h2>
 
-  <hr>
+
 </div>
 
 <p>
@@ -85,9 +85,7 @@
   Play.
 </p>
 
-<h3>
-  Related resources
-</h3>
+<h3 class="rel-resources clearfloat">Related resources</h3>
 
 <div class="resource-widget resource-flow-layout col-13" data-query=
 "collection:distribute/toolsreference/launchchecklist/understanding"
@@ -95,12 +93,12 @@
 data-maxresults="6">
 </div>
 
-<div class="headerLine clearfloat">
-  <h1 id="understand-policies">
+<div class="headerLine">
+  <h2 id="understand-policies">
     2. Understand Google Play Policies and Agreements
-  </h1>
+  </h2>
 
-  <hr>
+
 </div>
 
 <p>
@@ -110,21 +108,19 @@
   repeated violations, termination of your developer account.
 </p>
 
-<h3>
-  Related resources
-</h3>
+<h3 class="rel-resources clearfloat">Related resources</h3>
 
 <div class="resource-widget resource-flow-layout col-13" data-query=
 "collection:distribute/toolsreference/launchchecklist/policies" data-sortorder=
 "-timestamp" data-cardsizes="6x3" data-maxresults="6">
 </div>
 
-<div class="headerLine clearfloat">
-  <h1 id="test-quality">
+<div class="headerLine">
+  <h2 id="test-quality">
     3. Test for Quality
-  </h1>
+  </h2>
 
-  <hr>
+
 </div>
 
 <p>
@@ -154,21 +150,19 @@
   should exhibit.
 </p>
 
-<h3>
-  Related resources
-</h3>
+<h3 class="rel-resources clearfloat">Related resources</h3>
 
 <div class="resource-widget resource-flow-layout col-13" data-query=
 "collection:distribute/toolsreference/launchchecklist/quality" data-sortorder=
 "-timestamp" data-cardsizes="6x3,6x3,6x3,9x3,9x3,9x3" data-maxresults="6">
 </div>
 
-<div class="headerLine clearfloat">
-  <h1 id="determine-rating">
+<div class="headerLine">
+  <h2 id="determine-rating">
     4. Determine your App’s Content Rating
-  </h1>
+  </h2>
 
-  <hr>
+
 </div>
 
 <p>
@@ -212,21 +206,19 @@
   no changes are required in your app binary.
 </p>
 
-<h3>
-  Related resources
-</h3>
+<h3 class="rel-resources clearfloat">Related resources</h3>
 
 <div class="resource-widget resource-flow-layout col-13" data-query=
 "collection:distribute/toolsreference/launchchecklist/rating" data-sortorder=
 "-timestamp" data-cardsizes="9x3,6x3,6x3,9x3,9x3,9x3" data-maxresults="6">
 </div>
 
-<div class="headerLine clearfloat">
-  <h1 id="determine-country">
+<div class="headerLine">
+  <h2 id="determine-country">
     5. Determine Country Distribution
-  </h1>
+  </h2>
 
-  <hr>
+
 </div>
 
 <p>
@@ -280,21 +272,19 @@
   Checklist</a> for key steps and considerations in the localization process.
 </p>
 
-<h3>
-  Related resources
-</h3>
+<h3 class="rel-resources clearfloat">Related resources</h3>
 
 <div class="resource-widget resource-flow-layout col-13" data-query=
 "collection:distribute/toolsreference/launchchecklist/country" data-sortorder=
 "-timestamp" data-cardsizes="9x3,9x3,6x3,9x3,9x3,9x3" data-maxresults="6">
 </div>
 
-<div class="headerLine clearfloat">
-  <h1 id="confirm-size">
+<div class="headerLine">
+  <h2 id="confirm-size">
     6. Confirm the App's Overall Size
-  </h1>
+  </h2>
 
-  <hr>
+
 </div>
 
 <p>
@@ -335,21 +325,19 @@
   on your code when building your release-ready APK.
 </p>
 
-<h3>
-  Related resources
-</h3>
+<h3 class="rel-resources clearfloat">Related resources</h3>
 
 <div class="resource-widget resource-flow-layout col-13" data-query=
 "collection:distribute/toolsreference/launchchecklist/size" data-sortorder=
 "-timestamp" data-cardsizes="9x3,9x3,6x3,9x3,9x3,9x3" data-maxresults="6">
 </div>
 
-<div class="headerLine clearfloat">
-  <h1 id="confirm-platform">
+<div class="headerLine">
+  <h2 id="confirm-platform">
     7. Confirm the App's Platform and Screen Compatibility Ranges
-  </h1>
+  </h2>
 
-  <hr>
+
 </div>
 
 <p>
@@ -387,21 +375,19 @@
   <a href="{@docRoot}about/dashboards/index.html">Device Dashboard</a> charts.
 </p>
 
-<h3>
-  Related resources
-</h3>
+<h3 class="rel-resources clearfloat">Related resources</h3>
 
 <div class="resource-widget resource-flow-layout col-13" data-query=
 "collection:distribute/toolsreference/launchchecklist/platform" data-sortorder=
 "-timestamp" data-cardsizes="6x3,6x3,6x3" data-maxresults="6">
 </div>
 
-<div class="headerLine clearfloat">
-  <h1 id="decide-price">
+<div class="headerLine">
+  <h2 id="decide-price">
     8. Decide Whether your App will be Free or Priced
-  </h1>
+  </h2>
 
-  <hr>
+
 </div>
 
 <div class="figure">
@@ -450,21 +436,19 @@
   set up a Google Wallet Merchant Account</a> before you can publish.
 </p>
 
-<h3>
-  Related resources
-</h3>
+<h3 class="rel-resources clearfloat">Related resources</h3>
 
 <div class="resource-widget resource-flow-layout col-13" data-query=
 "collection:distribute/toolsreference/launchchecklist/price" data-sortorder=
 "-timestamp" data-cardsizes="9x3,9x3,6x3,9x3,9x3,9x3" data-maxresults="6">
 </div>
 
-<div class="headerLine clearfloat">
-  <h1 id="consider-billing">
+<div class="headerLine">
+  <h2 id="consider-billing">
     9. Consider using In-app Billing
-  </h1>
+  </h2>
 
-  <hr>
+
 </div>
 
 <p>
@@ -485,9 +469,7 @@
   complete and test your implementation before creating your release-ready APK.
 </p>
 
-<h3>
-  Related resources
-</h3>
+<h3 class="rel-resources clearfloat">Related resources</h3>
 
 <div class="resource-widget resource-flow-layout col-13" data-query=
 "collection:distribute/toolsreference/launchchecklist/purchasemethod"
@@ -495,12 +477,12 @@
 data-maxresults="6">
 </div>
 
-<div class="headerLine clearfloat">
-  <h1 id="set-prices">
+<div class="headerLine">
+  <h2 id="set-prices">
     10. Set Prices for your Products
-  </h1>
+  </h2>
 
-  <hr>
+
 </div>
 
 <p>
@@ -517,21 +499,19 @@
   available currencies through the Developer Console.
 </p>
 
-<h3>
-  Related resources
-</h3>
+<h3 class="rel-resources clearfloat">Related resources</h3>
 
 <div class="resource-widget resource-flow-layout col-13" data-query=
 "collection:distribute/toolsreference/launchchecklist/setprice" data-sortorder=
 "-timestamp" data-cardsizes="9x3,9x3,9x3,9x3,9x3,9x3" data-maxresults="6">
 </div>
 
-<div class="headerLine clearfloat">
-  <h1 id="start-localization">
+<div class="headerLine">
+  <h2 id="start-localization">
     11. Start Localization
-  </h1>
+  </h2>
 
-  <hr>
+
 </div>
 
 <p>
@@ -599,9 +579,7 @@
   listing.
 </p>
 
-<h3>
-  Related resources
-</h3>
+<h3 class="rel-resources clearfloat">Related resources</h3>
 
 <div class="resource-widget resource-flow-layout col-13" data-query=
 "collection:distribute/toolsreference/launchchecklist/localization"
@@ -609,12 +587,12 @@
 data-maxresults="6">
 </div>
 
-<div class="headerLine clearfloat">
-  <h1 id="prepare-graphics">
+<div class="headerLine">
+  <h2 id="prepare-graphics">
     12. Prepare Promotional Graphics, Screenshots, and Videos
-  </h1>
+  </h2>
 
-  <hr>
+
 </div>
 
 <p>
@@ -659,21 +637,19 @@
   publishing date.
 </p>
 
-<h3>
-  Related resources
-</h3>
+<h3 class="rel-resources clearfloat">Related resources</h3>
 
 <div class="resource-widget resource-flow-layout col-13" data-query=
 "collection:distribute/toolsreference/launchchecklist/graphics" data-sortorder=
 "-timestamp" data-cardsizes="9x3,9x3,6x3,9x3,9x3,9x3" data-maxresults="6">
 </div>
 
-<div class="headerLine clearfloat">
-  <h1 id="build-upload">
+<div class="headerLine">
+  <h2 id="build-upload">
     13. Build and Upload the Release-ready APK
-  </h1>
+  </h2>
 
-  <hr>
+
 </div>
 
 <p>
@@ -701,7 +677,7 @@
   Developer Console. If necessary, you can replace an APK with a more recent
   version before publishing.
 </p>
-<!--<h3>Related resources</h3>
+<!--<h3 class="rel-resources clearfloat">Related resources</h3>
 
 <div class="resource-widget resource-flow-layout col-13"
   data-query="collection:distribute/toolsreference/launchchecklist/build"
@@ -709,12 +685,12 @@
   data-cardSizes="9x3,9x3,6x3,9x3,9x3,9x3"
   data-maxResults="6"></div>-->
 
-<div class="headerLine clearfloat">
-  <h1 id="plan-beta">
+<div class="headerLine">
+  <h2 id="plan-beta">
     14. Plan a Beta Release
-  </h1>
+  </h2>
 
-  <hr>
+
 </div>
 
 <div class="sidebox-wrapper" style="float:right;">
@@ -765,11 +741,11 @@
 </table> -->
 
 <div class="headerLine">
-  <h1 id="complete-details">
+  <h2 id="complete-details">
     15. Complete the Apps’ Store Listing
-  </h1>
+  </h2>
 
-  <hr>
+
 </div>
 
 <p>
@@ -815,9 +791,7 @@
   elsewhere.
 </p>
 
-<h3>
-  Related resources
-</h3>
+<h3 class="rel-resources clearfloat">Related resources</h3>
 
 <div class="resource-widget resource-flow-layout col-13" data-query=
 "collection:distribute/toolsreference/launchchecklist/productdetails"
@@ -825,12 +799,12 @@
 data-maxresults="6">
 </div>
 
-<div class="headerLine clearfloat">
-  <h1 id="use-badges">
+<div class="headerLine">
+  <h2 id="use-badges">
     16. Use Google Play Badges and Links in your Promotional Campaigns
-  </h1>
+  </h2>
 
-  <hr>
+
 </div>
 
 <p>
@@ -853,21 +827,19 @@
   available.
 </p>
 
-<h3>
-  Related resources
-</h3>
+<h3 class="rel-resources clearfloat">Related resources</h3>
 
 <div class="resource-widget resource-flow-layout col-13" data-query=
 "collection:distribute/toolsreference/launchchecklist/badges" data-sortorder=
 "-timestamp" data-cardsizes="9x3,9x3,6x3,9x3,9x3,9x3" data-maxresults="6">
 </div>
 
-<div class="headerLine clearfloat">
-  <h1 id="final-checks">
+<div class="headerLine">
+  <h2 id="final-checks">
     17. Final Checks and Publishing
-  </h1>
+  </h2>
 
-  <hr>
+
 </div>
 
 <p>
@@ -959,9 +931,7 @@
   linking from your promotional campaigns.
 </p>
 
-<h3>
-  Related resources
-</h3>
+<h3 class="rel-resources clearfloat">Related resources</h3>
 
 <div class="resource-widget resource-flow-layout col-13" data-query=
 "collection:distribute/toolsreference/launchchecklist/finalchecks"
@@ -969,12 +939,12 @@
 data-maxresults="6">
 </div>
 
-<div class="headerLine clearfloat">
-  <h1 id="support-users">
+<div class="headerLine">
+  <h2 id="support-users">
     18. Support Users after Launch
-  </h1>
+  </h2>
 
-  <hr>
+
 </div>
 
 <p>
@@ -1063,7 +1033,7 @@
 </ul>
 </ul>
 
-<h3>Related resources</h3>
+<h3 class="rel-resources clearfloat">Related resources</h3>
 
 <div class="resource-widget resource-flow-layout col-13"
   data-query="collection:distribute/toolsreference/launchchecklist/afterlaunch"
diff --git a/docs/html/distribute/tools/localization-checklist.jd b/docs/html/distribute/tools/localization-checklist.jd
index 7a638ed..08a8143 100644
--- a/docs/html/distribute/tools/localization-checklist.jd
+++ b/docs/html/distribute/tools/localization-checklist.jd
@@ -41,11 +41,11 @@
 </p>
 
 <div class="headerLine">
-  <h1 id="identify-languages">
+  <h2 id="identify-languages">
     1. Identify target languages and locales
-  </h1>
+  </h2>
 
-  <hr>
+
 </div>
 
 <p>
@@ -84,21 +84,19 @@
   development, translation, testing, and marketing efforts to these markets.
 </p>
 
-<h3 id="related-resources">
-  Related Resources
-</h3>
+<h3 class="rel-resources clearfloat">Related resources</h3>
 
 <div class="resource-widget resource-flow-layout col-13" data-query=
 "collection:distribute/toolsreference/localizationchecklist/identifylocales"
 data-sortorder="-timestamp" data-cardsizes="9x3," data-maxresults="6">
 </div>
 
-<div class="headerLine clearfloat">
-  <h1 id="design">
+<div class="headerLine">
+  <h2 id="design">
     2. Design for localization
-  </h1>
+  </h2>
 
-  <hr>
+
 </div>
 
 <p>
@@ -224,21 +222,19 @@
   directories, without language or locale qualifiers.
 </p>
 
-<h3>
-  Related resources
-</h3>
+<h3 class="rel-resources clearfloat">Related resources</h3>
 
 <div class="resource-widget resource-flow-layout col-13" data-query=
 "collection:distribute/tools/loc/designforloc" data-sortorder="-timestamp"
 data-cardsizes="9x3" data-maxresults="6">
 </div>
 
-<div class="headerLine clearfloat">
-  <h1 id="manage-strings">
+<div class="headerLine">
+  <h2 id="manage-strings">
     3. Manage strings for localization
-  </h1>
+  </h2>
 
-  <hr>
+
 </div>
 
 <p>
@@ -399,21 +395,19 @@
 
 &lt;/resources&gt;
 </pre>
-<h3 class="clearfloat">
-  Related resources
-</h3>
+<h3 class="rel-resources clearfloat">Related resources</h3>
 
 <div class="resource-widget resource-flow-layout col-13" data-query=
 "collection:distribute/toolsreference/localizationchecklist/managestrings"
 data-sortorder="-timestamp" data-cardsizes="9x3" data-maxresults="6">
 </div>
 
-<div class="headerLine clearfloat">
-  <h1 id="translate-strings">
+<div class="headerLine">
+  <h2 id="translate-strings">
     4. Translate UI strings and other resources
-  </h1>
+  </h2>
 
-  <hr>
+
 </div>
 
 <p>
@@ -570,21 +564,19 @@
   <img src="{@docRoot}images/gp-localization-trans-0.png" class="border-img">
 </div>
 
-<h3>
-  Related resources
-</h3>
+<h3 class="rel-resources clearfloat">Related resources</h3>
 
 <div class="resource-widget resource-flow-layout col-13" data-query=
 "collection:distribute/toolsreference/localizationchecklist/translatestrings"
 data-sortorder="-timestamp" data-cardsizes="9x3" data-maxresults="6">
 </div>
 
-<div class="headerLine clearfloat">
-  <h1 id="test">
+<div class="headerLine">
+  <h2 id="test">
     5. Test your localized app
-  </h1>
+  </h2>
 
-  <hr>
+
 </div>
 
 <p>
@@ -690,7 +682,7 @@
   your localized apps. One way to do that is through beta testing with regional
   users &mdash; Google Play can help you do this. <!-- </p>
 
-<h3 class="clearfloat">Related resources</h3>
+<h3 class="rel-resources clearfloat">Related resources</h3>
 
 <div class="resource-widget resource-flow-layout col-13"
   data-query="collection:distribute/toolsreference/localizationchecklist/test"
@@ -699,12 +691,12 @@
   data-maxResults="6"></div> -->
 </p>
 
-<div class="headerLine clearfloat">
-  <h1 id="prepare-launch">
+<div class="headerLine">
+  <h2 id="prepare-launch">
     6. Prepare for international launch
-  </h1>
+  </h2>
 
-  <hr>
+
 </div>
 
 <p>
@@ -887,9 +879,7 @@
   helpful reminders for a successful localized launch.
 </p>
 
-<h3>
-  Related resources
-</h3>
+<h3 class="rel-resources clearfloat">Related resources</h3>
 
 <div class="resource-widget resource-flow-layout col-13" data-query=
 "collection:distribute/toolsreference/localizationchecklist/preplaunch"
@@ -897,12 +887,12 @@
 data-maxresults="6">
 </div>
 
-<div class="headerLine clearfloat">
-  <h1 id="support-users">
+<div class="headerLine">
+  <h2 id="support-users">
     7. Support international users after launch
-  </h1>
+  </h2>
 
-  <hr>
+
 </div>
 
 <p>
@@ -968,7 +958,7 @@
   "{@docRoot}distribute/tools/launch-checklist.html">Launch Checklist</a> to
   learn more about how to plan, build, and launch your app on Google Play.
 </p>
-<h3 class="clearfloat">Related resources</h3>
+<h3 class="rel-resources clearfloat">Related resources</h3>
 
 <div class="resource-widget resource-flow-layout col-13"
   data-query="collection:distribute/toolsreference/localizationchecklist/supportlaunch"
diff --git a/docs/html/distribute/tools/open-distribution.jd b/docs/html/distribute/tools/open-distribution.jd
index f804af2..e28102d 100644
--- a/docs/html/distribute/tools/open-distribution.jd
+++ b/docs/html/distribute/tools/open-distribution.jd
@@ -26,11 +26,11 @@
 </p>
 
 <div class="headerLine">
-  <h1>
+  <h2>
   Distributing Through an App Marketplace
-  </h1>
+  </h2>
 
-  <hr>
+
 </div>
 
 <p>
@@ -55,11 +55,11 @@
 </p>
 
 <div class="headerLine">
-  <h1>
+  <h2>
   Distributing Your Apps by Email
-  </h1>
+  </h2>
 
-  <hr>
+
 </div>
 
 <div class="figure" style="width:300px;">
@@ -95,11 +95,11 @@
 </p>
 
 <div class="headerLine">
-  <h1>
+  <h2>
   Distributing Through a Website
-  </h1>
+  </h2>
 
-  <hr>
+
 </div>
 
 <p>
@@ -119,12 +119,12 @@
   sources</a>.
 </p>
 
-<div class="headerLine clearfloat">
-  <h1>
+<div class="headerLine">
+  <h2>
   User Opt-In for Apps from Unknown Sources
-  </h1>
+  </h2>
 
-  <hr>
+
 </div>
 
 <div class="figure" style="width:325px;">
diff --git a/docs/html/distribute/tools/promote/badge-files.jd b/docs/html/distribute/tools/promote/badge-files.jd
index cce3632..e65e698 100644
--- a/docs/html/distribute/tools/promote/badge-files.jd
+++ b/docs/html/distribute/tools/promote/badge-files.jd
@@ -12,7 +12,7 @@
 <p>The following links provide the Adobe&reg; Illustrator&reg; (.ai) file for the
 two Google Play badges.</p>
 
-<hr>
+
 <img src="{@docRoot}images/brand/en_generic_rgb_wo_60.png" alt="Get It On Google Play">
 
 <div style="clear:left">&nbsp;</div>
@@ -21,9 +21,10 @@
 
        <a href="{@docRoot}downloads/brand/v2/english_get.ai">English (English)</a><br/>
 
+       <a href="{@docRoot}downloads/brand/af_generic_rgb_wo.ai">Afrikaans (Afrikaans)</a><br/>
+
        <a href="{@docRoot}downloads/brand/v2/amharic_get.ai">ኣማርኛ (Amharic)</a><br/>
 
-       <a href="{@docRoot}downloads/brand/af_generic_rgb_wo.ai">Afrikaans (Afrikaans)</a><br/>
 <!--
        <a href="{@docRoot}downloads/brand/ar_generic_rgb_wo.ai">العربية (Arabic)</a><br/>
 -->
@@ -137,7 +138,7 @@
 
 
 
-<hr>
+
 <img src="{@docRoot}images/brand/en_app_rgb_wo_60.png" alt="Android App On Google Play">
 
 <div style="clear:left">&nbsp;</div>
@@ -286,6 +287,3 @@
 <p>To quickly create a badge that links to your apps on Google Play,
 use the <a
 href="{@docRoot}distribute/tools/promote/badges.html">Googe Play badge generator</a>.</p>
-    
-    
-    
\ No newline at end of file
diff --git a/docs/html/distribute/tools/promote/device-art.jd b/docs/html/distribute/tools/promote/device-art.jd
index b0b5f84..a204ea1 100644
--- a/docs/html/distribute/tools/promote/device-art.jd
+++ b/docs/html/distribute/tools/promote/device-art.jd
@@ -12,7 +12,7 @@
 <p class="note"><strong>Note</strong>: Do <em>not</em> use graphics created here in your 1024x500
 feature image or screenshots for your Google Play app listing.</p>
 
-<hr>
+
 
 <div class="supported-browser">
 
@@ -28,7 +28,7 @@
   </div>
 </div>
 
-<hr>
+
 
 <div class="layout-content-row">
   <div class="layout-content-col span-3">
diff --git a/docs/html/distribute/users/build-buzz.jd b/docs/html/distribute/users/build-buzz.jd
index b76498e..412589f 100644
--- a/docs/html/distribute/users/build-buzz.jd
+++ b/docs/html/distribute/users/build-buzz.jd
@@ -65,11 +65,11 @@
 </p>
 
 <div class="headerLine">
-  <h1 id="link-to-your-apps">
+  <h2 id="link-to-your-apps">
     Link to Your Apps in Google Play
-  </h1>
+  </h2>
 
-  <hr>
+
 </div>
 
 <p>
@@ -101,12 +101,12 @@
   </li>
 </ul>
 
-<div class="headerLine clearfloat">
-  <h1 id="use-the-google-play-badge">
+<div class="headerLine">
+  <h2 id="use-the-google-play-badge">
     Use the Google Play Badge
-  </h1>
+  </h2>
 
-  <hr>
+
 </div>
 
 <div class="figure" style="margin:0 3em;">
@@ -126,12 +126,12 @@
   also easy to make and available in multiple languages.
 </p>
 
-<div class="headerLine clearfloat">
-  <h1 id="cross-promote-from-your-other-apps">
+<div class="headerLine">
+  <h2 id="cross-promote-from-your-other-apps">
     Cross-Promote from Your Other Apps
-  </h1>
+  </h2>
 
-  <hr>
+
 </div>
 
 <div class="figure-right">
@@ -158,11 +158,11 @@
 </p>
 
 <div class="headerLine">
-  <h1 id="hold-a-contest">
+  <h2 id="hold-a-contest">
     Hold a Contest
-  </h1>
+  </h2>
 
-  <hr>
+
 </div>
 
 <p>
@@ -180,11 +180,11 @@
 </p>
 
 <div class="headerLine">
-  <h1 id="leverage-pr">
+  <h2 id="leverage-pr">
     Leverage PR
-  </h1>
+  </h2>
 
-  <hr>
+
 </div>
 
 <p>
@@ -196,11 +196,11 @@
 </p>
 
 <div class="headerLine">
-  <h1 id="use-social-media">
+  <h2 id="use-social-media">
     Use Social Media
-  </h1>
+  </h2>
 
-  <hr>
+
 </div>
 
 <p>
@@ -220,12 +220,12 @@
   review your apps. A review on the right blog is a great promotion.
 </p>
 
-<div class="headerLine clearfloat">
-  <h1 id="publish-youtube-videos">
+<div class="headerLine">
+  <h2 id="publish-youtube-videos">
     Publish YouTube Videos
-  </h1>
+  </h2>
 
-  <hr>
+
 </div>
 
 <div class="center-img" style="padding-top:1em;">
@@ -240,11 +240,11 @@
 </p>
 
 <div class="headerLine">
-  <h1 id="advertise">
+  <h2 id="advertise">
     Advertise
-  </h1>
+  </h2>
 
-  <hr>
+
 </div>
 
 <div class="figure">
@@ -262,12 +262,12 @@
   Sign up for an AdMob account</a> to get started.
 </p>
 
-<div class="headerLine clearfloat">
-  <h1 id="maximize-your-marketing-spend">
+<div class="headerLine">
+  <h2 id="maximize-your-marketing-spend">
     Maximize your Marketing Spend
-  </h1>
+  </h2>
 
-  <hr>
+
 </div>
 
 <div class="figure" style="margin: 0 3em;">
@@ -285,12 +285,12 @@
   platforms simultaneously helps you maximize your return on investment.
 </p>
 
-<div class="headerLine clearfloat">
-  <h1 id="related-resources">
+<div class="headerLine">
+  <h2 id="related-resources">
     Related Resources
-  </h1>
+  </h2>
 
-  <hr>
+
 </div>
 
 <div class="resource-widget resource-flow-layout col-13"
diff --git a/docs/html/distribute/users/build-community.jd b/docs/html/distribute/users/build-community.jd
index 1623939..5cdabea 100644
--- a/docs/html/distribute/users/build-community.jd
+++ b/docs/html/distribute/users/build-community.jd
@@ -43,11 +43,11 @@
 </p>
 
 <div class="headerLine">
-  <h1 id="starting-your-community">
+  <h2 id="starting-your-community">
   Starting Your Community
-  </h1>
+  </h2>
 
-  <hr>
+
 </div>
 
 <p>
@@ -116,11 +116,11 @@
 </p>
 
 <div class="headerLine">
-  <h1 id="tools-to-build-your-community">
+  <h2 id="tools-to-build-your-community">
   Tools to Build Your Community
-  </h1>
+  </h2>
 
-  <hr>
+
 </div>
 
 <p>
@@ -158,11 +158,11 @@
 </p>
 
 <div class="headerLine">
-  <h1 id="managing-your-community">
+  <h2 id="managing-your-community">
   Managing Your Community
-  </h1>
+  </h2>
 
-  <hr>
+
 </div>
 
 <div class="figure">
@@ -189,11 +189,11 @@
   versions or new apps to make them feel special.
 </p>
 
-<div class="headerLine clearfloat">
-  <h1 id="related-resources">
+<div class="headerLine">
+  <h2 id="related-resources">
   Related Resources
-  </h1>
-  <hr>
+  </h2>
+
 </div>
 <div class="resource-widget resource-flow-layout col-13"
   data-query="collection:distribute/users/buildcommunity"
diff --git a/docs/html/distribute/users/expand-to-new-markets.jd b/docs/html/distribute/users/expand-to-new-markets.jd
index cc94a92..df1a0fb 100644
--- a/docs/html/distribute/users/expand-to-new-markets.jd
+++ b/docs/html/distribute/users/expand-to-new-markets.jd
@@ -77,11 +77,11 @@
 </p>
 
 <div class="headerLine">
-  <h1 id="localize-your-product">
+  <h2 id="localize-your-product">
     Localize Your Product
-  </h1>
+  </h2>
 
-  <hr>
+
 </div>
 
 <div class="sidebox-wrapper" style="float:right;">
@@ -124,20 +124,16 @@
   high-quality professional translations at competitive prices.
 </p>
 
-<div style="float:left; width:48%; padding:8px;">
-  <img src="{@docRoot}images/gp-listing-3.jpg">
-</div>
+<img src="{@docRoot}images/gp-listing-3.jpg" style="padding:8px 0">
 
-<div style="width:48%; padding:8px; float:left">
-  <img src="{@docRoot}images/gp-expand-2.jpg">
-</div>
+<img src="{@docRoot}images/gp-expand-2.jpg" style="padding:8px 0">
 
-<div class="headerLine clearfloat">
-  <h1 id="testing-and-support">
+<div class="headerLine">
+  <h2 id="testing-and-support">
     Testing and Support
-  </h1>
+  </h2>
 
-  <hr>
+
 </div>
 
 <p>
@@ -162,11 +158,11 @@
 </p>
 
 <div class="headerLine">
-  <h1 id="localize-your-google-play-listing">
+  <h2 id="localize-your-google-play-listing">
     Localize Your Google Play Store Listing
-  </h1>
+  </h2>
 
-  <hr>
+
 </div>
 
 <div class="sidebox-wrapper" style="float:right;">
@@ -295,11 +291,11 @@
 </p>
 
 <div class="headerLine">
-  <h1 id="marketing">
+  <h2 id="marketing">
     Marketing
-  </h1>
+  </h2>
 
-  <hr>
+
 </div>
 
 <div class="figure">
@@ -316,7 +312,7 @@
   for each language you support.
 </p>
 
-<div class="headerLine clearfloat"><h1 id="related-resources">Related Resources</h1><hr></div>
+<div class="headerLine"><h2 id="related-resources">Related Resources</h2></div>
 
 <div class="resource-widget resource-flow-layout col-13"
   data-query="collection:distribute/getusers/expandnewmarkets"
diff --git a/docs/html/distribute/users/know-your-user.jd b/docs/html/distribute/users/know-your-user.jd
index fb91a05..1fbcb9c 100644
--- a/docs/html/distribute/users/know-your-user.jd
+++ b/docs/html/distribute/users/know-your-user.jd
@@ -15,13 +15,9 @@
   when they use your app, and how often they return to it.
 </p>
 
-<div class="headerLine">
-  <h1 id="read-ratings-comments">
+  <h2 id="read-ratings-comments">
   Read Ratings Comments
-  </h1>
-
-  <hr>
-</div>
+  </h2>
 
 <p>
   The most obvious way to get to know your users is by reading review comments
@@ -46,11 +42,11 @@
 </div>
 
 <div class="headerLine">
-  <h1 id="start-community">
+  <h2 id="start-community">
   Start a Community
-  </h1>
+  </h2>
 
-  <hr>
+
 </div>
 
 <p>
@@ -68,11 +64,11 @@
 </p>
 
 <div class="headerLine">
-  <h1 id="create-survey">
+  <h2 id="create-survey">
   Create a Survey
-  </h1>
+  </h2>
 
-  <hr>
+
 </div>
 
 <p>
@@ -92,11 +88,11 @@
 </p>
 
 <div class="headerLine">
-  <h1 id="add-analytics">
+  <h2 id="add-analytics">
   Add Analytics to your Apps
-  </h1>
+  </h2>
 
-  <hr>
+
 </div>
 
 <p>
@@ -121,11 +117,11 @@
 </div>
 
 <div class="headerLine">
-  <h1 id="use-google">
+  <h2 id="use-google">
   Use Google+
-  </h1>
+  </h2>
 
-  <hr>
+
 </div>
 
 <p>
@@ -142,8 +138,8 @@
   <img src="{@docRoot}images/gp-your-user-2.jpg">
 </div>
 
-<div class="headerLine clearfloat">
-<h1 id="related-resources">Related Resources</h1><hr>
+<div class="headerLine">
+<h2 id="related-resources">Related Resources</h2>
 </div>
 
 <div class="resource-widget resource-flow-layout col-13"
diff --git a/docs/html/distribute/users/your-listing.jd b/docs/html/distribute/users/your-listing.jd
index cc72fff..f869950 100644
--- a/docs/html/distribute/users/your-listing.jd
+++ b/docs/html/distribute/users/your-listing.jd
@@ -13,11 +13,11 @@
 </p>
 
 <div class="headerLine">
-  <h1 id="graphics-imagery">
+  <h2 id="graphics-imagery">
     Graphics &amp; Imagery Tips
-  </h1>
+  </h2>
 
-  <hr>
+
 </div>
 
 <p>
@@ -94,12 +94,12 @@
   Featured-Image Guidelines</a>.
 </p>
 
-<div class="headerLine clearfloat">
-  <h1 id="localization-tips">
+<div class="headerLine">
+  <h2 id="localization-tips">
     Localization Tips
-  </h1>
+  </h2>
 
-  <hr>
+
 </div>
 
 <div class="sidebox-wrapper" style="float:right;">
@@ -125,16 +125,14 @@
   the growing international audience</a>.
 </p>
 
-<div style="float:left;">
   <img src="{@docRoot}images/gp-listing-3.jpg">
-</div>
 
-<div class="headerLine clearfloat">
-  <h1 id="keyword-tips">
+<div class="headerLine">
+  <h2 id="keyword-tips">
     Keyword Tips
-  </h1>
+  </h2>
 
-  <hr>
+
 </div>
 
 <p>
@@ -146,11 +144,11 @@
 </p>
 
 <div class="headerLine">
-  <h1 id="app-indexing">
+  <h2 id="app-indexing">
     Sign Up for App Indexing
-  </h1>
+  </h2>
 
-  <hr>
+
 </div>
 
 <p>
@@ -164,11 +162,11 @@
 </div>
 
 <div class="headerLine">
-  <h1 id="education-app">
+  <h2 id="education-app">
     Education App Tips
-  </h1>
+  </h2>
 
-  <hr>
+
 </div>
 
 <p>
@@ -181,7 +179,7 @@
 </p>
 
 <div class="headerLine">
-<h1 id="related-resources">Related Resources</h1><hr>
+<h2 id="related-resources">Related Resources</h2>
 </div>
 
 <div class="resource-widget resource-flow-layout col-13"
diff --git a/docs/html/google/play/billing/billing_admin.jd b/docs/html/google/play/billing/billing_admin.jd
index 46ad905..5904b03 100644
--- a/docs/html/google/play/billing/billing_admin.jd
+++ b/docs/html/google/play/billing/billing_admin.jd
@@ -66,7 +66,8 @@
 </p>
 </div>
 
-<p>You can create a product list for any published application or any draft application that's been
+<p>You can create a product list for any published application, or any
+application in the alpha or beta channels, that's been
 uploaded and saved to the Developer Console. However, you must have a Google Wallet merchant
 account and the application's manifest must include the <code>com.android.vending.BILLING</code>
 permission. If an application's manifest does not include this permission, you will be able to edit
@@ -75,6 +76,13 @@
 <a href="{@docRoot}google/play/billing/billing_integrate.html#billing-permission">Updating Your
 Application's Manifest</a>.</p>
 
+<p class="note"><strong>Note:</strong> Previously you could test an app by
+uploading an unpublished "draft" version. This functionality is no longer
+supported; instead, you must publish it to the alpha or beta distribution
+channel. For more information, see <a
+href="{@docRoot}google/play/billing/billing_testing.html#draft_apps">Draft Apps
+are No Longer Supported</a>.
+
 <p>In addition, an application package can have only one product list. If you create a product
 list for an application, and you use the <a
 href="{@docRoot}google/play/publishing/multiple-apks.html">multiple APK feature</a> to distribute
diff --git a/docs/html/google/play/billing/billing_testing.jd b/docs/html/google/play/billing/billing_testing.jd
index df6c657..8a49433 100644
--- a/docs/html/google/play/billing/billing_testing.jd
+++ b/docs/html/google/play/billing/billing_testing.jd
@@ -8,8 +8,9 @@
   <h2>In this document</h2>
   <ol>
     <li><a href="#testing-purchases">Testing In-app Purchases</a></li>
-        <li><a href="#billing-testing-static">Testing with static responses</a></li>
+    <li><a href="#billing-testing-static">Testing with Static Responses</a></li>
     <li><a href="#billing-testing-real">Setting Up for Test Purchases</a></li>
+    <li><a href="#draft_apps">Draft Apps are No Longer Supported</a></li>
   </ol>
   <h2>See also</h2>
   <ol>
@@ -79,8 +80,7 @@
 <p>First, upload and publish in-app products that you want testers to be able to
 purchase. You can upload and publish in-app products in the Developer Console. 
 Note that you can upload and publish your in-app items before you publish the
-APK itself. For example, you can publish your in-app items while your APK is
-still a draft. </p>
+APK itself.</p>
 
 <p>Next, create license test accounts for authorized users. In the Developer
 Console, go to <strong>Settings</strong> &gt; <strong>Account details</strong>,
@@ -149,11 +149,12 @@
 only be able to make test purchases. </p>
 
 
-<h2 id="billing-testing-static">Testing with static responses</h2>
+<h2 id="billing-testing-static">Testing with Static Responses</h2>
 
 <p>We recommend that you first test your In-app Billing implementation using static responses from
 Google Play. This enables you to verify that your application is handling the primary Google
-Play responses correctly and that your application is able to verify signatures correctly.</p>
+Play responses correctly and that your application is able to verify signatures correctly. You can do this
+even if the app hasn't been published yet.</p>
 
 <p>To test your implementation with static responses, you make an In-app Billing request using a
 special item that has a reserved product ID. Each reserved product ID returns a specific static
@@ -173,6 +174,12 @@
 install your application on a device, log into the device, and make billing requests using the
 reserved product IDs.</p>
 
+<p class="note"><strong>Note:</strong> Previously you could test an app by
+uploading an unpublished "draft" version. This functionality is no longer
+supported. However, you can test your app with static responses even before you
+upload it to the Google Play store. For more information, see <a
+href="#draft_apps">Draft Apps are No Longer Supported</a>.
+
 <p>There are four reserved product IDs for testing static In-app Billing responses:</p>
 
 <ul>
@@ -205,67 +212,12 @@
   </li>
 </ul>
 
-<p>In some cases, the reserved items may return signed static responses, which lets you test
-signature verification in your application. To test signature verification with the special reserved
-product IDs, you may need to set up <a
-href="{@docRoot}google/play/billing/billing_admin.html#billing-testing-setup">test accounts</a> or
-upload your application as a unpublished draft application. Table 1 shows you the conditions under
-which static responses are signed.</p>
-
-<p class="table-caption" id="static-responses-table"><strong>Table 1.</strong>
-Conditions under which static responses are signed.</p>
-
-<table>
-<tr>
-<th>Application ever been published?</th>
-<th>Draft application uploaded and unpublished?</th>
-<th>User who is running the application</th>
-<th>Static response signature</th>
-</tr>
-
-<tr>
-<td>No</td>
-<td>No</td>
-<td>Any</td>
-<td>Unsigned</td>
-</tr>
-
-<tr>
-<td>No</td>
-<td>No</td>
-<td>Developer</td>
-<td>Signed</td>
-</tr>
-
-<tr>
-<td>Yes</td>
-<td>No</td>
-<td>Any</td>
-<td>Unsigned</td>
-</tr>
-
-<tr>
-<td>Yes</td>
-<td>No</td>
-<td>Developer</td>
-<td>Signed</td>
-</tr>
-
-<tr>
-<td>Yes</td>
-<td>No</td>
-<td>Test account</td>
-<td>Signed</td>
-</tr>
-
-<tr>
-<td>Yes</td>
-<td>Yes</td>
-<td>Any</td>
-<td>Signed</td>
-</tr>
-
-</table>
+<p>In some cases, the reserved items may return signed static responses, which
+lets you test signature verification in your application. The reserved items
+only return signed responses if the user running the application has a <a
+href="{@docRoot}distribute/googleplay/start.html">developer</a> or <a
+href="{@docRoot}google/play/billing/billing_admin.html#billing-testing-setup">test
+account.</a>
 
 <p>To make an In-app Billing request with a reserved product ID, you simply construct a normal
 <code>REQUEST_PURCHASE</code> request, but instead of using a real product ID from your
@@ -310,9 +262,11 @@
 experience, including the actual purchases from Google Play and the actual checkout flow that
 users will experience in your application.</p>
 
-<p class="note"><strong>Note</strong>: You do not need to publish your application to do end-to-end
-testing. You only need to upload your application as a draft application to perform end-to-end
-testing.</p>
+<p class="note"><strong>Note:</strong> You can do end-to-end testing of your app
+  by publishing it to an <a
+  href="{@docRoot}distribute/googleplay/developer-console.html#alpha-beta">alpha
+  distribution channel</a>. This allows you to publish the app to the Google
+  Play store, but limit its availability to just the testers you designate. </p>
 
 <p>To test your In-app Billing implementation with actual in-app purchases, you will need to
 register at least one test account on the Google Play Developer Console. You cannot use your
@@ -327,14 +281,16 @@
 <p>To test your In-app Billing implementation with actual purchases, follow these steps:</p>
 
 <ol>
-  <li><strong>Upload your application as a draft application to the Developer Console.</strong>
-    <p>You do not need to publish your application to perform end-to-end testing with real product
-    IDs; you only need to upload your application as a draft application. However, you must sign
-    your application with your release key before you upload it as a draft application. Also, the
-    version number of the uploaded application must match the version number of the application you
-    load to your device for testing. To learn how to upload an application to Google Play, see
-    <a href="http://support.google.com/googleplay/android-developer/bin/answer.py?hl=en&answer=113469">Uploading
-    applications</a>.</p>
+  <li><strong>Upload your application to the <a
+  href="{@docRoot}distribute/googleplay/developer-console.html#alpha-beta">alpha
+  distribution channel</a> with the Developer Console.</strong>
+
+   <p class="note"><strong>Note:</strong> Previously you could test an app by
+    uploading an unpublished "draft" version. This functionality is no longer
+    supported; instead, you must publish it to the alpha or beta distribution
+    channel. For more information, see <a href="#draft_apps">Draft Apps are No
+    Longer Supported</a>.
+
   </li>
   <li><strong>Add items to the application's product list.</strong>
     <p>Make sure that you publish the items (the application can remain unpublished). See <a
@@ -370,3 +326,24 @@
 href="{@docRoot}distribute/tools/launch-checklist.html">publishing on Google Play</a>.
 </p>
 
+<h2 id="draft_apps">Draft Apps are No Longer Supported</h2>
+
+<p>Previously, you could publish a "draft" version of your app for testing. This
+functionality is no longer supported. Instead, there are two ways you can test
+how a pre-release app functions on the Google Play store:</p>
+
+<ul>
+
+  <li>You can publish an app to the <a
+  href="{@docRoot}distribute/googleplay/developer-console.html#alpha-beta">alpha
+  or beta distribution channels</a>. This makes the app available on the Google
+  Play store, but only to the testers you put on a "whitelist".</li>
+
+  <li>In a few cases, you can test Google Play functionality with an unpublished
+  app. For example, you can test an unpublished app's in-app billing support by
+  using <a
+  href="{@docRoot}google/play/billing/billing_testing.html#billing-testing-static">static
+  responses</a>, special reserved product IDs that always return a specific
+  result (like "purchased" or "refunded").</li>
+
+</ul>
diff --git a/docs/html/google/play/billing/v2/billing_integrate.jd b/docs/html/google/play/billing/v2/billing_integrate.jd
index ca41e0b..5eb17d5 100644
--- a/docs/html/google/play/billing/v2/billing_integrate.jd
+++ b/docs/html/google/play/billing/v2/billing_integrate.jd
@@ -208,6 +208,14 @@
 a draft to the Google Play Developer Console. You also need to create a product list for the in-app
 items that are available for purchase in the sample application. The following instructions show you
 how to do this.</p>
+
+<p class="caution"><strong>Caution:</strong> Draft applications are no longer
+supported. To test an application, publish it in the <a
+href="{@docRoot}distribute/googleplay/developer-console.html#alpha-beta">alpha
+or beta channels</a>. For more information, see <a
+href="{@docRoot}google/play/billing/billing_testing.html#draft_apps">Draft Apps
+are No Longer Supported</a>.</p>
+
 <ol>
   <li><strong>Upload the release version of the sample application to Google Play.</strong>
     <p>Do not publish the sample application; leave it as an unpublished draft application. The
@@ -928,10 +936,12 @@
   // Intent actions that we receive in the BillingReceiver from Google Play.
   // These are defined by Google Play and cannot be changed.
   // The sample application defines these in the Consts.java file.
-  public static final String ACTION_NOTIFY = "com.android.vending.billing.IN_APP_NOTIFY";
-  public static final String ACTION_RESPONSE_CODE = "com.android.vending.billing.RESPONSE_CODE";
+  public static final String ACTION_NOTIFY =
+      "com.android.vending.billing.IN_APP_NOTIFY";
+  public static final String ACTION_RESPONSE_CODE =
+      "com.android.vending.billing.RESPONSE_CODE";
   public static final String ACTION_PURCHASE_STATE_CHANGED =
-    "com.android.vending.billing.PURCHASE_STATE_CHANGED";
+      "com.android.vending.billing.PURCHASE_STATE_CHANGED";
 
   // The intent extras that are passed in an intent from Google Play.
   // These are defined by Google Play and cannot be changed.
@@ -962,7 +972,8 @@
       Log.w(TAG, "unexpected action: " + action);
     }
   }
-  // Perform other processing here, such as forwarding intent messages to your local service.
+  // Perform other processing here, such as forwarding intent messages
+  // to your local service.
 }
 </pre>
 
diff --git a/docs/html/google/play/expansion-files.jd b/docs/html/google/play/expansion-files.jd
index e90f8fa..601ea48 100644
--- a/docs/html/google/play/expansion-files.jd
+++ b/docs/html/google/play/expansion-files.jd
@@ -527,17 +527,21 @@
     &lt;!-- Required to download files from Google Play -->
     &lt;uses-permission android:name="android.permission.INTERNET" />
 
-    &lt;!-- Required to keep CPU alive while downloading files (NOT to keep screen awake) -->
+    &lt;!-- Required to keep CPU alive while downloading files
+        (NOT to keep screen awake) -->
     &lt;uses-permission android:name="android.permission.WAKE_LOCK" />
 
-    &lt;!-- Required to poll the state of the network connection and respond to changes -->
-    &lt;uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
+    &lt;!-- Required to poll the state of the network connection
+        and respond to changes -->
+    &lt;uses-permission
+        android:name="android.permission.ACCESS_NETWORK_STATE" />
 
     &lt;!-- Required to check whether Wi-Fi is enabled -->
     &lt;uses-permission android:name="android.permission.ACCESS_WIFI_STATE"/>
 
     &lt;!-- Required to read and write the expansion files on shared storage -->
-    &lt;uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
+    &lt;uses-permission
+        android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
     ...
 &lt;/manifest>
 </pre>
@@ -650,8 +654,8 @@
     &#64;Override
     public void onReceive(Context context, Intent intent) {
         try {
-            DownloaderClientMarshaller.startDownloadServiceIfRequired(context, intent,
-                    SampleDownloaderService.class);
+            DownloaderClientMarshaller.startDownloadServiceIfRequired(context,
+                intent, SampleDownloaderService.class);
         } catch (NameNotFoundException e) {
             e.printStackTrace();
         }
@@ -693,16 +697,19 @@
     <p>For example, the sample app provided in the Apk Expansion package calls the
 following method in the activity's {@link android.app.Activity#onCreate onCreate()} method to check
 whether the expansion files already exist on the device:</p>
+
 <pre>
 boolean expansionFilesDelivered() {
     for (XAPKFile xf : xAPKS) {
-        String fileName = Helpers.getExpansionAPKFileName(this, xf.mIsBase, xf.mFileVersion);
+        String fileName = Helpers.getExpansionAPKFileName(this, xf.mIsBase,
+            xf.mFileVersion);
         if (!Helpers.doesFileExist(this, fileName, xf.mFileSize, false))
             return false;
     }
     return true;
 }
 </pre>
+
     <p>In this case, each {@code XAPKFile} object holds the version number and file size of a known
 expansion file and a boolean as to whether it's the main expansion file. (See the sample
 application's {@code SampleDownloaderActivity} class for details.)</p>
@@ -740,6 +747,7 @@
 display the download progress (see the next step). If the response <em>is</em> {@code
 NO_DOWNLOAD_REQUIRED}, then the files are available and your application can start.</p>
     <p>For example:</p>
+
 <pre>
 &#64;Override
 public void onCreate(Bundle savedInstanceState) {
@@ -754,11 +762,14 @@
                 notifierIntent, PendingIntent.FLAG_UPDATE_CURRENT);
 
         // Start the download service (if required)
-        int startResult = DownloaderClientMarshaller.startDownloadServiceIfRequired(this,
+        int startResult =
+            DownloaderClientMarshaller.startDownloadServiceIfRequired(this,
                         pendingIntent, SampleDownloaderService.class);
-        // If download has started, initialize this activity to show download progress
+        // If download has started, initialize this activity to show
+        // download progress
         if (startResult != DownloaderClientMarshaller.NO_DOWNLOAD_REQUIRED) {
-            // This is where you do set up to display the download progress (next step)
+            // This is where you do set up to display the download
+            // progress (next step)
             ...
             return;
         } // If the download wasn't necessary, fall through to start the app
@@ -766,6 +777,7 @@
     startApp(); // Expansion files are available, start the app
 }
 </pre>
+
   </li>
   <li>When the {@code startDownloadServiceIfRequired()} method returns anything <em>other
 than</em> {@code NO_DOWNLOAD_REQUIRED}, create an instance of {@code IStub} by
@@ -783,9 +795,11 @@
 starts the download. </p>
     <p>For example, in the previous code sample for {@link android.app.Activity#onCreate
 onCreate()}, you can respond to the {@code startDownloadServiceIfRequired()} result like this:</p>
+
 <pre>
         // Start the download service (if required)
-        int startResult = DownloaderClientMarshaller.startDownloadServiceIfRequired(this,
+        int startResult =
+            DownloaderClientMarshaller.startDownloadServiceIfRequired(this,
                         pendingIntent, SampleDownloaderService.class);
         // If download has started, initialize activity to show progress
         if (startResult != DownloaderClientMarshaller.NO_DOWNLOAD_REQUIRED) {
@@ -892,7 +906,8 @@
 expansion files. You might want to provide a user preference to enable downloads over
 the cellular network. In which case, you can call:
 <pre>
-mRemoteService.setDownloadFlags(IDownloaderService.FLAGS_DOWNLOAD_OVER_CELLULAR);
+mRemoteService
+    .setDownloadFlags(IDownloaderService.FLAGS_DOWNLOAD_OVER_CELLULAR);
 </pre>
 </dd>
 </dl>
@@ -975,10 +990,12 @@
 // The shared path to all app expansion files
 private final static String EXP_PATH = "/Android/obb/";
 
-static String[] getAPKExpansionFiles(Context ctx, int mainVersion, int patchVersion) {
+static String[] getAPKExpansionFiles(Context ctx, int mainVersion,
+      int patchVersion) {
     String packageName = ctx.getPackageName();
     Vector&lt;String> ret = new Vector&lt;String>();
-    if (Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {
+    if (Environment.getExternalStorageState()
+          .equals(Environment.MEDIA_MOUNTED)) {
         // Build the full path to the app's expansion files
         File root = Environment.getExternalStorageDirectory();
         File expPath = new File(root.toString() + EXP_PATH + packageName);
@@ -1102,7 +1119,8 @@
 
 <pre>
 // Get a ZipResourceFile representing a merger of both the main and patch files
-ZipResourceFile expansionFile = APKExpansionSupport.getAPKExpansionZipFile(appContext,
+ZipResourceFile expansionFile =
+    APKExpansionSupport.getAPKExpansionZipFile(appContext,
         mainVersion, patchVersion);
 
 // Get an input stream for a known file inside the expansion file ZIPs
@@ -1190,28 +1208,18 @@
 opens, it's important that you test this process to be sure your application can successfully query
 for the URLs, download the files, and save them to the device.</p>
 
-<p>To test your application's implementation of the manual download procedure, you must upload
-your application to Google Play as a "draft" to make your expansion files available for
-download:</p>
-
-<ol>
-  <li>Upload your APK and corresponding expansion files using the Google Play Developer
-Console.</li>
-  <li>Fill in the necessary application details (title, screenshots, etc.). You can come back and
-finalize these details before publishing your application.
-  <p>Click the <strong>Save</strong> button. <em>Do not click Publish.</em> This saves
-the application as a draft, such that your application is not published for Google Play users,
-but the expansion files are available for you to test the download process.</p></li>
-  <li>Install the application on your test device using the Eclipse tools or <a
-href="{@docRoot}tools/help/adb.html">{@code adb}</a>.</li>
-  <li>Launch the app.</li>
-</ol>
-
-<p>If everything works as expected, your application should begin downloading the expansion
+<p>To test your application's implementation of the manual download procedure,
+you can publish it to the alpha or beta channel, so it will only be available to
+authorized testers.
+If everything works as expected, your application should begin downloading the expansion
 files as soon as the main activity starts.</p>
 
-
-
+<p class="note"><strong>Note:</strong> Previously you could test an app by
+uploading an unpublished "draft" version. This functionality is no longer
+supported; instead, you must publish it to the alpha or beta distribution
+channel. For more information, see <a
+href="{@docRoot}google/play/billing/billing_testing.html#draft_apps">Draft Apps
+are No Longer Supported</a>.
 
 <h2 id="Updating">Updating Your Application</h2>
 
diff --git a/docs/html/google/play/licensing/licensing-reference.jd b/docs/html/google/play/licensing/licensing-reference.jd
index 7bfa61a..d4ca79a 100644
--- a/docs/html/google/play/licensing/licensing-reference.jd
+++ b/docs/html/google/play/licensing/licensing-reference.jd
@@ -151,7 +151,8 @@
 <tr>
 <td>{@code LICENSED}</td>
 <td>The application is licensed to the user. The user has purchased the
-application or the application only exists as a draft.</td>
+application, or is authorized to download and install the alpha or beta version
+of the application.</td>
 <td>Yes</td>
 <td><code>VT</code>,&nbsp;<code>GT</code>, <code>GR</code></td>
 <td><em>Allow access according to {@code Policy} constraints.</em></td>
@@ -233,16 +234,14 @@
 href="{@docRoot}google/play/licensing/setting-up.html#test-env">
 Setting Up The Testing Environment</a>, the response code can be manually
 overridden for the application developer and any registered test users via the
-Google Play Developer Console.
-<br/><br/>
-Additionally, as noted above, applications that are in draft mode (in other
-words, applications that have been uploaded but have <em>never</em> been
-published) will return {@code LICENSED} for all users, even if not listed as a test
-user. Since the application has never been offered for download, it is assumed
-that any users running it must have obtained it from an authorized channel for
-testing purposes.</p>
+Google Play Developer Console.</p>
 
-
+<p class="note"><strong>Note:</strong> Previously you could test an app by
+uploading an unpublished "draft" version. This functionality is no longer
+supported; instead, you must publish it to the alpha or beta distribution
+channel. For more information, see <a
+href="{@docRoot}google/play/billing/billing_testing.html#draft_apps">Draft Apps
+are No Longer Supported</a>.
 
 
 <h2 id="extras">Server Response Extras</h2>
@@ -430,8 +429,8 @@
         }
     } else if (mLastResponse == LicenseResponse.RETRY &amp;&amp;
                 ts &lt; mLastResponseTime + MILLIS_PER_MINUTE) {
-        // Only allow access if we are within the retry period or we haven't used up our
-        // max retries.
+        // Only allow access if we are within the retry period
+        // or we haven't used up our max retries.
         return (ts &lt;= mRetryUntil || mRetryCount &lt;= mMaxRetries);
     }
     return false;
diff --git a/docs/html/google/play/licensing/overview.jd b/docs/html/google/play/licensing/overview.jd
index 4e1a9c9..a2d5379 100644
--- a/docs/html/google/play/licensing/overview.jd
+++ b/docs/html/google/play/licensing/overview.jd
@@ -38,12 +38,11 @@
 the result to your application, which can allow or disallow further use of the
 application as needed.</p>
 
-<p class="note"><strong>Note:</strong> If a paid application has been uploaded 
-to Google Play, but saved only as a draft application (the app is 
-unpublished), the licensing server considers all users to be licensed users of 
-the application (because it's not even possible to purchase the app). This 
-exception is necessary in order for you to perform testing of your licensing 
-implementation.</p>
+<p class="note"><strong>Note:</strong> If a version of an app is in the alpha or
+beta channel, all users who are authorized to download and install that app are
+considered to be licensed users of the app. For more information, see <a
+href="{@docRoot}distribute/googleplay/developer-console.html#alpha-beta">Alpha
+and Beta Testing</a>.</p>
 
 <div class="figure" style="width:469px">
 <img src="{@docRoot}images/licensing_arch.png" alt=""/>
@@ -52,6 +51,12 @@
 client, which handles communication with the Google Play server.</p>
 </div>
 
+<p class="note"><strong>Note:</strong> Previously you could test an app by
+uploading an unpublished "draft" version. This functionality is no longer
+supported; instead, you must publish it to the alpha or beta distribution
+channel. For more information, see <a
+href="{@docRoot}google/play/billing/billing_testing.html#draft_apps">Draft Apps
+are No Longer Supported</a>.
 
 <p>To properly identify the user and determine the license status, the licensing server requires
 information about the application and user&mdash;your application and the Google Play client work
diff --git a/docs/html/guide/components/fundamentals.jd b/docs/html/guide/components/fundamentals.jd
index 9ac063e..fd1a7a8 100644
--- a/docs/html/guide/components/fundamentals.jd
+++ b/docs/html/guide/components/fundamentals.jd
@@ -335,8 +335,8 @@
 {@link android.content.Intent} to start activities, services, and broadcast receivers. You can do so
 by explicitly naming the target component (using the component class name) in the intent. However,
 the real power of intents lies in the concept of <em>implicit intents</em>. An implicit intent
-simply describe the type of action to perform (and optionally, the data upon which you’d like to
-perform the action) and allow the system to find a component on the device that can perform the
+simply describes the type of action to perform (and, optionally, the data upon which you’d like to
+perform the action) and allows the system to find a component on the device that can perform the
 action and start it. If there are multiple components that can perform the action described by the
 intent, then the user selects which one to use.</p>
 
diff --git a/docs/html/tools/device.jd b/docs/html/tools/device.jd
index e9caa44..e748b12 100644
--- a/docs/html/tools/device.jd
+++ b/docs/html/tools/device.jd
@@ -280,6 +280,10 @@
     <td><code>0fce</code></td>
   </tr>
   <tr>
+    <td>Sony Mobile Communications</td>
+    <td><code>0fce</code></td>
+  </tr>
+  <tr>
     <td>Teleepoch</td>
     <td><code>2340</code></td>
   </tr>
diff --git a/graphics/java/android/graphics/Camera.java b/graphics/java/android/graphics/Camera.java
index a40085b..57e0f27 100644
--- a/graphics/java/android/graphics/Camera.java
+++ b/graphics/java/android/graphics/Camera.java
@@ -154,7 +154,7 @@
             getMatrix(mMatrix);
             canvas.concat(mMatrix);
         } else {
-            nativeApplyToCanvas(canvas.getNativeCanvas());
+            nativeApplyToCanvas(canvas.getNativeCanvasWrapper());
         }
     }
 
diff --git a/graphics/java/android/graphics/Canvas.java b/graphics/java/android/graphics/Canvas.java
index d4ea7a9..bd868f2 100644
--- a/graphics/java/android/graphics/Canvas.java
+++ b/graphics/java/android/graphics/Canvas.java
@@ -39,11 +39,11 @@
 public class Canvas {
 
     // assigned in constructors or setBitmap, freed in finalizer
-    private long mNativeCanvas;
+    private long mNativeCanvasWrapper;
 
     /** @hide */
-    public long getNativeCanvas() {
-        return mNativeCanvas;
+    public long getNativeCanvasWrapper() {
+        return mNativeCanvasWrapper;
     }
 
     // may be null
@@ -88,10 +88,10 @@
     private final CanvasFinalizer mFinalizer;
 
     private static final class CanvasFinalizer {
-        private long mNativeCanvas;
+        private long mNativeCanvasWrapper;
 
         public CanvasFinalizer(long nativeCanvas) {
-            mNativeCanvas = nativeCanvas;
+            mNativeCanvasWrapper = nativeCanvas;
         }
 
         @Override
@@ -104,9 +104,9 @@
         }
 
         public void dispose() {
-            if (mNativeCanvas != 0) {
-                finalizer(mNativeCanvas);
-                mNativeCanvas = 0;
+            if (mNativeCanvasWrapper != 0) {
+                finalizer(mNativeCanvasWrapper);
+                mNativeCanvasWrapper = 0;
             }
         }
     }
@@ -120,8 +120,8 @@
     public Canvas() {
         if (!isHardwareAccelerated()) {
             // 0 means no native bitmap
-            mNativeCanvas = initRaster(0);
-            mFinalizer = new CanvasFinalizer(mNativeCanvas);
+            mNativeCanvasWrapper = initRaster(0);
+            mFinalizer = new CanvasFinalizer(mNativeCanvasWrapper);
         } else {
             mFinalizer = null;
         }
@@ -141,8 +141,8 @@
             throw new IllegalStateException("Immutable bitmap passed to Canvas constructor");
         }
         throwIfCannotDraw(bitmap);
-        mNativeCanvas = initRaster(bitmap.ni());
-        mFinalizer = new CanvasFinalizer(mNativeCanvas);
+        mNativeCanvasWrapper = initRaster(bitmap.ni());
+        mFinalizer = new CanvasFinalizer(mNativeCanvasWrapper);
         mBitmap = bitmap;
         mDensity = bitmap.mDensity;
     }
@@ -152,26 +152,12 @@
         if (nativeCanvas == 0) {
             throw new IllegalStateException();
         }
-        mNativeCanvas = nativeCanvas;
-        mFinalizer = new CanvasFinalizer(mNativeCanvas);
+        mNativeCanvasWrapper = initCanvas(nativeCanvas);
+        mFinalizer = new CanvasFinalizer(mNativeCanvasWrapper);
         mDensity = Bitmap.getDefaultDensity();
     }
 
     /**
-     * Replace existing canvas while ensuring that the swap has occurred before
-     * the previous native canvas is unreferenced.
-     */
-    private void safeCanvasSwap(long nativeCanvas, boolean copyState) {
-        final long oldCanvas = mNativeCanvas;
-        mNativeCanvas = nativeCanvas;
-        mFinalizer.mNativeCanvas = nativeCanvas;
-        if (copyState) {
-            copyNativeCanvasState(oldCanvas, mNativeCanvas);
-        }
-        finalizer(oldCanvas);
-    }
-
-    /**
      * Returns null.
      *
      * @deprecated This method is not supported and should not be invoked.
@@ -212,7 +198,7 @@
         }
 
         if (bitmap == null) {
-            safeCanvasSwap(initRaster(0), false);
+            native_setBitmap(mNativeCanvasWrapper, 0, false);
             mDensity = Bitmap.DENSITY_NONE;
         } else {
             if (!bitmap.isMutable()) {
@@ -220,7 +206,7 @@
             }
             throwIfCannotDraw(bitmap);
 
-            safeCanvasSwap(initRaster(bitmap.ni()), true);
+            native_setBitmap(mNativeCanvasWrapper, bitmap.ni(), true);
             mDensity = bitmap.mDensity;
         }
 
@@ -228,6 +214,13 @@
     }
 
     /**
+     * setBitmap() variant for native callers with a raw bitmap handle.
+     */
+    private void setNativeBitmap(long bitmapHandle) {
+        native_setBitmap(mNativeCanvasWrapper, bitmapHandle, false);
+    }
+
+    /**
      * Set the viewport dimensions if this canvas is GL based. If it is not,
      * this method is ignored and no exception is thrown.
      *
@@ -382,7 +375,7 @@
      * @return       value to pass to restoreToCount() to balance this save()
      */
     public int saveLayer(RectF bounds, Paint paint, int saveFlags) {
-        return native_saveLayer(mNativeCanvas, bounds,
+        return native_saveLayer(mNativeCanvasWrapper, bounds,
                 paint != null ? paint.mNativePaint : 0,
                 saveFlags);
     }
@@ -399,7 +392,7 @@
      */
     public int saveLayer(float left, float top, float right, float bottom, Paint paint,
             int saveFlags) {
-        return native_saveLayer(mNativeCanvas, left, top, right, bottom,
+        return native_saveLayer(mNativeCanvasWrapper, left, top, right, bottom,
                 paint != null ? paint.mNativePaint : 0,
                 saveFlags);
     }
@@ -429,7 +422,7 @@
      */
     public int saveLayerAlpha(RectF bounds, int alpha, int saveFlags) {
         alpha = Math.min(255, Math.max(0, alpha));
-        return native_saveLayerAlpha(mNativeCanvas, bounds, alpha, saveFlags);
+        return native_saveLayerAlpha(mNativeCanvasWrapper, bounds, alpha, saveFlags);
     }
 
     /**
@@ -444,7 +437,7 @@
      */
     public int saveLayerAlpha(float left, float top, float right, float bottom, int alpha,
             int saveFlags) {
-        return native_saveLayerAlpha(mNativeCanvas, left, top, right, bottom,
+        return native_saveLayerAlpha(mNativeCanvasWrapper, left, top, right, bottom,
                                      alpha, saveFlags);
     }
 
@@ -548,7 +541,7 @@
      * @param matrix The matrix to preconcatenate with the current matrix
      */
     public void concat(Matrix matrix) {
-        if (matrix != null) native_concat(mNativeCanvas, matrix.native_instance);
+        if (matrix != null) native_concat(mNativeCanvasWrapper, matrix.native_instance);
     }
     
     /**
@@ -565,7 +558,7 @@
      * @see #concat(Matrix) 
      */
     public void setMatrix(Matrix matrix) {
-        native_setMatrix(mNativeCanvas,
+        native_setMatrix(mNativeCanvasWrapper,
                          matrix == null ? 0 : matrix.native_instance);
     }
     
@@ -575,7 +568,7 @@
      */
     @Deprecated
     public void getMatrix(Matrix ctm) {
-        native_getCTM(mNativeCanvas, ctm.native_instance);
+        native_getCTM(mNativeCanvasWrapper, ctm.native_instance);
     }
 
     /**
@@ -598,7 +591,7 @@
      * @return true if the resulting clip is non-empty
      */
     public boolean clipRect(RectF rect, Region.Op op) {
-        return native_clipRect(mNativeCanvas, rect.left, rect.top, rect.right, rect.bottom,
+        return native_clipRect(mNativeCanvasWrapper, rect.left, rect.top, rect.right, rect.bottom,
                 op.nativeInt);
     }
 
@@ -611,7 +604,7 @@
      * @return true if the resulting clip is non-empty
      */
     public boolean clipRect(Rect rect, Region.Op op) {
-        return native_clipRect(mNativeCanvas, rect.left, rect.top, rect.right, rect.bottom,
+        return native_clipRect(mNativeCanvasWrapper, rect.left, rect.top, rect.right, rect.bottom,
                 op.nativeInt);
     }
 
@@ -649,7 +642,7 @@
      * @return       true if the resulting clip is non-empty
      */
     public boolean clipRect(float left, float top, float right, float bottom, Region.Op op) {
-        return native_clipRect(mNativeCanvas, left, top, right, bottom, op.nativeInt);
+        return native_clipRect(mNativeCanvasWrapper, left, top, right, bottom, op.nativeInt);
     }
 
     /**
@@ -690,7 +683,7 @@
      * @return     true if the resulting is non-empty
      */
     public boolean clipPath(Path path, Region.Op op) {
-        return native_clipPath(mNativeCanvas, path.ni(), op.nativeInt);
+        return native_clipPath(mNativeCanvasWrapper, path.ni(), op.nativeInt);
     }
     
     /**
@@ -713,9 +706,12 @@
      * @param region The region to operate on the current clip, based on op
      * @param op How the clip is modified
      * @return true if the resulting is non-empty
+     *
+     * @deprecated Unlike all other clip calls this API does not respect the
+     *             current matrix. Use {@link #clipRect(Rect)} as an alternative.
      */
     public boolean clipRegion(Region region, Region.Op op) {
-        return native_clipRegion(mNativeCanvas, region.ni(), op.nativeInt);
+        return native_clipRegion(mNativeCanvasWrapper, region.ni(), op.nativeInt);
     }
 
     /**
@@ -727,6 +723,9 @@
      *
      * @param region The region to operate on the current clip, based on op
      * @return true if the resulting is non-empty
+     *
+     * @deprecated Unlike all other clip calls this API does not respect the
+     *             current matrix. Use {@link #clipRect(Rect)} as an alternative.
      */
     public boolean clipRegion(Region region) {
         return clipRegion(region, Region.Op.INTERSECT);
@@ -742,7 +741,7 @@
             nativeFilter = filter.mNativeInt;
         }
         mDrawFilter = filter;
-        nativeSetDrawFilter(mNativeCanvas, nativeFilter);
+        nativeSetDrawFilter(mNativeCanvasWrapper, nativeFilter);
     }
 
     public enum EdgeType {
@@ -781,7 +780,7 @@
      *              does not intersect with the canvas' clip
      */
     public boolean quickReject(RectF rect, EdgeType type) {
-        return native_quickReject(mNativeCanvas, rect);
+        return native_quickReject(mNativeCanvasWrapper, rect);
     }
 
     /**
@@ -800,7 +799,7 @@
      *                    does not intersect with the canvas' clip
      */
     public boolean quickReject(Path path, EdgeType type) {
-        return native_quickReject(mNativeCanvas, path.ni());
+        return native_quickReject(mNativeCanvasWrapper, path.ni());
     }
 
     /**
@@ -825,7 +824,7 @@
      */
     public boolean quickReject(float left, float top, float right, float bottom,
                                EdgeType type) {
-        return native_quickReject(mNativeCanvas, left, top, right, bottom);
+        return native_quickReject(mNativeCanvasWrapper, left, top, right, bottom);
     }
 
     /**
@@ -839,7 +838,7 @@
      * @return true if the current clip is non-empty.
      */
     public boolean getClipBounds(Rect bounds) {
-        return native_getClipBounds(mNativeCanvas, bounds);
+        return native_getClipBounds(mNativeCanvasWrapper, bounds);
     }
     
     /**
@@ -862,7 +861,7 @@
      * @param b blue component (0..255) of the color to draw onto the canvas
      */
     public void drawRGB(int r, int g, int b) {
-        native_drawRGB(mNativeCanvas, r, g, b);
+        native_drawRGB(mNativeCanvasWrapper, r, g, b);
     }
 
     /**
@@ -875,7 +874,7 @@
      * @param b blue component (0..255) of the color to draw onto the canvas
      */
     public void drawARGB(int a, int r, int g, int b) {
-        native_drawARGB(mNativeCanvas, a, r, g, b);
+        native_drawARGB(mNativeCanvasWrapper, a, r, g, b);
     }
 
     /**
@@ -885,7 +884,7 @@
      * @param color the color to draw onto the canvas
      */
     public void drawColor(int color) {
-        native_drawColor(mNativeCanvas, color);
+        native_drawColor(mNativeCanvasWrapper, color);
     }
 
     /**
@@ -896,7 +895,7 @@
      * @param mode  the porter-duff mode to apply to the color
      */
     public void drawColor(int color, PorterDuff.Mode mode) {
-        native_drawColor(mNativeCanvas, color, mode.nativeInt);
+        native_drawColor(mNativeCanvasWrapper, color, mode.nativeInt);
     }
 
     /**
@@ -907,7 +906,7 @@
      * @param paint The paint used to draw onto the canvas
      */
     public void drawPaint(Paint paint) {
-        native_drawPaint(mNativeCanvas, paint.mNativePaint);
+        native_drawPaint(mNativeCanvasWrapper, paint.mNativePaint);
     }
     
     /**
@@ -953,7 +952,7 @@
      * @param paint  The paint used to draw the line
      */
     public void drawLine(float startX, float startY, float stopX, float stopY, Paint paint) {
-        native_drawLine(mNativeCanvas, startX, startY, stopX, stopY, paint.mNativePaint);
+        native_drawLine(mNativeCanvasWrapper, startX, startY, stopX, stopY, paint.mNativePaint);
     }
 
     /**
@@ -985,7 +984,7 @@
      * @param paint The paint used to draw the rect
      */
     public void drawRect(RectF rect, Paint paint) {
-        native_drawRect(mNativeCanvas, rect, paint.mNativePaint);
+        native_drawRect(mNativeCanvasWrapper, rect, paint.mNativePaint);
     }
 
     /**
@@ -1011,7 +1010,7 @@
      * @param paint  The paint used to draw the rect
      */
     public void drawRect(float left, float top, float right, float bottom, Paint paint) {
-        native_drawRect(mNativeCanvas, left, top, right, bottom, paint.mNativePaint);
+        native_drawRect(mNativeCanvasWrapper, left, top, right, bottom, paint.mNativePaint);
     }
 
     /**
@@ -1024,7 +1023,7 @@
         if (oval == null) {
             throw new NullPointerException();
         }
-        native_drawOval(mNativeCanvas, oval, paint.mNativePaint);
+        native_drawOval(mNativeCanvasWrapper, oval, paint.mNativePaint);
     }
 
     /**
@@ -1038,7 +1037,7 @@
      * @param paint  The paint used to draw the circle
      */
     public void drawCircle(float cx, float cy, float radius, Paint paint) {
-        native_drawCircle(mNativeCanvas, cx, cy, radius, paint.mNativePaint);
+        native_drawCircle(mNativeCanvasWrapper, cx, cy, radius, paint.mNativePaint);
     }
 
     /**
@@ -1069,7 +1068,7 @@
         if (oval == null) {
             throw new NullPointerException();
         }
-        native_drawArc(mNativeCanvas, oval, startAngle, sweepAngle,
+        native_drawArc(mNativeCanvasWrapper, oval, startAngle, sweepAngle,
                 useCenter, paint.mNativePaint);
     }
 
@@ -1096,7 +1095,7 @@
      */
     public void drawRoundRect(float left, float top, float right, float bottom, float rx, float ry,
             Paint paint) {
-        native_drawRoundRect(mNativeCanvas, left, top, right, bottom, rx, ry, paint.mNativePaint);
+        native_drawRoundRect(mNativeCanvasWrapper, left, top, right, bottom, rx, ry, paint.mNativePaint);
     }
 
     /**
@@ -1107,7 +1106,7 @@
      * @param paint The paint used to draw the path
      */
     public void drawPath(Path path, Paint paint) {
-        native_drawPath(mNativeCanvas, path.ni(), paint.mNativePaint);
+        native_drawPath(mNativeCanvasWrapper, path.ni(), paint.mNativePaint);
     }
 
     /**
@@ -1171,7 +1170,7 @@
      */
     public void drawBitmap(Bitmap bitmap, float left, float top, Paint paint) {
         throwIfCannotDraw(bitmap);
-        native_drawBitmap(mNativeCanvas, bitmap.ni(), left, top,
+        native_drawBitmap(mNativeCanvasWrapper, bitmap.ni(), left, top,
                 paint != null ? paint.mNativePaint : 0, mDensity, mScreenDensity, bitmap.mDensity);
     }
 
@@ -1202,7 +1201,7 @@
             throw new NullPointerException();
         }
         throwIfCannotDraw(bitmap);
-        native_drawBitmap(mNativeCanvas, bitmap.ni(), src, dst,
+        native_drawBitmap(mNativeCanvasWrapper, bitmap.ni(), src, dst,
                           paint != null ? paint.mNativePaint : 0, mScreenDensity, bitmap.mDensity);
     }
 
@@ -1233,7 +1232,7 @@
             throw new NullPointerException();
         }
         throwIfCannotDraw(bitmap);
-        native_drawBitmap(mNativeCanvas, bitmap.ni(), src, dst,
+        native_drawBitmap(mNativeCanvasWrapper, bitmap.ni(), src, dst,
                 paint != null ? paint.mNativePaint : 0, mScreenDensity, bitmap.mDensity);
     }
     
@@ -1285,7 +1284,7 @@
             return;
         }
         // punch down to native for the actual draw
-        native_drawBitmap(mNativeCanvas, colors, offset, stride, x, y, width, height, hasAlpha,
+        native_drawBitmap(mNativeCanvasWrapper, colors, offset, stride, x, y, width, height, hasAlpha,
                 paint != null ? paint.mNativePaint : 0);
     }
 
@@ -1313,7 +1312,7 @@
      * @param paint  May be null. The paint used to draw the bitmap
      */
     public void drawBitmap(Bitmap bitmap, Matrix matrix, Paint paint) {
-        nativeDrawBitmapMatrix(mNativeCanvas, bitmap.ni(), matrix.ni(),
+        nativeDrawBitmapMatrix(mNativeCanvasWrapper, bitmap.ni(), matrix.ni(),
                 paint != null ? paint.mNativePaint : 0);
     }
 
@@ -1367,7 +1366,7 @@
             // no mul by 2, since we need only 1 color per vertex
             checkRange(colors.length, colorOffset, count);
         }
-        nativeDrawBitmapMesh(mNativeCanvas, bitmap.ni(), meshWidth, meshHeight,
+        nativeDrawBitmapMesh(mNativeCanvasWrapper, bitmap.ni(), meshWidth, meshHeight,
                 verts, vertOffset, colors, colorOffset,
                 paint != null ? paint.mNativePaint : 0);
     }
@@ -1430,7 +1429,7 @@
         if (indices != null) {
             checkRange(indices.length, indexOffset, indexCount);
         }
-        nativeDrawVertices(mNativeCanvas, mode.nativeInt, vertexCount, verts,
+        nativeDrawVertices(mNativeCanvasWrapper, mode.nativeInt, vertexCount, verts,
                 vertOffset, texs, texOffset, colors, colorOffset,
                 indices, indexOffset, indexCount, paint.mNativePaint);
     }
@@ -1449,7 +1448,7 @@
             (text.length - index - count)) < 0) {
             throw new IndexOutOfBoundsException();
         }
-        native_drawText(mNativeCanvas, text, index, count, x, y, paint.mBidiFlags,
+        native_drawText(mNativeCanvasWrapper, text, index, count, x, y, paint.mBidiFlags,
                 paint.mNativePaint, paint.mNativeTypeface);
     }
 
@@ -1463,7 +1462,7 @@
      * @param paint The paint used for the text (e.g. color, size, style)
      */
     public void drawText(String text, float x, float y, Paint paint) {
-        native_drawText(mNativeCanvas, text, 0, text.length(), x, y, paint.mBidiFlags,
+        native_drawText(mNativeCanvasWrapper, text, 0, text.length(), x, y, paint.mBidiFlags,
                 paint.mNativePaint, paint.mNativeTypeface);
     }
 
@@ -1482,7 +1481,7 @@
         if ((start | end | (end - start) | (text.length() - end)) < 0) {
             throw new IndexOutOfBoundsException();
         }
-        native_drawText(mNativeCanvas, text, start, end, x, y, paint.mBidiFlags,
+        native_drawText(mNativeCanvasWrapper, text, start, end, x, y, paint.mBidiFlags,
                 paint.mNativePaint, paint.mNativeTypeface);
     }
 
@@ -1502,7 +1501,7 @@
     public void drawText(CharSequence text, int start, int end, float x, float y, Paint paint) {
         if (text instanceof String || text instanceof SpannedString ||
             text instanceof SpannableString) {
-            native_drawText(mNativeCanvas, text.toString(), start, end, x, y,
+            native_drawText(mNativeCanvasWrapper, text.toString(), start, end, x, y,
                     paint.mBidiFlags, paint.mNativePaint, paint.mNativeTypeface);
         } else if (text instanceof GraphicsOperations) {
             ((GraphicsOperations) text).drawText(this, start, end, x, y,
@@ -1510,7 +1509,7 @@
         } else {
             char[] buf = TemporaryBuffer.obtain(end - start);
             TextUtils.getChars(text, start, end, buf, 0);
-            native_drawText(mNativeCanvas, buf, 0, end - start, x, y,
+            native_drawText(mNativeCanvasWrapper, buf, 0, end - start, x, y,
                     paint.mBidiFlags, paint.mNativePaint, paint.mNativeTypeface);
             TemporaryBuffer.recycle(buf);
         }
@@ -1553,7 +1552,7 @@
             throw new IllegalArgumentException("unknown dir: " + dir);
         }
 
-        native_drawTextRun(mNativeCanvas, text, index, count,
+        native_drawTextRun(mNativeCanvasWrapper, text, index, count,
                 contextIndex, contextCount, x, y, dir, paint.mNativePaint, paint.mNativeTypeface);
     }
 
@@ -1591,7 +1590,7 @@
 
         if (text instanceof String || text instanceof SpannedString ||
                 text instanceof SpannableString) {
-            native_drawTextRun(mNativeCanvas, text.toString(), start, end,
+            native_drawTextRun(mNativeCanvasWrapper, text.toString(), start, end,
                     contextStart, contextEnd, x, y, flags, paint.mNativePaint, paint.mNativeTypeface);
         } else if (text instanceof GraphicsOperations) {
             ((GraphicsOperations) text).drawTextRun(this, start, end,
@@ -1601,7 +1600,7 @@
             int len = end - start;
             char[] buf = TemporaryBuffer.obtain(contextLen);
             TextUtils.getChars(text, contextStart, contextEnd, buf, 0);
-            native_drawTextRun(mNativeCanvas, buf, start - contextStart, len,
+            native_drawTextRun(mNativeCanvasWrapper, buf, start - contextStart, len,
                     0, contextLen, x, y, flags, paint.mNativePaint, paint.mNativeTypeface);
             TemporaryBuffer.recycle(buf);
         }
@@ -1626,7 +1625,7 @@
         if (index < 0 || index + count > text.length || count*2 > pos.length) {
             throw new IndexOutOfBoundsException();
         }
-        native_drawPosText(mNativeCanvas, text, index, count, pos,
+        native_drawPosText(mNativeCanvasWrapper, text, index, count, pos,
                 paint.mNativePaint);
     }
 
@@ -1646,7 +1645,7 @@
         if (text.length()*2 > pos.length) {
             throw new ArrayIndexOutOfBoundsException();
         }
-        native_drawPosText(mNativeCanvas, text, pos, paint.mNativePaint);
+        native_drawPosText(mNativeCanvasWrapper, text, pos, paint.mNativePaint);
     }
 
     /**
@@ -1667,7 +1666,7 @@
         if (index < 0 || index + count > text.length) {
             throw new ArrayIndexOutOfBoundsException();
         }
-        native_drawTextOnPath(mNativeCanvas, text, index, count,
+        native_drawTextOnPath(mNativeCanvasWrapper, text, index, count,
                 path.ni(), hOffset, vOffset,
                 paint.mBidiFlags, paint.mNativePaint);
     }
@@ -1687,7 +1686,7 @@
      */
     public void drawTextOnPath(String text, Path path, float hOffset, float vOffset, Paint paint) {
         if (text.length() > 0) {
-            native_drawTextOnPath(mNativeCanvas, text, path.ni(), hOffset, vOffset,
+            native_drawTextOnPath(mNativeCanvasWrapper, text, path.ni(), hOffset, vOffset,
                     paint.mBidiFlags, paint.mNativePaint);
         }
     }
@@ -1761,8 +1760,10 @@
     public static native void freeTextLayoutCaches();
 
     private static native long initRaster(long nativeBitmapOrZero);
-    private static native void copyNativeCanvasState(long nativeSrcCanvas,
-                                                     long nativeDstCanvas);
+    private static native long initCanvas(long canvasHandle);
+    private static native void native_setBitmap(long canvasHandle,
+                                                long bitmapHandle,
+                                                boolean copyState);
     private static native int native_saveLayer(long nativeCanvas,
                                                RectF bounds,
                                                long nativePaint,
diff --git a/graphics/java/android/graphics/FontFamily.java b/graphics/java/android/graphics/FontFamily.java
index a759a79..11d3165 100644
--- a/graphics/java/android/graphics/FontFamily.java
+++ b/graphics/java/android/graphics/FontFamily.java
@@ -16,6 +16,8 @@
 
 package android.graphics;
 
+import android.content.res.AssetManager;
+
 import java.io.File;
 
 /**
@@ -30,10 +32,22 @@
     public long mNativePtr;
 
     public FontFamily() {
-        mNativePtr = nCreateFamily();
-        mNativePtr = nCreateFamily();
+        mNativePtr = nCreateFamily(null, 0);
         if (mNativePtr == 0) {
-            throw new RuntimeException();
+            throw new IllegalStateException("error creating native FontFamily");
+        }
+    }
+
+    public FontFamily(String lang, String variant) {
+        int varEnum = 0;
+        if ("compact".equals(variant)) {
+            varEnum = 1;
+        } else if ("elegant".equals(variant)) {
+            varEnum = 2;
+        }
+        mNativePtr = nCreateFamily(lang, varEnum);
+        if (mNativePtr == 0) {
+            throw new IllegalStateException("error creating native FontFamily");
         }
     }
 
@@ -46,11 +60,17 @@
         }
     }
 
-    public boolean addFont(File path) {
-        return nAddFont(mNativePtr, path.getAbsolutePath());
+    public boolean addFont(String path) {
+        return nAddFont(mNativePtr, path);
     }
 
-    static native long nCreateFamily();
-    static native void nUnrefFamily(long nativePtr);
-    static native boolean nAddFont(long nativeFamily, String path);
+    public boolean addFontFromAsset(AssetManager mgr, String path) {
+        return nAddFontFromAsset(mNativePtr, mgr, path);
+    }
+
+    private static native long nCreateFamily(String lang, int variant);
+    private static native void nUnrefFamily(long nativePtr);
+    private static native boolean nAddFont(long nativeFamily, String path);
+    private static native boolean nAddFontFromAsset(long nativeFamily, AssetManager mgr,
+            String path);
 }
diff --git a/graphics/java/android/graphics/FontListParser.java b/graphics/java/android/graphics/FontListParser.java
index f304f4e..a863a06 100644
--- a/graphics/java/android/graphics/FontListParser.java
+++ b/graphics/java/android/graphics/FontListParser.java
@@ -34,14 +34,18 @@
 public class FontListParser {
 
     public static class Family {
-        public Family(List<String> names, List<String> fontFiles) {
+        public Family(List<String> names, List<String> fontFiles, String lang, String variant) {
             this.names = names;
             this.fontFiles = fontFiles;
+            this.lang = lang;
+            this.variant = variant;
         }
 
         public List<String> names;
         // todo: need attributes for font files
         public List<String> fontFiles;
+        public String lang;
+        public String variant;
     }
 
     /* Parse fallback list (no names) */
@@ -75,6 +79,8 @@
             throws XmlPullParserException, IOException {
         List<String> names = null;
         List<String> fontFiles = new ArrayList<String>();
+        String lang = null;
+        String variant = null;
         while (parser.next() != XmlPullParser.END_TAG) {
             if (parser.getEventType() != XmlPullParser.START_TAG) continue;
             String tag = parser.getName();
@@ -82,6 +88,12 @@
                 while (parser.next() != XmlPullParser.END_TAG) {
                     if (parser.getEventType() != XmlPullParser.START_TAG) continue;
                     if (parser.getName().equals("file")) {
+                        if (lang == null) {
+                            lang = parser.getAttributeValue(null, "lang");
+                        }
+                        if (variant == null) {
+                            variant = parser.getAttributeValue(null, "variant");
+                        }
                         String filename = parser.nextText();
                         String fullFilename = "/system/fonts/" + filename;
                         fontFiles.add(fullFilename);
@@ -98,7 +110,7 @@
                 }
             }
         }
-        return new Family(names, fontFiles);
+        return new Family(names, fontFiles, lang, variant);
     }
 
     private static void skip(XmlPullParser parser) throws XmlPullParserException, IOException {
diff --git a/graphics/java/android/graphics/NinePatch.java b/graphics/java/android/graphics/NinePatch.java
index 6ff5f4f..befac92 100644
--- a/graphics/java/android/graphics/NinePatch.java
+++ b/graphics/java/android/graphics/NinePatch.java
@@ -164,12 +164,12 @@
     }
 
     void drawSoftware(Canvas canvas, RectF location, Paint paint) {
-        nativeDraw(canvas.getNativeCanvas(), location, mBitmap.ni(), mNativeChunk,
+        nativeDraw(canvas.getNativeCanvasWrapper(), location, mBitmap.ni(), mNativeChunk,
                 paint != null ? paint.mNativePaint : 0, canvas.mDensity, mBitmap.mDensity);
     }
 
     void drawSoftware(Canvas canvas, Rect location, Paint paint) {
-        nativeDraw(canvas.getNativeCanvas(), location, mBitmap.ni(), mNativeChunk,
+        nativeDraw(canvas.getNativeCanvasWrapper(), location, mBitmap.ni(), mNativeChunk,
                 paint != null ? paint.mNativePaint : 0, canvas.mDensity, mBitmap.mDensity);
     }
 
diff --git a/graphics/java/android/graphics/Picture.java b/graphics/java/android/graphics/Picture.java
index a16c099..de458af 100644
--- a/graphics/java/android/graphics/Picture.java
+++ b/graphics/java/android/graphics/Picture.java
@@ -107,7 +107,7 @@
         if (mRecordingCanvas != null) {
             endRecording();
         }
-        nativeDraw(canvas.getNativeCanvas(), mNativePicture);
+        nativeDraw(canvas.getNativeCanvasWrapper(), mNativePicture);
     }
 
     /**
diff --git a/graphics/java/android/graphics/Typeface.java b/graphics/java/android/graphics/Typeface.java
index 64451c4..cb48de2 100644
--- a/graphics/java/android/graphics/Typeface.java
+++ b/graphics/java/android/graphics/Typeface.java
@@ -66,6 +66,9 @@
     static Map<String, Typeface> sSystemFontMap;
     static FontFamily[] sFallbackFonts;
 
+    static final String SYSTEM_FONTS_CONFIG = "system_fonts.xml";
+    static final String FALLBACK_FONTS_CONFIG = "fallback_fonts.xml";
+
     /**
      * @hide
      */
@@ -175,6 +178,15 @@
      * @return The new typeface.
      */
     public static Typeface createFromAsset(AssetManager mgr, String path) {
+        if (sFallbackFonts != null) {
+            FontFamily fontFamily = new FontFamily();
+            if (fontFamily.addFontFromAsset(mgr, path)) {
+                FontFamily[] families = { fontFamily };
+                return createFromFamiliesWithDefault(families);
+            } else {
+                return null;
+            }
+        }
         return new Typeface(nativeCreateFromAsset(mgr, path));
     }
 
@@ -185,7 +197,7 @@
      * @return The new typeface.
      */
     public static Typeface createFromFile(File path) {
-        return new Typeface(nativeCreateFromFile(path.getAbsolutePath()));
+        return createFromFile(path.getAbsolutePath());
     }
 
     /**
@@ -195,6 +207,15 @@
      * @return The new typeface.
      */
     public static Typeface createFromFile(String path) {
+        if (sFallbackFonts != null) {
+            FontFamily fontFamily = new FontFamily();
+            if (fontFamily.addFont(path)) {
+                FontFamily[] families = { fontFamily };
+                return createFromFamiliesWithDefault(families);
+            } else {
+                return null;
+            }
+        }
         return new Typeface(nativeCreateFromFile(path));
     }
 
@@ -242,17 +263,23 @@
 
     private static FontFamily makeFamilyFromParsed(FontListParser.Family family) {
         // TODO: expand to handle attributes like lang and variant
-        FontFamily fontFamily = new FontFamily();
+        FontFamily fontFamily = new FontFamily(family.lang, family.variant);
         for (String fontFile : family.fontFiles) {
-            fontFamily.addFont(new File(fontFile));
+            fontFamily.addFont(fontFile);
         }
         return fontFamily;
     }
 
-    static {
+    /*
+     * (non-Javadoc)
+     *
+     * This should only be called once, from the static class initializer block.
+     */
+    private static void init() {
         // Load font config and initialize Minikin state
-        String systemConfigFilename = "/system/etc/system_fonts.xml";
-        String configFilename = "/system/etc/fallback_fonts.xml";
+        File systemFontConfigLocation = getSystemFontConfigLocation();
+        File systemConfigFilename = new File(systemFontConfigLocation, SYSTEM_FONTS_CONFIG);
+        File configFilename = new File(systemFontConfigLocation, FALLBACK_FONTS_CONFIG);
         try {
             // TODO: throws an exception non-Minikin builds, to fail early;
             // remove when Minikin-only
@@ -301,7 +328,10 @@
         } catch (XmlPullParserException e) {
             Log.e(TAG, "XML parse exception for " + configFilename);
         }
+    }
 
+    static {
+        init();
         // Set up defaults and typefaces exposed in public API
         DEFAULT         = create((String) null, 0);
         DEFAULT_BOLD    = create((String) null, Typeface.BOLD);
@@ -315,6 +345,11 @@
             create((String) null, Typeface.ITALIC),
             create((String) null, Typeface.BOLD_ITALIC),
         };
+
+    }
+
+    private static File getSystemFontConfigLocation() {
+        return new File("/system/etc/");
     }
 
     @Override
diff --git a/graphics/java/android/graphics/drawable/AnimatedRotateDrawable.java b/graphics/java/android/graphics/drawable/AnimatedRotateDrawable.java
index a37ceef..e8024f7 100644
--- a/graphics/java/android/graphics/drawable/AnimatedRotateDrawable.java
+++ b/graphics/java/android/graphics/drawable/AnimatedRotateDrawable.java
@@ -260,7 +260,7 @@
                 continue;
             }
 
-            if ((drawable = Drawable.createFromXmlInnerThemed(r, parser, attrs, theme)) == null) {
+            if ((drawable = Drawable.createFromXmlInner(r, parser, attrs, theme)) == null) {
                 Log.w("drawable", "Bad element under <animated-rotate>: "
                         + parser .getName());
             }
diff --git a/graphics/java/android/graphics/drawable/AnimatedStateListDrawable.java b/graphics/java/android/graphics/drawable/AnimatedStateListDrawable.java
index 42872e9..a5a074c 100644
--- a/graphics/java/android/graphics/drawable/AnimatedStateListDrawable.java
+++ b/graphics/java/android/graphics/drawable/AnimatedStateListDrawable.java
@@ -301,7 +301,7 @@
                                 + ": <item> tag requires a 'drawable' attribute or "
                                 + "child tag defining a drawable");
             }
-            dr = Drawable.createFromXmlInnerThemed(r, parser, attrs, theme);
+            dr = Drawable.createFromXmlInner(r, parser, attrs, theme);
         }
 
         final AnimationDrawable anim;
@@ -355,7 +355,7 @@
                                 + ": <item> tag requires a 'drawable' attribute or "
                                 + "child tag defining a drawable");
             }
-            dr = Drawable.createFromXmlInnerThemed(r, parser, attrs, theme);
+            dr = Drawable.createFromXmlInner(r, parser, attrs, theme);
         }
 
         return mState.addStateSet(states, dr, keyframeId);
diff --git a/graphics/java/android/graphics/drawable/AnimationDrawable.java b/graphics/java/android/graphics/drawable/AnimationDrawable.java
index da4bc10..0ee253a 100644
--- a/graphics/java/android/graphics/drawable/AnimationDrawable.java
+++ b/graphics/java/android/graphics/drawable/AnimationDrawable.java
@@ -293,7 +293,7 @@
                             ": <item> tag requires a 'drawable' attribute or child tag" +
                             " defining a drawable");
                 }
-                dr = Drawable.createFromXmlInnerThemed(r, parser, attrs, theme);
+                dr = Drawable.createFromXmlInner(r, parser, attrs, theme);
             }
             
             mAnimationState.addFrame(dr, duration);
diff --git a/graphics/java/android/graphics/drawable/ClipDrawable.java b/graphics/java/android/graphics/drawable/ClipDrawable.java
index 3dbd235..3ac9972 100644
--- a/graphics/java/android/graphics/drawable/ClipDrawable.java
+++ b/graphics/java/android/graphics/drawable/ClipDrawable.java
@@ -95,7 +95,7 @@
             if (type != XmlPullParser.START_TAG) {
                 continue;
             }
-            dr = Drawable.createFromXmlInnerThemed(r, parser, attrs, theme);
+            dr = Drawable.createFromXmlInner(r, parser, attrs, theme);
         }
 
         if (dr == null) {
diff --git a/graphics/java/android/graphics/drawable/Drawable.java b/graphics/java/android/graphics/drawable/Drawable.java
index cc2a595..f29b9f0 100644
--- a/graphics/java/android/graphics/drawable/Drawable.java
+++ b/graphics/java/android/graphics/drawable/Drawable.java
@@ -892,13 +892,9 @@
      * Create a drawable from an inputstream
      */
     public static Drawable createFromStream(InputStream is, String srcName) {
-        return createFromStreamThemed(is, srcName, null);
-    }
-
-    public static Drawable createFromStreamThemed(InputStream is, String srcName, Theme theme) {
         Trace.traceBegin(Trace.TRACE_TAG_RESOURCES, srcName != null ? srcName : "Unknown drawable");
         try {
-            return createFromResourceStreamThemed(null, null, is, srcName, theme);
+            return createFromResourceStream(null, null, is, srcName);
         } finally {
             Trace.traceEnd(Trace.TRACE_TAG_RESOURCES);
         }
@@ -910,14 +906,9 @@
      */
     public static Drawable createFromResourceStream(Resources res, TypedValue value,
             InputStream is, String srcName) {
-        return createFromResourceStreamThemed(res, value, is, srcName, null);
-    }
-
-    public static Drawable createFromResourceStreamThemed(Resources res, TypedValue value,
-            InputStream is, String srcName, Theme theme) {
         Trace.traceBegin(Trace.TRACE_TAG_RESOURCES, srcName != null ? srcName : "Unknown drawable");
         try {
-            return createFromResourceStreamThemed(res, value, is, srcName, null, theme);
+            return createFromResourceStream(res, value, is, srcName);
         } finally {
             Trace.traceEnd(Trace.TRACE_TAG_RESOURCES);
         }
@@ -929,11 +920,6 @@
      */
     public static Drawable createFromResourceStream(Resources res, TypedValue value,
             InputStream is, String srcName, BitmapFactory.Options opts) {
-        return createFromResourceStreamThemed(res, value, is, srcName, opts, null);
-    }
-
-    public static Drawable createFromResourceStreamThemed(Resources res, TypedValue value,
-            InputStream is, String srcName, BitmapFactory.Options opts, Theme theme) {
         if (is == null) {
             return null;
         }
@@ -981,15 +967,15 @@
      */
     public static Drawable createFromXml(Resources r, XmlPullParser parser)
             throws XmlPullParserException, IOException {
-        return createFromXmlThemed(r, parser, null);
+        return createFromXml(r, parser, null);
     }
 
     /**
-     * Create a themed drawable from an XML document. For more information on
-     * how to create resources in XML, see
+     * Create a drawable from an XML document using an optional {@link Theme}.
+     * For more information on how to create resources in XML, see
      * <a href="{@docRoot}guide/topics/resources/drawable-resource.html">Drawable Resources</a>.
      */
-    public static Drawable createFromXmlThemed(Resources r, XmlPullParser parser, Theme theme)
+    public static Drawable createFromXml(Resources r, XmlPullParser parser, Theme theme)
             throws XmlPullParserException, IOException {
         AttributeSet attrs = Xml.asAttributeSet(parser);
 
@@ -1003,7 +989,7 @@
             throw new XmlPullParserException("No start tag found");
         }
 
-        Drawable drawable = createFromXmlInnerThemed(r, parser, attrs, theme);
+        Drawable drawable = createFromXmlInner(r, parser, attrs, theme);
 
         if (drawable == null) {
             throw new RuntimeException("Unknown initial tag: " + parser.getName());
@@ -1019,16 +1005,17 @@
      */
     public static Drawable createFromXmlInner(Resources r, XmlPullParser parser, AttributeSet attrs)
             throws XmlPullParserException, IOException {
-        return createFromXmlInnerThemed(r, parser, attrs, null);
+        return createFromXmlInner(r, parser, attrs, null);
     }
 
     /**
-     * Create a themed drawable from inside an XML document. Called on a parser
-     * positioned at a tag in an XML document, tries to create a Drawable from
-     * that tag. Returns null if the tag is not a valid drawable.
+     * Create a drawable from inside an XML document using an optional
+     * {@link Theme}. Called on a parser positioned at a tag in an XML
+     * document, tries to create a Drawable from that tag. Returns {@code null}
+     * if the tag is not a valid drawable.
      */
-    public static Drawable createFromXmlInnerThemed(Resources r, XmlPullParser parser,
-            AttributeSet attrs, Theme theme) throws XmlPullParserException, IOException {
+    public static Drawable createFromXmlInner(Resources r, XmlPullParser parser, AttributeSet attrs,
+            Theme theme) throws XmlPullParserException, IOException {
         final Drawable drawable;
 
         final String name = parser.getName();
diff --git a/graphics/java/android/graphics/drawable/GradientDrawable.java b/graphics/java/android/graphics/drawable/GradientDrawable.java
index f82acc3..1512da5 100644
--- a/graphics/java/android/graphics/drawable/GradientDrawable.java
+++ b/graphics/java/android/graphics/drawable/GradientDrawable.java
@@ -612,7 +612,9 @@
             case LINE: {
                 RectF r = mRect;
                 float y = r.centerY();
-                canvas.drawLine(r.left, y, r.right, y, mStrokePaint);
+                if (haveStroke) {
+                    canvas.drawLine(r.left, y, r.right, y, mStrokePaint);
+                }
                 break;
             }
             case RING:
@@ -1153,6 +1155,10 @@
         // Extract the theme attributes, if any.
         st.mAttrPadding = a.extractThemeAttrs();
 
+        if (st.mPadding == null) {
+            st.mPadding = new Rect();
+        }
+
         final Rect pad = st.mPadding;
         pad.set(a.getDimensionPixelOffset(R.styleable.GradientDrawablePadding_left, pad.left),
                 a.getDimensionPixelOffset(R.styleable.GradientDrawablePadding_top, pad.top),
@@ -1424,12 +1430,10 @@
     }
 
     final static class GradientState extends ConstantState {
-        public final Rect mPadding = new Rect();
-
         public int mChangingConfigurations;
         public int mShape = RECTANGLE;
         public int mGradient = LINEAR_GRADIENT;
-        public int mAngle;
+        public int mAngle = 0;
         public Orientation mOrientation;
         public ColorStateList mColorStateList;
         public ColorStateList mStrokeColorStateList;
@@ -1437,11 +1441,12 @@
         public int[] mTempColors; // no need to copy
         public float[] mTempPositions; // no need to copy
         public float[] mPositions;
-        public int mStrokeWidth = -1;   // if >= 0 use stroking.
-        public float mStrokeDashWidth;
-        public float mStrokeDashGap;
-        public float mRadius;    // use this if mRadiusArray is null
-        public float[] mRadiusArray;
+        public int mStrokeWidth = -1; // if >= 0 use stroking.
+        public float mStrokeDashWidth = 0.0f;
+        public float mStrokeDashGap = 0.0f;
+        public float mRadius = 0.0f; // use this if mRadiusArray is null
+        public float[] mRadiusArray = null;
+        public Rect mPadding = null;
         public int mWidth = -1;
         public int mHeight = -1;
         public float mInnerRadiusRatio = DEFAULT_INNER_RADIUS_RATIO;
@@ -1491,7 +1496,7 @@
                 mRadiusArray = state.mRadiusArray.clone();
             }
             if (state.mPadding != null) {
-                mPadding.set(state.mPadding);
+                mPadding = new Rect(state.mPadding);
             }
             mWidth = state.mWidth;
             mHeight = state.mHeight;
diff --git a/graphics/java/android/graphics/drawable/InsetDrawable.java b/graphics/java/android/graphics/drawable/InsetDrawable.java
index 2fa5929..9e0ab86 100644
--- a/graphics/java/android/graphics/drawable/InsetDrawable.java
+++ b/graphics/java/android/graphics/drawable/InsetDrawable.java
@@ -111,7 +111,7 @@
                         + ": <inset> tag requires a 'drawable' attribute or "
                         + "child tag defining a drawable");
             }
-            dr = Drawable.createFromXmlInnerThemed(r, parser, attrs, theme);
+            dr = Drawable.createFromXmlInner(r, parser, attrs, theme);
         }
 
         if (dr == null) {
diff --git a/graphics/java/android/graphics/drawable/LayerDrawable.java b/graphics/java/android/graphics/drawable/LayerDrawable.java
index 2e47d3a..27f0a9d 100644
--- a/graphics/java/android/graphics/drawable/LayerDrawable.java
+++ b/graphics/java/android/graphics/drawable/LayerDrawable.java
@@ -211,7 +211,7 @@
                             + ": <item> tag requires a 'drawable' attribute or "
                             + "child tag defining a drawable");
                 }
-                dr = Drawable.createFromXmlInnerThemed(r, parser, attrs, theme);
+                dr = Drawable.createFromXmlInner(r, parser, attrs, theme);
             }
 
             addLayer(dr, themeAttrs, id, left, top, right, bottom);
@@ -304,7 +304,7 @@
      * @param right The right padding of the new layer.
      * @param bottom The bottom padding of the new layer.
      */
-    private void addLayer(Drawable layer, int[] themeAttrs, int id, int left, int top, int right,
+    void addLayer(Drawable layer, int[] themeAttrs, int id, int left, int top, int right,
             int bottom) {
         final LayerState st = mLayerState;
         final int N = st.mChildren != null ? st.mChildren.length : 0;
diff --git a/graphics/java/android/graphics/drawable/LevelListDrawable.java b/graphics/java/android/graphics/drawable/LevelListDrawable.java
index 9f6c0ad..7271b0e 100644
--- a/graphics/java/android/graphics/drawable/LevelListDrawable.java
+++ b/graphics/java/android/graphics/drawable/LevelListDrawable.java
@@ -134,7 +134,7 @@
                                     + ": <item> tag requires a 'drawable' attribute or "
                                     + "child tag defining a drawable");
                 }
-                dr = Drawable.createFromXmlInnerThemed(r, parser, attrs, theme);
+                dr = Drawable.createFromXmlInner(r, parser, attrs, theme);
             }
 
             mLevelListState.addLevel(low, high, dr);
diff --git a/graphics/java/android/graphics/drawable/Ripple.java b/graphics/java/android/graphics/drawable/Ripple.java
index 24e8de6..ada741b 100644
--- a/graphics/java/android/graphics/drawable/Ripple.java
+++ b/graphics/java/android/graphics/drawable/Ripple.java
@@ -25,9 +25,10 @@
 import android.graphics.Paint;
 import android.graphics.Paint.Style;
 import android.graphics.Rect;
+import android.util.MathUtils;
 import android.view.HardwareCanvas;
 import android.view.RenderNodeAnimator;
-import android.view.animation.AccelerateInterpolator;
+import android.view.animation.LinearInterpolator;
 
 import java.util.ArrayList;
 
@@ -35,7 +36,7 @@
  * Draws a Quantum Paper ripple.
  */
 class Ripple {
-    private static final TimeInterpolator INTERPOLATOR = new AccelerateInterpolator();
+    private static final TimeInterpolator LINEAR_INTERPOLATOR = new LinearInterpolator();
 
     private static final float GLOBAL_SPEED = 1.0f;
     private static final float WAVE_TOUCH_DOWN_ACCELERATION = 512.0f * GLOBAL_SPEED;
@@ -47,17 +48,23 @@
     private final ArrayList<RenderNodeAnimator> mRunningAnimations = new ArrayList<>();
     private final ArrayList<RenderNodeAnimator> mPendingAnimations = new ArrayList<>();
 
-    private final Drawable mOwner;
+    private final RippleDrawable mOwner;
 
     /** Bounds used for computing max radius. */
     private final Rect mBounds;
 
     /** Full-opacity color for drawing this ripple. */
-    private final int mColor;
+    private int mColor;
 
     /** Maximum ripple radius. */
     private float mOuterRadius;
 
+    /** Screen density used to adjust pixel-based velocities. */
+    private float mDensity;
+
+    private float mStartingX;
+    private float mStartingY;
+
     // Hardware rendering properties.
     private CanvasProperty<Paint> mPropPaint;
     private CanvasProperty<Float> mPropRadius;
@@ -78,13 +85,13 @@
     // Software rendering properties.
     private float mOuterOpacity = 0;
     private float mOpacity = 1;
-    private float mRadius = 0;
     private float mOuterX;
     private float mOuterY;
-    private float mX;
-    private float mY;
 
-    private boolean mFinished;
+    // Values used to tween between the start and end positions.
+    private float mTweenRadius = 0;
+    private float mTweenX = 0;
+    private float mTweenY = 0;
 
     /** Whether we should be drawing hardware animations. */
     private boolean mHardwareAnimating;
@@ -92,28 +99,42 @@
     /** Whether we can use hardware acceleration for the exit animation. */
     private boolean mCanUseHardware;
 
+    /** Whether we have an explicit maximum radius. */
+    private boolean mHasMaxRadius;
+
     /**
      * Creates a new ripple.
      */
-    public Ripple(Drawable owner, Rect bounds, int color) {
+    public Ripple(RippleDrawable owner, Rect bounds, float startingX, float startingY) {
         mOwner = owner;
         mBounds = bounds;
+        mStartingX = startingX;
+        mStartingY = startingY;
+    }
+
+    public void setup(int maxRadius, int color, float density) {
         mColor = color | 0xFF000000;
 
-        final float halfWidth = bounds.width() / 2.0f;
-        final float halfHeight = bounds.height() / 2.0f;
-        mOuterRadius = (float) Math.sqrt(halfWidth * halfWidth + halfHeight * halfHeight);
+        if (maxRadius != RippleDrawable.RADIUS_AUTO) {
+            mHasMaxRadius = true;
+            mOuterRadius = maxRadius;
+        } else {
+            final float halfWidth = mBounds.width() / 2.0f;
+            final float halfHeight = mBounds.height() / 2.0f;
+            mOuterRadius = (float) Math.sqrt(halfWidth * halfWidth + halfHeight * halfHeight);
+        }
+
         mOuterX = 0;
         mOuterY = 0;
+        mDensity = density;
     }
 
-    public void setRadius(float r) {
-        mRadius = r;
-        invalidateSelf();
-    }
-
-    public float getRadius() {
-        return mRadius;
+    public void onHotspotBoundsChanged() {
+        if (!mHasMaxRadius) {
+            final float halfWidth = mBounds.width() / 2.0f;
+            final float halfHeight = mBounds.height() / 2.0f;
+            mOuterRadius = (float) Math.sqrt(halfWidth * halfWidth + halfHeight * halfHeight);
+        }
     }
 
     public void setOpacity(float a) {
@@ -134,29 +155,31 @@
         return mOuterOpacity;
     }
 
-    public void setX(float x) {
-        mX = x;
+    public void setRadiusGravity(float r) {
+        mTweenRadius = r;
         invalidateSelf();
     }
 
-    public float getX() {
-        return mX;
+    public float getRadiusGravity() {
+        return mTweenRadius;
     }
 
-    public void setY(float y) {
-        mY = y;
+    public void setXGravity(float x) {
+        mTweenX = x;
         invalidateSelf();
     }
 
-    public float getY() {
-        return mY;
+    public float getXGravity() {
+        return mTweenX;
     }
 
-    /**
-     * Returns whether this ripple has finished exiting.
-     */
-    public boolean isFinished() {
-        return mFinished;
+    public void setYGravity(float y) {
+        mTweenY = y;
+        invalidateSelf();
+    }
+
+    public float getYGravity() {
+        return mTweenY;
     }
 
     /**
@@ -204,28 +227,27 @@
     }
 
     private boolean drawSoftware(Canvas c, Paint p) {
-        final float radius = mRadius;
-        final float opacity = mOpacity;
-        final float outerOpacity = mOuterOpacity;
+        boolean hasContent = false;
 
         // Cache the paint alpha so we can restore it later.
         final int paintAlpha = p.getAlpha();
-        final int alpha = (int) (255 * opacity + 0.5f);
-        final int outerAlpha = (int) (255 * outerOpacity + 0.5f);
 
-        boolean hasContent = false;
-
-        if (outerAlpha > 0 && alpha > 0) {
-            p.setAlpha(Math.min(alpha, outerAlpha));
+        final int outerAlpha = (int) (255 * mOuterOpacity + 0.5f);
+        if (outerAlpha > 0 && mOuterRadius > 0) {
+            p.setAlpha(outerAlpha);
             p.setStyle(Style.FILL);
             c.drawCircle(mOuterX, mOuterY, mOuterRadius, p);
             hasContent = true;
         }
 
-        if (opacity > 0 && radius > 0) {
+        final int alpha = (int) (255 * mOpacity + 0.5f);
+        final float radius = MathUtils.lerp(0, mOuterRadius, mTweenRadius);
+        if (alpha > 0 && radius > 0) {
+            final float x = MathUtils.lerp(mStartingX - mBounds.exactCenterX(), mOuterX, mTweenX);
+            final float y = MathUtils.lerp(mStartingY - mBounds.exactCenterY(), mOuterY, mTweenY);
             p.setAlpha(alpha);
             p.setStyle(Style.FILL);
-            c.drawCircle(mX, mY, radius, p);
+            c.drawCircle(x, y, radius, p);
             hasContent = true;
         }
 
@@ -235,45 +257,49 @@
     }
 
     /**
-     * Returns the maximum bounds for this ripple.
+     * Returns the maximum bounds of the ripple relative to the ripple center.
      */
     public void getBounds(Rect bounds) {
         final int outerX = (int) mOuterX;
         final int outerY = (int) mOuterY;
         final int r = (int) mOuterRadius;
         bounds.set(outerX - r, outerY - r, outerX + r, outerY + r);
-
-        final int x = (int) mX;
-        final int y = (int) mY;
-        bounds.union(x - r, y - r, x + r, y + r);
     }
 
     /**
-     * Starts the enter animation at the specified absolute coordinates.
+     * Specifies the starting position relative to the drawable bounds. No-op if
+     * the ripple has already entered.
      */
-    public void enter(float x, float y) {
-        mX = x - mBounds.exactCenterX();
-        mY = y - mBounds.exactCenterY();
+    public void move(float x, float y) {
+        mStartingX = x;
+        mStartingY = y;
+    }
 
+    /**
+     * Starts the enter animation.
+     */
+    public void enter() {
         final int radiusDuration = (int)
-                (1000 * Math.sqrt(mOuterRadius / WAVE_TOUCH_DOWN_ACCELERATION) + 0.5);
+                (1000 * Math.sqrt(mOuterRadius / WAVE_TOUCH_DOWN_ACCELERATION * mDensity) + 0.5);
         final int outerDuration = (int) (1000 * 1.0f / WAVE_OUTER_OPACITY_VELOCITY);
 
-        final ObjectAnimator radius = ObjectAnimator.ofFloat(this, "radius", 0, mOuterRadius);
+        final ObjectAnimator radius = ObjectAnimator.ofFloat(this, "radiusGravity", 1);
         radius.setAutoCancel(true);
         radius.setDuration(radiusDuration);
+        radius.setInterpolator(LINEAR_INTERPOLATOR);
 
-        final ObjectAnimator cX = ObjectAnimator.ofFloat(this, "x", mOuterX);
+        final ObjectAnimator cX = ObjectAnimator.ofFloat(this, "xGravity", 1);
         cX.setAutoCancel(true);
         cX.setDuration(radiusDuration);
 
-        final ObjectAnimator cY = ObjectAnimator.ofFloat(this, "y", mOuterY);
+        final ObjectAnimator cY = ObjectAnimator.ofFloat(this, "yGravity", 1);
         cY.setAutoCancel(true);
         cY.setDuration(radiusDuration);
 
         final ObjectAnimator outer = ObjectAnimator.ofFloat(this, "outerOpacity", 0, 1);
         outer.setAutoCancel(true);
         outer.setDuration(outerDuration);
+        outer.setInterpolator(LINEAR_INTERPOLATOR);
 
         mAnimRadius = radius;
         mAnimOuterOpacity = outer;
@@ -295,15 +321,16 @@
     public void exit() {
         cancelSoftwareAnimations();
 
+        final float radius = MathUtils.lerp(0, mOuterRadius, mTweenRadius);
         final float remaining;
         if (mAnimRadius != null && mAnimRadius.isRunning()) {
-            remaining = mOuterRadius - mRadius;
+            remaining = mOuterRadius - radius;
         } else {
             remaining = mOuterRadius;
         }
 
         final int radiusDuration = (int) (1000 * Math.sqrt(remaining / (WAVE_TOUCH_UP_ACCELERATION
-                + WAVE_TOUCH_DOWN_ACCELERATION)) + 0.5);
+                + WAVE_TOUCH_DOWN_ACCELERATION) * mDensity) + 0.5);
         final int opacityDuration = (int) (1000 * mOpacity / WAVE_OPACITY_DECAY_VELOCITY + 0.5f);
 
         // Determine at what time the inner and outer opacity intersect.
@@ -325,6 +352,8 @@
             int inflectionOpacity) {
         mPendingAnimations.clear();
 
+        final float startX = MathUtils.lerp(mStartingX - mBounds.exactCenterX(), mOuterX, mTweenX);
+        final float startY = MathUtils.lerp(mStartingY - mBounds.exactCenterY(), mOuterY, mTweenY);
         final Paint outerPaint = new Paint();
         outerPaint.setAntiAlias(true);
         outerPaint.setColor(mColor);
@@ -335,58 +364,69 @@
         mPropOuterX = CanvasProperty.createFloat(mOuterX);
         mPropOuterY = CanvasProperty.createFloat(mOuterY);
 
+        final float startRadius = MathUtils.lerp(0, mOuterRadius, mTweenRadius);
         final Paint paint = new Paint();
         paint.setAntiAlias(true);
         paint.setColor(mColor);
         paint.setAlpha((int) (255 * mOpacity + 0.5f));
         paint.setStyle(Style.FILL);
         mPropPaint = CanvasProperty.createPaint(paint);
-        mPropRadius = CanvasProperty.createFloat(mRadius);
-        mPropX = CanvasProperty.createFloat(mX);
-        mPropY = CanvasProperty.createFloat(mY);
+        mPropRadius = CanvasProperty.createFloat(startRadius);
+        mPropX = CanvasProperty.createFloat(startX);
+        mPropY = CanvasProperty.createFloat(startY);
 
-        final RenderNodeAnimator radius = new RenderNodeAnimator(mPropRadius, mOuterRadius);
-        radius.setDuration(radiusDuration);
+        final RenderNodeAnimator radiusAnim = new RenderNodeAnimator(mPropRadius, mOuterRadius);
+        radiusAnim.setDuration(radiusDuration);
+        radiusAnim.setInterpolator(LINEAR_INTERPOLATOR);
 
-        final RenderNodeAnimator x = new RenderNodeAnimator(mPropX, mOuterX);
-        x.setDuration(radiusDuration);
+        final RenderNodeAnimator xAnim = new RenderNodeAnimator(mPropX, mOuterX);
+        xAnim.setDuration(radiusDuration);
+        xAnim.setInterpolator(LINEAR_INTERPOLATOR);
 
-        final RenderNodeAnimator y = new RenderNodeAnimator(mPropY, mOuterY);
-        y.setDuration(radiusDuration);
+        final RenderNodeAnimator yAnim = new RenderNodeAnimator(mPropY, mOuterY);
+        yAnim.setDuration(radiusDuration);
+        yAnim.setInterpolator(LINEAR_INTERPOLATOR);
 
-        final RenderNodeAnimator opacity = new RenderNodeAnimator(mPropPaint,
+        final RenderNodeAnimator opacityAnim = new RenderNodeAnimator(mPropPaint,
                 RenderNodeAnimator.PAINT_ALPHA, 0);
-        opacity.setDuration(opacityDuration);
-        opacity.addListener(mAnimationListener);
+        opacityAnim.setDuration(opacityDuration);
+        opacityAnim.setInterpolator(LINEAR_INTERPOLATOR);
 
-        final RenderNodeAnimator outerOpacity;
+        final RenderNodeAnimator outerOpacityAnim;
         if (outerInflection > 0) {
             // Outer opacity continues to increase for a bit.
-            outerOpacity = new RenderNodeAnimator(
+            outerOpacityAnim = new RenderNodeAnimator(
                     mPropOuterPaint, RenderNodeAnimator.PAINT_ALPHA, inflectionOpacity);
-            outerOpacity.setDuration(outerInflection);
+            outerOpacityAnim.setDuration(outerInflection);
+            outerOpacityAnim.setInterpolator(LINEAR_INTERPOLATOR);
 
             // Chain the outer opacity exit animation.
             final int outerDuration = opacityDuration - outerInflection;
             if (outerDuration > 0) {
-                final RenderNodeAnimator outerFadeOut = new RenderNodeAnimator(
+                final RenderNodeAnimator outerFadeOutAnim = new RenderNodeAnimator(
                         mPropOuterPaint, RenderNodeAnimator.PAINT_ALPHA, 0);
-                outerFadeOut.setDuration(outerDuration);
-                outerFadeOut.setStartDelay(outerInflection);
+                outerFadeOutAnim.setDuration(outerDuration);
+                outerFadeOutAnim.setInterpolator(LINEAR_INTERPOLATOR);
+                outerFadeOutAnim.setStartDelay(outerInflection);
+                outerFadeOutAnim.addListener(mAnimationListener);
 
-                mPendingAnimations.add(outerFadeOut);
+                mPendingAnimations.add(outerFadeOutAnim);
+            } else {
+                outerOpacityAnim.addListener(mAnimationListener);
             }
         } else {
-            outerOpacity = new RenderNodeAnimator(
+            outerOpacityAnim = new RenderNodeAnimator(
                     mPropOuterPaint, RenderNodeAnimator.PAINT_ALPHA, 0);
-            outerOpacity.setDuration(opacityDuration);
+            outerOpacityAnim.setInterpolator(LINEAR_INTERPOLATOR);
+            outerOpacityAnim.setDuration(opacityDuration);
+            outerOpacityAnim.addListener(mAnimationListener);
         }
 
-        mPendingAnimations.add(radius);
-        mPendingAnimations.add(opacity);
-        mPendingAnimations.add(outerOpacity);
-        mPendingAnimations.add(x);
-        mPendingAnimations.add(y);
+        mPendingAnimations.add(radiusAnim);
+        mPendingAnimations.add(opacityAnim);
+        mPendingAnimations.add(outerOpacityAnim);
+        mPendingAnimations.add(xAnim);
+        mPendingAnimations.add(yAnim);
 
         mHardwareAnimating = true;
 
@@ -394,65 +434,80 @@
     }
 
     private void exitSoftware(int radiusDuration, int opacityDuration, int outerInflection,
-            float inflectionOpacity) {
-        final ObjectAnimator radius = ObjectAnimator.ofFloat(this, "radius", mOuterRadius);
-        radius.setAutoCancel(true);
-        radius.setDuration(radiusDuration);
+            int inflectionOpacity) {
+        final ObjectAnimator radiusAnim = ObjectAnimator.ofFloat(this, "radiusGravity", 1);
+        radiusAnim.setAutoCancel(true);
+        radiusAnim.setDuration(radiusDuration);
+        radiusAnim.setInterpolator(LINEAR_INTERPOLATOR);
 
-        final ObjectAnimator x = ObjectAnimator.ofFloat(this, "x", mOuterX);
-        x.setAutoCancel(true);
-        x.setDuration(radiusDuration);
+        final ObjectAnimator xAnim = ObjectAnimator.ofFloat(this, "xGravity", 1);
+        xAnim.setAutoCancel(true);
+        xAnim.setDuration(radiusDuration);
+        xAnim.setInterpolator(LINEAR_INTERPOLATOR);
 
-        final ObjectAnimator y = ObjectAnimator.ofFloat(this, "y", mOuterY);
-        y.setAutoCancel(true);
-        y.setDuration(radiusDuration);
+        final ObjectAnimator yAnim = ObjectAnimator.ofFloat(this, "yGravity", 1);
+        yAnim.setAutoCancel(true);
+        yAnim.setDuration(radiusDuration);
+        yAnim.setInterpolator(LINEAR_INTERPOLATOR);
 
-        final ObjectAnimator opacity = ObjectAnimator.ofFloat(this, "opacity", 0);
-        opacity.setAutoCancel(true);
-        opacity.setDuration(opacityDuration);
-        opacity.addListener(mAnimationListener);
+        final ObjectAnimator opacityAnim = ObjectAnimator.ofFloat(this, "opacity", 0);
+        opacityAnim.setAutoCancel(true);
+        opacityAnim.setDuration(opacityDuration);
+        opacityAnim.setInterpolator(LINEAR_INTERPOLATOR);
 
-        final ObjectAnimator outerOpacity;
+        final ObjectAnimator outerOpacityAnim;
         if (outerInflection > 0) {
             // Outer opacity continues to increase for a bit.
-            outerOpacity = ObjectAnimator.ofFloat(this, "outerOpacity", inflectionOpacity);
-            outerOpacity.setDuration(outerInflection);
+            outerOpacityAnim = ObjectAnimator.ofFloat(this,
+                    "outerOpacity", inflectionOpacity / 255.0f);
+            outerOpacityAnim.setAutoCancel(true);
+            outerOpacityAnim.setDuration(outerInflection);
+            outerOpacityAnim.setInterpolator(LINEAR_INTERPOLATOR);
 
             // Chain the outer opacity exit animation.
             final int outerDuration = opacityDuration - outerInflection;
-            outerOpacity.addListener(new AnimatorListenerAdapter() {
-                @Override
-                public void onAnimationEnd(Animator animation) {
-                    final ObjectAnimator outerFadeOut = ObjectAnimator.ofFloat(Ripple.this,
-                            "outerOpacity", 0);
-                    outerFadeOut.setDuration(outerDuration);
+            if (outerDuration > 0) {
+                outerOpacityAnim.addListener(new AnimatorListenerAdapter() {
+                    @Override
+                    public void onAnimationEnd(Animator animation) {
+                        final ObjectAnimator outerFadeOutAnim = ObjectAnimator.ofFloat(Ripple.this,
+                                "outerOpacity", 0);
+                        outerFadeOutAnim.setAutoCancel(true);
+                        outerFadeOutAnim.setDuration(outerDuration);
+                        outerFadeOutAnim.setInterpolator(LINEAR_INTERPOLATOR);
+                        outerFadeOutAnim.addListener(mAnimationListener);
 
-                    mAnimOuterOpacity = outerFadeOut;
+                        mAnimOuterOpacity = outerFadeOutAnim;
 
-                    outerFadeOut.start();
-                }
+                        outerFadeOutAnim.start();
+                    }
 
-                @Override
-                public void onAnimationCancel(Animator animation) {
-                    animation.removeListener(this);
-                }
-            });
+                    @Override
+                    public void onAnimationCancel(Animator animation) {
+                        animation.removeListener(this);
+                    }
+                });
+            } else {
+                outerOpacityAnim.addListener(mAnimationListener);
+            }
         } else {
-            outerOpacity = ObjectAnimator.ofFloat(this, "outerOpacity", 0);
-            outerOpacity.setDuration(opacityDuration);
+            outerOpacityAnim = ObjectAnimator.ofFloat(this, "outerOpacity", 0);
+            outerOpacityAnim.setAutoCancel(true);
+            outerOpacityAnim.setDuration(opacityDuration);
+            outerOpacityAnim.addListener(mAnimationListener);
         }
 
-        mAnimRadius = radius;
-        mAnimOpacity = opacity;
-        mAnimOuterOpacity = outerOpacity;
-        mAnimX = opacity;
-        mAnimY = opacity;
+        mAnimRadius = radiusAnim;
+        mAnimOpacity = opacityAnim;
+        mAnimOuterOpacity = outerOpacityAnim;
+        mAnimX = opacityAnim;
+        mAnimY = opacityAnim;
 
-        radius.start();
-        opacity.start();
-        outerOpacity.start();
-        x.start();
-        y.start();
+        radiusAnim.start();
+        opacityAnim.start();
+        outerOpacityAnim.start();
+        xAnim.start();
+        yAnim.start();
     }
 
     /**
@@ -498,6 +553,11 @@
         runningAnimations.clear();
     }
 
+    private void removeSelf() {
+        // The owner will invalidate itself.
+        mOwner.removeRipple(this);
+    }
+
     private void invalidateSelf() {
         mOwner.invalidateSelf();
     }
@@ -505,12 +565,7 @@
     private final AnimatorListenerAdapter mAnimationListener = new AnimatorListenerAdapter() {
         @Override
         public void onAnimationEnd(Animator animation) {
-            mFinished = true;
-        }
-
-        @Override
-        public void onAnimationCancel(Animator animation) {
-            mFinished = true;
+            removeSelf();
         }
     };
 }
diff --git a/graphics/java/android/graphics/drawable/RippleDrawable.java b/graphics/java/android/graphics/drawable/RippleDrawable.java
index 1bd7cac..9d7a8b6 100644
--- a/graphics/java/android/graphics/drawable/RippleDrawable.java
+++ b/graphics/java/android/graphics/drawable/RippleDrawable.java
@@ -25,17 +25,14 @@
 import android.graphics.ColorFilter;
 import android.graphics.Paint;
 import android.graphics.PixelFormat;
-import android.graphics.PointF;
 import android.graphics.PorterDuff.Mode;
 import android.graphics.PorterDuffXfermode;
 import android.graphics.Rect;
 import android.util.AttributeSet;
 import android.util.DisplayMetrics;
 import android.util.Log;
-import android.util.SparseArray;
 
 import com.android.internal.R;
-import com.android.org.bouncycastle.util.Arrays;
 
 import org.xmlpull.v1.XmlPullParser;
 import org.xmlpull.v1.XmlPullParserException;
@@ -71,10 +68,17 @@
 public class RippleDrawable extends LayerDrawable {
     private static final String LOG_TAG = RippleDrawable.class.getSimpleName();
     private static final PorterDuffXfermode DST_IN = new PorterDuffXfermode(Mode.DST_IN);
-    private static final PorterDuffXfermode DST_ATOP = new PorterDuffXfermode(Mode.DST_ATOP);
     private static final PorterDuffXfermode SRC_ATOP = new PorterDuffXfermode(Mode.SRC_ATOP);
     private static final PorterDuffXfermode SRC_OVER = new PorterDuffXfermode(Mode.SRC_OVER);
 
+    /**
+     * Constant for automatically determining the maximum ripple radius.
+     *
+     * @see #setMaxRadius(int)
+     * @hide
+     */
+    public static final int RADIUS_AUTO = -1;
+
     /** The maximum number of ripples supported. */
     private static final int MAX_RIPPLES = 10;
 
@@ -91,17 +95,8 @@
 
     private final RippleState mState;
 
-    /**
-     * Lazily-created map of pending hotspot locations. These may be modified by
-     * calls to {@link #setHotspot(float, float)}.
-     */
-    private SparseArray<PointF> mPendingHotspots;
-
-    /**
-     * Lazily-created map of active hotspot locations. These may be modified by
-     * calls to {@link #setHotspot(float, float)}.
-     */
-    private SparseArray<Ripple> mActiveHotspots;
+    /** The current hotspot. May be actively animating or pending entry. */
+    private Ripple mHotspot;
 
     /**
      * Lazily-created array of actively animating ripples. Inactive ripples are
@@ -122,18 +117,46 @@
     /** Whether bounds are being overridden. */
     private boolean mOverrideBounds;
 
+    /** Whether the hotspot is currently active (e.g. focused or pressed). */
+    private boolean mActive;
+
     RippleDrawable() {
+        this(null, null);
+    }
+
+    /**
+     * Creates a new ripple drawable with the specified content and mask
+     * drawables.
+     *
+     * @param content The content drawable, may be {@code null}
+     * @param mask The mask drawable, may be {@code null}
+     */
+    public RippleDrawable(Drawable content, Drawable mask) {
         this(new RippleState(null, null, null), null, null);
+
+        if (content != null) {
+            addLayer(content, null, 0, 0, 0, 0, 0);
+        }
+
+        if (mask != null) {
+            addLayer(content, null, android.R.id.mask, 0, 0, 0, 0);
+        }
+
+        ensurePadding();
     }
 
     @Override
     public void setAlpha(int alpha) {
-        
+        super.setAlpha(alpha);
+
+        // TODO: Should we support this?
     }
 
     @Override
     public void setColorFilter(ColorFilter cf) {
-        
+        super.setColorFilter(cf);
+
+        // TODO: Should we support this?
     }
 
     @Override
@@ -146,20 +169,18 @@
     protected boolean onStateChange(int[] stateSet) {
         super.onStateChange(stateSet);
 
-        final boolean pressed = Arrays.contains(stateSet, R.attr.state_pressed);
-        if (!pressed) {
-            removeHotspot(R.attr.state_pressed);
-        } else {
-            activateHotspot(R.attr.state_pressed);
+        boolean active = false;
+        final int N = stateSet.length;
+        for (int i = 0; i < N; i++) {
+            if (stateSet[i] == R.attr.state_focused
+                    || stateSet[i] == R.attr.state_pressed) {
+                active = true;
+                break;
+            }
         }
+        setActive(active);
 
-        final boolean focused = Arrays.contains(stateSet, R.attr.state_focused);
-        if (!focused) {
-            removeHotspot(R.attr.state_focused);
-        } else {
-            activateHotspot(R.attr.state_focused);
-        }
-
+        // Update the paint color. Only applicable when animated in software.
         if (mRipplePaint != null && mState.mTint != null) {
             final ColorStateList stateList = mState.mTint;
             final int newColor = stateList.getColorForState(stateSet, 0);
@@ -174,12 +195,25 @@
         return false;
     }
 
+    private void setActive(boolean active) {
+        if (mActive != active) {
+            mActive = active;
+
+            if (active) {
+                activateHotspot();
+            } else {
+                removeHotspot();
+            }
+        }
+    }
+
     @Override
     protected void onBoundsChange(Rect bounds) {
         super.onBoundsChange(bounds);
 
         if (!mOverrideBounds) {
             mHotspotBounds.set(bounds);
+            onHotspotBoundsChanged();
         }
 
         invalidateSelf();
@@ -272,7 +306,7 @@
     /**
      * Initializes the constant state from the values in the typed array.
      */
-    private void updateStateFromTypedArray(TypedArray a) {
+    private void updateStateFromTypedArray(TypedArray a) throws XmlPullParserException {
         final RippleState state = mState;
 
         // Extract the theme attributes, if any.
@@ -289,6 +323,12 @@
         }
 
         mState.mPinned = a.getBoolean(R.styleable.RippleDrawable_pinned, mState.mPinned);
+
+        // If we're not waiting on a theme, verify required attributes.
+        if (state.mTouchThemeAttrs == null && mState.mTint == null) {
+            throw new XmlPullParserException(a.getPositionDescription() +
+                    ": <ripple> requires a valid tint attribute");
+        }
     }
 
     /**
@@ -314,8 +354,13 @@
 
         final TypedArray a = t.resolveAttributes(state.mTouchThemeAttrs,
                 R.styleable.RippleDrawable);
-        updateStateFromTypedArray(a);
-        a.recycle();
+        try {
+            updateStateFromTypedArray(a);
+        } catch (XmlPullParserException e) {
+            throw new RuntimeException(e);
+        } finally {
+            a.recycle();
+        }
     }
 
     @Override
@@ -330,36 +375,15 @@
             y = mHotspotBounds.exactCenterY();
         }
 
-        // TODO: We should only have a single pending/active hotspot.
-        final int id = R.attr.state_pressed;
-        final int[] stateSet = getState();
-        if (!Arrays.contains(stateSet, id)) {
-            // The hotspot is not active, so just modify the pending location.
-            getOrCreatePendingHotspot(id).set(x, y);
-            return;
-        }
+        if (mHotspot == null) {
+            mHotspot = new Ripple(this, mHotspotBounds, x, y);
 
-        if (mAnimatingRipplesCount >= MAX_RIPPLES) {
-            // This should never happen unless the user is tapping like a maniac
-            // or there is a bug that's preventing ripples from being removed.
-            Log.d(LOG_TAG, "Max ripple count exceeded", new RuntimeException());
-            return;
+            if (mActive) {
+                activateHotspot();
+            }
+        } else {
+            mHotspot.move(x, y);
         }
-
-        if (mActiveHotspots == null) {
-            mActiveHotspots = new SparseArray<Ripple>();
-            mAnimatingRipples = new Ripple[MAX_RIPPLES];
-        }
-
-        final Ripple ripple = mActiveHotspots.get(id);
-        if (ripple != null) {
-            // The hotspot is active, but we can't move it because it's probably
-            // busy animating the center position.
-            return;
-        }
-
-        // The hotspot needs to be made active.
-        createActiveHotspot(id, x, y);
     }
 
     private boolean circleContains(Rect bounds, float x, float y) {
@@ -374,74 +398,44 @@
         return pointRadius < boundsRadius;
     }
 
-    private PointF getOrCreatePendingHotspot(int id) {
-        final PointF p;
-        if (mPendingHotspots == null) {
-            mPendingHotspots = new SparseArray<>(2);
-            p = null;
-        } else {
-            p = mPendingHotspots.get(id);
-        }
-
-        if (p == null) {
-            final PointF newPoint = new PointF();
-            mPendingHotspots.put(id, newPoint);
-            return newPoint;
-        } else {
-            return p;
-        }
-    }
-
-    /**
-     * Moves a hotspot from pending to active.
-     */
-    private void activateHotspot(int id) {
-        final SparseArray<PointF> pendingHotspots = mPendingHotspots;
-        if (pendingHotspots != null) {
-            final int index = pendingHotspots.indexOfKey(id);
-            if (index >= 0) {
-                final PointF hotspot = pendingHotspots.valueAt(index);
-                pendingHotspots.removeAt(index);
-                createActiveHotspot(id, hotspot.x, hotspot.y);
-            }
-        }
-    }
-
     /**
      * Creates an active hotspot at the specified location.
      */
-    private void createActiveHotspot(int id, float x, float y) {
+    private void activateHotspot() {
+        if (mAnimatingRipplesCount >= MAX_RIPPLES) {
+            // This should never happen unless the user is tapping like a maniac
+            // or there is a bug that's preventing ripples from being removed.
+            Log.d(LOG_TAG, "Max ripple count exceeded", new RuntimeException());
+            return;
+        }
+
+        if (mHotspot == null) {
+            final float x = mHotspotBounds.exactCenterX();
+            final float y = mHotspotBounds.exactCenterY();
+            mHotspot = new Ripple(this, mHotspotBounds, x, y);
+        }
+
         final int color = mState.mTint.getColorForState(getState(), Color.TRANSPARENT);
-        final Ripple newRipple = new Ripple(this, mHotspotBounds, color);
-        newRipple.enter(x, y);
+        mHotspot.setup(mState.mMaxRadius, color, mDensity);
+        mHotspot.enter();
 
         if (mAnimatingRipples == null) {
             mAnimatingRipples = new Ripple[MAX_RIPPLES];
         }
-        mAnimatingRipples[mAnimatingRipplesCount++] = newRipple;
-
-        if (mActiveHotspots == null) {
-            mActiveHotspots = new SparseArray<Ripple>();
-        }
-        mActiveHotspots.put(id, newRipple);
+        mAnimatingRipples[mAnimatingRipplesCount++] = mHotspot;
     }
 
-    private void removeHotspot(int id) {
-        if (mActiveHotspots == null) {
-            return;
-        }
-
-        final Ripple ripple = mActiveHotspots.get(id);
-        if (ripple != null) {
-            ripple.exit();
-
-            mActiveHotspots.remove(id);
+    private void removeHotspot() {
+        if (mHotspot != null) {
+            mHotspot.exit();
+            mHotspot = null;
         }
     }
 
     private void clearHotspots() {
-        if (mActiveHotspots != null) {
-            mActiveHotspots.clear();
+        if (mHotspot != null) {
+            mHotspot.cancel();
+            mHotspot = null;
         }
 
         final int count = mAnimatingRipplesCount;
@@ -459,73 +453,113 @@
     public void setHotspotBounds(int left, int top, int right, int bottom) {
         mOverrideBounds = true;
         mHotspotBounds.set(left, top, right, bottom);
+
+        onHotspotBoundsChanged();
+    }
+
+    /**
+     * Notifies all the animating ripples that the hotspot bounds have changed.
+     */
+    private void onHotspotBoundsChanged() {
+        final int count = mAnimatingRipplesCount;
+        final Ripple[] ripples = mAnimatingRipples;
+        for (int i = 0; i < count; i++) {
+            ripples[i].onHotspotBoundsChanged();
+        }
     }
 
     @Override
     public void draw(Canvas canvas) {
-        final int N = mLayerState.mNum;
-        final Rect bounds = getBounds();
-        final ChildDrawable[] array = mLayerState.mChildren;
-        final boolean maskOnly = mState.mMask != null && N == 1;
+        final Rect bounds = isProjected() ? getDirtyBounds() : getBounds();
 
-        int restoreToCount = drawRippleLayer(canvas, maskOnly);
+        // Draw the content into a layer first.
+        final int contentLayer = drawContentLayer(canvas, bounds, SRC_OVER);
 
-        if (restoreToCount >= 0) {
-            // We have a ripple layer that contains ripples. If we also have an
-            // explicit mask drawable, apply it now using DST_IN blending.
-            if (mState.mMask != null) {
-                canvas.saveLayer(bounds.left, bounds.top, bounds.right,
-                        bounds.bottom, getMaskingPaint(DST_IN));
-                mState.mMask.draw(canvas);
-                canvas.restoreToCount(restoreToCount);
-                restoreToCount = -1;
-            }
+        // Next, draw the ripples into a layer.
+        final int rippleLayer = drawRippleLayer(canvas, bounds, mState.mTintXfermode);
 
-            // If there's more content, we need an extra masking layer to merge
-            // the ripples over the content.
-            if (!maskOnly) {
-                final PorterDuffXfermode xfermode = mState.getTintXfermodeInverse();
-                final int count = canvas.saveLayer(bounds.left, bounds.top,
-                        bounds.right, bounds.bottom, getMaskingPaint(xfermode));
-                if (restoreToCount < 0) {
-                    restoreToCount = count;
-                }
-            }
+        // If we have ripples, draw the masking layer.
+        if (rippleLayer >= 0) {
+            drawMaskingLayer(canvas, bounds, DST_IN);
         }
 
+        // Composite the layers if needed.
+        if (contentLayer >= 0) {
+            canvas.restoreToCount(contentLayer);
+        } else if (rippleLayer >= 0) {
+            canvas.restoreToCount(rippleLayer);
+        }
+    }
+
+    /**
+     * Removes a ripple from the animating ripple list.
+     *
+     * @param ripple the ripple to remove
+     */
+    void removeRipple(Ripple ripple) {
+        // Ripple ripple ripple ripple. Ripple ripple.
+        final Ripple[] ripples = mAnimatingRipples;
+        final int count = mAnimatingRipplesCount;
+        final int index = getRippleIndex(ripple);
+        if (index >= 0) {
+            for (int i = index + 1; i < count; i++) {
+                ripples[i - 1] = ripples[i];
+            }
+            ripples[count - 1] = null;
+            mAnimatingRipplesCount--;
+            invalidateSelf();
+        }
+    }
+
+    private int getRippleIndex(Ripple ripple) {
+        final Ripple[] ripples = mAnimatingRipples;
+        final int count = mAnimatingRipplesCount;
+        for (int i = 0; i < count; i++) {
+            if (ripples[i] == ripple) {
+                return i;
+            }
+        }
+        return -1;
+    }
+
+    private int drawContentLayer(Canvas canvas, Rect bounds, PorterDuffXfermode mode) {
+        final int count = mLayerState.mNum;
+        if (count == 0 || (mState.mMask != null && count == 1)) {
+            return -1;
+        }
+
+        final Paint maskingPaint = getMaskingPaint(mode);
+        final int restoreToCount = canvas.saveLayer(bounds.left, bounds.top,
+                bounds.right, bounds.bottom, maskingPaint);
+
         // Draw everything except the mask.
-        for (int i = 0; i < N; i++) {
+        final ChildDrawable[] array = mLayerState.mChildren;
+        for (int i = 0; i < count; i++) {
             if (array[i].mId != R.id.mask) {
                 array[i].mDrawable.draw(canvas);
             }
         }
 
-        // Composite the layers if needed.
-        if (restoreToCount >= 0) {
-            canvas.restoreToCount(restoreToCount);
-        }
+        return restoreToCount;
     }
 
-    private int drawRippleLayer(Canvas canvas, boolean maskOnly) {
+    private int drawRippleLayer(Canvas canvas, Rect bounds, PorterDuffXfermode mode) {
         final int count = mAnimatingRipplesCount;
         if (count == 0) {
             return -1;
         }
 
-        final Ripple[] ripples = mAnimatingRipples;
-        final boolean projected = isProjected();
-        final Rect layerBounds = projected ? getDirtyBounds() : getBounds();
-
         // Separate the ripple color and alpha channel. The alpha will be
         // applied when we merge the ripples down to the canvas.
-        final int rippleColor;
+        final int rippleARGB;
         if (mState.mTint != null) {
-            rippleColor = mState.mTint.getColorForState(getState(), Color.TRANSPARENT);
+            rippleARGB = mState.mTint.getColorForState(getState(), Color.TRANSPARENT);
         } else {
-            rippleColor = Color.TRANSPARENT;
+            rippleARGB = Color.TRANSPARENT;
         }
-        final int rippleAlpha = Color.alpha(rippleColor);
 
+        final int rippleAlpha = Color.alpha(rippleARGB);
+        final int rippleColor = rippleARGB | (0xFF << 24);
         if (mRipplePaint == null) {
             mRipplePaint = new Paint();
             mRipplePaint.setAntiAlias(true);
@@ -536,36 +570,20 @@
         boolean drewRipples = false;
         int restoreToCount = -1;
         int restoreTranslate = -1;
-        int animatingCount = 0;
 
         // Draw ripples and update the animating ripples array.
+        final Ripple[] ripples = mAnimatingRipples;
         for (int i = 0; i < count; i++) {
             final Ripple ripple = ripples[i];
 
-            // Mark and skip finished ripples.
-            if (ripple.isFinished()) {
-                ripples[i] = null;
-                continue;
-            }
-
             // If we're masking the ripple layer, make sure we have a layer
             // first. This will merge SRC_OVER (directly) onto the canvas.
             if (restoreToCount < 0) {
-                // If we're projecting or we only have a mask, we want to treat the
-                // underlying canvas as our content and merge the ripple layer down
-                // using the tint xfermode.
-                final PorterDuffXfermode xfermode;
-                if (projected || maskOnly) {
-                    xfermode = mState.getTintXfermode();
-                } else {
-                    xfermode = SRC_OVER;
-                }
-
-                final Paint layerPaint = getMaskingPaint(xfermode);
-                layerPaint.setAlpha(rippleAlpha);
-                restoreToCount = canvas.saveLayer(layerBounds.left, layerBounds.top,
-                        layerBounds.right, layerBounds.bottom, layerPaint);
-                layerPaint.setAlpha(255);
+                final Paint maskingPaint = getMaskingPaint(mode);
+                maskingPaint.setAlpha(rippleAlpha);
+                restoreToCount = canvas.saveLayer(bounds.left, bounds.top,
+                        bounds.right, bounds.bottom, maskingPaint);
+                maskingPaint.setAlpha(255);
 
                 restoreTranslate = canvas.save();
                 // Translate the canvas to the current hotspot bounds.
@@ -573,13 +591,8 @@
             }
 
             drewRipples |= ripple.draw(canvas, ripplePaint);
-
-            ripples[animatingCount] = ripples[i];
-            animatingCount++;
         }
 
-        mAnimatingRipplesCount = animatingCount;
-
         // Always restore the translation.
         if (restoreTranslate >= 0) {
             canvas.restoreToCount(restoreTranslate);
@@ -594,6 +607,20 @@
         return restoreToCount;
     }
 
+    private int drawMaskingLayer(Canvas canvas, Rect bounds, PorterDuffXfermode mode) {
+        final Drawable mask = mState.mMask;
+        if (mask == null) {
+            return -1;
+        }
+
+        final int restoreToCount = canvas.saveLayer(bounds.left, bounds.top,
+                bounds.right, bounds.bottom, getMaskingPaint(mode));
+
+        mask.draw(canvas);
+
+        return restoreToCount;
+    }
+
     private Paint getMaskingPaint(PorterDuffXfermode xfermode) {
         if (mMaskingPaint == null) {
             mMaskingPaint = new Paint();
@@ -634,8 +661,8 @@
         int[] mTouchThemeAttrs;
         ColorStateList mTint = null;
         PorterDuffXfermode mTintXfermode = SRC_ATOP;
-        PorterDuffXfermode mTintXfermodeInverse = DST_ATOP;
         Drawable mMask;
+        int mMaxRadius = RADIUS_AUTO;
         boolean mPinned = false;
 
         public RippleState(RippleState orig, RippleDrawable owner, Resources res) {
@@ -645,14 +672,12 @@
                 mTouchThemeAttrs = orig.mTouchThemeAttrs;
                 mTint = orig.mTint;
                 mTintXfermode = orig.mTintXfermode;
-                mTintXfermodeInverse = orig.mTintXfermodeInverse;
+                mMaxRadius = orig.mMaxRadius;
                 mPinned = orig.mPinned;
             }
         }
 
         public void setTintMode(Mode mode) {
-            final Mode invertedMode = RippleState.invertPorterDuffMode(mode);
-            mTintXfermodeInverse = new PorterDuffXfermode(invertedMode);
             mTintXfermode = new PorterDuffXfermode(mode);
         }
 
@@ -660,10 +685,6 @@
             return mTintXfermode;
         }
 
-        public PorterDuffXfermode getTintXfermodeInverse() {
-            return mTintXfermodeInverse;
-        }
-
         @Override
         public boolean canApplyTheme() {
             return mTouchThemeAttrs != null || super.canApplyTheme();
@@ -683,33 +704,36 @@
         public Drawable newDrawable(Resources res, Theme theme) {
             return new RippleDrawable(this, res, theme);
         }
+    }
 
-        /**
-         * Inverts SRC and DST in PorterDuff blending modes.
-         */
-        private static Mode invertPorterDuffMode(Mode src) {
-            switch (src) {
-                case SRC_ATOP:
-                    return Mode.DST_ATOP;
-                case SRC_IN:
-                    return Mode.DST_IN;
-                case SRC_OUT:
-                    return Mode.DST_OUT;
-                case SRC_OVER:
-                    return Mode.DST_OVER;
-                case DST_ATOP:
-                    return Mode.SRC_ATOP;
-                case DST_IN:
-                    return Mode.SRC_IN;
-                case DST_OUT:
-                    return Mode.SRC_OUT;
-                case DST_OVER:
-                    return Mode.SRC_OVER;
-                default:
-                    // Everything else is agnostic to SRC versus DST.
-                    return src;
-            }
+    /**
+     * Sets the maximum ripple radius in pixels. The default value of
+     * {@link #RADIUS_AUTO} defines the radius as the distance from the center
+     * of the drawable bounds (or hotspot bounds, if specified) to a corner.
+     *
+     * @param maxRadius the maximum ripple radius in pixels or
+     *            {@link #RADIUS_AUTO} to automatically determine the maximum
+     *            radius based on the bounds
+     * @see #getMaxRadius()
+     * @see #setHotspotBounds(int, int, int, int)
+     * @hide
+     */
+    public void setMaxRadius(int maxRadius) {
+        if (maxRadius != RADIUS_AUTO && maxRadius < 0) {
+            throw new IllegalArgumentException("maxRadius must be RADIUS_AUTO or >= 0");
         }
+
+        mState.mMaxRadius = maxRadius;
+    }
+
+    /**
+     * @return the maximum ripple radius in pixels, or {@link #RADIUS_AUTO} if
+     *         the radius is determined automatically
+     * @see #setMaxRadius(int)
+     * @hide
+     */
+    public int getMaxRadius() {
+        return mState.mMaxRadius;
     }
 
     private RippleDrawable(RippleState state, Resources res, Theme theme) {
diff --git a/graphics/java/android/graphics/drawable/RotateDrawable.java b/graphics/java/android/graphics/drawable/RotateDrawable.java
index 5f9d1cd..06aeb98 100644
--- a/graphics/java/android/graphics/drawable/RotateDrawable.java
+++ b/graphics/java/android/graphics/drawable/RotateDrawable.java
@@ -438,7 +438,7 @@
                 continue;
             }
 
-            if ((drawable = Drawable.createFromXmlInnerThemed(r, parser, attrs, theme)) == null) {
+            if ((drawable = Drawable.createFromXmlInner(r, parser, attrs, theme)) == null) {
                 Log.w("drawable", "Bad element under <rotate>: "
                         + parser .getName());
             }
diff --git a/graphics/java/android/graphics/drawable/ScaleDrawable.java b/graphics/java/android/graphics/drawable/ScaleDrawable.java
index 4c4d9af..f090c11 100644
--- a/graphics/java/android/graphics/drawable/ScaleDrawable.java
+++ b/graphics/java/android/graphics/drawable/ScaleDrawable.java
@@ -108,7 +108,7 @@
             if (type != XmlPullParser.START_TAG) {
                 continue;
             }
-            dr = Drawable.createFromXmlInnerThemed(r, parser, attrs, theme);
+            dr = Drawable.createFromXmlInner(r, parser, attrs, theme);
         }
 
         if (dr == null) {
diff --git a/graphics/java/android/graphics/drawable/ShapeDrawable.java b/graphics/java/android/graphics/drawable/ShapeDrawable.java
index 99ab4dd..024f77c 100644
--- a/graphics/java/android/graphics/drawable/ShapeDrawable.java
+++ b/graphics/java/android/graphics/drawable/ShapeDrawable.java
@@ -291,41 +291,10 @@
         return mShapeState.mAlpha;
     }
 
-    /**
-     * Specifies a tint for this drawable.
-     * <p>
-     * Setting a color filter via {@link #setColorFilter(ColorFilter)} overrides
-     * tint.
-     *
-     * @param tint Color state list to use for tinting this drawable, or null to
-     *            clear the tint
-     */
-    public void setTint(ColorStateList tint) {
-        if (mShapeState.mTint != tint) {
+    @Override
+    public void setTint(ColorStateList tint, Mode tintMode) {
+        if (mShapeState.mTint != tint || mShapeState.mTintMode != tintMode) {
             mShapeState.mTint = tint;
-            updateTintFilter();
-            invalidateSelf();
-        }
-    }
-
-    /**
-     * Returns the tint color for this drawable.
-     *
-     * @return Color state list to use for tinting this drawable, or null if
-     *         none set
-     */
-    public ColorStateList getTint() {
-        return mShapeState.mTint;
-    }
-
-    /**
-     * Specifies the blending mode used to apply tint.
-     *
-     * @param tintMode A Porter-Duff blending mode
-     * @hide Pending finalization of supported Modes
-     */
-    public void setTintMode(Mode tintMode) {
-        if (mShapeState.mTintMode != tintMode) {
             mShapeState.mTintMode = tintMode;
             updateTintFilter();
             invalidateSelf();
@@ -350,16 +319,6 @@
         }
     }
 
-    /**
-     * Returns the blending mode used to apply tint.
-     *
-     * @return The Porter-Duff blending mode used to apply tint.
-     * @hide Pending finalization of supported Modes
-     */
-    public Mode getTintMode() {
-        return mShapeState.mTintMode;
-    }
-
     @Override
     public void setColorFilter(ColorFilter cf) {
         mShapeState.mPaint.setColorFilter(cf);
diff --git a/graphics/java/android/graphics/drawable/StateListDrawable.java b/graphics/java/android/graphics/drawable/StateListDrawable.java
index f22a063..b2fac9b 100644
--- a/graphics/java/android/graphics/drawable/StateListDrawable.java
+++ b/graphics/java/android/graphics/drawable/StateListDrawable.java
@@ -185,7 +185,7 @@
                                     + ": <item> tag requires a 'drawable' attribute or "
                                     + "child tag defining a drawable");
                 }
-                dr = Drawable.createFromXmlInnerThemed(r, parser, attrs, theme);
+                dr = Drawable.createFromXmlInner(r, parser, attrs, theme);
             }
 
             mStateListState.addStateSet(states, dr);
diff --git a/graphics/java/android/graphics/drawable/VectorDrawable.java b/graphics/java/android/graphics/drawable/VectorDrawable.java
index e2bd50d..c6c5b31 100644
--- a/graphics/java/android/graphics/drawable/VectorDrawable.java
+++ b/graphics/java/android/graphics/drawable/VectorDrawable.java
@@ -18,6 +18,7 @@
 import android.content.res.Resources.Theme;
 import android.content.res.TypedArray;
 import android.graphics.Canvas;
+import android.graphics.Color;
 import android.graphics.ColorFilter;
 import android.graphics.Matrix;
 import android.graphics.Paint;
@@ -39,8 +40,8 @@
 import java.io.IOException;
 import java.util.ArrayList;
 import java.util.Arrays;
-import java.util.Collection;
 import java.util.HashMap;
+import java.util.Stack;
 
 /**
  * This lets you create a drawable based on an XML vector graphic It can be
@@ -58,9 +59,26 @@
  * <dd>Used to defined the size of the virtual canvas the paths are drawn on.
  * The size is defined using the attributes <code>android:viewportHeight</code>
  * <code>android:viewportWidth</code></dd>
+ * <dt><code>&lt;group></code></dt>
+ * <dd>Defines a group of paths or subgroups, plus transformation information.
+ * The transformations are defined in the same coordinates as the viewport.
+ * And the transformations are applied in the order of scale, rotate then translate. </dd>
+ * <dt><code>android:rotation</code>
+ * <dd>The degrees of rotation of the group.</dd></dt>
+ * <dt><code>android:pivotX</code>
+ * <dd>The X coordinate of the pivot for the scale and rotation of the group</dd></dt>
+ * <dt><code>android:pivotY</code>
+ * <dd>The Y coordinate of the pivot for the scale and rotation of the group</dd></dt>
+ * <dt><code>android:scaleX</code>
+ * <dd>The amount of scale on the X Coordinate</dd></dt>
+ * <dt><code>android:scaleY</code>
+ * <dd>The amount of scale on the Y coordinate</dd></dt>
+ * <dt><code>android:translateX</code>
+ * <dd>The amount of translation on the X coordinate</dd></dt>
+ * <dt><code>android:translateY</code>
+ * <dd>The amount of translation on the Y coordinate</dd></dt>
  * <dt><code>&lt;path></code></dt>
- * <dd>Defines paths to be drawn. Multiple paths can be defined in one xml file.
- * The paths are drawn in the order of their definition order.
+ * <dd>Defines paths to be drawn.
  * <dl>
  * <dt><code>android:name</code>
  * <dd>Defines the name of the path.</dd></dt>
@@ -76,12 +94,6 @@
  * <dd>The width a path stroke</dd></dt>
  * <dt><code>android:strokeOpacity</code>
  * <dd>The opacity of a path stroke</dd></dt>
- * <dt><code>android:rotation</code>
- * <dd>The amount to rotation the path stroke.</dd></dt>
- * <dt><code>android:pivotX</code>
- * <dd>The X coordinate of the center of rotation of a path</dd></dt>
- * <dt><code>android:pivotY</code>
- * <dd>The Y coordinate of the center of rotation of a path</dd></dt>
  * <dt><code>android:fillOpacity</code>
  * <dd>The opacity to fill the path with</dd></dt>
  * <dt><code>android:trimPathStart</code>
@@ -107,6 +119,7 @@
 
     private static final String SHAPE_SIZE = "size";
     private static final String SHAPE_VIEWPORT = "viewport";
+    private static final String SHAPE_GROUP = "group";
     private static final String SHAPE_PATH = "path";
     private static final String SHAPE_VECTOR = "vector";
 
@@ -118,9 +131,9 @@
     private static final int LINEJOIN_ROUND = 1;
     private static final int LINEJOIN_BEVEL = 2;
 
-    private final VectorDrawableState mVectorState;
+    private static final boolean DBG_VECTOR_DRAWABLE = false;
 
-    private int mAlpha = 0xFF;
+    private final VectorDrawableState mVectorState;
 
     public VectorDrawable() {
         mVectorState = new VectorDrawableState(null);
@@ -150,9 +163,8 @@
 
     @Override
     public void setAlpha(int alpha) {
-        // TODO correct handling of transparent
-        if (mAlpha != alpha) {
-            mAlpha = alpha;
+        if (mVectorState.mVPathRenderer.getRootAlpha() != alpha) {
+            mVectorState.mVPathRenderer.setRootAlpha(alpha);
             invalidateSelf();
         }
     }
@@ -259,20 +271,33 @@
         return null;
     }
 
+    private static int applyAlpha(int color, float alpha) {
+        int alphaBytes = Color.alpha(color);
+        color &= 0x00FFFFFF;
+        color |= ((int) (alphaBytes * alpha)) << 24;
+        return color;
+    }
+
     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;
 
-        VGroup currentGroup = new VGroup();
+        // Use a stack to help to build the group tree.
+        // The top of the stack is always the current group.
+        final Stack<VGroup> groupStack = new Stack<VGroup>();
+        groupStack.push(pathRenderer.mRootGroup);
 
         int eventType = parser.getEventType();
         while (eventType != XmlPullParser.END_DOCUMENT) {
             if (eventType == XmlPullParser.START_TAG) {
                 final String tagName = parser.getName();
+                final VGroup currentGroup = groupStack.peek();
+
                 if (SHAPE_PATH.equals(tagName)) {
                     final VPath path = new VPath();
                     path.inflate(res, attrs, theme);
@@ -284,12 +309,27 @@
                 } else if (SHAPE_VIEWPORT.equals(tagName)) {
                     pathRenderer.parseViewport(res, attrs);
                     noViewportTag = false;
+                } else if (SHAPE_GROUP.equals(tagName)) {
+                    VGroup newChildGroup = new VGroup();
+                    newChildGroup.inflate(res, attrs, theme);
+                    currentGroup.mChildGroupList.add(newChildGroup);
+                    groupStack.push(newChildGroup);
+                    noGroupTag = false;
+                }
+            } else if (eventType == XmlPullParser.END_TAG) {
+                final String tagName = parser.getName();
+                if (SHAPE_GROUP.equals(tagName)) {
+                    groupStack.pop();
                 }
             }
-
             eventType = parser.next();
         }
 
+        // Print the tree out for debug.
+        if (DBG_VECTOR_DRAWABLE) {
+            printGroupTree(pathRenderer.mRootGroup, 0);
+        }
+
         if (noSizeTag || noViewportTag || noPathTag) {
             final StringBuffer tag = new StringBuffer();
 
@@ -314,12 +354,24 @@
             throw new XmlPullParserException("no " + tag + " defined");
         }
 
-        pathRenderer.mCurrentGroup = currentGroup;
-        // post parse cleanup
-        pathRenderer.parseFinish();
         return pathRenderer;
     }
 
+    private void printGroupTree(VGroup currentGroup, int level) {
+        String indent = "";
+        for (int i = 0 ; i < level ; i++) {
+            indent += "    ";
+        }
+        // Print the current node
+        Log.v(LOGTAG, indent + "current group is :" +  currentGroup.getName()
+                + " rotation is " + currentGroup.mRotate);
+        Log.v(LOGTAG, indent + "matrix is :" +  currentGroup.getLocalMatrix().toString());
+        // Then print all the children
+        for (int i = 0 ; i < currentGroup.mChildGroupList.size(); i++) {
+            printGroupTree(currentGroup.mChildGroupList.get(i), level + 1);
+        }
+    }
+
     private void setPathRenderer(VPathRenderer pathRenderer) {
         mVectorState.mVPathRenderer = pathRenderer;
     }
@@ -332,6 +384,7 @@
         public VectorDrawableState(VectorDrawableState copy) {
             if (copy != null) {
                 mChangingConfigurations = copy.mChangingConfigurations;
+                // TODO: Make sure the constant state are handled correctly.
                 mVPathRenderer = new VPathRenderer(copy.mVPathRenderer);
                 mPadding = new Rect(copy.mPadding);
             }
@@ -359,35 +412,51 @@
     }
 
     private static class VPathRenderer {
+        /* Right now the internal data structure is organized as a tree.
+         * Each node can be a group node, or a path.
+         * A group node can have groups or paths as children, but a path node has
+         * no children.
+         * One example can be:
+         *                 Root Group
+         *                /    |     \
+         *           Group    Path    Group
+         *          /     \             |
+         *         Path   Path         Path
+         *
+         */
+        private final VGroup mRootGroup;
+
         private final Path mPath = new Path();
         private final Path mRenderPath = new Path();
-        private final Matrix mMatrix = new Matrix();
+        private static final Matrix IDENTITY_MATRIX = new Matrix();
 
-        private VPath[] mCurrentPaths;
         private Paint mStrokePaint;
         private Paint mFillPaint;
         private ColorFilter mColorFilter;
         private PathMeasure mPathMeasure;
 
-        private VGroup mCurrentGroup = new VGroup();
+        private float mBaseWidth = 0;
+        private float mBaseHeight = 0;
+        private float mViewportWidth = 0;
+        private float mViewportHeight = 0;
+        private int mRootAlpha = 0xFF;
 
-        float mBaseWidth = 0;
-        float mBaseHeight = 0;
-        float mViewportWidth = 0;
-        float mViewportHeight = 0;
+        private final Matrix mFinalPathMatrix = new Matrix();
 
         public VPathRenderer() {
+            mRootGroup = new VGroup();
+        }
+
+        public void setRootAlpha(int alpha) {
+            mRootAlpha = alpha;
+        }
+
+        public int getRootAlpha() {
+            return mRootAlpha;
         }
 
         public VPathRenderer(VPathRenderer copy) {
-            mCurrentGroup = copy.mCurrentGroup;
-            if (copy.mCurrentPaths != null) {
-                mCurrentPaths = new VPath[copy.mCurrentPaths.length];
-                for (int i = 0; i < mCurrentPaths.length; i++) {
-                    mCurrentPaths[i] = new VPath(copy.mCurrentPaths[i]);
-                }
-            }
-
+            mRootGroup = copy.mRootGroup;
             mBaseWidth = copy.mBaseWidth;
             mBaseHeight = copy.mBaseHeight;
             mViewportWidth = copy.mViewportHeight;
@@ -395,24 +464,59 @@
         }
 
         public boolean canApplyTheme() {
-            final ArrayList<VPath> paths = mCurrentGroup.mVGList;
+            // If one of the paths can apply theme, then return true;
+            return recursiveCanApplyTheme(mRootGroup);
+        }
+
+        private boolean recursiveCanApplyTheme(VGroup currentGroup) {
+            // We can do a tree traverse here, if there is one path return true,
+            // then we return true for the whole tree.
+            final ArrayList<VPath> paths = currentGroup.mPathList;
             for (int j = paths.size() - 1; j >= 0; j--) {
                 final VPath path = paths.get(j);
                 if (path.canApplyTheme()) {
                     return true;
                 }
             }
+
+            final ArrayList<VGroup> childGroups = currentGroup.mChildGroupList;
+
+            for (int i = 0; i < childGroups.size(); i++) {
+                VGroup childGroup = childGroups.get(i);
+                if (childGroup.canApplyTheme()
+                        || recursiveCanApplyTheme(childGroup)) {
+                    return true;
+                }
+            }
             return false;
         }
 
         public void applyTheme(Theme t) {
-            final ArrayList<VPath> paths = mCurrentGroup.mVGList;
+            // Apply theme to every path of the tree.
+            recursiveApplyTheme(mRootGroup, t);
+        }
+
+        private void recursiveApplyTheme(VGroup currentGroup, Theme t) {
+            // We can do a tree traverse here, apply theme to all paths which
+            // can apply theme.
+            final ArrayList<VPath> paths = currentGroup.mPathList;
             for (int j = paths.size() - 1; j >= 0; j--) {
                 final VPath path = paths.get(j);
                 if (path.canApplyTheme()) {
                     path.applyTheme(t);
                 }
             }
+
+            final ArrayList<VGroup> childGroups = currentGroup.mChildGroupList;
+
+            for (int i = 0; i < childGroups.size(); i++) {
+                VGroup childGroup = childGroups.get(i);
+                if (childGroup.canApplyTheme()) {
+                    childGroup.applyTheme(t);
+                }
+                recursiveApplyTheme(childGroup, t);
+            }
+
         }
 
         public void setColorFilter(ColorFilter colorFilter) {
@@ -425,107 +529,110 @@
             if (mStrokePaint != null) {
                 mStrokePaint.setColorFilter(colorFilter);
             }
+
+        }
+
+        private void drawGroupTree(VGroup currentGroup, Matrix currentMatrix,
+                float currentAlpha, Canvas canvas, int w, int h) {
+            // Calculate current group's matrix by preConcat the parent's and
+            // and the current one on the top of the stack.
+            // Basically the Mfinal = Mviewport * M0 * M1 * M2;
+            // Mi the local matrix at level i of the group tree.
+            currentGroup.mStackedMatrix.set(currentMatrix);
+
+            currentGroup.mStackedMatrix.preConcat(currentGroup.mLocalMatrix);
+
+            float stackedAlpha = currentAlpha * currentGroup.mGroupAlpha;
+            drawPath(currentGroup, stackedAlpha, canvas, w, h);
+            // Draw the group tree in post order.
+            for (int i = 0 ; i < currentGroup.mChildGroupList.size(); i++) {
+                drawGroupTree(currentGroup.mChildGroupList.get(i),
+                        currentGroup.mStackedMatrix, stackedAlpha, canvas, w, h);
+            }
         }
 
         public void draw(Canvas canvas, int w, int h) {
-            if (mCurrentPaths == null) {
-                Log.e(LOGTAG,"mCurrentPaths == null");
-                return;
-            }
-
-            for (int i = 0; i < mCurrentPaths.length; i++) {
-                if (mCurrentPaths[i] != null) {
-                    drawPath(mCurrentPaths[i], canvas, w, h);
-                }
-            }
+            // Travese the tree in pre-order to draw.
+            drawGroupTree(mRootGroup, IDENTITY_MATRIX, ((float) mRootAlpha) / 0xFF, canvas, w, h);
         }
 
-        private void drawPath(VPath vPath, Canvas canvas, int w, int h) {
+        private void drawPath(VGroup vGroup, float stackedAlpha, Canvas canvas, int w, int h) {
             final float scale = Math.min(h / mViewportHeight, w / mViewportWidth);
 
-            vPath.toPath(mPath);
-            final Path path = mPath;
+            mFinalPathMatrix.set(vGroup.mStackedMatrix);
+            mFinalPathMatrix.postScale(scale, scale, mViewportWidth / 2f, mViewportHeight / 2f);
+            mFinalPathMatrix.postTranslate(w / 2f - mViewportWidth / 2f, h / 2f - mViewportHeight / 2f);
 
-            if (vPath.mTrimPathStart != 0.0f || vPath.mTrimPathEnd != 1.0f) {
-                float start = (vPath.mTrimPathStart + vPath.mTrimPathOffset) % 1.0f;
-                float end = (vPath.mTrimPathEnd + vPath.mTrimPathOffset) % 1.0f;
+            ArrayList<VPath> paths = vGroup.getPaths();
+            for (int i = 0; i < paths.size(); i++) {
+                VPath vPath = paths.get(i);
+                vPath.toPath(mPath);
+                final Path path = mPath;
 
-                if (mPathMeasure == null) {
-                    mPathMeasure = new PathMeasure();
+                if (vPath.mTrimPathStart != 0.0f || vPath.mTrimPathEnd != 1.0f) {
+                    float start = (vPath.mTrimPathStart + vPath.mTrimPathOffset) % 1.0f;
+                    float end = (vPath.mTrimPathEnd + vPath.mTrimPathOffset) % 1.0f;
+
+                    if (mPathMeasure == null) {
+                        mPathMeasure = new PathMeasure();
+                    }
+                    mPathMeasure.setPath(mPath, false);
+
+                    float len = mPathMeasure.getLength();
+                    start = start * len;
+                    end = end * len;
+                    path.reset();
+                    if (start > end) {
+                        mPathMeasure.getSegment(start, len, path, true);
+                        mPathMeasure.getSegment(0f, end, path, true);
+                    } else {
+                        mPathMeasure.getSegment(start, end, path, true);
+                    }
+                    path.rLineTo(0, 0); // fix bug in measure
                 }
-                mPathMeasure.setPath(mPath, false);
 
-                float len = mPathMeasure.getLength();
-                start = start * len;
-                end = end * len;
-                path.reset();
-                if (start > end) {
-                    mPathMeasure.getSegment(start, len, path, true);
-                    mPathMeasure.getSegment(0f, end, path, true);
+                mRenderPath.reset();
+
+                mRenderPath.addPath(path, mFinalPathMatrix);
+
+                if (vPath.mClip) {
+                    canvas.clipPath(mRenderPath, Region.Op.REPLACE);
                 } else {
-                    mPathMeasure.getSegment(start, end, path, true);
+                   if (vPath.mFillColor != 0) {
+                        if (mFillPaint == null) {
+                            mFillPaint = new Paint();
+                            mFillPaint.setColorFilter(mColorFilter);
+                            mFillPaint.setStyle(Paint.Style.FILL);
+                            mFillPaint.setAntiAlias(true);
+                        }
+                        mFillPaint.setColor(applyAlpha(vPath.mFillColor, stackedAlpha));
+                        canvas.drawPath(mRenderPath, mFillPaint);
+                    }
+
+                    if (vPath.mStrokeColor != 0) {
+                        if (mStrokePaint == null) {
+                            mStrokePaint = new Paint();
+                            mStrokePaint.setColorFilter(mColorFilter);
+                            mStrokePaint.setStyle(Paint.Style.STROKE);
+                            mStrokePaint.setAntiAlias(true);
+                        }
+
+                        final Paint strokePaint = mStrokePaint;
+                        if (vPath.mStrokeLineJoin != null) {
+                            strokePaint.setStrokeJoin(vPath.mStrokeLineJoin);
+                        }
+
+                        if (vPath.mStrokeLineCap != null) {
+                            strokePaint.setStrokeCap(vPath.mStrokeLineCap);
+                        }
+
+                        strokePaint.setStrokeMiter(vPath.mStrokeMiterlimit * scale);
+
+                        strokePaint.setColor(applyAlpha(vPath.mStrokeColor, stackedAlpha));
+                        strokePaint.setStrokeWidth(vPath.mStrokeWidth * scale);
+                        canvas.drawPath(mRenderPath, strokePaint);
+                    }
                 }
-                path.rLineTo(0, 0); // fix bug in measure
-            }
-
-            mRenderPath.reset();
-            mMatrix.reset();
-
-            mMatrix.postRotate(vPath.mRotate, vPath.mPivotX, vPath.mPivotY);
-            mMatrix.postScale(scale, scale, mViewportWidth / 2f, mViewportHeight / 2f);
-            mMatrix.postTranslate(w / 2f - mViewportWidth / 2f, h / 2f - mViewportHeight / 2f);
-
-            mRenderPath.addPath(path, mMatrix);
-
-            if (vPath.mClip) {
-                canvas.clipPath(mRenderPath, Region.Op.REPLACE);
-            }
-
-            if (vPath.mFillColor != 0) {
-                if (mFillPaint == null) {
-                    mFillPaint = new Paint();
-                    mFillPaint.setColorFilter(mColorFilter);
-                    mFillPaint.setStyle(Paint.Style.FILL);
-                    mFillPaint.setAntiAlias(true);
-                }
-
-                mFillPaint.setColor(vPath.mFillColor);
-                canvas.drawPath(mRenderPath, mFillPaint);
-            }
-
-            if (vPath.mStrokeColor != 0) {
-                if (mStrokePaint == null) {
-                    mStrokePaint = new Paint();
-                    mStrokePaint.setColorFilter(mColorFilter);
-                    mStrokePaint.setStyle(Paint.Style.STROKE);
-                    mStrokePaint.setAntiAlias(true);
-                }
-
-                final Paint strokePaint = mStrokePaint;
-                if (vPath.mStrokeLineJoin != null) {
-                    strokePaint.setStrokeJoin(vPath.mStrokeLineJoin);
-                }
-
-                if (vPath.mStrokeLineCap != null) {
-                    strokePaint.setStrokeCap(vPath.mStrokeLineCap);
-                }
-
-                strokePaint.setStrokeMiter(vPath.mStrokeMiterlimit * scale);
-                strokePaint.setColor(vPath.mStrokeColor);
-                strokePaint.setStrokeWidth(vPath.mStrokeWidth * scale);
-                canvas.drawPath(mRenderPath, strokePaint);
-            }
-        }
-
-        /**
-         * Build the "current" path based on the current group
-         * TODO: improve memory use & performance or move to C++
-         */
-        public void parseFinish() {
-            final Collection<VPath> paths = mCurrentGroup.getPaths();
-            mCurrentPaths = paths.toArray(new VPath[paths.size()]);
-            for (int i = 0; i < mCurrentPaths.length; i++) {
-                mCurrentPaths[i] = new VPath(mCurrentPaths[i]);
             }
         }
 
@@ -567,20 +674,133 @@
 
     private static class VGroup {
         private final HashMap<String, VPath> mVGPathMap = new HashMap<String, VPath>();
-        private final ArrayList<VPath> mVGList = new ArrayList<VPath>();
+        private final ArrayList<VPath> mPathList = new ArrayList<VPath>();
+        private final ArrayList<VGroup> mChildGroupList = new ArrayList<VGroup>();
+
+        private float mRotate = 0;
+        private float mPivotX = 0;
+        private float mPivotY = 0;
+        private float mScaleX = 1;
+        private float mScaleY = 1;
+        private float mTranslateX = 0;
+        private float mTranslateY = 0;
+        private float mGroupAlpha = 1;
+
+        // mLocalMatrix is parsed from the XML.
+        private final Matrix mLocalMatrix = new Matrix();
+        // mStackedMatrix is only used when drawing, it combines all the
+        // parents' local matrices with the current one.
+        private final Matrix mStackedMatrix = new Matrix();
+
+        private int[] mThemeAttrs;
+
+        private String mName = null;
+
+        public String getName() {
+            return mName;
+        }
+
+        public Matrix getLocalMatrix() {
+            return mLocalMatrix;
+        }
 
         public void add(VPath path) {
             String id = path.getID();
             mVGPathMap.put(id, path);
-            mVGList.add(path);
+            mPathList.add(path);
          }
 
+        public boolean canApplyTheme() {
+            return mThemeAttrs != null;
+        }
+
+        public void applyTheme(Theme t) {
+            if (mThemeAttrs == null) {
+                return;
+            }
+
+            final TypedArray a = t.resolveAttributes(
+                    mThemeAttrs, R.styleable.VectorDrawablePath);
+
+            mRotate = a.getFloat(R.styleable.VectorDrawableGroup_rotation, mRotate);
+            mPivotX = a.getFloat(R.styleable.VectorDrawableGroup_pivotX, mPivotX);
+            mPivotY = a.getFloat(R.styleable.VectorDrawableGroup_pivotY, mPivotY);
+            mScaleX = a.getFloat(R.styleable.VectorDrawableGroup_scaleX, mScaleX);
+            mScaleY = a.getFloat(R.styleable.VectorDrawableGroup_scaleY, mScaleY);
+            mTranslateX = a.getFloat(R.styleable.VectorDrawableGroup_translateX, mTranslateX);
+            mTranslateY = a.getFloat(R.styleable.VectorDrawableGroup_translateY, mTranslateY);
+            mGroupAlpha = a.getFloat(R.styleable.VectorDrawableGroup_alpha, mGroupAlpha);
+            updateLocalMatrix();
+            if (a.hasValue(R.styleable.VectorDrawableGroup_name)) {
+                mName = a.getString(R.styleable.VectorDrawableGroup_name);
+            }
+            a.recycle();
+        }
+
+        public void inflate(Resources res, AttributeSet attrs, Theme theme) {
+            final TypedArray a = obtainAttributes(res, theme, attrs, R.styleable.VectorDrawableGroup);
+            final int[] themeAttrs = a.extractThemeAttrs();
+
+            mThemeAttrs = themeAttrs;
+            // NOTE: The set of attributes loaded here MUST match the
+            // set of attributes loaded in applyTheme.
+
+            if (themeAttrs == null || themeAttrs[R.styleable.VectorDrawableGroup_rotation] == 0) {
+                mRotate = a.getFloat(R.styleable.VectorDrawableGroup_rotation, mRotate);
+            }
+
+            if (themeAttrs == null || themeAttrs[R.styleable.VectorDrawableGroup_pivotX] == 0) {
+                mPivotX = a.getFloat(R.styleable.VectorDrawableGroup_pivotX, mPivotX);
+            }
+
+            if (themeAttrs == null || themeAttrs[R.styleable.VectorDrawableGroup_pivotY] == 0) {
+                mPivotY = a.getFloat(R.styleable.VectorDrawableGroup_pivotY, mPivotY);
+            }
+
+            if (themeAttrs == null || themeAttrs[R.styleable.VectorDrawableGroup_scaleX] == 0) {
+                mScaleX = a.getFloat(R.styleable.VectorDrawableGroup_scaleX, mScaleX);
+            }
+
+            if (themeAttrs == null || themeAttrs[R.styleable.VectorDrawableGroup_scaleY] == 0) {
+                mScaleY = a.getFloat(R.styleable.VectorDrawableGroup_scaleY, mScaleY);
+            }
+
+            if (themeAttrs == null || themeAttrs[R.styleable.VectorDrawableGroup_translateX] == 0) {
+                mTranslateX = a.getFloat(R.styleable.VectorDrawableGroup_translateX, mTranslateX);
+            }
+
+            if (themeAttrs == null || themeAttrs[R.styleable.VectorDrawableGroup_translateY] == 0) {
+                mTranslateY = a.getFloat(R.styleable.VectorDrawableGroup_translateY, mTranslateY);
+            }
+
+            if (themeAttrs == null || themeAttrs[R.styleable.VectorDrawableGroup_name] == 0) {
+                mName = a.getString(R.styleable.VectorDrawableGroup_name);
+            }
+
+            if (themeAttrs == null || themeAttrs[R.styleable.VectorDrawableGroup_alpha] == 0) {
+                mGroupAlpha = a.getFloat(R.styleable.VectorDrawableGroup_alpha, mGroupAlpha);
+            }
+
+            updateLocalMatrix();
+            a.recycle();
+        }
+
+        private void updateLocalMatrix() {
+            // The order we apply is the same as the
+            // RenderNode.cpp::applyViewPropertyTransforms().
+            mLocalMatrix.reset();
+            mLocalMatrix.postTranslate(-mPivotX, -mPivotY);
+            mLocalMatrix.postScale(mScaleX, mScaleY);
+            mLocalMatrix.postRotate(mRotate, 0, 0);
+            mLocalMatrix.postTranslate(mTranslateX + mPivotX, mTranslateY + mPivotY);
+        }
+
         /**
          * Must return in order of adding
          * @return ordered list of paths
          */
-        public Collection<VPath> getPaths() {
-            return mVGList;
+        public ArrayList<VPath> getPaths() {
+            return mPathList;
         }
 
     }
@@ -594,14 +814,10 @@
         float mStrokeWidth = 0;
         float mStrokeOpacity = Float.NaN;
 
-        int mFillColor = 0;
+        int mFillColor = Color.BLACK;
         int mFillRule;
         float mFillOpacity = Float.NaN;
 
-        float mRotate = 0;
-        float mPivotX = 0;
-        float mPivotY = 0;
-
         float mTrimPathStart = 0;
         float mTrimPathEnd = 1;
         float mTrimPathOffset = 0;
@@ -613,18 +829,11 @@
 
         private VNode[] mNode = null;
         private String mId;
-        private int[] mCheckState = new int[MAX_STATES];
-        private boolean[] mCheckValue = new boolean[MAX_STATES];
-        private int mNumberOfStates = 0;
 
         public VPath() {
             // Empty constructor.
         }
 
-        public VPath(VPath p) {
-            copyFrom(p);
-        }
-
         public void toPath(Path path) {
             path.reset();
             if (mNode != null) {
@@ -689,18 +898,6 @@
                 mFillOpacity = a.getFloat(R.styleable.VectorDrawablePath_fillOpacity, mFillOpacity);
             }
 
-            if (themeAttrs == null || themeAttrs[R.styleable.VectorDrawablePath_rotation] == 0) {
-                mRotate = a.getFloat(R.styleable.VectorDrawablePath_rotation, mRotate);
-            }
-
-            if (themeAttrs == null || themeAttrs[R.styleable.VectorDrawablePath_pivotX] == 0) {
-                mPivotX = a.getFloat(R.styleable.VectorDrawablePath_pivotX, mPivotX);
-            }
-
-            if (themeAttrs == null || themeAttrs[R.styleable.VectorDrawablePath_pivotY] == 0) {
-                mPivotY = a.getFloat(R.styleable.VectorDrawablePath_pivotY, mPivotY);
-            }
-
             if (themeAttrs == null
                     || themeAttrs[R.styleable.VectorDrawablePath_strokeLineCap] == 0) {
                 mStrokeLineCap = getStrokeLineCap(
@@ -779,10 +976,6 @@
             mFillColor = a.getColor(R.styleable.VectorDrawablePath_fill, mFillColor);
             mFillOpacity = a.getFloat(R.styleable.VectorDrawablePath_fillOpacity, mFillOpacity);
 
-            mRotate = a.getFloat(R.styleable.VectorDrawablePath_rotation, mRotate);
-            mPivotX = a.getFloat(R.styleable.VectorDrawablePath_pivotX, mPivotX);
-            mPivotY = a.getFloat(R.styleable.VectorDrawablePath_pivotY, mPivotY);
-
             mStrokeLineCap = getStrokeLineCap(a.getInt(
                     R.styleable.VectorDrawablePath_strokeLineCap, -1), mStrokeLineCap);
             mStrokeLineJoin = getStrokeLineJoin(a.getInt(
@@ -801,17 +994,16 @@
                     R.styleable.VectorDrawablePath_trimPathStart, mTrimPathStart);
 
             updateColorAlphas();
+            a.recycle();
         }
 
         private void updateColorAlphas() {
             if (!Float.isNaN(mFillOpacity)) {
-                mFillColor &= 0x00FFFFFF;
-                mFillColor |= ((int) (0xFF * mFillOpacity)) << 24;
+                mFillColor = applyAlpha(mFillColor, mFillOpacity);
             }
 
             if (!Float.isNaN(mStrokeOpacity)) {
-                mStrokeColor &= 0x00FFFFFF;
-                mStrokeColor |= ((int) (0xFF * mStrokeOpacity)) << 24;
+                mStrokeColor = applyAlpha(mStrokeColor, mStrokeOpacity);
             }
         }
 
@@ -903,33 +1095,6 @@
             }
             return list.toArray(new VectorDrawable.VNode[list.size()]);
         }
-
-        public void copyFrom(VPath p1) {
-            mNode = new VNode[p1.mNode.length];
-            for (int i = 0; i < mNode.length; i++) {
-                mNode[i] = new VNode(p1.mNode[i]);
-            }
-            mId = p1.mId;
-            mStrokeColor = p1.mStrokeColor;
-            mFillColor = p1.mFillColor;
-            mStrokeWidth = p1.mStrokeWidth;
-            mRotate = p1.mRotate;
-            mPivotX = p1.mPivotX;
-            mPivotY = p1.mPivotY;
-            mTrimPathStart = p1.mTrimPathStart;
-            mTrimPathEnd = p1.mTrimPathEnd;
-            mTrimPathOffset = p1.mTrimPathOffset;
-            mStrokeLineCap = p1.mStrokeLineCap;
-            mStrokeLineJoin = p1.mStrokeLineJoin;
-            mStrokeMiterlimit = p1.mStrokeMiterlimit;
-            mNumberOfStates = p1.mNumberOfStates;
-            for (int i = 0; i < mNumberOfStates; i++) {
-                mCheckState[i] = p1.mCheckState[i];
-                mCheckValue[i] = p1.mCheckValue[i];
-            }
-
-            mFillRule = p1.mFillRule;
-        }
     }
 
     private static class VNode {
diff --git a/libs/hwui/Android.mk b/libs/hwui/Android.mk
index 2cadf09..e5c8898 100644
--- a/libs/hwui/Android.mk
+++ b/libs/hwui/Android.mk
@@ -23,6 +23,7 @@
 		DisplayListLogBuffer.cpp \
 		DisplayListRenderer.cpp \
 		Dither.cpp \
+		DrawProfiler.cpp \
 		Extensions.cpp \
 		FboCache.cpp \
 		GradientCache.cpp \
@@ -70,8 +71,6 @@
 		$(LOCAL_PATH)/../../include/utils \
 		external/skia/src/core
 
-	include external/stlport/libstlport.mk
-
 	LOCAL_CFLAGS += -DUSE_OPENGL_RENDERER -DEGL_EGLEXT_PROTOTYPES -DGL_GLEXT_PROTOTYPES
 	LOCAL_CFLAGS += -Wno-unused-parameter
 	LOCAL_MODULE_CLASS := SHARED_LIBRARIES
@@ -79,16 +78,15 @@
 	LOCAL_MODULE := libhwui
 	LOCAL_MODULE_TAGS := optional
 
+	include external/stlport/libstlport.mk
+
 	ifneq (false,$(ANDROID_ENABLE_RENDERSCRIPT))
 		LOCAL_CFLAGS += -DANDROID_ENABLE_RENDERSCRIPT
-		LOCAL_SHARED_LIBRARIES += libRS libRScpp libstlport
+		LOCAL_SHARED_LIBRARIES += libRS libRScpp
 		LOCAL_C_INCLUDES += \
 			$(intermediates) \
 			frameworks/rs/cpp \
-			frameworks/rs \
-			external/stlport/stlport \
-			bionic/ \
-			bionic/libstdc++/include
+			frameworks/rs
 	endif
 
 	ifndef HWUI_COMPILE_SYMBOLS
diff --git a/libs/hwui/Animator.cpp b/libs/hwui/Animator.cpp
index b80f7e9..eff3011 100644
--- a/libs/hwui/Animator.cpp
+++ b/libs/hwui/Animator.cpp
@@ -63,7 +63,6 @@
 void BaseRenderNodeAnimator::setupStartValueIfNecessary(RenderNode* target, TreeInfo& info) {
     if (mPlayState == NEEDS_START) {
         setStartValue(getValue(target));
-        mPlayState = PENDING;
     }
 }
 
@@ -154,7 +153,8 @@
 }
 
 void RenderPropertyAnimator::onAttached(RenderNode* target) {
-    if (target->isPropertyFieldDirty(mPropertyAccess->dirtyMask)) {
+    if (mPlayState == NEEDS_START
+            && target->isPropertyFieldDirty(mPropertyAccess->dirtyMask)) {
         setStartValue((target->stagingProperties().*mPropertyAccess->getter)());
     }
     (target->mutateStagingProperties().*mPropertyAccess->setter)(finalValue());
diff --git a/libs/hwui/Animator.h b/libs/hwui/Animator.h
index 7741617..a0c7c55 100644
--- a/libs/hwui/Animator.h
+++ b/libs/hwui/Animator.h
@@ -41,6 +41,7 @@
 class BaseRenderNodeAnimator : public VirtualLightRefBase {
     PREVENT_COPY_AND_ASSIGN(BaseRenderNodeAnimator);
 public:
+    ANDROID_API void setStartValue(float value);
     ANDROID_API void setInterpolator(Interpolator* interpolator);
     ANDROID_API void setDuration(nsecs_t durationInMs);
     ANDROID_API nsecs_t duration() { return mDuration; }
@@ -64,11 +65,9 @@
     BaseRenderNodeAnimator(float finalValue);
     virtual ~BaseRenderNodeAnimator();
 
-    void setStartValue(float value);
     virtual float getValue(RenderNode* target) const = 0;
     virtual void setValue(RenderNode* target, float value) = 0;
 
-private:
     void callOnFinishedListener(TreeInfo& info);
 
     enum PlayState {
diff --git a/libs/hwui/DeferredLayerUpdater.cpp b/libs/hwui/DeferredLayerUpdater.cpp
index 285c8c3..97e9bf6 100644
--- a/libs/hwui/DeferredLayerUpdater.cpp
+++ b/libs/hwui/DeferredLayerUpdater.cpp
@@ -22,14 +22,19 @@
 namespace android {
 namespace uirenderer {
 
-DeferredLayerUpdater::DeferredLayerUpdater(Layer* layer, OpenGLRenderer* renderer)
+static void defaultLayerDestroyer(Layer* layer) {
+    Caches::getInstance().resourceCache.decrementRefcount(layer);
+}
+
+DeferredLayerUpdater::DeferredLayerUpdater(Layer* layer, LayerDestroyer destroyer)
         : mDisplayList(0)
         , mSurfaceTexture(0)
         , mTransform(0)
         , mNeedsGLContextAttach(false)
         , mUpdateTexImage(false)
         , mLayer(layer)
-        , mCaches(Caches::getInstance()) {
+        , mCaches(Caches::getInstance())
+        , mDestroyer(destroyer) {
     mWidth = mLayer->layer.getWidth();
     mHeight = mLayer->layer.getHeight();
     mBlend = mLayer->isBlend();
@@ -37,14 +42,16 @@
     mAlpha = mLayer->getAlpha();
     mMode = mLayer->getMode();
     mDirtyRect.setEmpty();
+
+    if (!mDestroyer) {
+        mDestroyer = defaultLayerDestroyer;
+    }
 }
 
 DeferredLayerUpdater::~DeferredLayerUpdater() {
     SkSafeUnref(mColorFilter);
     setTransform(0);
-    if (mLayer) {
-        mCaches.resourceCache.decrementRefcount(mLayer);
-    }
+    mDestroyer(mLayer);
 }
 
 void DeferredLayerUpdater::setPaint(const SkPaint* paint) {
diff --git a/libs/hwui/DeferredLayerUpdater.h b/libs/hwui/DeferredLayerUpdater.h
index cc62caa..b7cfe80 100644
--- a/libs/hwui/DeferredLayerUpdater.h
+++ b/libs/hwui/DeferredLayerUpdater.h
@@ -30,13 +30,15 @@
 namespace android {
 namespace uirenderer {
 
+typedef void (*LayerDestroyer)(Layer* layer);
+
 // Container to hold the properties a layer should be set to at the start
 // of a render pass
-class DeferredLayerUpdater {
+class DeferredLayerUpdater : public VirtualLightRefBase {
 public:
     // Note that DeferredLayerUpdater assumes it is taking ownership of the layer
     // and will not call incrementRef on it as a result.
-    ANDROID_API DeferredLayerUpdater(Layer* layer, OpenGLRenderer* renderer = 0);
+    ANDROID_API DeferredLayerUpdater(Layer* layer, LayerDestroyer = 0);
     ANDROID_API ~DeferredLayerUpdater();
 
     ANDROID_API bool setSize(uint32_t width, uint32_t height) {
@@ -83,12 +85,6 @@
         return mLayer;
     }
 
-    ANDROID_API Layer* detachBackingLayer() {
-        Layer* layer = mLayer;
-        mLayer = 0;
-        return layer;
-    }
-
 private:
     // Generic properties
     uint32_t mWidth;
@@ -111,6 +107,8 @@
     Layer* mLayer;
     Caches& mCaches;
 
+    LayerDestroyer mDestroyer;
+
     void doUpdateTexImage();
 };
 
diff --git a/libs/hwui/DisplayList.cpp b/libs/hwui/DisplayList.cpp
index 96c6292..f418c9b 100644
--- a/libs/hwui/DisplayList.cpp
+++ b/libs/hwui/DisplayList.cpp
@@ -80,10 +80,6 @@
         delete paths.itemAt(i);
     }
 
-    for (size_t i = 0; i < matrices.size(); i++) {
-        delete matrices.itemAt(i);
-    }
-
     bitmapResources.clear();
     ownedBitmapResources.clear();
     patchResources.clear();
@@ -91,7 +87,6 @@
     paints.clear();
     regions.clear();
     paths.clear();
-    matrices.clear();
     layers.clear();
 }
 
diff --git a/libs/hwui/DisplayList.h b/libs/hwui/DisplayList.h
index 11e78b0..7b7dc16 100644
--- a/libs/hwui/DisplayList.h
+++ b/libs/hwui/DisplayList.h
@@ -125,7 +125,6 @@
     Vector<const SkPath*> paths;
     SortedVector<const SkPath*> sourcePaths;
     Vector<const SkRegion*> regions;
-    Vector<const SkMatrix*> matrices;
     Vector<Layer*> layers;
     uint32_t functorCount;
     bool hasDrawOps;
diff --git a/libs/hwui/DisplayListOp.h b/libs/hwui/DisplayListOp.h
index ea3e7a8..9212b9d 100644
--- a/libs/hwui/DisplayListOp.h
+++ b/libs/hwui/DisplayListOp.h
@@ -472,7 +472,7 @@
 
 class SetMatrixOp : public StateOp {
 public:
-    SetMatrixOp(const SkMatrix* matrix)
+    SetMatrixOp(const SkMatrix& matrix)
             : mMatrix(matrix) {}
 
     virtual void applyState(OpenGLRenderer& renderer, int saveCount) const {
@@ -480,22 +480,22 @@
     }
 
     virtual void output(int level, uint32_t logFlags) const {
-        if (mMatrix) {
-            OP_LOG("SetMatrix " SK_MATRIX_STRING, SK_MATRIX_ARGS(mMatrix));
-        } else {
+        if (mMatrix.isIdentity()) {
             OP_LOGS("SetMatrix (reset)");
+        } else {
+            OP_LOG("SetMatrix " SK_MATRIX_STRING, SK_MATRIX_ARGS(&mMatrix));
         }
     }
 
     virtual const char* name() { return "SetMatrix"; }
 
 private:
-    const SkMatrix* mMatrix;
+    const SkMatrix mMatrix;
 };
 
 class ConcatMatrixOp : public StateOp {
 public:
-    ConcatMatrixOp(const SkMatrix* matrix)
+    ConcatMatrixOp(const SkMatrix& matrix)
             : mMatrix(matrix) {}
 
     virtual void applyState(OpenGLRenderer& renderer, int saveCount) const {
@@ -503,13 +503,13 @@
     }
 
     virtual void output(int level, uint32_t logFlags) const {
-        OP_LOG("ConcatMatrix " SK_MATRIX_STRING, SK_MATRIX_ARGS(mMatrix));
+        OP_LOG("ConcatMatrix " SK_MATRIX_STRING, SK_MATRIX_ARGS(&mMatrix));
     }
 
     virtual const char* name() { return "ConcatMatrix"; }
 
 private:
-    const SkMatrix* mMatrix;
+    const SkMatrix mMatrix;
 };
 
 class ClipOp : public StateOp {
@@ -746,10 +746,10 @@
 
 class DrawBitmapMatrixOp : public DrawBoundedOp {
 public:
-    DrawBitmapMatrixOp(const SkBitmap* bitmap, const SkMatrix* matrix, const SkPaint* paint)
+    DrawBitmapMatrixOp(const SkBitmap* bitmap, const SkMatrix& matrix, const SkPaint* paint)
             : DrawBoundedOp(paint), mBitmap(bitmap), mMatrix(matrix) {
         mLocalBounds.set(0, 0, bitmap->width(), bitmap->height());
-        const mat4 transform(*matrix);
+        const mat4 transform(matrix);
         transform.mapRect(mLocalBounds);
     }
 
@@ -758,7 +758,7 @@
     }
 
     virtual void output(int level, uint32_t logFlags) const {
-        OP_LOG("Draw bitmap %p matrix " SK_MATRIX_STRING, mBitmap, SK_MATRIX_ARGS(mMatrix));
+        OP_LOG("Draw bitmap %p matrix " SK_MATRIX_STRING, mBitmap, SK_MATRIX_ARGS(&mMatrix));
     }
 
     virtual const char* name() { return "DrawBitmapMatrix"; }
@@ -770,7 +770,7 @@
 
 private:
     const SkBitmap* mBitmap;
-    const SkMatrix* mMatrix;
+    const SkMatrix mMatrix;
 };
 
 class DrawBitmapRectOp : public DrawBoundedOp {
@@ -788,7 +788,7 @@
     }
 
     virtual void output(int level, uint32_t logFlags) const {
-        OP_LOG("Draw bitmap %p src="RECT_STRING", dst="RECT_STRING,
+        OP_LOG("Draw bitmap %p src=" RECT_STRING ", dst=" RECT_STRING,
                 mBitmap, RECT_ARGS(mSrc), RECT_ARGS(mLocalBounds));
     }
 
@@ -978,7 +978,7 @@
     }
 
     virtual void output(int level, uint32_t logFlags) const {
-        OP_LOG("Draw patch "RECT_STRING, RECT_ARGS(mLocalBounds));
+        OP_LOG("Draw patch " RECT_STRING, RECT_ARGS(mLocalBounds));
     }
 
     virtual const char* name() { return "DrawPatch"; }
@@ -1060,7 +1060,7 @@
     }
 
     virtual void output(int level, uint32_t logFlags) const {
-        OP_LOG("Draw Rect "RECT_STRING, RECT_ARGS(mLocalBounds));
+        OP_LOG("Draw Rect " RECT_STRING, RECT_ARGS(mLocalBounds));
     }
 
     virtual void onDefer(OpenGLRenderer& renderer, DeferInfo& deferInfo,
@@ -1111,7 +1111,7 @@
     }
 
     virtual void output(int level, uint32_t logFlags) const {
-        OP_LOG("Draw RoundRect "RECT_STRING", rx %f, ry %f", RECT_ARGS(mLocalBounds), mRx, mRy);
+        OP_LOG("Draw RoundRect " RECT_STRING ", rx %f, ry %f", RECT_ARGS(mLocalBounds), mRx, mRy);
     }
 
     virtual const char* name() { return "DrawRoundRect"; }
@@ -1175,7 +1175,7 @@
     }
 
     virtual void output(int level, uint32_t logFlags) const {
-        OP_LOG("Draw Oval "RECT_STRING, RECT_ARGS(mLocalBounds));
+        OP_LOG("Draw Oval " RECT_STRING, RECT_ARGS(mLocalBounds));
     }
 
     virtual const char* name() { return "DrawOval"; }
@@ -1195,7 +1195,7 @@
     }
 
     virtual void output(int level, uint32_t logFlags) const {
-        OP_LOG("Draw Arc "RECT_STRING", start %f, sweep %f, useCenter %d",
+        OP_LOG("Draw Arc " RECT_STRING ", start %f, sweep %f, useCenter %d",
                 RECT_ARGS(mLocalBounds), mStartAngle, mSweepAngle, mUseCenter);
     }
 
@@ -1232,7 +1232,7 @@
     }
 
     virtual void output(int level, uint32_t logFlags) const {
-        OP_LOG("Draw Path %p in "RECT_STRING, mPath, RECT_ARGS(mLocalBounds));
+        OP_LOG("Draw Path %p in " RECT_STRING, mPath, RECT_ARGS(mLocalBounds));
     }
 
     virtual const char* name() { return "DrawPath"; }
diff --git a/libs/hwui/DisplayListRenderer.cpp b/libs/hwui/DisplayListRenderer.cpp
index 229afdf..0e47c6e 100644
--- a/libs/hwui/DisplayListRenderer.cpp
+++ b/libs/hwui/DisplayListRenderer.cpp
@@ -141,14 +141,12 @@
     StatefulBaseRenderer::skew(sx, sy);
 }
 
-void DisplayListRenderer::setMatrix(const SkMatrix* matrix) {
-    matrix = refMatrix(matrix);
+void DisplayListRenderer::setMatrix(const SkMatrix& matrix) {
     addStateOp(new (alloc()) SetMatrixOp(matrix));
     StatefulBaseRenderer::setMatrix(matrix);
 }
 
-void DisplayListRenderer::concatMatrix(const SkMatrix* matrix) {
-    matrix = refMatrix(matrix);
+void DisplayListRenderer::concatMatrix(const SkMatrix& matrix) {
     addStateOp(new (alloc()) ConcatMatrixOp(matrix));
     StatefulBaseRenderer::concatMatrix(matrix);
 }
@@ -203,10 +201,9 @@
     return DrawGlInfo::kStatusDone;
 }
 
-status_t DisplayListRenderer::drawBitmap(const SkBitmap* bitmap, const SkMatrix* matrix,
+status_t DisplayListRenderer::drawBitmap(const SkBitmap* bitmap, const SkMatrix& matrix,
         const SkPaint* paint) {
     bitmap = refBitmap(bitmap);
-    matrix = refMatrix(matrix);
     paint = refPaint(paint);
 
     addDrawOp(new (alloc()) DrawBitmapMatrixOp(bitmap, matrix, paint));
diff --git a/libs/hwui/DisplayListRenderer.h b/libs/hwui/DisplayListRenderer.h
index f0ae00f..195b00b 100644
--- a/libs/hwui/DisplayListRenderer.h
+++ b/libs/hwui/DisplayListRenderer.h
@@ -86,8 +86,8 @@
     virtual void scale(float sx, float sy);
     virtual void skew(float sx, float sy);
 
-    virtual void setMatrix(const SkMatrix* matrix);
-    virtual void concatMatrix(const SkMatrix* matrix);
+    virtual void setMatrix(const SkMatrix& matrix);
+    virtual void concatMatrix(const SkMatrix& matrix);
 
     // Clip
     virtual bool clipRect(float left, float top, float right, float bottom, SkRegion::Op op);
@@ -106,7 +106,7 @@
     // Bitmap-based
     virtual status_t drawBitmap(const SkBitmap* bitmap, float left, float top,
             const SkPaint* paint);
-    virtual status_t drawBitmap(const SkBitmap* bitmap, const SkMatrix* matrix,
+    virtual status_t drawBitmap(const SkBitmap* bitmap, const SkMatrix& matrix,
             const SkPaint* paint);
     virtual status_t drawBitmap(const SkBitmap* bitmap, float srcLeft, float srcTop,
             float srcRight, float srcBottom, float dstLeft, float dstTop,
@@ -233,17 +233,6 @@
         return regionCopy;
     }
 
-    inline const SkMatrix* refMatrix(const SkMatrix* matrix) {
-        if (matrix) {
-            // Copying the matrix is cheap and prevents against the user changing
-            // the original matrix before the operation that uses it
-            const SkMatrix* copy = new SkMatrix(*matrix);
-            mDisplayListData->matrices.add(copy);
-            return copy;
-        }
-        return matrix;
-    }
-
     inline Layer* refLayer(Layer* layer) {
         mDisplayListData->layers.add(layer);
         mCaches.resourceCache.incrementRefcount(layer);
diff --git a/libs/hwui/DrawProfiler.cpp b/libs/hwui/DrawProfiler.cpp
new file mode 100644
index 0000000..971a66e
--- /dev/null
+++ b/libs/hwui/DrawProfiler.cpp
@@ -0,0 +1,261 @@
+/*
+ * 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 "DrawProfiler.h"
+
+#include <cutils/compiler.h>
+
+#include "OpenGLRenderer.h"
+#include "Properties.h"
+
+#define DEFAULT_MAX_FRAMES 128
+
+#define RETURN_IF_DISABLED() if (CC_LIKELY(mType == kNone)) return
+
+#define NANOS_TO_MILLIS_FLOAT(nanos) ((nanos) * 0.000001f)
+
+#define PROFILE_DRAW_WIDTH 3
+#define PROFILE_DRAW_THRESHOLD_STROKE_WIDTH 2
+#define PROFILE_DRAW_DP_PER_MS 7
+
+// Number of floats we want to display from FrameTimingData
+// If this is changed make sure to update the indexes below
+#define NUM_ELEMENTS 4
+
+#define RECORD_INDEX 0
+#define PREPARE_INDEX 1
+#define PLAYBACK_INDEX 2
+#define SWAPBUFFERS_INDEX 3
+
+// Must be NUM_ELEMENTS in size
+static const SkColor ELEMENT_COLORS[] = { 0xcf3e66cc, 0xcf8f00ff, 0xcfdc3912, 0xcfe69800 };
+static const SkColor CURRENT_FRAME_COLOR = 0xcf5faa4d;
+static const SkColor THRESHOLD_COLOR = 0xff5faa4d;
+
+// We could get this from TimeLord and use the actual frame interval, but
+// this is good enough
+#define FRAME_THRESHOLD 16
+
+namespace android {
+namespace uirenderer {
+
+static int dpToPx(int dp, float density) {
+    return (int) (dp * density + 0.5f);
+}
+
+DrawProfiler::DrawProfiler()
+        : mType(kNone)
+        , mDensity(0)
+        , mData(NULL)
+        , mDataSize(0)
+        , mCurrentFrame(-1)
+        , mPreviousTime(0)
+        , mVerticalUnit(0)
+        , mHorizontalUnit(0)
+        , mThresholdStroke(0) {
+    setDensity(1);
+}
+
+DrawProfiler::~DrawProfiler() {
+    destroyData();
+}
+
+void DrawProfiler::setDensity(float density) {
+    if (CC_UNLIKELY(mDensity != density)) {
+        mDensity = density;
+        mVerticalUnit = dpToPx(PROFILE_DRAW_DP_PER_MS, density);
+        mHorizontalUnit = dpToPx(PROFILE_DRAW_WIDTH, density);
+        mThresholdStroke = dpToPx(PROFILE_DRAW_THRESHOLD_STROKE_WIDTH, density);
+    }
+}
+
+void DrawProfiler::startFrame(nsecs_t recordDurationNanos) {
+    RETURN_IF_DISABLED();
+    mData[mCurrentFrame].record = NANOS_TO_MILLIS_FLOAT(recordDurationNanos);
+    mPreviousTime = systemTime(CLOCK_MONOTONIC);
+}
+
+void DrawProfiler::markPlaybackStart() {
+    RETURN_IF_DISABLED();
+    nsecs_t now = systemTime(CLOCK_MONOTONIC);
+    mData[mCurrentFrame].prepare = NANOS_TO_MILLIS_FLOAT(now - mPreviousTime);
+    mPreviousTime = now;
+}
+
+void DrawProfiler::markPlaybackEnd() {
+    RETURN_IF_DISABLED();
+    nsecs_t now = systemTime(CLOCK_MONOTONIC);
+    mData[mCurrentFrame].playback = NANOS_TO_MILLIS_FLOAT(now - mPreviousTime);
+    mPreviousTime = now;
+}
+
+void DrawProfiler::finishFrame() {
+    RETURN_IF_DISABLED();
+    nsecs_t now = systemTime(CLOCK_MONOTONIC);
+    mData[mCurrentFrame].swapBuffers = NANOS_TO_MILLIS_FLOAT(now - mPreviousTime);
+    mPreviousTime = now;
+    mCurrentFrame = (mCurrentFrame + 1) % mDataSize;
+}
+
+void DrawProfiler::unionDirty(Rect* dirty) {
+    RETURN_IF_DISABLED();
+    // Not worth worrying about minimizing the dirty region for debugging, so just
+    // dirty the entire viewport.
+    if (dirty) {
+        dirty->setEmpty();
+    }
+}
+
+void DrawProfiler::draw(OpenGLRenderer* canvas) {
+    if (CC_LIKELY(mType != kBars)) {
+        return;
+    }
+
+    prepareShapes(canvas->getViewportHeight());
+    drawGraph(canvas);
+    drawCurrentFrame(canvas);
+    drawThreshold(canvas);
+}
+
+void DrawProfiler::createData() {
+    if (mData) return;
+
+    mDataSize = property_get_int32(PROPERTY_PROFILE_MAXFRAMES, DEFAULT_MAX_FRAMES);
+    if (mDataSize <= 0) mDataSize = 1;
+    if (mDataSize > 4096) mDataSize = 4096; // Reasonable maximum
+    mData = (FrameTimingData*) calloc(mDataSize, sizeof(FrameTimingData));
+    mRects = new float*[NUM_ELEMENTS];
+    for (int i = 0; i < NUM_ELEMENTS; i++) {
+        // 4 floats per rect
+        mRects[i] = (float*) calloc(mDataSize, 4 * sizeof(float));
+    }
+    mCurrentFrame = 0;
+}
+
+void DrawProfiler::destroyData() {
+    delete mData;
+    mData = NULL;
+}
+
+void DrawProfiler::addRect(Rect& r, float data, float* shapeOutput) {
+    r.top = r.bottom - (data * mVerticalUnit);
+    shapeOutput[0] = r.left;
+    shapeOutput[1] = r.top;
+    shapeOutput[2] = r.right;
+    shapeOutput[3] = r.bottom;
+    r.bottom = r.top;
+}
+
+void DrawProfiler::prepareShapes(const int baseline) {
+    Rect r;
+    r.right = mHorizontalUnit;
+    for (int i = 0; i < mDataSize; i++) {
+        const int shapeIndex = i * 4;
+        r.bottom = baseline;
+        addRect(r, mData[i].record, mRects[RECORD_INDEX] + shapeIndex);
+        addRect(r, mData[i].prepare, mRects[PREPARE_INDEX] + shapeIndex);
+        addRect(r, mData[i].playback, mRects[PLAYBACK_INDEX] + shapeIndex);
+        addRect(r, mData[i].swapBuffers, mRects[SWAPBUFFERS_INDEX] + shapeIndex);
+        r.translate(mHorizontalUnit, 0);
+    }
+}
+
+void DrawProfiler::drawGraph(OpenGLRenderer* canvas) {
+    SkPaint paint;
+    for (int i = 0; i < NUM_ELEMENTS; i++) {
+        paint.setColor(ELEMENT_COLORS[i]);
+        canvas->drawRects(mRects[i], mDataSize * 4, &paint);
+    }
+}
+
+void DrawProfiler::drawCurrentFrame(OpenGLRenderer* canvas) {
+    // This draws a solid rect over the entirety of the current frame's shape
+    // To do so we use the bottom of mRects[0] and the top of mRects[NUM_ELEMENTS-1]
+    // which will therefore fully overlap the previously drawn rects
+    SkPaint paint;
+    paint.setColor(CURRENT_FRAME_COLOR);
+    const int i = mCurrentFrame * 4;
+    canvas->drawRect(mRects[0][i], mRects[NUM_ELEMENTS-1][i+1], mRects[0][i+2],
+            mRects[0][i+3], &paint);
+}
+
+void DrawProfiler::drawThreshold(OpenGLRenderer* canvas) {
+    SkPaint paint;
+    paint.setColor(THRESHOLD_COLOR);
+    paint.setStrokeWidth(mThresholdStroke);
+
+    float pts[4];
+    pts[0] = 0.0f;
+    pts[1] = pts[3] = canvas->getViewportHeight() - (FRAME_THRESHOLD * mVerticalUnit);
+    pts[2] = canvas->getViewportWidth();
+    canvas->drawLines(pts, 4, &paint);
+}
+
+DrawProfiler::ProfileType DrawProfiler::loadRequestedProfileType() {
+    ProfileType type = kNone;
+    char buf[PROPERTY_VALUE_MAX] = {'\0',};
+    if (property_get(PROPERTY_PROFILE, buf, "") > 0) {
+        if (!strcmp(buf, PROPERTY_PROFILE_VISUALIZE_BARS)) {
+            type = kBars;
+        } else if (!strcmp(buf, "true")) {
+            type = kConsole;
+        }
+    }
+    return type;
+}
+
+bool DrawProfiler::loadSystemProperties() {
+    ProfileType newType = loadRequestedProfileType();
+    if (newType != mType) {
+        mType = newType;
+        if (mType == kNone) {
+            destroyData();
+        } else {
+            createData();
+        }
+        return true;
+    }
+    return false;
+}
+
+void DrawProfiler::dumpData(int fd) {
+    RETURN_IF_DISABLED();
+
+    // This method logs the last N frames (where N is <= mDataSize) since the
+    // last call to dumpData(). In other words if there's a dumpData(), draw frame,
+    // dumpData(), the last dumpData() should only log 1 frame.
+
+    const FrameTimingData emptyData = {0, 0, 0, 0};
+
+    FILE *file = fdopen(fd, "a");
+    fprintf(file, "\n\tDraw\tPrepare\tProcess\tExecute\n");
+
+    for (int frameOffset = 1; frameOffset <= mDataSize; frameOffset++) {
+        int i = (mCurrentFrame + frameOffset) % mDataSize;
+        if (!memcmp(mData + i, &emptyData, sizeof(FrameTimingData))) {
+            continue;
+        }
+        fprintf(file, "\t%3.2f\t%3.2f\t%3.2f\t%3.2f\n",
+                mData[i].record, mData[i].prepare, mData[i].playback, mData[i].swapBuffers);
+    }
+    // reset the buffer
+    memset(mData, 0, sizeof(FrameTimingData) * mDataSize);
+    mCurrentFrame = 0;
+
+    fflush(file);
+}
+
+} /* namespace uirenderer */
+} /* namespace android */
diff --git a/libs/hwui/DrawProfiler.h b/libs/hwui/DrawProfiler.h
new file mode 100644
index 0000000..c1aa1c6
--- /dev/null
+++ b/libs/hwui/DrawProfiler.h
@@ -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.
+ */
+#ifndef DRAWPROFILER_H
+#define DRAWPROFILER_H
+
+#include <utils/Timers.h>
+#include "Rect.h"
+
+namespace android {
+namespace uirenderer {
+
+class OpenGLRenderer;
+
+class DrawProfiler {
+public:
+    DrawProfiler();
+    ~DrawProfiler();
+
+    bool loadSystemProperties();
+    void setDensity(float density);
+
+    void startFrame(nsecs_t recordDurationNanos = 0);
+    void markPlaybackStart();
+    void markPlaybackEnd();
+    void finishFrame();
+
+    void unionDirty(Rect* dirty);
+    void draw(OpenGLRenderer* canvas);
+
+    void dumpData(int fd);
+
+private:
+    enum ProfileType {
+        kNone,
+        kConsole,
+        kBars,
+    };
+
+    typedef struct {
+        float record;
+        float prepare;
+        float playback;
+        float swapBuffers;
+    } FrameTimingData;
+
+    void createData();
+    void destroyData();
+
+    void addRect(Rect& r, float data, float* shapeOutput);
+    void prepareShapes(const int baseline);
+    void drawGraph(OpenGLRenderer* canvas);
+    void drawCurrentFrame(OpenGLRenderer* canvas);
+    void drawThreshold(OpenGLRenderer* canvas);
+
+    ProfileType loadRequestedProfileType();
+
+    ProfileType mType;
+    float mDensity;
+
+    FrameTimingData* mData;
+    int mDataSize;
+
+    int mCurrentFrame;
+    nsecs_t mPreviousTime;
+
+    int mVerticalUnit;
+    int mHorizontalUnit;
+    int mThresholdStroke;
+
+    /*
+     * mRects represents an array of rect shapes, divided into NUM_ELEMENTS
+     * groups such that each group is drawn with the same paint.
+     * For example mRects[0] is the array of rect floats suitable for
+     * OpenGLRenderer:drawRects() that makes up all the FrameTimingData:record
+     * information.
+     */
+    float** mRects;
+};
+
+} /* namespace uirenderer */
+} /* namespace android */
+
+#endif /* DRAWPROFILER_H */
diff --git a/libs/hwui/OpenGLRenderer.cpp b/libs/hwui/OpenGLRenderer.cpp
index 71836dd..cd09f86 100644
--- a/libs/hwui/OpenGLRenderer.cpp
+++ b/libs/hwui/OpenGLRenderer.cpp
@@ -2042,10 +2042,10 @@
     return DrawGlInfo::kStatusDrew;
 }
 
-status_t OpenGLRenderer::drawBitmap(const SkBitmap* bitmap, const SkMatrix* matrix,
+status_t OpenGLRenderer::drawBitmap(const SkBitmap* bitmap, const SkMatrix& matrix,
         const SkPaint* paint) {
     Rect r(0.0f, 0.0f, bitmap->width(), bitmap->height());
-    const mat4 transform(*matrix);
+    const mat4 transform(matrix);
     transform.mapRect(r);
 
     if (quickRejectSetupScissor(r.left, r.top, r.right, r.bottom)) {
diff --git a/libs/hwui/OpenGLRenderer.h b/libs/hwui/OpenGLRenderer.h
index fc27947..0f953a5 100644
--- a/libs/hwui/OpenGLRenderer.h
+++ b/libs/hwui/OpenGLRenderer.h
@@ -170,7 +170,7 @@
             const SkPaint* paint);
     status_t drawBitmaps(const SkBitmap* bitmap, AssetAtlas::Entry* entry, int bitmapCount,
             TextureVertex* vertices, bool pureTranslate, const Rect& bounds, const SkPaint* paint);
-    virtual status_t drawBitmap(const SkBitmap* bitmap, const SkMatrix* matrix,
+    virtual status_t drawBitmap(const SkBitmap* bitmap, const SkMatrix& matrix,
             const SkPaint* paint);
     virtual status_t drawBitmap(const SkBitmap* bitmap, float srcLeft, float srcTop,
             float srcRight, float srcBottom, float dstLeft, float dstTop,
diff --git a/libs/hwui/PathCache.cpp b/libs/hwui/PathCache.cpp
index 5a49f38..d9c06d3 100644
--- a/libs/hwui/PathCache.cpp
+++ b/libs/hwui/PathCache.cpp
@@ -346,7 +346,7 @@
 
     float left, top, offset;
     uint32_t width, height;
-    PathCache::computePathBounds(t->path, t->paint, left, top, offset, width, height);
+    PathCache::computePathBounds(t->path, &t->paint, left, top, offset, width, height);
 
     PathTexture* texture = t->texture;
     texture->left = left;
@@ -357,7 +357,7 @@
 
     if (width <= mMaxTextureSize && height <= mMaxTextureSize) {
         SkBitmap* bitmap = new SkBitmap();
-        drawPath(t->path, t->paint, *bitmap, left, top, offset, width, height);
+        drawPath(t->path, &t->paint, *bitmap, left, top, offset, width, height);
         t->setResult(bitmap);
     } else {
         texture->width = 0;
diff --git a/libs/hwui/PathCache.h b/libs/hwui/PathCache.h
index 847853a..bcfb367 100644
--- a/libs/hwui/PathCache.h
+++ b/libs/hwui/PathCache.h
@@ -293,7 +293,7 @@
     class PathTask: public Task<SkBitmap*> {
     public:
         PathTask(const SkPath* path, const SkPaint* paint, PathTexture* texture):
-            path(path), paint(paint), texture(texture) {
+            path(path), paint(*paint), texture(texture) {
         }
 
         ~PathTask() {
@@ -301,7 +301,8 @@
         }
 
         const SkPath* path;
-        const SkPaint* paint;
+        //copied, since input paint may not be immutable
+        const SkPaint paint;
         PathTexture* texture;
     };
 
diff --git a/libs/hwui/Properties.h b/libs/hwui/Properties.h
index 20b8f2f..12241b8 100644
--- a/libs/hwui/Properties.h
+++ b/libs/hwui/Properties.h
@@ -89,6 +89,36 @@
 #define PROPERTY_DEBUG_NV_PROFILING "debug.hwui.nv_profiling"
 
 /**
+ *  System property used to enable or disable hardware rendering profiling.
+ * The default value of this property is assumed to be false.
+ *
+ * When profiling is enabled, the adb shell dumpsys gfxinfo command will
+ * output extra information about the time taken to execute by the last
+ * frames.
+ *
+ * Possible values:
+ * "true", to enable profiling
+ * "visual_bars", to enable profiling and visualize the results on screen
+ * "false", to disable profiling
+ */
+#define PROPERTY_PROFILE "debug.hwui.profile"
+#define PROPERTY_PROFILE_VISUALIZE_BARS "visual_bars"
+
+/**
+ * System property used to specify the number of frames to be used
+ * when doing hardware rendering profiling.
+ * The default value of this property is #PROFILE_MAX_FRAMES.
+ *
+ * When profiling is enabled, the adb shell dumpsys gfxinfo command will
+ * output extra information about the time taken to execute by the last
+ * frames.
+ *
+ * Possible values:
+ * "60", to set the limit of frames to 60
+ */
+#define PROPERTY_PROFILE_MAXFRAMES "debug.hwui.profile.maxframes"
+
+/**
  * Used to enable/disable non-rectangular clipping debugging.
  *
  * The accepted values are:
diff --git a/libs/hwui/RenderNode.cpp b/libs/hwui/RenderNode.cpp
index df74f31..baf372a 100644
--- a/libs/hwui/RenderNode.cpp
+++ b/libs/hwui/RenderNode.cpp
@@ -93,6 +93,17 @@
     ALOGD("%*sDone (%p, %s)", (level - 1) * 2, "", this, getName());
 }
 
+int RenderNode::getDebugSize() {
+    int size = sizeof(RenderNode);
+    if (mStagingDisplayListData) {
+        size += mStagingDisplayListData->allocator.usedSize();
+    }
+    if (mDisplayListData && mDisplayListData != mStagingDisplayListData) {
+        size += mDisplayListData->allocator.usedSize();
+    }
+    return size;
+}
+
 void RenderNode::prepareTree(TreeInfo& info) {
     ATRACE_CALL();
 
@@ -212,9 +223,9 @@
         renderer.translate(properties().getLeft(), properties().getTop());
     }
     if (properties().getStaticMatrix()) {
-        renderer.concatMatrix(properties().getStaticMatrix());
+        renderer.concatMatrix(*properties().getStaticMatrix());
     } else if (properties().getAnimationMatrix()) {
-        renderer.concatMatrix(properties().getAnimationMatrix());
+        renderer.concatMatrix(*properties().getAnimationMatrix());
     }
     if (properties().hasTransformMatrix()) {
         if (properties().isTransformTranslateOnly()) {
diff --git a/libs/hwui/RenderNode.h b/libs/hwui/RenderNode.h
index 1811a7b..1a5377b 100644
--- a/libs/hwui/RenderNode.h
+++ b/libs/hwui/RenderNode.h
@@ -119,6 +119,7 @@
     void replayNodeInParent(ReplayStateStruct& replayStruct, const int level);
 
     ANDROID_API void output(uint32_t level = 1);
+    ANDROID_API int getDebugSize();
 
     bool isRenderable() const {
         return mDisplayListData && mDisplayListData->hasDrawOps;
diff --git a/libs/hwui/Renderer.h b/libs/hwui/Renderer.h
index 23cab0e..320895c 100644
--- a/libs/hwui/Renderer.h
+++ b/libs/hwui/Renderer.h
@@ -170,8 +170,8 @@
     virtual void scale(float sx, float sy) = 0;
     virtual void skew(float sx, float sy) = 0;
 
-    virtual void setMatrix(const SkMatrix* matrix) = 0;
-    virtual void concatMatrix(const SkMatrix* matrix) = 0;
+    virtual void setMatrix(const SkMatrix& matrix) = 0;
+    virtual void concatMatrix(const SkMatrix& matrix) = 0;
 
     // clip
     virtual const Rect& getLocalClipBounds() const = 0;
@@ -193,7 +193,7 @@
     // Bitmap-based
     virtual status_t drawBitmap(const SkBitmap* bitmap, float left, float top,
             const SkPaint* paint) = 0;
-    virtual status_t drawBitmap(const SkBitmap* bitmap, const SkMatrix* matrix,
+    virtual status_t drawBitmap(const SkBitmap* bitmap, const SkMatrix& matrix,
             const SkPaint* paint) = 0;
     virtual status_t drawBitmap(const SkBitmap* bitmap, float srcLeft, float srcTop,
             float srcRight, float srcBottom, float dstLeft, float dstTop,
diff --git a/libs/hwui/StatefulBaseRenderer.cpp b/libs/hwui/StatefulBaseRenderer.cpp
index 90039e9..fae25a6 100644
--- a/libs/hwui/StatefulBaseRenderer.cpp
+++ b/libs/hwui/StatefulBaseRenderer.cpp
@@ -121,20 +121,16 @@
     mSnapshot->transform->skew(sx, sy);
 }
 
-void StatefulBaseRenderer::setMatrix(const SkMatrix* matrix) {
-    if (matrix) {
-        mSnapshot->transform->load(*matrix);
-    } else {
-        mSnapshot->transform->loadIdentity();
-    }
+void StatefulBaseRenderer::setMatrix(const SkMatrix& matrix) {
+    mSnapshot->transform->load(matrix);
 }
 
 void StatefulBaseRenderer::setMatrix(const Matrix4& matrix) {
     mSnapshot->transform->load(matrix);
 }
 
-void StatefulBaseRenderer::concatMatrix(const SkMatrix* matrix) {
-    mat4 transform(*matrix);
+void StatefulBaseRenderer::concatMatrix(const SkMatrix& matrix) {
+    mat4 transform(matrix);
     mSnapshot->transform->multiply(transform);
 }
 
diff --git a/libs/hwui/StatefulBaseRenderer.h b/libs/hwui/StatefulBaseRenderer.h
index 057006b..f38c752 100644
--- a/libs/hwui/StatefulBaseRenderer.h
+++ b/libs/hwui/StatefulBaseRenderer.h
@@ -75,9 +75,9 @@
     virtual void scale(float sx, float sy);
     virtual void skew(float sx, float sy);
 
-    virtual void setMatrix(const SkMatrix* matrix);
+    virtual void setMatrix(const SkMatrix& matrix);
     void setMatrix(const Matrix4& matrix); // internal only convenience method
-    virtual void concatMatrix(const SkMatrix* matrix);
+    virtual void concatMatrix(const SkMatrix& matrix);
     void concatMatrix(const Matrix4& matrix); // internal only convenience method
 
     // Clip
diff --git a/libs/hwui/renderthread/CanvasContext.cpp b/libs/hwui/renderthread/CanvasContext.cpp
index b6b3428..9ebee1d 100644
--- a/libs/hwui/renderthread/CanvasContext.cpp
+++ b/libs/hwui/renderthread/CanvasContext.cpp
@@ -31,7 +31,6 @@
 
 #define PROPERTY_RENDER_DIRTY_REGIONS "debug.hwui.render_dirty_regions"
 #define GLES_VERSION 2
-#define USE_TEXTURE_ATLAS false
 
 // Android-specific addition that is used to show when frames began in systrace
 EGLAPI void EGLAPIENTRY eglBeginFrame(EGLDisplay dpy, EGLSurface surface);
@@ -229,7 +228,7 @@
 }
 
 void GlobalContext::initAtlas() {
-    if (USE_TEXTURE_ATLAS) {
+    if (mAtlasBuffer.get()) {
         Caches::getInstance().assetAtlas.init(mAtlasBuffer, mAtlasMap, mAtlasMapSize);
     }
 }
@@ -428,27 +427,11 @@
     mHaveNewSurface |= mGlobalContext->makeCurrent(mEglSurface);
 }
 
-void CanvasContext::prepareDraw(const Vector<DeferredLayerUpdater*>* layerUpdaters,
-        TreeInfo& info) {
-    LOG_ALWAYS_FATAL_IF(!mCanvas, "Cannot prepareDraw without a canvas!");
-    makeCurrent();
-
-    processLayerUpdates(layerUpdaters, info);
-    if (info.out.hasAnimations) {
-        // TODO: Uh... crap?
-    }
-    prepareTree(info);
-}
-
-void CanvasContext::processLayerUpdates(const Vector<DeferredLayerUpdater*>* layerUpdaters,
-        TreeInfo& info) {
-    for (size_t i = 0; i < layerUpdaters->size(); i++) {
-        DeferredLayerUpdater* update = layerUpdaters->itemAt(i);
-        bool success = update->apply(info);
-        LOG_ALWAYS_FATAL_IF(!success, "Failed to update layer!");
-        if (update->backingLayer()->deferredUpdateScheduled) {
-            mCanvas->pushLayerUpdate(update->backingLayer());
-        }
+void CanvasContext::processLayerUpdate(DeferredLayerUpdater* layerUpdater, TreeInfo& info) {
+    bool success = layerUpdater->apply(info);
+    LOG_ALWAYS_FATAL_IF(!success, "Failed to update layer!");
+    if (layerUpdater->backingLayer()->deferredUpdateScheduled) {
+        mCanvas->pushLayerUpdate(layerUpdater->backingLayer());
     }
 }
 
@@ -486,6 +469,8 @@
     LOG_ALWAYS_FATAL_IF(!mCanvas || mEglSurface == EGL_NO_SURFACE,
             "drawDisplayList called on a context with no canvas or surface!");
 
+    profiler().markPlaybackStart();
+
     EGLint width, height;
     mGlobalContext->beginFrame(mEglSurface, &width, &height);
     if (width != mCanvas->getViewportWidth() || height != mCanvas->getViewportHeight()) {
@@ -493,6 +478,8 @@
         dirty = NULL;
     } else if (!mDirtyRegionsEnabled || mHaveNewSurface) {
         dirty = NULL;
+    } else {
+        profiler().unionDirty(dirty);
     }
 
     status_t status;
@@ -506,14 +493,17 @@
     Rect outBounds;
     status |= mCanvas->drawDisplayList(mRootRenderNode.get(), outBounds);
 
-    // TODO: Draw debug info
-    // TODO: Performance tracking
+    profiler().draw(mCanvas);
 
     mCanvas->finish();
 
+    profiler().markPlaybackEnd();
+
     if (status & DrawGlInfo::kStatusDrew) {
         swapBuffers();
     }
+
+    profiler().finishFrame();
 }
 
 // Called by choreographer to do an RT-driven animation
@@ -524,6 +514,8 @@
 
     ATRACE_CALL();
 
+    profiler().startFrame();
+
     TreeInfo info;
     info.evaluateAnimations = true;
     info.performStagingPush = false;
@@ -556,6 +548,13 @@
     return LayerRenderer::copyLayer(layer->backingLayer(), bitmap);
 }
 
+void CanvasContext::flushCaches(Caches::FlushMode flushMode) {
+    if (mGlobalContext->hasContext()) {
+        requireGlContext();
+        Caches::getInstance().flush(flushMode);
+    }
+}
+
 void CanvasContext::runWithGlContext(RenderTask* task) {
     requireGlContext();
     task->run();
diff --git a/libs/hwui/renderthread/CanvasContext.h b/libs/hwui/renderthread/CanvasContext.h
index a54b33e..00c5bf0 100644
--- a/libs/hwui/renderthread/CanvasContext.h
+++ b/libs/hwui/renderthread/CanvasContext.h
@@ -23,6 +23,7 @@
 #include <utils/Functor.h>
 #include <utils/Vector.h>
 
+#include "../DrawProfiler.h"
 #include "../RenderNode.h"
 #include "RenderTask.h"
 #include "RenderThread.h"
@@ -54,7 +55,8 @@
     void setup(int width, int height, const Vector3& lightCenter, float lightRadius);
     void setOpaque(bool opaque);
     void makeCurrent();
-    void prepareDraw(const Vector<DeferredLayerUpdater*>* layerUpdaters, TreeInfo& info);
+    void processLayerUpdate(DeferredLayerUpdater* layerUpdater, TreeInfo& info);
+    void prepareTree(TreeInfo& info);
     void draw(Rect* dirty);
     void destroyCanvasAndSurface();
 
@@ -63,6 +65,8 @@
 
     bool copyLayerInto(DeferredLayerUpdater* layer, SkBitmap* bitmap);
 
+    void flushCaches(Caches::FlushMode flushMode);
+
     void invokeFunctor(Functor* functor);
 
     void runWithGlContext(RenderTask* task);
@@ -75,12 +79,11 @@
 
     void notifyFramePending();
 
+    DrawProfiler& profiler() { return mProfiler; }
+
 private:
     friend class RegisterFrameCallbackTask;
 
-    void processLayerUpdates(const Vector<DeferredLayerUpdater*>* layerUpdaters, TreeInfo& info);
-    void prepareTree(TreeInfo& info);
-
     void setSurface(ANativeWindow* window);
     void swapBuffers();
     void requireSurface();
@@ -98,6 +101,8 @@
     bool mHaveNewSurface;
 
     const sp<RenderNode> mRootRenderNode;
+
+    DrawProfiler mProfiler;
 };
 
 } /* namespace renderthread */
diff --git a/libs/hwui/renderthread/DrawFrameTask.cpp b/libs/hwui/renderthread/DrawFrameTask.cpp
index ee3e059..61d67ca 100644
--- a/libs/hwui/renderthread/DrawFrameTask.cpp
+++ b/libs/hwui/renderthread/DrawFrameTask.cpp
@@ -21,6 +21,7 @@
 #include <utils/Log.h>
 #include <utils/Trace.h>
 
+#include "../DeferredLayerUpdater.h"
 #include "../DisplayList.h"
 #include "../RenderNode.h"
 #include "CanvasContext.h"
@@ -34,6 +35,8 @@
         : mRenderThread(NULL)
         , mContext(NULL)
         , mFrameTimeNanos(0)
+        , mRecordDurationNanos(0)
+        , mDensity(1.0f) // safe enough default
         , mSyncResult(kSync_OK) {
 }
 
@@ -45,17 +48,22 @@
     mContext = context;
 }
 
-void DrawFrameTask::addLayer(DeferredLayerUpdater* layer) {
-    LOG_ALWAYS_FATAL_IF(!mContext, "Lifecycle violation, there's no context to addLayer with!");
+void DrawFrameTask::pushLayerUpdate(DeferredLayerUpdater* layer) {
+    LOG_ALWAYS_FATAL_IF(!mContext, "Lifecycle violation, there's no context to pushLayerUpdate with!");
 
-    mLayers.push(layer);
+    for (size_t i = 0; i < mLayers.size(); i++) {
+        if (mLayers[i].get() == layer) {
+            return;
+        }
+    }
+    mLayers.push_back(layer);
 }
 
-void DrawFrameTask::removeLayer(DeferredLayerUpdater* layer) {
+void DrawFrameTask::removeLayerUpdate(DeferredLayerUpdater* layer) {
     for (size_t i = 0; i < mLayers.size(); i++) {
-        if (mLayers[i] == layer) {
-            mLayers.removeAt(i);
-            break;
+        if (mLayers[i].get() == layer) {
+            mLayers.erase(mLayers.begin() + i);
+            return;
         }
     }
 }
@@ -64,15 +72,17 @@
     mDirty.set(left, top, right, bottom);
 }
 
-int DrawFrameTask::drawFrame(nsecs_t frameTimeNanos) {
+int DrawFrameTask::drawFrame(nsecs_t frameTimeNanos, nsecs_t recordDurationNanos) {
     LOG_ALWAYS_FATAL_IF(!mContext, "Cannot drawFrame with no CanvasContext!");
 
     mSyncResult = kSync_OK;
     mFrameTimeNanos = frameTimeNanos;
+    mRecordDurationNanos = recordDurationNanos;
     postAndWait();
 
     // Reset the single-frame data
     mFrameTimeNanos = 0;
+    mRecordDurationNanos = 0;
     mDirty.setEmpty();
 
     return mSyncResult;
@@ -87,6 +97,9 @@
 void DrawFrameTask::run() {
     ATRACE_NAME("DrawFrame");
 
+    mContext->profiler().setDensity(mDensity);
+    mContext->profiler().startFrame(mRecordDurationNanos);
+
     bool canUnblockUiThread;
     bool canDrawThisFrame;
     {
@@ -125,7 +138,16 @@
     mContext->makeCurrent();
     Caches::getInstance().textureCache.resetMarkInUse();
     initTreeInfo(info);
-    mContext->prepareDraw(&mLayers, info);
+
+    for (size_t i = 0; i < mLayers.size(); i++) {
+        mContext->processLayerUpdate(mLayers[i].get(), info);
+    }
+    mLayers.clear();
+    if (info.out.hasAnimations) {
+        // TODO: Uh... crap?
+    }
+    mContext->prepareTree(info);
+
     if (info.out.hasAnimations) {
         // TODO: dirty calculations, for now just do a full-screen inval
         mDirty.setEmpty();
diff --git a/libs/hwui/renderthread/DrawFrameTask.h b/libs/hwui/renderthread/DrawFrameTask.h
index acbc02a..d4129b6 100644
--- a/libs/hwui/renderthread/DrawFrameTask.h
+++ b/libs/hwui/renderthread/DrawFrameTask.h
@@ -16,10 +16,11 @@
 #ifndef DRAWFRAMETASK_H
 #define DRAWFRAMETASK_H
 
+#include <vector>
+
 #include <utils/Condition.h>
 #include <utils/Mutex.h>
 #include <utils/StrongPointer.h>
-#include <utils/Vector.h>
 
 #include "RenderTask.h"
 
@@ -56,11 +57,12 @@
 
     void setContext(RenderThread* thread, CanvasContext* context);
 
-    void addLayer(DeferredLayerUpdater* layer);
-    void removeLayer(DeferredLayerUpdater* layer);
+    void pushLayerUpdate(DeferredLayerUpdater* layer);
+    void removeLayerUpdate(DeferredLayerUpdater* layer);
 
     void setDirty(int left, int top, int right, int bottom);
-    int drawFrame(nsecs_t frameTimeNanos);
+    void setDensity(float density) { mDensity = density; }
+    int drawFrame(nsecs_t frameTimeNanos, nsecs_t recordDurationNanos);
 
     virtual void run();
 
@@ -80,13 +82,11 @@
      *********************************************/
     Rect mDirty;
     nsecs_t mFrameTimeNanos;
+    nsecs_t mRecordDurationNanos;
+    float mDensity;
+    std::vector< sp<DeferredLayerUpdater> > mLayers;
 
     int mSyncResult;
-
-    /*********************************************
-     *  Multi frame data
-     *********************************************/
-    Vector<DeferredLayerUpdater*> mLayers;
 };
 
 } /* namespace renderthread */
diff --git a/libs/hwui/renderthread/RenderProxy.cpp b/libs/hwui/renderthread/RenderProxy.cpp
index 2e103d8..0901963 100644
--- a/libs/hwui/renderthread/RenderProxy.cpp
+++ b/libs/hwui/renderthread/RenderProxy.cpp
@@ -99,16 +99,20 @@
     post(task);
 }
 
-CREATE_BRIDGE0(loadSystemProperties) {
+CREATE_BRIDGE1(loadSystemProperties, CanvasContext* context) {
     bool needsRedraw = false;
     if (Caches::hasInstance()) {
         needsRedraw = Caches::getInstance().initProperties();
     }
+    if (args->context->profiler().loadSystemProperties()) {
+        needsRedraw = true;
+    }
     return (void*) needsRedraw;
 }
 
 bool RenderProxy::loadSystemProperties() {
     SETUP_TASK(loadSystemProperties);
+    args->context = mContext;
     return (bool) postAndWait(task);
 }
 
@@ -175,10 +179,11 @@
     post(task);
 }
 
-int RenderProxy::syncAndDrawFrame(nsecs_t frameTimeNanos,
-        int dirtyLeft, int dirtyTop, int dirtyRight, int dirtyBottom) {
+int RenderProxy::syncAndDrawFrame(nsecs_t frameTimeNanos, nsecs_t recordDurationNanos,
+        float density, int dirtyLeft, int dirtyTop, int dirtyRight, int dirtyBottom) {
     mDrawFrameTask.setDirty(dirtyLeft, dirtyTop, dirtyRight, dirtyBottom);
-    return mDrawFrameTask.drawFrame(frameTimeNanos);
+    mDrawFrameTask.setDensity(density);
+    return mDrawFrameTask.drawFrame(frameTimeNanos, recordDurationNanos);
 }
 
 CREATE_BRIDGE1(destroyCanvasAndSurface, CanvasContext* context) {
@@ -224,10 +229,21 @@
     postAndWait(task);
 }
 
+CREATE_BRIDGE1(destroyLayer, Layer* layer) {
+    LayerRenderer::destroyLayer(args->layer);
+    return NULL;
+}
+
+static void enqueueDestroyLayer(Layer* layer) {
+    SETUP_TASK(destroyLayer);
+    args->layer = layer;
+    RenderThread::getInstance().queue(task);
+}
+
 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);
+    return new DeferredLayerUpdater(layer, enqueueDestroyLayer);
 }
 
 DeferredLayerUpdater* RenderProxy::createDisplayListLayer(int width, int height) {
@@ -237,14 +253,13 @@
     args->context = mContext;
     void* retval = postAndWait(task);
     DeferredLayerUpdater* layer = reinterpret_cast<DeferredLayerUpdater*>(retval);
-    mDrawFrameTask.addLayer(layer);
     return layer;
 }
 
 CREATE_BRIDGE1(createTextureLayer, CanvasContext* context) {
     Layer* layer = args->context->createTextureLayer();
     if (!layer) return 0;
-    return new DeferredLayerUpdater(layer);
+    return new DeferredLayerUpdater(layer, enqueueDestroyLayer);
 }
 
 DeferredLayerUpdater* RenderProxy::createTextureLayer() {
@@ -252,15 +267,9 @@
     args->context = mContext;
     void* retval = postAndWait(task);
     DeferredLayerUpdater* layer = reinterpret_cast<DeferredLayerUpdater*>(retval);
-    mDrawFrameTask.addLayer(layer);
     return layer;
 }
 
-CREATE_BRIDGE1(destroyLayer, Layer* layer) {
-    LayerRenderer::destroyLayer(args->layer);
-    return NULL;
-}
-
 CREATE_BRIDGE3(copyLayerInto, CanvasContext* context, DeferredLayerUpdater* layer,
         SkBitmap* bitmap) {
     bool success = args->context->copyLayerInto(args->layer, args->bitmap);
@@ -275,10 +284,23 @@
     return (bool) postAndWait(task);
 }
 
-void RenderProxy::destroyLayer(DeferredLayerUpdater* layer) {
-    mDrawFrameTask.removeLayer(layer);
-    SETUP_TASK(destroyLayer);
-    args->layer = layer->detachBackingLayer();
+void RenderProxy::pushLayerUpdate(DeferredLayerUpdater* layer) {
+    mDrawFrameTask.pushLayerUpdate(layer);
+}
+
+void RenderProxy::cancelLayerUpdate(DeferredLayerUpdater* layer) {
+    mDrawFrameTask.removeLayerUpdate(layer);
+}
+
+CREATE_BRIDGE2(flushCaches, CanvasContext* context, Caches::FlushMode flushMode) {
+    args->context->flushCaches(args->flushMode);
+    return NULL;
+}
+
+void RenderProxy::flushCaches(Caches::FlushMode flushMode) {
+    SETUP_TASK(flushCaches);
+    args->context = mContext;
+    args->flushMode = flushMode;
     post(task);
 }
 
@@ -303,6 +325,18 @@
     mRenderThread.queueAtFront(task);
 }
 
+CREATE_BRIDGE2(dumpProfileInfo, CanvasContext* context, int fd) {
+    args->context->profiler().dumpData(args->fd);
+    return NULL;
+}
+
+void RenderProxy::dumpProfileInfo(int fd) {
+    SETUP_TASK(dumpProfileInfo);
+    args->context = mContext;
+    args->fd = fd;
+    postAndWait(task);
+}
+
 void RenderProxy::post(RenderTask* task) {
     mRenderThread.queue(task);
 }
diff --git a/libs/hwui/renderthread/RenderProxy.h b/libs/hwui/renderthread/RenderProxy.h
index 8aeb264..944ff9c 100644
--- a/libs/hwui/renderthread/RenderProxy.h
+++ b/libs/hwui/renderthread/RenderProxy.h
@@ -29,6 +29,7 @@
 #include <utils/StrongPointer.h>
 #include <utils/Vector.h>
 
+#include "../Caches.h"
 #include "DrawFrameTask.h"
 
 namespace android {
@@ -68,8 +69,8 @@
     ANDROID_API void pauseSurface(const sp<ANativeWindow>& window);
     ANDROID_API void setup(int width, int height, const Vector3& lightCenter, float lightRadius);
     ANDROID_API void setOpaque(bool opaque);
-    ANDROID_API int syncAndDrawFrame(nsecs_t frameTimeNanos,
-            int dirtyLeft, int dirtyTop, int dirtyRight, int dirtyBottom);
+    ANDROID_API int syncAndDrawFrame(nsecs_t frameTimeNanos, nsecs_t recordDurationNanos,
+            float density, int dirtyLeft, int dirtyTop, int dirtyRight, int dirtyBottom);
     ANDROID_API void destroyCanvasAndSurface();
 
     ANDROID_API void invokeFunctor(Functor* functor, bool waitForCompletion);
@@ -79,11 +80,16 @@
     ANDROID_API DeferredLayerUpdater* createDisplayListLayer(int width, int height);
     ANDROID_API DeferredLayerUpdater* createTextureLayer();
     ANDROID_API bool copyLayerInto(DeferredLayerUpdater* layer, SkBitmap* bitmap);
-    ANDROID_API void destroyLayer(DeferredLayerUpdater* layer);
+    ANDROID_API void pushLayerUpdate(DeferredLayerUpdater* layer);
+    ANDROID_API void cancelLayerUpdate(DeferredLayerUpdater* layer);
+
+    ANDROID_API void flushCaches(Caches::FlushMode flushMode);
 
     ANDROID_API void fence();
     ANDROID_API void notifyFramePending();
 
+    ANDROID_API void dumpProfileInfo(int fd);
+
 private:
     RenderThread& mRenderThread;
     CanvasContext* mContext;
diff --git a/media/java/android/media/AudioDevicePort.java b/media/java/android/media/AudioDevicePort.java
new file mode 100644
index 0000000..c088906
--- /dev/null
+++ b/media/java/android/media/AudioDevicePort.java
@@ -0,0 +1,85 @@
+/*
+ * 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.media;
+
+/**
+ * The AudioDevicePort is a specialized type of AudioPort
+ * describing an input (e.g microphone) or output device (e.g speaker)
+ * of the system.
+ * An AudioDevicePort is an AudioPort controlled by the audio HAL, almost always a physical
+ * device at the boundary of the audio system.
+ * In addition to base audio port attributes, the device descriptor contains:
+ * - the device type (e.g AudioManager.DEVICE_OUT_SPEAKER)
+ * - the device address (e.g MAC adddress for AD2P sink).
+ * @see AudioPort
+ * @hide
+ */
+
+public class AudioDevicePort extends AudioPort {
+
+    private final int mType;
+    private final String mAddress;
+
+    AudioDevicePort(AudioHandle handle, int[] samplingRates, int[] channelMasks,
+            int[] formats, AudioGain[] gains, int type, String address) {
+        super(handle,
+             (AudioManager.isInputDevice(type) == true)  ?
+                        AudioPort.ROLE_SOURCE : AudioPort.ROLE_SINK,
+             samplingRates, channelMasks, formats, gains);
+        mType = type;
+        mAddress = address;
+    }
+
+    /**
+     * Get the device type (e.g AudioManager.DEVICE_OUT_SPEAKER)
+     */
+    public int type() {
+        return mType;
+    }
+
+    /**
+     * Get the device address. Address format varies with the device type.
+     * - USB devices ({@link AudioManager#DEVICE_OUT_USB_DEVICE},
+     * {@link AudioManager#DEVICE_IN_USB_DEVICE}) use an address composed of the ALSA card number
+     * and device number: "card=2;device=1"
+     * - Bluetooth devices ({@link AudioManager#DEVICE_OUT_BLUETOOTH_SCO},
+     * {@link AudioManager#DEVICE_OUT_BLUETOOTH_SCO}, {@link AudioManager#DEVICE_OUT_BLUETOOTH_A2DP})
+     * use the MAC address of the bluetooth device in the form "00:11:22:AA:BB:CC" as reported by
+     * {@link BluetoothDevice#getAddress()}.
+     * - Deivces that do not have an address will indicate an empty string "".
+     */
+    public String address() {
+        return mAddress;
+    }
+
+    /**
+     * Build a specific configuration of this audio device port for use by methods
+     * like AudioManager.connectAudioPatch().
+     */
+    public AudioDevicePortConfig buildConfig(int samplingRate, int channelMask, int format,
+                                          AudioGainConfig gain) {
+        return new AudioDevicePortConfig(this, samplingRate, channelMask, format, gain);
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (o == null || !(o instanceof AudioDevicePort)) {
+            return false;
+        }
+        return super.equals(o);
+    }
+}
diff --git a/media/java/android/media/AudioDevicePortConfig.java b/media/java/android/media/AudioDevicePortConfig.java
new file mode 100644
index 0000000..a381e10
--- /dev/null
+++ b/media/java/android/media/AudioDevicePortConfig.java
@@ -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.
+ */
+
+package android.media;
+
+/**
+ * An AudioDevicePortConfig describes a possible configuration of an output or input device
+ * (speaker, headphone, microphone ...).
+ * It is used to specify a sink or source when creating a connection with
+ * AudioManager.connectAudioPatch().
+ * An AudioDevicePortConfig is obtained from AudioDevicePort.buildConfig().
+ * @hide
+ */
+
+public class AudioDevicePortConfig extends AudioPortConfig {
+    AudioDevicePortConfig(AudioDevicePort devicePort, int samplingRate, int channelMask,
+            int format, AudioGainConfig gain) {
+        super((AudioPort)devicePort, samplingRate, channelMask, format, gain);
+    }
+
+    /**
+     * Returns the audio device port this AudioDevicePortConfig is issued from.
+     */
+    public AudioDevicePort port() {
+        return (AudioDevicePort)mPort;
+    }
+}
+
diff --git a/media/java/android/media/AudioFormat.java b/media/java/android/media/AudioFormat.java
index b07d2c5..4b4be1b 100644
--- a/media/java/android/media/AudioFormat.java
+++ b/media/java/android/media/AudioFormat.java
@@ -16,6 +16,11 @@
 
 package android.media;
 
+import android.annotation.IntDef;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
 /**
  * The AudioFormat class is used to access a number of audio format and
  * channel configuration constants. They are for instance used
@@ -162,4 +167,151 @@
         throw new UnsupportedOperationException("There is no valid usage of this constructor");
     }
 
+    /**
+     * Private constructor with an ignored argument to differentiate from the removed default ctor
+     * @param ignoredArgument
+     */
+    private AudioFormat(int ignoredArgument) {
+    }
+
+    /** @hide */
+    public final static int AUDIO_FORMAT_HAS_PROPERTY_NONE = 0x0;
+    /** @hide */
+    public final static int AUDIO_FORMAT_HAS_PROPERTY_ENCODING = 0x1 << 0;
+    /** @hide */
+    public final static int AUDIO_FORMAT_HAS_PROPERTY_SAMPLE_RATE = 0x1 << 1;
+    /** @hide */
+    public final static int AUDIO_FORMAT_HAS_PROPERTY_CHANNEL_MASK = 0x1 << 2;
+
+    private int mEncoding;
+    private int mSampleRate;
+    private int mChannelMask;
+    private int mPropertySetMask;
+
+    /**
+     * @hide CANDIDATE FOR PUBLIC API
+     * Builder class for {@link AudioFormat} objects.
+     */
+    public static class Builder {
+        private int mEncoding = ENCODING_DEFAULT;
+        private int mSampleRate = 0;
+        private int mChannelMask = CHANNEL_INVALID;
+        private int mPropertySetMask = AUDIO_FORMAT_HAS_PROPERTY_NONE;
+
+        /**
+         * Constructs a new Builder with the defaults.
+         */
+        public Builder() {
+        }
+
+        /**
+         * Constructs a new Builder from a given {@link AudioFormat}.
+         * @param af the {@link AudioFormat} object whose data will be reused in the new Builder.
+         */
+        public Builder(AudioFormat af) {
+            mEncoding = af.mEncoding;
+            mSampleRate = af.mSampleRate;
+            mChannelMask = af.mChannelMask;
+            mPropertySetMask = af.mPropertySetMask;
+        }
+
+        /**
+         * Combines all of the format characteristics that have been set and return a new
+         * {@link AudioFormat} object.
+         * @return a new {@link AudioFormat} object
+         */
+        public AudioFormat build() {
+            AudioFormat af = new AudioFormat(1980/*ignored*/);
+            af.mEncoding = mEncoding;
+            af.mSampleRate = mSampleRate;
+            af.mChannelMask = mChannelMask;
+            af.mPropertySetMask = mPropertySetMask;
+            return af;
+        }
+
+        /**
+         * Sets the data encoding format.
+         * @param encoding one of {@link AudioFormat#ENCODING_DEFAULT},
+         *     {@link AudioFormat#ENCODING_PCM_8BIT},
+         *     {@link AudioFormat#ENCODING_PCM_16BIT},
+         *     {@link AudioFormat#ENCODING_PCM_FLOAT}.
+         * @return the same Builder instance.
+         * @throws java.lang.IllegalArgumentException
+         */
+        public Builder setEncoding(@Encoding int encoding) throws IllegalArgumentException {
+            switch (encoding) {
+                case ENCODING_DEFAULT:
+                    mEncoding = ENCODING_PCM_16BIT;
+                    break;
+                case ENCODING_PCM_8BIT:
+                case ENCODING_PCM_16BIT:
+                case ENCODING_PCM_FLOAT:
+                    mEncoding = encoding;
+                    break;
+                case ENCODING_INVALID:
+                default:
+                    throw new IllegalArgumentException("Invalid encoding " + encoding);
+            }
+            mPropertySetMask |= AUDIO_FORMAT_HAS_PROPERTY_ENCODING;
+            return this;
+        }
+
+        /**
+         * Sets the channel mask.
+         * @param channelMask describes the configuration of the audio channels.
+         *    <p>For output, the mask should be a combination of
+         *    {@link AudioFormat#CHANNEL_OUT_FRONT_LEFT},
+         *    {@link AudioFormat#CHANNEL_OUT_FRONT_CENTER},
+         *    {@link AudioFormat#CHANNEL_OUT_FRONT_RIGHT},
+         *    {@link AudioFormat#CHANNEL_OUT_SIDE_LEFT},
+         *    {@link AudioFormat#CHANNEL_OUT_SIDE_RIGHT},
+         *    {@link AudioFormat#CHANNEL_OUT_BACK_LEFT},
+         *    {@link AudioFormat#CHANNEL_OUT_BACK_RIGHT}.
+         *    <p>for input, the mask should be {@link AudioFormat#CHANNEL_IN_MONO} or
+         *    {@link AudioFormat#CHANNEL_IN_STEREO}.  {@link AudioFormat#CHANNEL_IN_MONO} is
+         *    guaranteed to work on all devices.
+         * @return the same Builder instance.
+         */
+        public Builder setChannelMask(int channelMask) {
+            // only validated when used, with input or output context
+            mChannelMask = channelMask;
+            mPropertySetMask |= AUDIO_FORMAT_HAS_PROPERTY_CHANNEL_MASK;
+            return this;
+        }
+
+        /**
+         * Sets the sample rate.
+         * @param sampleRate the sample rate expressed in Hz
+         * @return the same Builder instance.
+         * @throws java.lang.IllegalArgumentException
+         */
+        public Builder setSampleRate(int sampleRate) throws IllegalArgumentException {
+            if ((sampleRate <= 0) || (sampleRate > 192000)) {
+                throw new IllegalArgumentException("Invalid sample rate " + sampleRate);
+            }
+            mSampleRate = sampleRate;
+            mPropertySetMask |= AUDIO_FORMAT_HAS_PROPERTY_SAMPLE_RATE;
+            return this;
+        }
+    }
+
+    @Override
+    public String toString () {
+        return new String("AudioFormat:"
+                + " props=" + mPropertySetMask
+                + " enc=" + mEncoding
+                + " chan=0x" + Integer.toHexString(mChannelMask)
+                + " rate=" + mSampleRate);
+    }
+
+    /** @hide */
+    @IntDef({
+        ENCODING_DEFAULT,
+        ENCODING_PCM_8BIT,
+        ENCODING_PCM_16BIT,
+        ENCODING_PCM_FLOAT
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface Encoding {}
+
 }
diff --git a/media/java/android/media/AudioGain.java b/media/java/android/media/AudioGain.java
new file mode 100644
index 0000000..57709d5
--- /dev/null
+++ b/media/java/android/media/AudioGain.java
@@ -0,0 +1,159 @@
+/*
+ * 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.media;
+
+/**
+ * The AudioGain describes a gain controller. Gain controllers are exposed by
+ * audio ports when the gain is configurable at this port's input or output.
+ * Gain values are expressed in millibels.
+ * A gain controller has the following attributes:
+ * - mode: defines modes of operation or features
+ *    MODE_JOINT: all channel gains are controlled simultaneously
+ *    MODE_CHANNELS: each channel gain is controlled individually
+ *    MODE_RAMP: ramps can be applied when gain changes
+ * - channel mask: indicates for which channels the gain can be controlled
+ * - min value: minimum gain value in millibel
+ * - max value: maximum gain value in millibel
+ * - default value: gain value after reset in millibel
+ * - step value: granularity of gain control in millibel
+ * - min ramp duration: minimum ramp duration in milliseconds
+ * - max ramp duration: maximum ramp duration in milliseconds
+ *
+ * This object is always created by the framework and read only by applications.
+ * Applications get a list of AudioGainDescriptors from AudioPortDescriptor.gains() and can build a
+ * valid gain configuration from AudioGain.buildConfig()
+ * @hide
+ */
+public class AudioGain {
+
+    /**
+     * Bit of AudioGain.mode() field indicating that
+     * all channel gains are controlled simultaneously
+     */
+    public static final int MODE_JOINT = 1;
+    /**
+     * Bit of AudioGain.mode() field indicating that
+     * each channel gain is controlled individually
+     */
+    public static final int MODE_CHANNELS = 2;
+    /**
+     * Bit of AudioGain.mode() field indicating that
+     * ramps can be applied when gain changes. The type of ramp (linear, log etc...) is
+     * implementation specific.
+     */
+    public static final int MODE_RAMP = 4;
+
+    private final int mIndex;
+    private final int mMode;
+    private final int mChannelMask;
+    private final int mMinValue;
+    private final int mMaxValue;
+    private final int mDefaultValue;
+    private final int mStepValue;
+    private final int mRampDurationMinMs;
+    private final int mRampDurationMaxMs;
+
+    // The channel mask passed to the constructor is as specified in AudioFormat
+    // (e.g. AudioFormat.CHANNEL_OUT_STEREO)
+    AudioGain(int index, int mode, int channelMask,
+                        int minValue, int maxValue, int defaultValue, int stepValue,
+                        int rampDurationMinMs, int rampDurationMaxMs) {
+        mIndex = index;
+        mMode = mode;
+        mChannelMask = channelMask;
+        mMinValue = minValue;
+        mMaxValue = maxValue;
+        mDefaultValue = defaultValue;
+        mStepValue = stepValue;
+        mRampDurationMinMs = rampDurationMinMs;
+        mRampDurationMaxMs = rampDurationMaxMs;
+    }
+
+    /**
+     * Bit field indicating supported modes of operation
+     */
+    public int mode() {
+        return mMode;
+    }
+
+    /**
+     * Indicates for which channels the gain can be controlled
+     * (e.g. AudioFormat.CHANNEL_OUT_STEREO)
+     */
+    public int channelMask() {
+        return mChannelMask;
+    }
+
+    /**
+     * Minimum gain value in millibel
+     */
+    public int minValue() {
+        return mMinValue;
+    }
+
+    /**
+     * Maximum gain value in millibel
+     */
+    public int maxValue() {
+        return mMaxValue;
+    }
+
+    /**
+     * Default gain value in millibel
+     */
+    public int defaultValue() {
+        return mDefaultValue;
+    }
+
+    /**
+     * Granularity of gain control in millibel
+     */
+    public int stepValue() {
+        return mStepValue;
+    }
+
+    /**
+     * Minimum ramp duration in milliseconds
+     * 0 if MODE_RAMP not set
+     */
+    public int rampDurationMinMs() {
+        return mRampDurationMinMs;
+    }
+
+    /**
+     * Maximum ramp duration in milliseconds
+     * 0 if MODE_RAMP not set
+     */
+    public int rampDurationMaxMs() {
+        return mRampDurationMaxMs;
+    }
+
+    /**
+     * Build a valid gain configuration for this gain controller for use by
+     * AudioPortDescriptor.setGain()
+     * @param mode: desired mode of operation
+     * @param channelMask: channels of which the gain should be modified.
+     * @param values: gain values for each channels.
+     * @param rampDurationMs: ramp duration if mode MODE_RAMP is set.
+     * ignored if MODE_JOINT.
+     */
+    public AudioGainConfig buildConfig(int mode, int channelMask,
+                                       int[] values, int rampDurationMs) {
+        //TODO: check params here
+        return new AudioGainConfig(mIndex, this, mode, channelMask, values, rampDurationMs);
+    }
+}
diff --git a/media/java/android/media/AudioGainConfig.java b/media/java/android/media/AudioGainConfig.java
new file mode 100644
index 0000000..ea61679
--- /dev/null
+++ b/media/java/android/media/AudioGainConfig.java
@@ -0,0 +1,84 @@
+/*
+ * 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.media;
+
+/**
+ * The AudioGainConfig is used by APIs setting or getting values on a given gain
+ * controller. It contains a valid configuration (value, channels...) for a gain controller
+ * exposed by an audio port.
+ * @see AudioGain
+ * @see AudioPort
+ * @hide
+ */
+public class AudioGainConfig {
+    AudioGain mGain;
+    private final int mIndex;
+    private final int mMode;
+    private final int mChannelMask;
+    private final int mValues[];
+    private final int mRampDurationMs;
+
+    AudioGainConfig(int index, AudioGain gain, int mode, int channelMask,
+            int[] values, int rampDurationMs) {
+        mIndex = index;
+        mGain = gain;
+        mMode = mode;
+        mChannelMask = channelMask;
+        mValues = values;
+        mRampDurationMs = rampDurationMs;
+    }
+
+    /**
+     * get the index of the parent gain.
+     * frameworks use only.
+     */
+    int index() {
+        return mIndex;
+    }
+
+    /**
+     * Bit field indicating requested modes of operation. See {@link AudioGain#MODE_JOINT},
+     * {@link AudioGain#MODE_CHANNELS}, {@link AudioGain#MODE_RAMP}
+     */
+    public int mode() {
+        return mMode;
+    }
+
+    /**
+     * Indicates for which channels the gain is set.
+     * See {@link AudioFormat#CHANNEL_OUT_STEREO}, {@link AudioFormat#CHANNEL_OUT_MONO} ...
+     */
+    public int channelMask() {
+        return mChannelMask;
+    }
+
+    /**
+     * Gain values for each channel in the order of bits set in
+     * channelMask() from LSB to MSB
+     */
+    public int[] values() {
+        return mValues;
+    }
+
+    /**
+     * Ramp duration in milliseconds. N/A if mode() does not
+     * specify MODE_RAMP.
+     */
+    public int rampDurationMs() {
+        return mRampDurationMs;
+    }
+}
diff --git a/media/java/android/media/AudioHandle.java b/media/java/android/media/AudioHandle.java
new file mode 100644
index 0000000..b58e7a3
--- /dev/null
+++ b/media/java/android/media/AudioHandle.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.media;
+
+/**
+ * The AudioHandle is used by the audio framework implementation to
+ * uniquely identify a particular component of the routing topology
+ * (AudioPort or AudioPatch)
+ * It is not visible or used at the API.
+ */
+class AudioHandle {
+    private final int mId;
+
+    AudioHandle(int id) {
+        mId = id;
+    }
+
+    int id() {
+        return mId;
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (o == null || !(o instanceof AudioHandle)) {
+            return false;
+        }
+        AudioHandle ah = (AudioHandle)o;
+        return mId == ah.id();
+    }
+
+    @Override
+    public int hashCode() {
+        return mId;
+    }
+}
diff --git a/media/java/android/media/AudioManager.java b/media/java/android/media/AudioManager.java
index 9803161..84d4ab6 100644
--- a/media/java/android/media/AudioManager.java
+++ b/media/java/android/media/AudioManager.java
@@ -39,6 +39,8 @@
 import android.view.KeyEvent;
 
 import java.util.HashMap;
+import java.util.ArrayList;
+
 
 /**
  * AudioManager provides access to volume and ringer mode control.
@@ -60,6 +62,7 @@
     private final boolean mUseVolumeKeySounds;
     private final Binder mToken = new Binder();
     private static String TAG = "AudioManager";
+    AudioPortEventHandler mAudioPortEventHandler;
 
     /**
      * Broadcast intent, a hint for applications that audio is about to become
@@ -336,6 +339,12 @@
     public static final int FLAG_BLUETOOTH_ABS_VOLUME = 1 << 6;
 
     /**
+     * Adjusting the volume was prevented due to silent mode, display a hint in the UI.
+     * @hide
+     */
+    public static final int FLAG_SHOW_SILENT_HINT = 1 << 7;
+
+    /**
      * Ringer mode that will be silent and will not vibrate. (This overrides the
      * vibrate setting.)
      *
@@ -437,6 +446,7 @@
                 com.android.internal.R.bool.config_useMasterVolume);
         mUseVolumeKeySounds = mContext.getResources().getBoolean(
                 com.android.internal.R.bool.config_useVolumeKeySounds);
+        mAudioPortEventHandler = new AudioPortEventHandler(this);
     }
 
     private static IAudioService getService()
@@ -634,7 +644,12 @@
             if (mUseMasterVolume) {
                 service.adjustMasterVolume(direction, flags, mContext.getOpPackageName());
             } else {
-                service.adjustVolume(direction, flags, mContext.getOpPackageName());
+                if (USE_SESSIONS) {
+                    MediaSessionLegacyHelper helper = MediaSessionLegacyHelper.getHelper(mContext);
+                    helper.sendAdjustVolumeBy(USE_DEFAULT_STREAM_TYPE, direction, flags);
+                } else {
+                    service.adjustVolume(direction, flags, mContext.getOpPackageName());
+                }
             }
         } catch (RemoteException e) {
             Log.e(TAG, "Dead object in adjustVolume", e);
@@ -664,8 +679,13 @@
             if (mUseMasterVolume) {
                 service.adjustMasterVolume(direction, flags, mContext.getOpPackageName());
             } else {
-                service.adjustSuggestedStreamVolume(direction, suggestedStreamType, flags,
-                        mContext.getOpPackageName());
+                if (USE_SESSIONS) {
+                    MediaSessionLegacyHelper helper = MediaSessionLegacyHelper.getHelper(mContext);
+                    helper.sendAdjustVolumeBy(suggestedStreamType, direction, flags);
+                } else {
+                    service.adjustSuggestedStreamVolume(direction, suggestedStreamType, flags,
+                            mContext.getOpPackageName());
+                }
             }
         } catch (RemoteException e) {
             Log.e(TAG, "Dead object in adjustSuggestedStreamVolume", e);
@@ -2817,18 +2837,22 @@
     }
 
      /**
-     * Indicate A2DP sink connection state change.
+     * Indicate A2DP source or sink connection state change.
      * @param device Bluetooth device connected/disconnected
      * @param state  new connection state (BluetoothProfile.STATE_xxx)
+     * @param profile profile for the A2DP device
+     * (either {@link android.bluetooth.BluetoothProfile.A2DP} or
+     * {@link android.bluetooth.BluetoothProfile.A2DP_SINK})
      * @return a delay in ms that the caller should wait before broadcasting
      * BluetoothA2dp.ACTION_CONNECTION_STATE_CHANGED intent.
      * {@hide}
      */
-    public int setBluetoothA2dpDeviceConnectionState(BluetoothDevice device, int state) {
+    public int setBluetoothA2dpDeviceConnectionState(BluetoothDevice device, int state,
+            int profile) {
         IAudioService service = getService();
         int delay = 0;
         try {
-            delay = service.setBluetoothA2dpDeviceConnectionState(device, state);
+            delay = service.setBluetoothA2dpDeviceConnectionState(device, state, profile);
         } catch (RemoteException e) {
             Log.e(TAG, "Dead object in setBluetoothA2dpDeviceConnectionState "+e);
         } finally {
@@ -2966,4 +2990,280 @@
             Log.w(TAG, "Error disabling safe media volume", e);
         }
     }
+
+    /**
+     * Return codes for listAudioPorts(), createAudioPatch() ...
+     */
+
+    /** @hide
+     */
+    public static final int SUCCESS = AudioSystem.SUCCESS;
+    /** @hide
+     */
+    public static final int ERROR = AudioSystem.ERROR;
+    /** @hide
+     */
+    public static final int ERROR_BAD_VALUE = AudioSystem.BAD_VALUE;
+    /** @hide
+     */
+    public static final int ERROR_INVALID_OPERATION = AudioSystem.INVALID_OPERATION;
+    /** @hide
+     */
+    public static final int ERROR_PERMISSION_DENIED = AudioSystem.PERMISSION_DENIED;
+    /** @hide
+     */
+    public static final int ERROR_NO_INIT = AudioSystem.NO_INIT;
+    /** @hide
+     */
+    public static final int ERROR_DEAD_OBJECT = AudioSystem.DEAD_OBJECT;
+
+    /**
+     * Returns a list of descriptors for all audio ports managed by the audio framework.
+     * Audio ports are nodes in the audio framework or audio hardware that can be configured
+     * or connected and disconnected with createAudioPatch() or releaseAudioPatch().
+     * See AudioPort for a list of attributes of each audio port.
+     * @param ports An AudioPort ArrayList where the list will be returned.
+     * @hide
+     */
+    public int listAudioPorts(ArrayList<AudioPort> ports) {
+        return updateAudioPortCache(ports, null);
+    }
+
+    /**
+     * Specialized version of listAudioPorts() listing only audio devices (AudioDevicePort)
+     * @see listAudioPorts(ArrayList<AudioPort>)
+     * @hide
+     */
+    public int listAudioDevicePorts(ArrayList<AudioPort> devices) {
+        ArrayList<AudioPort> ports = new ArrayList<AudioPort>();
+        int status = updateAudioPortCache(ports, null);
+        if (status == SUCCESS) {
+            devices.clear();
+            for (int i = 0; i < ports.size(); i++) {
+                if (ports.get(i) instanceof AudioDevicePort) {
+                    devices.add(ports.get(i));
+                }
+            }
+        }
+        return status;
+    }
+
+    /**
+     * Create a connection between two or more devices. The framework will reject the request if
+     * device types are not compatible or the implementation does not support the requested
+     * configuration.
+     * NOTE: current implementation is limited to one source and one sink per patch.
+     * @param patch AudioPatch array where the newly created patch will be returned.
+     *              As input, if patch[0] is not null, the specified patch will be replaced by the
+     *              new patch created. This avoids calling releaseAudioPatch() when modifying a
+     *              patch and allows the implementation to optimize transitions.
+     * @param sources List of source audio ports. All must be AudioPort.ROLE_SOURCE.
+     * @param sinks   List of sink audio ports. All must be AudioPort.ROLE_SINK.
+     *
+     * @return - {@link #SUCCESS} if connection is successful.
+     *         - {@link #ERROR_BAD_VALUE} if incompatible device types are passed.
+     *         - {@link #ERROR_INVALID_OPERATION} if the requested connection is not supported.
+     *         - {@link #ERROR_PERMISSION_DENIED} if the client does not have permission to create
+     *         a patch.
+     *         - {@link #ERROR_DEAD_OBJECT} if the server process is dead
+     *         - {@link #ERROR} if patch cannot be connected for any other reason.
+     *
+     *         patch[0] contains the newly created patch
+     * @hide
+     */
+    public int createAudioPatch(AudioPatch[] patch,
+                                 AudioPortConfig[] sources,
+                                 AudioPortConfig[] sinks) {
+        return AudioSystem.createAudioPatch(patch, sources, sinks);
+    }
+
+    /**
+     * Releases an existing audio patch connection.
+     * @param patch The audio patch to disconnect.
+     * @return - {@link #SUCCESS} if disconnection is successful.
+     *         - {@link #ERROR_BAD_VALUE} if the specified patch does not exist.
+     *         - {@link #ERROR_PERMISSION_DENIED} if the client does not have permission to release
+     *         a patch.
+     *         - {@link #ERROR_DEAD_OBJECT} if the server process is dead
+     *         - {@link #ERROR} if patch cannot be released for any other reason.
+     * @hide
+     */
+    public int releaseAudioPatch(AudioPatch patch) {
+        return AudioSystem.releaseAudioPatch(patch);
+    }
+
+    /**
+     * List all existing connections between audio ports.
+     * @param patches An AudioPatch array where the list will be returned.
+     * @hide
+     */
+    public int listAudioPatches(ArrayList<AudioPatch> patches) {
+        return updateAudioPortCache(null, patches);
+    }
+
+    /**
+     * Set the gain on the specified AudioPort. The AudioGainConfig config is build by
+     * AudioGain.buildConfig()
+     * @hide
+     */
+    public int setAudioPortGain(AudioPort port, AudioGainConfig gain) {
+        if (port == null || gain == null) {
+            return ERROR_BAD_VALUE;
+        }
+        AudioPortConfig activeConfig = port.activeConfig();
+        AudioPortConfig config = new AudioPortConfig(port, activeConfig.samplingRate(),
+                                        activeConfig.channelMask(), activeConfig.format(), gain);
+        config.mConfigMask = AudioPortConfig.GAIN;
+        return AudioSystem.setAudioPortConfig(config);
+    }
+
+    /**
+     * Listener registered by client to be notified upon new audio port connections,
+     * disconnections or attributes update.
+     * @hide
+     */
+    public interface OnAudioPortUpdateListener {
+        /**
+         * Callback method called upon audio port list update.
+         * @param portList the updated list of audio ports
+         */
+        public void OnAudioPortListUpdate(AudioPort[] portList);
+
+        /**
+         * Callback method called upon audio patch list update.
+         * @param patchList the updated list of audio patches
+         */
+        public void OnAudioPatchListUpdate(AudioPatch[] patchList);
+
+        /**
+         * Callback method called when the mediaserver dies
+         */
+        public void OnServiceDied();
+    }
+
+    /**
+     * Register an audio port list update listener.
+     * @hide
+     */
+    public void registerAudioPortUpdateListener(OnAudioPortUpdateListener l) {
+        mAudioPortEventHandler.registerListener(l);
+    }
+
+    /**
+     * Unregister an audio port list update listener.
+     * @hide
+     */
+    public void unregisterAudioPortUpdateListener(OnAudioPortUpdateListener l) {
+        mAudioPortEventHandler.unregisterListener(l);
+    }
+
+    //
+    // AudioPort implementation
+    //
+
+    static final int AUDIOPORT_GENERATION_INIT = 0;
+    Integer mAudioPortGeneration = new Integer(AUDIOPORT_GENERATION_INIT);
+    ArrayList<AudioPort> mAudioPortsCached = new ArrayList<AudioPort>();
+    ArrayList<AudioPatch> mAudioPatchesCached = new ArrayList<AudioPatch>();
+
+    int resetAudioPortGeneration() {
+        int generation;
+        synchronized (mAudioPortGeneration) {
+            generation = mAudioPortGeneration;
+            mAudioPortGeneration = AUDIOPORT_GENERATION_INIT;
+        }
+        return generation;
+    }
+
+    int updateAudioPortCache(ArrayList<AudioPort> ports, ArrayList<AudioPatch> patches) {
+        synchronized (mAudioPortGeneration) {
+
+            if (mAudioPortGeneration == AUDIOPORT_GENERATION_INIT) {
+                int[] patchGeneration = new int[1];
+                int[] portGeneration = new int[1];
+                int status;
+                ArrayList<AudioPort> newPorts = new ArrayList<AudioPort>();
+                ArrayList<AudioPatch> newPatches = new ArrayList<AudioPatch>();
+
+                do {
+                    newPorts.clear();
+                    status = AudioSystem.listAudioPorts(newPorts, portGeneration);
+                    Log.i(TAG, "updateAudioPortCache AudioSystem.listAudioPorts() status: "+
+                                    status+" num ports: "+ newPorts.size() +" portGeneration: "+portGeneration[0]);
+                    if (status != SUCCESS) {
+                        return status;
+                    }
+                    newPatches.clear();
+                    status = AudioSystem.listAudioPatches(newPatches, patchGeneration);
+                    Log.i(TAG, "updateAudioPortCache AudioSystem.listAudioPatches() status: "+
+                            status+" num patches: "+ newPatches.size() +" patchGeneration: "+patchGeneration[0]);
+                    if (status != SUCCESS) {
+                        return status;
+                    }
+                } while (patchGeneration[0] != portGeneration[0]);
+
+                for (int i = 0; i < newPatches.size(); i++) {
+                    for (int j = 0; j < newPatches.get(i).sources().length; j++) {
+                        AudioPortConfig portCfg = updatePortConfig(newPatches.get(i).sources()[j], newPorts);
+                        if (portCfg == null) {
+                            return ERROR;
+                        }
+                        newPatches.get(i).sources()[j] = portCfg;
+                    }
+                    for (int j = 0; j < newPatches.get(i).sinks().length; j++) {
+                        AudioPortConfig portCfg = updatePortConfig(newPatches.get(i).sinks()[j], newPorts);
+                        if (portCfg == null) {
+                            return ERROR;
+                        }
+                        newPatches.get(i).sinks()[j] = portCfg;
+                    }
+                }
+
+                mAudioPortsCached = newPorts;
+                mAudioPatchesCached = newPatches;
+                mAudioPortGeneration = portGeneration[0];
+            }
+            if (ports != null) {
+                ports.clear();
+                ports.addAll(mAudioPortsCached);
+            }
+            if (patches != null) {
+                patches.clear();
+                patches.addAll(mAudioPatchesCached);
+            }
+        }
+        return SUCCESS;
+    }
+
+    AudioPortConfig updatePortConfig(AudioPortConfig portCfg, ArrayList<AudioPort> ports) {
+        AudioPort port = portCfg.port();
+        int k;
+        for (k = 0; k < ports.size(); k++) {
+            // compare handles because the port returned by JNI is not of the correct
+            // subclass
+            if (ports.get(k).handle().equals(port.handle())) {
+                Log.i(TAG, "updatePortConfig match found for port handle: "+
+                            port.handle().id()+" port: "+ k);
+                port = ports.get(k);
+                break;
+            }
+        }
+        if (k == ports.size()) {
+            // this hould never happen
+            Log.e(TAG, "updatePortConfig port not found for handle: "+port.handle().id());
+            return null;
+        }
+        AudioGainConfig gainCfg = portCfg.gain();
+        if (gainCfg != null) {
+            AudioGain gain = port.gain(gainCfg.index());
+            gainCfg = gain.buildConfig(gainCfg.mode(),
+                                       gainCfg.channelMask(),
+                                       gainCfg.values(),
+                                       gainCfg.rampDurationMs());
+        }
+        return port.buildConfig(portCfg.samplingRate(),
+                                                 portCfg.channelMask(),
+                                                 portCfg.format(),
+                                                 gainCfg);
+    }
 }
diff --git a/media/java/android/media/AudioMixPort.java b/media/java/android/media/AudioMixPort.java
new file mode 100644
index 0000000..1500a43
--- /dev/null
+++ b/media/java/android/media/AudioMixPort.java
@@ -0,0 +1,51 @@
+/*
+ * 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.media;
+
+/**
+ * The AudioMixPort is a specialized type of AudioPort
+ * describing an audio mix or stream at an input or output stream of the audio
+ * framework.
+ * @see AudioPort
+ * @hide
+ */
+
+public class AudioMixPort extends AudioPort {
+
+    AudioMixPort(AudioHandle handle, int role, int[] samplingRates, int[] channelMasks,
+            int[] formats, AudioGain[] gains) {
+        super(handle, role, samplingRates, channelMasks, formats, gains);
+    }
+
+    /**
+     * Build a specific configuration of this audio mix port for use by methods
+     * like AudioManager.connectAudioPatch().
+     */
+    public AudioMixPortConfig buildConfig(int samplingRate, int channelMask, int format,
+                                       AudioGainConfig gain) {
+        return new AudioMixPortConfig(this, samplingRate, channelMask, format, gain);
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (o == null || !(o instanceof AudioMixPort)) {
+            return false;
+        }
+        return super.equals(o);
+    }
+
+}
diff --git a/media/java/android/media/AudioMixPortConfig.java b/media/java/android/media/AudioMixPortConfig.java
new file mode 100644
index 0000000..8eb9ef4
--- /dev/null
+++ b/media/java/android/media/AudioMixPortConfig.java
@@ -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.
+ */
+
+package android.media;
+
+/**
+ * An AudioMixPortConfig describes a possible configuration of an output or input mixer.
+ * It is used to specify a sink or source when creating a connection with
+ * AudioManager.connectAudioPatch().
+ * An AudioMixPortConfig is obtained from AudioMixPort.buildConfig().
+ * @hide
+ */
+
+public class AudioMixPortConfig extends AudioPortConfig {
+
+    AudioMixPortConfig(AudioMixPort mixPort, int samplingRate, int channelMask, int format,
+                AudioGainConfig gain) {
+        super((AudioPort)mixPort, samplingRate, channelMask, format, gain);
+    }
+
+    /**
+     * Returns the audio mix port this AudioMixPortConfig is issued from.
+     */
+    public AudioMixPort port() {
+        return (AudioMixPort)mPort;
+    }
+}
+
diff --git a/media/java/android/media/AudioPatch.java b/media/java/android/media/AudioPatch.java
new file mode 100644
index 0000000..72291f6
--- /dev/null
+++ b/media/java/android/media/AudioPatch.java
@@ -0,0 +1,55 @@
+/*
+ * 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.media;
+
+
+/**
+ * An AudioPatch describes a connection between audio sources and audio sinks.
+ * An audio source can be an output mix (playback AudioBus) or an input device (microphone).
+ * An audio sink can be an output device (speaker) or an input mix (capture AudioBus).
+ * An AudioPatch is created by AudioManager.connectAudioPatch() and released by
+ * AudioManager.disconnectAudioPatch()
+ * It contains the list of source and sink AudioPortConfig showing audio port configurations
+ * being connected.
+ * @hide
+ */
+public class AudioPatch {
+
+    private final AudioHandle mHandle;
+    private final AudioPortConfig[] mSources;
+    private final AudioPortConfig[] mSinks;
+
+    AudioPatch(AudioHandle patchHandle, AudioPortConfig[] sources, AudioPortConfig[] sinks) {
+        mHandle = patchHandle;
+        mSources = sources;
+        mSinks = sinks;
+    }
+
+    /**
+     * Retrieve the list of sources of this audio patch.
+     */
+    public AudioPortConfig[] sources() {
+        return mSources;
+    }
+
+    /**
+     * Retreive the list of sinks of this audio patch.
+     */
+    public AudioPortConfig[] sinks() {
+        return mSinks;
+    }
+}
diff --git a/media/java/android/media/AudioPort.java b/media/java/android/media/AudioPort.java
new file mode 100644
index 0000000..fbd5022
--- /dev/null
+++ b/media/java/android/media/AudioPort.java
@@ -0,0 +1,177 @@
+/*
+ * 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.media;
+
+
+/**
+ * An audio port is a node of the audio framework or hardware that can be connected to or
+ * disconnect from another audio node to create a specific audio routing configuration.
+ * Examples of audio ports are an output device (speaker) or an output mix (see AudioMixPort).
+ * All attributes that are relevant for applications to make routing selection are decribed
+ * in an AudioPort,  in particular:
+ * - possible channel mask configurations.
+ * - audio format (PCM 16bit, PCM 24bit...)
+ * - gain: a port can be associated with one or more gain controllers (see AudioGain).
+ *
+ * This object is always created by the framework and read only by applications.
+ * A list of all audio port descriptors currently available for applications to control
+ * is obtained by AudioManager.listAudioPorts().
+ * An application can obtain an AudioPortConfig for a valid configuration of this port
+ * by calling AudioPort.buildConfig() and use this configuration
+ * to create a connection between audio sinks and sources with AudioManager.connectAudioPatch()
+ *
+ * @hide
+ */
+public class AudioPort {
+
+    /**
+     * For use by the audio framework.
+     */
+    public static final int ROLE_NONE = 0;
+    /**
+     * The audio port is a source (produces audio)
+     */
+    public static final int ROLE_SOURCE = 1;
+    /**
+     * The audio port is a sink (consumes audio)
+     */
+    public static final int ROLE_SINK = 2;
+
+    /**
+     * audio port type for use by audio framework implementation
+     */
+    public static final int TYPE_NONE = 0;
+    /**
+     */
+    public static final int TYPE_DEVICE = 1;
+    /**
+     */
+    public static final int TYPE_SUBMIX = 2;
+    /**
+     */
+    public static final int TYPE_SESSION = 3;
+
+
+    AudioHandle mHandle;
+    private final int mRole;
+    private final int[] mSamplingRates;
+    private final int[] mChannelMasks;
+    private final int[] mFormats;
+    private final AudioGain[] mGains;
+    private AudioPortConfig mActiveConfig;
+
+    AudioPort(AudioHandle handle, int role, int[] samplingRates, int[] channelMasks,
+            int[] formats, AudioGain[] gains) {
+        mHandle = handle;
+        mRole = role;
+        mSamplingRates = samplingRates;
+        mChannelMasks = channelMasks;
+        mFormats = formats;
+        mGains = gains;
+    }
+
+    AudioHandle handle() {
+        return mHandle;
+    }
+
+    /**
+     * Get the audio port role
+     */
+    public int role() {
+        return mRole;
+    }
+
+    /**
+     * Get the list of supported sampling rates
+     * Empty array if sampling rate is not relevant for this audio port
+     */
+    public int[] samplingRates() {
+        return mSamplingRates;
+    }
+
+    /**
+     * Get the list of supported channel mask configurations
+     * (e.g AudioFormat.CHANNEL_OUT_STEREO)
+     * Empty array if channel mask is not relevant for this audio port
+     */
+    public int[] channelMasks() {
+        return mChannelMasks;
+    }
+
+    /**
+     * Get the list of supported audio format configurations
+     * (e.g AudioFormat.ENCODING_PCM_16BIT)
+     * Empty array if format is not relevant for this audio port
+     */
+    public int[] formats() {
+        return mFormats;
+    }
+
+    /**
+     * Get the list of gain descriptors
+     * Empty array if this port does not have gain control
+     */
+    public AudioGain[] gains() {
+        return mGains;
+    }
+
+    /**
+     * Get the gain descriptor at a given index
+     */
+    AudioGain gain(int index) {
+        if (index < mGains.length) {
+            return null;
+        }
+        return mGains[index];
+    }
+
+    /**
+     * Build a specific configuration of this audio port for use by methods
+     * like AudioManager.connectAudioPatch().
+     * @param channelMask The desired channel mask. AudioFormat.CHANNEL_OUT_DEFAULT if no change
+     * from active configuration requested.
+     * @param format The desired audio format. AudioFormat.ENCODING_DEFAULT if no change
+     * from active configuration requested.
+     * @param gain The desired gain. null if no gain changed requested.
+     */
+    public AudioPortConfig buildConfig(int samplingRate, int channelMask, int format,
+                                        AudioGainConfig gain) {
+        return new AudioPortConfig(this, samplingRate, channelMask, format, gain);
+    }
+
+    /**
+     * Get currently active configuration of this audio port.
+     */
+    public AudioPortConfig activeConfig() {
+        return mActiveConfig;
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (o == null || !(o instanceof AudioPort)) {
+            return false;
+        }
+        AudioPort ap = (AudioPort)o;
+        return mHandle.equals(ap.handle());
+    }
+
+    @Override
+    public int hashCode() {
+        return mHandle.hashCode();
+    }
+}
+
diff --git a/media/java/android/media/AudioPortConfig.java b/media/java/android/media/AudioPortConfig.java
new file mode 100644
index 0000000..5dc768d
--- /dev/null
+++ b/media/java/android/media/AudioPortConfig.java
@@ -0,0 +1,93 @@
+/*
+ * 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.media;
+
+/**
+ * An AudioPortConfig contains a possible configuration of an audio port chosen
+ * among all possible attributes described by an AudioPort.
+ * An AudioPortConfig is created by AudioPort.buildConfiguration().
+ * AudioPorts are used to specify the sources and sinks of a patch created
+ * with AudioManager.connectAudioPatch().
+ * Several specialized versions of AudioPortConfig exist to handle different categories of
+ * audio ports and their specific attributes:
+ * - AudioDevicePortConfig for input (e.g micropohone) and output devices (e.g speaker)
+ * - AudioMixPortConfig for input or output streams of the audio framework.
+ * @hide
+ */
+
+public class AudioPortConfig {
+    final AudioPort mPort;
+    private final int mSamplingRate;
+    private final int mChannelMask;
+    private final int mFormat;
+    private final AudioGainConfig mGain;
+
+    // mConfigMask indicates which fields in this configuration should be
+    // taken into account. Used with AudioSystem.setAudioPortConfig()
+    // framework use only.
+    static final int SAMPLE_RATE  = 0x1;
+    static final int CHANNEL_MASK = 0x2;
+    static final int FORMAT       = 0x4;
+    static final int GAIN         = 0x8;
+    int mConfigMask;
+
+    AudioPortConfig(AudioPort port, int samplingRate, int channelMask, int format,
+            AudioGainConfig gain) {
+        mPort = port;
+        mSamplingRate = samplingRate;
+        mChannelMask = channelMask;
+        mFormat = format;
+        mGain = gain;
+        mConfigMask = 0;
+    }
+
+    /**
+     * Returns the audio port this AudioPortConfig is issued from.
+     */
+    public AudioPort port() {
+        return mPort;
+    }
+
+    /**
+     * Sampling rate configured for this AudioPortConfig.
+     */
+    public int samplingRate() {
+        return mSamplingRate;
+    }
+
+    /**
+     * Channel mask configuration (e.g AudioFormat.CHANNEL_CONFIGURATION_STEREO).
+     */
+    public int channelMask() {
+        return mChannelMask;
+    }
+
+    /**
+     * Audio format configuration (e.g AudioFormat.ENCODING_PCM_16BIT).
+     */
+    public int format() {
+        return mFormat;
+    }
+
+    /**
+     * The gain configuration if this port supports gain control, null otherwise
+     * @see AudioGainConfig.
+     */
+    public AudioGainConfig gain() {
+        return mGain;
+    }
+}
diff --git a/media/java/android/media/AudioPortEventHandler.java b/media/java/android/media/AudioPortEventHandler.java
new file mode 100644
index 0000000..782ecd8
--- /dev/null
+++ b/media/java/android/media/AudioPortEventHandler.java
@@ -0,0 +1,172 @@
+/*
+ * 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.media;
+
+import android.os.Handler;
+import android.os.Looper;
+import android.os.Message;
+import android.util.Log;
+
+import java.util.ArrayList;
+import java.lang.ref.WeakReference;
+
+/**
+ * The AudioPortEventHandler handles AudioManager.OnAudioPortUpdateListener callbacks
+ * posted from JNI
+ * @hide
+ */
+
+class AudioPortEventHandler {
+    private final Handler mHandler;
+    private ArrayList<AudioManager.OnAudioPortUpdateListener> mListeners;
+    private AudioManager mAudioManager;
+
+    private static String TAG = "AudioPortEventHandler";
+
+    private static final int AUDIOPORT_EVENT_PORT_LIST_UPDATED = 1;
+    private static final int AUDIOPORT_EVENT_PATCH_LIST_UPDATED = 2;
+    private static final int AUDIOPORT_EVENT_SERVICE_DIED = 3;
+    private static final int AUDIOPORT_EVENT_NEW_LISTENER = 4;
+
+    AudioPortEventHandler(AudioManager audioManager) {
+        mAudioManager = audioManager;
+        mListeners = new ArrayList<AudioManager.OnAudioPortUpdateListener>();
+
+        // find the looper for our new event handler
+        Looper looper = Looper.myLooper();
+        if (looper == null) {
+            looper = Looper.getMainLooper();
+        }
+
+        if (looper != null) {
+            mHandler = new Handler(looper) {
+                @Override
+                public void handleMessage(Message msg) {
+                    Log.i(TAG, "handleMessage: "+msg.what);
+                    ArrayList<AudioManager.OnAudioPortUpdateListener> listeners;
+                    synchronized (this) {
+                        if (msg.what == AUDIOPORT_EVENT_NEW_LISTENER) {
+                            listeners = new ArrayList<AudioManager.OnAudioPortUpdateListener>();
+                            if (mListeners.contains(msg.obj)) {
+                                listeners.add((AudioManager.OnAudioPortUpdateListener)msg.obj);
+                            }
+                        } else {
+                            listeners = mListeners;
+                        }
+                    }
+                    if (listeners.isEmpty()) {
+                        return;
+                    }
+                    // reset audio port cache if the event corresponds to a change coming
+                    // from audio policy service or if mediaserver process died.
+                    if (msg.what == AUDIOPORT_EVENT_PORT_LIST_UPDATED ||
+                            msg.what == AUDIOPORT_EVENT_PATCH_LIST_UPDATED ||
+                            msg.what == AUDIOPORT_EVENT_SERVICE_DIED) {
+                        mAudioManager.resetAudioPortGeneration();
+                    }
+                    ArrayList<AudioPort> ports = new ArrayList<AudioPort>();
+                    ArrayList<AudioPatch> patches = new ArrayList<AudioPatch>();
+                    if (msg.what != AUDIOPORT_EVENT_SERVICE_DIED) {
+                        int status = mAudioManager.updateAudioPortCache(ports, patches);
+                        if (status != AudioManager.SUCCESS) {
+                            return;
+                        }
+                    }
+
+                    switch (msg.what) {
+                    case AUDIOPORT_EVENT_NEW_LISTENER:
+                    case AUDIOPORT_EVENT_PORT_LIST_UPDATED:
+                        AudioPort[] portList = ports.toArray(new AudioPort[0]);
+                        for (int i = 0; i < listeners.size(); i++) {
+                            listeners.get(i).OnAudioPortListUpdate(portList);
+                        }
+                        if (msg.what == AUDIOPORT_EVENT_PORT_LIST_UPDATED) {
+                            break;
+                        }
+                        // FALL THROUGH
+
+                    case AUDIOPORT_EVENT_PATCH_LIST_UPDATED:
+                        AudioPatch[] patchList = patches.toArray(new AudioPatch[0]);
+                        for (int i = 0; i < listeners.size(); i++) {
+                            listeners.get(i).OnAudioPatchListUpdate(patchList);
+                        }
+                        break;
+
+                    case AUDIOPORT_EVENT_SERVICE_DIED:
+                        for (int i = 0; i < listeners.size(); i++) {
+                            listeners.get(i).OnServiceDied();
+                        }
+                        break;
+
+                    default:
+                        break;
+                    }
+                }
+            };
+        } else {
+            mHandler = null;
+        }
+
+        native_setup(new WeakReference<AudioPortEventHandler>(this));
+    }
+    private native void native_setup(Object module_this);
+
+    @Override
+    protected void finalize() {
+        native_finalize();
+    }
+    private native void native_finalize();
+
+    void registerListener(AudioManager.OnAudioPortUpdateListener l) {
+        synchronized (this) {
+            mListeners.add(l);
+        }
+        if (mHandler != null) {
+            Message m = mHandler.obtainMessage(AUDIOPORT_EVENT_NEW_LISTENER, 0, 0, l);
+            mHandler.sendMessage(m);
+        }
+    }
+
+    void unregisterListener(AudioManager.OnAudioPortUpdateListener l) {
+        synchronized (this) {
+            mListeners.remove(l);
+        }
+    }
+
+    Handler handler() {
+        return mHandler;
+    }
+
+    @SuppressWarnings("unused")
+    private static void postEventFromNative(Object module_ref,
+                                            int what, int arg1, int arg2, Object obj) {
+        AudioPortEventHandler eventHandler =
+                (AudioPortEventHandler)((WeakReference)module_ref).get();
+        if (eventHandler == null) {
+            return;
+        }
+
+        if (eventHandler != null) {
+            Handler handler = eventHandler.handler();
+            if (handler != null) {
+                Message m = handler.obtainMessage(what, arg1, arg2, obj);
+                handler.sendMessage(m);
+            }
+        }
+    }
+
+}
diff --git a/media/java/android/media/AudioService.java b/media/java/android/media/AudioService.java
index 5a7d815..74f39b7 100644
--- a/media/java/android/media/AudioService.java
+++ b/media/java/android/media/AudioService.java
@@ -112,6 +112,12 @@
     private static final boolean USE_SESSIONS = true;
     private static final boolean DEBUG_SESSIONS = true;
 
+    /** Allow volume changes to set ringer mode to silent? */
+    private static final boolean VOLUME_SETS_RINGER_MODE_SILENT = false;
+
+    /** In silent mode, are volume adjustments (raises) prevented? */
+    private static final boolean PREVENT_VOLUME_ADJUSTMENT_IF_SILENT = true;
+
     /** How long to delay before persisting a change in volume/ringer mode. */
     private static final int PERSIST_DELAY = 500;
 
@@ -123,6 +129,11 @@
      */
     public static final int PLAY_SOUND_DELAY = 300;
 
+    /**
+     * Only used in the result from {@link #checkForRingerModeChange(int, int, int)}
+     */
+    private static final int FLAG_ADJUST_VOLUME = 1;
+
     private final Context mContext;
     private final ContentResolver mContentResolver;
     private final AppOpsManager mAppOps;
@@ -166,7 +177,8 @@
     //   these messages can only be queued, i.e. sent with queueMsgUnderWakeLock(),
     //   and not with sendMsg(..., ..., SENDMSG_QUEUE, ...)
     private static final int MSG_SET_WIRED_DEVICE_CONNECTION_STATE = 100;
-    private static final int MSG_SET_A2DP_CONNECTION_STATE = 101;
+    private static final int MSG_SET_A2DP_SRC_CONNECTION_STATE = 101;
+    private static final int MSG_SET_A2DP_SINK_CONNECTION_STATE = 102;
     // end of messages handled under wakelock
 
     private static final int BTA2DP_DOCK_TIMEOUT_MILLIS = 8000;
@@ -938,7 +950,12 @@
             }
             // Check if the ringer mode changes with this volume adjustment. If
             // it does, it will handle adjusting the volume, so we won't below
-            adjustVolume = checkForRingerModeChange(aliasIndex, direction, step);
+            final int result = checkForRingerModeChange(aliasIndex, direction, step);
+            adjustVolume = (result & FLAG_ADJUST_VOLUME) != 0;
+            // If suppressing a volume adjustment in silent mode, display the UI hint
+            if ((result & AudioManager.FLAG_SHOW_SILENT_HINT) != 0) {
+                flags |= AudioManager.FLAG_SHOW_SILENT_HINT;
+            }
         }
 
         int oldIndex = mStreamStates[streamType].getIndex(device);
@@ -1019,7 +1036,8 @@
             int newRingerMode;
             if (index == 0) {
                 newRingerMode = mHasVibrator ? AudioManager.RINGER_MODE_VIBRATE
-                                              : AudioManager.RINGER_MODE_SILENT;
+                        : VOLUME_SETS_RINGER_MODE_SILENT ? AudioManager.RINGER_MODE_SILENT
+                        : AudioManager.RINGER_MODE_NORMAL;
             } else {
                 newRingerMode = AudioManager.RINGER_MODE_NORMAL;
             }
@@ -2367,10 +2385,10 @@
                         synchronized (mConnectedDevices) {
                             int state = mA2dp.getConnectionState(btDevice);
                             int delay = checkSendBecomingNoisyIntent(
-                                                    AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP,
-                                                    (state == BluetoothA2dp.STATE_CONNECTED) ? 1 : 0);
+                                                AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP,
+                                                (state == BluetoothA2dp.STATE_CONNECTED) ? 1 : 0);
                             queueMsgUnderWakeLock(mAudioHandler,
-                                    MSG_SET_A2DP_CONNECTION_STATE,
+                                    MSG_SET_A2DP_SINK_CONNECTION_STATE,
                                     state,
                                     0,
                                     btDevice,
@@ -2380,6 +2398,22 @@
                 }
                 break;
 
+            case BluetoothProfile.A2DP_SINK:
+                deviceList = proxy.getConnectedDevices();
+                if (deviceList.size() > 0) {
+                    btDevice = deviceList.get(0);
+                    synchronized (mConnectedDevices) {
+                        int state = proxy.getConnectionState(btDevice);
+                        queueMsgUnderWakeLock(mAudioHandler,
+                                MSG_SET_A2DP_SRC_CONNECTION_STATE,
+                                state,
+                                0,
+                                btDevice,
+                                0 /* delay */);
+                    }
+                }
+                break;
+
             case BluetoothProfile.HEADSET:
                 synchronized (mScoClients) {
                     // Discard timeout message
@@ -2448,6 +2482,15 @@
                 }
                 break;
 
+            case BluetoothProfile.A2DP_SINK:
+                synchronized (mConnectedDevices) {
+                    if (mConnectedDevices.containsKey(AudioSystem.DEVICE_IN_BLUETOOTH_A2DP)) {
+                        makeA2dpSrcUnavailable(
+                                mConnectedDevices.get(AudioSystem.DEVICE_IN_BLUETOOTH_A2DP));
+                    }
+                }
+                break;
+
             case BluetoothProfile.HEADSET:
                 synchronized (mScoClients) {
                     mBluetoothHeadset = null;
@@ -2534,8 +2577,8 @@
      * adjusting volume. If so, this will set the proper ringer mode and volume
      * indices on the stream states.
      */
-    private boolean checkForRingerModeChange(int oldIndex, int direction,  int step) {
-        boolean adjustVolumeIndex = true;
+    private int checkForRingerModeChange(int oldIndex, int direction,  int step) {
+        int result = FLAG_ADJUST_VOLUME;
         int ringerMode = getRingerMode();
 
         switch (ringerMode) {
@@ -2552,7 +2595,9 @@
                     }
                 } else {
                     // (oldIndex < step) is equivalent to (old UI index == 0)
-                    if ((oldIndex < step) && mPrevVolDirection != AudioManager.ADJUST_LOWER) {
+                    if ((oldIndex < step)
+                            && VOLUME_SETS_RINGER_MODE_SILENT
+                            && mPrevVolDirection != AudioManager.ADJUST_LOWER) {
                         ringerMode = RINGER_MODE_SILENT;
                     }
                 }
@@ -2565,23 +2610,28 @@
                 break;
             }
             if ((direction == AudioManager.ADJUST_LOWER)) {
-                if (mPrevVolDirection != AudioManager.ADJUST_LOWER) {
+                if (VOLUME_SETS_RINGER_MODE_SILENT
+                        && mPrevVolDirection != AudioManager.ADJUST_LOWER) {
                     ringerMode = RINGER_MODE_SILENT;
                 }
             } else if (direction == AudioManager.ADJUST_RAISE) {
                 ringerMode = RINGER_MODE_NORMAL;
             }
-            adjustVolumeIndex = false;
+            result &= ~FLAG_ADJUST_VOLUME;
             break;
         case RINGER_MODE_SILENT:
             if (direction == AudioManager.ADJUST_RAISE) {
-                if (mHasVibrator) {
-                    ringerMode = RINGER_MODE_VIBRATE;
+                if (PREVENT_VOLUME_ADJUSTMENT_IF_SILENT) {
+                    result |= AudioManager.FLAG_SHOW_SILENT_HINT;
                 } else {
-                    ringerMode = RINGER_MODE_NORMAL;
+                  if (mHasVibrator) {
+                      ringerMode = RINGER_MODE_VIBRATE;
+                  } else {
+                      ringerMode = RINGER_MODE_NORMAL;
+                  }
                 }
             }
-            adjustVolumeIndex = false;
+            result &= ~FLAG_ADJUST_VOLUME;
             break;
         default:
             Log.e(TAG, "checkForRingerModeChange() wrong ringer mode: "+ringerMode);
@@ -2592,7 +2642,7 @@
 
         mPrevVolDirection = direction;
 
-        return adjustVolumeIndex;
+        return result;
     }
 
     @Override
@@ -2856,14 +2906,22 @@
         }
     }
 
-    public int setBluetoothA2dpDeviceConnectionState(BluetoothDevice device, int state)
+    public int setBluetoothA2dpDeviceConnectionState(BluetoothDevice device, int state, int profile)
     {
         int delay;
+        if (profile != BluetoothProfile.A2DP && profile != BluetoothProfile.A2DP_SINK) {
+            throw new IllegalArgumentException("invalid profile " + profile);
+        }
         synchronized (mConnectedDevices) {
-            delay = checkSendBecomingNoisyIntent(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP,
-                                            (state == BluetoothA2dp.STATE_CONNECTED) ? 1 : 0);
+            if (profile == BluetoothProfile.A2DP) {
+                delay = checkSendBecomingNoisyIntent(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP,
+                                                (state == BluetoothA2dp.STATE_CONNECTED) ? 1 : 0);
+            } else {
+                delay = 0;
+            }
             queueMsgUnderWakeLock(mAudioHandler,
-                    MSG_SET_A2DP_CONNECTION_STATE,
+                    (profile == BluetoothProfile.A2DP ?
+                        MSG_SET_A2DP_SINK_CONNECTION_STATE : MSG_SET_A2DP_SRC_CONNECTION_STATE),
                     state,
                     0,
                     device,
@@ -3719,8 +3777,13 @@
                     mAudioEventWakeLock.release();
                     break;
 
-                case MSG_SET_A2DP_CONNECTION_STATE:
-                    onSetA2dpConnectionState((BluetoothDevice)msg.obj, msg.arg1);
+                case MSG_SET_A2DP_SRC_CONNECTION_STATE:
+                    onSetA2dpSourceConnectionState((BluetoothDevice)msg.obj, msg.arg1);
+                    mAudioEventWakeLock.release();
+                    break;
+
+                case MSG_SET_A2DP_SINK_CONNECTION_STATE:
+                    onSetA2dpSinkConnectionState((BluetoothDevice)msg.obj, msg.arg1);
                     mAudioEventWakeLock.release();
                     break;
 
@@ -3847,6 +3910,23 @@
     }
 
     // must be called synchronized on mConnectedDevices
+    private void makeA2dpSrcAvailable(String address) {
+        AudioSystem.setDeviceConnectionState(AudioSystem.DEVICE_IN_BLUETOOTH_A2DP,
+                AudioSystem.DEVICE_STATE_AVAILABLE,
+                address);
+        mConnectedDevices.put( new Integer(AudioSystem.DEVICE_IN_BLUETOOTH_A2DP),
+                address);
+    }
+
+    // must be called synchronized on mConnectedDevices
+    private void makeA2dpSrcUnavailable(String address) {
+        AudioSystem.setDeviceConnectionState(AudioSystem.DEVICE_IN_BLUETOOTH_A2DP,
+                AudioSystem.DEVICE_STATE_UNAVAILABLE,
+                address);
+        mConnectedDevices.remove(AudioSystem.DEVICE_IN_BLUETOOTH_A2DP);
+    }
+
+    // must be called synchronized on mConnectedDevices
     private void cancelA2dpDeviceTimeout() {
         mAudioHandler.removeMessages(MSG_BTA2DP_DOCK_TIMEOUT);
     }
@@ -3856,9 +3936,11 @@
         return mAudioHandler.hasMessages(MSG_BTA2DP_DOCK_TIMEOUT);
     }
 
-    private void onSetA2dpConnectionState(BluetoothDevice btDevice, int state)
+    private void onSetA2dpSinkConnectionState(BluetoothDevice btDevice, int state)
     {
-        if (DEBUG_VOL) Log.d(TAG, "onSetA2dpConnectionState btDevice="+btDevice+" state="+state);
+        if (DEBUG_VOL) {
+            Log.d(TAG, "onSetA2dpSinkConnectionState btDevice="+btDevice+"state="+state);
+        }
         if (btDevice == null) {
             return;
         }
@@ -3917,6 +3999,32 @@
         }
     }
 
+    private void onSetA2dpSourceConnectionState(BluetoothDevice btDevice, int state)
+    {
+        if (DEBUG_VOL) {
+            Log.d(TAG, "onSetA2dpSourceConnectionState btDevice="+btDevice+" state="+state);
+        }
+        if (btDevice == null) {
+            return;
+        }
+        String address = btDevice.getAddress();
+        if (!BluetoothAdapter.checkBluetoothAddress(address)) {
+            address = "";
+        }
+
+        synchronized (mConnectedDevices) {
+                boolean isConnected =
+                (mConnectedDevices.containsKey(AudioSystem.DEVICE_IN_BLUETOOTH_A2DP) &&
+                 mConnectedDevices.get(AudioSystem.DEVICE_IN_BLUETOOTH_A2DP).equals(address));
+
+            if (isConnected && state != BluetoothProfile.STATE_CONNECTED) {
+                makeA2dpSrcUnavailable(address);
+            } else if (!isConnected && state == BluetoothProfile.STATE_CONNECTED) {
+                makeA2dpSrcAvailable(address);
+            }
+        }
+    }
+
     public void avrcpSupportsAbsoluteVolume(String address, boolean support) {
         // address is not used for now, but may be used when multiple a2dp devices are supported
         synchronized (mA2dpAvrcpLock) {
@@ -3982,7 +4090,8 @@
             }
         }
 
-        if (mAudioHandler.hasMessages(MSG_SET_A2DP_CONNECTION_STATE) ||
+        if (mAudioHandler.hasMessages(MSG_SET_A2DP_SRC_CONNECTION_STATE) ||
+                mAudioHandler.hasMessages(MSG_SET_A2DP_SINK_CONNECTION_STATE) ||
                 mAudioHandler.hasMessages(MSG_SET_WIRED_DEVICE_CONNECTION_STATE)) {
             delay = 1000;
         }
@@ -4049,8 +4158,9 @@
                     (device == AudioSystem.DEVICE_OUT_WIRED_HEADPHONE))) {
                 setBluetoothA2dpOnInt(true);
             }
-            boolean isUsb = ((device & AudioSystem.DEVICE_OUT_ALL_USB) != 0) ||
-                            ((device & AudioSystem.DEVICE_IN_ALL_USB) != 0);
+            boolean isUsb = ((device & ~AudioSystem.DEVICE_OUT_ALL_USB) == 0) ||
+                            (((device & AudioSystem.DEVICE_BIT_IN) != 0) &&
+                             ((device & ~AudioSystem.DEVICE_IN_ALL_USB) == 0));
             handleDeviceConnection((state == 1), device, (isUsb ? name : ""));
             if (state != 0) {
                 if ((device == AudioSystem.DEVICE_OUT_WIRED_HEADSET) ||
@@ -4067,7 +4177,7 @@
                             MUSIC_ACTIVE_POLL_PERIOD_MS);
                 }
             }
-            if (!isUsb) {
+            if (!isUsb && (device != AudioSystem.DEVICE_IN_WIRED_HEADSET)) {
                 sendDeviceConnectionIntent(device, state, name);
             }
         }
@@ -4083,7 +4193,8 @@
         @Override
         public void onReceive(Context context, Intent intent) {
             String action = intent.getAction();
-            int device;
+            int outDevice;
+            int inDevice;
             int state;
 
             if (action.equals(Intent.ACTION_DOCK_EVENT)) {
@@ -4118,7 +4229,8 @@
             } else if (action.equals(BluetoothHeadset.ACTION_CONNECTION_STATE_CHANGED)) {
                 state = intent.getIntExtra(BluetoothProfile.EXTRA_STATE,
                                                BluetoothProfile.STATE_DISCONNECTED);
-                device = AudioSystem.DEVICE_OUT_BLUETOOTH_SCO;
+                outDevice = AudioSystem.DEVICE_OUT_BLUETOOTH_SCO;
+                inDevice = AudioSystem.DEVICE_IN_BLUETOOTH_SCO_HEADSET;
                 String address = null;
 
                 BluetoothDevice btDevice = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
@@ -4132,10 +4244,10 @@
                     switch (btClass.getDeviceClass()) {
                     case BluetoothClass.Device.AUDIO_VIDEO_WEARABLE_HEADSET:
                     case BluetoothClass.Device.AUDIO_VIDEO_HANDSFREE:
-                        device = AudioSystem.DEVICE_OUT_BLUETOOTH_SCO_HEADSET;
+                        outDevice = AudioSystem.DEVICE_OUT_BLUETOOTH_SCO_HEADSET;
                         break;
                     case BluetoothClass.Device.AUDIO_VIDEO_CAR_AUDIO:
-                        device = AudioSystem.DEVICE_OUT_BLUETOOTH_SCO_CARKIT;
+                        outDevice = AudioSystem.DEVICE_OUT_BLUETOOTH_SCO_CARKIT;
                         break;
                     }
                 }
@@ -4145,7 +4257,9 @@
                 }
 
                 boolean connected = (state == BluetoothProfile.STATE_CONNECTED);
-                if (handleDeviceConnection(connected, device, address)) {
+                boolean success = handleDeviceConnection(connected, outDevice, address) &&
+                                      handleDeviceConnection(connected, inDevice, address);
+                if (success) {
                     synchronized (mScoClients) {
                         if (connected) {
                             mBluetoothHeadsetDevice = btDevice;
@@ -4165,8 +4279,8 @@
                                     : "card=" + alsaCard + ";device=" + alsaDevice);
 
                 // Playback Device
-                device = AudioSystem.DEVICE_OUT_USB_ACCESSORY;
-                setWiredDeviceConnectionState(device, state, params);
+                outDevice = AudioSystem.DEVICE_OUT_USB_ACCESSORY;
+                setWiredDeviceConnectionState(outDevice, state, params);
             } else if (action.equals(Intent.ACTION_USB_AUDIO_DEVICE_PLUG)) {
                 state = intent.getIntExtra("state", 0);
 
@@ -4181,14 +4295,14 @@
 
                 // Playback Device
                 if (hasPlayback) {
-                    device = AudioSystem.DEVICE_OUT_USB_DEVICE;
-                    setWiredDeviceConnectionState(device, state, params);
+                    outDevice = AudioSystem.DEVICE_OUT_USB_DEVICE;
+                    setWiredDeviceConnectionState(outDevice, state, params);
                 }
 
                 // Capture Device
                 if (hasCapture) {
-                    device = AudioSystem.DEVICE_IN_USB_DEVICE;
-                    setWiredDeviceConnectionState(device, state, params);
+                    inDevice = AudioSystem.DEVICE_IN_USB_DEVICE;
+                    setWiredDeviceConnectionState(inDevice, state, params);
                 }
             } else if (action.equals(BluetoothHeadset.ACTION_AUDIO_STATE_CHANGED)) {
                 boolean broadcast = false;
diff --git a/media/java/android/media/AudioSystem.java b/media/java/android/media/AudioSystem.java
index 0c45443..c8d64ce 100644
--- a/media/java/android/media/AudioSystem.java
+++ b/media/java/android/media/AudioSystem.java
@@ -16,6 +16,7 @@
 
 package android.media;
 
+import java.util.ArrayList;
 
 /* IF YOU CHANGE ANY OF THE CONSTANTS IN THIS FILE, DO NOT FORGET
  * TO UPDATE THE CORRESPONDING NATIVE GLUE AND AudioManager.java.
@@ -299,7 +300,7 @@
     public static final int DEVICE_IN_TV_TUNER = DEVICE_BIT_IN | 0x4000;
     public static final int DEVICE_IN_LINE = DEVICE_BIT_IN | 0x8000;
     public static final int DEVICE_IN_SPDIF = DEVICE_BIT_IN | 0x10000;
-
+    public static final int DEVICE_IN_BLUETOOTH_A2DP = DEVICE_BIT_IN | 0x20000;
     public static final int DEVICE_IN_DEFAULT = DEVICE_BIT_IN | DEVICE_BIT_DEFAULT;
 
     public static final int DEVICE_IN_ALL = (DEVICE_IN_COMMUNICATION |
@@ -319,6 +320,7 @@
                                              DEVICE_IN_TV_TUNER |
                                              DEVICE_IN_LINE |
                                              DEVICE_IN_SPDIF |
+                                             DEVICE_IN_BLUETOOTH_A2DP |
                                              DEVICE_IN_DEFAULT);
     public static final int DEVICE_IN_ALL_SCO = DEVICE_IN_BLUETOOTH_SCO_HEADSET;
     public static final int DEVICE_IN_ALL_USB = (DEVICE_IN_USB_ACCESSORY |
@@ -458,4 +460,12 @@
 
     public static native int setLowRamDevice(boolean isLowRamDevice);
     public static native int checkAudioFlinger();
+
+    public static native int listAudioPorts(ArrayList<AudioPort> ports, int[] generation);
+    public static native int createAudioPatch(AudioPatch[] patch,
+                                            AudioPortConfig[] sources, AudioPortConfig[] sinks);
+    public static native int releaseAudioPatch(AudioPatch patch);
+    public static native int listAudioPatches(ArrayList<AudioPatch> patches, int[] generation);
+    public static native int setAudioPortConfig(AudioPortConfig config);
 }
+
diff --git a/media/java/android/media/AudioTrack.java b/media/java/android/media/AudioTrack.java
index 8eb83e4..cfd9c3b 100644
--- a/media/java/android/media/AudioTrack.java
+++ b/media/java/android/media/AudioTrack.java
@@ -816,6 +816,8 @@
     *         with the estimated time when that frame was presented or is committed to
     *         be presented.
     *         In the case that no timestamp is available, any supplied instance is left unaltered.
+    *         A timestamp may be temporarily unavailable while the audio clock is stabilizing,
+    *         or during and immediately after a route change.
     */
     // Add this text when the "on new timestamp" API is added:
     //   Use if you need to get the most recent timestamp outside of the event callback handler.
diff --git a/media/java/android/media/IAudioService.aidl b/media/java/android/media/IAudioService.aidl
index 30de4f9..e59623b 100644
--- a/media/java/android/media/IAudioService.aidl
+++ b/media/java/android/media/IAudioService.aidl
@@ -232,7 +232,7 @@
     int getMasterStreamType();
 
     void setWiredDeviceConnectionState(int device, int state, String name);
-    int setBluetoothA2dpDeviceConnectionState(in BluetoothDevice device, int state);
+    int setBluetoothA2dpDeviceConnectionState(in BluetoothDevice device, int state, int profile);
 
     AudioRoutesInfo startWatchingRoutes(in IAudioRoutesObserver observer);
 
diff --git a/media/java/android/media/MediaCodec.java b/media/java/android/media/MediaCodec.java
index c7b3fc9..f258063 100644
--- a/media/java/android/media/MediaCodec.java
+++ b/media/java/android/media/MediaCodec.java
@@ -744,12 +744,40 @@
         setParameters(keys, values);
     }
 
+    /**
+     * Sets the codec listener for actionable MediaCodec events.
+     * <p>Call this method with a null listener to stop receiving event notifications.
+     *
+     * @param cb The listener that will run.
+     *
+     * @hide
+     */
     public void setNotificationCallback(NotificationCallback cb) {
         mNotificationCallback = cb;
     }
 
-    public interface NotificationCallback {
-        void onCodecNotify(MediaCodec codec);
+    /**
+     * MediaCodec listener interface.  Used to notify the user of MediaCodec
+     * when there are available input and/or output buffers, a change in
+     * configuration or when a codec error happened.
+     *
+     * @hide
+     */
+    public static abstract class NotificationCallback {
+        /**
+         * Called on the listener to notify that there is an actionable
+         * MediaCodec event.  The application should call {@link #dequeueOutputBuffer}
+         * to receive the configuration change event, codec error or an
+         * available output buffer.  It should also call  {@link #dequeueInputBuffer}
+         * to receive any available input buffer.  For best performance, it
+         * is recommended to exhaust both available input and output buffers in
+         * the handling of a single callback, by calling the dequeue methods
+         * repeatedly with a zero timeout until {@link #INFO_TRY_AGAIN_LATER} is returned.
+         *
+         * @param codec the MediaCodec instance that has an actionable event.
+         *
+         */
+        public abstract void onCodecNotify(MediaCodec codec);
     }
 
     private void postEventFromNative(
diff --git a/media/java/android/media/MediaMetadata.java b/media/java/android/media/MediaMetadata.java
index ff73a10..5dc8e1b 100644
--- a/media/java/android/media/MediaMetadata.java
+++ b/media/java/android/media/MediaMetadata.java
@@ -23,6 +23,8 @@
 import android.util.Log;
 import android.util.SparseArray;
 
+import java.util.Set;
+
 /**
  * Contains metadata about an item, such as the title, artist, etc.
  */
@@ -301,6 +303,15 @@
     }
 
     /**
+     * Returns a Set containing the Strings used as keys in this metadata.
+     *
+     * @return a Set of String keys
+     */
+    public Set<String> keySet() {
+        return mBundle.keySet();
+    }
+
+    /**
      * Helper for getting the String key used by {@link MediaMetadata} from the
      * integer key that {@link MediaMetadataEditor} uses.
      *
diff --git a/media/java/android/media/RemoteControlClient.java b/media/java/android/media/RemoteControlClient.java
index 26ae3cc..0caea5f 100644
--- a/media/java/android/media/RemoteControlClient.java
+++ b/media/java/android/media/RemoteControlClient.java
@@ -27,7 +27,6 @@
 import android.media.session.MediaSessionLegacyHelper;
 import android.media.session.PlaybackState;
 import android.media.session.MediaSession;
-import android.media.session.TransportPerformer;
 import android.os.Bundle;
 import android.os.Handler;
 import android.os.IBinder;
@@ -584,7 +583,7 @@
 
                 // USE_SESSIONS
                 if (mSession != null && mMetadataBuilder != null) {
-                    mSession.getTransportPerformer().setMetadata(mMetadataBuilder.build());
+                    mSession.setMetadata(mMetadataBuilder.build());
                 }
                 mApplied = true;
             }
@@ -702,7 +701,7 @@
                     mSessionPlaybackState.setState(pbState, hasPosition ?
                             mPlaybackPositionMs : PlaybackState.PLAYBACK_POSITION_UNKNOWN,
                             playbackSpeed);
-                    mSession.getTransportPerformer().setPlaybackState(mSessionPlaybackState);
+                    mSession.setPlaybackState(mSessionPlaybackState);
                 }
             }
         }
@@ -789,7 +788,7 @@
             if (mSession != null) {
                 mSessionPlaybackState.setActions(PlaybackState
                         .getActionsFromRccControlFlags(transportControlFlags));
-                mSession.getTransportPerformer().setPlaybackState(mSessionPlaybackState);
+                mSession.setPlaybackState(mSessionPlaybackState);
             }
         }
     }
@@ -1317,7 +1316,8 @@
     }
 
     // USE_SESSIONS
-    private TransportPerformer.Listener mTransportListener = new TransportPerformer.Listener() {
+    private MediaSession.TransportControlsCallback mTransportListener
+            = new MediaSession.TransportControlsCallback() {
 
         @Override
         public void onSeekTo(long pos) {
@@ -1325,7 +1325,7 @@
         }
 
         @Override
-        public void onRate(Rating rating) {
+        public void onSetRating(Rating rating) {
             if ((mTransportControlFlags & FLAG_KEY_MEDIA_RATING) != 0) {
                 if (mEventHandler != null) {
                     mEventHandler.sendMessage(mEventHandler.obtainMessage(
diff --git a/media/java/android/media/session/ISession.aidl b/media/java/android/media/session/ISession.aidl
index c4233c3..1cfc5bc 100644
--- a/media/java/android/media/session/ISession.aidl
+++ b/media/java/android/media/session/ISession.aidl
@@ -47,4 +47,8 @@
     void setMetadata(in MediaMetadata metadata);
     void setPlaybackState(in PlaybackState state);
     void setRatingType(int type);
+
+    // These commands relate to volume handling
+    void configureVolumeHandling(int type, int arg1, int arg2);
+    void setCurrentVolume(int currentVolume);
 }
\ No newline at end of file
diff --git a/media/java/android/media/session/ISessionCallback.aidl b/media/java/android/media/session/ISessionCallback.aidl
index 103c3f1..0316d1f 100644
--- a/media/java/android/media/session/ISessionCallback.aidl
+++ b/media/java/android/media/session/ISessionCallback.aidl
@@ -45,4 +45,8 @@
     void onRewind();
     void onSeekTo(long pos);
     void onRate(in Rating rating);
+
+    // These callbacks are for volume handling
+    void onAdjustVolumeBy(int delta);
+    void onSetVolumeTo(int value);
 }
\ No newline at end of file
diff --git a/media/java/android/media/session/ISessionController.aidl b/media/java/android/media/session/ISessionController.aidl
index 5ddb6db..9ce0692 100644
--- a/media/java/android/media/session/ISessionController.aidl
+++ b/media/java/android/media/session/ISessionController.aidl
@@ -30,7 +30,7 @@
  */
 interface ISessionController {
     void sendCommand(String command, in Bundle extras, in ResultReceiver cb);
-    void sendMediaButton(in KeyEvent mediaButton);
+    boolean sendMediaButton(in KeyEvent mediaButton);
     void registerCallbackListener(in ISessionControllerCallback cb);
     void unregisterCallbackListener(in ISessionControllerCallback cb);
     boolean isTransportControlEnabled();
diff --git a/media/java/android/media/session/ISessionManager.aidl b/media/java/android/media/session/ISessionManager.aidl
index 38b9293..6d9888f 100644
--- a/media/java/android/media/session/ISessionManager.aidl
+++ b/media/java/android/media/session/ISessionManager.aidl
@@ -29,4 +29,5 @@
     ISession createSession(String packageName, in ISessionCallback cb, String tag, int userId);
     List<IBinder> getSessions(in ComponentName compName, int userId);
     void dispatchMediaKeyEvent(in KeyEvent keyEvent, boolean needWakeLock);
+    void dispatchAdjustVolumeBy(int suggestedStream, int delta, int flags);
 }
\ No newline at end of file
diff --git a/media/java/android/media/session/MediaController.java b/media/java/android/media/session/MediaController.java
index 642ac2f..caff1ad 100644
--- a/media/java/android/media/session/MediaController.java
+++ b/media/java/android/media/session/MediaController.java
@@ -17,6 +17,7 @@
 package android.media.session;
 
 import android.media.MediaMetadata;
+import android.media.Rating;
 import android.os.Bundle;
 import android.os.Handler;
 import android.os.Looper;
@@ -45,8 +46,8 @@
     private static final String TAG = "SessionController";
 
     private static final int MSG_EVENT = 1;
-    private static final int MESSAGE_PLAYBACK_STATE = 2;
-    private static final int MESSAGE_METADATA = 3;
+    private static final int MSG_UPDATE_PLAYBACK_STATE = 2;
+    private static final int MSG_UPDATE_METADATA = 3;
     private static final int MSG_ROUTE = 4;
 
     private final ISessionController mSessionBinder;
@@ -57,10 +58,11 @@
 
     private boolean mCbRegistered = false;
 
-    private TransportController mTransportController;
+    private TransportControls mTransportController;
 
     private MediaController(ISessionController sessionBinder) {
         mSessionBinder = sessionBinder;
+        mTransportController = new TransportControls();
     }
 
     /**
@@ -70,9 +72,6 @@
         MediaController controller = new MediaController(sessionBinder);
         try {
             controller.mSessionBinder.registerCallbackListener(controller.mCbStub);
-            if (controller.mSessionBinder.isTransportControlEnabled()) {
-                controller.mTransportController = new TransportController(sessionBinder);
-            }
         } catch (RemoteException e) {
             Log.wtf(TAG, "MediaController created with expired token", e);
             controller = null;
@@ -93,33 +92,84 @@
     }
 
     /**
-     * Get a TransportController if the session supports it. If it is not
-     * supported null will be returned.
+     * Get a {@link TransportControls} instance for this session.
      *
-     * @return A TransportController or null
+     * @return A controls instance
      */
-    public TransportController getTransportController() {
+    public TransportControls getTransportControls() {
         return mTransportController;
     }
 
     /**
-     * Send the specified media button to the session. Only media keys can be
-     * sent using this method.
+     * Send the specified media button event to the session. Only media keys can
+     * be sent by this method, other keys will be ignored.
      *
-     * @param keycode The media button keycode, such as
-     *            {@link KeyEvent#KEYCODE_MEDIA_PLAY}.
+     * @param keyEvent The media button event to dispatch.
+     * @return true if the event was sent to the session, false otherwise.
      */
-    public void sendMediaButton(int keycode) {
-        if (!KeyEvent.isMediaKey(keycode)) {
-            throw new IllegalArgumentException("May only send media buttons through "
-                    + "sendMediaButton");
+    public boolean dispatchMediaButtonEvent(KeyEvent keyEvent) {
+        if (keyEvent == null) {
+            throw new IllegalArgumentException("KeyEvent may not be null");
         }
-        // TODO do something better than key down/up events
-        KeyEvent event = new KeyEvent(KeyEvent.ACTION_UP, keycode);
+        if (!KeyEvent.isMediaKey(keyEvent.getKeyCode())) {
+            return false;
+        }
         try {
-            mSessionBinder.sendMediaButton(event);
+            return mSessionBinder.sendMediaButton(keyEvent);
         } catch (RemoteException e) {
-            Log.d(TAG, "Dead object in sendMediaButton", e);
+            // System is dead. =(
+        }
+        return false;
+    }
+
+    /**
+     * Get the current playback state for this session.
+     *
+     * @return The current PlaybackState or null
+     */
+    public PlaybackState getPlaybackState() {
+        try {
+            return mSessionBinder.getPlaybackState();
+        } catch (RemoteException e) {
+            Log.wtf(TAG, "Error calling getPlaybackState.", e);
+            return null;
+        }
+    }
+
+    /**
+     * Get the current metadata for this session.
+     *
+     * @return The current MediaMetadata or null.
+     */
+    public MediaMetadata getMetadata() {
+        try {
+            return mSessionBinder.getMetadata();
+        } catch (RemoteException e) {
+            Log.wtf(TAG, "Error calling getMetadata.", e);
+            return null;
+        }
+    }
+
+    /**
+     * Get the rating type supported by the session. One of:
+     * <ul>
+     * <li>{@link Rating#RATING_NONE}</li>
+     * <li>{@link Rating#RATING_HEART}</li>
+     * <li>{@link Rating#RATING_THUMB_UP_DOWN}</li>
+     * <li>{@link Rating#RATING_3_STARS}</li>
+     * <li>{@link Rating#RATING_4_STARS}</li>
+     * <li>{@link Rating#RATING_5_STARS}</li>
+     * <li>{@link Rating#RATING_PERCENTAGE}</li>
+     * </ul>
+     *
+     * @return The supported rating type
+     */
+    public int getRatingType() {
+        try {
+            return mSessionBinder.getRatingType();
+        } catch (RemoteException e) {
+            Log.wtf(TAG, "Error calling getRatingType.", e);
+            return Rating.RATING_NONE;
         }
     }
 
@@ -171,7 +221,7 @@
      * @param params Any parameters to include with the command
      * @param cb The callback to receive the result on
      */
-    public void sendCommand(String command, Bundle params, ResultReceiver cb) {
+    public void sendControlCommand(String command, Bundle params, ResultReceiver cb) {
         if (TextUtils.isEmpty(command)) {
             throw new IllegalArgumentException("command cannot be null or empty");
         }
@@ -254,18 +304,10 @@
         return null;
     }
 
-    private void postEvent(String event, Bundle extras) {
+    private final void postMessage(int what, Object obj, Bundle data) {
         synchronized (mLock) {
             for (int i = mCallbacks.size() - 1; i >= 0; i--) {
-                mCallbacks.get(i).post(MSG_EVENT, event, extras);
-            }
-        }
-    }
-
-    private void postRouteChanged(RouteInfo route) {
-        synchronized (mLock) {
-            for (int i = mCallbacks.size() - 1; i >= 0; i--) {
-                mCallbacks.get(i).post(MSG_ROUTE, route, null);
+                mCallbacks.get(i).post(what, obj, data);
             }
         }
     }
@@ -282,7 +324,7 @@
          *
          * @param event
          */
-        public void onEvent(String event, Bundle extras) {
+        public void onSessionEvent(String event, Bundle extras) {
         }
 
         /**
@@ -293,6 +335,143 @@
          */
         public void onRouteChanged(RouteInfo route) {
         }
+
+        /**
+         * Override to handle changes in playback state.
+         *
+         * @param state The new playback state of the session
+         */
+        public void onPlaybackStateChanged(PlaybackState state) {
+        }
+
+        /**
+         * Override to handle changes to the current metadata.
+         *
+         * @see MediaMetadata
+         * @param metadata The current metadata for the session or null
+         */
+        public void onMetadataChanged(MediaMetadata metadata) {
+        }
+    }
+
+    /**
+     * Interface for controlling media playback on a session. This allows an app
+     * to send media transport commands to the session.
+     */
+    public final class TransportControls {
+        private static final String TAG = "TransportController";
+
+        private TransportControls() {
+        }
+
+        /**
+         * Request that the player start its playback at its current position.
+         */
+        public void play() {
+            try {
+                mSessionBinder.play();
+            } catch (RemoteException e) {
+                Log.wtf(TAG, "Error calling play.", e);
+            }
+        }
+
+        /**
+         * Request that the player pause its playback and stay at its current
+         * position.
+         */
+        public void pause() {
+            try {
+                mSessionBinder.pause();
+            } catch (RemoteException e) {
+                Log.wtf(TAG, "Error calling pause.", e);
+            }
+        }
+
+        /**
+         * Request that the player stop its playback; it may clear its state in
+         * whatever way is appropriate.
+         */
+        public void stop() {
+            try {
+                mSessionBinder.stop();
+            } catch (RemoteException e) {
+                Log.wtf(TAG, "Error calling stop.", e);
+            }
+        }
+
+        /**
+         * Move to a new location in the media stream.
+         *
+         * @param pos Position to move to, in milliseconds.
+         */
+        public void seekTo(long pos) {
+            try {
+                mSessionBinder.seekTo(pos);
+            } catch (RemoteException e) {
+                Log.wtf(TAG, "Error calling seekTo.", e);
+            }
+        }
+
+        /**
+         * Start fast forwarding. If playback is already fast forwarding this
+         * may increase the rate.
+         */
+        public void fastForward() {
+            try {
+                mSessionBinder.fastForward();
+            } catch (RemoteException e) {
+                Log.wtf(TAG, "Error calling fastForward.", e);
+            }
+        }
+
+        /**
+         * Skip to the next item.
+         */
+        public void skipToNext() {
+            try {
+                mSessionBinder.next();
+            } catch (RemoteException e) {
+                Log.wtf(TAG, "Error calling next.", e);
+            }
+        }
+
+        /**
+         * Start rewinding. If playback is already rewinding this may increase
+         * the rate.
+         */
+        public void rewind() {
+            try {
+                mSessionBinder.rewind();
+            } catch (RemoteException e) {
+                Log.wtf(TAG, "Error calling rewind.", e);
+            }
+        }
+
+        /**
+         * Skip to the previous item.
+         */
+        public void skipToPrevious() {
+            try {
+                mSessionBinder.previous();
+            } catch (RemoteException e) {
+                Log.wtf(TAG, "Error calling previous.", e);
+            }
+        }
+
+        /**
+         * Rate the current content. This will cause the rating to be set for
+         * the current user. The Rating type must match the type returned by
+         * {@link #getRatingType()}.
+         *
+         * @param rating The rating to set for the current content
+         */
+        public void setRating(Rating rating) {
+            try {
+                mSessionBinder.rate(rating);
+            } catch (RemoteException e) {
+                Log.wtf(TAG, "Error calling rate.", e);
+            }
+        }
     }
 
     private final static class CallbackStub extends ISessionControllerCallback.Stub {
@@ -306,7 +485,7 @@
         public void onEvent(String event, Bundle extras) {
             MediaController controller = mController.get();
             if (controller != null) {
-                controller.postEvent(event, extras);
+                controller.postMessage(MSG_EVENT, event, extras);
             }
         }
 
@@ -314,7 +493,7 @@
         public void onRouteChanged(RouteInfo route) {
             MediaController controller = mController.get();
             if (controller != null) {
-                controller.postRouteChanged(route);
+                controller.postMessage(MSG_ROUTE, route, null);
             }
         }
 
@@ -322,10 +501,7 @@
         public void onPlaybackStateChanged(PlaybackState state) {
             MediaController controller = mController.get();
             if (controller != null) {
-                TransportController tc = controller.getTransportController();
-                if (tc != null) {
-                    tc.postPlaybackStateChanged(state);
-                }
+                controller.postMessage(MSG_UPDATE_PLAYBACK_STATE, state, null);
             }
         }
 
@@ -333,10 +509,7 @@
         public void onMetadataChanged(MediaMetadata metadata) {
             MediaController controller = mController.get();
             if (controller != null) {
-                TransportController tc = controller.getTransportController();
-                if (tc != null) {
-                    tc.postMetadataChanged(metadata);
-                }
+                controller.postMessage(MSG_UPDATE_METADATA, metadata, null);
             }
         }
 
@@ -354,10 +527,17 @@
         public void handleMessage(Message msg) {
             switch (msg.what) {
                 case MSG_EVENT:
-                    mCallback.onEvent((String) msg.obj, msg.getData());
+                    mCallback.onSessionEvent((String) msg.obj, msg.getData());
                     break;
                 case MSG_ROUTE:
                     mCallback.onRouteChanged((RouteInfo) msg.obj);
+                    break;
+                case MSG_UPDATE_PLAYBACK_STATE:
+                    mCallback.onPlaybackStateChanged((PlaybackState) msg.obj);
+                    break;
+                case MSG_UPDATE_METADATA:
+                    mCallback.onMetadataChanged((MediaMetadata) msg.obj);
+                    break;
             }
         }
 
diff --git a/media/java/android/media/session/MediaSession.java b/media/java/android/media/session/MediaSession.java
index 539dc3c..7972639 100644
--- a/media/java/android/media/session/MediaSession.java
+++ b/media/java/android/media/session/MediaSession.java
@@ -16,10 +16,12 @@
 
 package android.media.session;
 
+import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.app.PendingIntent;
 import android.content.Intent;
-import android.media.AudioAttributes;
 import android.media.AudioManager;
+import android.media.MediaMetadata;
 import android.media.Rating;
 import android.media.session.ISessionController;
 import android.media.session.ISession;
@@ -49,15 +51,13 @@
  * <p>
  * A MediaSession is created by calling
  * {@link MediaSessionManager#createSession(String)}. Once a session is created
- * apps that have the MEDIA_CONTENT_CONTROL permission can interact with the
- * session through
- * {@link MediaSessionManager#getActiveSessions(android.content.ComponentName)}.
- * The owner of the session may also use {@link #getSessionToken()} to allow
- * apps without this permission to create a {@link MediaController} to interact
- * with this session.
+ * the owner of the session may use {@link #getSessionToken()} to allow apps to
+ * create a {@link MediaController} to interact with this session.
  * <p>
- * To receive commands, media keys, and other events a Callback must be set with
- * {@link #addCallback(Callback)}.
+ * To receive commands, media keys, and other events a {@link Callback} must be
+ * set with {@link #addCallback(Callback)}. To receive transport control
+ * commands a {@link TransportControlsCallback} must be set with
+ * {@link #addTransportControlsCallback}.
  * <p>
  * When an app is finished performing playback it must call {@link #release()}
  * to clean up the session and notify any controllers.
@@ -74,9 +74,10 @@
     public static final int FLAG_HANDLES_MEDIA_BUTTONS = 1 << 0;
 
     /**
-     * Set this flag on the session to indicate that it handles commands through
-     * the {@link TransportPerformer}. The performer can be retrieved by calling
-     * {@link #getTransportPerformer()}.
+     * Set this flag on the session to indicate that it handles transport
+     * control commands through a {@link TransportControlsCallback}. The
+     * callback can be retrieved by calling
+     * {@link #addTransportControlsCallback}.
      */
     public static final int FLAG_HANDLES_TRANSPORT_CONTROLS = 1 << 1;
 
@@ -123,15 +124,21 @@
      */
     public static final int DISCONNECT_REASON_SESSION_DESTROYED = 5;
 
-    private static final int MSG_MEDIA_BUTTON = 1;
-    private static final int MSG_COMMAND = 2;
-    private static final int MSG_ROUTE_CHANGE = 3;
-    private static final int MSG_ROUTE_CONNECTED = 4;
-    private static final int MSG_ROUTE_DISCONNECTED = 5;
+    /**
+     * The session uses local playback. Used for configuring volume handling
+     * with the system.
+     *
+     * @hide
+     */
+    public static final int VOLUME_TYPE_LOCAL = 1;
 
-    private static final String KEY_COMMAND = "command";
-    private static final String KEY_EXTRAS = "extras";
-    private static final String KEY_CALLBACK = "callback";
+    /**
+     * The session uses remote playback. Used for configuring volume handling
+     * with the system.
+     *
+     * @hide
+     */
+    public static final int VOLUME_TYPE_REMOTE = 2;
 
     private final Object mLock = new Object();
 
@@ -139,13 +146,16 @@
     private final ISession mBinder;
     private final CallbackStub mCbStub;
 
-    private final ArrayList<MessageHandler> mCallbacks = new ArrayList<MessageHandler>();
+    private final ArrayList<CallbackMessageHandler> mCallbacks
+            = new ArrayList<CallbackMessageHandler>();
+    private final ArrayList<TransportMessageHandler> mTransportCallbacks
+            = new ArrayList<TransportMessageHandler>();
     // TODO route interfaces
     private final ArrayMap<String, RouteInterface.EventListener> mInterfaceListeners
             = new ArrayMap<String, RouteInterface.EventListener>();
 
-    private TransportPerformer mPerformer;
     private Route mRoute;
+    private RemoteVolumeProvider mVolumeProvider;
 
     private boolean mActive = false;;
 
@@ -162,11 +172,12 @@
             throw new RuntimeException("Dead object in MediaSessionController constructor: ", e);
         }
         mSessionToken = new MediaSessionToken(controllerBinder);
-        mPerformer = new TransportPerformer(mBinder);
     }
 
     /**
-     * Set the callback to receive updates on.
+     * Add a callback to receive updates on for the MediaSession. This includes
+     * media button and volume events. The caller's thread will be used to post
+     * events.
      *
      * @param callback The callback object
      */
@@ -193,7 +204,8 @@
             if (handler == null) {
                 handler = new Handler();
             }
-            MessageHandler msgHandler = new MessageHandler(handler.getLooper(), callback);
+            CallbackMessageHandler msgHandler = new CallbackMessageHandler(handler.getLooper(),
+                    callback);
             mCallbacks.add(msgHandler);
         }
     }
@@ -210,18 +222,6 @@
     }
 
     /**
-     * Retrieves the {@link TransportPerformer} for this session. To receive
-     * commands through the performer you must also set the
-     * {@link #FLAG_HANDLES_TRANSPORT_CONTROLS} flag using
-     * {@link #setFlags(int)}.
-     *
-     * @return The performer associated with this session.
-     */
-    public TransportPerformer getTransportPerformer() {
-        return mPerformer;
-    }
-
-    /**
      * Set an intent for launching UI for this Session. This can be used as a
      * quick link to an ongoing media screen.
      *
@@ -246,7 +246,7 @@
 
     /**
      * Set the stream this session is playing on. This will affect the system's
-     * volume handling for this session. If {@link #useRemotePlayback} was
+     * volume handling for this session. If {@link #setPlaybackToRemote} was
      * previously called it will stop receiving volume commands and the system
      * will begin sending volume changes to the appropriate stream.
      * <p>
@@ -254,25 +254,36 @@
      *
      * @param stream The {@link AudioManager} stream this session is playing on.
      */
-    public void useLocalPlayback(int stream) {
-        // TODO
+    public void setPlaybackToLocal(int stream) {
+        try {
+            mBinder.configureVolumeHandling(VOLUME_TYPE_LOCAL, stream, 0);
+        } catch (RemoteException e) {
+            Log.wtf(TAG, "Failure in setPlaybackToLocal.", e);
+        }
     }
 
     /**
      * Configure this session to use remote volume handling. This must be called
      * to receive volume button events, otherwise the system will adjust the
-     * current stream volume for this session. If {@link #useLocalPlayback} was
-     * previously called that stream will stop receiving volume changes for this
-     * session.
+     * current stream volume for this session. If {@link #setPlaybackToLocal}
+     * was previously called that stream will stop receiving volume changes for
+     * this session.
      *
      * @param volumeProvider The provider that will handle volume changes. May
      *            not be null.
      */
-    public void useRemotePlayback(RemoteVolumeProvider volumeProvider) {
+    public void setPlaybackToRemote(RemoteVolumeProvider volumeProvider) {
         if (volumeProvider == null) {
             throw new IllegalArgumentException("volumeProvider may not be null!");
         }
-        // TODO
+        mVolumeProvider = volumeProvider;
+
+        try {
+            mBinder.configureVolumeHandling(VOLUME_TYPE_REMOTE, volumeProvider.getVolumeControl(),
+                    volumeProvider.getMaxVolume());
+        } catch (RemoteException e) {
+            Log.wtf(TAG, "Failure in setPlaybackToRemote.", e);
+        }
     }
 
     /**
@@ -312,7 +323,7 @@
      * @param event The name of the event to send
      * @param extras Any extras included with the event
      */
-    public void sendEvent(String event, Bundle extras) {
+    public void sendSessionEvent(String event, Bundle extras) {
         if (TextUtils.isEmpty(event)) {
             throw new IllegalArgumentException("event cannot be null or empty");
         }
@@ -432,12 +443,160 @@
         return true;
     }
 
-    private MessageHandler getHandlerForCallbackLocked(Callback cb) {
+    /**
+     * Add a callback to receive transport controls on, such as play, rewind, or
+     * fast forward.
+     *
+     * @param callback The callback object
+     */
+    public void addTransportControlsCallback(@NonNull TransportControlsCallback callback) {
+        addTransportControlsCallback(callback, null);
+    }
+
+    /**
+     * Add a callback to receive transport controls on, such as play, rewind, or
+     * fast forward. The updates will be posted to the specified handler. If no
+     * handler is provided they will be posted to the caller's thread.
+     *
+     * @param callback The callback to receive updates on
+     * @param handler The handler to post the updates on
+     */
+    public void addTransportControlsCallback(@NonNull TransportControlsCallback callback,
+            @Nullable Handler handler) {
+        if (callback == null) {
+            throw new IllegalArgumentException("Callback cannot be null");
+        }
+        synchronized (mLock) {
+            if (getTransportControlsHandlerForCallbackLocked(callback) != null) {
+                Log.w(TAG, "Callback is already added, ignoring");
+                return;
+            }
+            if (handler == null) {
+                handler = new Handler();
+            }
+            TransportMessageHandler msgHandler = new TransportMessageHandler(handler.getLooper(),
+                    callback);
+            mTransportCallbacks.add(msgHandler);
+        }
+    }
+
+    /**
+     * Stop receiving transport controls on the specified callback. If an update
+     * has already been posted you may still receive it after this call returns.
+     *
+     * @param callback The callback to stop receiving updates on
+     */
+    public void removeTransportControlsCallback(@NonNull TransportControlsCallback callback) {
+        if (callback == null) {
+            throw new IllegalArgumentException("Callback cannot be null");
+        }
+        synchronized (mLock) {
+            removeTransportControlsCallbackLocked(callback);
+        }
+    }
+
+    /**
+     * Update the current playback state.
+     *
+     * @param state The current state of playback
+     */
+    public void setPlaybackState(PlaybackState state) {
+        try {
+            mBinder.setPlaybackState(state);
+        } catch (RemoteException e) {
+            Log.wtf(TAG, "Dead object in setPlaybackState.", e);
+        }
+    }
+
+    /**
+     * Update the current metadata. New metadata can be created using
+     * {@link android.media.MediaMetadata.Builder}.
+     *
+     * @param metadata The new metadata
+     */
+    public void setMetadata(MediaMetadata metadata) {
+        try {
+            mBinder.setMetadata(metadata);
+        } catch (RemoteException e) {
+            Log.wtf(TAG, "Dead object in setPlaybackState.", e);
+        }
+    }
+
+    private void dispatchPlay() {
+        postToTransportCallbacks(TransportMessageHandler.MSG_PLAY);
+    }
+
+    private void dispatchPause() {
+        postToTransportCallbacks(TransportMessageHandler.MSG_PAUSE);
+    }
+
+    private void dispatchStop() {
+        postToTransportCallbacks(TransportMessageHandler.MSG_STOP);
+    }
+
+    private void dispatchNext() {
+        postToTransportCallbacks(TransportMessageHandler.MSG_NEXT);
+    }
+
+    private void dispatchPrevious() {
+        postToTransportCallbacks(TransportMessageHandler.MSG_PREVIOUS);
+    }
+
+    private void dispatchFastForward() {
+        postToTransportCallbacks(TransportMessageHandler.MSG_FAST_FORWARD);
+    }
+
+    private void dispatchRewind() {
+        postToTransportCallbacks(TransportMessageHandler.MSG_REWIND);
+    }
+
+    private void dispatchSeekTo(long pos) {
+        postToTransportCallbacks(TransportMessageHandler.MSG_SEEK_TO, pos);
+    }
+
+    private void dispatchRate(Rating rating) {
+        postToTransportCallbacks(TransportMessageHandler.MSG_RATE, rating);
+    }
+
+    private TransportMessageHandler getTransportControlsHandlerForCallbackLocked(
+            TransportControlsCallback callback) {
+        for (int i = mTransportCallbacks.size() - 1; i >= 0; i--) {
+            TransportMessageHandler handler = mTransportCallbacks.get(i);
+            if (callback == handler.mCallback) {
+                return handler;
+            }
+        }
+        return null;
+    }
+
+    private boolean removeTransportControlsCallbackLocked(TransportControlsCallback callback) {
+        for (int i = mTransportCallbacks.size() - 1; i >= 0; i--) {
+            if (callback == mTransportCallbacks.get(i).mCallback) {
+                mTransportCallbacks.remove(i);
+                return true;
+            }
+        }
+        return false;
+    }
+
+    private void postToTransportCallbacks(int what, Object obj) {
+        synchronized (mLock) {
+            for (int i = mTransportCallbacks.size() - 1; i >= 0; i--) {
+                mTransportCallbacks.get(i).post(what, obj);
+            }
+        }
+    }
+
+    private void postToTransportCallbacks(int what) {
+        postToTransportCallbacks(what, null);
+    }
+
+    private CallbackMessageHandler getHandlerForCallbackLocked(Callback cb) {
         if (cb == null) {
             throw new IllegalArgumentException("Callback cannot be null");
         }
         for (int i = mCallbacks.size() - 1; i >= 0; i--) {
-            MessageHandler handler = mCallbacks.get(i);
+            CallbackMessageHandler handler = mCallbacks.get(i);
             if (cb == handler.mCallback) {
                 return handler;
             }
@@ -450,7 +609,7 @@
             throw new IllegalArgumentException("Callback cannot be null");
         }
         for (int i = mCallbacks.size() - 1; i >= 0; i--) {
-            MessageHandler handler = mCallbacks.get(i);
+            CallbackMessageHandler handler = mCallbacks.get(i);
             if (cb == handler.mCallback) {
                 mCallbacks.remove(i);
                 return true;
@@ -463,7 +622,7 @@
         Command cmd = new Command(command, extras, resultCb);
         synchronized (mLock) {
             for (int i = mCallbacks.size() - 1; i >= 0; i--) {
-                mCallbacks.get(i).post(MSG_COMMAND, cmd);
+                mCallbacks.get(i).post(CallbackMessageHandler.MSG_COMMAND, cmd);
             }
         }
     }
@@ -471,7 +630,7 @@
     private void postMediaButton(Intent mediaButtonIntent) {
         synchronized (mLock) {
             for (int i = mCallbacks.size() - 1; i >= 0; i--) {
-                mCallbacks.get(i).post(MSG_MEDIA_BUTTON, mediaButtonIntent);
+                mCallbacks.get(i).post(CallbackMessageHandler.MSG_MEDIA_BUTTON, mediaButtonIntent);
             }
         }
     }
@@ -479,7 +638,7 @@
     private void postRequestRouteChange(RouteInfo route) {
         synchronized (mLock) {
             for (int i = mCallbacks.size() - 1; i >= 0; i--) {
-                mCallbacks.get(i).post(MSG_ROUTE_CHANGE, route);
+                mCallbacks.get(i).post(CallbackMessageHandler.MSG_ROUTE_CHANGE, route);
             }
         }
     }
@@ -488,7 +647,7 @@
         synchronized (mLock) {
             mRoute = new Route(route, options, this);
             for (int i = mCallbacks.size() - 1; i >= 0; i--) {
-                mCallbacks.get(i).post(MSG_ROUTE_CONNECTED, mRoute);
+                mCallbacks.get(i).post(CallbackMessageHandler.MSG_ROUTE_CONNECTED, mRoute);
             }
         }
     }
@@ -497,16 +656,16 @@
         synchronized (mLock) {
             if (mRoute != null && TextUtils.equals(mRoute.getRouteInfo().getId(), route.getId())) {
                 for (int i = mCallbacks.size() - 1; i >= 0; i--) {
-                    mCallbacks.get(i).post(MSG_ROUTE_DISCONNECTED, mRoute, reason);
+                    mCallbacks.get(i).post(CallbackMessageHandler.MSG_ROUTE_DISCONNECTED, mRoute,
+                            reason);
                 }
             }
         }
     }
 
     /**
-     * Receives commands or updates from controllers and routes. An app can
-     * specify what commands and buttons it supports by setting them on the
-     * MediaSession.
+     * Receives generic commands or updates from controllers and the system.
+     * Callbacks may be registered using {@link #addCallback}.
      */
     public abstract static class Callback {
 
@@ -525,7 +684,7 @@
          * @param mediaButtonIntent an intent containing the KeyEvent as an
          *            extra
          */
-        public void onMediaButton(Intent mediaButtonIntent) {
+        public void onMediaButtonEvent(Intent mediaButtonIntent) {
         }
 
         /**
@@ -536,7 +695,7 @@
          * @param command
          * @param extras optional
          */
-        public void onCommand(String command, Bundle extras, ResultReceiver cb) {
+        public void onControlCommand(String command, Bundle extras, ResultReceiver cb) {
         }
 
         /**
@@ -582,6 +741,82 @@
     }
 
     /**
+     * Receives transport control commands. Callbacks may be registered using
+     * {@link #addTransportControlsCallback}.
+     */
+    public static abstract class TransportControlsCallback {
+
+        /**
+         * Override to handle requests to begin playback.
+         */
+        public void onPlay() {
+        }
+
+        /**
+         * Override to handle requests to pause playback.
+         */
+        public void onPause() {
+        }
+
+        /**
+         * Override to handle requests to skip to the next media item.
+         */
+        public void onSkipToNext() {
+        }
+
+        /**
+         * Override to handle requests to skip to the previous media item.
+         */
+        public void onSkipToPrevious() {
+        }
+
+        /**
+         * Override to handle requests to fast forward.
+         */
+        public void onFastForward() {
+        }
+
+        /**
+         * Override to handle requests to rewind.
+         */
+        public void onRewind() {
+        }
+
+        /**
+         * Override to handle requests to stop playback.
+         */
+        public void onStop() {
+        }
+
+        /**
+         * Override to handle requests to seek to a specific position in ms.
+         *
+         * @param pos New position to move to, in milliseconds.
+         */
+        public void onSeekTo(long pos) {
+        }
+
+        /**
+         * Override to handle the item being rated.
+         *
+         * @param rating
+         */
+        public void onSetRating(Rating rating) {
+        }
+
+        /**
+         * Report that audio focus has changed on the app. This only happens if
+         * you have indicated you have started playing with
+         * {@link #setPlaybackState}.
+         *
+         * @param focusChange The type of focus change, TBD.
+         * @hide
+         */
+        public void onRouteFocusChange(int focusChange) {
+        }
+    }
+
+    /**
      * @hide
      */
     public static class CallbackStub extends ISessionCallback.Stub {
@@ -643,10 +878,7 @@
         public void onPlay() throws RemoteException {
             MediaSession session = mMediaSession.get();
             if (session != null) {
-                TransportPerformer tp = session.getTransportPerformer();
-                if (tp != null) {
-                    tp.onPlay();
-                }
+                session.dispatchPlay();
             }
         }
 
@@ -654,10 +886,7 @@
         public void onPause() throws RemoteException {
             MediaSession session = mMediaSession.get();
             if (session != null) {
-                TransportPerformer tp = session.getTransportPerformer();
-                if (tp != null) {
-                    tp.onPause();
-                }
+                session.dispatchPause();
             }
         }
 
@@ -665,10 +894,7 @@
         public void onStop() throws RemoteException {
             MediaSession session = mMediaSession.get();
             if (session != null) {
-                TransportPerformer tp = session.getTransportPerformer();
-                if (tp != null) {
-                    tp.onStop();
-                }
+                session.dispatchStop();
             }
         }
 
@@ -676,10 +902,7 @@
         public void onNext() throws RemoteException {
             MediaSession session = mMediaSession.get();
             if (session != null) {
-                TransportPerformer tp = session.getTransportPerformer();
-                if (tp != null) {
-                    tp.onNext();
-                }
+                session.dispatchNext();
             }
         }
 
@@ -687,10 +910,7 @@
         public void onPrevious() throws RemoteException {
             MediaSession session = mMediaSession.get();
             if (session != null) {
-                TransportPerformer tp = session.getTransportPerformer();
-                if (tp != null) {
-                    tp.onPrevious();
-                }
+                session.dispatchPrevious();
             }
         }
 
@@ -698,10 +918,7 @@
         public void onFastForward() throws RemoteException {
             MediaSession session = mMediaSession.get();
             if (session != null) {
-                TransportPerformer tp = session.getTransportPerformer();
-                if (tp != null) {
-                    tp.onFastForward();
-                }
+                session.dispatchFastForward();
             }
         }
 
@@ -709,10 +926,7 @@
         public void onRewind() throws RemoteException {
             MediaSession session = mMediaSession.get();
             if (session != null) {
-                TransportPerformer tp = session.getTransportPerformer();
-                if (tp != null) {
-                    tp.onRewind();
-                }
+                session.dispatchRewind();
             }
         }
 
@@ -720,10 +934,7 @@
         public void onSeekTo(long pos) throws RemoteException {
             MediaSession session = mMediaSession.get();
             if (session != null) {
-                TransportPerformer tp = session.getTransportPerformer();
-                if (tp != null) {
-                    tp.onSeekTo(pos);
-                }
+                session.dispatchSeekTo(pos);
             }
         }
 
@@ -731,10 +942,7 @@
         public void onRate(Rating rating) throws RemoteException {
             MediaSession session = mMediaSession.get();
             if (session != null) {
-                TransportPerformer tp = session.getTransportPerformer();
-                if (tp != null) {
-                    tp.onRate(rating);
-                }
+                session.dispatchRate(rating);
             }
         }
 
@@ -758,12 +966,38 @@
 
         }
 
+        /*
+         * (non-Javadoc)
+         * @see android.media.session.ISessionCallback#onAdjustVolumeBy(int)
+         */
+        @Override
+        public void onAdjustVolumeBy(int delta) throws RemoteException {
+            // TODO(epastern): Auto-generated method stub
+
+        }
+
+        /*
+         * (non-Javadoc)
+         * @see android.media.session.ISessionCallback#onSetVolumeTo(int)
+         */
+        @Override
+        public void onSetVolumeTo(int value) throws RemoteException {
+            // TODO(epastern): Auto-generated method stub
+
+        }
+
     }
 
-    private class MessageHandler extends Handler {
+    private class CallbackMessageHandler extends Handler {
+        private static final int MSG_MEDIA_BUTTON = 1;
+        private static final int MSG_COMMAND = 2;
+        private static final int MSG_ROUTE_CHANGE = 3;
+        private static final int MSG_ROUTE_CONNECTED = 4;
+        private static final int MSG_ROUTE_DISCONNECTED = 5;
+
         private MediaSession.Callback mCallback;
 
-        public MessageHandler(Looper looper, MediaSession.Callback callback) {
+        public CallbackMessageHandler(Looper looper, MediaSession.Callback callback) {
             super(looper, null, true);
             mCallback = callback;
         }
@@ -776,11 +1010,11 @@
                 }
                 switch (msg.what) {
                     case MSG_MEDIA_BUTTON:
-                        mCallback.onMediaButton((Intent) msg.obj);
+                        mCallback.onMediaButtonEvent((Intent) msg.obj);
                         break;
                     case MSG_COMMAND:
                         Command cmd = (Command) msg.obj;
-                        mCallback.onCommand(cmd.command, cmd.extras, cmd.stub);
+                        mCallback.onControlCommand(cmd.command, cmd.extras, cmd.stub);
                         break;
                     case MSG_ROUTE_CHANGE:
                         mCallback.onRequestRouteChange((RouteInfo) msg.obj);
@@ -815,4 +1049,64 @@
             this.stub = stub;
         }
     }
+
+    private class TransportMessageHandler extends Handler {
+        private static final int MSG_PLAY = 1;
+        private static final int MSG_PAUSE = 2;
+        private static final int MSG_STOP = 3;
+        private static final int MSG_NEXT = 4;
+        private static final int MSG_PREVIOUS = 5;
+        private static final int MSG_FAST_FORWARD = 6;
+        private static final int MSG_REWIND = 7;
+        private static final int MSG_SEEK_TO = 8;
+        private static final int MSG_RATE = 9;
+
+        private TransportControlsCallback mCallback;
+
+        public TransportMessageHandler(Looper looper, TransportControlsCallback cb) {
+            super(looper);
+            mCallback = cb;
+        }
+
+        public void post(int what, Object obj) {
+            obtainMessage(what, obj).sendToTarget();
+        }
+
+        public void post(int what) {
+            post(what, null);
+        }
+
+        @Override
+        public void handleMessage(Message msg) {
+            switch (msg.what) {
+                case MSG_PLAY:
+                    mCallback.onPlay();
+                    break;
+                case MSG_PAUSE:
+                    mCallback.onPause();
+                    break;
+                case MSG_STOP:
+                    mCallback.onStop();
+                    break;
+                case MSG_NEXT:
+                    mCallback.onSkipToNext();
+                    break;
+                case MSG_PREVIOUS:
+                    mCallback.onSkipToPrevious();
+                    break;
+                case MSG_FAST_FORWARD:
+                    mCallback.onFastForward();
+                    break;
+                case MSG_REWIND:
+                    mCallback.onRewind();
+                    break;
+                case MSG_SEEK_TO:
+                    mCallback.onSeekTo((Long) msg.obj);
+                    break;
+                case MSG_RATE:
+                    mCallback.onSetRating((Rating) msg.obj);
+                    break;
+            }
+        }
+    }
 }
diff --git a/media/java/android/media/session/MediaSessionInfo.java b/media/java/android/media/session/MediaSessionInfo.java
index 3d8d33f..f701211 100644
--- a/media/java/android/media/session/MediaSessionInfo.java
+++ b/media/java/android/media/session/MediaSessionInfo.java
@@ -20,6 +20,8 @@
 
 /**
  * Information about a media session, including the owner's package name.
+ *
+ * @hide
  */
 public final class MediaSessionInfo implements Parcelable {
     private final String mId;
diff --git a/media/java/android/media/session/MediaSessionLegacyHelper.java b/media/java/android/media/session/MediaSessionLegacyHelper.java
index 249b9c4..099f601 100644
--- a/media/java/android/media/session/MediaSessionLegacyHelper.java
+++ b/media/java/android/media/session/MediaSessionLegacyHelper.java
@@ -76,13 +76,20 @@
         }
     }
 
-    public void addRccListener(PendingIntent pi, TransportPerformer.Listener listener) {
+    public void sendAdjustVolumeBy(int suggestedStream, int delta, int flags) {
+        mSessionManager.dispatchAdjustVolumeBy(suggestedStream, delta, flags);
+        if (DEBUG) {
+            Log.d(TAG, "dispatched volume adjustment");
+        }
+    }
+
+    public void addRccListener(PendingIntent pi,
+            MediaSession.TransportControlsCallback listener) {
         if (pi == null) {
             Log.w(TAG, "Pending intent was null, can't add rcc listener.");
             return;
         }
         SessionHolder holder = getHolder(pi, true);
-        TransportPerformer performer = holder.mSession.getTransportPerformer();
         if (holder.mRccListener != null) {
             if (holder.mRccListener == listener) {
                 if (DEBUG) {
@@ -92,9 +99,9 @@
                 return;
             }
             // Otherwise it changed so we need to switch to the new one
-            performer.removeListener(holder.mRccListener);
+            holder.mSession.removeTransportControlsCallback(holder.mRccListener);
         }
-        performer.addListener(listener, mHandler);
+        holder.mSession.addTransportControlsCallback(listener, mHandler);
         holder.mRccListener = listener;
         holder.mFlags |= MediaSession.FLAG_HANDLES_TRANSPORT_CONTROLS;
         holder.mSession.setFlags(holder.mFlags);
@@ -110,7 +117,7 @@
         }
         SessionHolder holder = getHolder(pi, false);
         if (holder != null && holder.mRccListener != null) {
-            holder.mSession.getTransportPerformer().removeListener(holder.mRccListener);
+            holder.mSession.removeTransportControlsCallback(holder.mRccListener);
             holder.mRccListener = null;
             holder.mFlags &= ~MediaSession.FLAG_HANDLES_TRANSPORT_CONTROLS;
             holder.mSession.setFlags(holder.mFlags);
@@ -141,7 +148,7 @@
         // set this flag
         holder.mFlags |= MediaSession.FLAG_HANDLES_MEDIA_BUTTONS;
         holder.mSession.setFlags(holder.mFlags);
-        holder.mSession.getTransportPerformer().addListener(holder.mMediaButtonListener, mHandler);
+        holder.mSession.addTransportControlsCallback(holder.mMediaButtonListener, mHandler);
 
         holder.mMediaButtonReceiver = new MediaButtonReceiver(pi, context);
         holder.mSession.addCallback(holder.mMediaButtonReceiver, mHandler);
@@ -156,7 +163,7 @@
         }
         SessionHolder holder = getHolder(pi, false);
         if (holder != null && holder.mMediaButtonListener != null) {
-            holder.mSession.getTransportPerformer().removeListener(holder.mMediaButtonListener);
+            holder.mSession.removeTransportControlsCallback(holder.mMediaButtonListener);
             holder.mFlags &= ~MediaSession.FLAG_HANDLES_MEDIA_BUTTONS;
             holder.mSession.setFlags(holder.mFlags);
             holder.mMediaButtonListener = null;
@@ -201,12 +208,12 @@
         }
 
         @Override
-        public void onMediaButton(Intent mediaButtonIntent) {
+        public void onMediaButtonEvent(Intent mediaButtonIntent) {
             MediaSessionLegacyHelper.sendKeyEvent(mPendingIntent, mContext, mediaButtonIntent);
         }
     }
 
-    private static final class MediaButtonListener extends TransportPerformer.Listener {
+    private static final class MediaButtonListener extends MediaSession.TransportControlsCallback {
         private final PendingIntent mPendingIntent;
         private final Context mContext;
 
@@ -226,12 +233,12 @@
         }
 
         @Override
-        public void onNext() {
+        public void onSkipToNext() {
             sendKeyEvent(KeyEvent.KEYCODE_MEDIA_NEXT);
         }
 
         @Override
-        public void onPrevious() {
+        public void onSkipToPrevious() {
             sendKeyEvent(KeyEvent.KEYCODE_MEDIA_PREVIOUS);
         }
 
@@ -272,7 +279,7 @@
         public final PendingIntent mPi;
         public MediaButtonListener mMediaButtonListener;
         public MediaButtonReceiver mMediaButtonReceiver;
-        public TransportPerformer.Listener mRccListener;
+        public MediaSession.TransportControlsCallback mRccListener;
         public int mFlags;
 
         public SessionHolder(MediaSession session, PendingIntent pi) {
diff --git a/media/java/android/media/session/MediaSessionManager.java b/media/java/android/media/session/MediaSessionManager.java
index 0589a7d..9e8b0d3 100644
--- a/media/java/android/media/session/MediaSessionManager.java
+++ b/media/java/android/media/session/MediaSessionManager.java
@@ -106,6 +106,7 @@
      * @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());
@@ -165,4 +166,22 @@
             Log.e(TAG, "Failed to send key event.", e);
         }
     }
+
+    /**
+     * Dispatch an adjust volume request to the system. It will be routed to the
+     * most relevant stream/session.
+     *
+     * @param suggestedStream The stream to fall back to if there isn't a
+     *            relevant stream
+     * @param delta The amount to adjust the volume by.
+     * @param flags Any flags to include with the volume change.
+     * @hide
+     */
+    public void dispatchAdjustVolumeBy(int suggestedStream, int delta, int flags) {
+        try {
+            mService.dispatchAdjustVolumeBy(suggestedStream, delta, flags);
+        } catch (RemoteException e) {
+            Log.e(TAG, "Failed to send adjust volume.", e);
+        }
+    }
 }
diff --git a/media/java/android/media/session/MediaSessionToken.java b/media/java/android/media/session/MediaSessionToken.java
index f5569a4..86f5662 100644
--- a/media/java/android/media/session/MediaSessionToken.java
+++ b/media/java/android/media/session/MediaSessionToken.java
@@ -20,7 +20,12 @@
 import android.os.Parcel;
 import android.os.Parcelable;
 
-public class MediaSessionToken implements Parcelable {
+/**
+ * Represents an ongoing session. This may be passed to apps by the session
+ * owner to allow them to create a {@link MediaController} to communicate with
+ * the session.
+ */
+public final class MediaSessionToken implements Parcelable {
     private ISessionController mBinder;
 
     /**
diff --git a/media/java/android/media/session/PlaybackState.java b/media/java/android/media/session/PlaybackState.java
index 7ef38eaa..e09ac3f 100644
--- a/media/java/android/media/session/PlaybackState.java
+++ b/media/java/android/media/session/PlaybackState.java
@@ -22,7 +22,7 @@
 
 /**
  * Playback state for a {@link MediaSession}. This includes a state like
- * {@link PlaybackState#PLAYSTATE_PLAYING}, the current playback position,
+ * {@link PlaybackState#STATE_PLAYING}, the current playback position,
  * and the current control capabilities.
  */
 public final class PlaybackState implements Parcelable {
@@ -59,28 +59,28 @@
      *
      * @see #setActions
      */
-    public static final long ACTION_PREVIOUS_ITEM = 1 << 4;
+    public static final long ACTION_SKIP_TO_PREVIOUS = 1 << 4;
 
     /**
      * Indicates this performer supports the next command.
      *
      * @see #setActions
      */
-    public static final long ACTION_NEXT_ITEM = 1 << 5;
+    public static final long ACTION_SKIP_TO_NEXT = 1 << 5;
 
     /**
      * Indicates this performer supports the fast forward command.
      *
      * @see #setActions
      */
-    public static final long ACTION_FASTFORWARD = 1 << 6;
+    public static final long ACTION_FAST_FORWARD = 1 << 6;
 
     /**
      * Indicates this performer supports the set rating command.
      *
      * @see #setActions
      */
-    public static final long ACTION_RATING = 1 << 7;
+    public static final long ACTION_SET_RATING = 1 << 7;
 
     /**
      * Indicates this performer supports the seek to command.
@@ -102,42 +102,42 @@
      *
      * @see #setState
      */
-    public final static int PLAYSTATE_NONE = 0;
+    public final static int STATE_NONE = 0;
 
     /**
      * State indicating this item is currently stopped.
      *
      * @see #setState
      */
-    public final static int PLAYSTATE_STOPPED = 1;
+    public final static int STATE_STOPPED = 1;
 
     /**
      * State indicating this item is currently paused.
      *
      * @see #setState
      */
-    public final static int PLAYSTATE_PAUSED = 2;
+    public final static int STATE_PAUSED = 2;
 
     /**
      * State indicating this item is currently playing.
      *
      * @see #setState
      */
-    public final static int PLAYSTATE_PLAYING = 3;
+    public final static int STATE_PLAYING = 3;
 
     /**
      * State indicating this item is currently fast forwarding.
      *
      * @see #setState
      */
-    public final static int PLAYSTATE_FAST_FORWARDING = 4;
+    public final static int STATE_FAST_FORWARDING = 4;
 
     /**
      * State indicating this item is currently rewinding.
      *
      * @see #setState
      */
-    public final static int PLAYSTATE_REWINDING = 5;
+    public final static int STATE_REWINDING = 5;
 
     /**
      * State indicating this item is currently buffering and will begin playing
@@ -145,7 +145,7 @@
      *
      * @see #setState
      */
-    public final static int PLAYSTATE_BUFFERING = 6;
+    public final static int STATE_BUFFERING = 6;
 
     /**
      * State indicating this item is currently in an error state. The error
@@ -153,30 +153,30 @@
      *
      * @see #setState
      */
-    public final static int PLAYSTATE_ERROR = 7;
+    public final static int STATE_ERROR = 7;
 
     /**
      * State indicating the class doing playback is currently connecting to a
      * route. Depending on the implementation you may return to the previous
-     * state when the connection finishes or enter {@link #PLAYSTATE_NONE}. If
-     * the connection failed {@link #PLAYSTATE_ERROR} should be used.
+     * state when the connection finishes or enter {@link #STATE_NONE}. If
+     * the connection failed {@link #STATE_ERROR} should be used.
      * @hide
      */
-    public final static int PLAYSTATE_CONNECTING = 8;
+    public final static int STATE_CONNECTING = 8;
 
     /**
      * State indicating the player is currently skipping to the previous item.
      *
      * @see #setState
      */
-    public final static int PLAYSTATE_SKIPPING_BACKWARDS = 9;
+    public final static int STATE_SKIPPING_TO_PREVIOUS = 9;
 
     /**
      * State indicating the player is currently skipping to the next item.
      *
      * @see #setState
      */
-    public final static int PLAYSTATE_SKIPPING_FORWARDS = 10;
+    public final static int STATE_SKIPPING_TO_NEXT = 10;
 
     /**
      * Use this value for the position to indicate the position is not known.
@@ -188,7 +188,7 @@
     private long mBufferPosition;
     private float mRate;
     private long mActions;
-    private String mErrorMessage;
+    private CharSequence mErrorMessage;
     private long mUpdateTime;
 
     /**
@@ -221,7 +221,7 @@
         mUpdateTime = in.readLong();
         mBufferPosition = in.readLong();
         mActions = in.readLong();
-        mErrorMessage = in.readString();
+        mErrorMessage = in.readCharSequence();
 
     }
 
@@ -252,20 +252,20 @@
         dest.writeLong(mUpdateTime);
         dest.writeLong(mBufferPosition);
         dest.writeLong(mActions);
-        dest.writeString(mErrorMessage);
+        dest.writeCharSequence(mErrorMessage);
     }
 
     /**
      * Get the current state of playback. One of the following:
      * <ul>
-     * <li> {@link PlaybackState#PLAYSTATE_NONE}</li>
-     * <li> {@link PlaybackState#PLAYSTATE_STOPPED}</li>
-     * <li> {@link PlaybackState#PLAYSTATE_PLAYING}</li>
-     * <li> {@link PlaybackState#PLAYSTATE_PAUSED}</li>
-     * <li> {@link PlaybackState#PLAYSTATE_FAST_FORWARDING}</li>
-     * <li> {@link PlaybackState#PLAYSTATE_REWINDING}</li>
-     * <li> {@link PlaybackState#PLAYSTATE_BUFFERING}</li>
-     * <li> {@link PlaybackState#PLAYSTATE_ERROR}</li>
+     * <li> {@link PlaybackState#STATE_NONE}</li>
+     * <li> {@link PlaybackState#STATE_STOPPED}</li>
+     * <li> {@link PlaybackState#STATE_PLAYING}</li>
+     * <li> {@link PlaybackState#STATE_PAUSED}</li>
+     * <li> {@link PlaybackState#STATE_FAST_FORWARDING}</li>
+     * <li> {@link PlaybackState#STATE_REWINDING}</li>
+     * <li> {@link PlaybackState#STATE_BUFFERING}</li>
+     * <li> {@link PlaybackState#STATE_ERROR}</li>
      */
     public int getState() {
         return mState;
@@ -283,25 +283,25 @@
      * <p>
      * The state must be one of the following:
      * <ul>
-     * <li> {@link PlaybackState#PLAYSTATE_NONE}</li>
-     * <li> {@link PlaybackState#PLAYSTATE_STOPPED}</li>
-     * <li> {@link PlaybackState#PLAYSTATE_PLAYING}</li>
-     * <li> {@link PlaybackState#PLAYSTATE_PAUSED}</li>
-     * <li> {@link PlaybackState#PLAYSTATE_FAST_FORWARDING}</li>
-     * <li> {@link PlaybackState#PLAYSTATE_REWINDING}</li>
-     * <li> {@link PlaybackState#PLAYSTATE_BUFFERING}</li>
-     * <li> {@link PlaybackState#PLAYSTATE_ERROR}</li>
+     * <li> {@link PlaybackState#STATE_NONE}</li>
+     * <li> {@link PlaybackState#STATE_STOPPED}</li>
+     * <li> {@link PlaybackState#STATE_PLAYING}</li>
+     * <li> {@link PlaybackState#STATE_PAUSED}</li>
+     * <li> {@link PlaybackState#STATE_FAST_FORWARDING}</li>
+     * <li> {@link PlaybackState#STATE_REWINDING}</li>
+     * <li> {@link PlaybackState#STATE_BUFFERING}</li>
+     * <li> {@link PlaybackState#STATE_ERROR}</li>
      * </ul>
      *
      * @param state The current state of playback.
      * @param position The position in the current track in ms.
-     * @param rate The current rate of playback as a multiple of normal
+     * @param playbackRate The current rate of playback as a multiple of normal
      *            playback.
      */
-    public void setState(int state, long position, float rate) {
+    public void setState(int state, long position, float playbackRate) {
         this.mState = state;
         this.mPosition = position;
-        this.mRate = rate;
+        this.mRate = playbackRate;
         mUpdateTime = SystemClock.elapsedRealtime();
     }
 
@@ -337,7 +337,7 @@
      *
      * @return The current rate of playback.
      */
-    public float getRate() {
+    public float getPlaybackRate() {
         return mRate;
     }
 
@@ -345,15 +345,15 @@
      * Get the current actions available on this session. This should use a
      * bitmask of the available actions.
      * <ul>
-     * <li> {@link PlaybackState#ACTION_PREVIOUS_ITEM}</li>
+     * <li> {@link PlaybackState#ACTION_SKIP_TO_PREVIOUS}</li>
      * <li> {@link PlaybackState#ACTION_REWIND}</li>
      * <li> {@link PlaybackState#ACTION_PLAY}</li>
      * <li> {@link PlaybackState#ACTION_PAUSE}</li>
      * <li> {@link PlaybackState#ACTION_STOP}</li>
-     * <li> {@link PlaybackState#ACTION_FASTFORWARD}</li>
-     * <li> {@link PlaybackState#ACTION_NEXT_ITEM}</li>
+     * <li> {@link PlaybackState#ACTION_FAST_FORWARD}</li>
+     * <li> {@link PlaybackState#ACTION_SKIP_TO_NEXT}</li>
      * <li> {@link PlaybackState#ACTION_SEEK_TO}</li>
-     * <li> {@link PlaybackState#ACTION_RATING}</li>
+     * <li> {@link PlaybackState#ACTION_SET_RATING}</li>
      * </ul>
      */
     public long getActions() {
@@ -364,15 +364,15 @@
      * Set the current capabilities available on this session. This should use a
      * bitmask of the available capabilities.
      * <ul>
-     * <li> {@link PlaybackState#ACTION_PREVIOUS_ITEM}</li>
+     * <li> {@link PlaybackState#ACTION_SKIP_TO_PREVIOUS}</li>
      * <li> {@link PlaybackState#ACTION_REWIND}</li>
      * <li> {@link PlaybackState#ACTION_PLAY}</li>
      * <li> {@link PlaybackState#ACTION_PAUSE}</li>
      * <li> {@link PlaybackState#ACTION_STOP}</li>
-     * <li> {@link PlaybackState#ACTION_FASTFORWARD}</li>
-     * <li> {@link PlaybackState#ACTION_NEXT_ITEM}</li>
+     * <li> {@link PlaybackState#ACTION_FAST_FORWARD}</li>
+     * <li> {@link PlaybackState#ACTION_SKIP_TO_NEXT}</li>
      * <li> {@link PlaybackState#ACTION_SEEK_TO}</li>
-     * <li> {@link PlaybackState#ACTION_RATING}</li>
+     * <li> {@link PlaybackState#ACTION_SET_RATING}</li>
      * </ul>
      */
     public void setActions(long capabilities) {
@@ -381,9 +381,9 @@
 
     /**
      * Get a user readable error message. This should be set when the state is
-     * {@link PlaybackState#PLAYSTATE_ERROR}.
+     * {@link PlaybackState#STATE_ERROR}.
      */
-    public String getErrorMessage() {
+    public CharSequence getErrorMessage() {
         return mErrorMessage;
     }
 
@@ -400,9 +400,9 @@
 
     /**
      * Set a user readable error message. This should be set when the state is
-     * {@link PlaybackState#PLAYSTATE_ERROR}.
+     * {@link PlaybackState#STATE_ERROR}.
      */
-    public void setErrorMessage(String errorMessage) {
+    public void setErrorMessage(CharSequence errorMessage) {
         mErrorMessage = errorMessage;
     }
 
@@ -417,23 +417,23 @@
     public static int getStateFromRccState(int rccState) {
         switch (rccState) {
             case RemoteControlClient.PLAYSTATE_BUFFERING:
-                return PLAYSTATE_BUFFERING;
+                return STATE_BUFFERING;
             case RemoteControlClient.PLAYSTATE_ERROR:
-                return PLAYSTATE_ERROR;
+                return STATE_ERROR;
             case RemoteControlClient.PLAYSTATE_FAST_FORWARDING:
-                return PLAYSTATE_FAST_FORWARDING;
+                return STATE_FAST_FORWARDING;
             case RemoteControlClient.PLAYSTATE_NONE:
-                return PLAYSTATE_NONE;
+                return STATE_NONE;
             case RemoteControlClient.PLAYSTATE_PAUSED:
-                return PLAYSTATE_PAUSED;
+                return STATE_PAUSED;
             case RemoteControlClient.PLAYSTATE_PLAYING:
-                return PLAYSTATE_PLAYING;
+                return STATE_PLAYING;
             case RemoteControlClient.PLAYSTATE_REWINDING:
-                return PLAYSTATE_REWINDING;
+                return STATE_REWINDING;
             case RemoteControlClient.PLAYSTATE_SKIPPING_BACKWARDS:
-                return PLAYSTATE_SKIPPING_BACKWARDS;
+                return STATE_SKIPPING_TO_PREVIOUS;
             case RemoteControlClient.PLAYSTATE_STOPPED:
-                return PLAYSTATE_STOPPED;
+                return STATE_STOPPED;
             default:
                 return -1;
         }
@@ -457,7 +457,7 @@
     private static long getActionForRccFlag(int flag) {
         switch (flag) {
             case RemoteControlClient.FLAG_KEY_MEDIA_PREVIOUS:
-                return ACTION_PREVIOUS_ITEM;
+                return ACTION_SKIP_TO_PREVIOUS;
             case RemoteControlClient.FLAG_KEY_MEDIA_REWIND:
                 return ACTION_REWIND;
             case RemoteControlClient.FLAG_KEY_MEDIA_PLAY:
@@ -469,13 +469,13 @@
             case RemoteControlClient.FLAG_KEY_MEDIA_STOP:
                 return ACTION_STOP;
             case RemoteControlClient.FLAG_KEY_MEDIA_FAST_FORWARD:
-                return ACTION_FASTFORWARD;
+                return ACTION_FAST_FORWARD;
             case RemoteControlClient.FLAG_KEY_MEDIA_NEXT:
-                return ACTION_NEXT_ITEM;
+                return ACTION_SKIP_TO_NEXT;
             case RemoteControlClient.FLAG_KEY_MEDIA_POSITION_UPDATE:
                 return ACTION_SEEK_TO;
             case RemoteControlClient.FLAG_KEY_MEDIA_RATING:
-                return ACTION_RATING;
+                return ACTION_SET_RATING;
         }
         return 0;
     }
diff --git a/media/java/android/media/session/RemoteVolumeProvider.java b/media/java/android/media/session/RemoteVolumeProvider.java
index 9526cc8..47f672f 100644
--- a/media/java/android/media/session/RemoteVolumeProvider.java
+++ b/media/java/android/media/session/RemoteVolumeProvider.java
@@ -1,35 +1,61 @@
+/*
+ * 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.media.session;
 
 /**
  * Handles requests to adjust or set the volume on a session. This is also used
  * to push volume updates back to the session after a request has been handled.
  * You can set a volume provider on a session by calling
- * {@link MediaSession#useRemotePlayback}.
+ * {@link MediaSession#setPlaybackToRemote}.
  */
 public abstract class RemoteVolumeProvider {
 
     /**
-     * Handles relative volume changes via {@link #onAdjustVolume(int)}.
+     * The volume is fixed and can not be modified. Requests to change volume
+     * should be ignored.
      */
-    public static final int FLAG_VOLUME_RELATIVE = 1 << 0;
+    public static final int VOLUME_CONTROL_FIXED = 0;
 
     /**
-     * Handles setting the volume via {@link #onSetVolume(int)}.
+     * The volume control uses relative adjustment via
+     * {@link #onAdjustVolumeBy(int)}. Attempts to set the volume to a specific
+     * value should be ignored.
      */
-    public static final int FLAG_VOLUME_ABSOLUTE = 1 << 1;
+    public static final int VOLUME_CONTROL_RELATIVE = 1;
 
-    private final int mFlags;
+    /**
+     * The volume control uses an absolute value. It may be adjusted using
+     * {@link #onAdjustVolumeBy(int)} or set directly using
+     * {@link #onSetVolumeTo(int)}.
+     */
+    public static final int VOLUME_CONTROL_ABSOLUTE = 2;
+
+    private final int mControlType;
     private final int mMaxVolume;
 
     /**
      * Create a new volume provider for handling volume events. You must specify
-     * the type of events and the maximum volume that can be used.
+     * the type of volume control and the maximum volume that can be used.
      *
-     * @param flags The flags to use with this provider.
+     * @param volumeControl The method for controlling volume that is used by
+     *            this provider.
      * @param maxVolume The maximum allowed volume.
      */
-    public RemoteVolumeProvider(int flags, int maxVolume) {
-        mFlags = flags;
+    public RemoteVolumeProvider(int volumeControl, int maxVolume) {
+        mControlType = volumeControl;
         mMaxVolume = maxVolume;
     }
 
@@ -38,15 +64,15 @@
      *
      * @return The current volume.
      */
-    public abstract int getCurrentVolume();
+    public abstract int onGetCurrentVolume();
 
     /**
-     * Get the flags that were set for this volume provider.
+     * Get the volume control type that this volume provider uses.
      *
-     * @return The flags for this volume provider
+     * @return The volume control type for this volume provider
      */
-    public final int getFlags() {
-        return mFlags;
+    public final int getVolumeControl() {
+        return mControlType;
     }
 
     /**
@@ -59,7 +85,7 @@
     }
 
     /**
-     * Notify the system that the remove playback's volume has been changed.
+     * Notify the system that the remote playback's volume has been changed.
      */
     public final void notifyVolumeChanged() {
         // TODO
@@ -70,7 +96,7 @@
      *
      * @param volume The volume to set the output to.
      */
-    public void onSetVolume(int volume) {
+    public void onSetVolumeTo(int volume) {
     }
 
     /**
@@ -79,6 +105,6 @@
      *
      * @param delta The amount to change the volume
      */
-    public void onAdjustVolume(int delta) {
+    public void onAdjustVolumeBy(int delta) {
     }
 }
\ No newline at end of file
diff --git a/media/java/android/media/session/TransportController.java b/media/java/android/media/session/TransportController.java
deleted file mode 100644
index 090489b..0000000
--- a/media/java/android/media/session/TransportController.java
+++ /dev/null
@@ -1,343 +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.media.session;
-
-import android.media.MediaMetadata;
-import android.media.Rating;
-import android.os.Handler;
-import android.os.Looper;
-import android.os.Message;
-import android.os.RemoteException;
-import android.util.Log;
-
-import java.util.ArrayList;
-
-/**
- * Interface for controlling media playback on a session. This allows an app to
- * request changes in playback, retrieve the current playback state and
- * metadata, and listen for changes to the playback state and metadata.
- */
-public final class TransportController {
-    private static final String TAG = "TransportController";
-
-    private final Object mLock = new Object();
-    private final ArrayList<MessageHandler> mListeners = new ArrayList<MessageHandler>();
-    private final ISessionController mBinder;
-
-    /**
-     * @hide
-     */
-    public TransportController(ISessionController binder) {
-        mBinder = binder;
-    }
-
-    /**
-     * Start listening to changes in playback state.
-     */
-    public void addStateListener(TransportStateListener listener) {
-        addStateListener(listener, null);
-    }
-
-    public void addStateListener(TransportStateListener listener, Handler handler) {
-        if (listener == null) {
-            throw new IllegalArgumentException("Listener cannot be null");
-        }
-        synchronized (mLock) {
-            if (getHandlerForListenerLocked(listener) != null) {
-                Log.w(TAG, "Listener is already added, ignoring");
-                return;
-            }
-            if (handler == null) {
-                handler = new Handler();
-            }
-
-            MessageHandler msgHandler = new MessageHandler(handler.getLooper(), listener);
-            mListeners.add(msgHandler);
-        }
-    }
-
-    /**
-     * Stop listening to changes in playback state.
-     */
-    public void removeStateListener(TransportStateListener listener) {
-        if (listener == null) {
-            throw new IllegalArgumentException("Listener cannot be null");
-        }
-        synchronized (mLock) {
-            removeStateListenerLocked(listener);
-        }
-    }
-
-    /**
-     * Request that the player start its playback at its current position.
-     */
-    public void play() {
-        try {
-            mBinder.play();
-        } catch (RemoteException e) {
-            Log.wtf(TAG, "Error calling play.", e);
-        }
-    }
-
-    /**
-     * Request that the player pause its playback and stay at its current
-     * position.
-     */
-    public void pause() {
-        try {
-            mBinder.pause();
-        } catch (RemoteException e) {
-            Log.wtf(TAG, "Error calling pause.", e);
-        }
-    }
-
-    /**
-     * Request that the player stop its playback; it may clear its state in
-     * whatever way is appropriate.
-     */
-    public void stop() {
-        try {
-            mBinder.stop();
-        } catch (RemoteException e) {
-            Log.wtf(TAG, "Error calling stop.", e);
-        }
-    }
-
-    /**
-     * Move to a new location in the media stream.
-     *
-     * @param pos Position to move to, in milliseconds.
-     */
-    public void seekTo(long pos) {
-        try {
-            mBinder.seekTo(pos);
-        } catch (RemoteException e) {
-            Log.wtf(TAG, "Error calling seekTo.", e);
-        }
-    }
-
-    /**
-     * Start fast forwarding. If playback is already fast forwarding this may
-     * increase the rate.
-     */
-    public void fastForward() {
-        try {
-            mBinder.fastForward();
-        } catch (RemoteException e) {
-            Log.wtf(TAG, "Error calling fastForward.", e);
-        }
-    }
-
-    /**
-     * Skip to the next item.
-     */
-    public void next() {
-        try {
-            mBinder.next();
-        } catch (RemoteException e) {
-            Log.wtf(TAG, "Error calling next.", e);
-        }
-    }
-
-    /**
-     * Start rewinding. If playback is already rewinding this may increase the
-     * rate.
-     */
-    public void rewind() {
-        try {
-            mBinder.rewind();
-        } catch (RemoteException e) {
-            Log.wtf(TAG, "Error calling rewind.", e);
-        }
-    }
-
-    /**
-     * Skip to the previous item.
-     */
-    public void previous() {
-        try {
-            mBinder.previous();
-        } catch (RemoteException e) {
-            Log.wtf(TAG, "Error calling previous.", e);
-        }
-    }
-
-    /**
-     * Rate the current content. This will cause the rating to be set for the
-     * current user. The Rating type must match the type returned by
-     * {@link #getRatingType()}.
-     *
-     * @param rating The rating to set for the current content
-     */
-    public void rate(Rating rating) {
-        try {
-            mBinder.rate(rating);
-        } catch (RemoteException e) {
-            Log.wtf(TAG, "Error calling rate.", e);
-        }
-    }
-
-    /**
-     * Get the rating type supported by the session. One of:
-     * <ul>
-     * <li>{@link Rating#RATING_NONE}</li>
-     * <li>{@link Rating#RATING_HEART}</li>
-     * <li>{@link Rating#RATING_THUMB_UP_DOWN}</li>
-     * <li>{@link Rating#RATING_3_STARS}</li>
-     * <li>{@link Rating#RATING_4_STARS}</li>
-     * <li>{@link Rating#RATING_5_STARS}</li>
-     * <li>{@link Rating#RATING_PERCENTAGE}</li>
-     * </ul>
-     *
-     * @return The supported rating type
-     */
-    public int getRatingType() {
-        try {
-            return mBinder.getRatingType();
-        } catch (RemoteException e) {
-            Log.wtf(TAG, "Error calling getRatingType.", e);
-            return Rating.RATING_NONE;
-        }
-    }
-
-    /**
-     * Get the current playback state for this session.
-     *
-     * @return The current PlaybackState or null
-     */
-    public PlaybackState getPlaybackState() {
-        try {
-            return mBinder.getPlaybackState();
-        } catch (RemoteException e) {
-            Log.wtf(TAG, "Error calling getPlaybackState.", e);
-            return null;
-        }
-    }
-
-    /**
-     * Get the current metadata for this session.
-     *
-     * @return The current MediaMetadata or null.
-     */
-    public MediaMetadata getMetadata() {
-        try {
-            return mBinder.getMetadata();
-        } catch (RemoteException e) {
-            Log.wtf(TAG, "Error calling getMetadata.", e);
-            return null;
-        }
-    }
-
-    /**
-     * @hide
-     */
-    public final void postPlaybackStateChanged(PlaybackState state) {
-        synchronized (mLock) {
-            for (int i = mListeners.size() - 1; i >= 0; i--) {
-                mListeners.get(i).post(MessageHandler.MSG_UPDATE_PLAYBACK_STATE, state);
-            }
-        }
-    }
-
-    /**
-     * @hide
-     */
-    public final void postMetadataChanged(MediaMetadata metadata) {
-        synchronized (mLock) {
-            for (int i = mListeners.size() - 1; i >= 0; i--) {
-                mListeners.get(i).post(MessageHandler.MSG_UPDATE_METADATA,
-                        metadata);
-            }
-        }
-    }
-
-    private MessageHandler getHandlerForListenerLocked(TransportStateListener listener) {
-        for (int i = mListeners.size() - 1; i >= 0; i--) {
-            MessageHandler handler = mListeners.get(i);
-            if (listener == handler.mListener) {
-                return handler;
-            }
-        }
-        return null;
-    }
-
-    private boolean removeStateListenerLocked(TransportStateListener listener) {
-        for (int i = mListeners.size() - 1; i >= 0; i--) {
-            if (listener == mListeners.get(i).mListener) {
-                mListeners.remove(i);
-                return true;
-            }
-        }
-        return false;
-    }
-
-    /**
-     * Register using {@link #addStateListener} to receive updates when there
-     * are playback changes on the session.
-     */
-    public static abstract class TransportStateListener {
-        private MessageHandler mHandler;
-        /**
-         * Override to handle changes in playback state.
-         *
-         * @param state The new playback state of the session
-         */
-        public void onPlaybackStateChanged(PlaybackState state) {
-        }
-
-        /**
-         * Override to handle changes to the current metadata.
-         *
-         * @see MediaMetadata
-         * @param metadata The current metadata for the session or null
-         */
-        public void onMetadataChanged(MediaMetadata metadata) {
-        }
-
-        private void setHandler(Handler handler) {
-            mHandler = new MessageHandler(handler.getLooper(), this);
-        }
-    }
-
-    private static class MessageHandler extends Handler {
-        private static final int MSG_UPDATE_PLAYBACK_STATE = 1;
-        private static final int MSG_UPDATE_METADATA = 2;
-
-        private TransportStateListener mListener;
-
-        public MessageHandler(Looper looper, TransportStateListener cb) {
-            super(looper, null, true);
-            mListener = cb;
-        }
-
-        public void post(int msg, Object obj) {
-            obtainMessage(msg, obj).sendToTarget();
-        }
-
-        @Override
-        public void handleMessage(Message msg) {
-            switch (msg.what) {
-                case MSG_UPDATE_PLAYBACK_STATE:
-                    mListener.onPlaybackStateChanged((PlaybackState) msg.obj);
-                    break;
-                case MSG_UPDATE_METADATA:
-                    mListener.onMetadataChanged((MediaMetadata) msg.obj);
-                    break;
-            }
-        }
-    }
-
-}
diff --git a/media/java/android/media/session/TransportPerformer.java b/media/java/android/media/session/TransportPerformer.java
deleted file mode 100644
index 1588d8f..0000000
--- a/media/java/android/media/session/TransportPerformer.java
+++ /dev/null
@@ -1,351 +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.media.session;
-
-import android.media.AudioManager;
-import android.media.MediaMetadata;
-import android.media.Rating;
-import android.os.Handler;
-import android.os.Looper;
-import android.os.Message;
-import android.os.RemoteException;
-import android.util.Log;
-
-import java.lang.ref.WeakReference;
-import java.util.ArrayList;
-
-/**
- * Allows broadcasting of playback changes.
- */
-public final class TransportPerformer {
-    private static final String TAG = "TransportPerformer";
-    private final Object mLock = new Object();
-    private final ArrayList<MessageHandler> mListeners = new ArrayList<MessageHandler>();
-
-    private ISession mBinder;
-
-    /**
-     * @hide
-     */
-    public TransportPerformer(ISession binder) {
-        mBinder = binder;
-    }
-
-    /**
-     * Add a listener to receive updates on.
-     *
-     * @param listener The callback object
-     */
-    public void addListener(Listener listener) {
-        addListener(listener, null);
-    }
-
-    /**
-     * Add a listener to receive updates on. The updates will be posted to the
-     * specified handler. If no handler is provided they will be posted to the
-     * caller's thread.
-     *
-     * @param listener The listener to receive updates on
-     * @param handler The handler to post the updates on
-     */
-    public void addListener(Listener listener, Handler handler) {
-        if (listener == null) {
-            throw new IllegalArgumentException("Listener cannot be null");
-        }
-        synchronized (mLock) {
-            if (getHandlerForListenerLocked(listener) != null) {
-                Log.w(TAG, "Listener is already added, ignoring");
-            }
-            if (handler == null) {
-                handler = new Handler();
-            }
-            MessageHandler msgHandler = new MessageHandler(handler.getLooper(), listener);
-            mListeners.add(msgHandler);
-        }
-    }
-
-    /**
-     * Stop receiving updates on the specified handler. If an update has already
-     * been posted you may still receive it after this call returns.
-     *
-     * @param listener The listener to stop receiving updates on
-     */
-    public void removeListener(Listener listener) {
-        if (listener == null) {
-            throw new IllegalArgumentException("Listener cannot be null");
-        }
-        synchronized (mLock) {
-            removeListenerLocked(listener);
-        }
-    }
-
-    /**
-     * Update the current playback state.
-     *
-     * @param state The current state of playback
-     */
-    public final void setPlaybackState(PlaybackState state) {
-        try {
-            mBinder.setPlaybackState(state);
-        } catch (RemoteException e) {
-            Log.wtf(TAG, "Dead object in setPlaybackState.", e);
-        }
-    }
-
-    /**
-     * Update the current metadata. New metadata can be created using
-     * {@link MediaMetadata.Builder}.
-     *
-     * @param metadata The new metadata
-     */
-    public final void setMetadata(MediaMetadata metadata) {
-        try {
-            mBinder.setMetadata(metadata);
-        } catch (RemoteException e) {
-            Log.wtf(TAG, "Dead object in setPlaybackState.", e);
-        }
-    }
-
-    /**
-     * @hide
-     */
-    public final void onPlay() {
-        post(MessageHandler.MESSAGE_PLAY);
-    }
-
-    /**
-     * @hide
-     */
-    public final void onPause() {
-        post(MessageHandler.MESSAGE_PAUSE);
-    }
-
-    /**
-     * @hide
-     */
-    public final void onStop() {
-        post(MessageHandler.MESSAGE_STOP);
-    }
-
-    /**
-     * @hide
-     */
-    public final void onNext() {
-        post(MessageHandler.MESSAGE_NEXT);
-    }
-
-    /**
-     * @hide
-     */
-    public final void onPrevious() {
-        post(MessageHandler.MESSAGE_PREVIOUS);
-    }
-
-    /**
-     * @hide
-     */
-    public final void onFastForward() {
-        post(MessageHandler.MESSAGE_FAST_FORWARD);
-    }
-
-    /**
-     * @hide
-     */
-    public final void onRewind() {
-        post(MessageHandler.MESSAGE_REWIND);
-    }
-
-    /**
-     * @hide
-     */
-    public final void onSeekTo(long pos) {
-        post(MessageHandler.MESSAGE_SEEK_TO, pos);
-    }
-
-    /**
-     * @hide
-     */
-    public final void onRate(Rating rating) {
-        post(MessageHandler.MESSAGE_RATE, rating);
-    }
-
-    private MessageHandler getHandlerForListenerLocked(Listener listener) {
-        for (int i = mListeners.size() - 1; i >= 0; i--) {
-            MessageHandler handler = mListeners.get(i);
-            if (listener == handler.mListener) {
-                return handler;
-            }
-        }
-        return null;
-    }
-
-    private boolean removeListenerLocked(Listener listener) {
-        for (int i = mListeners.size() - 1; i >= 0; i--) {
-            if (listener == mListeners.get(i).mListener) {
-                mListeners.remove(i);
-                return true;
-            }
-        }
-        return false;
-    }
-
-    private void post(int what, Object obj) {
-        synchronized (mLock) {
-            for (int i = mListeners.size() - 1; i >= 0; i--) {
-                mListeners.get(i).post(what, obj);
-            }
-        }
-    }
-
-    private void post(int what) {
-        post(what, null);
-    }
-
-    /**
-     * Extend Listener to handle transport controls. Listeners can be registered
-     * using {@link #addListener}.
-     */
-    public static abstract class Listener {
-
-        /**
-         * Override to handle requests to begin playback.
-         */
-        public void onPlay() {
-        }
-
-        /**
-         * Override to handle requests to pause playback.
-         */
-        public void onPause() {
-        }
-
-        /**
-         * Override to handle requests to skip to the next media item.
-         */
-        public void onNext() {
-        }
-
-        /**
-         * Override to handle requests to skip to the previous media item.
-         */
-        public void onPrevious() {
-        }
-
-        /**
-         * Override to handle requests to fast forward.
-         */
-        public void onFastForward() {
-        }
-
-        /**
-         * Override to handle requests to rewind.
-         */
-        public void onRewind() {
-        }
-
-        /**
-         * Override to handle requests to stop playback.
-         */
-        public void onStop() {
-        }
-
-        /**
-         * Override to handle requests to seek to a specific position in ms.
-         *
-         * @param pos New position to move to, in milliseconds.
-         */
-        public void onSeekTo(long pos) {
-        }
-
-        /**
-         * Override to handle the item being rated.
-         *
-         * @param rating
-         */
-        public void onRate(Rating rating) {
-        }
-
-        /**
-         * Report that audio focus has changed on the app. This only happens if
-         * you have indicated you have started playing with
-         * {@link #setPlaybackState}.
-         *
-         * @param focusChange The type of focus change, TBD.
-         */
-        public void onRouteFocusChange(int focusChange) {
-        }
-    }
-
-    private class MessageHandler extends Handler {
-        private static final int MESSAGE_PLAY = 1;
-        private static final int MESSAGE_PAUSE = 2;
-        private static final int MESSAGE_STOP = 3;
-        private static final int MESSAGE_NEXT = 4;
-        private static final int MESSAGE_PREVIOUS = 5;
-        private static final int MESSAGE_FAST_FORWARD = 6;
-        private static final int MESSAGE_REWIND = 7;
-        private static final int MESSAGE_SEEK_TO = 8;
-        private static final int MESSAGE_RATE = 9;
-
-        private Listener mListener;
-
-        public MessageHandler(Looper looper, Listener cb) {
-            super(looper);
-            mListener = cb;
-        }
-
-        public void post(int what, Object obj) {
-            obtainMessage(what, obj).sendToTarget();
-        }
-
-        public void post(int what) {
-            post(what, null);
-        }
-
-        @Override
-        public void handleMessage(Message msg) {
-            switch (msg.what) {
-                case MESSAGE_PLAY:
-                    mListener.onPlay();
-                    break;
-                case MESSAGE_PAUSE:
-                    mListener.onPause();
-                    break;
-                case MESSAGE_STOP:
-                    mListener.onStop();
-                    break;
-                case MESSAGE_NEXT:
-                    mListener.onNext();
-                    break;
-                case MESSAGE_PREVIOUS:
-                    mListener.onPrevious();
-                    break;
-                case MESSAGE_FAST_FORWARD:
-                    mListener.onFastForward();
-                    break;
-                case MESSAGE_REWIND:
-                    mListener.onRewind();
-                    break;
-                case MESSAGE_SEEK_TO:
-                    mListener.onSeekTo((Long) msg.obj);
-                    break;
-                case MESSAGE_RATE:
-                    mListener.onRate((Rating) msg.obj);
-                    break;
-            }
-        }
-    }
-}
diff --git a/core/java/android/tv/ITvInputClient.aidl b/media/java/android/media/tv/ITvInputClient.aidl
similarity index 83%
rename from core/java/android/tv/ITvInputClient.aidl
rename to media/java/android/media/tv/ITvInputClient.aidl
index ac83356..dc79a73 100644
--- a/core/java/android/tv/ITvInputClient.aidl
+++ b/media/java/android/media/tv/ITvInputClient.aidl
@@ -14,10 +14,11 @@
  * limitations under the License.
  */
 
-package android.tv;
+package android.media.tv;
 
 import android.content.ComponentName;
-import android.tv.ITvInputSession;
+import android.media.tv.ITvInputSession;
+import android.os.Bundle;
 import android.view.InputChannel;
 
 /**
@@ -29,4 +30,6 @@
     void onSessionCreated(in String inputId, IBinder token, in InputChannel channel, int seq);
     void onAvailabilityChanged(in String inputId, boolean isAvailable);
     void onSessionReleased(int seq);
+    void onSessionEvent(in String name, in Bundle args, int seq);
+    void onVideoSizeChanged(int width, int height, int seq);
 }
diff --git a/core/java/android/tv/ITvInputHardware.aidl b/media/java/android/media/tv/ITvInputHardware.aidl
similarity index 95%
rename from core/java/android/tv/ITvInputHardware.aidl
rename to media/java/android/media/tv/ITvInputHardware.aidl
index 7250453..f35e8f3 100644
--- a/core/java/android/tv/ITvInputHardware.aidl
+++ b/media/java/android/media/tv/ITvInputHardware.aidl
@@ -14,9 +14,9 @@
  * limitations under the License.
  */
 
-package android.tv;
+package android.media.tv;
 
-import android.tv.TvStreamConfig;
+import android.media.tv.TvStreamConfig;
 import android.view.KeyEvent;
 import android.view.Surface;
 
diff --git a/core/java/android/tv/ITvInputHardwareCallback.aidl b/media/java/android/media/tv/ITvInputHardwareCallback.aidl
similarity index 91%
rename from core/java/android/tv/ITvInputHardwareCallback.aidl
rename to media/java/android/media/tv/ITvInputHardwareCallback.aidl
index 83041be..870883b 100644
--- a/core/java/android/tv/ITvInputHardwareCallback.aidl
+++ b/media/java/android/media/tv/ITvInputHardwareCallback.aidl
@@ -14,9 +14,9 @@
  * limitations under the License.
  */
 
-package android.tv;
+package android.media.tv;
 
-import android.tv.TvStreamConfig;
+import android.media.tv.TvStreamConfig;
 
 /**
  * @hide
diff --git a/core/java/android/tv/ITvInputManager.aidl b/media/java/android/media/tv/ITvInputManager.aidl
similarity index 89%
rename from core/java/android/tv/ITvInputManager.aidl
rename to media/java/android/media/tv/ITvInputManager.aidl
index c6f8d79..6db5a18 100644
--- a/core/java/android/tv/ITvInputManager.aidl
+++ b/media/java/android/media/tv/ITvInputManager.aidl
@@ -14,16 +14,16 @@
  * limitations under the License.
  */
 
-package android.tv;
+package android.media.tv;
 
 import android.content.ComponentName;
 import android.graphics.Rect;
+import android.media.tv.ITvInputHardware;
+import android.media.tv.ITvInputHardwareCallback;
+import android.media.tv.ITvInputClient;
+import android.media.tv.TvInputHardwareInfo;
+import android.media.tv.TvInputInfo;
 import android.net.Uri;
-import android.tv.ITvInputHardware;
-import android.tv.ITvInputHardwareCallback;
-import android.tv.ITvInputClient;
-import android.tv.TvInputHardwareInfo;
-import android.tv.TvInputInfo;
 import android.view.Surface;
 
 /**
diff --git a/core/java/android/tv/ITvInputService.aidl b/media/java/android/media/tv/ITvInputService.aidl
similarity index 88%
rename from core/java/android/tv/ITvInputService.aidl
rename to media/java/android/media/tv/ITvInputService.aidl
index 4f1bc2b..992e424 100644
--- a/core/java/android/tv/ITvInputService.aidl
+++ b/media/java/android/media/tv/ITvInputService.aidl
@@ -14,10 +14,10 @@
  * limitations under the License.
  */
 
-package android.tv;
+package android.media.tv;
 
-import android.tv.ITvInputServiceCallback;
-import android.tv.ITvInputSessionCallback;
+import android.media.tv.ITvInputServiceCallback;
+import android.media.tv.ITvInputSessionCallback;
 import android.view.InputChannel;
 
 /**
diff --git a/core/java/android/tv/ITvInputServiceCallback.aidl b/media/java/android/media/tv/ITvInputServiceCallback.aidl
similarity index 96%
rename from core/java/android/tv/ITvInputServiceCallback.aidl
rename to media/java/android/media/tv/ITvInputServiceCallback.aidl
index 71fc780..c9484dd 100644
--- a/core/java/android/tv/ITvInputServiceCallback.aidl
+++ b/media/java/android/media/tv/ITvInputServiceCallback.aidl
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package android.tv;
+package android.media.tv;
 
 import android.content.ComponentName;
 
diff --git a/core/java/android/tv/ITvInputSession.aidl b/media/java/android/media/tv/ITvInputSession.aidl
similarity index 97%
rename from core/java/android/tv/ITvInputSession.aidl
rename to media/java/android/media/tv/ITvInputSession.aidl
index 32fee4b..fb2e251 100644
--- a/core/java/android/tv/ITvInputSession.aidl
+++ b/media/java/android/media/tv/ITvInputSession.aidl
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package android.tv;
+package android.media.tv;
 
 import android.graphics.Rect;
 import android.net.Uri;
diff --git a/core/java/android/tv/ITvInputSessionCallback.aidl b/media/java/android/media/tv/ITvInputSessionCallback.aidl
similarity index 80%
rename from core/java/android/tv/ITvInputSessionCallback.aidl
rename to media/java/android/media/tv/ITvInputSessionCallback.aidl
index a2bd0d7..71f2d07 100644
--- a/core/java/android/tv/ITvInputSessionCallback.aidl
+++ b/media/java/android/media/tv/ITvInputSessionCallback.aidl
@@ -14,9 +14,10 @@
  * limitations under the License.
  */
 
-package android.tv;
+package android.media.tv;
 
-import android.tv.ITvInputSession;
+import android.media.tv.ITvInputSession;
+import android.os.Bundle;
 
 /**
  * Helper interface for ITvInputSession to allow the TV input to notify the system service when a
@@ -25,4 +26,6 @@
  */
 oneway interface ITvInputSessionCallback {
     void onSessionCreated(ITvInputSession session);
+    void onSessionEvent(in String name, in Bundle args);
+    void onVideoSizeChanged(int width, int height);
 }
diff --git a/core/java/android/tv/ITvInputSessionWrapper.java b/media/java/android/media/tv/ITvInputSessionWrapper.java
similarity index 96%
rename from core/java/android/tv/ITvInputSessionWrapper.java
rename to media/java/android/media/tv/ITvInputSessionWrapper.java
index 3ccccf3..975e391 100644
--- a/core/java/android/tv/ITvInputSessionWrapper.java
+++ b/media/java/android/media/tv/ITvInputSessionWrapper.java
@@ -14,22 +14,20 @@
  * limitations under the License.
  */
 
-package android.tv;
+package android.media.tv;
 
 import android.content.Context;
 import android.graphics.Rect;
+import android.media.tv.TvInputManager.Session;
+import android.media.tv.TvInputService.TvInputSessionImpl;
 import android.net.Uri;
 import android.os.IBinder;
 import android.os.Looper;
 import android.os.Message;
-import android.tv.TvInputManager.Session;
-import android.tv.TvInputService.TvInputSessionImpl;
 import android.util.Log;
 import android.view.InputChannel;
 import android.view.InputEvent;
 import android.view.InputEventReceiver;
-import android.view.KeyEvent;
-import android.view.MotionEvent;
 import android.view.Surface;
 
 import com.android.internal.os.HandlerCaller;
diff --git a/core/java/android/provider/TvContract.java b/media/java/android/media/tv/TvContract.java
similarity index 82%
rename from core/java/android/provider/TvContract.java
rename to media/java/android/media/tv/TvContract.java
index e4f93a8..6e0586e 100644
--- a/core/java/android/provider/TvContract.java
+++ b/media/java/android/media/tv/TvContract.java
@@ -14,13 +14,13 @@
  * limitations under the License.
  */
 
-package android.provider;
+package android.media.tv;
 
 import android.content.ComponentName;
 import android.content.ContentResolver;
 import android.content.ContentUris;
 import android.net.Uri;
-import android.tv.TvInputService;
+import android.provider.BaseColumns;
 
 import java.util.List;
 
@@ -45,7 +45,7 @@
  */
 public final class TvContract {
     /** The authority for the TV provider. */
-    public static final String AUTHORITY = "com.android.tv";
+    public static final String AUTHORITY = "android.media.tv";
 
     private static final String PATH_CHANNEL = "channel";
     private static final String PATH_PROGRAM = "program";
@@ -74,7 +74,7 @@
      *
      * @hide
      */
-    public static final String PARAM_BROWSABLE_ONLY = "browable_only";
+    public static final String PARAM_BROWSABLE_ONLY = "browsable_only";
 
     /**
      * Builds a URI that points to a specific channel.
@@ -88,8 +88,8 @@
     /**
      * Builds a URI that points to all browsable channels from a given TV input.
      *
-     * @param name {@link ComponentName} of the {@link android.tv.TvInputService} that implements
-     *            the given TV input.
+     * @param name {@link ComponentName} of the {@link android.media.tv.TvInputService} that
+     *            implements the given TV input.
      */
     public static final Uri buildChannelsUriForInput(ComponentName name) {
         return buildChannelsUriForInput(name, true);
@@ -98,8 +98,8 @@
     /**
      * Builds a URI that points to all or browsable-only channels from a given TV input.
      *
-     * @param name {@link ComponentName} of the {@link android.tv.TvInputService} that implements
-     *            the given TV input.
+     * @param name {@link ComponentName} of the {@link android.media.tv.TvInputService} that
+     *            implements the given TV input.
      * @param browsableOnly If set to {@code true} the URI points to only browsable channels. If set
      *            to {@code false} the URI points to all channels regardless of whether they are
      *            browsable or not.
@@ -243,12 +243,10 @@
                 + PATH_CHANNEL);
 
         /** The MIME type of a directory of TV channels. */
-        public static final String CONTENT_TYPE =
-                "vnd.android.cursor.dir/vnd.com.android.tv.channels";
+        public static final String CONTENT_TYPE = "vnd.android.cursor.dir/channel";
 
         /** The MIME type of a single TV channel. */
-        public static final String CONTENT_ITEM_TYPE =
-                "vnd.android.cursor.item/vnd.com.android.tv.channels";
+        public static final String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/channel";
 
         /** A generic channel type. */
         public static final int TYPE_OTHER = 0x0;
@@ -319,11 +317,11 @@
         /** A generic service type. */
         public static final int SERVICE_TYPE_OTHER = 0x0;
 
-        /** The service type for regular TV channels. */
-        public static final int SERVICE_TYPE_TV = 0x1;
+        /** The service type for regular TV channels that have both audio and video. */
+        public static final int SERVICE_TYPE_AUDIO_VIDEO = 0x1;
 
-        /** The service type for radio channels. */
-        public static final int SERVICE_TYPE_RADIO = 0x2;
+        /** The service type for radio channels that have audio only. */
+        public static final int SERVICE_TYPE_AUDIO = 0x2;
 
         /**
          * The name of the {@link TvInputService} subclass that provides this TV channel. This
@@ -363,7 +361,7 @@
          * a radio-like channel. Use the same coding for {@code service_type} in the underlying
          * broadcast standard if it is defined there (e.g. ATSC A/53, ETSI EN 300 468 and ARIB
          * STD-B10). Otherwise use one of the followings: {@link #SERVICE_TYPE_OTHER},
-         * {@link #SERVICE_TYPE_TV}, {@link #SERVICE_TYPE_RADIO}
+         * {@link #SERVICE_TYPE_AUDIO_VIDEO}, {@link #SERVICE_TYPE_AUDIO}
          * </p><p>
          * This is a required field.
          * </p><p>
@@ -376,7 +374,7 @@
          * The original network ID of this TV channel.
          * <p>
          * This is used to identify the originating delivery system, if applicable. Use the same
-         * coding for {@code origianal_network_id} in the underlying broadcast standard if it is
+         * coding for {@code original_network_id} in the underlying broadcast standard if it is
          * defined there (e.g. ETSI EN 300 468/TR 101 211 and ARIB STD-B10). If channels cannot be
          * globally identified by 2-tuple {{@link #COLUMN_TRANSPORT_STREAM_ID},
          * {@link #COLUMN_SERVICE_ID}}, one must carefully assign a value to this field to form a
@@ -496,17 +494,20 @@
          * </p><p>
          * Type: INTEGER (boolean)
          * </p>
+         * @hide
          */
         public static final String COLUMN_LOCKED = "locked";
 
         /**
-         * Generic data used by individual TV input services.
+         * Internal data used by individual TV input services.
          * <p>
+         * This is internal to the provider that inserted it, and should not be decoded by other
+         * apps.
+         * </p><p>
          * Type: BLOB
          * </p>
          */
-        public static final String COLUMN_DATA = "data";
-
+        public static final String COLUMN_INTERNAL_PROVIDER_DATA = "internal_provider_data";
 
         /**
          * The version number of this row entry used by TV input services.
@@ -532,12 +533,10 @@
                 + PATH_PROGRAM);
 
         /** The MIME type of a directory of TV programs. */
-        public static final String CONTENT_TYPE =
-                "vnd.android.cursor.dir/vnd.com.android.tv.programs";
+        public static final String CONTENT_TYPE = "vnd.android.cursor.dir/program";
 
         /** The MIME type of a single TV program. */
-        public static final String CONTENT_ITEM_TYPE =
-                "vnd.android.cursor.item/vnd.com.android.tv.programs";
+        public static final String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/program";
 
         /**
          * The ID of the TV channel that contains this TV program.
@@ -578,44 +577,42 @@
          * <p>
          * Use the same language appeared in the underlying broadcast standard, if applicable. (For
          * example, one can refer to the genre strings used in Genre Descriptor of ATSC A/65 or
-         * Content Descriptor of ETSI EN 300 468, if appropriate.) Otherwise, use one of the
-         * following genres:
-         * <ul>
-         *     <li>Family/Kids</li>
-         *     <li>Sports</li>
-         *     <li>Shopping</li>
-         *     <li>Movies</li>
-         *     <li>Comedy</li>
-         *     <li>Travel</li>
-         *     <li>Drama</li>
-         *     <li>Education</li>
-         *     <li>Animal/Wildlife</li>
-         *     <li>News</li>
-         *     <li>Gaming</li>
-         *     <li>Others</li>
-         * </ul>
+         * Content Descriptor of ETSI EN 300 468, if appropriate.) Otherwise, leave empty.
          * </p><p>
          * Type: TEXT
          * </p>
          */
-        public static final String COLUMN_GENRE = "genre";
+        public static final String COLUMN_BROADCAST_GENRE = "broadcast_genre";
 
         /**
-         * The description of this TV program that is displayed to the user by default.
+         * The comma-separated canonical genre string of this TV program.
          * <p>
-         * The maximum length of this field is 256 characters.
+         * Canonical genres are defined in {@link Genres}. Use {@link Genres#encode Genres.encode()}
+         * to create a text that can be stored in this column. Use {@link Genres#decode
+         * Genres.decode()} to get the canonical genre strings from the text stored in this column.
+         * </p><p>
+         * Type: TEXT
+         * </p>
+         * @see Genres
+         */
+        public static final String COLUMN_CANONICAL_GENRE = "canonical_genre";
+
+        /**
+         * The short description of this TV program that is displayed to the user by default.
+         * <p>
+         * It is recommended to limit the length of the descriptions to 256 characters.
          * </p><p>
          * Type: TEXT
          * </p>
          */
-        public static final String COLUMN_DESCRIPTION = "description";
+        public static final String COLUMN_SHORT_DESCRIPTION = "short_description";
 
         /**
          * The detailed, lengthy description of this TV program that is displayed only when the user
          * wants to see more information.
          * <p>
-         * TV input services should leave this field empty if they have no additional
-         * details beyond {@link #COLUMN_DESCRIPTION}.
+         * TV input services should leave this field empty if they have no additional details beyond
+         * {@link #COLUMN_SHORT_DESCRIPTION}.
          * </p><p>
          * Type: TEXT
          * </p>
@@ -634,12 +631,15 @@
         public static final String COLUMN_AUDIO_LANGUAGE = "audio_language";
 
         /**
-         * Generic data used by TV input services.
+         * Internal data used by individual TV input services.
          * <p>
+         * This is internal to the provider that inserted it, and should not be decoded by other
+         * apps.
+         * </p><p>
          * Type: BLOB
          * </p>
          */
-        public static final String COLUMN_DATA = "data";
+        public static final String COLUMN_INTERNAL_PROVIDER_DATA = "internal_provider_data";
 
         /**
          * The version number of this row entry used by TV input services.
@@ -655,6 +655,72 @@
         public static final String COLUMN_VERSION_NUMBER = "version_number";
 
         private Programs() {}
+
+        /** Canonical genres for TV programs. */
+        public static final class Genres {
+            /** The genre for Family/Kids. */
+            public static final String FAMILY_KIDS = "Family/Kids";
+
+            /** The genre for Sports. */
+            public static final String SPORTS = "Sports";
+
+            /** The genre for Shopping. */
+            public static final String SHOPPING = "Shopping";
+
+            /** The genre for Movies. */
+            public static final String MOVIES = "Movies";
+
+            /** The genre for Comedy. */
+            public static final String COMEDY = "Comedy";
+
+            /** The genre for Travel. */
+            public static final String TRAVEL = "Travel";
+
+            /** The genre for Drama. */
+            public static final String DRAMA = "Drama";
+
+            /** The genre for Education. */
+            public static final String EDUCATION = "Education";
+
+            /** The genre for Animal/Wildlife. */
+            public static final String ANIMAL_WILDLIFE = "Animal/Wildlife";
+
+            /** The genre for News. */
+            public static final String NEWS = "News";
+
+            /** The genre for Gaming. */
+            public static final String GAMING = "Gaming";
+
+            private Genres() {}
+
+            /**
+             * Encodes canonical genre strings to a text that can be put into the database.
+             *
+             * @param genres Canonical genre strings. Use the strings defined in this class.
+             * @return an encoded genre string that can be inserted into the
+             *         {@link #COLUMN_CANONICAL_GENRE} column.
+             */
+            public static String encode(String... genres) {
+                StringBuilder sb = new StringBuilder();
+                String separator = "";
+                for (String genre : genres) {
+                    sb.append(separator).append(genre);
+                    separator = ",";
+                }
+                return sb.toString();
+            }
+
+            /**
+             * Decodes the canonical genre strings from the text stored in the database.
+             *
+             * @param genres The encoded genre string retrieved from the
+             *            {@link #COLUMN_CANONICAL_GENRE} column.
+             * @return canonical genre strings.
+             */
+            public static String[] decode(String genres) {
+                return genres.split("\\s*,\\s*");
+            }
+        }
     }
 
     /**
@@ -670,12 +736,10 @@
                 Uri.parse("content://" + AUTHORITY + "/watched_program");
 
         /** The MIME type of a directory of watched programs. */
-        public static final String CONTENT_TYPE =
-                "vnd.android.cursor.dir/vnd.com.android.tv.watched_programs";
+        public static final String CONTENT_TYPE = "vnd.android.cursor.dir/watched_program";
 
         /** The MIME type of a single item in this table. */
-        public static final String CONTENT_ITEM_TYPE =
-                "vnd.android.cursor.item/vnd.com.android.tv.watched_programs";
+        public static final String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/watched_program";
 
         /**
          * The UTC time that the user started watching this TV program, in milliseconds since the
diff --git a/core/java/android/tv/TvInputHardwareInfo.aidl b/media/java/android/media/tv/TvInputHardwareInfo.aidl
similarity index 95%
rename from core/java/android/tv/TvInputHardwareInfo.aidl
rename to media/java/android/media/tv/TvInputHardwareInfo.aidl
index 484ab60..a4c38bb 100644
--- a/core/java/android/tv/TvInputHardwareInfo.aidl
+++ b/media/java/android/media/tv/TvInputHardwareInfo.aidl
@@ -15,6 +15,6 @@
  * limitations under the License.
  */
 
-package android.tv;
+package android.media.tv;
 
 parcelable TvInputHardwareInfo;
diff --git a/core/java/android/tv/TvInputHardwareInfo.java b/media/java/android/media/tv/TvInputHardwareInfo.java
similarity index 98%
rename from core/java/android/tv/TvInputHardwareInfo.java
rename to media/java/android/media/tv/TvInputHardwareInfo.java
index b0dc58e..4beb960 100644
--- a/core/java/android/tv/TvInputHardwareInfo.java
+++ b/media/java/android/media/tv/TvInputHardwareInfo.java
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package android.tv;
+package android.media.tv;
 
 import android.os.Parcel;
 import android.os.Parcelable;
diff --git a/core/java/android/tv/TvInputInfo.aidl b/media/java/android/media/tv/TvInputInfo.aidl
similarity index 95%
rename from core/java/android/tv/TvInputInfo.aidl
rename to media/java/android/media/tv/TvInputInfo.aidl
index abc4b47..ba139a2 100644
--- a/core/java/android/tv/TvInputInfo.aidl
+++ b/media/java/android/media/tv/TvInputInfo.aidl
@@ -14,6 +14,6 @@
  * limitations under the License.
  */
 
-package android.tv;
+package android.media.tv;
 
 parcelable TvInputInfo;
diff --git a/media/java/android/media/tv/TvInputInfo.java b/media/java/android/media/tv/TvInputInfo.java
new file mode 100644
index 0000000..ed599ed
--- /dev/null
+++ b/media/java/android/media/tv/TvInputInfo.java
@@ -0,0 +1,280 @@
+/*
+ * 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.media.tv;
+
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.content.pm.PackageManager.NameNotFoundException;
+import android.content.pm.ResolveInfo;
+import android.content.pm.ServiceInfo;
+import android.content.res.Resources;
+import android.content.res.TypedArray;
+import android.content.res.XmlResourceParser;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.text.TextUtils;
+import android.util.AttributeSet;
+import android.util.Log;
+import android.util.Xml;
+
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+
+import java.io.IOException;
+
+/**
+ * This class is used to specify meta information of a TV input.
+ */
+public final class TvInputInfo implements Parcelable {
+    private static final boolean DEBUG = false;
+    private static final String TAG = "TvInputInfo";
+
+    /**
+     * The name of the TV input service to provide to the setup activity and settings activity.
+     */
+    public static final String EXTRA_SERVICE_NAME = "serviceName";
+
+    private static final String XML_START_TAG_NAME = "tv-input";
+
+    private final ResolveInfo mService;
+    private final String mId;
+
+    // Attributes from XML meta data.
+    private String mSetupActivity;
+    private String mSettingsActivity;
+
+    /**
+     * Create a new instance of the TvInputInfo class,
+     * instantiating it from the given Context and ResolveInfo.
+     *
+     * @param service The ResolveInfo returned from the package manager about this TV input service.
+     * @hide */
+    public static TvInputInfo createTvInputInfo(Context context, ResolveInfo service)
+            throws XmlPullParserException, IOException {
+        ServiceInfo si = service.serviceInfo;
+        PackageManager pm = context.getPackageManager();
+        XmlResourceParser parser = null;
+        try {
+            parser = si.loadXmlMetaData(pm, TvInputService.SERVICE_META_DATA);
+            if (parser == null) {
+                throw new XmlPullParserException("No " + TvInputService.SERVICE_META_DATA
+                        + " meta-data for " + si.name);
+            }
+
+            Resources res = pm.getResourcesForApplication(si.applicationInfo);
+            AttributeSet attrs = Xml.asAttributeSet(parser);
+
+            int type;
+            while ((type=parser.next()) != XmlPullParser.END_DOCUMENT
+                    && type != XmlPullParser.START_TAG) {
+            }
+
+            String nodeName = parser.getName();
+            if (!XML_START_TAG_NAME.equals(nodeName)) {
+                throw new XmlPullParserException(
+                        "Meta-data does not start with tv-input-service tag in " + si.name);
+            }
+
+            TvInputInfo input = new TvInputInfo(context, service);
+            TypedArray sa = res.obtainAttributes(attrs,
+                    com.android.internal.R.styleable.TvInputService);
+            input.mSetupActivity = sa.getString(
+                    com.android.internal.R.styleable.TvInputService_setupActivity);
+            if (DEBUG) {
+                Log.d(TAG, "Setup activity loaded. [" + input.mSetupActivity + "] for " + si.name);
+            }
+            input.mSettingsActivity = sa.getString(
+                    com.android.internal.R.styleable.TvInputService_settingsActivity);
+            if (DEBUG) {
+                Log.d(TAG, "Settings activity loaded. [" + input.mSettingsActivity + "] for "
+                        + si.name);
+            }
+            sa.recycle();
+
+            return input;
+        } catch (NameNotFoundException e) {
+            throw new XmlPullParserException("Unable to create context for: " + si.packageName);
+        } finally {
+            if (parser != null) {
+                parser.close();
+            }
+        }
+    }
+
+    /**
+     * Constructor.
+     *
+     * @param service The ResolveInfo returned from the package manager about this TV input service.
+     * @hide
+     */
+    private TvInputInfo(Context context, ResolveInfo service) {
+        mService = service;
+        ServiceInfo si = service.serviceInfo;
+        mId = generateInputIdForComponentName(new ComponentName(si.packageName, si.name));
+    }
+
+    /**
+     * Returns a unique ID for this TV input. The ID is generated from the package and class name
+     * implementing the TV input service.
+     */
+    public String getId() {
+        return mId;
+    }
+
+    /**
+     * Returns the .apk package that implements this TV input service.
+     */
+    public String getPackageName() {
+        return mService.serviceInfo.packageName;
+    }
+
+    /**
+     * Returns the class name of the service component that implements this TV input service.
+     */
+    public String getServiceName() {
+        return mService.serviceInfo.name;
+    }
+
+    /**
+     * Returns the component of the service that implements this TV input.
+     */
+    public ComponentName getComponent() {
+        return new ComponentName(mService.serviceInfo.packageName, mService.serviceInfo.name);
+    }
+
+    /**
+     * Returns an intent to start the setup activity for this TV input service.
+     */
+    public Intent getIntentForSetupActivity() {
+        if (!TextUtils.isEmpty(mSetupActivity)) {
+            Intent intent = new Intent(Intent.ACTION_MAIN);
+            intent.setClassName(getPackageName(), mSetupActivity);
+            intent.putExtra(EXTRA_SERVICE_NAME, getServiceName());
+            return intent;
+        }
+        return null;
+    }
+
+    /**
+     * Returns an intent to start the settings activity for this TV input service.
+     */
+    public Intent getIntentForSettingsActivity() {
+        if (!TextUtils.isEmpty(mSettingsActivity)) {
+            Intent intent = new Intent(Intent.ACTION_MAIN);
+            intent.setClassName(getPackageName(), mSettingsActivity);
+            intent.putExtra(EXTRA_SERVICE_NAME, getServiceName());
+            return intent;
+        }
+        return null;
+    }
+
+    /**
+     * Loads the user-displayed label for this TV input service.
+     *
+     * @param pm Supplies a PackageManager used to load the TV input's resources.
+     * @return a CharSequence containing the TV input's label. If the TV input does not have
+     *         a label, its name is returned.
+     */
+    public CharSequence loadLabel(PackageManager pm) {
+        return mService.loadLabel(pm);
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public int hashCode() {
+        return mId.hashCode();
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (o == this) {
+            return true;
+        }
+
+        if (!(o instanceof TvInputInfo)) {
+            return false;
+        }
+
+        TvInputInfo obj = (TvInputInfo) o;
+        return mId.equals(obj.mId)
+                && mService.serviceInfo.packageName.equals(obj.mService.serviceInfo.packageName)
+                && mService.serviceInfo.name.equals(obj.mService.serviceInfo.name);
+    }
+
+    @Override
+    public String toString() {
+        return "TvInputInfo{id=" + mId
+                + ", pkg=" + mService.serviceInfo.packageName
+                + ", service=" + mService.serviceInfo.name + "}";
+    }
+
+    /**
+     * Used to package this object into a {@link Parcel}.
+     *
+     * @param dest The {@link Parcel} to be written.
+     * @param flags The flags used for parceling.
+     */
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        dest.writeString(mId);
+        mService.writeToParcel(dest, flags);
+        dest.writeString(mSetupActivity);
+        dest.writeString(mSettingsActivity);
+    }
+
+    /**
+     * Used to generate an input id from a ComponentName.
+     *
+     * @param name the component name for generating an input id.
+     * @return the generated input id for the given {@code name}.
+     * @hide
+     */
+    public static final String generateInputIdForComponentName(ComponentName name) {
+        return name.flattenToShortString();
+    }
+
+    /**
+     * Used to make this class parcelable.
+     *
+     * @hide
+     */
+    public static final Parcelable.Creator<TvInputInfo> CREATOR =
+            new Parcelable.Creator<TvInputInfo>() {
+        @Override
+        public TvInputInfo createFromParcel(Parcel in) {
+            return new TvInputInfo(in);
+        }
+
+        @Override
+        public TvInputInfo[] newArray(int size) {
+            return new TvInputInfo[size];
+        }
+    };
+
+    private TvInputInfo(Parcel in) {
+        mId = in.readString();
+        mService = ResolveInfo.CREATOR.createFromParcel(in);
+        mSetupActivity = in.readString();
+        mSettingsActivity = in.readString();
+    }
+}
diff --git a/core/java/android/tv/TvInputManager.java b/media/java/android/media/tv/TvInputManager.java
similarity index 91%
rename from core/java/android/tv/TvInputManager.java
rename to media/java/android/media/tv/TvInputManager.java
index dfa84f8..1335a1b 100644
--- a/core/java/android/tv/TvInputManager.java
+++ b/media/java/android/media/tv/TvInputManager.java
@@ -14,10 +14,11 @@
  * limitations under the License.
  */
 
-package android.tv;
+package android.media.tv;
 
 import android.graphics.Rect;
 import android.net.Uri;
+import android.os.Bundle;
 import android.os.Handler;
 import android.os.IBinder;
 import android.os.Looper;
@@ -85,6 +86,29 @@
          */
         public void onSessionReleased(Session session) {
         }
+
+        /**
+         * This is called at the beginning of the playback of a channel and later when the size of
+         * the video has been changed.
+         *
+         * @param session A {@link TvInputManager.Session} associated with this callback
+         * @param width the width of the video
+         * @param height the height of the video
+         * @hide
+         */
+        public void onVideoSizeChanged(Session session, int width, int height) {
+        }
+
+        /**
+         * This is called when a custom event has been sent from this session.
+         *
+         * @param session A {@link TvInputManager.Session} associated with this callback
+         * @param eventType The type of the event.
+         * @param eventArgs Optional arguments of the event.
+         * @hide
+         */
+        public void onSessionEvent(Session session, String eventType, Bundle eventArgs) {
+        }
     }
 
     private static final class SessionCallbackRecord {
@@ -116,6 +140,24 @@
                 }
             });
         }
+
+        public void postVideoSizeChanged(final int width, final int height) {
+            mHandler.post(new Runnable() {
+                @Override
+                public void run() {
+                    mSessionCallback.onVideoSizeChanged(mSession, width, height);
+                }
+            });
+        }
+
+        public void postSessionEvent(final String eventType, final Bundle eventArgs) {
+            mHandler.post(new Runnable() {
+                @Override
+                public void run() {
+                    mSessionCallback.onSessionEvent(mSession, eventType, eventArgs);
+                }
+            });
+        }
     }
 
     /**
@@ -196,6 +238,30 @@
             }
 
             @Override
+            public void onVideoSizeChanged(int width, int height, int seq) {
+                synchronized (mSessionCallbackRecordMap) {
+                    SessionCallbackRecord record = mSessionCallbackRecordMap.get(seq);
+                    if (record == null) {
+                        Log.e(TAG, "Callback not found for seq " + seq);
+                        return;
+                    }
+                    record.postVideoSizeChanged(width, height);
+                }
+            }
+
+            @Override
+            public void onSessionEvent(String eventType, Bundle eventArgs, int seq) {
+                synchronized (mSessionCallbackRecordMap) {
+                    SessionCallbackRecord record = mSessionCallbackRecordMap.get(seq);
+                    if (record == null) {
+                        Log.e(TAG, "Callback not found for seq " + seq);
+                        return;
+                    }
+                    record.postSessionEvent(eventType, eventArgs);
+                }
+            }
+
+            @Override
             public void onAvailabilityChanged(String inputId, boolean isAvailable) {
                 synchronized (mTvInputListenerRecordsMap) {
                     List<TvInputListenerRecord> records = mTvInputListenerRecordsMap.get(inputId);
diff --git a/core/java/android/tv/TvInputService.java b/media/java/android/media/tv/TvInputService.java
similarity index 87%
rename from core/java/android/tv/TvInputService.java
rename to media/java/android/media/tv/TvInputService.java
index eeb738d..3213019 100644
--- a/core/java/android/tv/TvInputService.java
+++ b/media/java/android/media/tv/TvInputService.java
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package android.tv;
+package android.media.tv;
 
 import android.app.Service;
 import android.content.ComponentName;
@@ -22,13 +22,15 @@
 import android.content.Intent;
 import android.graphics.PixelFormat;
 import android.graphics.Rect;
+import android.media.tv.ITvInputService;
+import android.media.tv.TvInputManager.Session;
 import android.net.Uri;
+import android.os.Bundle;
 import android.os.Handler;
 import android.os.IBinder;
 import android.os.Message;
 import android.os.RemoteCallbackList;
 import android.os.RemoteException;
-import android.tv.TvInputManager.Session;
 import android.util.Log;
 import android.view.Gravity;
 import android.view.InputChannel;
@@ -58,7 +60,15 @@
      * must also require the {@link android.Manifest.permission#BIND_TV_INPUT} permission so that
      * other applications cannot abuse it.
      */
-    public static final String SERVICE_INTERFACE = "android.tv.TvInputService";
+    public static final String SERVICE_INTERFACE = "android.media.tv.TvInputService";
+
+    /**
+     * Name under which a TvInputService component publishes information about itself.
+     * This meta-data must reference an XML resource containing an
+     * <code>&lt;{@link android.R.styleable#TvInputService tv-input}&gt;</code>
+     * tag.
+     */
+    public static final String SERVICE_META_DATA = "android.media.tv.input";
 
     private String mId;
     private final Handler mHandler = new ServiceHandler();
@@ -69,7 +79,7 @@
     @Override
     public void onCreate() {
         super.onCreate();
-        mId = TvInputInfo.generateInputIdForComponenetName(
+        mId = TvInputInfo.generateInputIdForComponentName(
                 new ComponentName(getPackageName(), getClass().getName()));
     }
 
@@ -156,6 +166,7 @@
         private boolean mOverlayViewEnabled;
         private IBinder mWindowToken;
         private Rect mOverlayFrame;
+        private ITvInputSessionCallback mSessionCallback;
 
         public TvInputSessionImpl() {
             mWindowManager = (WindowManager) getSystemService(Context.WINDOW_SERVICE);
@@ -188,6 +199,52 @@
         }
 
         /**
+         * Dispatches an event to the application using this session.
+         *
+         * @param eventType The type of the event.
+         * @param eventArgs Optional arguments of the event.
+         * @hide
+         */
+        public void dispatchSessionEvent(final String eventType, final Bundle eventArgs) {
+            if (eventType == null) {
+                throw new IllegalArgumentException("eventType should not be null.");
+            }
+            mHandler.post(new Runnable() {
+                @Override
+                public void run() {
+                    try {
+                        if (DEBUG) Log.d(TAG, "dispatchSessionEvent(" + eventType + ")");
+                        mSessionCallback.onSessionEvent(eventType, eventArgs);
+                    } catch (RemoteException e) {
+                        Log.w(TAG, "error in sending event (event=" + eventType + ")");
+                    }
+                }
+            });
+        }
+
+        /**
+         * Sends the change on the size of the video. This is expected to be called at the
+         * beginning of the playback and later when the size has been changed.
+         *
+         * @param width The width of the video.
+         * @param height The height of the video.
+         * @hide
+         */
+        public void dispatchVideoSizeChanged(final int width, final int height) {
+            mHandler.post(new Runnable() {
+                @Override
+                public void run() {
+                    try {
+                        if (DEBUG) Log.d(TAG, "dispatchVideoSizeChanged");
+                        mSessionCallback.onVideoSizeChanged(width, height);
+                    } catch (RemoteException e) {
+                        Log.w(TAG, "error in dispatchVideoSizeChanged");
+                    }
+                }
+            });
+        }
+
+        /**
          * Called when the session is released.
          */
         public abstract void onRelease();
@@ -394,9 +451,7 @@
                 mWindowManager.removeView(mOverlayView);
                 mOverlayView = null;
             }
-            if (DEBUG) {
-                Log.d(TAG, "create overlay view(" + frame + ")");
-            }
+            if (DEBUG) Log.d(TAG, "create overlay view(" + frame + ")");
             mWindowToken = windowToken;
             mOverlayFrame = frame;
             if (!mOverlayViewEnabled) {
@@ -431,9 +486,7 @@
          * @param frame A new position of the overlay view.
          */
         void relayoutOverlayView(Rect frame) {
-            if (DEBUG) {
-                Log.d(TAG, "relayout overlay view(" + frame + ")");
-            }
+            if (DEBUG) Log.d(TAG, "relayoutOverlayView(" + frame + ")");
             mOverlayFrame = frame;
             if (!mOverlayViewEnabled || mOverlayView == null) {
                 return;
@@ -449,9 +502,7 @@
          * Removes the current overlay view.
          */
         void removeOverlayView(boolean clearWindowToken) {
-            if (DEBUG) {
-                Log.d(TAG, "remove overlay view(" + mOverlayView + ")");
-            }
+            if (DEBUG) Log.d(TAG, "removeOverlayView(" + mOverlayView + ")");
             if (clearWindowToken) {
                 mWindowToken = null;
                 mOverlayFrame = null;
@@ -498,6 +549,10 @@
             mOverlayView.getViewRootImpl().dispatchInputEvent(event, receiver);
             return Session.DISPATCH_IN_PROGRESS;
         }
+
+        private void setSessionCallback(ITvInputSessionCallback callback) {
+            mSessionCallback = callback;
+        }
     }
 
     private final class ServiceHandler extends Handler {
@@ -517,6 +572,7 @@
                             // Failed to create a session.
                             cb.onSessionCreated(null);
                         } else {
+                            sessionImpl.setSessionCallback(cb);
                             ITvInputSession stub = new ITvInputSessionWrapper(TvInputService.this,
                                     sessionImpl, channel);
                             cb.onSessionCreated(stub);
diff --git a/core/java/android/tv/TvStreamConfig.aidl b/media/java/android/media/tv/TvStreamConfig.aidl
similarity index 95%
rename from core/java/android/tv/TvStreamConfig.aidl
rename to media/java/android/media/tv/TvStreamConfig.aidl
index 4d0add4..569fcc0 100644
--- a/core/java/android/tv/TvStreamConfig.aidl
+++ b/media/java/android/media/tv/TvStreamConfig.aidl
@@ -15,6 +15,6 @@
  * limitations under the License.
  */
 
-package android.tv;
+package android.media.tv;
 
 parcelable TvStreamConfig;
\ No newline at end of file
diff --git a/core/java/android/tv/TvStreamConfig.java b/media/java/android/media/tv/TvStreamConfig.java
similarity index 98%
rename from core/java/android/tv/TvStreamConfig.java
rename to media/java/android/media/tv/TvStreamConfig.java
index 03e63b1..7f0c92f 100644
--- a/core/java/android/tv/TvStreamConfig.java
+++ b/media/java/android/media/tv/TvStreamConfig.java
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package android.tv;
+package android.media.tv;
 
 import android.os.Parcel;
 import android.os.Parcelable;
diff --git a/core/java/android/tv/TvView.java b/media/java/android/media/tv/TvView.java
similarity index 93%
rename from core/java/android/tv/TvView.java
rename to media/java/android/media/tv/TvView.java
index 59b6386..126d739 100644
--- a/core/java/android/tv/TvView.java
+++ b/media/java/android/media/tv/TvView.java
@@ -14,15 +14,16 @@
  * limitations under the License.
  */
 
-package android.tv;
+package android.media.tv;
 
 import android.content.Context;
 import android.graphics.Rect;
+import android.media.tv.TvInputManager.Session;
+import android.media.tv.TvInputManager.Session.FinishedInputEventCallback;
+import android.media.tv.TvInputManager.SessionCallback;
+import android.os.Bundle;
 import android.os.Handler;
 import android.text.TextUtils;
-import android.tv.TvInputManager.Session;
-import android.tv.TvInputManager.Session.FinishedInputEventCallback;
-import android.tv.TvInputManager.SessionCallback;
 import android.util.AttributeSet;
 import android.util.Log;
 import android.view.InputEvent;
@@ -379,5 +380,23 @@
                 mExternalCallback.onSessionReleased(session);
             }
         }
+
+        @Override
+        public void onVideoSizeChanged(Session session, int width, int height) {
+            if (DEBUG) {
+                Log.d(TAG, "onVideoSizeChanged(" + width + ", " + height + ")");
+            }
+            if (mExternalCallback != null) {
+                mExternalCallback.onVideoSizeChanged(session, width, height);
+            }
+        }
+
+        @Override
+        public void onSessionEvent(TvInputManager.Session session, String eventType,
+                Bundle eventArgs) {
+            if (mExternalCallback != null) {
+                mExternalCallback.onSessionEvent(session, eventType, eventArgs);
+            }
+        }
     }
 }
diff --git a/media/jni/android_mtp_MtpDatabase.cpp b/media/jni/android_mtp_MtpDatabase.cpp
index d781336..19b54a6 100644
--- a/media/jni/android_mtp_MtpDatabase.cpp
+++ b/media/jni/android_mtp_MtpDatabase.cpp
@@ -433,16 +433,14 @@
             case MTP_TYPE_STR:
             {
                 jstring stringValue = (jstring)env->GetObjectArrayElement(stringValuesArray, 0);
+                const char* str = (stringValue ? env->GetStringUTFChars(stringValue, NULL) : NULL);
                 if (stringValue) {
-                    const char* str = env->GetStringUTFChars(stringValue, NULL);
-                    if (str == NULL) {
-                        return MTP_RESPONSE_GENERAL_ERROR;
-                    }
                     packet.putString(str);
                     env->ReleaseStringUTFChars(stringValue, str);
                 } else {
                     packet.putEmptyString();
                 }
+                env->DeleteLocalRef(stringValue);
                 break;
              }
             default:
@@ -515,7 +513,7 @@
             break;
          }
         default:
-            ALOGE("unsupported type in getObjectPropertyValue\n");
+            ALOGE("unsupported type in setObjectPropertyValue\n");
             return MTP_RESPONSE_INVALID_OBJECT_PROP_FORMAT;
     }
 
diff --git a/media/lib/signer/Android.mk b/media/lib/signer/Android.mk
index bca643a..b0d3177 100644
--- a/media/lib/signer/Android.mk
+++ b/media/lib/signer/Android.mk
@@ -23,7 +23,8 @@
 LOCAL_MODULE_TAGS := optional
 
 LOCAL_SRC_FILES := \
-            $(call all-java-files-under, java)
+            $(call all-java-files-under, java) \
+            $(call all-aidl-files-under, java)
 
 include $(BUILD_JAVA_LIBRARY)
 
diff --git a/media/lib/signer/com.android.mediadrm.signer.xml b/media/lib/signer/com.android.mediadrm.signer.xml
index b5b1f09..fd3a115 100644
--- a/media/lib/signer/com.android.mediadrm.signer.xml
+++ b/media/lib/signer/com.android.mediadrm.signer.xml
@@ -15,6 +15,6 @@
 -->
 
 <permissions>
-    <library name="com.android.media.drm.signer"
-            file="/system/framework/com.android.media.drm.signer.jar" />
+    <library name="com.android.mediadrm.signer"
+            file="/system/framework/com.android.mediadrm.signer.jar" />
 </permissions>
diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/CameraMetadataTest.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/CameraMetadataTest.java
index a3caba2..ef06d2c 100644
--- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/CameraMetadataTest.java
+++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/CameraMetadataTest.java
@@ -18,6 +18,7 @@
 
 import android.test.suitebuilder.annotation.SmallTest;
 import android.util.Log;
+import android.util.Pair;
 import android.util.Range;
 import android.util.Rational;
 import android.util.SizeF;
@@ -41,6 +42,7 @@
 import android.hardware.camera2.params.StreamConfiguration;
 import android.hardware.camera2.params.StreamConfigurationDuration;
 import android.hardware.camera2.params.StreamConfigurationMap;
+import android.hardware.camera2.params.TonemapCurve;
 import android.hardware.camera2.utils.TypeReference;
 
 import static android.hardware.camera2.impl.CameraMetadataNative.*;
@@ -49,6 +51,7 @@
 import java.lang.reflect.Array;
 import java.nio.ByteBuffer;
 import java.nio.ByteOrder;
+import java.util.Arrays;
 import java.util.List;
 
 /**
@@ -788,6 +791,22 @@
     }
 
     @SmallTest
+    public void testReadWritePair() {
+        // float x 2
+        checkKeyMarshal("android.lens.focusRange",
+                new TypeReference<Pair<Float, Float>>() {{ }},
+                Pair.create(1.0f / 2.0f, 1.0f / 3.0f),
+                toByteArray(1.0f / 2.0f, 1.0f / 3.0f));
+
+        // byte, int (fake from TYPE_BYTE)
+        // This takes advantage of the TYPE_BYTE -> int marshaler designed for enums.
+        checkKeyMarshal("android.flash.mode",
+                new TypeReference<Pair<Byte, Integer>>() {{ }},
+                Pair.create((byte)123, 22),
+                toByteArray((byte)123, (byte)22));
+    }
+
+    @SmallTest
     public void testReadWriteRange() {
         // int32 x 2
         checkKeyMarshal("android.control.aeTargetFpsRange",
@@ -1015,6 +1034,29 @@
             assertNull(resultSimpleFaces[i].getMouthPosition());
         }
 
+        /**
+         * Read/Write TonemapCurve
+         */
+        float[] red = new float[] {0.0f, 0.0f, 1.0f, 1.0f};
+        float[] green = new float[] {0.0f, 1.0f, 1.0f, 0.0f};
+        float[] blue = new float[] {
+                0.0000f, 0.0000f, 0.0667f, 0.2920f, 0.1333f, 0.4002f, 0.2000f, 0.4812f,
+                0.2667f, 0.5484f, 0.3333f, 0.6069f, 0.4000f, 0.6594f, 0.4667f, 0.7072f,
+                0.5333f, 0.7515f, 0.6000f, 0.7928f, 0.6667f, 0.8317f, 0.7333f, 0.8685f,
+                0.8000f, 0.9035f, 0.8667f, 0.9370f, 0.9333f, 0.9691f, 1.0000f, 1.0000f};
+        TonemapCurve tcIn = new TonemapCurve(red, green, blue);
+        mMetadata.set(CaptureResult.TONEMAP_CURVE, tcIn);
+        float[] redOut = mMetadata.get(CaptureResult.TONEMAP_CURVE_RED);
+        float[] greenOut = mMetadata.get(CaptureResult.TONEMAP_CURVE_GREEN);
+        float[] blueOut = mMetadata.get(CaptureResult.TONEMAP_CURVE_BLUE);
+        assertArrayEquals(red, redOut);
+        assertArrayEquals(green, greenOut);
+        assertArrayEquals(blue, blueOut);
+        TonemapCurve tcOut = mMetadata.get(CaptureResult.TONEMAP_CURVE);
+        assertEquals(tcIn, tcOut);
+        mMetadata.set(CaptureResult.TONEMAP_CURVE_GREEN, null);
+        // If any of channel has null curve, return a null TonemapCurve
+        assertNull(mMetadata.get(CaptureResult.TONEMAP_CURVE));
     }
 
     /**
@@ -1166,6 +1208,48 @@
         }
     }
 
+    private <T> void assertKeyValueEquals(T expected, CameraCharacteristics.Key<T> key) {
+        assertKeyValueEquals(expected, key.getNativeKey());
+    }
+
+    private <T> void assertKeyValueEquals(T expected, Key<T> key) {
+        T actual = mMetadata.get(key);
+
+        assertEquals("Expected value for key " + key + " to match", expected, actual);
+    }
+
+    @SmallTest
+    public void testOverrideMaxRegions() {
+        // All keys are null before doing any writes.
+        assertKeyValueEquals(null, CameraCharacteristics.CONTROL_MAX_REGIONS_AE);
+        assertKeyValueEquals(null, CameraCharacteristics.CONTROL_MAX_REGIONS_AWB);
+        assertKeyValueEquals(null, CameraCharacteristics.CONTROL_MAX_REGIONS_AF);
+
+        mMetadata.set(CameraCharacteristics.CONTROL_MAX_REGIONS,
+                new int[] { /*AE*/1, /*AWB*/2, /*AF*/3 });
+
+        // All keys are the expected value after doing a write
+        assertKeyValueEquals(1, CameraCharacteristics.CONTROL_MAX_REGIONS_AE);
+        assertKeyValueEquals(2, CameraCharacteristics.CONTROL_MAX_REGIONS_AWB);
+        assertKeyValueEquals(3, CameraCharacteristics.CONTROL_MAX_REGIONS_AF);
+    }
+
+    @SmallTest
+    public void testOverrideMaxNumOutputStreams() {
+        // All keys are null before doing any writes.
+        assertKeyValueEquals(null, CameraCharacteristics.REQUEST_MAX_NUM_OUTPUT_RAW);
+        assertKeyValueEquals(null, CameraCharacteristics.REQUEST_MAX_NUM_OUTPUT_PROC);
+        assertKeyValueEquals(null, CameraCharacteristics.REQUEST_MAX_NUM_OUTPUT_PROC_STALLING);
+
+        mMetadata.set(CameraCharacteristics.REQUEST_MAX_NUM_OUTPUT_STREAMS,
+                new int[] { /*AE*/1, /*AWB*/2, /*AF*/3 });
+
+        // All keys are the expected value after doing a write
+        assertKeyValueEquals(1, CameraCharacteristics.REQUEST_MAX_NUM_OUTPUT_RAW);
+        assertKeyValueEquals(2, CameraCharacteristics.REQUEST_MAX_NUM_OUTPUT_PROC);
+        assertKeyValueEquals(3, CameraCharacteristics.REQUEST_MAX_NUM_OUTPUT_PROC_STALLING);
+    }
+
     @SmallTest
     public void testCaptureResult() {
         mMetadata.set(CaptureRequest.CONTROL_AE_MODE,
diff --git a/packages/DefaultContainerService/src/com/android/defcontainer/DefaultContainerService.java b/packages/DefaultContainerService/src/com/android/defcontainer/DefaultContainerService.java
index 36c1d5c..ec87c6e 100644
--- a/packages/DefaultContainerService/src/com/android/defcontainer/DefaultContainerService.java
+++ b/packages/DefaultContainerService/src/com/android/defcontainer/DefaultContainerService.java
@@ -88,7 +88,7 @@
     private IMediaContainerService.Stub mBinder = new IMediaContainerService.Stub() {
         /**
          * Creates a new container and copies resource there.
-         * @param paackageURI the uri of resource to be copied. Can be either
+         * @param packageURI the uri of resource to be copied. Can be either
          * a content uri or a file uri
          * @param cid the id of the secure container that should
          * be used for creating a secure container into which the resource
@@ -101,13 +101,13 @@
          */
         public String copyResourceToContainer(final Uri packageURI, final String cid,
                 final String key, final String resFileName, final String publicResFileName,
-                boolean isExternal, boolean isForwardLocked) {
+                boolean isExternal, boolean isForwardLocked, String abiOverride) {
             if (packageURI == null || cid == null) {
                 return null;
             }
 
             return copyResourceInner(packageURI, cid, key, resFileName, publicResFileName,
-                    isExternal, isForwardLocked);
+                    isExternal, isForwardLocked, abiOverride);
         }
 
         /**
@@ -153,13 +153,12 @@
         /**
          * Determine the recommended install location for package
          * specified by file uri location.
-         * @param fileUri the uri of resource to be copied. Should be a
-         * file uri
+         *
          * @return Returns PackageInfoLite object containing
          * the package info and recommended app location.
          */
         public PackageInfoLite getMinimalPackageInfo(final String packagePath, int flags,
-                long threshold) {
+                long threshold, String abiOverride) {
             PackageInfoLite ret = new PackageInfoLite();
 
             if (packagePath == null) {
@@ -191,7 +190,7 @@
             ret.verifiers = pkg.verifiers;
 
             ret.recommendedInstallLocation = recommendAppInstallLocation(pkg.installLocation,
-                    packagePath, flags, threshold);
+                    packagePath, flags, threshold, abiOverride);
 
             return ret;
         }
@@ -208,11 +207,11 @@
         }
 
         @Override
-        public boolean checkExternalFreeStorage(Uri packageUri, boolean isForwardLocked)
-                throws RemoteException {
+        public boolean checkExternalFreeStorage(Uri packageUri, boolean isForwardLocked,
+                String abiOverride) throws RemoteException {
             final File apkFile = new File(packageUri.getPath());
             try {
-                return isUnderExternalThreshold(apkFile, isForwardLocked);
+                return isUnderExternalThreshold(apkFile, isForwardLocked, abiOverride);
             } catch (IOException e) {
                 return true;
             }
@@ -265,11 +264,11 @@
         }
 
         @Override
-        public long calculateInstalledSize(String packagePath, boolean isForwardLocked)
-                throws RemoteException {
+        public long calculateInstalledSize(String packagePath, boolean isForwardLocked,
+                String abiOverride) throws RemoteException {
             final File packageFile = new File(packagePath);
             try {
-                return calculateContainerSize(packageFile, isForwardLocked) * 1024 * 1024;
+                return calculateContainerSize(packageFile, isForwardLocked, abiOverride) * 1024 * 1024;
             } catch (IOException e) {
                 /*
                  * Okay, something failed, so let's just estimate it to be 2x
@@ -328,7 +327,8 @@
     }
 
     private String copyResourceInner(Uri packageURI, String newCid, String key, String resFileName,
-            String publicResFileName, boolean isExternal, boolean isForwardLocked) {
+            String publicResFileName, boolean isExternal, boolean isForwardLocked,
+            String abiOverride) {
 
         if (isExternal) {
             // Make sure the sdcard is mounted.
@@ -343,7 +343,22 @@
         String codePath = packageURI.getPath();
         File codeFile = new File(codePath);
         NativeLibraryHelper.ApkHandle handle = new NativeLibraryHelper.ApkHandle(codePath);
-        final int abi = NativeLibraryHelper.findSupportedAbi(handle, Build.SUPPORTED_ABIS);
+        String[] abiList = Build.SUPPORTED_ABIS;
+        if (abiOverride != null) {
+            abiList = new String[] { abiOverride };
+        } else {
+            try {
+                if (Build.SUPPORTED_64_BIT_ABIS.length > 0 &&
+                        NativeLibraryHelper.hasRenderscriptBitcode(handle)) {
+                    abiList = Build.SUPPORTED_32_BIT_ABIS;
+                }
+            } catch (IOException ioe) {
+                Slog.w(TAG, "Problem determining ABI for: " + codeFile.getPath());
+                return null;
+            }
+        }
+
+        final int abi = NativeLibraryHelper.findSupportedAbi(handle, abiList);
 
         // Calculate size of container needed to hold base APK.
         final int sizeMb;
@@ -414,7 +429,7 @@
             int ret = PackageManager.INSTALL_SUCCEEDED;
             if (abi >= 0) {
                 ret = NativeLibraryHelper.copyNativeBinariesIfNeededLI(handle,
-                        sharedLibraryDir, Build.SUPPORTED_ABIS[abi]);
+                        sharedLibraryDir, abiList[abi]);
             } else if (abi != PackageManager.NO_NATIVE_LIBRARIES) {
                 ret = abi;
             }
@@ -672,7 +687,7 @@
     private static final int PREFER_EXTERNAL = 2;
 
     private int recommendAppInstallLocation(int installLocation, String archiveFilePath, int flags,
-            long threshold) {
+            long threshold, String abiOverride) {
         int prefer;
         boolean checkBoth = false;
 
@@ -741,7 +756,7 @@
         boolean fitsOnSd = false;
         if (!emulated && (checkBoth || prefer == PREFER_EXTERNAL)) {
             try {
-                fitsOnSd = isUnderExternalThreshold(apkFile, isForwardLocked);
+                fitsOnSd = isUnderExternalThreshold(apkFile, isForwardLocked, abiOverride);
             } catch (IOException e) {
                 return PackageHelper.RECOMMEND_FAILED_INVALID_URI;
             }
@@ -812,13 +827,13 @@
      * @return true if file fits
      * @throws IOException when file does not exist
      */
-    private boolean isUnderExternalThreshold(File apkFile, boolean isForwardLocked)
+    private boolean isUnderExternalThreshold(File apkFile, boolean isForwardLocked, String abiOverride)
             throws IOException {
         if (Environment.isExternalStorageEmulated()) {
             return false;
         }
 
-        final int sizeMb = calculateContainerSize(apkFile, isForwardLocked);
+        final int sizeMb = calculateContainerSize(apkFile, isForwardLocked, abiOverride);
 
         final int availSdMb;
         if (Environment.MEDIA_MOUNTED.equals(Environment.getExternalStorageState())) {
@@ -832,9 +847,11 @@
         return availSdMb > sizeMb;
     }
 
-    private int calculateContainerSize(File apkFile, boolean forwardLocked) throws IOException {
+    private int calculateContainerSize(File apkFile, boolean forwardLocked,
+            String abiOverride) throws IOException {
         NativeLibraryHelper.ApkHandle handle = new NativeLibraryHelper.ApkHandle(apkFile);
-        final int abi = NativeLibraryHelper.findSupportedAbi(handle, Build.SUPPORTED_ABIS);
+        final int abi = NativeLibraryHelper.findSupportedAbi(handle,
+                (abiOverride != null) ? new String[] { abiOverride } : Build.SUPPORTED_ABIS);
 
         try {
             return calculateContainerSize(handle, apkFile, abi, forwardLocked);
diff --git a/packages/DocumentsUI/res/values-el/strings.xml b/packages/DocumentsUI/res/values-el/strings.xml
index f0f7e10..24d66ce 100644
--- a/packages/DocumentsUI/res/values-el/strings.xml
+++ b/packages/DocumentsUI/res/values-el/strings.xml
@@ -27,7 +27,7 @@
     <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>
@@ -51,5 +51,5 @@
     <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>
+    <string name="share_via" msgid="8966594246261344259">"Κοινή χρήση μέσω"</string>
 </resources>
diff --git a/packages/DocumentsUI/res/values-fi/strings.xml b/packages/DocumentsUI/res/values-fi/strings.xml
index aa118ed..ae04e32 100644
--- a/packages/DocumentsUI/res/values-fi/strings.xml
+++ b/packages/DocumentsUI/res/values-fi/strings.xml
@@ -17,7 +17,7 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="app_label" msgid="2783841764617238354">"Asiakirjat"</string>
-    <string name="title_open" msgid="4353228937663917801">"Avoinna alkaen"</string>
+    <string name="title_open" msgid="4353228937663917801">"Avaa sijainnista"</string>
     <string name="title_save" msgid="2433679664882857999">"Tallenna kohteeseen"</string>
     <string name="menu_create_dir" msgid="5947289605844398389">"Luo kansio"</string>
     <string name="menu_grid" msgid="6878021334497835259">"Ruudukkonäkymä"</string>
diff --git a/packages/DocumentsUI/res/values-fr/strings.xml b/packages/DocumentsUI/res/values-fr/strings.xml
index 070b130..b85b518 100644
--- a/packages/DocumentsUI/res/values-fr/strings.xml
+++ b/packages/DocumentsUI/res/values-fr/strings.xml
@@ -16,7 +16,7 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="app_label" msgid="2783841764617238354">"Documents"</string>
+    <string name="app_label" msgid="2783841764617238354">"Docs"</string>
     <string name="title_open" msgid="4353228937663917801">"Ouvrir à partir de"</string>
     <string name="title_save" msgid="2433679664882857999">"Enregistrer sous"</string>
     <string name="menu_create_dir" msgid="5947289605844398389">"Créer un dossier"</string>
diff --git a/packages/InputDevices/res/values-af/strings.xml b/packages/InputDevices/res/values-af/strings.xml
index 45d1e2a..d67a9fd 100644
--- a/packages/InputDevices/res/values-af/strings.xml
+++ b/packages/InputDevices/res/values-af/strings.xml
@@ -39,6 +39,5 @@
     <string name="keyboard_layout_hebrew" msgid="7241473985890173812">"Hebreeus"</string>
     <string name="keyboard_layout_lithuanian" msgid="6943110873053106534">"Litaus"</string>
     <string name="keyboard_layout_spanish_latin" msgid="5690539836069535697">"Spaans (Latyn)"</string>
-    <!-- no translation found for keyboard_layout_latvian (4405417142306250595) -->
-    <skip />
+    <string name="keyboard_layout_latvian" msgid="4405417142306250595">"Letties"</string>
 </resources>
diff --git a/packages/InputDevices/res/values-am/strings.xml b/packages/InputDevices/res/values-am/strings.xml
index 3f62df6..3e84794 100644
--- a/packages/InputDevices/res/values-am/strings.xml
+++ b/packages/InputDevices/res/values-am/strings.xml
@@ -39,6 +39,5 @@
     <string name="keyboard_layout_hebrew" msgid="7241473985890173812">"ዕብራስጥ"</string>
     <string name="keyboard_layout_lithuanian" msgid="6943110873053106534">"ሊቱዌኒያኛ"</string>
     <string name="keyboard_layout_spanish_latin" msgid="5690539836069535697">"ስፓኒሽ (ላቲን)"</string>
-    <!-- no translation found for keyboard_layout_latvian (4405417142306250595) -->
-    <skip />
+    <string name="keyboard_layout_latvian" msgid="4405417142306250595">"ላትቪያኛ"</string>
 </resources>
diff --git a/packages/InputDevices/res/values-ar/strings.xml b/packages/InputDevices/res/values-ar/strings.xml
index e70cad4..a922a46 100644
--- a/packages/InputDevices/res/values-ar/strings.xml
+++ b/packages/InputDevices/res/values-ar/strings.xml
@@ -39,6 +39,5 @@
     <string name="keyboard_layout_hebrew" msgid="7241473985890173812">"العبرية"</string>
     <string name="keyboard_layout_lithuanian" msgid="6943110873053106534">"الليتوانية"</string>
     <string name="keyboard_layout_spanish_latin" msgid="5690539836069535697">"الإسبانية (اللاتينية)"</string>
-    <!-- no translation found for keyboard_layout_latvian (4405417142306250595) -->
-    <skip />
+    <string name="keyboard_layout_latvian" msgid="4405417142306250595">"اللاتفية"</string>
 </resources>
diff --git a/packages/InputDevices/res/values-bg/strings.xml b/packages/InputDevices/res/values-bg/strings.xml
index 7582a69..d68a347 100644
--- a/packages/InputDevices/res/values-bg/strings.xml
+++ b/packages/InputDevices/res/values-bg/strings.xml
@@ -39,6 +39,5 @@
     <string name="keyboard_layout_hebrew" msgid="7241473985890173812">"Ивритска клавиатурна подредба"</string>
     <string name="keyboard_layout_lithuanian" msgid="6943110873053106534">"Литовска клавиатурна подредба"</string>
     <string name="keyboard_layout_spanish_latin" msgid="5690539836069535697">"Исп. клав. подредба (Лат. Америка)"</string>
-    <!-- no translation found for keyboard_layout_latvian (4405417142306250595) -->
-    <skip />
+    <string name="keyboard_layout_latvian" msgid="4405417142306250595">"латвийски"</string>
 </resources>
diff --git a/packages/InputDevices/res/values-ca/strings.xml b/packages/InputDevices/res/values-ca/strings.xml
index e38b9a8..6baa5b8 100644
--- a/packages/InputDevices/res/values-ca/strings.xml
+++ b/packages/InputDevices/res/values-ca/strings.xml
@@ -39,6 +39,5 @@
     <string name="keyboard_layout_hebrew" msgid="7241473985890173812">"Hebreu"</string>
     <string name="keyboard_layout_lithuanian" msgid="6943110873053106534">"Lituà"</string>
     <string name="keyboard_layout_spanish_latin" msgid="5690539836069535697">"Espanyol (llatí)"</string>
-    <!-- no translation found for keyboard_layout_latvian (4405417142306250595) -->
-    <skip />
+    <string name="keyboard_layout_latvian" msgid="4405417142306250595">"Letó"</string>
 </resources>
diff --git a/packages/InputDevices/res/values-cs/strings.xml b/packages/InputDevices/res/values-cs/strings.xml
index 532f3c0..1c502fe 100644
--- a/packages/InputDevices/res/values-cs/strings.xml
+++ b/packages/InputDevices/res/values-cs/strings.xml
@@ -39,6 +39,5 @@
     <string name="keyboard_layout_hebrew" msgid="7241473985890173812">"hebrejština"</string>
     <string name="keyboard_layout_lithuanian" msgid="6943110873053106534">"litevština"</string>
     <string name="keyboard_layout_spanish_latin" msgid="5690539836069535697">"španělština (Latinská Amerika)"</string>
-    <!-- no translation found for keyboard_layout_latvian (4405417142306250595) -->
-    <skip />
+    <string name="keyboard_layout_latvian" msgid="4405417142306250595">"Lotyšská klávesnice"</string>
 </resources>
diff --git a/packages/InputDevices/res/values-da/strings.xml b/packages/InputDevices/res/values-da/strings.xml
index 3c907f8..043a5b3 100644
--- a/packages/InputDevices/res/values-da/strings.xml
+++ b/packages/InputDevices/res/values-da/strings.xml
@@ -39,6 +39,5 @@
     <string name="keyboard_layout_hebrew" msgid="7241473985890173812">"Hebræisk"</string>
     <string name="keyboard_layout_lithuanian" msgid="6943110873053106534">"Litauisk"</string>
     <string name="keyboard_layout_spanish_latin" msgid="5690539836069535697">"Spansk (latinamerika)"</string>
-    <!-- no translation found for keyboard_layout_latvian (4405417142306250595) -->
-    <skip />
+    <string name="keyboard_layout_latvian" msgid="4405417142306250595">"Lettisk"</string>
 </resources>
diff --git a/packages/InputDevices/res/values-de/strings.xml b/packages/InputDevices/res/values-de/strings.xml
index 83be031..04c19e3 100644
--- a/packages/InputDevices/res/values-de/strings.xml
+++ b/packages/InputDevices/res/values-de/strings.xml
@@ -39,6 +39,5 @@
     <string name="keyboard_layout_hebrew" msgid="7241473985890173812">"Hebräisch"</string>
     <string name="keyboard_layout_lithuanian" msgid="6943110873053106534">"Litauisch"</string>
     <string name="keyboard_layout_spanish_latin" msgid="5690539836069535697">"Spanisch (Lateinisch)"</string>
-    <!-- no translation found for keyboard_layout_latvian (4405417142306250595) -->
-    <skip />
+    <string name="keyboard_layout_latvian" msgid="4405417142306250595">"Lettisch"</string>
 </resources>
diff --git a/packages/InputDevices/res/values-el/strings.xml b/packages/InputDevices/res/values-el/strings.xml
index 99f77ed..025a288 100644
--- a/packages/InputDevices/res/values-el/strings.xml
+++ b/packages/InputDevices/res/values-el/strings.xml
@@ -39,6 +39,5 @@
     <string name="keyboard_layout_hebrew" msgid="7241473985890173812">"Εβραϊκά"</string>
     <string name="keyboard_layout_lithuanian" msgid="6943110873053106534">"Λιθουανικά"</string>
     <string name="keyboard_layout_spanish_latin" msgid="5690539836069535697">"Ισπανικά (Λατινικής Αμερικής)"</string>
-    <!-- no translation found for keyboard_layout_latvian (4405417142306250595) -->
-    <skip />
+    <string name="keyboard_layout_latvian" msgid="4405417142306250595">"Λετονικά"</string>
 </resources>
diff --git a/packages/InputDevices/res/values-en-rGB/strings.xml b/packages/InputDevices/res/values-en-rGB/strings.xml
index e22c675..d5797a0 100644
--- a/packages/InputDevices/res/values-en-rGB/strings.xml
+++ b/packages/InputDevices/res/values-en-rGB/strings.xml
@@ -39,6 +39,5 @@
     <string name="keyboard_layout_hebrew" msgid="7241473985890173812">"Hebrew"</string>
     <string name="keyboard_layout_lithuanian" msgid="6943110873053106534">"Lithuanian"</string>
     <string name="keyboard_layout_spanish_latin" msgid="5690539836069535697">"Spanish (Latin)"</string>
-    <!-- no translation found for keyboard_layout_latvian (4405417142306250595) -->
-    <skip />
+    <string name="keyboard_layout_latvian" msgid="4405417142306250595">"Latvian"</string>
 </resources>
diff --git a/packages/InputDevices/res/values-en-rIN/strings.xml b/packages/InputDevices/res/values-en-rIN/strings.xml
index e22c675..d5797a0 100644
--- a/packages/InputDevices/res/values-en-rIN/strings.xml
+++ b/packages/InputDevices/res/values-en-rIN/strings.xml
@@ -39,6 +39,5 @@
     <string name="keyboard_layout_hebrew" msgid="7241473985890173812">"Hebrew"</string>
     <string name="keyboard_layout_lithuanian" msgid="6943110873053106534">"Lithuanian"</string>
     <string name="keyboard_layout_spanish_latin" msgid="5690539836069535697">"Spanish (Latin)"</string>
-    <!-- no translation found for keyboard_layout_latvian (4405417142306250595) -->
-    <skip />
+    <string name="keyboard_layout_latvian" msgid="4405417142306250595">"Latvian"</string>
 </resources>
diff --git a/packages/InputDevices/res/values-es-rUS/strings.xml b/packages/InputDevices/res/values-es-rUS/strings.xml
index 5190fc5..0a9d2f3 100644
--- a/packages/InputDevices/res/values-es-rUS/strings.xml
+++ b/packages/InputDevices/res/values-es-rUS/strings.xml
@@ -39,6 +39,5 @@
     <string name="keyboard_layout_hebrew" msgid="7241473985890173812">"Hebreo"</string>
     <string name="keyboard_layout_lithuanian" msgid="6943110873053106534">"Lituano"</string>
     <string name="keyboard_layout_spanish_latin" msgid="5690539836069535697">"Español (latino)"</string>
-    <!-- no translation found for keyboard_layout_latvian (4405417142306250595) -->
-    <skip />
+    <string name="keyboard_layout_latvian" msgid="4405417142306250595">"Letón"</string>
 </resources>
diff --git a/packages/InputDevices/res/values-es/strings.xml b/packages/InputDevices/res/values-es/strings.xml
index a81ed29..6e41abf 100644
--- a/packages/InputDevices/res/values-es/strings.xml
+++ b/packages/InputDevices/res/values-es/strings.xml
@@ -39,6 +39,5 @@
     <string name="keyboard_layout_hebrew" msgid="7241473985890173812">"Hebreo"</string>
     <string name="keyboard_layout_lithuanian" msgid="6943110873053106534">"Lituano"</string>
     <string name="keyboard_layout_spanish_latin" msgid="5690539836069535697">"Español (Latinoamérica)"</string>
-    <!-- no translation found for keyboard_layout_latvian (4405417142306250595) -->
-    <skip />
+    <string name="keyboard_layout_latvian" msgid="4405417142306250595">"Letón"</string>
 </resources>
diff --git a/packages/InputDevices/res/values-et-rEE/strings.xml b/packages/InputDevices/res/values-et-rEE/strings.xml
index b90f825..0d931ce 100644
--- a/packages/InputDevices/res/values-et-rEE/strings.xml
+++ b/packages/InputDevices/res/values-et-rEE/strings.xml
@@ -39,6 +39,5 @@
     <string name="keyboard_layout_hebrew" msgid="7241473985890173812">"Heebrea"</string>
     <string name="keyboard_layout_lithuanian" msgid="6943110873053106534">"Leedu"</string>
     <string name="keyboard_layout_spanish_latin" msgid="5690539836069535697">"Hispaania (Ladina-Ameerika)"</string>
-    <!-- no translation found for keyboard_layout_latvian (4405417142306250595) -->
-    <skip />
+    <string name="keyboard_layout_latvian" msgid="4405417142306250595">"läti keel"</string>
 </resources>
diff --git a/packages/InputDevices/res/values-fa/strings.xml b/packages/InputDevices/res/values-fa/strings.xml
index 490b5c2..e87fbad 100644
--- a/packages/InputDevices/res/values-fa/strings.xml
+++ b/packages/InputDevices/res/values-fa/strings.xml
@@ -39,6 +39,5 @@
     <string name="keyboard_layout_hebrew" msgid="7241473985890173812">"عبری"</string>
     <string name="keyboard_layout_lithuanian" msgid="6943110873053106534">"لیتوانیایی"</string>
     <string name="keyboard_layout_spanish_latin" msgid="5690539836069535697">"اسپانیایی (لاتین)"</string>
-    <!-- no translation found for keyboard_layout_latvian (4405417142306250595) -->
-    <skip />
+    <string name="keyboard_layout_latvian" msgid="4405417142306250595">"لتونیایی"</string>
 </resources>
diff --git a/packages/InputDevices/res/values-fi/strings.xml b/packages/InputDevices/res/values-fi/strings.xml
index 060d0e7..5b39dfd 100644
--- a/packages/InputDevices/res/values-fi/strings.xml
+++ b/packages/InputDevices/res/values-fi/strings.xml
@@ -39,6 +39,5 @@
     <string name="keyboard_layout_hebrew" msgid="7241473985890173812">"heprea"</string>
     <string name="keyboard_layout_lithuanian" msgid="6943110873053106534">"liettua"</string>
     <string name="keyboard_layout_spanish_latin" msgid="5690539836069535697">"espanja (Latinalainen Amerikka)"</string>
-    <!-- no translation found for keyboard_layout_latvian (4405417142306250595) -->
-    <skip />
+    <string name="keyboard_layout_latvian" msgid="4405417142306250595">"latvialainen"</string>
 </resources>
diff --git a/packages/InputDevices/res/values-fr-rCA/strings.xml b/packages/InputDevices/res/values-fr-rCA/strings.xml
index 8fc9f79..9973918 100644
--- a/packages/InputDevices/res/values-fr-rCA/strings.xml
+++ b/packages/InputDevices/res/values-fr-rCA/strings.xml
@@ -39,6 +39,5 @@
     <string name="keyboard_layout_hebrew" msgid="7241473985890173812">"Hébreu"</string>
     <string name="keyboard_layout_lithuanian" msgid="6943110873053106534">"Lituanien"</string>
     <string name="keyboard_layout_spanish_latin" msgid="5690539836069535697">"Espagnol (latin)"</string>
-    <!-- no translation found for keyboard_layout_latvian (4405417142306250595) -->
-    <skip />
+    <string name="keyboard_layout_latvian" msgid="4405417142306250595">"Letton"</string>
 </resources>
diff --git a/packages/InputDevices/res/values-fr/strings.xml b/packages/InputDevices/res/values-fr/strings.xml
index b029b02..fa2977b 100644
--- a/packages/InputDevices/res/values-fr/strings.xml
+++ b/packages/InputDevices/res/values-fr/strings.xml
@@ -39,6 +39,5 @@
     <string name="keyboard_layout_hebrew" msgid="7241473985890173812">"Hébreu"</string>
     <string name="keyboard_layout_lithuanian" msgid="6943110873053106534">"Lituanien"</string>
     <string name="keyboard_layout_spanish_latin" msgid="5690539836069535697">"Espagnol (latin)"</string>
-    <!-- no translation found for keyboard_layout_latvian (4405417142306250595) -->
-    <skip />
+    <string name="keyboard_layout_latvian" msgid="4405417142306250595">"Letton"</string>
 </resources>
diff --git a/packages/InputDevices/res/values-hi/strings.xml b/packages/InputDevices/res/values-hi/strings.xml
index 8398c92..77cb8fe 100644
--- a/packages/InputDevices/res/values-hi/strings.xml
+++ b/packages/InputDevices/res/values-hi/strings.xml
@@ -39,6 +39,5 @@
     <string name="keyboard_layout_hebrew" msgid="7241473985890173812">"हिब्रू"</string>
     <string name="keyboard_layout_lithuanian" msgid="6943110873053106534">"लिथुआनियाई"</string>
     <string name="keyboard_layout_spanish_latin" msgid="5690539836069535697">"स्पेनिश (लैटिन)"</string>
-    <!-- no translation found for keyboard_layout_latvian (4405417142306250595) -->
-    <skip />
+    <string name="keyboard_layout_latvian" msgid="4405417142306250595">"लातवियाई"</string>
 </resources>
diff --git a/packages/InputDevices/res/values-hr/strings.xml b/packages/InputDevices/res/values-hr/strings.xml
index 68c868f..bad973d 100644
--- a/packages/InputDevices/res/values-hr/strings.xml
+++ b/packages/InputDevices/res/values-hr/strings.xml
@@ -39,6 +39,5 @@
     <string name="keyboard_layout_hebrew" msgid="7241473985890173812">"hebrejski"</string>
     <string name="keyboard_layout_lithuanian" msgid="6943110873053106534">"litavski"</string>
     <string name="keyboard_layout_spanish_latin" msgid="5690539836069535697">"španjolski (Latinska Amerika)"</string>
-    <!-- no translation found for keyboard_layout_latvian (4405417142306250595) -->
-    <skip />
+    <string name="keyboard_layout_latvian" msgid="4405417142306250595">"latvijska"</string>
 </resources>
diff --git a/packages/InputDevices/res/values-hu/strings.xml b/packages/InputDevices/res/values-hu/strings.xml
index af6a571..510591d 100644
--- a/packages/InputDevices/res/values-hu/strings.xml
+++ b/packages/InputDevices/res/values-hu/strings.xml
@@ -39,6 +39,5 @@
     <string name="keyboard_layout_hebrew" msgid="7241473985890173812">"héber"</string>
     <string name="keyboard_layout_lithuanian" msgid="6943110873053106534">"litván"</string>
     <string name="keyboard_layout_spanish_latin" msgid="5690539836069535697">"spanyol (latin-amerikai)"</string>
-    <!-- no translation found for keyboard_layout_latvian (4405417142306250595) -->
-    <skip />
+    <string name="keyboard_layout_latvian" msgid="4405417142306250595">"lett"</string>
 </resources>
diff --git a/packages/InputDevices/res/values-hy-rAM/strings.xml b/packages/InputDevices/res/values-hy-rAM/strings.xml
index 068e559..9ffa8bb 100644
--- a/packages/InputDevices/res/values-hy-rAM/strings.xml
+++ b/packages/InputDevices/res/values-hy-rAM/strings.xml
@@ -39,6 +39,5 @@
     <string name="keyboard_layout_hebrew" msgid="7241473985890173812">"Եբրայերեն"</string>
     <string name="keyboard_layout_lithuanian" msgid="6943110873053106534">"Լիտվերեն"</string>
     <string name="keyboard_layout_spanish_latin" msgid="5690539836069535697">"Իսպաներեն (Լատինական)"</string>
-    <!-- no translation found for keyboard_layout_latvian (4405417142306250595) -->
-    <skip />
+    <string name="keyboard_layout_latvian" msgid="4405417142306250595">"լատիշերեն"</string>
 </resources>
diff --git a/packages/InputDevices/res/values-in/strings.xml b/packages/InputDevices/res/values-in/strings.xml
index d32ac98..fccfa67 100644
--- a/packages/InputDevices/res/values-in/strings.xml
+++ b/packages/InputDevices/res/values-in/strings.xml
@@ -39,6 +39,5 @@
     <string name="keyboard_layout_hebrew" msgid="7241473985890173812">"Ibrani"</string>
     <string name="keyboard_layout_lithuanian" msgid="6943110873053106534">"Lithuania"</string>
     <string name="keyboard_layout_spanish_latin" msgid="5690539836069535697">"Spanyol (Latin)"</string>
-    <!-- no translation found for keyboard_layout_latvian (4405417142306250595) -->
-    <skip />
+    <string name="keyboard_layout_latvian" msgid="4405417142306250595">"Latvi"</string>
 </resources>
diff --git a/packages/InputDevices/res/values-it/strings.xml b/packages/InputDevices/res/values-it/strings.xml
index 280a23c..83dba70 100644
--- a/packages/InputDevices/res/values-it/strings.xml
+++ b/packages/InputDevices/res/values-it/strings.xml
@@ -39,6 +39,5 @@
     <string name="keyboard_layout_hebrew" msgid="7241473985890173812">"Ebraico"</string>
     <string name="keyboard_layout_lithuanian" msgid="6943110873053106534">"Lituano"</string>
     <string name="keyboard_layout_spanish_latin" msgid="5690539836069535697">"Spagnolo (America Latina)"</string>
-    <!-- no translation found for keyboard_layout_latvian (4405417142306250595) -->
-    <skip />
+    <string name="keyboard_layout_latvian" msgid="4405417142306250595">"Lettone"</string>
 </resources>
diff --git a/packages/InputDevices/res/values-iw/strings.xml b/packages/InputDevices/res/values-iw/strings.xml
index 9bb66d8..26fe662 100644
--- a/packages/InputDevices/res/values-iw/strings.xml
+++ b/packages/InputDevices/res/values-iw/strings.xml
@@ -39,6 +39,5 @@
     <string name="keyboard_layout_hebrew" msgid="7241473985890173812">"עברית"</string>
     <string name="keyboard_layout_lithuanian" msgid="6943110873053106534">"ליטאית"</string>
     <string name="keyboard_layout_spanish_latin" msgid="5690539836069535697">"ספרדית (לטינית)"</string>
-    <!-- no translation found for keyboard_layout_latvian (4405417142306250595) -->
-    <skip />
+    <string name="keyboard_layout_latvian" msgid="4405417142306250595">"לטבית"</string>
 </resources>
diff --git a/packages/InputDevices/res/values-ja/strings.xml b/packages/InputDevices/res/values-ja/strings.xml
index 26b1094..e2b154d 100644
--- a/packages/InputDevices/res/values-ja/strings.xml
+++ b/packages/InputDevices/res/values-ja/strings.xml
@@ -39,6 +39,5 @@
     <string name="keyboard_layout_hebrew" msgid="7241473985890173812">"ヘブライ語"</string>
     <string name="keyboard_layout_lithuanian" msgid="6943110873053106534">"リトアニア語"</string>
     <string name="keyboard_layout_spanish_latin" msgid="5690539836069535697">"スペイン語(中南米)"</string>
-    <!-- no translation found for keyboard_layout_latvian (4405417142306250595) -->
-    <skip />
+    <string name="keyboard_layout_latvian" msgid="4405417142306250595">"ラトビア語"</string>
 </resources>
diff --git a/packages/InputDevices/res/values-ka-rGE/strings.xml b/packages/InputDevices/res/values-ka-rGE/strings.xml
index 35085a1..eff4b04 100644
--- a/packages/InputDevices/res/values-ka-rGE/strings.xml
+++ b/packages/InputDevices/res/values-ka-rGE/strings.xml
@@ -39,6 +39,5 @@
     <string name="keyboard_layout_hebrew" msgid="7241473985890173812">"ებრაული"</string>
     <string name="keyboard_layout_lithuanian" msgid="6943110873053106534">"ლიტვური"</string>
     <string name="keyboard_layout_spanish_latin" msgid="5690539836069535697">"ესპანური (ლათინური)"</string>
-    <!-- no translation found for keyboard_layout_latvian (4405417142306250595) -->
-    <skip />
+    <string name="keyboard_layout_latvian" msgid="4405417142306250595">"ლატვიური"</string>
 </resources>
diff --git a/packages/InputDevices/res/values-km-rKH/strings.xml b/packages/InputDevices/res/values-km-rKH/strings.xml
index ea3b755..60a28b1 100644
--- a/packages/InputDevices/res/values-km-rKH/strings.xml
+++ b/packages/InputDevices/res/values-km-rKH/strings.xml
@@ -39,6 +39,5 @@
     <string name="keyboard_layout_hebrew" msgid="7241473985890173812">"អ៊ីស្រាអែល"</string>
     <string name="keyboard_layout_lithuanian" msgid="6943110873053106534">"លីទុយអានី"</string>
     <string name="keyboard_layout_spanish_latin" msgid="5690539836069535697">"អេស្ប៉ាញ (ឡាតាំង​)"</string>
-    <!-- no translation found for keyboard_layout_latvian (4405417142306250595) -->
-    <skip />
+    <string name="keyboard_layout_latvian" msgid="4405417142306250595">"ឡាតវីយ៉ា"</string>
 </resources>
diff --git a/packages/InputDevices/res/values-ko/strings.xml b/packages/InputDevices/res/values-ko/strings.xml
index e360ed0..3f563d1 100644
--- a/packages/InputDevices/res/values-ko/strings.xml
+++ b/packages/InputDevices/res/values-ko/strings.xml
@@ -39,6 +39,5 @@
     <string name="keyboard_layout_hebrew" msgid="7241473985890173812">"히브리어"</string>
     <string name="keyboard_layout_lithuanian" msgid="6943110873053106534">"리투아니아어"</string>
     <string name="keyboard_layout_spanish_latin" msgid="5690539836069535697">"스페인어(라틴)"</string>
-    <!-- no translation found for keyboard_layout_latvian (4405417142306250595) -->
-    <skip />
+    <string name="keyboard_layout_latvian" msgid="4405417142306250595">"라트비아어"</string>
 </resources>
diff --git a/packages/InputDevices/res/values-lo-rLA/strings.xml b/packages/InputDevices/res/values-lo-rLA/strings.xml
index fc37501..fb3fe17 100644
--- a/packages/InputDevices/res/values-lo-rLA/strings.xml
+++ b/packages/InputDevices/res/values-lo-rLA/strings.xml
@@ -39,6 +39,5 @@
     <string name="keyboard_layout_hebrew" msgid="7241473985890173812">"ຮີບຣິວ"</string>
     <string name="keyboard_layout_lithuanian" msgid="6943110873053106534">"​ລິ​ທົວ​ນຽນ"</string>
     <string name="keyboard_layout_spanish_latin" msgid="5690539836069535697">"​ສະ​ແປນ​ນິດ (ລາ​ຕິນ)"</string>
-    <!-- no translation found for keyboard_layout_latvian (4405417142306250595) -->
-    <skip />
+    <string name="keyboard_layout_latvian" msgid="4405417142306250595">"​ລັດ​ວຽນ"</string>
 </resources>
diff --git a/packages/InputDevices/res/values-lt/strings.xml b/packages/InputDevices/res/values-lt/strings.xml
index c197389..d0eb1f6 100644
--- a/packages/InputDevices/res/values-lt/strings.xml
+++ b/packages/InputDevices/res/values-lt/strings.xml
@@ -39,6 +39,5 @@
     <string name="keyboard_layout_hebrew" msgid="7241473985890173812">"Hebrajų"</string>
     <string name="keyboard_layout_lithuanian" msgid="6943110873053106534">"Lietuvių"</string>
     <string name="keyboard_layout_spanish_latin" msgid="5690539836069535697">"Ispanų (Lotynų Amerika)"</string>
-    <!-- no translation found for keyboard_layout_latvian (4405417142306250595) -->
-    <skip />
+    <string name="keyboard_layout_latvian" msgid="4405417142306250595">"Latvių k."</string>
 </resources>
diff --git a/packages/InputDevices/res/values-lv/strings.xml b/packages/InputDevices/res/values-lv/strings.xml
index 53d2467..0608bf0 100644
--- a/packages/InputDevices/res/values-lv/strings.xml
+++ b/packages/InputDevices/res/values-lv/strings.xml
@@ -39,6 +39,5 @@
     <string name="keyboard_layout_hebrew" msgid="7241473985890173812">"Ivrits"</string>
     <string name="keyboard_layout_lithuanian" msgid="6943110873053106534">"Lietuviešu"</string>
     <string name="keyboard_layout_spanish_latin" msgid="5690539836069535697">"Spāņu (latīņu)"</string>
-    <!-- no translation found for keyboard_layout_latvian (4405417142306250595) -->
-    <skip />
+    <string name="keyboard_layout_latvian" msgid="4405417142306250595">"Latviešu"</string>
 </resources>
diff --git a/packages/InputDevices/res/values-mn-rMN/strings.xml b/packages/InputDevices/res/values-mn-rMN/strings.xml
index 194c577..a28fd2a 100644
--- a/packages/InputDevices/res/values-mn-rMN/strings.xml
+++ b/packages/InputDevices/res/values-mn-rMN/strings.xml
@@ -39,6 +39,5 @@
     <string name="keyboard_layout_hebrew" msgid="7241473985890173812">"Еврей"</string>
     <string name="keyboard_layout_lithuanian" msgid="6943110873053106534">"Литви"</string>
     <string name="keyboard_layout_spanish_latin" msgid="5690539836069535697">"Испани (Латин)"</string>
-    <!-- no translation found for keyboard_layout_latvian (4405417142306250595) -->
-    <skip />
+    <string name="keyboard_layout_latvian" msgid="4405417142306250595">"Латви"</string>
 </resources>
diff --git a/packages/InputDevices/res/values-ms-rMY/strings.xml b/packages/InputDevices/res/values-ms-rMY/strings.xml
index a3098c5..a1a6d00 100644
--- a/packages/InputDevices/res/values-ms-rMY/strings.xml
+++ b/packages/InputDevices/res/values-ms-rMY/strings.xml
@@ -39,6 +39,5 @@
     <string name="keyboard_layout_hebrew" msgid="7241473985890173812">"Bahasa Ibrani"</string>
     <string name="keyboard_layout_lithuanian" msgid="6943110873053106534">"Bahasa Lithuania"</string>
     <string name="keyboard_layout_spanish_latin" msgid="5690539836069535697">"Bahasa Sepanyol (Latin)"</string>
-    <!-- no translation found for keyboard_layout_latvian (4405417142306250595) -->
-    <skip />
+    <string name="keyboard_layout_latvian" msgid="4405417142306250595">"Bahasa Latvia"</string>
 </resources>
diff --git a/packages/InputDevices/res/values-nb/strings.xml b/packages/InputDevices/res/values-nb/strings.xml
index 47cff5c..ad4b704 100644
--- a/packages/InputDevices/res/values-nb/strings.xml
+++ b/packages/InputDevices/res/values-nb/strings.xml
@@ -39,6 +39,5 @@
     <string name="keyboard_layout_hebrew" msgid="7241473985890173812">"Hebraisk"</string>
     <string name="keyboard_layout_lithuanian" msgid="6943110873053106534">"Litauisk"</string>
     <string name="keyboard_layout_spanish_latin" msgid="5690539836069535697">"Spansk (latinsk)"</string>
-    <!-- no translation found for keyboard_layout_latvian (4405417142306250595) -->
-    <skip />
+    <string name="keyboard_layout_latvian" msgid="4405417142306250595">"Latvisk"</string>
 </resources>
diff --git a/packages/InputDevices/res/values-nl/strings.xml b/packages/InputDevices/res/values-nl/strings.xml
index e858c9c..c57251e 100644
--- a/packages/InputDevices/res/values-nl/strings.xml
+++ b/packages/InputDevices/res/values-nl/strings.xml
@@ -39,6 +39,5 @@
     <string name="keyboard_layout_hebrew" msgid="7241473985890173812">"Hebreeuws"</string>
     <string name="keyboard_layout_lithuanian" msgid="6943110873053106534">"Litouws"</string>
     <string name="keyboard_layout_spanish_latin" msgid="5690539836069535697">"Spaans (Latijns-Amerika)"</string>
-    <!-- no translation found for keyboard_layout_latvian (4405417142306250595) -->
-    <skip />
+    <string name="keyboard_layout_latvian" msgid="4405417142306250595">"Lets"</string>
 </resources>
diff --git a/packages/InputDevices/res/values-pl/strings.xml b/packages/InputDevices/res/values-pl/strings.xml
index 0ca91ca..39fb3ec 100644
--- a/packages/InputDevices/res/values-pl/strings.xml
+++ b/packages/InputDevices/res/values-pl/strings.xml
@@ -39,6 +39,5 @@
     <string name="keyboard_layout_hebrew" msgid="7241473985890173812">"hebrajski"</string>
     <string name="keyboard_layout_lithuanian" msgid="6943110873053106534">"litewski"</string>
     <string name="keyboard_layout_spanish_latin" msgid="5690539836069535697">"hiszpański (Ameryka Łacińska)"</string>
-    <!-- no translation found for keyboard_layout_latvian (4405417142306250595) -->
-    <skip />
+    <string name="keyboard_layout_latvian" msgid="4405417142306250595">"łotewski"</string>
 </resources>
diff --git a/packages/InputDevices/res/values-pt-rPT/strings.xml b/packages/InputDevices/res/values-pt-rPT/strings.xml
index d5afe05..3ac3b84 100644
--- a/packages/InputDevices/res/values-pt-rPT/strings.xml
+++ b/packages/InputDevices/res/values-pt-rPT/strings.xml
@@ -39,6 +39,5 @@
     <string name="keyboard_layout_hebrew" msgid="7241473985890173812">"Hebraico"</string>
     <string name="keyboard_layout_lithuanian" msgid="6943110873053106534">"Lituano"</string>
     <string name="keyboard_layout_spanish_latin" msgid="5690539836069535697">"Espanhol (América Latina)"</string>
-    <!-- no translation found for keyboard_layout_latvian (4405417142306250595) -->
-    <skip />
+    <string name="keyboard_layout_latvian" msgid="4405417142306250595">"Letão"</string>
 </resources>
diff --git a/packages/InputDevices/res/values-pt/strings.xml b/packages/InputDevices/res/values-pt/strings.xml
index 84cf865..e9a0a38 100644
--- a/packages/InputDevices/res/values-pt/strings.xml
+++ b/packages/InputDevices/res/values-pt/strings.xml
@@ -39,6 +39,5 @@
     <string name="keyboard_layout_hebrew" msgid="7241473985890173812">"Hebraico"</string>
     <string name="keyboard_layout_lithuanian" msgid="6943110873053106534">"Lituano"</string>
     <string name="keyboard_layout_spanish_latin" msgid="5690539836069535697">"Espanhol (América Latina)"</string>
-    <!-- no translation found for keyboard_layout_latvian (4405417142306250595) -->
-    <skip />
+    <string name="keyboard_layout_latvian" msgid="4405417142306250595">"Letão"</string>
 </resources>
diff --git a/packages/InputDevices/res/values-ro/strings.xml b/packages/InputDevices/res/values-ro/strings.xml
index 5278643..c2392b1 100644
--- a/packages/InputDevices/res/values-ro/strings.xml
+++ b/packages/InputDevices/res/values-ro/strings.xml
@@ -39,6 +39,5 @@
     <string name="keyboard_layout_hebrew" msgid="7241473985890173812">"Ebraică"</string>
     <string name="keyboard_layout_lithuanian" msgid="6943110873053106534">"Lituaniană"</string>
     <string name="keyboard_layout_spanish_latin" msgid="5690539836069535697">"Spaniolă (America Latină)"</string>
-    <!-- no translation found for keyboard_layout_latvian (4405417142306250595) -->
-    <skip />
+    <string name="keyboard_layout_latvian" msgid="4405417142306250595">"Letonă"</string>
 </resources>
diff --git a/packages/InputDevices/res/values-ru/strings.xml b/packages/InputDevices/res/values-ru/strings.xml
index 3ae2e53..70ecf6e 100644
--- a/packages/InputDevices/res/values-ru/strings.xml
+++ b/packages/InputDevices/res/values-ru/strings.xml
@@ -39,6 +39,5 @@
     <string name="keyboard_layout_hebrew" msgid="7241473985890173812">"Иврит"</string>
     <string name="keyboard_layout_lithuanian" msgid="6943110873053106534">"Литовский"</string>
     <string name="keyboard_layout_spanish_latin" msgid="5690539836069535697">"Испанский (Латинская Америка)"</string>
-    <!-- no translation found for keyboard_layout_latvian (4405417142306250595) -->
-    <skip />
+    <string name="keyboard_layout_latvian" msgid="4405417142306250595">"латышский"</string>
 </resources>
diff --git a/packages/InputDevices/res/values-sk/strings.xml b/packages/InputDevices/res/values-sk/strings.xml
index 1e51167..d2ee0cf 100644
--- a/packages/InputDevices/res/values-sk/strings.xml
+++ b/packages/InputDevices/res/values-sk/strings.xml
@@ -39,6 +39,5 @@
     <string name="keyboard_layout_hebrew" msgid="7241473985890173812">"Hebrejčina"</string>
     <string name="keyboard_layout_lithuanian" msgid="6943110873053106534">"Litovčina"</string>
     <string name="keyboard_layout_spanish_latin" msgid="5690539836069535697">"Španielčina (Latinská Amerika)"</string>
-    <!-- no translation found for keyboard_layout_latvian (4405417142306250595) -->
-    <skip />
+    <string name="keyboard_layout_latvian" msgid="4405417142306250595">"Lotyština"</string>
 </resources>
diff --git a/packages/InputDevices/res/values-sl/strings.xml b/packages/InputDevices/res/values-sl/strings.xml
index 66e340e..38542ef 100644
--- a/packages/InputDevices/res/values-sl/strings.xml
+++ b/packages/InputDevices/res/values-sl/strings.xml
@@ -39,6 +39,5 @@
     <string name="keyboard_layout_hebrew" msgid="7241473985890173812">"hebrejščina"</string>
     <string name="keyboard_layout_lithuanian" msgid="6943110873053106534">"litovščina"</string>
     <string name="keyboard_layout_spanish_latin" msgid="5690539836069535697">"španščina (Latinska Amerika)"</string>
-    <!-- no translation found for keyboard_layout_latvian (4405417142306250595) -->
-    <skip />
+    <string name="keyboard_layout_latvian" msgid="4405417142306250595">"latvijščina"</string>
 </resources>
diff --git a/packages/InputDevices/res/values-sr/strings.xml b/packages/InputDevices/res/values-sr/strings.xml
index 14ccbf3..dd500e6 100644
--- a/packages/InputDevices/res/values-sr/strings.xml
+++ b/packages/InputDevices/res/values-sr/strings.xml
@@ -39,6 +39,5 @@
     <string name="keyboard_layout_hebrew" msgid="7241473985890173812">"хебрејски"</string>
     <string name="keyboard_layout_lithuanian" msgid="6943110873053106534">"литвански"</string>
     <string name="keyboard_layout_spanish_latin" msgid="5690539836069535697">"шпански (Латинска Америка)"</string>
-    <!-- no translation found for keyboard_layout_latvian (4405417142306250595) -->
-    <skip />
+    <string name="keyboard_layout_latvian" msgid="4405417142306250595">"летонски"</string>
 </resources>
diff --git a/packages/InputDevices/res/values-sv/strings.xml b/packages/InputDevices/res/values-sv/strings.xml
index f3338c6..c2406c0 100644
--- a/packages/InputDevices/res/values-sv/strings.xml
+++ b/packages/InputDevices/res/values-sv/strings.xml
@@ -39,6 +39,5 @@
     <string name="keyboard_layout_hebrew" msgid="7241473985890173812">"Hebreiska"</string>
     <string name="keyboard_layout_lithuanian" msgid="6943110873053106534">"Litauiska"</string>
     <string name="keyboard_layout_spanish_latin" msgid="5690539836069535697">"Spanska (latinamerikansk)"</string>
-    <!-- no translation found for keyboard_layout_latvian (4405417142306250595) -->
-    <skip />
+    <string name="keyboard_layout_latvian" msgid="4405417142306250595">"lettiska"</string>
 </resources>
diff --git a/packages/InputDevices/res/values-sw/strings.xml b/packages/InputDevices/res/values-sw/strings.xml
index 336cc33..f71a696 100644
--- a/packages/InputDevices/res/values-sw/strings.xml
+++ b/packages/InputDevices/res/values-sw/strings.xml
@@ -39,6 +39,5 @@
     <string name="keyboard_layout_hebrew" msgid="7241473985890173812">"Kiyahudi"</string>
     <string name="keyboard_layout_lithuanian" msgid="6943110873053106534">"Kilithuania"</string>
     <string name="keyboard_layout_spanish_latin" msgid="5690539836069535697">"Kihispania (Kilatini)"</string>
-    <!-- no translation found for keyboard_layout_latvian (4405417142306250595) -->
-    <skip />
+    <string name="keyboard_layout_latvian" msgid="4405417142306250595">"Kilatvia"</string>
 </resources>
diff --git a/packages/InputDevices/res/values-th/strings.xml b/packages/InputDevices/res/values-th/strings.xml
index 86a633a..296994b 100644
--- a/packages/InputDevices/res/values-th/strings.xml
+++ b/packages/InputDevices/res/values-th/strings.xml
@@ -39,6 +39,5 @@
     <string name="keyboard_layout_hebrew" msgid="7241473985890173812">"ฮิบรู"</string>
     <string name="keyboard_layout_lithuanian" msgid="6943110873053106534">"ลิทัวเนีย"</string>
     <string name="keyboard_layout_spanish_latin" msgid="5690539836069535697">"สเปน (ละติน)"</string>
-    <!-- no translation found for keyboard_layout_latvian (4405417142306250595) -->
-    <skip />
+    <string name="keyboard_layout_latvian" msgid="4405417142306250595">"ลัตเวีย"</string>
 </resources>
diff --git a/packages/InputDevices/res/values-tl/strings.xml b/packages/InputDevices/res/values-tl/strings.xml
index 702a0cb..d7920ed 100644
--- a/packages/InputDevices/res/values-tl/strings.xml
+++ b/packages/InputDevices/res/values-tl/strings.xml
@@ -39,6 +39,5 @@
     <string name="keyboard_layout_hebrew" msgid="7241473985890173812">"Hebrew"</string>
     <string name="keyboard_layout_lithuanian" msgid="6943110873053106534">"Lithuanian"</string>
     <string name="keyboard_layout_spanish_latin" msgid="5690539836069535697">"Spanish (Latin)"</string>
-    <!-- no translation found for keyboard_layout_latvian (4405417142306250595) -->
-    <skip />
+    <string name="keyboard_layout_latvian" msgid="4405417142306250595">"Latvian"</string>
 </resources>
diff --git a/packages/InputDevices/res/values-tr/strings.xml b/packages/InputDevices/res/values-tr/strings.xml
index b3ce0a3..c0c70be 100644
--- a/packages/InputDevices/res/values-tr/strings.xml
+++ b/packages/InputDevices/res/values-tr/strings.xml
@@ -39,6 +39,5 @@
     <string name="keyboard_layout_hebrew" msgid="7241473985890173812">"İbranice"</string>
     <string name="keyboard_layout_lithuanian" msgid="6943110873053106534">"Litvanca"</string>
     <string name="keyboard_layout_spanish_latin" msgid="5690539836069535697">"İspanyolca (Latin)"</string>
-    <!-- no translation found for keyboard_layout_latvian (4405417142306250595) -->
-    <skip />
+    <string name="keyboard_layout_latvian" msgid="4405417142306250595">"Letonca"</string>
 </resources>
diff --git a/packages/InputDevices/res/values-uk/strings.xml b/packages/InputDevices/res/values-uk/strings.xml
index 5193a90..d8152d4 100644
--- a/packages/InputDevices/res/values-uk/strings.xml
+++ b/packages/InputDevices/res/values-uk/strings.xml
@@ -39,6 +39,5 @@
     <string name="keyboard_layout_hebrew" msgid="7241473985890173812">"Іврит"</string>
     <string name="keyboard_layout_lithuanian" msgid="6943110873053106534">"Литовська"</string>
     <string name="keyboard_layout_spanish_latin" msgid="5690539836069535697">"Іспанська (латиниця)"</string>
-    <!-- no translation found for keyboard_layout_latvian (4405417142306250595) -->
-    <skip />
+    <string name="keyboard_layout_latvian" msgid="4405417142306250595">"Латвійська"</string>
 </resources>
diff --git a/packages/InputDevices/res/values-vi/strings.xml b/packages/InputDevices/res/values-vi/strings.xml
index 2240100..a90c1cd 100644
--- a/packages/InputDevices/res/values-vi/strings.xml
+++ b/packages/InputDevices/res/values-vi/strings.xml
@@ -39,6 +39,5 @@
     <string name="keyboard_layout_hebrew" msgid="7241473985890173812">"Tiếng Do Thái"</string>
     <string name="keyboard_layout_lithuanian" msgid="6943110873053106534">"Tiếng Lithuania"</string>
     <string name="keyboard_layout_spanish_latin" msgid="5690539836069535697">"Tiếng Tây Ban Nha (La tinh)"</string>
-    <!-- no translation found for keyboard_layout_latvian (4405417142306250595) -->
-    <skip />
+    <string name="keyboard_layout_latvian" msgid="4405417142306250595">"Tiếng Latvia"</string>
 </resources>
diff --git a/packages/InputDevices/res/values-zh-rCN/strings.xml b/packages/InputDevices/res/values-zh-rCN/strings.xml
index 161b600..206f97c 100644
--- a/packages/InputDevices/res/values-zh-rCN/strings.xml
+++ b/packages/InputDevices/res/values-zh-rCN/strings.xml
@@ -39,6 +39,5 @@
     <string name="keyboard_layout_hebrew" msgid="7241473985890173812">"希伯来语"</string>
     <string name="keyboard_layout_lithuanian" msgid="6943110873053106534">"立陶宛语"</string>
     <string name="keyboard_layout_spanish_latin" msgid="5690539836069535697">"西班牙语(拉丁美洲)"</string>
-    <!-- no translation found for keyboard_layout_latvian (4405417142306250595) -->
-    <skip />
+    <string name="keyboard_layout_latvian" msgid="4405417142306250595">"拉脱维亚语"</string>
 </resources>
diff --git a/packages/InputDevices/res/values-zh-rHK/strings.xml b/packages/InputDevices/res/values-zh-rHK/strings.xml
index f0df88c..ff5570e 100644
--- a/packages/InputDevices/res/values-zh-rHK/strings.xml
+++ b/packages/InputDevices/res/values-zh-rHK/strings.xml
@@ -39,6 +39,5 @@
     <string name="keyboard_layout_hebrew" msgid="7241473985890173812">"希伯來文"</string>
     <string name="keyboard_layout_lithuanian" msgid="6943110873053106534">"立陶宛文"</string>
     <string name="keyboard_layout_spanish_latin" msgid="5690539836069535697">"西班牙文 (拉丁美洲)"</string>
-    <!-- no translation found for keyboard_layout_latvian (4405417142306250595) -->
-    <skip />
+    <string name="keyboard_layout_latvian" msgid="4405417142306250595">"拉脫維亞文"</string>
 </resources>
diff --git a/packages/InputDevices/res/values-zh-rTW/strings.xml b/packages/InputDevices/res/values-zh-rTW/strings.xml
index ca43326..859983d 100644
--- a/packages/InputDevices/res/values-zh-rTW/strings.xml
+++ b/packages/InputDevices/res/values-zh-rTW/strings.xml
@@ -39,6 +39,5 @@
     <string name="keyboard_layout_hebrew" msgid="7241473985890173812">"希伯來文"</string>
     <string name="keyboard_layout_lithuanian" msgid="6943110873053106534">"立陶宛文"</string>
     <string name="keyboard_layout_spanish_latin" msgid="5690539836069535697">"西班牙文 (拉丁美洲)"</string>
-    <!-- no translation found for keyboard_layout_latvian (4405417142306250595) -->
-    <skip />
+    <string name="keyboard_layout_latvian" msgid="4405417142306250595">"拉脫維亞文"</string>
 </resources>
diff --git a/packages/InputDevices/res/values-zu/strings.xml b/packages/InputDevices/res/values-zu/strings.xml
index 677ed29..9f30f7a 100644
--- a/packages/InputDevices/res/values-zu/strings.xml
+++ b/packages/InputDevices/res/values-zu/strings.xml
@@ -39,6 +39,5 @@
     <string name="keyboard_layout_hebrew" msgid="7241473985890173812">"Isi-Hebrew"</string>
     <string name="keyboard_layout_lithuanian" msgid="6943110873053106534">"Isi-Lithuanian"</string>
     <string name="keyboard_layout_spanish_latin" msgid="5690539836069535697">"Isi-Spanish (Latin)"</string>
-    <!-- no translation found for keyboard_layout_latvian (4405417142306250595) -->
-    <skip />
+    <string name="keyboard_layout_latvian" msgid="4405417142306250595">"Isi-Latvian"</string>
 </resources>
diff --git a/packages/Keyguard/Android.mk b/packages/Keyguard/Android.mk
index 1be44f9..96ed2e7 100644
--- a/packages/Keyguard/Android.mk
+++ b/packages/Keyguard/Android.mk
@@ -16,8 +16,7 @@
 LOCAL_PATH:= $(call my-dir)
 include $(CLEAR_VARS)
 
-LOCAL_SRC_FILES := $(call all-java-files-under, src) $(call all-subdir-Iaidl-files) \
-                   $(call all-proto-files-under,src)
+LOCAL_SRC_FILES := $(call all-java-files-under, src) $(call all-subdir-Iaidl-files)
 
 LOCAL_MODULE := Keyguard
 
@@ -27,9 +26,6 @@
 
 LOCAL_PROGUARD_FLAG_FILES := proguard.flags
 
-LOCAL_PROTOC_OPTIMIZE_TYPE := nano
-LOCAL_PROTO_JAVA_OUTPUT_PARAMS := optional_field_style=accessors
-
 LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res
 
 include $(BUILD_STATIC_JAVA_LIBRARY)
diff --git a/packages/Keyguard/res/layout/keyguard_emergency_carrier_area.xml b/packages/Keyguard/res/layout/keyguard_emergency_carrier_area.xml
index b2d0219..78b5746 100644
--- a/packages/Keyguard/res/layout/keyguard_emergency_carrier_area.xml
+++ b/packages/Keyguard/res/layout/keyguard_emergency_carrier_area.xml
@@ -37,6 +37,7 @@
         android:textAppearance="?android:attr/textAppearanceMedium"
         android:textSize="@dimen/kg_status_line_font_size"
         android:textColor="?android:attr/textColorSecondary"
+        android:visibility="gone"
         androidprv:allCaps="@bool/kg_use_all_caps" />
 
     <LinearLayout
diff --git a/packages/Keyguard/res/layout/keyguard_pin_view.xml b/packages/Keyguard/res/layout/keyguard_pin_view.xml
index a804c8c..a8e330b 100644
--- a/packages/Keyguard/res/layout/keyguard_pin_view.xml
+++ b/packages/Keyguard/res/layout/keyguard_pin_view.xml
@@ -41,28 +41,16 @@
        android:layout_weight="1"
        android:layoutDirection="ltr"
        >
-       <LinearLayout
+       <RelativeLayout
+          android:id="@+id/row0"
           android:layout_width="match_parent"
           android:layout_height="0dp"
-          android:orientation="horizontal"
           android:layout_weight="1"
           >
-          <TextView android:id="@+id/pinEntry"
-               android:editable="true"
-               android:layout_width="0dip"
-               android:layout_height="match_parent"
-               android:layout_weight="1"
-               android:gravity="center"
-               android:layout_marginStart="@dimen/keyguard_lockscreen_pin_margin_left"
-               android:singleLine="true"
-               android:cursorVisible="false"
-               android:background="@null"
-               android:textAppearance="@style/TextAppearance.NumPadKey"
-               android:imeOptions="flagForceAscii|actionDone"
-               />
-           <ImageButton android:id="@+id/delete_button"
+          <ImageButton android:id="@+id/delete_button"
                android:layout_width="wrap_content"
                android:layout_height="match_parent"
+               android:layout_alignParentEnd="true"
                android:gravity="center_vertical"
                android:src="@drawable/ic_input_delete"
                android:clickable="true"
@@ -73,13 +61,30 @@
                android:background="?android:attr/selectableItemBackground"
                android:contentDescription="@string/keyboardview_keycode_delete"
                />
-       </LinearLayout>
-       <View
-           android:layout_width="wrap_content"
-           android:layout_height="1dp"
-           android:background="#55FFFFFF"
-           />
+           <TextView android:id="@+id/pinEntry"
+               android:editable="true"
+               android:layout_width="match_parent"
+               android:layout_height="match_parent"
+               android:layout_toStartOf="@+id/delete_button"
+               android:layout_alignParentStart="true"
+               android:gravity="center"
+               android:layout_marginStart="@dimen/keyguard_lockscreen_pin_margin_left"
+               android:singleLine="true"
+               android:cursorVisible="false"
+               android:background="@null"
+               android:textAppearance="@style/TextAppearance.NumPadKey"
+               android:imeOptions="flagForceAscii|actionDone"
+               />
+            <View
+               android:id="@+id/divider"
+               android:layout_width="match_parent"
+               android:layout_height="1dp"
+               android:layout_alignParentBottom="true"
+               android:background="#55FFFFFF"
+               />
+       </RelativeLayout>
        <LinearLayout
+           android:id="@+id/row1"
            android:layout_width="match_parent"
            android:layout_height="0dp"
            android:layout_weight="1"
@@ -114,6 +119,7 @@
                />
        </LinearLayout>
        <LinearLayout
+           android:id="@+id/row2"
            android:layout_width="match_parent"
            android:layout_height="0dp"
            android:layout_weight="1"
@@ -148,6 +154,7 @@
                />
        </LinearLayout>
        <LinearLayout
+           android:id="@+id/row3"
            android:layout_width="match_parent"
            android:layout_height="0dp"
            android:orientation="horizontal"
@@ -182,6 +189,7 @@
                />
        </LinearLayout>
        <LinearLayout
+           android:id="@+id/row4"
            android:layout_width="match_parent"
            android:layout_height="0dp"
            android:layout_weight="1"
diff --git a/packages/Keyguard/res/values/dimens.xml b/packages/Keyguard/res/values/dimens.xml
index 3830df7..e9cdfcd 100644
--- a/packages/Keyguard/res/values/dimens.xml
+++ b/packages/Keyguard/res/values/dimens.xml
@@ -161,4 +161,6 @@
     <dimen name="widget_big_font_size">68dp</dimen>
     <dimen name="big_font_size">120dp</dimen>
 
+    <!-- The y translation to apply at the start in appear animations. -->
+    <dimen name="appear_y_translation_start">32dp</dimen>
 </resources>
diff --git a/packages/Keyguard/src/com/android/keyguard/AppearAnimationCreator.java b/packages/Keyguard/src/com/android/keyguard/AppearAnimationCreator.java
new file mode 100644
index 0000000..0d30ea6
--- /dev/null
+++ b/packages/Keyguard/src/com/android/keyguard/AppearAnimationCreator.java
@@ -0,0 +1,29 @@
+/*
+ * 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.keyguard;
+
+import android.animation.Animator;
+import android.view.animation.Interpolator;
+
+/**
+ * An interface which can create animations when starting an appear animation with
+ * {@link com.android.keyguard.AppearAnimationUtils}
+ */
+public interface AppearAnimationCreator<T> {
+     void createAnimation(T animatedObject, long delay, long duration,
+            float startTranslationY, Interpolator interpolator, Runnable finishListener);
+}
diff --git a/packages/Keyguard/src/com/android/keyguard/AppearAnimationUtils.java b/packages/Keyguard/src/com/android/keyguard/AppearAnimationUtils.java
new file mode 100644
index 0000000..6bb1f2c
--- /dev/null
+++ b/packages/Keyguard/src/com/android/keyguard/AppearAnimationUtils.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 com.android.keyguard;
+
+import android.content.Context;
+import android.view.View;
+import android.view.animation.AnimationUtils;
+import android.view.animation.Interpolator;
+
+/**
+ * A class to make nice appear transitions for views in a tabular layout.
+ */
+public class AppearAnimationUtils implements AppearAnimationCreator<View> {
+
+    public static final long APPEAR_DURATION = 220;
+
+    private final Interpolator mLinearOutSlowIn;
+    private final float mStartTranslation;
+    private final AppearAnimationProperties mProperties = new AppearAnimationProperties();
+    private final float mDelayScale;
+
+    public AppearAnimationUtils(Context ctx) {
+        this(ctx, 1.0f, 1.0f);
+    }
+
+    public AppearAnimationUtils(Context ctx, float delayScaleFactor,
+            float translationScaleFactor) {
+        mLinearOutSlowIn = AnimationUtils.loadInterpolator(
+                ctx, android.R.interpolator.linear_out_slow_in);
+        mStartTranslation = ctx.getResources().getDimensionPixelOffset(
+                R.dimen.appear_y_translation_start) * translationScaleFactor;
+        mDelayScale = delayScaleFactor;
+    }
+
+    public void startAppearAnimation(View[][] objects, final Runnable finishListener) {
+        startAppearAnimation(objects, finishListener, this);
+    }
+
+    public <T> void startAppearAnimation(T[][] objects, final Runnable finishListener,
+            AppearAnimationCreator<T> creator) {
+        AppearAnimationProperties properties = getDelays(objects);
+        startAnimations(properties, objects, finishListener, creator);
+    }
+
+    private <T> void startAnimations(AppearAnimationProperties properties, T[][] objects,
+            final Runnable finishListener, AppearAnimationCreator creator) {;
+        if (properties.maxDelayRowIndex == -1 || properties.maxDelayColIndex == -1) {
+            finishListener.run();
+            return;
+        }
+        for (int row = 0; row < properties.delays.length; row++) {
+            long[] columns = properties.delays[row];
+            for (int col = 0; col < columns.length; col++) {
+                long delay = columns[col];
+                Runnable endRunnable = null;
+                if (properties.maxDelayRowIndex == row && properties.maxDelayColIndex == col) {
+                    endRunnable = finishListener;
+                }
+                creator.createAnimation(objects[row][col], delay, APPEAR_DURATION,
+                        mStartTranslation, mLinearOutSlowIn, endRunnable);
+            }
+        }
+
+    }
+
+    private <T> AppearAnimationProperties getDelays(T[][] items) {
+        long maxDelay = 0;
+        mProperties.maxDelayColIndex = -1;
+        mProperties.maxDelayRowIndex = -1;
+        mProperties.delays = new long[items.length][];
+        for (int row = 0; row < items.length; row++) {
+            T[] columns = items[row];
+            mProperties.delays[row] = new long[columns.length];
+            for (int col = 0; col < columns.length; col++) {
+                long delay = calculateDelay(row, col);
+                mProperties.delays[row][col] = delay;
+                if (items[row][col] != null && delay > maxDelay) {
+                    maxDelay = delay;
+                    mProperties.maxDelayColIndex = col;
+                    mProperties.maxDelayRowIndex = row;
+                }
+            }
+        }
+        return mProperties;
+    }
+
+    private long calculateDelay(int row, int col) {
+        return (long) ((row * 40 + col * (Math.pow(row, 0.4) + 0.4) * 20) * mDelayScale);
+    }
+
+    public Interpolator getInterpolator() {
+        return mLinearOutSlowIn;
+    }
+
+    public float getStartTranslation() {
+        return mStartTranslation;
+    }
+
+    @Override
+    public void createAnimation(View view, long delay, long duration, float startTranslationY,
+            Interpolator interpolator, Runnable endRunnable) {
+        if (view != null) {
+            view.setAlpha(0f);
+            view.setTranslationY(startTranslationY);
+            view.animate()
+                    .alpha(1f)
+                    .translationY(0)
+                    .setInterpolator(interpolator)
+                    .setDuration(duration)
+                    .setStartDelay(delay);
+            if (view.hasOverlappingRendering()) {
+                view.animate().withLayer();
+            }
+            if (endRunnable != null) {
+                view.animate().withEndAction(endRunnable);
+            }
+        }
+    }
+
+    public class AppearAnimationProperties {
+        public long[][] delays;
+        public int maxDelayRowIndex;
+        public int maxDelayColIndex;
+    }
+}
diff --git a/packages/Keyguard/src/com/android/keyguard/EmergencyCarrierArea.java b/packages/Keyguard/src/com/android/keyguard/EmergencyCarrierArea.java
index 6d392fc..a592db9 100644
--- a/packages/Keyguard/src/com/android/keyguard/EmergencyCarrierArea.java
+++ b/packages/Keyguard/src/com/android/keyguard/EmergencyCarrierArea.java
@@ -17,13 +17,12 @@
 package com.android.keyguard;
 
 import android.content.Context;
+import android.content.res.TypedArray;
 import android.util.AttributeSet;
 import android.view.MotionEvent;
 import android.view.View;
 import android.widget.LinearLayout;
 
-import com.android.keyguard.R;
-
 public class EmergencyCarrierArea extends LinearLayout {
 
     private CarrierText mCarrierText;
@@ -48,6 +47,7 @@
         mEmergencyButton.setOnTouchListener(new OnTouchListener(){
             @Override
             public boolean onTouch(View v, MotionEvent event) {
+                if (mCarrierText.getVisibility() != View.VISIBLE) return false;
                 switch(event.getAction()) {
                     case MotionEvent.ACTION_DOWN:
                         mCarrierText.animate().alpha(0);
@@ -59,4 +59,8 @@
                 return false;
             }});
     }
+
+    public void setCarrierTextVisible(boolean visible) {
+        mCarrierText.setVisibility(visible ? View.VISIBLE : View.GONE);
+    }
 }
diff --git a/packages/Keyguard/src/com/android/keyguard/KeyguardAccountView.java b/packages/Keyguard/src/com/android/keyguard/KeyguardAccountView.java
index f69fa5f..69abc7a 100644
--- a/packages/Keyguard/src/com/android/keyguard/KeyguardAccountView.java
+++ b/packages/Keyguard/src/com/android/keyguard/KeyguardAccountView.java
@@ -328,5 +328,10 @@
     @Override
     public void hideBouncer(int duration) {
     }
+
+    @Override
+    public void startAppearAnimation() {
+        // TODO.
+    }
 }
 
diff --git a/packages/Keyguard/src/com/android/keyguard/KeyguardFaceUnlockView.java b/packages/Keyguard/src/com/android/keyguard/KeyguardFaceUnlockView.java
index 701d15f..c9fe93c 100644
--- a/packages/Keyguard/src/com/android/keyguard/KeyguardFaceUnlockView.java
+++ b/packages/Keyguard/src/com/android/keyguard/KeyguardFaceUnlockView.java
@@ -349,4 +349,8 @@
                 hideBouncer(mSecurityMessageDisplay, mEcaView, mBouncerFrame, duration);
     }
 
+    @Override
+    public void startAppearAnimation() {
+        // TODO.
+    }
 }
diff --git a/packages/Keyguard/src/com/android/keyguard/KeyguardMessageArea.java b/packages/Keyguard/src/com/android/keyguard/KeyguardMessageArea.java
index ede23ef..d589283 100644
--- a/packages/Keyguard/src/com/android/keyguard/KeyguardMessageArea.java
+++ b/packages/Keyguard/src/com/android/keyguard/KeyguardMessageArea.java
@@ -48,35 +48,19 @@
      */
     private static final long ANNOUNCEMENT_DELAY = 250;
 
-    static final int CHARGING_ICON = 0; //R.drawable.ic_lock_idle_charging;
-    static final int BATTERY_LOW_ICON = 0; //R.drawable.ic_lock_idle_low_battery;
-
     static final int SECURITY_MESSAGE_DURATION = 5000;
     protected static final int FADE_DURATION = 750;
 
     private static final String TAG = "KeyguardMessageArea";
 
-    // are we showing battery information?
-    boolean mShowingBatteryInfo = false;
-
     // is the bouncer up?
     boolean mShowingBouncer = false;
 
-    // last known plugged in state
-    boolean mCharging = false;
-
-    // last known battery level
-    int mBatteryLevel = 100;
-
     KeyguardUpdateMonitor mUpdateMonitor;
 
     // Timeout before we reset the message to show charging/owner info
     long mTimeout = SECURITY_MESSAGE_DURATION;
 
-    // Shadowed text values
-    protected boolean mBatteryCharged;
-    protected boolean mBatteryIsLow;
-
     private Handler mHandler;
 
     CharSequence mMessage;
@@ -146,16 +130,6 @@
     }
 
     private KeyguardUpdateMonitorCallback mInfoCallback = new KeyguardUpdateMonitorCallback() {
-        @Override
-        public void onRefreshBatteryInfo(KeyguardUpdateMonitor.BatteryStatus status) {
-            mShowingBatteryInfo = status.isPluggedIn() || status.isBatteryLow();
-            mCharging = status.status == BatteryManager.BATTERY_STATUS_CHARGING
-                     || status.status == BatteryManager.BATTERY_STATUS_FULL;
-            mBatteryLevel = status.level;
-            mBatteryCharged = status.isCharged();
-            mBatteryIsLow = status.isBatteryLow();
-            update();
-        }
         public void onScreenTurnedOff(int why) {
             setSelected(false);
         };
@@ -212,7 +186,7 @@
      */
     void update() {
         MutableInt icon = new MutableInt(0);
-        CharSequence status = concat(getChargeInfo(icon), getOwnerInfo(), getCurrentMessage());
+        CharSequence status = concat(getOwnerInfo(), getCurrentMessage());
         setCompoundDrawablesWithIntrinsicBounds(icon.value, 0, 0, 0);
         setText(status);
     }
@@ -248,25 +222,6 @@
         return info;
     }
 
-    private CharSequence getChargeInfo(MutableInt icon) {
-        CharSequence string = null;
-        if (mShowingBatteryInfo && !mShowingMessage) {
-            // Battery status
-            if (mCharging) {
-                // Charging, charged or waiting to charge.
-                string = getContext().getString(mBatteryCharged
-                        ? R.string.keyguard_charged
-                        : R.string.keyguard_plugged_in, mBatteryLevel);
-                icon.value = CHARGING_ICON;
-            } else if (mBatteryIsLow) {
-                // Battery is low
-                string = getContext().getString(R.string.keyguard_low_battery);
-                icon.value = BATTERY_LOW_ICON;
-            }
-        }
-        return string;
-    }
-
     private void hideMessage(int duration, boolean thenUpdate) {
         if (duration > 0) {
             Animator anim = ObjectAnimator.ofFloat(this, "alpha", 0f);
diff --git a/packages/Keyguard/src/com/android/keyguard/KeyguardPINView.java b/packages/Keyguard/src/com/android/keyguard/KeyguardPINView.java
index ca2d615..1f3c176 100644
--- a/packages/Keyguard/src/com/android/keyguard/KeyguardPINView.java
+++ b/packages/Keyguard/src/com/android/keyguard/KeyguardPINView.java
@@ -22,6 +22,7 @@
 import android.text.method.DigitsKeyListener;
 import android.util.AttributeSet;
 import android.view.View;
+import android.view.ViewGroup;
 import android.widget.TextView.OnEditorActionListener;
 
 /**
@@ -30,12 +31,21 @@
 public class KeyguardPINView extends KeyguardAbsKeyInputView
         implements KeyguardSecurityView, OnEditorActionListener, TextWatcher {
 
+    private final AppearAnimationUtils mAppearAnimationUtils;
+    private ViewGroup mKeyguardBouncerFrame;
+    private ViewGroup mRow0;
+    private ViewGroup mRow1;
+    private ViewGroup mRow2;
+    private ViewGroup mRow3;
+    private View mDivider;
+
     public KeyguardPINView(Context context) {
         this(context, null);
     }
 
     public KeyguardPINView(Context context, AttributeSet attrs) {
         super(context, attrs);
+        mAppearAnimationUtils = new AppearAnimationUtils(context);
     }
 
     protected void resetState() {
@@ -56,6 +66,12 @@
     protected void onFinishInflate() {
         super.onFinishInflate();
 
+        mKeyguardBouncerFrame = (ViewGroup) findViewById(R.id.keyguard_bouncer_frame);
+        mRow0 = (ViewGroup) findViewById(R.id.row0);
+        mRow1 = (ViewGroup) findViewById(R.id.row1);
+        mRow2 = (ViewGroup) findViewById(R.id.row2);
+        mRow3 = (ViewGroup) findViewById(R.id.row3);
+        mDivider = findViewById(R.id.divider);
         final View ok = findViewById(R.id.key_enter);
         if (ok != null) {
             ok.setOnClickListener(new View.OnClickListener() {
@@ -114,4 +130,48 @@
     public int getWrongPasswordStringId() {
         return R.string.kg_wrong_pin;
     }
+
+    @Override
+    public void startAppearAnimation() {
+        enableClipping(false);
+        setTranslationY(mAppearAnimationUtils.getStartTranslation());
+        animate()
+                .setDuration(500)
+                .setInterpolator(mAppearAnimationUtils.getInterpolator())
+                .translationY(0);
+        mAppearAnimationUtils.startAppearAnimation(new View[][] {
+                new View[] {
+                        mRow0, null, null
+                },
+                new View[] {
+                        findViewById(R.id.key1), findViewById(R.id.key2), findViewById(R.id.key3)
+                },
+                new View[] {
+                        findViewById(R.id.key4), findViewById(R.id.key5), findViewById(R.id.key6)
+                },
+                new View[] {
+                        findViewById(R.id.key7), findViewById(R.id.key8), findViewById(R.id.key9)
+                },
+                new View[] {
+                        null, findViewById(R.id.key0), findViewById(R.id.key_enter)
+                },
+                new View[] {
+                        null, mEcaView, null
+                }},
+                new Runnable() {
+                    @Override
+                    public void run() {
+                        enableClipping(true);
+                    }
+                });
+    }
+
+    private void enableClipping(boolean enable) {
+        mKeyguardBouncerFrame.setClipToPadding(enable);
+        mKeyguardBouncerFrame.setClipChildren(enable);
+        mRow1.setClipToPadding(enable);
+        mRow2.setClipToPadding(enable);
+        mRow3.setClipToPadding(enable);
+        setClipChildren(enable);
+    }
 }
diff --git a/packages/Keyguard/src/com/android/keyguard/KeyguardPasswordView.java b/packages/Keyguard/src/com/android/keyguard/KeyguardPasswordView.java
index e733afc..0c385da 100644
--- a/packages/Keyguard/src/com/android/keyguard/KeyguardPasswordView.java
+++ b/packages/Keyguard/src/com/android/keyguard/KeyguardPasswordView.java
@@ -198,4 +198,11 @@
     public int getWrongPasswordStringId() {
         return R.string.kg_wrong_password;
     }
+
+    @Override
+    public void startAppearAnimation() {
+        // TODO: Fancy animation.
+        setAlpha(0);
+        animate().alpha(1).withLayer().setDuration(200);
+    }
 }
diff --git a/packages/Keyguard/src/com/android/keyguard/KeyguardPatternView.java b/packages/Keyguard/src/com/android/keyguard/KeyguardPatternView.java
index 98122fc..e6de72f 100644
--- a/packages/Keyguard/src/com/android/keyguard/KeyguardPatternView.java
+++ b/packages/Keyguard/src/com/android/keyguard/KeyguardPatternView.java
@@ -21,6 +21,9 @@
 import android.accounts.AccountManagerFuture;
 import android.accounts.AuthenticatorException;
 import android.accounts.OperationCanceledException;
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.animation.ValueAnimator;
 import android.content.Context;
 import android.graphics.Rect;
 import android.graphics.drawable.Drawable;
@@ -28,10 +31,13 @@
 import android.os.CountDownTimer;
 import android.os.SystemClock;
 import android.os.UserHandle;
+import android.text.TextUtils;
 import android.util.AttributeSet;
 import android.util.Log;
 import android.view.MotionEvent;
 import android.view.View;
+import android.view.ViewGroup;
+import android.view.animation.Interpolator;
 import android.widget.Button;
 import android.widget.LinearLayout;
 
@@ -41,7 +47,8 @@
 import java.io.IOException;
 import java.util.List;
 
-public class KeyguardPatternView extends LinearLayout implements KeyguardSecurityView {
+public class KeyguardPatternView extends LinearLayout implements KeyguardSecurityView,
+        AppearAnimationCreator<LockPatternView.CellState> {
 
     private static final String TAG = "SecurityPatternView";
     private static final boolean DEBUG = KeyguardConstants.DEBUG;
@@ -59,6 +66,7 @@
     private static final int MIN_PATTERN_BEFORE_POKE_WAKELOCK = 2;
 
     private final KeyguardUpdateMonitor mKeyguardUpdateMonitor;
+    private final AppearAnimationUtils mAppearAnimationUtils;
 
     private CountDownTimer mCountdownTimer = null;
     private LockPatternUtils mLockPatternUtils;
@@ -87,6 +95,8 @@
     private SecurityMessageDisplay mSecurityMessageDisplay;
     private View mEcaView;
     private Drawable mBouncerFrame;
+    private ViewGroup mKeyguardBouncerFrame;
+    private KeyguardMessageArea mHelpMessage;
 
     enum FooterMode {
         Normal,
@@ -101,6 +111,8 @@
     public KeyguardPatternView(Context context, AttributeSet attrs) {
         super(context, attrs);
         mKeyguardUpdateMonitor = KeyguardUpdateMonitor.getInstance(mContext);
+        mAppearAnimationUtils = new AppearAnimationUtils(context, 1.5f /* delayScale */,
+                2.0f /* transitionScale */);
     }
 
     public void setKeyguardCallback(KeyguardSecurityCallback callback) {
@@ -148,6 +160,9 @@
         if (bouncerFrameView != null) {
             mBouncerFrame = bouncerFrameView.getBackground();
         }
+
+        mKeyguardBouncerFrame = (ViewGroup) findViewById(R.id.keyguard_bouncer_frame);
+        mHelpMessage = (KeyguardMessageArea) findViewById(R.id.keyguard_message_area);
     }
 
     private void updateFooter(FooterMode mode) {
@@ -400,4 +415,72 @@
         KeyguardSecurityViewHelper.
                 hideBouncer(mSecurityMessageDisplay, mEcaView, mBouncerFrame, duration);
     }
+
+    @Override
+    public void startAppearAnimation() {
+        enableClipping(false);
+        mAppearAnimationUtils.startAppearAnimation(
+                mLockPatternView.getCellStates(),
+                new Runnable() {
+                    @Override
+                    public void run() {
+                        enableClipping(true);
+                    }
+                },
+                this);
+        if (!TextUtils.isEmpty(mHelpMessage.getText())) {
+            mAppearAnimationUtils.createAnimation(mHelpMessage, 0,
+                    AppearAnimationUtils.APPEAR_DURATION,
+                    mAppearAnimationUtils.getStartTranslation(),
+                    mAppearAnimationUtils.getInterpolator(),
+                    null /* finishRunnable */);
+        }
+    }
+
+    private void enableClipping(boolean enable) {
+        setClipChildren(enable);
+        mKeyguardBouncerFrame.setClipToPadding(enable);
+        mKeyguardBouncerFrame.setClipChildren(enable);
+    }
+
+    @Override
+    public void createAnimation(final LockPatternView.CellState animatedCell, long delay,
+            long duration, float startTranslationY, Interpolator interpolator,
+            final Runnable finishListener) {
+        animatedCell.scale = 0.0f;
+        animatedCell.translateY = startTranslationY;
+        ValueAnimator animator = ValueAnimator.ofFloat(startTranslationY, 0.0f);
+        animator.setInterpolator(interpolator);
+        animator.setDuration(duration);
+        animator.setStartDelay(delay);
+        animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
+            @Override
+            public void onAnimationUpdate(ValueAnimator animation) {
+                float animatedFraction = animation.getAnimatedFraction();
+                animatedCell.scale = animatedFraction;
+                animatedCell.translateY = (float) animation.getAnimatedValue();
+                mLockPatternView.invalidate();
+            }
+        });
+        if (finishListener != null) {
+            animator.addListener(new AnimatorListenerAdapter() {
+                @Override
+                public void onAnimationEnd(Animator animation) {
+                    finishListener.run();
+                }
+            });
+
+            // Also animate the Emergency call
+            mAppearAnimationUtils.createAnimation(mEcaView, delay, duration, startTranslationY,
+            interpolator, null);
+
+            // And the forgot pattern button
+            if (mForgotPatternButton.getVisibility() == View.VISIBLE) {
+                mAppearAnimationUtils.createAnimation(mForgotPatternButton, delay, duration,
+                        startTranslationY, interpolator, null);
+            }
+        }
+        animator.start();
+        mLockPatternView.invalidate();
+    }
 }
diff --git a/packages/Keyguard/src/com/android/keyguard/KeyguardSecurityContainer.java b/packages/Keyguard/src/com/android/keyguard/KeyguardSecurityContainer.java
index 94edc07..382cbec 100644
--- a/packages/Keyguard/src/com/android/keyguard/KeyguardSecurityContainer.java
+++ b/packages/Keyguard/src/com/android/keyguard/KeyguardSecurityContainer.java
@@ -81,6 +81,10 @@
         getSecurityView(mCurrentSecuritySelection).onPause();
     }
 
+    public void startAppearAnimation() {
+        getSecurityView(mCurrentSecuritySelection).startAppearAnimation();
+    }
+
     void updateSecurityViews(boolean isBouncing) {
         int children = mSecurityViewFlipper.getChildCount();
         for (int i = 0; i < children; i++) {
diff --git a/packages/Keyguard/src/com/android/keyguard/KeyguardSecurityView.java b/packages/Keyguard/src/com/android/keyguard/KeyguardSecurityView.java
index dfeacf3..86bd877 100644
--- a/packages/Keyguard/src/com/android/keyguard/KeyguardSecurityView.java
+++ b/packages/Keyguard/src/com/android/keyguard/KeyguardSecurityView.java
@@ -84,4 +84,9 @@
      * @param duration millisends for the transisiton animation.
      */
     void hideBouncer(int duration);
+
+    /**
+     * Starts the animation which should run when the security view appears.
+     */
+    void startAppearAnimation();
 }
diff --git a/packages/Keyguard/src/com/android/keyguard/KeyguardSecurityViewFlipper.java b/packages/Keyguard/src/com/android/keyguard/KeyguardSecurityViewFlipper.java
index 07239d1..178ca5e 100644
--- a/packages/Keyguard/src/com/android/keyguard/KeyguardSecurityViewFlipper.java
+++ b/packages/Keyguard/src/com/android/keyguard/KeyguardSecurityViewFlipper.java
@@ -159,6 +159,14 @@
     }
 
     @Override
+    public void startAppearAnimation() {
+        KeyguardSecurityView ksv = getSecurityView();
+        if (ksv != null) {
+            ksv.startAppearAnimation();
+        }
+    }
+
+    @Override
     protected boolean checkLayoutParams(ViewGroup.LayoutParams p) {
         return p instanceof LayoutParams;
     }
diff --git a/packages/Keyguard/src/com/android/keyguard/KeyguardSelectorView.java b/packages/Keyguard/src/com/android/keyguard/KeyguardSelectorView.java
index 03e7b07..98baa04 100644
--- a/packages/Keyguard/src/com/android/keyguard/KeyguardSelectorView.java
+++ b/packages/Keyguard/src/com/android/keyguard/KeyguardSelectorView.java
@@ -244,4 +244,9 @@
         KeyguardSecurityViewHelper.
                 hideBouncer(mSecurityMessageDisplay, mFadeView, mBouncerFrame, duration);
     }
+
+    @Override
+    public void startAppearAnimation() {
+        // noop.
+    }
 }
diff --git a/packages/Keyguard/src/com/android/keyguard/KeyguardSimPinView.java b/packages/Keyguard/src/com/android/keyguard/KeyguardSimPinView.java
index 4791956..09c4e7c 100644
--- a/packages/Keyguard/src/com/android/keyguard/KeyguardSimPinView.java
+++ b/packages/Keyguard/src/com/android/keyguard/KeyguardSimPinView.java
@@ -135,6 +135,9 @@
         mPasswordEntry.requestFocus();
 
         mSecurityMessageDisplay.setTimeout(0); // don't show ownerinfo/charging status by default
+        if (mEcaView instanceof EmergencyCarrierArea) {
+            ((EmergencyCarrierArea) mEcaView).setCarrierTextVisible(true);
+        }
     }
 
     @Override
@@ -270,5 +273,10 @@
             mCheckSimPinThread.start();
         }
     }
+
+    @Override
+    public void startAppearAnimation() {
+        // noop.
+    }
 }
 
diff --git a/packages/Keyguard/src/com/android/keyguard/KeyguardSimPukView.java b/packages/Keyguard/src/com/android/keyguard/KeyguardSimPukView.java
index b9c7f51..6215d34 100644
--- a/packages/Keyguard/src/com/android/keyguard/KeyguardSimPukView.java
+++ b/packages/Keyguard/src/com/android/keyguard/KeyguardSimPukView.java
@@ -186,6 +186,9 @@
         mPasswordEntry.requestFocus();
 
         mSecurityMessageDisplay.setTimeout(0); // don't show ownerinfo/charging status by default
+        if (mEcaView instanceof EmergencyCarrierArea) {
+            ((EmergencyCarrierArea) mEcaView).setCarrierTextVisible(true);
+        }
     }
 
     @Override
@@ -339,6 +342,11 @@
     protected void verifyPasswordAndUnlock() {
         mStateMachine.next();
     }
+
+    @Override
+    public void startAppearAnimation() {
+        // noop.
+    }
 }
 
 
diff --git a/packages/Keyguard/src/com/android/keyguard/KeyguardViewBase.java b/packages/Keyguard/src/com/android/keyguard/KeyguardViewBase.java
index 48b7be9..3e444fa 100644
--- a/packages/Keyguard/src/com/android/keyguard/KeyguardViewBase.java
+++ b/packages/Keyguard/src/com/android/keyguard/KeyguardViewBase.java
@@ -241,6 +241,13 @@
     }
 
     /**
+     * Starts the animation when the Keyguard gets shown.
+     */
+    public void startAppearAnimation() {
+        mSecurityContainer.startAppearAnimation();
+    }
+
+    /**
      * Verify that the user can get past the keyguard securely.  This is called,
      * for example, when the phone disables the keyguard but then wants to launch
      * something else that requires secure access.
diff --git a/packages/Keyguard/src/com/android/keyguard/analytics/KeyguardAnalytics.java b/packages/Keyguard/src/com/android/keyguard/analytics/KeyguardAnalytics.java
deleted file mode 100644
index 20af2f1..0000000
--- a/packages/Keyguard/src/com/android/keyguard/analytics/KeyguardAnalytics.java
+++ /dev/null
@@ -1,278 +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.keyguard.analytics;
-
-import com.google.protobuf.nano.CodedOutputByteBufferNano;
-import com.google.protobuf.nano.MessageNano;
-
-import android.content.Context;
-import android.hardware.Sensor;
-import android.hardware.SensorEvent;
-import android.hardware.SensorEventListener;
-import android.hardware.SensorManager;
-import android.os.AsyncTask;
-import android.util.Log;
-import android.view.MotionEvent;
-
-import java.io.File;
-import java.io.FileOutputStream;
-import java.io.IOException;
-import java.io.OutputStream;
-
-/**
- * Tracks sessions, touch and sensor events in Keyguard.
- *
- * A session starts when the user is presented with the Keyguard and ends when the Keyguard is no
- * longer visible to the user.
- */
-public class KeyguardAnalytics implements SensorEventListener {
-
-    private static final boolean DEBUG = false;
-    private static final String TAG = "KeyguardAnalytics";
-    private static final long TIMEOUT_MILLIS = 11000; // 11 seconds.
-
-    private static final int[] SENSORS = new int[] {
-            Sensor.TYPE_ACCELEROMETER,
-            Sensor.TYPE_GYROSCOPE,
-            Sensor.TYPE_PROXIMITY,
-            Sensor.TYPE_LIGHT,
-            Sensor.TYPE_ROTATION_VECTOR,
-    };
-
-    private Session mCurrentSession = null;
-    // Err on the side of caution, so logging is not started after a crash even tough the screen
-    // is off.
-    private boolean mScreenOn = false;
-    private boolean mHidden = false;
-
-    private final SensorManager mSensorManager;
-    private final SessionTypeAdapter mSessionTypeAdapter;
-    private final File mAnalyticsFile;
-
-    public KeyguardAnalytics(Context context, SessionTypeAdapter sessionTypeAdapter,
-            File analyticsFile) {
-        mSensorManager = (SensorManager) context.getSystemService(Context.SENSOR_SERVICE);
-        mSessionTypeAdapter = sessionTypeAdapter;
-        mAnalyticsFile = analyticsFile;
-    }
-
-    public Callback getCallback() {
-        return mCallback;
-    }
-
-    public interface Callback {
-        public void onShow();
-        public void onHide();
-        public void onScreenOn();
-        public void onScreenOff();
-        public boolean onTouchEvent(MotionEvent ev, int width, int height);
-        public void onSetOccluded(boolean hidden);
-    }
-
-    public interface SessionTypeAdapter {
-        public int getSessionType();
-    }
-
-    private void sessionEntrypoint() {
-        if (mCurrentSession == null && mScreenOn && !mHidden) {
-            onSessionStart();
-        }
-    }
-
-    private void sessionExitpoint(int result) {
-        if (mCurrentSession != null) {
-            onSessionEnd(result);
-        }
-    }
-
-    private void onSessionStart() {
-        int type = mSessionTypeAdapter.getSessionType();
-        mCurrentSession = new Session(System.currentTimeMillis(), System.nanoTime(), type);
-        if (type == Session.TYPE_KEYGUARD_SECURE) {
-            mCurrentSession.setRedactTouchEvents();
-        }
-        for (int sensorType : SENSORS) {
-            Sensor s = mSensorManager.getDefaultSensor(sensorType);
-            if (s != null) {
-                mSensorManager.registerListener(this, s, SensorManager.SENSOR_DELAY_GAME);
-            }
-        }
-        if (DEBUG) {
-            Log.d(TAG, "onSessionStart()");
-        }
-    }
-
-    private void onSessionEnd(int result) {
-        if (DEBUG) {
-            Log.d(TAG, String.format("onSessionEnd(success=%d)", result));
-        }
-        mSensorManager.unregisterListener(this);
-
-        Session session = mCurrentSession;
-        mCurrentSession = null;
-
-        session.end(System.currentTimeMillis(), result);
-        queueSession(session);
-    }
-
-    private void queueSession(final Session currentSession) {
-        if (DEBUG) {
-            Log.i(TAG, "Saving session.");
-        }
-        new AsyncTask<Void, Void, Void>() {
-            @Override
-            protected Void doInBackground(Void... params) {
-                try {
-                    byte[] b = writeDelimitedProto(currentSession.toProto());
-                    OutputStream os = new FileOutputStream(mAnalyticsFile, true /* append */);
-                    if (DEBUG) {
-                        Log.d(TAG, String.format("Serialized size: %d kB.", b.length / 1024));
-                    }
-                    try {
-                        os.write(b);
-                        os.flush();
-                    } finally {
-                        try {
-                            os.close();
-                        } catch (IOException e) {
-                            Log.e(TAG, "Exception while closing file", e);
-                        }
-                    }
-                } catch (IOException e) {
-                    Log.e(TAG, "Exception while writing file", e);
-                }
-                return null;
-            }
-
-            private byte[] writeDelimitedProto(MessageNano proto)
-                    throws IOException {
-                byte[] result = new byte[CodedOutputByteBufferNano.computeMessageSizeNoTag(proto)];
-                CodedOutputByteBufferNano ob = CodedOutputByteBufferNano.newInstance(result);
-                ob.writeMessageNoTag(proto);
-                ob.checkNoSpaceLeft();
-                return result;
-            }
-        }.executeOnExecutor(AsyncTask.SERIAL_EXECUTOR);
-    }
-
-    @Override
-    public synchronized void onSensorChanged(SensorEvent event) {
-        if (false) {
-            Log.v(TAG, String.format(
-                    "onSensorChanged(name=%s, values[0]=%f)",
-                    event.sensor.getName(), event.values[0]));
-        }
-        if (mCurrentSession != null) {
-            mCurrentSession.addSensorEvent(event, System.nanoTime());
-            enforceTimeout();
-        }
-    }
-
-    private void enforceTimeout() {
-        if (System.currentTimeMillis() - mCurrentSession.getStartTimestampMillis()
-                > TIMEOUT_MILLIS) {
-            onSessionEnd(Session.RESULT_UNKNOWN);
-            if (DEBUG) {
-                Log.i(TAG, "Analytics timed out.");
-            }
-        }
-    }
-
-    @Override
-    public void onAccuracyChanged(Sensor sensor, int accuracy) {
-    }
-
-    private final Callback mCallback = new Callback() {
-        @Override
-        public void onShow() {
-            if (DEBUG) {
-                Log.d(TAG, "onShow()");
-            }
-            synchronized (KeyguardAnalytics.this) {
-                sessionEntrypoint();
-            }
-        }
-
-        @Override
-        public void onHide() {
-            if (DEBUG) {
-                Log.d(TAG, "onHide()");
-            }
-            synchronized (KeyguardAnalytics.this) {
-                sessionExitpoint(Session.RESULT_SUCCESS);
-            }
-        }
-
-        @Override
-        public void onScreenOn() {
-            if (DEBUG) {
-                Log.d(TAG, "onScreenOn()");
-            }
-            synchronized (KeyguardAnalytics.this) {
-                mScreenOn = true;
-                sessionEntrypoint();
-            }
-        }
-
-        @Override
-        public void onScreenOff() {
-            if (DEBUG) {
-                Log.d(TAG, "onScreenOff()");
-            }
-            synchronized (KeyguardAnalytics.this) {
-                mScreenOn = false;
-                sessionExitpoint(Session.RESULT_FAILURE);
-            }
-        }
-
-        @Override
-        public boolean onTouchEvent(MotionEvent ev, int width, int height) {
-            if (DEBUG) {
-                Log.v(TAG, "onTouchEvent(ev.action="
-                        + MotionEvent.actionToString(ev.getAction()) + ")");
-            }
-            synchronized (KeyguardAnalytics.this) {
-                if (mCurrentSession != null) {
-                    mCurrentSession.addMotionEvent(ev);
-                    mCurrentSession.setTouchArea(width, height);
-                    enforceTimeout();
-                }
-            }
-            return true;
-        }
-
-        @Override
-        public void onSetOccluded(boolean hidden) {
-            synchronized (KeyguardAnalytics.this) {
-                if (hidden != mHidden) {
-                    if (DEBUG) {
-                        Log.d(TAG, "onSetOccluded(" + hidden + ")");
-                    }
-                    mHidden = hidden;
-                    if (hidden) {
-                        // Could have gone to camera on purpose / by falsing or an app could have
-                        // launched on top of the lockscreen.
-                        sessionExitpoint(Session.RESULT_UNKNOWN);
-                    } else {
-                        sessionEntrypoint();
-                    }
-                }
-            }
-        }
-    };
-
-}
diff --git a/packages/Keyguard/src/com/android/keyguard/analytics/PointerTracker.java b/packages/Keyguard/src/com/android/keyguard/analytics/PointerTracker.java
deleted file mode 100644
index e68f751..0000000
--- a/packages/Keyguard/src/com/android/keyguard/analytics/PointerTracker.java
+++ /dev/null
@@ -1,109 +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.keyguard.analytics;
-
-import android.graphics.RectF;
-import android.util.FloatMath;
-import android.util.SparseArray;
-import android.view.MotionEvent;
-
-import java.util.HashMap;
-import java.util.Map;
-
-import static com.android.keyguard.analytics.KeyguardAnalyticsProtos.Session.TouchEvent.BoundingBox;
-
-/**
- * Takes motion events and tracks the length and bounding box of each pointer gesture as well as
- * the bounding box of the whole gesture.
- */
-public class PointerTracker {
-    private SparseArray<Pointer> mPointerInfoMap = new SparseArray<Pointer>();
-    private RectF mTotalBoundingBox = new RectF();
-
-    public void addMotionEvent(MotionEvent ev) {
-        if (ev.getActionMasked() == MotionEvent.ACTION_DOWN) {
-            float x = ev.getX();
-            float y = ev.getY();
-            mTotalBoundingBox.set(x, y, x, y);
-        }
-        for (int i = 0; i < ev.getPointerCount(); i++) {
-            int id = ev.getPointerId(i);
-            Pointer pointer = getPointer(id);
-            float x = ev.getX(i);
-            float y = ev.getY(i);
-            boolean down = ev.getActionMasked() == MotionEvent.ACTION_DOWN
-                    || (ev.getActionMasked() == MotionEvent.ACTION_POINTER_DOWN
-                            && ev.getActionIndex() == i);
-            pointer.addPoint(x, y, down);
-            mTotalBoundingBox.union(x, y);
-        }
-    }
-
-    public float getPointerLength(int id) {
-        return getPointer(id).length;
-    }
-
-    public BoundingBox getBoundingBox() {
-        return boundingBoxFromRect(mTotalBoundingBox);
-    }
-
-    public BoundingBox getPointerBoundingBox(int id) {
-        return boundingBoxFromRect(getPointer(id).boundingBox);
-    }
-
-    private BoundingBox boundingBoxFromRect(RectF f) {
-        BoundingBox bb = new BoundingBox();
-        bb.setHeight(f.height());
-        bb.setWidth(f.width());
-        return bb;
-    }
-
-    private Pointer getPointer(int id) {
-        Pointer p = mPointerInfoMap.get(id);
-        if (p == null) {
-            p = new Pointer();
-            mPointerInfoMap.put(id, p);
-        }
-        return p;
-    }
-
-    private static class Pointer {
-        public float length;
-        public final RectF boundingBox = new RectF();
-
-        private float mLastX;
-        private float mLastY;
-
-        public void addPoint(float x, float y, boolean down) {
-            float deltaX;
-            float deltaY;
-            if (down) {
-                boundingBox.set(x, y, x, y);
-                length = 0f;
-                deltaX = 0;
-                deltaY = 0;
-            } else {
-                deltaX = x - mLastX;
-                deltaY = y - mLastY;
-            }
-            mLastX = x;
-            mLastY = y;
-            length += FloatMath.sqrt(deltaX * deltaX + deltaY * deltaY);
-            boundingBox.union(x, y);
-        }
-    }
-}
diff --git a/packages/Keyguard/src/com/android/keyguard/analytics/Session.java b/packages/Keyguard/src/com/android/keyguard/analytics/Session.java
deleted file mode 100644
index 05f9165..0000000
--- a/packages/Keyguard/src/com/android/keyguard/analytics/Session.java
+++ /dev/null
@@ -1,220 +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.keyguard.analytics;
-
-import android.os.Build;
-import android.util.Slog;
-import android.view.MotionEvent;
-
-import java.util.ArrayList;
-
-import static com.android.keyguard.analytics.KeyguardAnalyticsProtos.Session.SensorEvent;
-import static com.android.keyguard.analytics.KeyguardAnalyticsProtos.Session.TouchEvent;
-
-/**
- * Records data about one keyguard session.
- *
- * The recorded data contains start and end of the session, whether it unlocked the device
- * successfully, sensor data and touch data.
- *
- * If the keyguard is secure, the recorded touch data will correlate or contain the user pattern or
- * PIN. If this is not desired, the touch coordinates can be redacted before serialization.
- */
-public class Session {
-
-    private static final String TAG = "KeyguardAnalytics";
-    private static final boolean DEBUG = false;
-
-    /**
-     * The user has failed to unlock the device in this session.
-     */
-    public static final int RESULT_FAILURE = KeyguardAnalyticsProtos.Session.FAILURE;
-    /**
-     * The user has succeeded in unlocking the device in this session.
-     */
-    public static final int RESULT_SUCCESS = KeyguardAnalyticsProtos.Session.SUCCESS;
-
-    /**
-     * It is unknown how the session with the keyguard ended.
-     */
-    public static final int RESULT_UNKNOWN = KeyguardAnalyticsProtos.Session.UNKNOWN;
-
-    /**
-     * This session took place on an insecure keyguard.
-     */
-    public static final int TYPE_KEYGUARD_INSECURE
-            = KeyguardAnalyticsProtos.Session.KEYGUARD_INSECURE;
-
-    /**
-     * This session took place on an secure keyguard.
-     */
-    public static final int TYPE_KEYGUARD_SECURE
-            = KeyguardAnalyticsProtos.Session.KEYGUARD_SECURE;
-
-    /**
-     * This session took place during a fake wake up of the device.
-     */
-    public static final int TYPE_RANDOM_WAKEUP = KeyguardAnalyticsProtos.Session.RANDOM_WAKEUP;
-
-
-    private final PointerTracker mPointerTracker = new PointerTracker();
-
-    private final long mStartTimestampMillis;
-    private final long mStartSystemTimeNanos;
-    private final int mType;
-
-    private boolean mRedactTouchEvents;
-    private ArrayList<TouchEvent> mMotionEvents = new ArrayList<TouchEvent>(200);
-    private ArrayList<SensorEvent> mSensorEvents = new ArrayList<SensorEvent>(600);
-    private int mTouchAreaHeight;
-    private int mTouchAreaWidth;
-
-    private long mEndTimestampMillis;
-    private int mResult;
-    private boolean mEnded;
-
-    public Session(long startTimestampMillis, long startSystemTimeNanos, int type) {
-        mStartTimestampMillis = startTimestampMillis;
-        mStartSystemTimeNanos = startSystemTimeNanos;
-        mType = type;
-    }
-
-    public void end(long endTimestampMillis, int result) {
-        mEnded = true;
-        mEndTimestampMillis = endTimestampMillis;
-        mResult = result;
-    }
-
-    public void addMotionEvent(MotionEvent motionEvent) {
-        if (mEnded) {
-            return;
-        }
-        mPointerTracker.addMotionEvent(motionEvent);
-        mMotionEvents.add(protoFromMotionEvent(motionEvent));
-    }
-
-    public void addSensorEvent(android.hardware.SensorEvent eventOrig, long systemTimeNanos) {
-        if (mEnded) {
-            return;
-        }
-        SensorEvent event = protoFromSensorEvent(eventOrig, systemTimeNanos);
-        mSensorEvents.add(event);
-        if (DEBUG) {
-            Slog.v(TAG, String.format("addSensorEvent(name=%s, values[0]=%f",
-                    event.getType(), event.values[0]));
-        }
-    }
-
-    @Override
-    public String toString() {
-        final StringBuilder sb = new StringBuilder("Session{");
-        sb.append("mType=").append(mType);
-        sb.append(", mStartTimestampMillis=").append(mStartTimestampMillis);
-        sb.append(", mStartSystemTimeNanos=").append(mStartSystemTimeNanos);
-        sb.append(", mEndTimestampMillis=").append(mEndTimestampMillis);
-        sb.append(", mResult=").append(mResult);
-        sb.append(", mRedactTouchEvents=").append(mRedactTouchEvents);
-        sb.append(", mTouchAreaHeight=").append(mTouchAreaHeight);
-        sb.append(", mTouchAreaWidth=").append(mTouchAreaWidth);
-        sb.append(", mMotionEvents=[size=").append(mMotionEvents.size()).append("]");
-        sb.append(", mSensorEvents=[size=").append(mSensorEvents.size()).append("]");
-        sb.append('}');
-        return sb.toString();
-    }
-
-    public KeyguardAnalyticsProtos.Session toProto() {
-        KeyguardAnalyticsProtos.Session proto = new KeyguardAnalyticsProtos.Session();
-        proto.setStartTimestampMillis(mStartTimestampMillis);
-        proto.setDurationMillis(mEndTimestampMillis - mStartTimestampMillis);
-        proto.setBuild(Build.FINGERPRINT);
-        proto.setResult(mResult);
-        proto.sensorEvents = mSensorEvents.toArray(proto.sensorEvents);
-        proto.touchEvents = mMotionEvents.toArray(proto.touchEvents);
-        proto.setTouchAreaWidth(mTouchAreaWidth);
-        proto.setTouchAreaHeight(mTouchAreaHeight);
-        proto.setType(mType);
-        if (mRedactTouchEvents) {
-            redactTouchEvents(proto.touchEvents);
-        }
-        return proto;
-    }
-
-    private void redactTouchEvents(TouchEvent[] touchEvents) {
-        for (int i = 0; i < touchEvents.length; i++) {
-            TouchEvent t = touchEvents[i];
-            for (int j = 0; j < t.pointers.length; j++) {
-                TouchEvent.Pointer p = t.pointers[j];
-                p.clearX();
-                p.clearY();
-            }
-            t.setRedacted(true);
-        }
-    }
-
-    private SensorEvent protoFromSensorEvent(android.hardware.SensorEvent ev, long sysTimeNanos) {
-        SensorEvent proto = new SensorEvent();
-        proto.setType(ev.sensor.getType());
-        proto.setTimeOffsetNanos(sysTimeNanos - mStartSystemTimeNanos);
-        proto.setTimestamp(ev.timestamp);
-        proto.values = ev.values.clone();
-        return proto;
-    }
-
-    private TouchEvent protoFromMotionEvent(MotionEvent ev) {
-        int count = ev.getPointerCount();
-        TouchEvent proto = new TouchEvent();
-        proto.setTimeOffsetNanos(ev.getEventTimeNano() - mStartSystemTimeNanos);
-        proto.setAction(ev.getActionMasked());
-        proto.setActionIndex(ev.getActionIndex());
-        proto.pointers = new TouchEvent.Pointer[count];
-        for (int i = 0; i < count; i++) {
-            TouchEvent.Pointer p = new TouchEvent.Pointer();
-            p.setX(ev.getX(i));
-            p.setY(ev.getY(i));
-            p.setSize(ev.getSize(i));
-            p.setPressure(ev.getPressure(i));
-            p.setId(ev.getPointerId(i));
-            proto.pointers[i] = p;
-            if ((ev.getActionMasked() == MotionEvent.ACTION_POINTER_UP && ev.getActionIndex() == i)
-                    || ev.getActionMasked() == MotionEvent.ACTION_UP) {
-                p.boundingBox = mPointerTracker.getPointerBoundingBox(p.getId());
-                p.setLength(mPointerTracker.getPointerLength(p.getId()));
-            }
-        }
-        if (ev.getActionMasked() == MotionEvent.ACTION_UP) {
-            proto.boundingBox = mPointerTracker.getBoundingBox();
-        }
-        return proto;
-    }
-
-    /**
-     * Discards the x / y coordinates of the touch events on serialization. Retained are the
-     * size of the individual and overall bounding boxes and the length of each pointer's gesture.
-     */
-    public void setRedactTouchEvents() {
-        mRedactTouchEvents = true;
-    }
-
-    public void setTouchArea(int width, int height) {
-        mTouchAreaWidth = width;
-        mTouchAreaHeight = height;
-    }
-
-    public long getStartTimestampMillis() {
-        return mStartTimestampMillis;
-    }
-}
diff --git a/packages/Keyguard/src/com/android/keyguard/analytics/keyguard_analytics.proto b/packages/Keyguard/src/com/android/keyguard/analytics/keyguard_analytics.proto
deleted file mode 100644
index 68b1590..0000000
--- a/packages/Keyguard/src/com/android/keyguard/analytics/keyguard_analytics.proto
+++ /dev/null
@@ -1,102 +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
- */
-
-syntax = "proto2";
-
-package keyguard;
-
-option java_package = "com.android.keyguard.analytics";
-option java_outer_classname = "KeyguardAnalyticsProtos";
-
-message Session {
-    message TouchEvent {
-        message BoundingBox {
-            optional float width = 1;
-            optional float height = 2;
-        }
-
-        enum Action {
-            // Keep in sync with MotionEvent.
-            DOWN = 0;
-            UP = 1;
-            MOVE = 2;
-            CANCEL = 3;
-            OUTSIDE = 4;
-            POINTER_DOWN = 5;
-            POINTER_UP = 6;
-        }
-
-        message Pointer {
-            optional float x = 1;
-            optional float y = 2;
-            optional float size = 3;
-            optional float pressure = 4;
-            optional int32 id = 5;
-            optional float length = 6;
-            // Bounding box of the pointer. Only set on UP or POINTER_UP event of this pointer.
-            optional BoundingBox boundingBox = 7;
-        }
-
-        optional uint64 timeOffsetNanos = 1;
-        optional Action action = 2;
-        optional int32 actionIndex = 3;
-        repeated Pointer pointers = 4;
-        /* If true, the the x / y coordinates of the touch events were redacted. Retained are the
-           size of the individual and overall bounding boxes and the length of each pointer's
-           gesture. */
-        optional bool redacted = 5;
-        // Bounding box of the whole gesture. Only set on UP event.
-        optional BoundingBox boundingBox = 6;
-    }
-
-    message SensorEvent {
-        enum Type {
-            ACCELEROMETER = 1;
-            GYROSCOPE = 4;
-            LIGHT = 5;
-            PROXIMITY = 8;
-            ROTATION_VECTOR = 11;
-        }
-
-        optional Type type = 1;
-        optional uint64 timeOffsetNanos = 2;
-        repeated float values = 3;
-        optional uint64 timestamp = 4;
-    }
-
-    enum Result {
-        FAILURE = 0;
-        SUCCESS = 1;
-        UNKNOWN = 2;
-    }
-
-    enum Type {
-        KEYGUARD_INSECURE = 0;
-        KEYGUARD_SECURE = 1;
-        RANDOM_WAKEUP = 2;
-    }
-
-    optional uint64 startTimestampMillis = 1;
-    optional uint64 durationMillis = 2;
-    optional string build = 3;
-    optional Result result = 4;
-    repeated TouchEvent touchEvents = 5;
-    repeated SensorEvent sensorEvents = 6;
-
-    optional int32 touchAreaWidth = 9;
-    optional int32 touchAreaHeight = 10;
-    optional Type type = 11;
-}
diff --git a/core/java/android/tv/TvInputHardwareInfo.aidl b/packages/SettingsProvider/res/values-af/defaults.xml
similarity index 62%
copy from core/java/android/tv/TvInputHardwareInfo.aidl
copy to packages/SettingsProvider/res/values-af/defaults.xml
index 484ab60..295b4f5 100644
--- a/core/java/android/tv/TvInputHardwareInfo.aidl
+++ b/packages/SettingsProvider/res/values-af/defaults.xml
@@ -1,6 +1,7 @@
-/*
- *
- * Copyright 2014, The Android Open Source Project
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+/**
+ * Copyright (c) 2009, 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,7 +15,9 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+ -->
 
-package android.tv;
-
-parcelable TvInputHardwareInfo;
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="def_device_name" msgid="6309317409634339402">"%1$s %2$s"</string>
+</resources>
diff --git a/core/java/android/tv/TvInputHardwareInfo.aidl b/packages/SettingsProvider/res/values-am/defaults.xml
similarity index 62%
copy from core/java/android/tv/TvInputHardwareInfo.aidl
copy to packages/SettingsProvider/res/values-am/defaults.xml
index 484ab60..295b4f5 100644
--- a/core/java/android/tv/TvInputHardwareInfo.aidl
+++ b/packages/SettingsProvider/res/values-am/defaults.xml
@@ -1,6 +1,7 @@
-/*
- *
- * Copyright 2014, The Android Open Source Project
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+/**
+ * Copyright (c) 2009, 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,7 +15,9 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+ -->
 
-package android.tv;
-
-parcelable TvInputHardwareInfo;
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="def_device_name" msgid="6309317409634339402">"%1$s %2$s"</string>
+</resources>
diff --git a/core/java/android/tv/TvInputHardwareInfo.aidl b/packages/SettingsProvider/res/values-ar/defaults.xml
similarity index 62%
copy from core/java/android/tv/TvInputHardwareInfo.aidl
copy to packages/SettingsProvider/res/values-ar/defaults.xml
index 484ab60..295b4f5 100644
--- a/core/java/android/tv/TvInputHardwareInfo.aidl
+++ b/packages/SettingsProvider/res/values-ar/defaults.xml
@@ -1,6 +1,7 @@
-/*
- *
- * Copyright 2014, The Android Open Source Project
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+/**
+ * Copyright (c) 2009, 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,7 +15,9 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+ -->
 
-package android.tv;
-
-parcelable TvInputHardwareInfo;
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="def_device_name" msgid="6309317409634339402">"%1$s %2$s"</string>
+</resources>
diff --git a/core/java/android/tv/TvInputHardwareInfo.aidl b/packages/SettingsProvider/res/values-bg/defaults.xml
similarity index 61%
copy from core/java/android/tv/TvInputHardwareInfo.aidl
copy to packages/SettingsProvider/res/values-bg/defaults.xml
index 484ab60..1d546ff 100644
--- a/core/java/android/tv/TvInputHardwareInfo.aidl
+++ b/packages/SettingsProvider/res/values-bg/defaults.xml
@@ -1,6 +1,7 @@
-/*
- *
- * Copyright 2014, The Android Open Source Project
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+/**
+ * Copyright (c) 2009, 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,7 +15,9 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+ -->
 
-package android.tv;
-
-parcelable TvInputHardwareInfo;
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="def_device_name" msgid="6309317409634339402">"%2$s от %1$s"</string>
+</resources>
diff --git a/core/java/android/tv/TvInputHardwareInfo.aidl b/packages/SettingsProvider/res/values-ca/defaults.xml
similarity index 62%
copy from core/java/android/tv/TvInputHardwareInfo.aidl
copy to packages/SettingsProvider/res/values-ca/defaults.xml
index 484ab60..295b4f5 100644
--- a/core/java/android/tv/TvInputHardwareInfo.aidl
+++ b/packages/SettingsProvider/res/values-ca/defaults.xml
@@ -1,6 +1,7 @@
-/*
- *
- * Copyright 2014, The Android Open Source Project
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+/**
+ * Copyright (c) 2009, 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,7 +15,9 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+ -->
 
-package android.tv;
-
-parcelable TvInputHardwareInfo;
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="def_device_name" msgid="6309317409634339402">"%1$s %2$s"</string>
+</resources>
diff --git a/core/java/android/tv/TvInputHardwareInfo.aidl b/packages/SettingsProvider/res/values-cs/defaults.xml
similarity index 62%
copy from core/java/android/tv/TvInputHardwareInfo.aidl
copy to packages/SettingsProvider/res/values-cs/defaults.xml
index 484ab60..295b4f5 100644
--- a/core/java/android/tv/TvInputHardwareInfo.aidl
+++ b/packages/SettingsProvider/res/values-cs/defaults.xml
@@ -1,6 +1,7 @@
-/*
- *
- * Copyright 2014, The Android Open Source Project
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+/**
+ * Copyright (c) 2009, 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,7 +15,9 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+ -->
 
-package android.tv;
-
-parcelable TvInputHardwareInfo;
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="def_device_name" msgid="6309317409634339402">"%1$s %2$s"</string>
+</resources>
diff --git a/core/java/android/tv/TvInputHardwareInfo.aidl b/packages/SettingsProvider/res/values-da/defaults.xml
similarity index 62%
copy from core/java/android/tv/TvInputHardwareInfo.aidl
copy to packages/SettingsProvider/res/values-da/defaults.xml
index 484ab60..295b4f5 100644
--- a/core/java/android/tv/TvInputHardwareInfo.aidl
+++ b/packages/SettingsProvider/res/values-da/defaults.xml
@@ -1,6 +1,7 @@
-/*
- *
- * Copyright 2014, The Android Open Source Project
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+/**
+ * Copyright (c) 2009, 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,7 +15,9 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+ -->
 
-package android.tv;
-
-parcelable TvInputHardwareInfo;
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="def_device_name" msgid="6309317409634339402">"%1$s %2$s"</string>
+</resources>
diff --git a/core/java/android/tv/TvInputHardwareInfo.aidl b/packages/SettingsProvider/res/values-de/defaults.xml
similarity index 62%
copy from core/java/android/tv/TvInputHardwareInfo.aidl
copy to packages/SettingsProvider/res/values-de/defaults.xml
index 484ab60..295b4f5 100644
--- a/core/java/android/tv/TvInputHardwareInfo.aidl
+++ b/packages/SettingsProvider/res/values-de/defaults.xml
@@ -1,6 +1,7 @@
-/*
- *
- * Copyright 2014, The Android Open Source Project
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+/**
+ * Copyright (c) 2009, 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,7 +15,9 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+ -->
 
-package android.tv;
-
-parcelable TvInputHardwareInfo;
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="def_device_name" msgid="6309317409634339402">"%1$s %2$s"</string>
+</resources>
diff --git a/core/java/android/tv/TvInputHardwareInfo.aidl b/packages/SettingsProvider/res/values-el/defaults.xml
similarity index 62%
copy from core/java/android/tv/TvInputHardwareInfo.aidl
copy to packages/SettingsProvider/res/values-el/defaults.xml
index 484ab60..295b4f5 100644
--- a/core/java/android/tv/TvInputHardwareInfo.aidl
+++ b/packages/SettingsProvider/res/values-el/defaults.xml
@@ -1,6 +1,7 @@
-/*
- *
- * Copyright 2014, The Android Open Source Project
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+/**
+ * Copyright (c) 2009, 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,7 +15,9 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+ -->
 
-package android.tv;
-
-parcelable TvInputHardwareInfo;
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="def_device_name" msgid="6309317409634339402">"%1$s %2$s"</string>
+</resources>
diff --git a/core/java/android/tv/TvInputHardwareInfo.aidl b/packages/SettingsProvider/res/values-en-rGB/defaults.xml
similarity index 62%
copy from core/java/android/tv/TvInputHardwareInfo.aidl
copy to packages/SettingsProvider/res/values-en-rGB/defaults.xml
index 484ab60..295b4f5 100644
--- a/core/java/android/tv/TvInputHardwareInfo.aidl
+++ b/packages/SettingsProvider/res/values-en-rGB/defaults.xml
@@ -1,6 +1,7 @@
-/*
- *
- * Copyright 2014, The Android Open Source Project
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+/**
+ * Copyright (c) 2009, 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,7 +15,9 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+ -->
 
-package android.tv;
-
-parcelable TvInputHardwareInfo;
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="def_device_name" msgid="6309317409634339402">"%1$s %2$s"</string>
+</resources>
diff --git a/core/java/android/tv/TvInputHardwareInfo.aidl b/packages/SettingsProvider/res/values-en-rIN/defaults.xml
similarity index 62%
copy from core/java/android/tv/TvInputHardwareInfo.aidl
copy to packages/SettingsProvider/res/values-en-rIN/defaults.xml
index 484ab60..295b4f5 100644
--- a/core/java/android/tv/TvInputHardwareInfo.aidl
+++ b/packages/SettingsProvider/res/values-en-rIN/defaults.xml
@@ -1,6 +1,7 @@
-/*
- *
- * Copyright 2014, The Android Open Source Project
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+/**
+ * Copyright (c) 2009, 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,7 +15,9 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+ -->
 
-package android.tv;
-
-parcelable TvInputHardwareInfo;
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="def_device_name" msgid="6309317409634339402">"%1$s %2$s"</string>
+</resources>
diff --git a/core/java/android/tv/TvInputHardwareInfo.aidl b/packages/SettingsProvider/res/values-es-rUS/defaults.xml
similarity index 62%
copy from core/java/android/tv/TvInputHardwareInfo.aidl
copy to packages/SettingsProvider/res/values-es-rUS/defaults.xml
index 484ab60..295b4f5 100644
--- a/core/java/android/tv/TvInputHardwareInfo.aidl
+++ b/packages/SettingsProvider/res/values-es-rUS/defaults.xml
@@ -1,6 +1,7 @@
-/*
- *
- * Copyright 2014, The Android Open Source Project
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+/**
+ * Copyright (c) 2009, 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,7 +15,9 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+ -->
 
-package android.tv;
-
-parcelable TvInputHardwareInfo;
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="def_device_name" msgid="6309317409634339402">"%1$s %2$s"</string>
+</resources>
diff --git a/core/java/android/tv/TvInputHardwareInfo.aidl b/packages/SettingsProvider/res/values-es/defaults.xml
similarity index 62%
copy from core/java/android/tv/TvInputHardwareInfo.aidl
copy to packages/SettingsProvider/res/values-es/defaults.xml
index 484ab60..295b4f5 100644
--- a/core/java/android/tv/TvInputHardwareInfo.aidl
+++ b/packages/SettingsProvider/res/values-es/defaults.xml
@@ -1,6 +1,7 @@
-/*
- *
- * Copyright 2014, The Android Open Source Project
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+/**
+ * Copyright (c) 2009, 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,7 +15,9 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+ -->
 
-package android.tv;
-
-parcelable TvInputHardwareInfo;
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="def_device_name" msgid="6309317409634339402">"%1$s %2$s"</string>
+</resources>
diff --git a/core/java/android/tv/TvInputHardwareInfo.aidl b/packages/SettingsProvider/res/values-et-rEE/defaults.xml
similarity index 62%
copy from core/java/android/tv/TvInputHardwareInfo.aidl
copy to packages/SettingsProvider/res/values-et-rEE/defaults.xml
index 484ab60..5f99ed9 100644
--- a/core/java/android/tv/TvInputHardwareInfo.aidl
+++ b/packages/SettingsProvider/res/values-et-rEE/defaults.xml
@@ -1,6 +1,7 @@
-/*
- *
- * Copyright 2014, The Android Open Source Project
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+/**
+ * Copyright (c) 2009, 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,7 +15,9 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+ -->
 
-package android.tv;
-
-parcelable TvInputHardwareInfo;
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="def_device_name" msgid="6309317409634339402">"%2$s, %1$s"</string>
+</resources>
diff --git a/core/java/android/tv/TvInputHardwareInfo.aidl b/packages/SettingsProvider/res/values-fa/defaults.xml
similarity index 62%
copy from core/java/android/tv/TvInputHardwareInfo.aidl
copy to packages/SettingsProvider/res/values-fa/defaults.xml
index 484ab60..295b4f5 100644
--- a/core/java/android/tv/TvInputHardwareInfo.aidl
+++ b/packages/SettingsProvider/res/values-fa/defaults.xml
@@ -1,6 +1,7 @@
-/*
- *
- * Copyright 2014, The Android Open Source Project
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+/**
+ * Copyright (c) 2009, 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,7 +15,9 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+ -->
 
-package android.tv;
-
-parcelable TvInputHardwareInfo;
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="def_device_name" msgid="6309317409634339402">"%1$s %2$s"</string>
+</resources>
diff --git a/core/java/android/tv/TvInputHardwareInfo.aidl b/packages/SettingsProvider/res/values-fi/defaults.xml
similarity index 62%
copy from core/java/android/tv/TvInputHardwareInfo.aidl
copy to packages/SettingsProvider/res/values-fi/defaults.xml
index 484ab60..295b4f5 100644
--- a/core/java/android/tv/TvInputHardwareInfo.aidl
+++ b/packages/SettingsProvider/res/values-fi/defaults.xml
@@ -1,6 +1,7 @@
-/*
- *
- * Copyright 2014, The Android Open Source Project
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+/**
+ * Copyright (c) 2009, 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,7 +15,9 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+ -->
 
-package android.tv;
-
-parcelable TvInputHardwareInfo;
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="def_device_name" msgid="6309317409634339402">"%1$s %2$s"</string>
+</resources>
diff --git a/core/java/android/tv/TvInputHardwareInfo.aidl b/packages/SettingsProvider/res/values-fr-rCA/defaults.xml
similarity index 62%
copy from core/java/android/tv/TvInputHardwareInfo.aidl
copy to packages/SettingsProvider/res/values-fr-rCA/defaults.xml
index 484ab60..1a04b0f 100644
--- a/core/java/android/tv/TvInputHardwareInfo.aidl
+++ b/packages/SettingsProvider/res/values-fr-rCA/defaults.xml
@@ -1,6 +1,7 @@
-/*
- *
- * Copyright 2014, The Android Open Source Project
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+/**
+ * Copyright (c) 2009, 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,7 +15,9 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+ -->
 
-package android.tv;
-
-parcelable TvInputHardwareInfo;
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="def_device_name" msgid="6309317409634339402">"%2$s de %1$s"</string>
+</resources>
diff --git a/core/java/android/tv/TvInputHardwareInfo.aidl b/packages/SettingsProvider/res/values-fr/defaults.xml
similarity index 62%
copy from core/java/android/tv/TvInputHardwareInfo.aidl
copy to packages/SettingsProvider/res/values-fr/defaults.xml
index 484ab60..295b4f5 100644
--- a/core/java/android/tv/TvInputHardwareInfo.aidl
+++ b/packages/SettingsProvider/res/values-fr/defaults.xml
@@ -1,6 +1,7 @@
-/*
- *
- * Copyright 2014, The Android Open Source Project
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+/**
+ * Copyright (c) 2009, 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,7 +15,9 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+ -->
 
-package android.tv;
-
-parcelable TvInputHardwareInfo;
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="def_device_name" msgid="6309317409634339402">"%1$s %2$s"</string>
+</resources>
diff --git a/core/java/android/tv/TvInputHardwareInfo.aidl b/packages/SettingsProvider/res/values-hi/defaults.xml
similarity index 62%
copy from core/java/android/tv/TvInputHardwareInfo.aidl
copy to packages/SettingsProvider/res/values-hi/defaults.xml
index 484ab60..295b4f5 100644
--- a/core/java/android/tv/TvInputHardwareInfo.aidl
+++ b/packages/SettingsProvider/res/values-hi/defaults.xml
@@ -1,6 +1,7 @@
-/*
- *
- * Copyright 2014, The Android Open Source Project
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+/**
+ * Copyright (c) 2009, 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,7 +15,9 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+ -->
 
-package android.tv;
-
-parcelable TvInputHardwareInfo;
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="def_device_name" msgid="6309317409634339402">"%1$s %2$s"</string>
+</resources>
diff --git a/core/java/android/tv/TvInputHardwareInfo.aidl b/packages/SettingsProvider/res/values-hr/defaults.xml
similarity index 62%
copy from core/java/android/tv/TvInputHardwareInfo.aidl
copy to packages/SettingsProvider/res/values-hr/defaults.xml
index 484ab60..295b4f5 100644
--- a/core/java/android/tv/TvInputHardwareInfo.aidl
+++ b/packages/SettingsProvider/res/values-hr/defaults.xml
@@ -1,6 +1,7 @@
-/*
- *
- * Copyright 2014, The Android Open Source Project
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+/**
+ * Copyright (c) 2009, 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,7 +15,9 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+ -->
 
-package android.tv;
-
-parcelable TvInputHardwareInfo;
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="def_device_name" msgid="6309317409634339402">"%1$s %2$s"</string>
+</resources>
diff --git a/core/java/android/tv/TvInputHardwareInfo.aidl b/packages/SettingsProvider/res/values-hu/defaults.xml
similarity index 62%
copy from core/java/android/tv/TvInputHardwareInfo.aidl
copy to packages/SettingsProvider/res/values-hu/defaults.xml
index 484ab60..295b4f5 100644
--- a/core/java/android/tv/TvInputHardwareInfo.aidl
+++ b/packages/SettingsProvider/res/values-hu/defaults.xml
@@ -1,6 +1,7 @@
-/*
- *
- * Copyright 2014, The Android Open Source Project
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+/**
+ * Copyright (c) 2009, 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,7 +15,9 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+ -->
 
-package android.tv;
-
-parcelable TvInputHardwareInfo;
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="def_device_name" msgid="6309317409634339402">"%1$s %2$s"</string>
+</resources>
diff --git a/core/java/android/tv/TvInputHardwareInfo.aidl b/packages/SettingsProvider/res/values-hy-rAM/defaults.xml
similarity index 62%
copy from core/java/android/tv/TvInputHardwareInfo.aidl
copy to packages/SettingsProvider/res/values-hy-rAM/defaults.xml
index 484ab60..295b4f5 100644
--- a/core/java/android/tv/TvInputHardwareInfo.aidl
+++ b/packages/SettingsProvider/res/values-hy-rAM/defaults.xml
@@ -1,6 +1,7 @@
-/*
- *
- * Copyright 2014, The Android Open Source Project
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+/**
+ * Copyright (c) 2009, 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,7 +15,9 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+ -->
 
-package android.tv;
-
-parcelable TvInputHardwareInfo;
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="def_device_name" msgid="6309317409634339402">"%1$s %2$s"</string>
+</resources>
diff --git a/core/java/android/tv/TvInputHardwareInfo.aidl b/packages/SettingsProvider/res/values-in/defaults.xml
similarity index 62%
copy from core/java/android/tv/TvInputHardwareInfo.aidl
copy to packages/SettingsProvider/res/values-in/defaults.xml
index 484ab60..295b4f5 100644
--- a/core/java/android/tv/TvInputHardwareInfo.aidl
+++ b/packages/SettingsProvider/res/values-in/defaults.xml
@@ -1,6 +1,7 @@
-/*
- *
- * Copyright 2014, The Android Open Source Project
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+/**
+ * Copyright (c) 2009, 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,7 +15,9 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+ -->
 
-package android.tv;
-
-parcelable TvInputHardwareInfo;
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="def_device_name" msgid="6309317409634339402">"%1$s %2$s"</string>
+</resources>
diff --git a/core/java/android/tv/TvInputHardwareInfo.aidl b/packages/SettingsProvider/res/values-it/defaults.xml
similarity index 62%
copy from core/java/android/tv/TvInputHardwareInfo.aidl
copy to packages/SettingsProvider/res/values-it/defaults.xml
index 484ab60..bc995b0 100644
--- a/core/java/android/tv/TvInputHardwareInfo.aidl
+++ b/packages/SettingsProvider/res/values-it/defaults.xml
@@ -1,6 +1,7 @@
-/*
- *
- * Copyright 2014, The Android Open Source Project
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+/**
+ * Copyright (c) 2009, 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,7 +15,9 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+ -->
 
-package android.tv;
-
-parcelable TvInputHardwareInfo;
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="def_device_name" msgid="6309317409634339402">"%2$s %1$s"</string>
+</resources>
diff --git a/core/java/android/tv/TvInputHardwareInfo.aidl b/packages/SettingsProvider/res/values-iw/defaults.xml
similarity index 62%
copy from core/java/android/tv/TvInputHardwareInfo.aidl
copy to packages/SettingsProvider/res/values-iw/defaults.xml
index 484ab60..295b4f5 100644
--- a/core/java/android/tv/TvInputHardwareInfo.aidl
+++ b/packages/SettingsProvider/res/values-iw/defaults.xml
@@ -1,6 +1,7 @@
-/*
- *
- * Copyright 2014, The Android Open Source Project
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+/**
+ * Copyright (c) 2009, 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,7 +15,9 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+ -->
 
-package android.tv;
-
-parcelable TvInputHardwareInfo;
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="def_device_name" msgid="6309317409634339402">"%1$s %2$s"</string>
+</resources>
diff --git a/core/java/android/tv/TvInputHardwareInfo.aidl b/packages/SettingsProvider/res/values-ja/defaults.xml
similarity index 62%
copy from core/java/android/tv/TvInputHardwareInfo.aidl
copy to packages/SettingsProvider/res/values-ja/defaults.xml
index 484ab60..bc995b0 100644
--- a/core/java/android/tv/TvInputHardwareInfo.aidl
+++ b/packages/SettingsProvider/res/values-ja/defaults.xml
@@ -1,6 +1,7 @@
-/*
- *
- * Copyright 2014, The Android Open Source Project
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+/**
+ * Copyright (c) 2009, 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,7 +15,9 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+ -->
 
-package android.tv;
-
-parcelable TvInputHardwareInfo;
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="def_device_name" msgid="6309317409634339402">"%2$s %1$s"</string>
+</resources>
diff --git a/core/java/android/tv/TvInputHardwareInfo.aidl b/packages/SettingsProvider/res/values-ka-rGE/defaults.xml
similarity index 62%
copy from core/java/android/tv/TvInputHardwareInfo.aidl
copy to packages/SettingsProvider/res/values-ka-rGE/defaults.xml
index 484ab60..295b4f5 100644
--- a/core/java/android/tv/TvInputHardwareInfo.aidl
+++ b/packages/SettingsProvider/res/values-ka-rGE/defaults.xml
@@ -1,6 +1,7 @@
-/*
- *
- * Copyright 2014, The Android Open Source Project
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+/**
+ * Copyright (c) 2009, 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,7 +15,9 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+ -->
 
-package android.tv;
-
-parcelable TvInputHardwareInfo;
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="def_device_name" msgid="6309317409634339402">"%1$s %2$s"</string>
+</resources>
diff --git a/core/java/android/tv/TvInputHardwareInfo.aidl b/packages/SettingsProvider/res/values-km-rKH/defaults.xml
similarity index 62%
copy from core/java/android/tv/TvInputHardwareInfo.aidl
copy to packages/SettingsProvider/res/values-km-rKH/defaults.xml
index 484ab60..295b4f5 100644
--- a/core/java/android/tv/TvInputHardwareInfo.aidl
+++ b/packages/SettingsProvider/res/values-km-rKH/defaults.xml
@@ -1,6 +1,7 @@
-/*
- *
- * Copyright 2014, The Android Open Source Project
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+/**
+ * Copyright (c) 2009, 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,7 +15,9 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+ -->
 
-package android.tv;
-
-parcelable TvInputHardwareInfo;
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="def_device_name" msgid="6309317409634339402">"%1$s %2$s"</string>
+</resources>
diff --git a/core/java/android/tv/TvInputHardwareInfo.aidl b/packages/SettingsProvider/res/values-ko/defaults.xml
similarity index 62%
copy from core/java/android/tv/TvInputHardwareInfo.aidl
copy to packages/SettingsProvider/res/values-ko/defaults.xml
index 484ab60..295b4f5 100644
--- a/core/java/android/tv/TvInputHardwareInfo.aidl
+++ b/packages/SettingsProvider/res/values-ko/defaults.xml
@@ -1,6 +1,7 @@
-/*
- *
- * Copyright 2014, The Android Open Source Project
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+/**
+ * Copyright (c) 2009, 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,7 +15,9 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+ -->
 
-package android.tv;
-
-parcelable TvInputHardwareInfo;
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="def_device_name" msgid="6309317409634339402">"%1$s %2$s"</string>
+</resources>
diff --git a/core/java/android/tv/TvInputHardwareInfo.aidl b/packages/SettingsProvider/res/values-lt/defaults.xml
similarity index 62%
copy from core/java/android/tv/TvInputHardwareInfo.aidl
copy to packages/SettingsProvider/res/values-lt/defaults.xml
index 484ab60..295b4f5 100644
--- a/core/java/android/tv/TvInputHardwareInfo.aidl
+++ b/packages/SettingsProvider/res/values-lt/defaults.xml
@@ -1,6 +1,7 @@
-/*
- *
- * Copyright 2014, The Android Open Source Project
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+/**
+ * Copyright (c) 2009, 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,7 +15,9 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+ -->
 
-package android.tv;
-
-parcelable TvInputHardwareInfo;
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="def_device_name" msgid="6309317409634339402">"%1$s %2$s"</string>
+</resources>
diff --git a/core/java/android/tv/TvInputHardwareInfo.aidl b/packages/SettingsProvider/res/values-lv/defaults.xml
similarity index 62%
copy from core/java/android/tv/TvInputHardwareInfo.aidl
copy to packages/SettingsProvider/res/values-lv/defaults.xml
index 484ab60..295b4f5 100644
--- a/core/java/android/tv/TvInputHardwareInfo.aidl
+++ b/packages/SettingsProvider/res/values-lv/defaults.xml
@@ -1,6 +1,7 @@
-/*
- *
- * Copyright 2014, The Android Open Source Project
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+/**
+ * Copyright (c) 2009, 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,7 +15,9 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+ -->
 
-package android.tv;
-
-parcelable TvInputHardwareInfo;
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="def_device_name" msgid="6309317409634339402">"%1$s %2$s"</string>
+</resources>
diff --git a/core/java/android/tv/TvInputHardwareInfo.aidl b/packages/SettingsProvider/res/values-ms-rMY/defaults.xml
similarity index 62%
copy from core/java/android/tv/TvInputHardwareInfo.aidl
copy to packages/SettingsProvider/res/values-ms-rMY/defaults.xml
index 484ab60..295b4f5 100644
--- a/core/java/android/tv/TvInputHardwareInfo.aidl
+++ b/packages/SettingsProvider/res/values-ms-rMY/defaults.xml
@@ -1,6 +1,7 @@
-/*
- *
- * Copyright 2014, The Android Open Source Project
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+/**
+ * Copyright (c) 2009, 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,7 +15,9 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+ -->
 
-package android.tv;
-
-parcelable TvInputHardwareInfo;
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="def_device_name" msgid="6309317409634339402">"%1$s %2$s"</string>
+</resources>
diff --git a/core/java/android/tv/TvInputHardwareInfo.aidl b/packages/SettingsProvider/res/values-nb/defaults.xml
similarity index 62%
copy from core/java/android/tv/TvInputHardwareInfo.aidl
copy to packages/SettingsProvider/res/values-nb/defaults.xml
index 484ab60..295b4f5 100644
--- a/core/java/android/tv/TvInputHardwareInfo.aidl
+++ b/packages/SettingsProvider/res/values-nb/defaults.xml
@@ -1,6 +1,7 @@
-/*
- *
- * Copyright 2014, The Android Open Source Project
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+/**
+ * Copyright (c) 2009, 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,7 +15,9 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+ -->
 
-package android.tv;
-
-parcelable TvInputHardwareInfo;
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="def_device_name" msgid="6309317409634339402">"%1$s %2$s"</string>
+</resources>
diff --git a/core/java/android/tv/TvInputHardwareInfo.aidl b/packages/SettingsProvider/res/values-nl/defaults.xml
similarity index 62%
copy from core/java/android/tv/TvInputHardwareInfo.aidl
copy to packages/SettingsProvider/res/values-nl/defaults.xml
index 484ab60..295b4f5 100644
--- a/core/java/android/tv/TvInputHardwareInfo.aidl
+++ b/packages/SettingsProvider/res/values-nl/defaults.xml
@@ -1,6 +1,7 @@
-/*
- *
- * Copyright 2014, The Android Open Source Project
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+/**
+ * Copyright (c) 2009, 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,7 +15,9 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+ -->
 
-package android.tv;
-
-parcelable TvInputHardwareInfo;
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="def_device_name" msgid="6309317409634339402">"%1$s %2$s"</string>
+</resources>
diff --git a/core/java/android/tv/TvInputHardwareInfo.aidl b/packages/SettingsProvider/res/values-pl/defaults.xml
similarity index 62%
copy from core/java/android/tv/TvInputHardwareInfo.aidl
copy to packages/SettingsProvider/res/values-pl/defaults.xml
index 484ab60..295b4f5 100644
--- a/core/java/android/tv/TvInputHardwareInfo.aidl
+++ b/packages/SettingsProvider/res/values-pl/defaults.xml
@@ -1,6 +1,7 @@
-/*
- *
- * Copyright 2014, The Android Open Source Project
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+/**
+ * Copyright (c) 2009, 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,7 +15,9 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+ -->
 
-package android.tv;
-
-parcelable TvInputHardwareInfo;
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="def_device_name" msgid="6309317409634339402">"%1$s %2$s"</string>
+</resources>
diff --git a/core/java/android/tv/TvInputHardwareInfo.aidl b/packages/SettingsProvider/res/values-pt-rPT/defaults.xml
similarity index 62%
copy from core/java/android/tv/TvInputHardwareInfo.aidl
copy to packages/SettingsProvider/res/values-pt-rPT/defaults.xml
index 484ab60..295b4f5 100644
--- a/core/java/android/tv/TvInputHardwareInfo.aidl
+++ b/packages/SettingsProvider/res/values-pt-rPT/defaults.xml
@@ -1,6 +1,7 @@
-/*
- *
- * Copyright 2014, The Android Open Source Project
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+/**
+ * Copyright (c) 2009, 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,7 +15,9 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+ -->
 
-package android.tv;
-
-parcelable TvInputHardwareInfo;
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="def_device_name" msgid="6309317409634339402">"%1$s %2$s"</string>
+</resources>
diff --git a/core/java/android/tv/TvInputHardwareInfo.aidl b/packages/SettingsProvider/res/values-pt/defaults.xml
similarity index 62%
copy from core/java/android/tv/TvInputHardwareInfo.aidl
copy to packages/SettingsProvider/res/values-pt/defaults.xml
index 484ab60..295b4f5 100644
--- a/core/java/android/tv/TvInputHardwareInfo.aidl
+++ b/packages/SettingsProvider/res/values-pt/defaults.xml
@@ -1,6 +1,7 @@
-/*
- *
- * Copyright 2014, The Android Open Source Project
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+/**
+ * Copyright (c) 2009, 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,7 +15,9 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+ -->
 
-package android.tv;
-
-parcelable TvInputHardwareInfo;
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="def_device_name" msgid="6309317409634339402">"%1$s %2$s"</string>
+</resources>
diff --git a/core/java/android/tv/TvInputHardwareInfo.aidl b/packages/SettingsProvider/res/values-ro/defaults.xml
similarity index 62%
copy from core/java/android/tv/TvInputHardwareInfo.aidl
copy to packages/SettingsProvider/res/values-ro/defaults.xml
index 484ab60..295b4f5 100644
--- a/core/java/android/tv/TvInputHardwareInfo.aidl
+++ b/packages/SettingsProvider/res/values-ro/defaults.xml
@@ -1,6 +1,7 @@
-/*
- *
- * Copyright 2014, The Android Open Source Project
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+/**
+ * Copyright (c) 2009, 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,7 +15,9 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+ -->
 
-package android.tv;
-
-parcelable TvInputHardwareInfo;
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="def_device_name" msgid="6309317409634339402">"%1$s %2$s"</string>
+</resources>
diff --git a/core/java/android/tv/TvInputHardwareInfo.aidl b/packages/SettingsProvider/res/values-ru/defaults.xml
similarity index 62%
copy from core/java/android/tv/TvInputHardwareInfo.aidl
copy to packages/SettingsProvider/res/values-ru/defaults.xml
index 484ab60..bc995b0 100644
--- a/core/java/android/tv/TvInputHardwareInfo.aidl
+++ b/packages/SettingsProvider/res/values-ru/defaults.xml
@@ -1,6 +1,7 @@
-/*
- *
- * Copyright 2014, The Android Open Source Project
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+/**
+ * Copyright (c) 2009, 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,7 +15,9 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+ -->
 
-package android.tv;
-
-parcelable TvInputHardwareInfo;
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="def_device_name" msgid="6309317409634339402">"%2$s %1$s"</string>
+</resources>
diff --git a/core/java/android/tv/TvInputHardwareInfo.aidl b/packages/SettingsProvider/res/values-sk/defaults.xml
similarity index 62%
copy from core/java/android/tv/TvInputHardwareInfo.aidl
copy to packages/SettingsProvider/res/values-sk/defaults.xml
index 484ab60..295b4f5 100644
--- a/core/java/android/tv/TvInputHardwareInfo.aidl
+++ b/packages/SettingsProvider/res/values-sk/defaults.xml
@@ -1,6 +1,7 @@
-/*
- *
- * Copyright 2014, The Android Open Source Project
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+/**
+ * Copyright (c) 2009, 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,7 +15,9 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+ -->
 
-package android.tv;
-
-parcelable TvInputHardwareInfo;
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="def_device_name" msgid="6309317409634339402">"%1$s %2$s"</string>
+</resources>
diff --git a/core/java/android/tv/TvInputHardwareInfo.aidl b/packages/SettingsProvider/res/values-sl/defaults.xml
similarity index 62%
copy from core/java/android/tv/TvInputHardwareInfo.aidl
copy to packages/SettingsProvider/res/values-sl/defaults.xml
index 484ab60..295b4f5 100644
--- a/core/java/android/tv/TvInputHardwareInfo.aidl
+++ b/packages/SettingsProvider/res/values-sl/defaults.xml
@@ -1,6 +1,7 @@
-/*
- *
- * Copyright 2014, The Android Open Source Project
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+/**
+ * Copyright (c) 2009, 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,7 +15,9 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+ -->
 
-package android.tv;
-
-parcelable TvInputHardwareInfo;
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="def_device_name" msgid="6309317409634339402">"%1$s %2$s"</string>
+</resources>
diff --git a/core/java/android/tv/TvInputHardwareInfo.aidl b/packages/SettingsProvider/res/values-sr/defaults.xml
similarity index 62%
copy from core/java/android/tv/TvInputHardwareInfo.aidl
copy to packages/SettingsProvider/res/values-sr/defaults.xml
index 484ab60..bc995b0 100644
--- a/core/java/android/tv/TvInputHardwareInfo.aidl
+++ b/packages/SettingsProvider/res/values-sr/defaults.xml
@@ -1,6 +1,7 @@
-/*
- *
- * Copyright 2014, The Android Open Source Project
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+/**
+ * Copyright (c) 2009, 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,7 +15,9 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+ -->
 
-package android.tv;
-
-parcelable TvInputHardwareInfo;
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="def_device_name" msgid="6309317409634339402">"%2$s %1$s"</string>
+</resources>
diff --git a/core/java/android/tv/TvInputHardwareInfo.aidl b/packages/SettingsProvider/res/values-sv/defaults.xml
similarity index 62%
copy from core/java/android/tv/TvInputHardwareInfo.aidl
copy to packages/SettingsProvider/res/values-sv/defaults.xml
index 484ab60..295b4f5 100644
--- a/core/java/android/tv/TvInputHardwareInfo.aidl
+++ b/packages/SettingsProvider/res/values-sv/defaults.xml
@@ -1,6 +1,7 @@
-/*
- *
- * Copyright 2014, The Android Open Source Project
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+/**
+ * Copyright (c) 2009, 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,7 +15,9 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+ -->
 
-package android.tv;
-
-parcelable TvInputHardwareInfo;
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="def_device_name" msgid="6309317409634339402">"%1$s %2$s"</string>
+</resources>
diff --git a/core/java/android/tv/TvInputHardwareInfo.aidl b/packages/SettingsProvider/res/values-sw/defaults.xml
similarity index 62%
copy from core/java/android/tv/TvInputHardwareInfo.aidl
copy to packages/SettingsProvider/res/values-sw/defaults.xml
index 484ab60..295b4f5 100644
--- a/core/java/android/tv/TvInputHardwareInfo.aidl
+++ b/packages/SettingsProvider/res/values-sw/defaults.xml
@@ -1,6 +1,7 @@
-/*
- *
- * Copyright 2014, The Android Open Source Project
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+/**
+ * Copyright (c) 2009, 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,7 +15,9 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+ -->
 
-package android.tv;
-
-parcelable TvInputHardwareInfo;
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="def_device_name" msgid="6309317409634339402">"%1$s %2$s"</string>
+</resources>
diff --git a/core/java/android/tv/TvInputHardwareInfo.aidl b/packages/SettingsProvider/res/values-th/defaults.xml
similarity index 62%
copy from core/java/android/tv/TvInputHardwareInfo.aidl
copy to packages/SettingsProvider/res/values-th/defaults.xml
index 484ab60..295b4f5 100644
--- a/core/java/android/tv/TvInputHardwareInfo.aidl
+++ b/packages/SettingsProvider/res/values-th/defaults.xml
@@ -1,6 +1,7 @@
-/*
- *
- * Copyright 2014, The Android Open Source Project
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+/**
+ * Copyright (c) 2009, 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,7 +15,9 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+ -->
 
-package android.tv;
-
-parcelable TvInputHardwareInfo;
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="def_device_name" msgid="6309317409634339402">"%1$s %2$s"</string>
+</resources>
diff --git a/core/java/android/tv/TvInputHardwareInfo.aidl b/packages/SettingsProvider/res/values-tl/defaults.xml
similarity index 62%
copy from core/java/android/tv/TvInputHardwareInfo.aidl
copy to packages/SettingsProvider/res/values-tl/defaults.xml
index 484ab60..295b4f5 100644
--- a/core/java/android/tv/TvInputHardwareInfo.aidl
+++ b/packages/SettingsProvider/res/values-tl/defaults.xml
@@ -1,6 +1,7 @@
-/*
- *
- * Copyright 2014, The Android Open Source Project
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+/**
+ * Copyright (c) 2009, 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,7 +15,9 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+ -->
 
-package android.tv;
-
-parcelable TvInputHardwareInfo;
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="def_device_name" msgid="6309317409634339402">"%1$s %2$s"</string>
+</resources>
diff --git a/core/java/android/tv/TvInputHardwareInfo.aidl b/packages/SettingsProvider/res/values-tr/defaults.xml
similarity index 62%
copy from core/java/android/tv/TvInputHardwareInfo.aidl
copy to packages/SettingsProvider/res/values-tr/defaults.xml
index 484ab60..295b4f5 100644
--- a/core/java/android/tv/TvInputHardwareInfo.aidl
+++ b/packages/SettingsProvider/res/values-tr/defaults.xml
@@ -1,6 +1,7 @@
-/*
- *
- * Copyright 2014, The Android Open Source Project
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+/**
+ * Copyright (c) 2009, 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,7 +15,9 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+ -->
 
-package android.tv;
-
-parcelable TvInputHardwareInfo;
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="def_device_name" msgid="6309317409634339402">"%1$s %2$s"</string>
+</resources>
diff --git a/core/java/android/tv/TvInputHardwareInfo.aidl b/packages/SettingsProvider/res/values-uk/defaults.xml
similarity index 62%
copy from core/java/android/tv/TvInputHardwareInfo.aidl
copy to packages/SettingsProvider/res/values-uk/defaults.xml
index 484ab60..8ca4583 100644
--- a/core/java/android/tv/TvInputHardwareInfo.aidl
+++ b/packages/SettingsProvider/res/values-uk/defaults.xml
@@ -1,6 +1,7 @@
-/*
- *
- * Copyright 2014, The Android Open Source Project
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+/**
+ * Copyright (c) 2009, 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,7 +15,9 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+ -->
 
-package android.tv;
-
-parcelable TvInputHardwareInfo;
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="def_device_name" msgid="6309317409634339402">"%2$s о %1$s"</string>
+</resources>
diff --git a/core/java/android/tv/TvInputHardwareInfo.aidl b/packages/SettingsProvider/res/values-vi/defaults.xml
similarity index 62%
copy from core/java/android/tv/TvInputHardwareInfo.aidl
copy to packages/SettingsProvider/res/values-vi/defaults.xml
index 484ab60..295b4f5 100644
--- a/core/java/android/tv/TvInputHardwareInfo.aidl
+++ b/packages/SettingsProvider/res/values-vi/defaults.xml
@@ -1,6 +1,7 @@
-/*
- *
- * Copyright 2014, The Android Open Source Project
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+/**
+ * Copyright (c) 2009, 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,7 +15,9 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+ -->
 
-package android.tv;
-
-parcelable TvInputHardwareInfo;
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="def_device_name" msgid="6309317409634339402">"%1$s %2$s"</string>
+</resources>
diff --git a/core/java/android/tv/TvInputHardwareInfo.aidl b/packages/SettingsProvider/res/values-zh-rCN/defaults.xml
similarity index 62%
copy from core/java/android/tv/TvInputHardwareInfo.aidl
copy to packages/SettingsProvider/res/values-zh-rCN/defaults.xml
index 484ab60..295b4f5 100644
--- a/core/java/android/tv/TvInputHardwareInfo.aidl
+++ b/packages/SettingsProvider/res/values-zh-rCN/defaults.xml
@@ -1,6 +1,7 @@
-/*
- *
- * Copyright 2014, The Android Open Source Project
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+/**
+ * Copyright (c) 2009, 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,7 +15,9 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+ -->
 
-package android.tv;
-
-parcelable TvInputHardwareInfo;
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="def_device_name" msgid="6309317409634339402">"%1$s %2$s"</string>
+</resources>
diff --git a/core/java/android/tv/TvInputHardwareInfo.aidl b/packages/SettingsProvider/res/values-zh-rHK/defaults.xml
similarity index 62%
copy from core/java/android/tv/TvInputHardwareInfo.aidl
copy to packages/SettingsProvider/res/values-zh-rHK/defaults.xml
index 484ab60..295b4f5 100644
--- a/core/java/android/tv/TvInputHardwareInfo.aidl
+++ b/packages/SettingsProvider/res/values-zh-rHK/defaults.xml
@@ -1,6 +1,7 @@
-/*
- *
- * Copyright 2014, The Android Open Source Project
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+/**
+ * Copyright (c) 2009, 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,7 +15,9 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+ -->
 
-package android.tv;
-
-parcelable TvInputHardwareInfo;
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="def_device_name" msgid="6309317409634339402">"%1$s %2$s"</string>
+</resources>
diff --git a/core/java/android/tv/TvInputHardwareInfo.aidl b/packages/SettingsProvider/res/values-zh-rTW/defaults.xml
similarity index 62%
copy from core/java/android/tv/TvInputHardwareInfo.aidl
copy to packages/SettingsProvider/res/values-zh-rTW/defaults.xml
index 484ab60..295b4f5 100644
--- a/core/java/android/tv/TvInputHardwareInfo.aidl
+++ b/packages/SettingsProvider/res/values-zh-rTW/defaults.xml
@@ -1,6 +1,7 @@
-/*
- *
- * Copyright 2014, The Android Open Source Project
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+/**
+ * Copyright (c) 2009, 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,7 +15,9 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+ -->
 
-package android.tv;
-
-parcelable TvInputHardwareInfo;
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="def_device_name" msgid="6309317409634339402">"%1$s %2$s"</string>
+</resources>
diff --git a/core/java/android/tv/TvInputHardwareInfo.aidl b/packages/SettingsProvider/res/values-zu/defaults.xml
similarity index 62%
copy from core/java/android/tv/TvInputHardwareInfo.aidl
copy to packages/SettingsProvider/res/values-zu/defaults.xml
index 484ab60..295b4f5 100644
--- a/core/java/android/tv/TvInputHardwareInfo.aidl
+++ b/packages/SettingsProvider/res/values-zu/defaults.xml
@@ -1,6 +1,7 @@
-/*
- *
- * Copyright 2014, The Android Open Source Project
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+/**
+ * Copyright (c) 2009, 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,7 +15,9 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+ -->
 
-package android.tv;
-
-parcelable TvInputHardwareInfo;
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="def_device_name" msgid="6309317409634339402">"%1$s %2$s"</string>
+</resources>
diff --git a/packages/SettingsProvider/res/values/defaults.xml b/packages/SettingsProvider/res/values/defaults.xml
index bf97fc0..0e025a9 100644
--- a/packages/SettingsProvider/res/values/defaults.xml
+++ b/packages/SettingsProvider/res/values/defaults.xml
@@ -192,4 +192,7 @@
     <!-- Default for Settings.Global.DEVICE_NAME $1=BRAND $2=MODEL-->
     <string name="def_device_name">%1$s %2$s</string>
 
+    <!-- Default for Settings.Secure.WAKE_GESTURE_ENABLED -->
+    <bool name="def_wake_gesture_enabled">true</bool>
+
 </resources>
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java b/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java
index 286921e..c4a54b7 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java
@@ -70,7 +70,7 @@
     // database gets upgraded properly. At a minimum, please confirm that 'upgradeVersion'
     // is properly propagated through your change.  Not doing so will result in a loss of user
     // settings.
-    private static final int DATABASE_VERSION = 103;
+    private static final int DATABASE_VERSION = 104;
 
     private Context mContext;
     private int mUserHandle;
@@ -1660,6 +1660,23 @@
             }
             upgradeVersion = 103;
         }
+
+        if (upgradeVersion == 103) {
+            db.beginTransaction();
+            SQLiteStatement stmt = null;
+            try {
+                stmt = db.compileStatement("INSERT OR REPLACE INTO secure(name,value)"
+                        + " VALUES(?,?);");
+                loadBooleanSetting(stmt, Settings.Secure.WAKE_GESTURE_ENABLED,
+                        R.bool.def_wake_gesture_enabled);
+                db.setTransactionSuccessful();
+            } finally {
+                db.endTransaction();
+                if (stmt != null) stmt.close();
+            }
+            upgradeVersion = 104;
+        }
+
         // *** Remember to update DATABASE_VERSION above!
 
         if (upgradeVersion != currentVersion) {
@@ -2222,6 +2239,9 @@
             loadBooleanSetting(stmt, Settings.Secure.INSTALL_NON_MARKET_APPS,
                     R.bool.def_install_non_market_apps);
 
+            loadBooleanSetting(stmt, Settings.Secure.WAKE_GESTURE_ENABLED,
+                    R.bool.def_wake_gesture_enabled);
+
         } finally {
             if (stmt != null) stmt.close();
         }
diff --git a/packages/Shell/res/values-de/strings.xml b/packages/Shell/res/values-de/strings.xml
index 99522b1..34481ba 100644
--- a/packages/Shell/res/values-de/strings.xml
+++ b/packages/Shell/res/values-de/strings.xml
@@ -18,7 +18,7 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="app_label" msgid="3701846017049540910">"Shell"</string>
     <string name="bugreport_finished_title" msgid="2293711546892863898">"Fehlerbericht erfasst"</string>
-    <string name="bugreport_finished_text" msgid="3559904746859400732">"Berühren, um Fehlerbericht zu teilen"</string>
+    <string name="bugreport_finished_text" msgid="3559904746859400732">"Tippen, um Fehlerbericht zu teilen"</string>
     <string name="bugreport_confirm" msgid="5130698467795669780">"Fehlerberichte enthalten Daten aus verschiedenen Protokolldateien des Systems, darunter auch personenbezogene und private Daten. Teilen Sie Fehlerberichte nur mit Apps und Personen, denen Sie vertrauen."</string>
     <string name="bugreport_confirm_repeat" msgid="4926842460688645058">"Diese Nachricht nächstes Mal zeigen"</string>
 </resources>
diff --git a/packages/Shell/res/values-fr/strings.xml b/packages/Shell/res/values-fr/strings.xml
index 1da6f1f..12f5e88 100644
--- a/packages/Shell/res/values-fr/strings.xml
+++ b/packages/Shell/res/values-fr/strings.xml
@@ -18,7 +18,7 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="app_label" msgid="3701846017049540910">"Shell"</string>
     <string name="bugreport_finished_title" msgid="2293711546892863898">"Rapport de bug enregistré"</string>
-    <string name="bugreport_finished_text" msgid="3559904746859400732">"Appuyer ici pour partager votre rapport de bug"</string>
+    <string name="bugreport_finished_text" msgid="3559904746859400732">"Appuyez ici pour partager le rapport de bug"</string>
     <string name="bugreport_confirm" msgid="5130698467795669780">"Les rapports de bug contiennent des données des fichiers journaux du système, y compris des informations personnelles et privées. Ne partagez les rapports de bug qu\'avec les applications et les personnes que vous estimez fiables."</string>
     <string name="bugreport_confirm_repeat" msgid="4926842460688645058">"Afficher ce message la prochaine fois"</string>
 </resources>
diff --git a/packages/SystemUI/res/drawable-hdpi/search_light.png b/packages/SystemUI/res/drawable-hdpi/search_light.png
deleted file mode 100644
index 3c0dc4e..0000000
--- a/packages/SystemUI/res/drawable-hdpi/search_light.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/search_light_land.png b/packages/SystemUI/res/drawable-hdpi/search_light_land.png
deleted file mode 100644
index 731f19b..0000000
--- a/packages/SystemUI/res/drawable-hdpi/search_light_land.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/search_light.png b/packages/SystemUI/res/drawable-mdpi/search_light.png
deleted file mode 100644
index 8010ce7..0000000
--- a/packages/SystemUI/res/drawable-mdpi/search_light.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/search_light_land.png b/packages/SystemUI/res/drawable-mdpi/search_light_land.png
deleted file mode 100644
index a4d82f0..0000000
--- a/packages/SystemUI/res/drawable-mdpi/search_light_land.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/search_light.png b/packages/SystemUI/res/drawable-xhdpi/search_light.png
deleted file mode 100644
index 6d46fdd..0000000
--- a/packages/SystemUI/res/drawable-xhdpi/search_light.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/search_light_land.png b/packages/SystemUI/res/drawable-xhdpi/search_light_land.png
deleted file mode 100644
index b62c74e..0000000
--- a/packages/SystemUI/res/drawable-xhdpi/search_light_land.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xxhdpi/search_light.png b/packages/SystemUI/res/drawable-xxhdpi/search_light.png
deleted file mode 100644
index 7742207..0000000
--- a/packages/SystemUI/res/drawable-xxhdpi/search_light.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xxhdpi/search_light_land.png b/packages/SystemUI/res/drawable-xxhdpi/search_light_land.png
deleted file mode 100644
index f364577..0000000
--- a/packages/SystemUI/res/drawable-xxhdpi/search_light_land.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable/heads_up_scrim.xml b/packages/SystemUI/res/drawable/heads_up_scrim.xml
new file mode 100644
index 0000000..59000fc
--- /dev/null
+++ b/packages/SystemUI/res/drawable/heads_up_scrim.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
+  -->
+
+<shape xmlns:android="http://schemas.android.com/apk/res/android">
+    <gradient
+            android:type="linear"
+            android:angle="-90"
+            android:startColor="#55000000"
+            android:endColor="#00000000" />
+</shape>
\ No newline at end of file
diff --git a/packages/SystemUI/res/drawable/ic_qs_airplane_off.xml b/packages/SystemUI/res/drawable/ic_qs_airplane_off.xml
index 9f0ec67..c68238f 100644
--- a/packages/SystemUI/res/drawable/ic_qs_airplane_off.xml
+++ b/packages/SystemUI/res/drawable/ic_qs_airplane_off.xml
@@ -19,17 +19,10 @@
         android:height="64dp"/>
 
     <viewport
-        android:viewportWidth="24.0"
-        android:viewportHeight="24.0"/>
+        android:viewportWidth="48.0"
+        android:viewportHeight="48.0"/>
 
     <path
-        android:fill="#00000000"
-        android:stroke="#CCCCCC"
-        android:strokeWidth="1.0"
-        android:pathData="M10.2,9.0"/>
-    <path
-        android:fill="#00000000"
-        android:stroke="#CCCCCC"
-        android:strokeWidth="1.0"
-        android:pathData="M21.0,16.0l0.0,-2.0l-8.0,-5.0L13.0,3.5C13.0,2.7 12.3,2.0 11.5,2.0C10.7,2.0 10.0,2.7 10.0,3.5L10.0,9.0l-8.0,5.0l0.0,2.0l8.0,-2.5L10.0,19.0l-2.0,1.5L8.0,22.0l3.5,-1.0l3.5,1.0l0.0,-1.5L13.0,19.0l0.0,-5.5L21.0,16.0z"/>
+        android:fill="#4DFFFFFF"
+        android:pathData="M26.0,18.0L26.0,7.0c0.0,-1.7 -1.3,-3.0 -3.0,-3.0c-1.7,0.0 -3.0,1.3 -3.0,3.0l0.0,7.4L35.7,30.0l6.3,2.0l0.0,-4.0L26.0,18.0zM6.0,10.5l10.0,10.0L4.0,28.0l0.0,4.0l16.0,-5.0l0.0,11.0l-4.0,3.0l0.0,3.0l7.0,-2.0l7.0,2.0l0.0,-3.0l-4.0,-3.0l0.0,-7.5L37.5,42.0l2.5,-2.5L8.5,8.0L6.0,10.5z"/>
 </vector>
diff --git a/packages/SystemUI/res/drawable/ic_qs_airplane_on.xml b/packages/SystemUI/res/drawable/ic_qs_airplane_on.xml
index 95c20bb..c1e3c7e 100644
--- a/packages/SystemUI/res/drawable/ic_qs_airplane_on.xml
+++ b/packages/SystemUI/res/drawable/ic_qs_airplane_on.xml
@@ -19,13 +19,13 @@
         android:height="64dp"/>
 
     <viewport
-        android:viewportWidth="24.0"
-        android:viewportHeight="24.0"/>
+        android:viewportWidth="48.0"
+        android:viewportHeight="48.0"/>
 
     <path
         android:fill="#FFFFFFFF"
-        android:pathData="M10.2,9.0"/>
+        android:pathData="M20.4,18.0"/>
     <path
         android:fill="#FFFFFFFF"
-        android:pathData="M21.0,16.0l0.0,-2.0l-8.0,-5.0L13.0,3.5C13.0,2.7 12.3,2.0 11.5,2.0C10.7,2.0 10.0,2.7 10.0,3.5L10.0,9.0l-8.0,5.0l0.0,2.0l8.0,-2.5L10.0,19.0l-2.0,1.5L8.0,22.0l3.5,-1.0l3.5,1.0l0.0,-1.5L13.0,19.0l0.0,-5.5L21.0,16.0z"/>
+        android:pathData="M42.0,32.0l0.0,-4.0L26.0,18.0L26.0,7.0c0.0,-1.7 -1.3,-3.0 -3.0,-3.0c-1.7,0.0 -3.0,1.3 -3.0,3.0l0.0,11.0L4.0,28.0l0.0,4.0l16.0,-5.0l0.0,11.0l-4.0,3.0l0.0,3.0l7.0,-2.0l7.0,2.0l0.0,-3.0l-4.0,-3.0L26.0,27.0L42.0,32.0z"/>
 </vector>
diff --git a/packages/SystemUI/res/drawable/ic_qs_close.xml b/packages/SystemUI/res/drawable/ic_qs_back.xml
similarity index 78%
rename from packages/SystemUI/res/drawable/ic_qs_close.xml
rename to packages/SystemUI/res/drawable/ic_qs_back.xml
index dd43e6c..52039f5 100644
--- a/packages/SystemUI/res/drawable/ic_qs_close.xml
+++ b/packages/SystemUI/res/drawable/ic_qs_back.xml
@@ -24,5 +24,5 @@
 
     <path
         android:fill="#FFFFFFFF"
-        android:pathData="M19.0,6.4l-1.3999996,-1.4000001 -5.6000004,5.6000004 -5.6,-5.6000004 -1.4000001,1.4000001 5.6000004,5.6 -5.6000004,5.6000004 1.4000001,1.3999996 5.6,-5.6000004 5.6000004,5.6000004 1.3999996,-1.3999996 -5.6000004,-5.6000004z"/>
+        android:pathData="M20.0,11.0L7.8,11.0l5.6,-5.6L12.0,4.0l-8.0,8.0l8.0,8.0l1.4,-1.4L7.8,13.0L20.0,13.0L20.0,11.0z"/>
 </vector>
diff --git a/packages/SystemUI/res/drawable/ic_qs_bluetooth_connected.xml b/packages/SystemUI/res/drawable/ic_qs_bluetooth_connected.xml
index 61a7777..3957d02 100644
--- a/packages/SystemUI/res/drawable/ic_qs_bluetooth_connected.xml
+++ b/packages/SystemUI/res/drawable/ic_qs_bluetooth_connected.xml
@@ -19,10 +19,10 @@
         android:height="64dp"/>
 
     <viewport
-        android:viewportWidth="24.0"
-        android:viewportHeight="24.0"/>
+        android:viewportWidth="48.0"
+        android:viewportHeight="48.0"/>
 
     <path
         android:fill="#FFFFFFFF"
-        android:pathData="M17.7,7.7L12.0,2.0l-1.0,0.0l0.0,7.6L6.4,5.0L5.0,6.4l5.6,5.6L5.0,17.6L6.4,19.0l4.6,-4.6L11.0,22.0l1.0,0.0l5.7,-5.7L13.4,12.0L17.7,7.7zM13.0,5.8l1.9,1.9L13.0,9.6L13.0,5.8zM14.9,16.3L13.0,18.2l0.0,-3.8L14.9,16.3z"/>
+        android:pathData="M14.0,24.0l-4.0,-4.0l-4.0,4.0l4.0,4.0L14.0,24.0zM35.4,15.4L24.0,4.0l-2.0,0.0l0.0,15.2L12.8,10.0L10.0,12.8L21.2,24.0L10.0,35.2l2.8,2.8l9.2,-9.2L22.0,44.0l2.0,0.0l11.4,-11.4L26.8,24.0L35.4,15.4zM26.0,11.7l3.8,3.8L26.0,19.2L26.0,11.7zM29.8,32.6L26.0,36.3l0.0,-7.5L29.8,32.6zM38.0,20.0l-4.0,4.0l4.0,4.0l4.0,-4.0L38.0,20.0z"/>
 </vector>
diff --git a/packages/SystemUI/res/drawable/ic_qs_close.xml b/packages/SystemUI/res/drawable/ic_qs_bluetooth_connecting.xml
similarity index 60%
copy from packages/SystemUI/res/drawable/ic_qs_close.xml
copy to packages/SystemUI/res/drawable/ic_qs_bluetooth_connecting.xml
index dd43e6c..e4038f9 100644
--- a/packages/SystemUI/res/drawable/ic_qs_close.xml
+++ b/packages/SystemUI/res/drawable/ic_qs_bluetooth_connecting.xml
@@ -19,10 +19,10 @@
         android:height="64dp"/>
 
     <viewport
-        android:viewportWidth="24.0"
-        android:viewportHeight="24.0"/>
+        android:viewportWidth="48.0"
+        android:viewportHeight="48.0"/>
 
     <path
         android:fill="#FFFFFFFF"
-        android:pathData="M19.0,6.4l-1.3999996,-1.4000001 -5.6000004,5.6000004 -5.6,-5.6000004 -1.4000001,1.4000001 5.6000004,5.6 -5.6000004,5.6000004 1.4000001,1.3999996 5.6,-5.6000004 5.6000004,5.6000004 1.3999996,-1.3999996 -5.6000004,-5.6000004z"/>
+        android:pathData="M28.5,24.0l4.6,4.6c0.6,-1.4 0.9,-3.0 0.9,-4.7c0.0,-1.6 -0.3,-3.2 -0.9,-4.6L28.5,24.0zM39.1,13.4L36.5,16.0c1.3,2.4 2.0,5.1 2.0,8.0s-0.7,5.6 -2.0,8.0l2.4,2.4c1.9,-3.1 3.1,-6.7 3.1,-10.6C42.0,20.0 40.9,16.5 39.1,13.4zM31.4,15.4L20.0,4.0l-2.0,0.0l0.0,15.2L8.8,10.0L6.0,12.8L17.2,24.0L6.0,35.2L8.8,38.0l9.2,-9.2L18.0,44.0l2.0,0.0l11.4,-11.4L22.8,24.0L31.4,15.4zM22.0,11.7l3.8,3.8L22.0,19.2L22.0,11.7zM25.8,32.6L22.0,36.3l0.0,-7.5L25.8,32.6z"/>
 </vector>
diff --git a/packages/SystemUI/res/drawable/ic_qs_bluetooth_off.xml b/packages/SystemUI/res/drawable/ic_qs_bluetooth_off.xml
index 7ac1cb9..00c5af8 100644
--- a/packages/SystemUI/res/drawable/ic_qs_bluetooth_off.xml
+++ b/packages/SystemUI/res/drawable/ic_qs_bluetooth_off.xml
@@ -19,12 +19,10 @@
         android:height="64dp"/>
 
     <viewport
-        android:viewportWidth="24.0"
-        android:viewportHeight="24.0"/>
+        android:viewportWidth="48.0"
+        android:viewportHeight="48.0"/>
 
     <path
-        android:fill="#00000000"
-        android:stroke="#CCCCCC"
-        android:strokeWidth="1.0"
-        android:pathData="M17.7,7.7L12.0,2.0l-1.0,0.0l0.0,7.6L6.4,5.0L5.0,6.4l5.6,5.6L5.0,17.6L6.4,19.0l4.6,-4.6L11.0,22.0l1.0,0.0l5.7,-5.7L13.4,12.0L17.7,7.7zM13.0,5.8l1.9,1.9L13.0,9.6L13.0,5.8zM14.9,16.3L13.0,18.2l0.0,-3.8L14.9,16.3z"/>
+        android:fill="#4DFFFFFF"
+        android:pathData="M26.0,11.8l3.8,3.8l-3.2,3.2l2.8,2.8l6.0,-6.0L24.0,4.2l-2.0,0.0l0.0,10.1l4.0,4.0L26.0,11.8zM10.8,8.2L8.0,11.0l13.2,13.2L10.0,35.3l2.8,2.8L22.0,29.0l0.0,15.2l2.0,0.0l8.6,-8.6l4.6,4.6l2.8,-2.8L10.8,8.2zM26.0,36.5L26.0,29.0l3.8,3.8L26.0,36.5z"/>
 </vector>
diff --git a/packages/SystemUI/res/drawable/ic_qs_bluetooth_on.xml b/packages/SystemUI/res/drawable/ic_qs_bluetooth_on.xml
index 61a7777..2b14f33 100644
--- a/packages/SystemUI/res/drawable/ic_qs_bluetooth_on.xml
+++ b/packages/SystemUI/res/drawable/ic_qs_bluetooth_on.xml
@@ -19,10 +19,10 @@
         android:height="64dp"/>
 
     <viewport
-        android:viewportWidth="24.0"
-        android:viewportHeight="24.0"/>
+        android:viewportWidth="48.0"
+        android:viewportHeight="48.0"/>
 
     <path
         android:fill="#FFFFFFFF"
-        android:pathData="M17.7,7.7L12.0,2.0l-1.0,0.0l0.0,7.6L6.4,5.0L5.0,6.4l5.6,5.6L5.0,17.6L6.4,19.0l4.6,-4.6L11.0,22.0l1.0,0.0l5.7,-5.7L13.4,12.0L17.7,7.7zM13.0,5.8l1.9,1.9L13.0,9.6L13.0,5.8zM14.9,16.3L13.0,18.2l0.0,-3.8L14.9,16.3z"/>
+        android:pathData="M35.4,15.4L24.0,4.0l-2.0,0.0l0.0,15.2L12.8,10.0L10.0,12.8L21.2,24.0L10.0,35.2l2.8,2.8l9.2,-9.2L22.0,44.0l2.0,0.0l11.4,-11.4L26.8,24.0L35.4,15.4zM26.0,11.7l3.8,3.8L26.0,19.2L26.0,11.7zM29.8,32.6L26.0,36.3l0.0,-7.5L29.8,32.6z"/>
 </vector>
diff --git a/packages/SystemUI/res/drawable/ic_qs_cast_off.xml b/packages/SystemUI/res/drawable/ic_qs_cast_off.xml
index 130c639..2a9541e 100644
--- a/packages/SystemUI/res/drawable/ic_qs_cast_off.xml
+++ b/packages/SystemUI/res/drawable/ic_qs_cast_off.xml
@@ -19,12 +19,10 @@
         android:height="64dp"/>
 
     <viewport
-        android:viewportWidth="24.0"
-        android:viewportHeight="24.0"/>
+        android:viewportWidth="48.0"
+        android:viewportHeight="48.0"/>
 
     <path
-        android:fill="#00000000"
-        android:stroke="#CCCCCC"
-        android:strokeWidth="1.0"
-        android:pathData="M21.0,3.0L3.0,3.0C1.9,3.0 1.0,3.9 1.0,5.0l0.0,3.0l2.0,0.0L3.0,5.0l18.0,0.0l0.0,14.0l-7.0,0.0l0.0,2.0l7.0,0.0c1.1,0.0 2.0,-0.9 2.0,-2.0L23.0,5.0C23.0,3.9 22.1,3.0 21.0,3.0zM1.0,18.0l0.0,3.0l3.0,0.0C4.0,19.3 2.7,18.0 1.0,18.0zM1.0,14.0l0.0,2.0c2.8,0.0 5.0,2.2 5.0,5.0l2.0,0.0C8.0,17.1 4.9,14.0 1.0,14.0zM1.0,10.0l0.0,2.0c5.0,0.0 9.0,4.0 9.0,9.0l2.0,0.0C12.0,14.9 7.1,10.0 1.0,10.0z"/>
+        android:fill="#4DFFFFFF"
+        android:pathData="M42.0,6.0L6.0,6.0c-2.2,0.0 -4.0,1.8 -4.0,4.0l0.0,6.0l4.0,0.0l0.0,-6.0l36.0,0.0l0.0,28.0L28.0,38.0l0.0,4.0l14.0,0.0c2.2,0.0 4.0,-1.8 4.0,-4.0L46.0,10.0C46.0,7.8 44.2,6.0 42.0,6.0zM2.0,36.0l0.0,6.0l6.0,0.0C8.0,38.7 5.3,36.0 2.0,36.0zM2.0,28.0l0.0,4.0c5.5,0.0 10.0,4.5 10.0,10.0l4.0,0.0C16.0,34.3 9.7,28.0 2.0,28.0zM2.0,20.0l0.0,4.0c9.9,0.0 18.0,8.1 18.0,18.0l4.0,0.0C24.0,29.8 14.1,20.0 2.0,20.0z"/>
 </vector>
diff --git a/packages/SystemUI/res/drawable/ic_qs_cast_on.xml b/packages/SystemUI/res/drawable/ic_qs_cast_on.xml
index 6c82b1c..8dacdc9 100644
--- a/packages/SystemUI/res/drawable/ic_qs_cast_on.xml
+++ b/packages/SystemUI/res/drawable/ic_qs_cast_on.xml
@@ -19,10 +19,10 @@
         android:height="64dp"/>
 
     <viewport
-        android:viewportWidth="24.0"
-        android:viewportHeight="24.0"/>
+        android:viewportWidth="48.0"
+        android:viewportHeight="48.0"/>
 
     <path
         android:fill="#FFFFFFFF"
-        android:pathData="M21.0,3.0L3.0,3.0C1.9,3.0 1.0,3.9 1.0,5.0l0.0,3.0l2.0,0.0L3.0,5.0l18.0,0.0l0.0,14.0l-7.0,0.0l0.0,2.0l7.0,0.0c1.1,0.0 2.0,-0.9 2.0,-2.0L23.0,5.0C23.0,3.9 22.1,3.0 21.0,3.0zM1.0,18.0l0.0,3.0l3.0,0.0C4.0,19.3 2.7,18.0 1.0,18.0zM1.0,14.0l0.0,2.0c2.8,0.0 5.0,2.2 5.0,5.0l2.0,0.0C8.0,17.1 4.9,14.0 1.0,14.0zM1.0,10.0l0.0,2.0c5.0,0.0 9.0,4.0 9.0,9.0l2.0,0.0C12.0,14.9 7.1,10.0 1.0,10.0z"/>
+        android:pathData="M42.0,6.0L6.0,6.0c-2.2,0.0 -4.0,1.8 -4.0,4.0l0.0,6.0l4.0,0.0l0.0,-6.0l36.0,0.0l0.0,28.0L28.0,38.0l0.0,4.0l14.0,0.0c2.2,0.0 4.0,-1.8 4.0,-4.0L46.0,10.0C46.0,7.8 44.2,6.0 42.0,6.0zM2.0,36.0l0.0,6.0l6.0,0.0C8.0,38.7 5.3,36.0 2.0,36.0zM2.0,28.0l0.0,4.0c5.5,0.0 10.0,4.5 10.0,10.0l4.0,0.0C16.0,34.3 9.7,28.0 2.0,28.0zM2.0,20.0l0.0,4.0c9.9,0.0 18.0,8.1 18.0,18.0l4.0,0.0C24.0,29.8 14.1,20.0 2.0,20.0z"/>
 </vector>
diff --git a/packages/SystemUI/res/drawable/ic_qs_ringer_audible.xml b/packages/SystemUI/res/drawable/ic_qs_ringer_audible.xml
index 3a20c58..787eec5 100644
--- a/packages/SystemUI/res/drawable/ic_qs_ringer_audible.xml
+++ b/packages/SystemUI/res/drawable/ic_qs_ringer_audible.xml
@@ -24,5 +24,5 @@
 
     <path
         android:fill="#FFFFFFFF"
-        android:pathData="M6.6,3.6L5.2,2.2C2.8,4.0 1.2,6.8 1.0,10.0l2.0,0.0C3.2,7.3 4.5,5.0 6.6,3.6zM20.0,10.0l2.0,0.0c-0.2,-3.2 -1.7,-6.0 -4.1,-7.8l-1.4,1.4C18.5,5.0 19.8,7.3 20.0,10.0zM18.0,10.5c0.0,-3.1 -2.1,-5.6 -5.0,-6.3L13.0,3.5C13.0,2.7 12.3,2.0 11.5,2.0C10.7,2.0 10.0,2.7 10.0,3.5l0.0,0.7c-2.9,0.7 -5.0,3.2 -5.0,6.3L5.0,16.0l-2.0,2.0l0.0,1.0l17.0,0.0l0.0,-1.0l-2.0,-2.0L18.0,10.5zM11.5,22.0c0.1,0.0 0.3,0.0 0.4,0.0c0.7,-0.1 1.2,-0.6 1.4,-1.2c0.1,-0.2 0.2,-0.5 0.2,-0.8l-4.0,0.0C9.5,21.1 10.4,22.0 11.5,22.0z" />
+        android:pathData="M11.5,22.0c1.1,0.0 2.0,-0.9 2.0,-2.0l-4.0,0.0C9.5,21.1 10.4,22.0 11.5,22.0zM18.0,16.0l0.0,-5.5c0.0,-3.1 -2.1,-5.6 -5.0,-6.3L13.0,3.5C13.0,2.7 12.3,2.0 11.5,2.0C10.7,2.0 10.0,2.7 10.0,3.5l0.0,0.7c-2.9,0.7 -5.0,3.2 -5.0,6.3L5.0,16.0l-2.0,2.0l0.0,1.0l17.0,0.0l0.0,-1.0L18.0,16.0z"/>
 </vector>
diff --git a/packages/SystemUI/res/drawable/ic_qs_zen_off.xml b/packages/SystemUI/res/drawable/ic_qs_zen_off.xml
deleted file mode 100644
index 73886ec..0000000
--- a/packages/SystemUI/res/drawable/ic_qs_zen_off.xml
+++ /dev/null
@@ -1,30 +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="64dp"
-        android:height="64dp"/>
-
-    <viewport
-        android:viewportWidth="24.0"
-        android:viewportHeight="24.0"/>
-
-    <path
-        android:fill="#00000000"
-        android:stroke="#CCCCCC"
-        android:strokeWidth="1.0"
-        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.0zM4.0,12.0c0.0,-4.4 3.6,-8.0 8.0,-8.0c1.8,0.0 3.5,0.6 4.9,1.7L5.7,16.9C4.6,15.5 4.0,13.8 4.0,12.0zM12.0,20.0c-1.8,0.0 -3.5,-0.6 -4.9,-1.7L18.3,7.1C19.4,8.5 20.0,10.2 20.0,12.0C20.0,16.4 16.4,20.0 12.0,20.0z" />
-</vector>
diff --git a/packages/SystemUI/res/drawable/ic_qs_zen_on.xml b/packages/SystemUI/res/drawable/ic_qs_zen_on.xml
index 8dff318..4887b32 100644
--- a/packages/SystemUI/res/drawable/ic_qs_zen_on.xml
+++ b/packages/SystemUI/res/drawable/ic_qs_zen_on.xml
@@ -19,10 +19,10 @@
         android:height="64dp"/>
 
     <viewport
-        android:viewportWidth="24.0"
-        android:viewportHeight="24.0"/>
+        android:viewportWidth="48.0"
+        android:viewportHeight="48.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.0zM4.0,12.0c0.0,-4.4 3.6,-8.0 8.0,-8.0c1.8,0.0 3.5,0.6 4.9,1.7L5.7,16.9C4.6,15.5 4.0,13.8 4.0,12.0zM12.0,20.0c-1.8,0.0 -3.5,-0.6 -4.9,-1.7L18.3,7.1C19.4,8.5 20.0,10.2 20.0,12.0C20.0,16.4 16.4,20.0 12.0,20.0z" />
+        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/ic_qs_close.xml b/packages/SystemUI/res/drawable/ic_ringer_audible.xml
similarity index 69%
copy from packages/SystemUI/res/drawable/ic_qs_close.xml
copy to packages/SystemUI/res/drawable/ic_ringer_audible.xml
index dd43e6c..2969948 100644
--- a/packages/SystemUI/res/drawable/ic_qs_close.xml
+++ b/packages/SystemUI/res/drawable/ic_ringer_audible.xml
@@ -15,8 +15,8 @@
 -->
 <vector xmlns:android="http://schemas.android.com/apk/res/android" >
     <size
-        android:width="64dp"
-        android:height="64dp"/>
+        android:width="32dp"
+        android:height="32dp"/>
 
     <viewport
         android:viewportWidth="24.0"
@@ -24,5 +24,5 @@
 
     <path
         android:fill="#FFFFFFFF"
-        android:pathData="M19.0,6.4l-1.3999996,-1.4000001 -5.6000004,5.6000004 -5.6,-5.6000004 -1.4000001,1.4000001 5.6000004,5.6 -5.6000004,5.6000004 1.4000001,1.3999996 5.6,-5.6000004 5.6000004,5.6000004 1.3999996,-1.3999996 -5.6000004,-5.6000004z"/>
+        android:pathData="M11.5,22.0c1.1,0.0 2.0,-0.9 2.0,-2.0l-4.0,0.0C9.5,21.1 10.4,22.0 11.5,22.0zM18.0,16.0l0.0,-5.5c0.0,-3.1 -2.1,-5.6 -5.0,-6.3L13.0,3.5C13.0,2.7 12.3,2.0 11.5,2.0C10.7,2.0 10.0,2.7 10.0,3.5l0.0,0.7c-2.9,0.7 -5.0,3.2 -5.0,6.3L5.0,16.0l-2.0,2.0l0.0,1.0l17.0,0.0l0.0,-1.0L18.0,16.0z"/>
 </vector>
diff --git a/packages/SystemUI/res/drawable/ic_qs_close.xml b/packages/SystemUI/res/drawable/ic_ringer_silent.xml
similarity index 66%
copy from packages/SystemUI/res/drawable/ic_qs_close.xml
copy to packages/SystemUI/res/drawable/ic_ringer_silent.xml
index dd43e6c..b5837f6 100644
--- a/packages/SystemUI/res/drawable/ic_qs_close.xml
+++ b/packages/SystemUI/res/drawable/ic_ringer_silent.xml
@@ -15,8 +15,8 @@
 -->
 <vector xmlns:android="http://schemas.android.com/apk/res/android" >
     <size
-        android:width="64dp"
-        android:height="64dp"/>
+        android:width="32dp"
+        android:height="32dp"/>
 
     <viewport
         android:viewportWidth="24.0"
@@ -24,5 +24,5 @@
 
     <path
         android:fill="#FFFFFFFF"
-        android:pathData="M19.0,6.4l-1.3999996,-1.4000001 -5.6000004,5.6000004 -5.6,-5.6000004 -1.4000001,1.4000001 5.6000004,5.6 -5.6000004,5.6000004 1.4000001,1.3999996 5.6,-5.6000004 5.6000004,5.6000004 1.3999996,-1.3999996 -5.6000004,-5.6000004z"/>
+        android:pathData="M11.5,22.0c1.1,0.0 2.0,-0.9 2.0,-2.0l-4.0,0.0C9.5,21.1 10.4,22.0 11.5,22.0zM18.0,10.5c0.0,-3.1 -2.1,-5.6 -5.0,-6.3L13.0,3.5C13.0,2.7 12.3,2.0 11.5,2.0C10.7,2.0 10.0,2.7 10.0,3.5l0.0,0.7C9.5,4.3 9.0,4.5 8.6,4.7l9.4,9.4L18.0,10.5zM17.7,19.0l2.0,2.0l1.3,-1.3L4.3,3.0L3.0,4.3l2.9,2.9C5.3,8.2 5.0,9.3 5.0,10.5L5.0,16.0l-2.0,2.0l0.0,1.0L17.7,19.0z" />
 </vector>
diff --git a/packages/SystemUI/res/drawable/ic_qs_close.xml b/packages/SystemUI/res/drawable/ic_ringer_vibrate.xml
similarity index 64%
copy from packages/SystemUI/res/drawable/ic_qs_close.xml
copy to packages/SystemUI/res/drawable/ic_ringer_vibrate.xml
index dd43e6c..d8ded58 100644
--- a/packages/SystemUI/res/drawable/ic_qs_close.xml
+++ b/packages/SystemUI/res/drawable/ic_ringer_vibrate.xml
@@ -15,8 +15,8 @@
 -->
 <vector xmlns:android="http://schemas.android.com/apk/res/android" >
     <size
-        android:width="64dp"
-        android:height="64dp"/>
+        android:width="32dp"
+        android:height="32dp"/>
 
     <viewport
         android:viewportWidth="24.0"
@@ -24,5 +24,5 @@
 
     <path
         android:fill="#FFFFFFFF"
-        android:pathData="M19.0,6.4l-1.3999996,-1.4000001 -5.6000004,5.6000004 -5.6,-5.6000004 -1.4000001,1.4000001 5.6000004,5.6 -5.6000004,5.6000004 1.4000001,1.3999996 5.6,-5.6000004 5.6000004,5.6000004 1.3999996,-1.3999996 -5.6000004,-5.6000004z"/>
+        android:pathData="M0.0,15.0l2.0,0.0L2.0,9.0L0.0,9.0L0.0,15.0zM3.0,17.0l2.0,0.0L5.0,7.0L3.0,7.0L3.0,17.0zM22.0,9.0l0.0,6.0l2.0,0.0L24.0,9.0L22.0,9.0zM19.0,17.0l2.0,0.0L21.0,7.0l-2.0,0.0L19.0,17.0zM16.5,3.0l-9.0,0.0C6.7,3.0 6.0,3.7 6.0,4.5l0.0,15.0C6.0,20.3 6.7,21.0 7.5,21.0l9.0,0.0c0.8,0.0 1.5,-0.7 1.5,-1.5l0.0,-15.0C18.0,3.7 17.3,3.0 16.5,3.0zM16.0,19.0L8.0,19.0L8.0,5.0l8.0,0.0L16.0,19.0z"/>
 </vector>
diff --git a/packages/SystemUI/res/drawable/ic_vol_zen_off.xml b/packages/SystemUI/res/drawable/ic_vol_zen_off.xml
new file mode 100644
index 0000000..477c36b
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_vol_zen_off.xml
@@ -0,0 +1,28 @@
+<!--
+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="32dp"
+        android:height="32dp"/>
+
+    <viewport
+        android:viewportWidth="48.0"
+        android:viewportHeight="48.0"/>
+
+    <path
+        android:fill="#4DFFFFFF"
+        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/ic_qs_close.xml b/packages/SystemUI/res/drawable/ic_vol_zen_on.xml
similarity index 61%
copy from packages/SystemUI/res/drawable/ic_qs_close.xml
copy to packages/SystemUI/res/drawable/ic_vol_zen_on.xml
index dd43e6c..0a43a7b 100644
--- a/packages/SystemUI/res/drawable/ic_qs_close.xml
+++ b/packages/SystemUI/res/drawable/ic_vol_zen_on.xml
@@ -15,14 +15,14 @@
 -->
 <vector xmlns:android="http://schemas.android.com/apk/res/android" >
     <size
-        android:width="64dp"
-        android:height="64dp"/>
+        android:width="32dp"
+        android:height="32dp"/>
 
     <viewport
-        android:viewportWidth="24.0"
-        android:viewportHeight="24.0"/>
+        android:viewportWidth="48.0"
+        android:viewportHeight="48.0"/>
 
     <path
         android:fill="#FFFFFFFF"
-        android:pathData="M19.0,6.4l-1.3999996,-1.4000001 -5.6000004,5.6000004 -5.6,-5.6000004 -1.4000001,1.4000001 5.6000004,5.6 -5.6000004,5.6000004 1.4000001,1.3999996 5.6,-5.6000004 5.6000004,5.6000004 1.3999996,-1.3999996 -5.6000004,-5.6000004z"/>
+        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/stat_sys_ringer_zen.xml b/packages/SystemUI/res/drawable/stat_sys_ringer_zen.xml
index afab88f..5992470 100644
--- a/packages/SystemUI/res/drawable/stat_sys_ringer_zen.xml
+++ b/packages/SystemUI/res/drawable/stat_sys_ringer_zen.xml
@@ -15,14 +15,14 @@
 -->
 <vector xmlns:android="http://schemas.android.com/apk/res/android" >
     <size
-        android:width="19dp"
-        android:height="19dp"/>
+        android:width="18dp"
+        android:height="18dp"/>
 
     <viewport
-        android:viewportWidth="24.0"
-        android:viewportHeight="24.0"/>
+        android:viewportWidth="48.0"
+        android:viewportHeight="48.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.0zM4.0,12.0c0.0,-4.4 3.6,-8.0 8.0,-8.0c1.8,0.0 3.5,0.6 4.9,1.7L5.7,16.9C4.6,15.5 4.0,13.8 4.0,12.0zM12.0,20.0c-1.8,0.0 -3.5,-0.6 -4.9,-1.7L18.3,7.1C19.4,8.5 20.0,10.2 20.0,12.0C20.0,16.4 16.4,20.0 12.0,20.0z" />
+        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/layout-ldrtl/navigation_bar.xml b/packages/SystemUI/res/layout-ldrtl/navigation_bar.xml
index 84f0950..a291495 100644
--- a/packages/SystemUI/res/layout-ldrtl/navigation_bar.xml
+++ b/packages/SystemUI/res/layout-ldrtl/navigation_bar.xml
@@ -158,17 +158,6 @@
                 />
         </LinearLayout>
 
-        <com.android.systemui.statusbar.policy.KeyButtonView
-            android:layout_width="80dp"
-            android:id="@+id/search_light"
-            android:layout_height="match_parent"
-            android:layout_gravity="center_horizontal"
-            android:src="@drawable/search_light"
-            android:scaleType="center"
-            android:visibility="gone"
-            android:contentDescription="@string/accessibility_search_light"
-            />
-
         <com.android.systemui.statusbar.policy.DeadZone
             android:id="@+id/deadzone"
             android:layout_height="match_parent"
@@ -316,17 +305,6 @@
                 />
         </LinearLayout>
 
-        <com.android.systemui.statusbar.policy.KeyButtonView
-            android:id="@+id/search_light"
-            android:layout_height="80dp"
-            android:layout_width="match_parent"
-            android:layout_gravity="center_vertical"
-            android:src="@drawable/search_light"
-            android:scaleType="center"
-            android:visibility="gone"
-            android:contentDescription="@string/accessibility_search_light"
-            />
-
         <com.android.systemui.statusbar.policy.DeadZone
             android:id="@+id/deadzone"
             android:layout_height="match_parent"
diff --git a/packages/SystemUI/res/layout-sw600dp/navigation_bar.xml b/packages/SystemUI/res/layout-sw600dp/navigation_bar.xml
index 34b674b..f8b7bae 100644
--- a/packages/SystemUI/res/layout-sw600dp/navigation_bar.xml
+++ b/packages/SystemUI/res/layout-sw600dp/navigation_bar.xml
@@ -156,17 +156,6 @@
                 />
         </LinearLayout>
 
-        <com.android.systemui.statusbar.policy.KeyButtonView
-            android:layout_width="128dp"
-            android:id="@+id/search_light"
-            android:layout_height="match_parent"
-            android:layout_gravity="center_horizontal"
-            android:src="@drawable/search_light"
-            android:scaleType="center"
-            android:visibility="gone"
-            android:contentDescription="@string/accessibility_search_light"
-            />
-
         <com.android.systemui.statusbar.policy.DeadZone
             android:id="@+id/deadzone"
             android:layout_height="match_parent"
@@ -313,17 +302,6 @@
                 />
         </LinearLayout>
 
-        <com.android.systemui.statusbar.policy.KeyButtonView
-            android:layout_width="162dp"
-            android:id="@+id/search_light"
-            android:layout_height="match_parent"
-            android:layout_gravity="center_horizontal"
-            android:src="@drawable/search_light"
-            android:scaleType="center"
-            android:visibility="gone"
-            android:contentDescription="@string/accessibility_search_light"
-            />
-
         <com.android.systemui.statusbar.policy.DeadZone
             android:id="@+id/deadzone"
             android:layout_height="match_parent"
diff --git a/packages/SystemUI/res/layout/heads_up.xml b/packages/SystemUI/res/layout/heads_up.xml
index 236fdc3..0e2b6d6 100644
--- a/packages/SystemUI/res/layout/heads_up.xml
+++ b/packages/SystemUI/res/layout/heads_up.xml
@@ -17,13 +17,14 @@
 <com.android.systemui.statusbar.policy.HeadsUpNotificationView
         xmlns:android="http://schemas.android.com/apk/res/android"
         android:layout_height="match_parent"
-        android:layout_width="match_parent">
+        android:layout_width="match_parent"
+        android:background="@drawable/heads_up_scrim">
 
         <FrameLayout
                 android:layout_height="wrap_content"
-                android:layout_marginStart="@dimen/notification_side_padding"
-                android:layout_marginEnd="@dimen/notification_side_padding"
-                android:elevation="16dp"
+                android:paddingStart="@dimen/notification_side_padding"
+                android:paddingEnd="@dimen/notification_side_padding"
+                android:elevation="8dp"
                 android:id="@+id/content_holder"
                 style="@style/NotificationsQuickSettings" />
 
diff --git a/packages/SystemUI/res/layout/keyguard_bottom_area.xml b/packages/SystemUI/res/layout/keyguard_bottom_area.xml
index 936f73b..9bf42b2 100644
--- a/packages/SystemUI/res/layout/keyguard_bottom_area.xml
+++ b/packages/SystemUI/res/layout/keyguard_bottom_area.xml
@@ -22,7 +22,7 @@
     android:layout_height="match_parent"
     android:layout_width="match_parent"
     >
-    <com.android.systemui.statusbar.phone.SwipeAffordanceView
+    <com.android.systemui.statusbar.AlphaImageView
         android:id="@+id/camera_button"
         android:layout_height="64dp"
         android:layout_width="64dp"
@@ -30,10 +30,9 @@
         android:tint="#ffffffff"
         android:src="@drawable/ic_camera_alt_24dp"
         android:scaleType="center"
-        android:contentDescription="@string/accessibility_camera_button"
-        systemui:swipeDirection="start"/>
+        android:contentDescription="@string/accessibility_camera_button" />
 
-    <com.android.systemui.statusbar.phone.SwipeAffordanceView
+    <com.android.systemui.statusbar.AlphaImageView
         android:id="@+id/phone_button"
         android:layout_height="64dp"
         android:layout_width="64dp"
@@ -41,8 +40,7 @@
         android:tint="#ffffffff"
         android:src="@drawable/ic_phone_24dp"
         android:scaleType="center"
-        android:contentDescription="@string/accessibility_phone_button"
-        systemui:swipeDirection="end"/>
+        android:contentDescription="@string/accessibility_phone_button" />
 
     <com.android.systemui.statusbar.phone.KeyguardIndicationTextView
         android:id="@+id/keyguard_indication_text"
@@ -54,15 +52,13 @@
         android:textColor="#ffffff"
         android:textAppearance="?android:attr/textAppearanceSmall"/>
 
-    <ImageView
+    <com.android.systemui.statusbar.AlphaImageView
         android:id="@+id/lock_icon"
         android:layout_width="64dp"
         android:layout_height="64dp"
         android:layout_gravity="bottom|center_horizontal"
         android:src="@drawable/ic_lock_24dp"
         android:scaleType="center"
-        android:alpha="0.7"
-        android:layerType="hardware"
-        android:tint="#ffffffff"/>
+        android:tint="#ffffffff" />
 
 </com.android.systemui.statusbar.phone.KeyguardBottomAreaView>
diff --git a/packages/SystemUI/res/layout/navigation_bar.xml b/packages/SystemUI/res/layout/navigation_bar.xml
index fa6f7f5..7616cb1 100644
--- a/packages/SystemUI/res/layout/navigation_bar.xml
+++ b/packages/SystemUI/res/layout/navigation_bar.xml
@@ -160,22 +160,6 @@
                 />
         </LinearLayout>
 
-        <FrameLayout
-            android:layout_width="match_parent"
-            android:layout_height="match_parent">
-
-            <com.android.systemui.statusbar.policy.KeyButtonView
-                android:layout_width="80dp"
-                android:id="@+id/search_light"
-                android:layout_height="match_parent"
-                android:layout_gravity="center"
-                android:src="@drawable/search_light"
-                android:scaleType="center"
-                android:visibility="gone"
-                android:contentDescription="@string/accessibility_search_light"
-                />
-        </FrameLayout>
-
         <com.android.systemui.statusbar.policy.DeadZone
             android:id="@+id/deadzone"
             android:layout_height="match_parent"
@@ -325,19 +309,6 @@
                 />
         </LinearLayout>
 
-        <com.android.systemui.statusbar.policy.KeyButtonView
-            android:id="@+id/search_light"
-            android:layout_height="80dp"
-            android:layout_width="match_parent"
-            android:layout_gravity="center_vertical"
-            android:src="@drawable/search_light_land"
-            android:scaleType="center"
-            android:visibility="gone"
-            android:contentDescription="@string/accessibility_search_light"
-            />
-
-        <!-- No camera button in landscape mode -->
-
         <com.android.systemui.statusbar.policy.DeadZone
             android:id="@+id/deadzone"
             android:layout_height="match_parent"
diff --git a/packages/SystemUI/res/layout/qs_detail.xml b/packages/SystemUI/res/layout/qs_detail.xml
new file mode 100644
index 0000000..e1c460c
--- /dev/null
+++ b/packages/SystemUI/res/layout/qs_detail.xml
@@ -0,0 +1,57 @@
+<?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="match_parent"
+    android:layout_height="match_parent"
+    android:background="@color/system_primary_color" >
+
+    <ImageView
+        android:id="@android:id/button1"
+        android:layout_width="64dp"
+        android:layout_height="64dp"
+        android:layout_alignParentStart="true"
+        android:contentDescription="@string/accessibility_quick_settings_close"
+        android:padding="@dimen/qs_panel_padding"
+        android:src="@drawable/ic_qs_back" />
+
+    <TextView
+        android:id="@android:id/title"
+        android:layout_width="match_parent"
+        android:layout_height="64dp"
+        android:layout_alignParentTop="true"
+        android:layout_toEndOf="@android:id/button1"
+        android:layout_toStartOf="@android:id/checkbox"
+        android:gravity="center_vertical"
+        android:textAppearance="@style/TextAppearance.QS.DetailHeader" />
+
+    <ImageView
+        android:id="@android:id/custom"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:layout_below="@android:id/title"
+        android:layout_marginLeft="16dip"
+        android:layout_marginRight="16dip"
+        android:scaleType="fitXY"
+        android:src="?android:attr/dividerHorizontal" />
+
+    <FrameLayout
+        android:id="@android:id/content"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        android:layout_below="@android:id/custom" />
+
+</RelativeLayout>
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/qs_zen_mode_detail.xml b/packages/SystemUI/res/layout/qs_zen_mode_detail.xml
deleted file mode 100644
index 85b294d..0000000
--- a/packages/SystemUI/res/layout/qs_zen_mode_detail.xml
+++ /dev/null
@@ -1,77 +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.
--->
-<com.android.systemui.qs.tiles.ZenModeDetail xmlns:android="http://schemas.android.com/apk/res/android"
-    android:layout_width="match_parent"
-    android:layout_height="match_parent"
-    android:background="@color/system_secondary_color" >
-
-    <ImageView
-        android:id="@android:id/button1"
-        android:src="@drawable/ic_qs_close"
-        android:layout_width="64dp"
-        android:layout_height="64dp"
-        android:layout_alignParentStart="true"
-        android:padding="@dimen/qs_panel_padding" />
-
-    <Switch
-        android:id="@android:id/checkbox"
-        android:layout_width="wrap_content"
-        android:layout_height="64dp"
-        android:layout_alignParentEnd="true"
-        android:gravity="center"
-        android:padding="@dimen/qs_panel_padding" />
-
-    <TextView
-        android:id="@android:id/title"
-        android:layout_width="match_parent"
-        android:layout_height="64dp"
-        android:layout_toEndOf="@android:id/button1"
-        android:layout_toStartOf="@android:id/checkbox"
-        android:textAppearance="@style/TextAppearance.StatusBar.Expanded.Clock"
-        android:gravity="center_vertical"
-        android:paddingStart="@dimen/qs_panel_padding"
-        android:text="@string/zen_mode_title" />
-
-    <View
-        android:id="@android:id/custom"
-        android:layout_width="match_parent"
-        android:layout_height="2dp"
-        android:layout_below="@android:id/title"
-        android:background="#888" />
-
-    <ListView
-        android:id="@android:id/content"
-        android:layout_width="match_parent"
-        android:layout_height="match_parent"
-        android:layout_above="@android:id/button2"
-        android:layout_below="@android:id/custom"
-        android:divider="#00000000"
-        android:dividerHeight="0px" />
-
-    <TextView
-        android:id="@android:id/button2"
-        style="@style/QSBorderless"
-        android:layout_width="wrap_content"
-        android:layout_height="wrap_content"
-        android:layout_alignParentBottom="true"
-        android:layout_alignParentEnd="true"
-        android:textAppearance="@style/TextAppearance.StatusBar.Expanded.Clock"
-        android:padding="@dimen/qs_panel_padding"
-        android:text="@string/quick_settings_more_settings"
-        android:textAllCaps="true" />
-
-</com.android.systemui.qs.tiles.ZenModeDetail>
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/qs_zen_mode_detail_condition.xml b/packages/SystemUI/res/layout/qs_zen_mode_detail_condition.xml
deleted file mode 100644
index fd27aaf..0000000
--- a/packages/SystemUI/res/layout/qs_zen_mode_detail_condition.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.
--->
-<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
-    android:layout_width="match_parent"
-    android:layout_height="match_parent" >
-
-    <RadioButton
-        android:id="@android:id/checkbox"
-        android:layout_width="32dp"
-        android:layout_height="64dp"
-        android:layout_alignParentStart="true"
-        android:layout_marginStart="@dimen/qs_panel_padding"
-        android:gravity="center" />
-
-    <TextView
-        android:id="@android:id/title"
-        android:layout_width="match_parent"
-        android:layout_height="64dp"
-        android:layout_toEndOf="@android:id/checkbox"
-        android:layout_toStartOf="@android:id/button1"
-        android:ellipsize="end"
-        android:textAppearance="@style/TextAppearance.StatusBar.Expanded.Clock"
-        android:gravity="center_vertical"
-        android:maxLines="1"
-        android:text="@string/accessibility_back" />
-
-    <ImageView
-        android:id="@android:id/button1"
-        android:src="@drawable/ic_qs_minus"
-        android:layout_width="64dp"
-        android:layout_height="64dp"
-        android:layout_alignParentEnd="true"
-        android:layout_marginEnd="48dp"
-        android:padding="@dimen/qs_panel_padding"
-        android:paddingRight="0px" />
-
-    <ImageView
-        android:id="@android:id/button2"
-        android:src="@drawable/ic_qs_plus"
-        android:layout_width="64dp"
-        android:layout_height="64dp"
-        android:layout_alignParentEnd="true"
-        android:padding="@dimen/qs_panel_padding" />
-
-</RelativeLayout>
diff --git a/packages/SystemUI/res/layout/recents_empty.xml b/packages/SystemUI/res/layout/recents_empty.xml
index 6268628..ac6450b 100644
--- a/packages/SystemUI/res/layout/recents_empty.xml
+++ b/packages/SystemUI/res/layout/recents_empty.xml
@@ -19,8 +19,9 @@
     android:layout_width="wrap_content"
     android:layout_height="wrap_content"
     android:gravity="center"
-    android:textSize="40sp"
+    android:textSize="20sp"
     android:textColor="#ffffffff"
+    android:textStyle="italic"
     android:text="@string/recents_empty_message"
-    android:fontFamily="sans-serif-thin"
+    android:fontFamily="sans-serif-light"
     android:visibility="gone" />
\ 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 7de421c..85d2f16 100644
--- a/packages/SystemUI/res/layout/recents_task_view.xml
+++ b/packages/SystemUI/res/layout/recents_task_view.xml
@@ -25,36 +25,37 @@
     <com.android.systemui.recents.views.TaskBarView
         android:id="@+id/task_view_bar"
         android:layout_width="match_parent"
-        android:layout_height="wrap_content"
+        android:layout_height="56dp"
         android:layout_gravity="top|center_horizontal"
         android:background="@color/recents_task_bar_default_background_color">
         <ImageView
             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_gravity="center_vertical|start"
-            android:padding="8dp" />
+            android:layout_marginStart="16dp"
+            android:layout_gravity="center_vertical|start" />
         <TextView
             android:id="@+id/activity_description"
             android:layout_width="match_parent"
             android:layout_height="wrap_content"
-            android:layout_gravity="center_vertical|left"
-            android:layout_marginStart="@dimen/recents_task_view_application_icon_size"
-            android:layout_marginEnd="@dimen/recents_task_view_application_icon_size"
-            android:textSize="22sp"
+            android:layout_gravity="center_vertical|start"
+            android:layout_marginStart="64dp"
+            android:layout_marginEnd="64dp"
+            android:textSize="16sp"
             android:textColor="#ffffffff"
             android:text="@string/recents_empty_message"
-            android:fontFamily="sans-serif-light"
+            android:fontFamily="sans-serif-medium"
             android:singleLine="true"
             android:maxLines="2"
             android:ellipsize="marquee"
             android:fadingEdge="horizontal" />
         <ImageView
             android:id="@+id/dismiss_task"
-            android:layout_width="@dimen/recents_task_view_application_icon_size"
-            android:layout_height="@dimen/recents_task_view_application_icon_size"
+            android:layout_width="48dp"
+            android:layout_height="48dp"
+            android:layout_marginEnd="4dp"
             android:layout_gravity="center_vertical|end"
-            android:padding="23dp"
+            android:padding="18dp"
             android:src="@drawable/recents_dismiss_light" />
     </com.android.systemui.recents.views.TaskBarView>
 </com.android.systemui.recents.views.TaskView>
diff --git a/packages/SystemUI/res/layout/status_bar_expanded_header.xml b/packages/SystemUI/res/layout/status_bar_expanded_header.xml
index dfc3b22..7f34041 100644
--- a/packages/SystemUI/res/layout/status_bar_expanded_header.xml
+++ b/packages/SystemUI/res/layout/status_bar_expanded_header.xml
@@ -98,7 +98,8 @@
         android:gravity="center_vertical"
         android:ellipsize="marquee"
         android:textAppearance="?android:attr/textAppearanceSmall"
-        android:textColor="#ffffff" />
+        android:textColor="#ffffff"
+        android:singleLine="true" />
 
     <include
         layout="@layout/quick_settings_brightness_dialog"
diff --git a/packages/SystemUI/res/layout/user_switcher_host.xml b/packages/SystemUI/res/layout/user_switcher_host.xml
index 70c5042..816af57 100644
--- a/packages/SystemUI/res/layout/user_switcher_host.xml
+++ b/packages/SystemUI/res/layout/user_switcher_host.xml
@@ -27,7 +27,7 @@
     <FrameLayout
             android:layout_width="match_parent"
             android:layout_height="wrap_content"
-            android:layout_marginTop="@*android:dimen/volume_panel_top"
+            android:layout_marginTop="@dimen/volume_panel_top"
             android:background="@*android:drawable/dialog_full_holo_dark">
         <ListView android:id="@android:id/list"
                 android:layout_width="match_parent"
diff --git a/packages/SystemUI/res/layout/volume_dialog.xml b/packages/SystemUI/res/layout/volume_dialog.xml
new file mode 100644
index 0000000..5afa967
--- /dev/null
+++ b/packages/SystemUI/res/layout/volume_dialog.xml
@@ -0,0 +1,26 @@
+<?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.
+-->
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content"
+    android:background="@drawable/qs_panel_background"
+    android:translationZ="@dimen/volume_panel_z"
+    android:layout_margin="@dimen/volume_panel_z">
+
+        <include layout="@layout/volume_panel" />
+
+</FrameLayout>
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/volume_panel.xml b/packages/SystemUI/res/layout/volume_panel.xml
new file mode 100644
index 0000000..046862f
--- /dev/null
+++ b/packages/SystemUI/res/layout/volume_panel.xml
@@ -0,0 +1,68 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+     Copyright (C) 2007 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:id="@+id/visible_panel"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content"
+    android:orientation="horizontal" >
+
+    <FrameLayout
+        android:id="@+id/slider_panel"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:minHeight="64dip"
+        android:layout_toLeftOf="@+id/expand_button_divider" />
+
+    <ImageView
+        android:id="@+id/expand_button_divider"
+        android:layout_width="wrap_content"
+        android:layout_height="32dip"
+        android:layout_gravity="top"
+        android:layout_marginBottom="16dip"
+        android:layout_marginTop="16dip"
+        android:layout_toLeftOf="@+id/expand_button"
+        android:scaleType="fitXY"
+        android:src="?android:attr/dividerVertical" />
+
+    <ImageView
+        android:id="@+id/expand_button"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_alignParentRight="true"
+        android:layout_gravity="top"
+        style="@style/BorderlessButton.Tiny"
+        android:padding="16dip" />
+
+    <ImageView
+        android:id="@+id/zen_panel_divider"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:layout_below="@+id/slider_panel"
+        android:layout_marginLeft="16dip"
+        android:layout_marginRight="16dip"
+        android:scaleType="fitXY"
+        android:src="?android:attr/dividerHorizontal" />
+
+    <ViewStub
+        android:id="@+id/zen_panel_stub"
+        android:layout_below="@+id/zen_panel_divider"
+        android:inflatedId="@+id/zen_panel"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:layout="@layout/zen_mode_panel" />
+
+</RelativeLayout>
diff --git a/packages/SystemUI/res/layout/volume_panel_item.xml b/packages/SystemUI/res/layout/volume_panel_item.xml
new file mode 100644
index 0000000..4a2a0c0
--- /dev/null
+++ b/packages/SystemUI/res/layout/volume_panel_item.xml
@@ -0,0 +1,48 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2011 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="80dip"
+        android:orientation="horizontal"
+        android:layout_marginTop="8dip"
+        android:layout_marginBottom="8dip"
+        android:gravity="start|center_vertical">
+
+    <ImageView
+            android:id="@+id/stream_icon"
+            style="@style/BorderlessButton.Tiny"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:padding="16dip"
+            android:contentDescription="@null" />
+    <FrameLayout
+            android:id="@+id/seekbar_container"
+            android:layout_width="0dp"
+            android:layout_height="wrap_content"
+            android:layout_weight="1">
+        <SeekBar
+                style="?android:attr/seekBarStyle"
+                android:id="@+id/seekbar"
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                android:paddingTop="16dip"
+                android:paddingBottom="16dip"
+                android:paddingStart="11dip"
+                android:paddingEnd="11dip"
+                android:layout_marginEnd="16dip" />
+    </FrameLayout>
+</LinearLayout>
diff --git a/packages/SystemUI/res/layout/zen_mode_condition.xml b/packages/SystemUI/res/layout/zen_mode_condition.xml
new file mode 100644
index 0000000..6d63bb0
--- /dev/null
+++ b/packages/SystemUI/res/layout/zen_mode_condition.xml
@@ -0,0 +1,65 @@
+<?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="match_parent"
+    android:layout_height="wrap_content"
+    android:layout_marginLeft="@dimen/zen_mode_condition_detail_button_padding"
+    android:layout_marginRight="@dimen/zen_mode_condition_detail_button_padding" >
+
+    <RadioButton
+        android:id="@android:id/checkbox"
+        android:layout_width="40dp"
+        android:layout_marginStart="2dp"
+        android:layout_height="@dimen/zen_mode_condition_height"
+        android:layout_alignParentStart="true"
+        android:gravity="center" />
+
+    <TextView
+        android:id="@android:id/title"
+        android:layout_width="match_parent"
+        android:layout_height="@dimen/zen_mode_condition_height"
+        android:layout_toEndOf="@android:id/checkbox"
+        android:layout_toStartOf="@android:id/button1"
+        android:ellipsize="end"
+        android:gravity="center_vertical"
+        android:maxLines="1"
+        android:textAppearance="@style/TextAppearance.QS.DetailItemPrimary" />
+
+    <ImageView
+        android:id="@android:id/button1"
+        style="@style/BorderlessButton"
+        android:layout_width="@dimen/zen_mode_condition_height"
+        android:layout_height="@dimen/zen_mode_condition_height"
+        android:layout_alignParentEnd="true"
+        android:layout_centerVertical="true"
+        android:layout_marginEnd="@dimen/zen_mode_condition_height"
+        android:contentDescription="@string/accessibility_quick_settings_less_time"
+        android:padding="@dimen/zen_mode_condition_detail_button_padding"
+        android:src="@drawable/ic_qs_minus" />
+
+    <ImageView
+        android:id="@android:id/button2"
+        style="@style/BorderlessButton"
+        android:layout_width="@dimen/zen_mode_condition_height"
+        android:layout_height="@dimen/zen_mode_condition_height"
+        android:layout_alignParentEnd="true"
+        android:layout_centerVertical="true"
+        android:contentDescription="@string/accessibility_quick_settings_more_time"
+        android:padding="@dimen/zen_mode_condition_detail_button_padding"
+        android:src="@drawable/ic_qs_plus" />
+
+</RelativeLayout>
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/zen_mode_panel.xml b/packages/SystemUI/res/layout/zen_mode_panel.xml
new file mode 100644
index 0000000..0a8f852
--- /dev/null
+++ b/packages/SystemUI/res/layout/zen_mode_panel.xml
@@ -0,0 +1,54 @@
+<?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.
+-->
+<!-- extends LinearLayout -->
+<com.android.systemui.volume.ZenModePanel xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@+id/zen_mode_panel"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content"
+    android:background="@color/system_primary_color"
+    android:orientation="vertical"
+    android:paddingTop="@dimen/qs_panel_padding" >
+
+    <TextView
+        android:id="@android:id/title"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_alignParentStart="true"
+        android:layout_marginBottom="8dp"
+        android:layout_marginStart="@dimen/qs_panel_padding"
+        android:layout_marginEnd="@dimen/qs_panel_padding"
+        android:text="@string/zen_mode_title"
+        android:textAppearance="@style/TextAppearance.QS.DetailItemPrimary" />
+
+    <LinearLayout
+        android:id="@android:id/content"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:orientation="vertical" />
+
+    <TextView
+        android:id="@android:id/button2"
+        style="@style/BorderlessButton"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_alignParentEnd="true"
+        android:layout_marginEnd="4dp"
+        android:layout_gravity="end"
+        android:text="@string/quick_settings_more_settings"
+        android:textAppearance="@style/TextAppearance.QS.DetailButton" />
+
+</com.android.systemui.volume.ZenModePanel>
\ No newline at end of file
diff --git a/packages/SystemUI/res/values-af/strings.xml b/packages/SystemUI/res/values-af/strings.xml
index e43464c..c642626 100644
--- a/packages/SystemUI/res/values-af/strings.xml
+++ b/packages/SystemUI/res/values-af/strings.xml
@@ -120,6 +120,8 @@
     <string name="accessibility_desc_on" msgid="2385254693624345265">"Aan."</string>
     <string name="accessibility_desc_off" msgid="6475508157786853157">"Af"</string>
     <string name="accessibility_desc_connected" msgid="8366256693719499665">"Gekoppel."</string>
+    <!-- no translation found for accessibility_desc_connecting (3812924520316280149) -->
+    <skip />
     <string name="accessibility_data_connection_gprs" msgid="1606477224486747751">"GPRS"</string>
     <string name="accessibility_data_connection_1x" msgid="994133468120244018">"1 X"</string>
     <string name="accessibility_data_connection_hspa" msgid="2032328855462645198">"HSPA"</string>
@@ -156,6 +158,9 @@
     <string name="accessibility_quick_settings_bluetooth" msgid="5749054971341882340">"Bluetooth <xliff:g id="STATE">%s</xliff:g>."</string>
     <string name="accessibility_quick_settings_location" msgid="4577282329866813100">"Ligging <xliff:g id="STATE">%s</xliff:g>."</string>
     <string name="accessibility_quick_settings_alarm" msgid="3959908972897295660">"Wekker gestel vir <xliff:g id="TIME">%s</xliff:g>."</string>
+    <string name="accessibility_quick_settings_close" msgid="2571790856136835943">"Maak paneel toe"</string>
+    <string name="accessibility_quick_settings_more_time" msgid="5778794273488176726">"Meer tyd"</string>
+    <string name="accessibility_quick_settings_less_time" msgid="101026945195230084">"Minder tyd"</string>
     <string name="data_usage_disabled_dialog_3g_title" msgid="5257833881698644687">"2G-3G-data gedeaktiveer"</string>
     <string name="data_usage_disabled_dialog_4g_title" msgid="4789143363492682629">"4G data gedeaktiveer"</string>
     <string name="data_usage_disabled_dialog_mobile_title" msgid="1046047248844821202">"Mobieldata gedeaktiveer"</string>
@@ -207,7 +212,8 @@
     <string name="quick_settings_more_settings" msgid="326112621462813682">"Meer instellings"</string>
     <string name="quick_settings_tethering_label" msgid="7153452060448575549">"USB-verbinding"</string>
     <string name="quick_settings_hotspot_label" msgid="6046917934974004879">"Warmkol"</string>
-    <string name="recents_empty_message" msgid="2269156590813544104">"ONLANGS"</string>
+    <string name="quick_settings_notifications_label" msgid="4818156442169154523">"Kennisgewings"</string>
+    <string name="recents_empty_message" msgid="7883614615463619450">"Geen onlangse programme nie"</string>
     <string name="recents_app_info_button_label" msgid="2890317189376000030">"Programinligting"</string>
     <string name="recents_search_bar_label" msgid="8074997400187836677">"soek"</string>
     <string name="ssl_ca_cert_warning" msgid="9005954106902053641">"Netwerk word\ndalk gemonitor"</string>
@@ -226,4 +232,13 @@
     <string name="speed_bump_explanation" msgid="1288875699658819755">"Minder dringende kennisgewings hieronder"</string>
     <string name="notification_tap_again" msgid="7590196980943943842">"Tik weer om oop te maak"</string>
     <string name="keyguard_unlock" msgid="8043466894212841998">"Sleep op om te ontsluit"</string>
+    <string name="zen_mode_forever" msgid="7420011936770086993">"Totdat jy dit afskakel"</string>
+  <plurals name="zen_mode_duration_minutes">
+    <item quantity="one" msgid="9040808414992812341">"Een minuut lank"</item>
+    <item quantity="other" msgid="6924190729213550991">"%d minute lank"</item>
+  </plurals>
+  <plurals name="zen_mode_duration_hours">
+    <item quantity="one" msgid="3480040795582254384">"Een uur lank"</item>
+    <item quantity="other" msgid="5408537517529822157">"%d uur lank"</item>
+  </plurals>
 </resources>
diff --git a/packages/SystemUI/res/values-am/strings.xml b/packages/SystemUI/res/values-am/strings.xml
index ce3d14b..d864f9f 100644
--- a/packages/SystemUI/res/values-am/strings.xml
+++ b/packages/SystemUI/res/values-am/strings.xml
@@ -120,6 +120,8 @@
     <string name="accessibility_desc_on" msgid="2385254693624345265">"በርቷል።"</string>
     <string name="accessibility_desc_off" msgid="6475508157786853157">"ጠፍቷል።"</string>
     <string name="accessibility_desc_connected" msgid="8366256693719499665">"ተገናኝቷል።"</string>
+    <!-- no translation found for accessibility_desc_connecting (3812924520316280149) -->
+    <skip />
     <string name="accessibility_data_connection_gprs" msgid="1606477224486747751">"GPRS"</string>
     <string name="accessibility_data_connection_1x" msgid="994133468120244018">"1 X"</string>
     <string name="accessibility_data_connection_hspa" msgid="2032328855462645198">"HSPA"</string>
@@ -156,6 +158,9 @@
     <string name="accessibility_quick_settings_bluetooth" msgid="5749054971341882340">"ብሉቱዝ <xliff:g id="STATE">%s</xliff:g>።"</string>
     <string name="accessibility_quick_settings_location" msgid="4577282329866813100">"አካባቢ <xliff:g id="STATE">%s</xliff:g>።"</string>
     <string name="accessibility_quick_settings_alarm" msgid="3959908972897295660">"ማንቂያ ለ<xliff:g id="TIME">%s</xliff:g> ተዋቅሯል።"</string>
+    <string name="accessibility_quick_settings_close" msgid="2571790856136835943">"ፓነል ዝጋ"</string>
+    <string name="accessibility_quick_settings_more_time" msgid="5778794273488176726">"ተጨማሪ ጊዜ"</string>
+    <string name="accessibility_quick_settings_less_time" msgid="101026945195230084">"ያነሰ ጊዜ"</string>
     <string name="data_usage_disabled_dialog_3g_title" msgid="5257833881698644687">"2G-3G ውሂብ ቦዝኗል"</string>
     <string name="data_usage_disabled_dialog_4g_title" msgid="4789143363492682629">"4G ውሂብ ቦዝኗል"</string>
     <string name="data_usage_disabled_dialog_mobile_title" msgid="1046047248844821202">"የተንቀሳቃሽ ውሂብ ቦዝኗል"</string>
@@ -207,7 +212,8 @@
     <string name="quick_settings_more_settings" msgid="326112621462813682">"ተጨማሪ ቅንብሮች"</string>
     <string name="quick_settings_tethering_label" msgid="7153452060448575549">"በማገናኘት ላይ"</string>
     <string name="quick_settings_hotspot_label" msgid="6046917934974004879">"መገናኛ ነጥብ"</string>
-    <string name="recents_empty_message" msgid="2269156590813544104">"የቅርብ ጊዜዎች"</string>
+    <string name="quick_settings_notifications_label" msgid="4818156442169154523">"ማሳወቂያዎች"</string>
+    <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="ssl_ca_cert_warning" msgid="9005954106902053641">"አውታረ መረብ\nክትትል ሊደረግበት ይችላል"</string>
@@ -226,4 +232,13 @@
     <string name="speed_bump_explanation" msgid="1288875699658819755">"በጣም አስቸካይ ያልሆኑ ማሳወቂያዎች ከታች"</string>
     <string name="notification_tap_again" msgid="7590196980943943842">"ለመክፈት ዳግም መታ ያድርጉ"</string>
     <string name="keyguard_unlock" msgid="8043466894212841998">"ለማስከፈት ወደ ላይ ያንሸራትቱ"</string>
+    <string name="zen_mode_forever" msgid="7420011936770086993">"ይህን እስኪያጠፉት ድረስ"</string>
+  <plurals name="zen_mode_duration_minutes">
+    <item quantity="one" msgid="9040808414992812341">"ለአንድ ደቂቃ"</item>
+    <item quantity="other" msgid="6924190729213550991">"ለ%d ደቂቃዎች"</item>
+  </plurals>
+  <plurals name="zen_mode_duration_hours">
+    <item quantity="one" msgid="3480040795582254384">"ለአንድ ሰዓት"</item>
+    <item quantity="other" msgid="5408537517529822157">"ለ%d ሰዓቶች"</item>
+  </plurals>
 </resources>
diff --git a/packages/SystemUI/res/values-ar/strings.xml b/packages/SystemUI/res/values-ar/strings.xml
index c9ddc23..3906bfa 100644
--- a/packages/SystemUI/res/values-ar/strings.xml
+++ b/packages/SystemUI/res/values-ar/strings.xml
@@ -120,6 +120,8 @@
     <string name="accessibility_desc_on" msgid="2385254693624345265">"تم التشغيل."</string>
     <string name="accessibility_desc_off" msgid="6475508157786853157">"تم الإيقاف."</string>
     <string name="accessibility_desc_connected" msgid="8366256693719499665">"متصل."</string>
+    <!-- no translation found for accessibility_desc_connecting (3812924520316280149) -->
+    <skip />
     <string name="accessibility_data_connection_gprs" msgid="1606477224486747751">"GPRS"</string>
     <string name="accessibility_data_connection_1x" msgid="994133468120244018">"1‎ X‎"</string>
     <string name="accessibility_data_connection_hspa" msgid="2032328855462645198">"HSPA"</string>
@@ -156,6 +158,9 @@
     <string name="accessibility_quick_settings_bluetooth" msgid="5749054971341882340">"البلوتوث <xliff:g id="STATE">%s</xliff:g>."</string>
     <string name="accessibility_quick_settings_location" msgid="4577282329866813100">"حالة الموقع: <xliff:g id="STATE">%s</xliff:g>."</string>
     <string name="accessibility_quick_settings_alarm" msgid="3959908972897295660">"تم ضبط المنبه على <xliff:g id="TIME">%s</xliff:g>."</string>
+    <string name="accessibility_quick_settings_close" msgid="2571790856136835943">"إغلاق اللوحة"</string>
+    <string name="accessibility_quick_settings_more_time" msgid="5778794273488176726">"وقت أكثر"</string>
+    <string name="accessibility_quick_settings_less_time" msgid="101026945195230084">"وقت أقل"</string>
     <string name="data_usage_disabled_dialog_3g_title" msgid="5257833881698644687">"تم تعطيل بيانات شبكات الجيل الثاني والجيل الثالث"</string>
     <string name="data_usage_disabled_dialog_4g_title" msgid="4789143363492682629">"تم تعطيل بيانات شبكة الجيل الرابع"</string>
     <string name="data_usage_disabled_dialog_mobile_title" msgid="1046047248844821202">"تم تعطيل بيانات الجوال"</string>
@@ -207,7 +212,8 @@
     <string name="quick_settings_more_settings" msgid="326112621462813682">"المزيد من الإعدادات"</string>
     <string name="quick_settings_tethering_label" msgid="7153452060448575549">"النطاق"</string>
     <string name="quick_settings_hotspot_label" msgid="6046917934974004879">"نقطة اتصال"</string>
-    <string name="recents_empty_message" msgid="2269156590813544104">"الأخيرة"</string>
+    <string name="quick_settings_notifications_label" msgid="4818156442169154523">"الإشعارات"</string>
+    <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="ssl_ca_cert_warning" msgid="9005954106902053641">"قد تكون الشبكة\nخاضعة للرقابة"</string>
@@ -226,4 +232,13 @@
     <string name="speed_bump_explanation" msgid="1288875699658819755">"الإشعارات الأقل إلحاحًا أدناه"</string>
     <string name="notification_tap_again" msgid="7590196980943943842">"انقر مرة أخرى للفتح"</string>
     <string name="keyguard_unlock" msgid="8043466894212841998">"مرر سريعًا لأعلى لإلغاء القفل"</string>
+    <string name="zen_mode_forever" msgid="7420011936770086993">"لحين تعطيل هذا الإعداد"</string>
+  <plurals name="zen_mode_duration_minutes">
+    <item quantity="one" msgid="9040808414992812341">"لمدة دقيقة واحدة"</item>
+    <item quantity="other" msgid="6924190729213550991">"‏لمدة %d من الدقائق"</item>
+  </plurals>
+  <plurals name="zen_mode_duration_hours">
+    <item quantity="one" msgid="3480040795582254384">"لمدة ساعة واحدة"</item>
+    <item quantity="other" msgid="5408537517529822157">"‏لمدة %d من الساعات"</item>
+  </plurals>
 </resources>
diff --git a/packages/SystemUI/res/values-bg/strings.xml b/packages/SystemUI/res/values-bg/strings.xml
index d396b8c..6ef23ad 100644
--- a/packages/SystemUI/res/values-bg/strings.xml
+++ b/packages/SystemUI/res/values-bg/strings.xml
@@ -120,6 +120,8 @@
     <string name="accessibility_desc_on" msgid="2385254693624345265">"Вкл."</string>
     <string name="accessibility_desc_off" msgid="6475508157786853157">"Изкл."</string>
     <string name="accessibility_desc_connected" msgid="8366256693719499665">"Има връзка."</string>
+    <!-- no translation found for accessibility_desc_connecting (3812924520316280149) -->
+    <skip />
     <string name="accessibility_data_connection_gprs" msgid="1606477224486747751">"GPRS"</string>
     <string name="accessibility_data_connection_1x" msgid="994133468120244018">"1 X"</string>
     <string name="accessibility_data_connection_hspa" msgid="2032328855462645198">"HSPA"</string>
@@ -156,6 +158,9 @@
     <string name="accessibility_quick_settings_bluetooth" msgid="5749054971341882340">"Bluetooth: <xliff:g id="STATE">%s</xliff:g>."</string>
     <string name="accessibility_quick_settings_location" msgid="4577282329866813100">"Местоположението е <xliff:g id="STATE">%s</xliff:g>."</string>
     <string name="accessibility_quick_settings_alarm" msgid="3959908972897295660">"Будилникът е навит за <xliff:g id="TIME">%s</xliff:g>."</string>
+    <string name="accessibility_quick_settings_close" msgid="2571790856136835943">"Затваряне на панела"</string>
+    <string name="accessibility_quick_settings_more_time" msgid="5778794273488176726">"Повече време"</string>
+    <string name="accessibility_quick_settings_less_time" msgid="101026945195230084">"По-малко време"</string>
     <string name="data_usage_disabled_dialog_3g_title" msgid="5257833881698644687">"2G-3G данните са деактивирани"</string>
     <string name="data_usage_disabled_dialog_4g_title" msgid="4789143363492682629">"4G данните са деактивирани"</string>
     <string name="data_usage_disabled_dialog_mobile_title" msgid="1046047248844821202">"Мобилните данни са деактивирани"</string>
@@ -207,7 +212,8 @@
     <string name="quick_settings_more_settings" msgid="326112621462813682">"Още настройки"</string>
     <string name="quick_settings_tethering_label" msgid="7153452060448575549">"Тетъринг"</string>
     <string name="quick_settings_hotspot_label" msgid="6046917934974004879">"Точка за достъп"</string>
-    <string name="recents_empty_message" msgid="2269156590813544104">"СКОРОШНИ"</string>
+    <string name="quick_settings_notifications_label" msgid="4818156442169154523">"Известия"</string>
+    <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="ssl_ca_cert_warning" msgid="9005954106902053641">"Мрежата може\nда се наблюдава"</string>
@@ -226,4 +232,13 @@
     <string name="speed_bump_explanation" msgid="1288875699658819755">"Ппоказване на по-малко спешните известия по-долу"</string>
     <string name="notification_tap_again" msgid="7590196980943943842">"Докоснете отново, за да отворите"</string>
     <string name="keyguard_unlock" msgid="8043466894212841998">"Прекарайте пръст нагоре, за да отключите"</string>
+    <string name="zen_mode_forever" msgid="7420011936770086993">"Докато не изключите това"</string>
+  <plurals name="zen_mode_duration_minutes">
+    <item quantity="one" msgid="9040808414992812341">"За една минута"</item>
+    <item quantity="other" msgid="6924190729213550991">"За %d минути"</item>
+  </plurals>
+  <plurals name="zen_mode_duration_hours">
+    <item quantity="one" msgid="3480040795582254384">"За един час"</item>
+    <item quantity="other" msgid="5408537517529822157">"За %d часа"</item>
+  </plurals>
 </resources>
diff --git a/packages/SystemUI/res/values-ca/strings.xml b/packages/SystemUI/res/values-ca/strings.xml
index 51790b5..9e3495e 100644
--- a/packages/SystemUI/res/values-ca/strings.xml
+++ b/packages/SystemUI/res/values-ca/strings.xml
@@ -120,6 +120,8 @@
     <string name="accessibility_desc_on" msgid="2385254693624345265">"Activat."</string>
     <string name="accessibility_desc_off" msgid="6475508157786853157">"Desactivat."</string>
     <string name="accessibility_desc_connected" msgid="8366256693719499665">"Connectat."</string>
+    <!-- no translation found for accessibility_desc_connecting (3812924520316280149) -->
+    <skip />
     <string name="accessibility_data_connection_gprs" msgid="1606477224486747751">"GPRS"</string>
     <string name="accessibility_data_connection_1x" msgid="994133468120244018">"1 X"</string>
     <string name="accessibility_data_connection_hspa" msgid="2032328855462645198">"HSPA"</string>
@@ -158,6 +160,9 @@
     <string name="accessibility_quick_settings_bluetooth" msgid="5749054971341882340">"Bluetooth <xliff:g id="STATE">%s</xliff:g>."</string>
     <string name="accessibility_quick_settings_location" msgid="4577282329866813100">"Ubicació: <xliff:g id="STATE">%s</xliff:g>."</string>
     <string name="accessibility_quick_settings_alarm" msgid="3959908972897295660">"Alarma establerta a les <xliff:g id="TIME">%s</xliff:g>."</string>
+    <string name="accessibility_quick_settings_close" msgid="2571790856136835943">"Tanca el tauler."</string>
+    <string name="accessibility_quick_settings_more_time" msgid="5778794273488176726">"Més temps"</string>
+    <string name="accessibility_quick_settings_less_time" msgid="101026945195230084">"Menys temps"</string>
     <string name="data_usage_disabled_dialog_3g_title" msgid="5257833881698644687">"Dades 2G-3G desactivades"</string>
     <string name="data_usage_disabled_dialog_4g_title" msgid="4789143363492682629">"Dades 4G desactivades"</string>
     <string name="data_usage_disabled_dialog_mobile_title" msgid="1046047248844821202">"Dades mòbils desactivades"</string>
@@ -209,7 +214,8 @@
     <string name="quick_settings_more_settings" msgid="326112621462813682">"Més opcions"</string>
     <string name="quick_settings_tethering_label" msgid="7153452060448575549">"Ancoratge a xarxa"</string>
     <string name="quick_settings_hotspot_label" msgid="6046917934974004879">"Zona Wi-Fi"</string>
-    <string name="recents_empty_message" msgid="2269156590813544104">"RECENTS"</string>
+    <string name="quick_settings_notifications_label" msgid="4818156442169154523">"Notificacions"</string>
+    <string name="recents_empty_message" msgid="7883614615463619450">"No hi ha aplicacions recents."</string>
     <string name="recents_app_info_button_label" msgid="2890317189376000030">"Informació de l\'aplicació"</string>
     <string name="recents_search_bar_label" msgid="8074997400187836677">"cerca"</string>
     <string name="ssl_ca_cert_warning" msgid="9005954106902053641">"És possible que la xarxa\nestigui controlada"</string>
@@ -228,4 +234,13 @@
     <string name="speed_bump_explanation" msgid="1288875699658819755">"Notificacions menys urgents a continuació"</string>
     <string name="notification_tap_again" msgid="7590196980943943842">"Torna a tocar per obrir-la."</string>
     <string name="keyguard_unlock" msgid="8043466894212841998">"Fes lliscar el dit cap amunt per desbloquejar el teclat."</string>
+    <string name="zen_mode_forever" msgid="7420011936770086993">"Fins que no ho desactivis"</string>
+  <plurals name="zen_mode_duration_minutes">
+    <item quantity="one" msgid="9040808414992812341">"Durant un minut"</item>
+    <item quantity="other" msgid="6924190729213550991">"Durant %d minuts"</item>
+  </plurals>
+  <plurals name="zen_mode_duration_hours">
+    <item quantity="one" msgid="3480040795582254384">"Durant una hora"</item>
+    <item quantity="other" msgid="5408537517529822157">"Durant %d hores"</item>
+  </plurals>
 </resources>
diff --git a/packages/SystemUI/res/values-cs/strings.xml b/packages/SystemUI/res/values-cs/strings.xml
index 91b8f6e..6050da4 100644
--- a/packages/SystemUI/res/values-cs/strings.xml
+++ b/packages/SystemUI/res/values-cs/strings.xml
@@ -120,6 +120,8 @@
     <string name="accessibility_desc_on" msgid="2385254693624345265">"Zapnuto."</string>
     <string name="accessibility_desc_off" msgid="6475508157786853157">"Vypnuto."</string>
     <string name="accessibility_desc_connected" msgid="8366256693719499665">"Připojeno."</string>
+    <!-- no translation found for accessibility_desc_connecting (3812924520316280149) -->
+    <skip />
     <string name="accessibility_data_connection_gprs" msgid="1606477224486747751">"GPRS"</string>
     <string name="accessibility_data_connection_1x" msgid="994133468120244018">"1 X"</string>
     <string name="accessibility_data_connection_hspa" msgid="2032328855462645198">"HSPA"</string>
@@ -158,6 +160,9 @@
     <string name="accessibility_quick_settings_bluetooth" msgid="5749054971341882340">"Bluetooth: <xliff:g id="STATE">%s</xliff:g>."</string>
     <string name="accessibility_quick_settings_location" msgid="4577282329866813100">"Poloha: <xliff:g id="STATE">%s</xliff:g>"</string>
     <string name="accessibility_quick_settings_alarm" msgid="3959908972897295660">"Budík je nastaven na <xliff:g id="TIME">%s</xliff:g>."</string>
+    <string name="accessibility_quick_settings_close" msgid="2571790856136835943">"Zavřít panel"</string>
+    <string name="accessibility_quick_settings_more_time" msgid="5778794273488176726">"Delší doba"</string>
+    <string name="accessibility_quick_settings_less_time" msgid="101026945195230084">"Kratší doba"</string>
     <string name="data_usage_disabled_dialog_3g_title" msgid="5257833881698644687">"Datové přenosy 2G a 3G jsou zakázány"</string>
     <string name="data_usage_disabled_dialog_4g_title" msgid="4789143363492682629">"Datové přenosy 4G jsou zakázány"</string>
     <string name="data_usage_disabled_dialog_mobile_title" msgid="1046047248844821202">"Mobilní data jsou zakázána"</string>
@@ -209,7 +214,8 @@
     <string name="quick_settings_more_settings" msgid="326112621462813682">"Další nastavení"</string>
     <string name="quick_settings_tethering_label" msgid="7153452060448575549">"Sdílení datového připojení"</string>
     <string name="quick_settings_hotspot_label" msgid="6046917934974004879">"Hotspot"</string>
-    <string name="recents_empty_message" msgid="2269156590813544104">"POSLEDNÍ"</string>
+    <string name="quick_settings_notifications_label" msgid="4818156442169154523">"Oznámení"</string>
+    <string name="recents_empty_message" msgid="7883614615463619450">"Žádné nedávné aplikace"</string>
     <string name="recents_app_info_button_label" msgid="2890317189376000030">"Informace o aplikaci"</string>
     <string name="recents_search_bar_label" msgid="8074997400187836677">"vyhledat"</string>
     <string name="ssl_ca_cert_warning" msgid="9005954106902053641">"Síť může být\nmonitorována"</string>
@@ -228,4 +234,13 @@
     <string name="speed_bump_explanation" msgid="1288875699658819755">"Méně urgentní oznámení níže"</string>
     <string name="notification_tap_again" msgid="7590196980943943842">"Oznámení otevřete opětovným klepnutím"</string>
     <string name="keyguard_unlock" msgid="8043466894212841998">"Zařízení odemknete přejetím prstem nahoru"</string>
+    <string name="zen_mode_forever" msgid="7420011936770086993">"Dokud tuto funkci nevypnete"</string>
+  <plurals name="zen_mode_duration_minutes">
+    <item quantity="one" msgid="9040808414992812341">"Na jednu minutu"</item>
+    <item quantity="other" msgid="6924190729213550991">"Na %d min"</item>
+  </plurals>
+  <plurals name="zen_mode_duration_hours">
+    <item quantity="one" msgid="3480040795582254384">"Na jednu hodinu"</item>
+    <item quantity="other" msgid="5408537517529822157">"Na %d h"</item>
+  </plurals>
 </resources>
diff --git a/packages/SystemUI/res/values-da/strings.xml b/packages/SystemUI/res/values-da/strings.xml
index 1d1ca0a..830bebf 100644
--- a/packages/SystemUI/res/values-da/strings.xml
+++ b/packages/SystemUI/res/values-da/strings.xml
@@ -120,6 +120,8 @@
     <string name="accessibility_desc_on" msgid="2385254693624345265">"Til."</string>
     <string name="accessibility_desc_off" msgid="6475508157786853157">"Fra."</string>
     <string name="accessibility_desc_connected" msgid="8366256693719499665">"Forbundet."</string>
+    <!-- no translation found for accessibility_desc_connecting (3812924520316280149) -->
+    <skip />
     <string name="accessibility_data_connection_gprs" msgid="1606477224486747751">"GPRS"</string>
     <string name="accessibility_data_connection_1x" msgid="994133468120244018">"1 X"</string>
     <string name="accessibility_data_connection_hspa" msgid="2032328855462645198">"HSPA"</string>
@@ -156,6 +158,9 @@
     <string name="accessibility_quick_settings_bluetooth" msgid="5749054971341882340">"Bluetooth <xliff:g id="STATE">%s</xliff:g>."</string>
     <string name="accessibility_quick_settings_location" msgid="4577282329866813100">"Placering <xliff:g id="STATE">%s</xliff:g>."</string>
     <string name="accessibility_quick_settings_alarm" msgid="3959908972897295660">"Alarmen er indstillet til <xliff:g id="TIME">%s</xliff:g>."</string>
+    <string name="accessibility_quick_settings_close" msgid="2571790856136835943">"Luk panelet"</string>
+    <string name="accessibility_quick_settings_more_time" msgid="5778794273488176726">"Mere tid"</string>
+    <string name="accessibility_quick_settings_less_time" msgid="101026945195230084">"Mindre tid"</string>
     <string name="data_usage_disabled_dialog_3g_title" msgid="5257833881698644687">"2G-3G-data er deaktiveret"</string>
     <string name="data_usage_disabled_dialog_4g_title" msgid="4789143363492682629">"4G-data er deaktiveret"</string>
     <string name="data_usage_disabled_dialog_mobile_title" msgid="1046047248844821202">"Mobildata er deaktiveret"</string>
@@ -207,7 +212,8 @@
     <string name="quick_settings_more_settings" msgid="326112621462813682">"Flere indstillinger"</string>
     <string name="quick_settings_tethering_label" msgid="7153452060448575549">"Netdeling"</string>
     <string name="quick_settings_hotspot_label" msgid="6046917934974004879">"Hotspot"</string>
-    <string name="recents_empty_message" msgid="2269156590813544104">"SENESTE"</string>
+    <string name="quick_settings_notifications_label" msgid="4818156442169154523">"Underretninger"</string>
+    <string name="recents_empty_message" msgid="7883614615463619450">"Der er ingen seneste apps"</string>
     <string name="recents_app_info_button_label" msgid="2890317189376000030">"Oplysninger om applikationen"</string>
     <string name="recents_search_bar_label" msgid="8074997400187836677">"søg"</string>
     <string name="ssl_ca_cert_warning" msgid="9005954106902053641">"Netværket kan\nvære overvåget"</string>
@@ -226,4 +232,13 @@
     <string name="speed_bump_explanation" msgid="1288875699658819755">"Mindre presserende underretninger nedenfor"</string>
     <string name="notification_tap_again" msgid="7590196980943943842">"Tryk igen for at åbne"</string>
     <string name="keyguard_unlock" msgid="8043466894212841998">"Stryg for at låse op"</string>
+    <string name="zen_mode_forever" msgid="7420011936770086993">"Indtil du slår denne indstilling fra"</string>
+  <plurals name="zen_mode_duration_minutes">
+    <item quantity="one" msgid="9040808414992812341">"I ét minut"</item>
+    <item quantity="other" msgid="6924190729213550991">"I %d minutter"</item>
+  </plurals>
+  <plurals name="zen_mode_duration_hours">
+    <item quantity="one" msgid="3480040795582254384">"I én time"</item>
+    <item quantity="other" msgid="5408537517529822157">"I %d timer"</item>
+  </plurals>
 </resources>
diff --git a/packages/SystemUI/res/values-de/strings.xml b/packages/SystemUI/res/values-de/strings.xml
index a79193d..72c9d64 100644
--- a/packages/SystemUI/res/values-de/strings.xml
+++ b/packages/SystemUI/res/values-de/strings.xml
@@ -120,6 +120,8 @@
     <string name="accessibility_desc_on" msgid="2385254693624345265">"An"</string>
     <string name="accessibility_desc_off" msgid="6475508157786853157">"Aus"</string>
     <string name="accessibility_desc_connected" msgid="8366256693719499665">"Verbunden"</string>
+    <!-- no translation found for accessibility_desc_connecting (3812924520316280149) -->
+    <skip />
     <string name="accessibility_data_connection_gprs" msgid="1606477224486747751">"GPRS"</string>
     <string name="accessibility_data_connection_1x" msgid="994133468120244018">"1 X"</string>
     <string name="accessibility_data_connection_hspa" msgid="2032328855462645198">"HSPA"</string>
@@ -158,6 +160,9 @@
     <string name="accessibility_quick_settings_bluetooth" msgid="5749054971341882340">"Bluetooth: <xliff:g id="STATE">%s</xliff:g>"</string>
     <string name="accessibility_quick_settings_location" msgid="4577282329866813100">"Standort <xliff:g id="STATE">%s</xliff:g>"</string>
     <string name="accessibility_quick_settings_alarm" msgid="3959908972897295660">"Wecker gestellt für <xliff:g id="TIME">%s</xliff:g>"</string>
+    <string name="accessibility_quick_settings_close" msgid="2571790856136835943">"Fenster schließen"</string>
+    <string name="accessibility_quick_settings_more_time" msgid="5778794273488176726">"Mehr Zeit"</string>
+    <string name="accessibility_quick_settings_less_time" msgid="101026945195230084">"Weniger Zeit"</string>
     <string name="data_usage_disabled_dialog_3g_title" msgid="5257833881698644687">"2G-/3G-Daten deaktiviert"</string>
     <string name="data_usage_disabled_dialog_4g_title" msgid="4789143363492682629">"4G-Daten deaktiviert"</string>
     <string name="data_usage_disabled_dialog_mobile_title" msgid="1046047248844821202">"Mobilfunk Daten deaktiviert"</string>
@@ -209,7 +214,8 @@
     <string name="quick_settings_more_settings" msgid="326112621462813682">"Weitere Einstellungen"</string>
     <string name="quick_settings_tethering_label" msgid="7153452060448575549">"Tethering"</string>
     <string name="quick_settings_hotspot_label" msgid="6046917934974004879">"Hotspot"</string>
-    <string name="recents_empty_message" msgid="2269156590813544104">"Letzte"</string>
+    <string name="quick_settings_notifications_label" msgid="4818156442169154523">"Benachrichtigungen"</string>
+    <string name="recents_empty_message" msgid="7883614615463619450">"Keine neuen Apps"</string>
     <string name="recents_app_info_button_label" msgid="2890317189376000030">"App-Info"</string>
     <string name="recents_search_bar_label" msgid="8074997400187836677">"Suche"</string>
     <string name="ssl_ca_cert_warning" msgid="9005954106902053641">"Netzwerk wird\neventuell überwacht."</string>
@@ -228,4 +234,13 @@
     <string name="speed_bump_explanation" msgid="1288875699658819755">"Weniger dringende Benachrichtigungen unten"</string>
     <string name="notification_tap_again" msgid="7590196980943943842">"Erneut tippen, um Benachrichtigung zu öffnen"</string>
     <string name="keyguard_unlock" msgid="8043466894212841998">"Zum Entsperren nach oben wischen"</string>
+    <string name="zen_mode_forever" msgid="7420011936770086993">"Bis zur Deaktivierung"</string>
+  <plurals name="zen_mode_duration_minutes">
+    <item quantity="one" msgid="9040808414992812341">"Für eine Minute"</item>
+    <item quantity="other" msgid="6924190729213550991">"Für %d Minuten"</item>
+  </plurals>
+  <plurals name="zen_mode_duration_hours">
+    <item quantity="one" msgid="3480040795582254384">"Für eine Stunde"</item>
+    <item quantity="other" msgid="5408537517529822157">"Für %d Stunden"</item>
+  </plurals>
 </resources>
diff --git a/packages/SystemUI/res/values-el/strings.xml b/packages/SystemUI/res/values-el/strings.xml
index 32b7090..2d3f246 100644
--- a/packages/SystemUI/res/values-el/strings.xml
+++ b/packages/SystemUI/res/values-el/strings.xml
@@ -120,6 +120,8 @@
     <string name="accessibility_desc_on" msgid="2385254693624345265">"Ενεργό."</string>
     <string name="accessibility_desc_off" msgid="6475508157786853157">"Ανενεργό."</string>
     <string name="accessibility_desc_connected" msgid="8366256693719499665">"Έχει συνδεθεί."</string>
+    <!-- no translation found for accessibility_desc_connecting (3812924520316280149) -->
+    <skip />
     <string name="accessibility_data_connection_gprs" msgid="1606477224486747751">"GPRS"</string>
     <string name="accessibility_data_connection_1x" msgid="994133468120244018">"1 X"</string>
     <string name="accessibility_data_connection_hspa" msgid="2032328855462645198">"HSPA"</string>
@@ -158,6 +160,9 @@
     <string name="accessibility_quick_settings_bluetooth" msgid="5749054971341882340">"Bluetooth <xliff:g id="STATE">%s</xliff:g>."</string>
     <string name="accessibility_quick_settings_location" msgid="4577282329866813100">"Τοποθεσία <xliff:g id="STATE">%s</xliff:g>."</string>
     <string name="accessibility_quick_settings_alarm" msgid="3959908972897295660">"Το ξυπνητήρι έχει οριστεί στις <xliff:g id="TIME">%s</xliff:g>."</string>
+    <string name="accessibility_quick_settings_close" msgid="2571790856136835943">"Κλείσιμο παραθύρου"</string>
+    <string name="accessibility_quick_settings_more_time" msgid="5778794273488176726">"Περισσότερος χρόνος"</string>
+    <string name="accessibility_quick_settings_less_time" msgid="101026945195230084">"Λιγότερος χρόνος"</string>
     <string name="data_usage_disabled_dialog_3g_title" msgid="5257833881698644687">"Τα δεδομένα 2G-3G απενεργοποιήθηκαν"</string>
     <string name="data_usage_disabled_dialog_4g_title" msgid="4789143363492682629">"Τα δεδομένα 4G απενεργοποιήθηκαν"</string>
     <string name="data_usage_disabled_dialog_mobile_title" msgid="1046047248844821202">"Τα δεδομένα κινητής τηλεφωνίας απενεργοποιήθηκαν"</string>
@@ -209,7 +214,8 @@
     <string name="quick_settings_more_settings" msgid="326112621462813682">"Περισσότερες ρυθμίσεις"</string>
     <string name="quick_settings_tethering_label" msgid="7153452060448575549">"Πρόσδεση"</string>
     <string name="quick_settings_hotspot_label" msgid="6046917934974004879">"Σημείο πρόσβασης Wi-Fi"</string>
-    <string name="recents_empty_message" msgid="2269156590813544104">"ΠΡΟΣΦΑΤΑ"</string>
+    <string name="quick_settings_notifications_label" msgid="4818156442169154523">"Ειδοποιήσεις"</string>
+    <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="ssl_ca_cert_warning" msgid="9005954106902053641">"Το δίκτυο μπορεί\nνα παρακολουθείται"</string>
@@ -228,4 +234,13 @@
     <string name="speed_bump_explanation" msgid="1288875699658819755">"Λιγότερο επείγουσες ειδοποιήσεις παρακάτω"</string>
     <string name="notification_tap_again" msgid="7590196980943943842">"Πατήστε ξανά για να ανοίξετε"</string>
     <string name="keyguard_unlock" msgid="8043466894212841998">"Σύρετε για να ξεκλειδώσετε"</string>
+    <string name="zen_mode_forever" msgid="7420011936770086993">"Μέχρι να το απενεργοποιήσετε"</string>
+  <plurals name="zen_mode_duration_minutes">
+    <item quantity="one" msgid="9040808414992812341">"Για ένα λεπτό"</item>
+    <item quantity="other" msgid="6924190729213550991">"Για %d λεπτά"</item>
+  </plurals>
+  <plurals name="zen_mode_duration_hours">
+    <item quantity="one" msgid="3480040795582254384">"Για μία ώρα"</item>
+    <item quantity="other" msgid="5408537517529822157">"Για %d ώρες"</item>
+  </plurals>
 </resources>
diff --git a/packages/SystemUI/res/values-en-rGB/strings.xml b/packages/SystemUI/res/values-en-rGB/strings.xml
index d26cfae..66233cc 100644
--- a/packages/SystemUI/res/values-en-rGB/strings.xml
+++ b/packages/SystemUI/res/values-en-rGB/strings.xml
@@ -120,6 +120,8 @@
     <string name="accessibility_desc_on" msgid="2385254693624345265">"On."</string>
     <string name="accessibility_desc_off" msgid="6475508157786853157">"Off."</string>
     <string name="accessibility_desc_connected" msgid="8366256693719499665">"Connected."</string>
+    <!-- no translation found for accessibility_desc_connecting (3812924520316280149) -->
+    <skip />
     <string name="accessibility_data_connection_gprs" msgid="1606477224486747751">"GPRS"</string>
     <string name="accessibility_data_connection_1x" msgid="994133468120244018">"1 X"</string>
     <string name="accessibility_data_connection_hspa" msgid="2032328855462645198">"HSPA"</string>
@@ -156,6 +158,9 @@
     <string name="accessibility_quick_settings_bluetooth" msgid="5749054971341882340">"Bluetooth <xliff:g id="STATE">%s</xliff:g>."</string>
     <string name="accessibility_quick_settings_location" msgid="4577282329866813100">"Location <xliff:g id="STATE">%s</xliff:g>."</string>
     <string name="accessibility_quick_settings_alarm" msgid="3959908972897295660">"Alarm set for <xliff:g id="TIME">%s</xliff:g>."</string>
+    <string name="accessibility_quick_settings_close" msgid="2571790856136835943">"Close panel"</string>
+    <string name="accessibility_quick_settings_more_time" msgid="5778794273488176726">"More time"</string>
+    <string name="accessibility_quick_settings_less_time" msgid="101026945195230084">"Less time"</string>
     <string name="data_usage_disabled_dialog_3g_title" msgid="5257833881698644687">"2G-3G data disabled"</string>
     <string name="data_usage_disabled_dialog_4g_title" msgid="4789143363492682629">"4G data disabled"</string>
     <string name="data_usage_disabled_dialog_mobile_title" msgid="1046047248844821202">"Mobile data disabled"</string>
@@ -207,7 +212,8 @@
     <string name="quick_settings_more_settings" msgid="326112621462813682">"More settings"</string>
     <string name="quick_settings_tethering_label" msgid="7153452060448575549">"Tethering"</string>
     <string name="quick_settings_hotspot_label" msgid="6046917934974004879">"Hotspot"</string>
-    <string name="recents_empty_message" msgid="2269156590813544104">"RECENTS"</string>
+    <string name="quick_settings_notifications_label" msgid="4818156442169154523">"Notifications"</string>
+    <string name="recents_empty_message" msgid="7883614615463619450">"No recent apps"</string>
     <string name="recents_app_info_button_label" msgid="2890317189376000030">"Application Info"</string>
     <string name="recents_search_bar_label" msgid="8074997400187836677">"search"</string>
     <string name="ssl_ca_cert_warning" msgid="9005954106902053641">"Network may\nbe monitored"</string>
@@ -226,4 +232,13 @@
     <string name="speed_bump_explanation" msgid="1288875699658819755">"Less urgent notifications below"</string>
     <string name="notification_tap_again" msgid="7590196980943943842">"Tap again to open"</string>
     <string name="keyguard_unlock" msgid="8043466894212841998">"Swipe up to unlock"</string>
+    <string name="zen_mode_forever" msgid="7420011936770086993">"Until you turn this off"</string>
+  <plurals name="zen_mode_duration_minutes">
+    <item quantity="one" msgid="9040808414992812341">"For one minute"</item>
+    <item quantity="other" msgid="6924190729213550991">"For %d minutes"</item>
+  </plurals>
+  <plurals name="zen_mode_duration_hours">
+    <item quantity="one" msgid="3480040795582254384">"For one hour"</item>
+    <item quantity="other" msgid="5408537517529822157">"For %d hours"</item>
+  </plurals>
 </resources>
diff --git a/packages/SystemUI/res/values-en-rIN/strings.xml b/packages/SystemUI/res/values-en-rIN/strings.xml
index d48e627..66233cc 100644
--- a/packages/SystemUI/res/values-en-rIN/strings.xml
+++ b/packages/SystemUI/res/values-en-rIN/strings.xml
@@ -120,6 +120,8 @@
     <string name="accessibility_desc_on" msgid="2385254693624345265">"On."</string>
     <string name="accessibility_desc_off" msgid="6475508157786853157">"Off."</string>
     <string name="accessibility_desc_connected" msgid="8366256693719499665">"Connected."</string>
+    <!-- no translation found for accessibility_desc_connecting (3812924520316280149) -->
+    <skip />
     <string name="accessibility_data_connection_gprs" msgid="1606477224486747751">"GPRS"</string>
     <string name="accessibility_data_connection_1x" msgid="994133468120244018">"1 X"</string>
     <string name="accessibility_data_connection_hspa" msgid="2032328855462645198">"HSPA"</string>
@@ -156,6 +158,9 @@
     <string name="accessibility_quick_settings_bluetooth" msgid="5749054971341882340">"Bluetooth <xliff:g id="STATE">%s</xliff:g>."</string>
     <string name="accessibility_quick_settings_location" msgid="4577282329866813100">"Location <xliff:g id="STATE">%s</xliff:g>."</string>
     <string name="accessibility_quick_settings_alarm" msgid="3959908972897295660">"Alarm set for <xliff:g id="TIME">%s</xliff:g>."</string>
+    <string name="accessibility_quick_settings_close" msgid="2571790856136835943">"Close panel"</string>
+    <string name="accessibility_quick_settings_more_time" msgid="5778794273488176726">"More time"</string>
+    <string name="accessibility_quick_settings_less_time" msgid="101026945195230084">"Less time"</string>
     <string name="data_usage_disabled_dialog_3g_title" msgid="5257833881698644687">"2G-3G data disabled"</string>
     <string name="data_usage_disabled_dialog_4g_title" msgid="4789143363492682629">"4G data disabled"</string>
     <string name="data_usage_disabled_dialog_mobile_title" msgid="1046047248844821202">"Mobile data disabled"</string>
@@ -207,7 +212,8 @@
     <string name="quick_settings_more_settings" msgid="326112621462813682">"More settings"</string>
     <string name="quick_settings_tethering_label" msgid="7153452060448575549">"Tethering"</string>
     <string name="quick_settings_hotspot_label" msgid="6046917934974004879">"Hotspot"</string>
-    <string name="recents_empty_message" msgid="2269156590813544104">"RECENTS"</string>
+    <string name="quick_settings_notifications_label" msgid="4818156442169154523">"Notifications"</string>
+    <string name="recents_empty_message" msgid="7883614615463619450">"No recent apps"</string>
     <string name="recents_app_info_button_label" msgid="2890317189376000030">"Application Info"</string>
     <string name="recents_search_bar_label" msgid="8074997400187836677">"search"</string>
     <string name="ssl_ca_cert_warning" msgid="9005954106902053641">"Network may\nbe monitored"</string>
@@ -223,8 +229,16 @@
   <plurals name="keyguard_more_overflow_text">
     <item quantity="other" msgid="9180696159506883684">"%d more"</item>
   </plurals>
-    <!-- no translation found for speed_bump_explanation (1288875699658819755) -->
-    <skip />
+    <string name="speed_bump_explanation" msgid="1288875699658819755">"Less urgent notifications below"</string>
     <string name="notification_tap_again" msgid="7590196980943943842">"Tap again to open"</string>
     <string name="keyguard_unlock" msgid="8043466894212841998">"Swipe up to unlock"</string>
+    <string name="zen_mode_forever" msgid="7420011936770086993">"Until you turn this off"</string>
+  <plurals name="zen_mode_duration_minutes">
+    <item quantity="one" msgid="9040808414992812341">"For one minute"</item>
+    <item quantity="other" msgid="6924190729213550991">"For %d minutes"</item>
+  </plurals>
+  <plurals name="zen_mode_duration_hours">
+    <item quantity="one" msgid="3480040795582254384">"For one hour"</item>
+    <item quantity="other" msgid="5408537517529822157">"For %d hours"</item>
+  </plurals>
 </resources>
diff --git a/packages/SystemUI/res/values-es-rUS/strings.xml b/packages/SystemUI/res/values-es-rUS/strings.xml
index 23bcea2..0810ee7 100644
--- a/packages/SystemUI/res/values-es-rUS/strings.xml
+++ b/packages/SystemUI/res/values-es-rUS/strings.xml
@@ -120,6 +120,8 @@
     <string name="accessibility_desc_on" msgid="2385254693624345265">"Activado"</string>
     <string name="accessibility_desc_off" msgid="6475508157786853157">"Desactivado"</string>
     <string name="accessibility_desc_connected" msgid="8366256693719499665">"Conectado"</string>
+    <!-- no translation found for accessibility_desc_connecting (3812924520316280149) -->
+    <skip />
     <string name="accessibility_data_connection_gprs" msgid="1606477224486747751">"GPRS"</string>
     <string name="accessibility_data_connection_1x" msgid="994133468120244018">"1 X"</string>
     <string name="accessibility_data_connection_hspa" msgid="2032328855462645198">"HSPA"</string>
@@ -158,6 +160,9 @@
     <string name="accessibility_quick_settings_bluetooth" msgid="5749054971341882340">"Bluetooth <xliff:g id="STATE">%s</xliff:g>"</string>
     <string name="accessibility_quick_settings_location" msgid="4577282329866813100">"Ubicación <xliff:g id="STATE">%s</xliff:g>"</string>
     <string name="accessibility_quick_settings_alarm" msgid="3959908972897295660">"Alarma: <xliff:g id="TIME">%s</xliff:g>"</string>
+    <string name="accessibility_quick_settings_close" msgid="2571790856136835943">"Cerrar panel"</string>
+    <string name="accessibility_quick_settings_more_time" msgid="5778794273488176726">"Más tiempo"</string>
+    <string name="accessibility_quick_settings_less_time" msgid="101026945195230084">"Menos tiempo"</string>
     <string name="data_usage_disabled_dialog_3g_title" msgid="5257833881698644687">"Datos de 2G-3G inhabilitados"</string>
     <string name="data_usage_disabled_dialog_4g_title" msgid="4789143363492682629">"Datos de 4G inhabilitados"</string>
     <string name="data_usage_disabled_dialog_mobile_title" msgid="1046047248844821202">"Se inhabilitaron los datos móviles"</string>
@@ -209,7 +214,9 @@
     <string name="quick_settings_more_settings" msgid="326112621462813682">"Más configuraciones"</string>
     <string name="quick_settings_tethering_label" msgid="7153452060448575549">"Anclaje a red"</string>
     <string name="quick_settings_hotspot_label" msgid="6046917934974004879">"Zona"</string>
-    <string name="recents_empty_message" msgid="2269156590813544104">"RECIENTES"</string>
+    <string name="quick_settings_notifications_label" msgid="4818156442169154523">"Notificaciones"</string>
+    <!-- no translation found for recents_empty_message (7883614615463619450) -->
+    <skip />
     <string name="recents_app_info_button_label" msgid="2890317189376000030">"Información de la aplicación"</string>
     <string name="recents_search_bar_label" msgid="8074997400187836677">"buscar"</string>
     <string name="ssl_ca_cert_warning" msgid="9005954106902053641">"Es posible que la red\nesté supervisada."</string>
@@ -225,8 +232,16 @@
   <plurals name="keyguard_more_overflow_text">
     <item quantity="other" msgid="9180696159506883684">"%d más"</item>
   </plurals>
-    <!-- no translation found for speed_bump_explanation (1288875699658819755) -->
-    <skip />
+    <string name="speed_bump_explanation" msgid="1288875699658819755">"Notificaciones menos urgentes abajo"</string>
     <string name="notification_tap_again" msgid="7590196980943943842">"Presionar de nuevo para abrir"</string>
     <string name="keyguard_unlock" msgid="8043466894212841998">"Deslizar el dedo hacia arriba para desbloquear"</string>
+    <string name="zen_mode_forever" msgid="7420011936770086993">"Hasta que lo desactives"</string>
+  <plurals name="zen_mode_duration_minutes">
+    <item quantity="one" msgid="9040808414992812341">"Durante un minuto"</item>
+    <item quantity="other" msgid="6924190729213550991">"Durante %d minutos"</item>
+  </plurals>
+  <plurals name="zen_mode_duration_hours">
+    <item quantity="one" msgid="3480040795582254384">"Durante una hora"</item>
+    <item quantity="other" msgid="5408537517529822157">"Durante %d horas"</item>
+  </plurals>
 </resources>
diff --git a/packages/SystemUI/res/values-es/strings.xml b/packages/SystemUI/res/values-es/strings.xml
index 3bded8b..ad92c24 100644
--- a/packages/SystemUI/res/values-es/strings.xml
+++ b/packages/SystemUI/res/values-es/strings.xml
@@ -120,6 +120,8 @@
     <string name="accessibility_desc_on" msgid="2385254693624345265">"Sí"</string>
     <string name="accessibility_desc_off" msgid="6475508157786853157">"No"</string>
     <string name="accessibility_desc_connected" msgid="8366256693719499665">"Conectado"</string>
+    <!-- no translation found for accessibility_desc_connecting (3812924520316280149) -->
+    <skip />
     <string name="accessibility_data_connection_gprs" msgid="1606477224486747751">"GPRS"</string>
     <string name="accessibility_data_connection_1x" msgid="994133468120244018">"1 X"</string>
     <string name="accessibility_data_connection_hspa" msgid="2032328855462645198">"HSPA"</string>
@@ -156,6 +158,9 @@
     <string name="accessibility_quick_settings_bluetooth" msgid="5749054971341882340">"Bluetooth <xliff:g id="STATE">%s</xliff:g>"</string>
     <string name="accessibility_quick_settings_location" msgid="4577282329866813100">"Ubicación <xliff:g id="STATE">%s</xliff:g>."</string>
     <string name="accessibility_quick_settings_alarm" msgid="3959908972897295660">"La alarma sonará a la(s) <xliff:g id="TIME">%s</xliff:g>."</string>
+    <string name="accessibility_quick_settings_close" msgid="2571790856136835943">"Cerrar panel"</string>
+    <string name="accessibility_quick_settings_more_time" msgid="5778794273488176726">"Más tiempo"</string>
+    <string name="accessibility_quick_settings_less_time" msgid="101026945195230084">"Menos tiempo"</string>
     <string name="data_usage_disabled_dialog_3g_title" msgid="5257833881698644687">"Datos 2G-3G inhabilitados"</string>
     <string name="data_usage_disabled_dialog_4g_title" msgid="4789143363492682629">"Datos 4G inhabilitados"</string>
     <string name="data_usage_disabled_dialog_mobile_title" msgid="1046047248844821202">"Datos móviles inhabilitados"</string>
@@ -207,7 +212,8 @@
     <string name="quick_settings_more_settings" msgid="326112621462813682">"Más opciones"</string>
     <string name="quick_settings_tethering_label" msgid="7153452060448575549">"Anclaje a red"</string>
     <string name="quick_settings_hotspot_label" msgid="6046917934974004879">"Zona Wi-Fi"</string>
-    <string name="recents_empty_message" msgid="2269156590813544104">"RECIENTES"</string>
+    <string name="quick_settings_notifications_label" msgid="4818156442169154523">"Notificaciones"</string>
+    <string name="recents_empty_message" msgid="7883614615463619450">"No hay aplicaciones recientes"</string>
     <string name="recents_app_info_button_label" msgid="2890317189376000030">"Información de la aplicación"</string>
     <string name="recents_search_bar_label" msgid="8074997400187836677">"buscar"</string>
     <string name="ssl_ca_cert_warning" msgid="9005954106902053641">"La red se\npuede supervisar"</string>
@@ -226,4 +232,13 @@
     <string name="speed_bump_explanation" msgid="1288875699658819755">"Notificaciones menos urgente abajo"</string>
     <string name="notification_tap_again" msgid="7590196980943943842">"Toca de nuevo para abrir"</string>
     <string name="keyguard_unlock" msgid="8043466894212841998">"Desliza el dedo hacia arriba para desbloquear"</string>
+    <string name="zen_mode_forever" msgid="7420011936770086993">"Hasta apagar el dispositivo"</string>
+  <plurals name="zen_mode_duration_minutes">
+    <item quantity="one" msgid="9040808414992812341">"Durante un minuto"</item>
+    <item quantity="other" msgid="6924190729213550991">"Durante %d minutos"</item>
+  </plurals>
+  <plurals name="zen_mode_duration_hours">
+    <item quantity="one" msgid="3480040795582254384">"Durante una hora"</item>
+    <item quantity="other" msgid="5408537517529822157">"Durante %d horas"</item>
+  </plurals>
 </resources>
diff --git a/packages/SystemUI/res/values-et-rEE/strings.xml b/packages/SystemUI/res/values-et-rEE/strings.xml
index 1979c55..71e2af6 100644
--- a/packages/SystemUI/res/values-et-rEE/strings.xml
+++ b/packages/SystemUI/res/values-et-rEE/strings.xml
@@ -120,6 +120,8 @@
     <string name="accessibility_desc_on" msgid="2385254693624345265">"Sees."</string>
     <string name="accessibility_desc_off" msgid="6475508157786853157">"Väljas."</string>
     <string name="accessibility_desc_connected" msgid="8366256693719499665">"Ühendatud."</string>
+    <!-- no translation found for accessibility_desc_connecting (3812924520316280149) -->
+    <skip />
     <string name="accessibility_data_connection_gprs" msgid="1606477224486747751">"GPRS"</string>
     <string name="accessibility_data_connection_1x" msgid="994133468120244018">"1 X"</string>
     <string name="accessibility_data_connection_hspa" msgid="2032328855462645198">"HSPA"</string>
@@ -156,6 +158,9 @@
     <string name="accessibility_quick_settings_bluetooth" msgid="5749054971341882340">"Bluetooth: <xliff:g id="STATE">%s</xliff:g>."</string>
     <string name="accessibility_quick_settings_location" msgid="4577282329866813100">"Asukoht: <xliff:g id="STATE">%s</xliff:g>."</string>
     <string name="accessibility_quick_settings_alarm" msgid="3959908972897295660">"Määratud äratus: <xliff:g id="TIME">%s</xliff:g>"</string>
+    <string name="accessibility_quick_settings_close" msgid="2571790856136835943">"Sule paneel"</string>
+    <string name="accessibility_quick_settings_more_time" msgid="5778794273488176726">"Rohkem aega"</string>
+    <string name="accessibility_quick_settings_less_time" msgid="101026945195230084">"Vähem aega"</string>
     <string name="data_usage_disabled_dialog_3g_title" msgid="5257833881698644687">"2G–3G andmeside keelatud"</string>
     <string name="data_usage_disabled_dialog_4g_title" msgid="4789143363492682629">"4G andmeside keelatud"</string>
     <string name="data_usage_disabled_dialog_mobile_title" msgid="1046047248844821202">"Mobiilne andmeside keelatud"</string>
@@ -207,7 +212,8 @@
     <string name="quick_settings_more_settings" msgid="326112621462813682">"Rohkem seadeid"</string>
     <string name="quick_settings_tethering_label" msgid="7153452060448575549">"Jagamine"</string>
     <string name="quick_settings_hotspot_label" msgid="6046917934974004879">"Leviala"</string>
-    <string name="recents_empty_message" msgid="2269156590813544104">"HILJUTISED"</string>
+    <string name="quick_settings_notifications_label" msgid="4818156442169154523">"Märguanded"</string>
+    <string name="recents_empty_message" msgid="7883614615463619450">"Hiljutisi rakendusi pole"</string>
     <string name="recents_app_info_button_label" msgid="2890317189376000030">"Rakenduste teave"</string>
     <string name="recents_search_bar_label" msgid="8074997400187836677">"otsing"</string>
     <string name="ssl_ca_cert_warning" msgid="9005954106902053641">"Võrku võidakse\njälgida"</string>
@@ -224,4 +230,13 @@
     <string name="speed_bump_explanation" msgid="1288875699658819755">"Vähem kiireloomulised märguanded on allpool"</string>
     <string name="notification_tap_again" msgid="7590196980943943842">"Avamiseks puudutage uuesti"</string>
     <string name="keyguard_unlock" msgid="8043466894212841998">"Lukustuse tühistamiseks pühkige üles"</string>
+    <string name="zen_mode_forever" msgid="7420011936770086993">"Kuni lülitate selle välja"</string>
+  <plurals name="zen_mode_duration_minutes">
+    <item quantity="one" msgid="9040808414992812341">"Üheks minutiks"</item>
+    <item quantity="other" msgid="6924190729213550991">"%d minutiks"</item>
+  </plurals>
+  <plurals name="zen_mode_duration_hours">
+    <item quantity="one" msgid="3480040795582254384">"Üheks tunniks"</item>
+    <item quantity="other" msgid="5408537517529822157">"%d tunniks"</item>
+  </plurals>
 </resources>
diff --git a/packages/SystemUI/res/values-fa/strings.xml b/packages/SystemUI/res/values-fa/strings.xml
index 399740a..3ea2110 100644
--- a/packages/SystemUI/res/values-fa/strings.xml
+++ b/packages/SystemUI/res/values-fa/strings.xml
@@ -120,6 +120,8 @@
     <string name="accessibility_desc_on" msgid="2385254693624345265">"روشن."</string>
     <string name="accessibility_desc_off" msgid="6475508157786853157">"خاموش."</string>
     <string name="accessibility_desc_connected" msgid="8366256693719499665">"متصل."</string>
+    <!-- no translation found for accessibility_desc_connecting (3812924520316280149) -->
+    <skip />
     <string name="accessibility_data_connection_gprs" msgid="1606477224486747751">"GPRS"</string>
     <string name="accessibility_data_connection_1x" msgid="994133468120244018">"1 X"</string>
     <string name="accessibility_data_connection_hspa" msgid="2032328855462645198">"HSPA"</string>
@@ -156,6 +158,9 @@
     <string name="accessibility_quick_settings_bluetooth" msgid="5749054971341882340">"بلوتوث <xliff:g id="STATE">%s</xliff:g>."</string>
     <string name="accessibility_quick_settings_location" msgid="4577282329866813100">"مکان <xliff:g id="STATE">%s</xliff:g>."</string>
     <string name="accessibility_quick_settings_alarm" msgid="3959908972897295660">"هشدار برای <xliff:g id="TIME">%s</xliff:g> تنظیم شد."</string>
+    <string name="accessibility_quick_settings_close" msgid="2571790856136835943">"بستن پانل"</string>
+    <string name="accessibility_quick_settings_more_time" msgid="5778794273488176726">"زمان بیشتر"</string>
+    <string name="accessibility_quick_settings_less_time" msgid="101026945195230084">"زمان کمتر"</string>
     <string name="data_usage_disabled_dialog_3g_title" msgid="5257833881698644687">"‏داده 2G-3G غیرفعال شد"</string>
     <string name="data_usage_disabled_dialog_4g_title" msgid="4789143363492682629">"‏داده 4G غیر فعال شد"</string>
     <string name="data_usage_disabled_dialog_mobile_title" msgid="1046047248844821202">"داده‌های تلفن همراه غیرفعال است"</string>
@@ -207,7 +212,8 @@
     <string name="quick_settings_more_settings" msgid="326112621462813682">"تنظیمات بیشتر"</string>
     <string name="quick_settings_tethering_label" msgid="7153452060448575549">"اتصال به اینترنت با تلفن همراه"</string>
     <string name="quick_settings_hotspot_label" msgid="6046917934974004879">"نقطه اتصال"</string>
-    <string name="recents_empty_message" msgid="2269156590813544104">"موارد اخیر"</string>
+    <string name="quick_settings_notifications_label" msgid="4818156442169154523">"اعلان‌ها"</string>
+    <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="ssl_ca_cert_warning" msgid="9005954106902053641">"ممکن است شبکه\nتحت نظارت باشد"</string>
@@ -226,4 +232,13 @@
     <string name="speed_bump_explanation" msgid="1288875699658819755">"اعلان‌های کمتر فوری در زیر"</string>
     <string name="notification_tap_again" msgid="7590196980943943842">"برای باز کردن دوباره ضربه بزنید"</string>
     <string name="keyguard_unlock" msgid="8043466894212841998">"برای باز کردن قفل سریع به بالا بکشید"</string>
+    <string name="zen_mode_forever" msgid="7420011936770086993">"تا وقتی آن را خاموش کنید"</string>
+  <plurals name="zen_mode_duration_minutes">
+    <item quantity="one" msgid="9040808414992812341">"برای یک دقیقه"</item>
+    <item quantity="other" msgid="6924190729213550991">"‏برای %d دقیقه"</item>
+  </plurals>
+  <plurals name="zen_mode_duration_hours">
+    <item quantity="one" msgid="3480040795582254384">"برای یک ساعت"</item>
+    <item quantity="other" msgid="5408537517529822157">"‏برای %d ساعت"</item>
+  </plurals>
 </resources>
diff --git a/packages/SystemUI/res/values-fi/strings.xml b/packages/SystemUI/res/values-fi/strings.xml
index be7eb9b..eb68ea6 100644
--- a/packages/SystemUI/res/values-fi/strings.xml
+++ b/packages/SystemUI/res/values-fi/strings.xml
@@ -120,6 +120,8 @@
     <string name="accessibility_desc_on" msgid="2385254693624345265">"Käytössä."</string>
     <string name="accessibility_desc_off" msgid="6475508157786853157">"Pois käytöstä."</string>
     <string name="accessibility_desc_connected" msgid="8366256693719499665">"Yhdistetty."</string>
+    <!-- no translation found for accessibility_desc_connecting (3812924520316280149) -->
+    <skip />
     <string name="accessibility_data_connection_gprs" msgid="1606477224486747751">"GPRS"</string>
     <string name="accessibility_data_connection_1x" msgid="994133468120244018">"1 X"</string>
     <string name="accessibility_data_connection_hspa" msgid="2032328855462645198">"HSPA"</string>
@@ -156,6 +158,9 @@
     <string name="accessibility_quick_settings_bluetooth" msgid="5749054971341882340">"Bluetooth: <xliff:g id="STATE">%s</xliff:g>."</string>
     <string name="accessibility_quick_settings_location" msgid="4577282329866813100">"Sijainti <xliff:g id="STATE">%s</xliff:g>."</string>
     <string name="accessibility_quick_settings_alarm" msgid="3959908972897295660">"Hälytys asetettu, aika: <xliff:g id="TIME">%s</xliff:g>."</string>
+    <string name="accessibility_quick_settings_close" msgid="2571790856136835943">"Sulje paneeli"</string>
+    <string name="accessibility_quick_settings_more_time" msgid="5778794273488176726">"Lisää aikaa"</string>
+    <string name="accessibility_quick_settings_less_time" msgid="101026945195230084">"Vähennä aikaa"</string>
     <string name="data_usage_disabled_dialog_3g_title" msgid="5257833881698644687">"2G-3G-tiedonsiirto pois käytöstä"</string>
     <string name="data_usage_disabled_dialog_4g_title" msgid="4789143363492682629">"4G-tiedonsiirto pois käytöstä"</string>
     <string name="data_usage_disabled_dialog_mobile_title" msgid="1046047248844821202">"Mobiilitiedonsiirto pois käytöstä"</string>
@@ -207,7 +212,9 @@
     <string name="quick_settings_more_settings" msgid="326112621462813682">"Lisäasetukset"</string>
     <string name="quick_settings_tethering_label" msgid="7153452060448575549">"Jaettu yhteys"</string>
     <string name="quick_settings_hotspot_label" msgid="6046917934974004879">"Hotspot"</string>
-    <string name="recents_empty_message" msgid="2269156590813544104">"VIIMEISIMMÄT"</string>
+    <string name="quick_settings_notifications_label" msgid="4818156442169154523">"Ilmoitukset"</string>
+    <!-- no translation found for recents_empty_message (7883614615463619450) -->
+    <skip />
     <string name="recents_app_info_button_label" msgid="2890317189376000030">"Sovellustiedot"</string>
     <string name="recents_search_bar_label" msgid="8074997400187836677">"haku"</string>
     <string name="ssl_ca_cert_warning" msgid="9005954106902053641">"Verkkoa saatetaan\nvalvoa"</string>
@@ -226,4 +233,13 @@
     <string name="speed_bump_explanation" msgid="1288875699658819755">"Vähemmän kiireelliset ilmoitukset ovat alla"</string>
     <string name="notification_tap_again" msgid="7590196980943943842">"Avaa napauttamalla uudelleen"</string>
     <string name="keyguard_unlock" msgid="8043466894212841998">"Avaa lukitus pyyhkäisemällä ylös"</string>
+    <string name="zen_mode_forever" msgid="7420011936770086993">"Kunnes poistat tämän käytöstä"</string>
+  <plurals name="zen_mode_duration_minutes">
+    <item quantity="one" msgid="9040808414992812341">"Minuutiksi"</item>
+    <item quantity="other" msgid="6924190729213550991">"%d minuutiksi"</item>
+  </plurals>
+  <plurals name="zen_mode_duration_hours">
+    <item quantity="one" msgid="3480040795582254384">"Tunniksi"</item>
+    <item quantity="other" msgid="5408537517529822157">"%d tunniksi"</item>
+  </plurals>
 </resources>
diff --git a/packages/SystemUI/res/values-fr-rCA/strings.xml b/packages/SystemUI/res/values-fr-rCA/strings.xml
index 390ec81..4c1df8c 100644
--- a/packages/SystemUI/res/values-fr-rCA/strings.xml
+++ b/packages/SystemUI/res/values-fr-rCA/strings.xml
@@ -120,6 +120,8 @@
     <string name="accessibility_desc_on" msgid="2385254693624345265">"Activé"</string>
     <string name="accessibility_desc_off" msgid="6475508157786853157">"Désactivé"</string>
     <string name="accessibility_desc_connected" msgid="8366256693719499665">"Connecté"</string>
+    <!-- no translation found for accessibility_desc_connecting (3812924520316280149) -->
+    <skip />
     <string name="accessibility_data_connection_gprs" msgid="1606477224486747751">"GPRS"</string>
     <string name="accessibility_data_connection_1x" msgid="994133468120244018">"1x"</string>
     <string name="accessibility_data_connection_hspa" msgid="2032328855462645198">"3G+"</string>
@@ -158,6 +160,9 @@
     <string name="accessibility_quick_settings_bluetooth" msgid="5749054971341882340">"Bluetooth : <xliff:g id="STATE">%s</xliff:g>"</string>
     <string name="accessibility_quick_settings_location" msgid="4577282329866813100">"Localisation <xliff:g id="STATE">%s</xliff:g>"</string>
     <string name="accessibility_quick_settings_alarm" msgid="3959908972897295660">"Alarme réglée sur <xliff:g id="TIME">%s</xliff:g>"</string>
+    <string name="accessibility_quick_settings_close" msgid="2571790856136835943">"Fermer le panneau"</string>
+    <string name="accessibility_quick_settings_more_time" msgid="5778794273488176726">"Plus longtemps"</string>
+    <string name="accessibility_quick_settings_less_time" msgid="101026945195230084">"Moins longtemps"</string>
     <string name="data_usage_disabled_dialog_3g_title" msgid="5257833881698644687">"Données 2G-3G  désactivées"</string>
     <string name="data_usage_disabled_dialog_4g_title" msgid="4789143363492682629">"Données 4G désactivées"</string>
     <string name="data_usage_disabled_dialog_mobile_title" msgid="1046047248844821202">"Données mobiles désactivées"</string>
@@ -209,7 +214,9 @@
     <string name="quick_settings_more_settings" msgid="326112621462813682">"Plus de paramètres"</string>
     <string name="quick_settings_tethering_label" msgid="7153452060448575549">"Partage de connexion"</string>
     <string name="quick_settings_hotspot_label" msgid="6046917934974004879">"Point d\'accès sans fil"</string>
-    <string name="recents_empty_message" msgid="2269156590813544104">"RÉCENTS"</string>
+    <string name="quick_settings_notifications_label" msgid="4818156442169154523">"Notifications"</string>
+    <!-- no translation found for recents_empty_message (7883614615463619450) -->
+    <skip />
     <string name="recents_app_info_button_label" msgid="2890317189376000030">"Détails de l\'application"</string>
     <string name="recents_search_bar_label" msgid="8074997400187836677">"rechercher"</string>
     <string name="ssl_ca_cert_warning" msgid="9005954106902053641">"Le réseau peut\nêtre surveillé."</string>
@@ -228,4 +235,13 @@
     <string name="speed_bump_explanation" msgid="1288875699658819755">"Notifications moins urgentes affichées ci-dessous"</string>
     <string name="notification_tap_again" msgid="7590196980943943842">"Touchez à nouveau pour ouvrir"</string>
     <string name="keyguard_unlock" msgid="8043466894212841998">"Glissez vers le haut pour déverrouiller"</string>
+    <string name="zen_mode_forever" msgid="7420011936770086993">"Jusqu\'à la désactivation"</string>
+  <plurals name="zen_mode_duration_minutes">
+    <item quantity="one" msgid="9040808414992812341">"Pendant une minute"</item>
+    <item quantity="other" msgid="6924190729213550991">"Pendant %d minutes"</item>
+  </plurals>
+  <plurals name="zen_mode_duration_hours">
+    <item quantity="one" msgid="3480040795582254384">"Pendant une heure"</item>
+    <item quantity="other" msgid="5408537517529822157">"Pendant %d heures"</item>
+  </plurals>
 </resources>
diff --git a/packages/SystemUI/res/values-fr/strings.xml b/packages/SystemUI/res/values-fr/strings.xml
index a0c5cfe..bd709a0 100644
--- a/packages/SystemUI/res/values-fr/strings.xml
+++ b/packages/SystemUI/res/values-fr/strings.xml
@@ -120,6 +120,8 @@
     <string name="accessibility_desc_on" msgid="2385254693624345265">"Activé"</string>
     <string name="accessibility_desc_off" msgid="6475508157786853157">"Désactivé"</string>
     <string name="accessibility_desc_connected" msgid="8366256693719499665">"Connecté"</string>
+    <!-- no translation found for accessibility_desc_connecting (3812924520316280149) -->
+    <skip />
     <string name="accessibility_data_connection_gprs" msgid="1606477224486747751">"GPRS"</string>
     <string name="accessibility_data_connection_1x" msgid="994133468120244018">"1x"</string>
     <string name="accessibility_data_connection_hspa" msgid="2032328855462645198">"HSPA"</string>
@@ -158,6 +160,9 @@
     <string name="accessibility_quick_settings_bluetooth" msgid="5749054971341882340">"Bluetooth : <xliff:g id="STATE">%s</xliff:g>"</string>
     <string name="accessibility_quick_settings_location" msgid="4577282329866813100">"Localisation <xliff:g id="STATE">%s</xliff:g>"</string>
     <string name="accessibility_quick_settings_alarm" msgid="3959908972897295660">"Alarme réglée sur <xliff:g id="TIME">%s</xliff:g>"</string>
+    <string name="accessibility_quick_settings_close" msgid="2571790856136835943">"Fermer le panneau"</string>
+    <string name="accessibility_quick_settings_more_time" msgid="5778794273488176726">"Plus longtemps"</string>
+    <string name="accessibility_quick_settings_less_time" msgid="101026945195230084">"Moins longtemps"</string>
     <string name="data_usage_disabled_dialog_3g_title" msgid="5257833881698644687">"Données 2G-3G désactivées"</string>
     <string name="data_usage_disabled_dialog_4g_title" msgid="4789143363492682629">"Données 4G désactivées"</string>
     <string name="data_usage_disabled_dialog_mobile_title" msgid="1046047248844821202">"Données mobiles désactivées"</string>
@@ -209,7 +214,8 @@
     <string name="quick_settings_more_settings" msgid="326112621462813682">"Plus de paramètres"</string>
     <string name="quick_settings_tethering_label" msgid="7153452060448575549">"Partage de connexion"</string>
     <string name="quick_settings_hotspot_label" msgid="6046917934974004879">"Point d\'accès"</string>
-    <string name="recents_empty_message" msgid="2269156590813544104">"RÉCENTS"</string>
+    <string name="quick_settings_notifications_label" msgid="4818156442169154523">"Notifications"</string>
+    <string name="recents_empty_message" msgid="7883614615463619450">"Aucune application récente"</string>
     <string name="recents_app_info_button_label" msgid="2890317189376000030">"Informations sur l\'application"</string>
     <string name="recents_search_bar_label" msgid="8074997400187836677">"rechercher"</string>
     <string name="ssl_ca_cert_warning" msgid="9005954106902053641">"Le réseau peut\nêtre surveillé."</string>
@@ -228,4 +234,13 @@
     <string name="speed_bump_explanation" msgid="1288875699658819755">"Notifications moins urgentes ci-dessous"</string>
     <string name="notification_tap_again" msgid="7590196980943943842">"Appuyer à nouveau pour ouvrir"</string>
     <string name="keyguard_unlock" msgid="8043466894212841998">"Faire glisser pour déverrouiller"</string>
+    <string name="zen_mode_forever" msgid="7420011936770086993">"Jusqu\'à la désactivation"</string>
+  <plurals name="zen_mode_duration_minutes">
+    <item quantity="one" msgid="9040808414992812341">"Pendant une minute"</item>
+    <item quantity="other" msgid="6924190729213550991">"Pendant %d minutes"</item>
+  </plurals>
+  <plurals name="zen_mode_duration_hours">
+    <item quantity="one" msgid="3480040795582254384">"Pendant une heure"</item>
+    <item quantity="other" msgid="5408537517529822157">"Pendant %d heures"</item>
+  </plurals>
 </resources>
diff --git a/packages/SystemUI/res/values-hi/strings.xml b/packages/SystemUI/res/values-hi/strings.xml
index 6850b3d..d6b6d6a 100644
--- a/packages/SystemUI/res/values-hi/strings.xml
+++ b/packages/SystemUI/res/values-hi/strings.xml
@@ -120,6 +120,8 @@
     <string name="accessibility_desc_on" msgid="2385254693624345265">"चालू."</string>
     <string name="accessibility_desc_off" msgid="6475508157786853157">"बंद."</string>
     <string name="accessibility_desc_connected" msgid="8366256693719499665">"कनेक्ट है."</string>
+    <!-- no translation found for accessibility_desc_connecting (3812924520316280149) -->
+    <skip />
     <string name="accessibility_data_connection_gprs" msgid="1606477224486747751">"GPRS"</string>
     <string name="accessibility_data_connection_1x" msgid="994133468120244018">"1 X"</string>
     <string name="accessibility_data_connection_hspa" msgid="2032328855462645198">"HSPA"</string>
@@ -156,6 +158,9 @@
     <string name="accessibility_quick_settings_bluetooth" msgid="5749054971341882340">"Bluetooth <xliff:g id="STATE">%s</xliff:g>."</string>
     <string name="accessibility_quick_settings_location" msgid="4577282329866813100">"स्थान <xliff:g id="STATE">%s</xliff:g>."</string>
     <string name="accessibility_quick_settings_alarm" msgid="3959908972897295660">"<xliff:g id="TIME">%s</xliff:g> के लिए अलार्म सेट किया गया."</string>
+    <string name="accessibility_quick_settings_close" msgid="2571790856136835943">"फलक बंद करें"</string>
+    <string name="accessibility_quick_settings_more_time" msgid="5778794273488176726">"अधिक समय"</string>
+    <string name="accessibility_quick_settings_less_time" msgid="101026945195230084">"कम समय"</string>
     <string name="data_usage_disabled_dialog_3g_title" msgid="5257833881698644687">"2G-3G डेटा अक्षम"</string>
     <string name="data_usage_disabled_dialog_4g_title" msgid="4789143363492682629">"4G डेटा अक्षम"</string>
     <string name="data_usage_disabled_dialog_mobile_title" msgid="1046047248844821202">"मोबाइल डेटा अक्षम"</string>
@@ -207,7 +212,8 @@
     <string name="quick_settings_more_settings" msgid="326112621462813682">"और सेटिंग"</string>
     <string name="quick_settings_tethering_label" msgid="7153452060448575549">"टेदरिंग"</string>
     <string name="quick_settings_hotspot_label" msgid="6046917934974004879">"हॉटस्पॉट"</string>
-    <string name="recents_empty_message" msgid="2269156590813544104">"हाल ही का"</string>
+    <string name="quick_settings_notifications_label" msgid="4818156442169154523">"सूचनाएं"</string>
+    <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="ssl_ca_cert_warning" msgid="9005954106902053641">"नेटवर्क को\nमॉनीटर किया जा सकता है"</string>
@@ -226,4 +232,13 @@
     <string name="speed_bump_explanation" msgid="1288875699658819755">"कम अत्यावश्यक सूचनाएं नीचे दी गई हैं"</string>
     <string name="notification_tap_again" msgid="7590196980943943842">"खोलने के लिए पुन: टैप करें"</string>
     <string name="keyguard_unlock" msgid="8043466894212841998">"अनलॉक करने के लिए ऊपर स्वाइप करें"</string>
+    <string name="zen_mode_forever" msgid="7420011936770086993">"जब तक आप इसे बंद नहीं कर देते"</string>
+  <plurals name="zen_mode_duration_minutes">
+    <item quantity="one" msgid="9040808414992812341">"एक मिनट के लिए"</item>
+    <item quantity="other" msgid="6924190729213550991">"%d मिनट के लिए"</item>
+  </plurals>
+  <plurals name="zen_mode_duration_hours">
+    <item quantity="one" msgid="3480040795582254384">"एक घंटे के लिए"</item>
+    <item quantity="other" msgid="5408537517529822157">"%d घंटे के लिए"</item>
+  </plurals>
 </resources>
diff --git a/packages/SystemUI/res/values-hr/strings.xml b/packages/SystemUI/res/values-hr/strings.xml
index b00c382..79d7003 100644
--- a/packages/SystemUI/res/values-hr/strings.xml
+++ b/packages/SystemUI/res/values-hr/strings.xml
@@ -120,6 +120,8 @@
     <string name="accessibility_desc_on" msgid="2385254693624345265">"Uključeno."</string>
     <string name="accessibility_desc_off" msgid="6475508157786853157">"Isključeno."</string>
     <string name="accessibility_desc_connected" msgid="8366256693719499665">"Povezano."</string>
+    <!-- no translation found for accessibility_desc_connecting (3812924520316280149) -->
+    <skip />
     <string name="accessibility_data_connection_gprs" msgid="1606477224486747751">"GPRS"</string>
     <string name="accessibility_data_connection_1x" msgid="994133468120244018">"1 X"</string>
     <string name="accessibility_data_connection_hspa" msgid="2032328855462645198">"HSPA"</string>
@@ -156,6 +158,9 @@
     <string name="accessibility_quick_settings_bluetooth" msgid="5749054971341882340">"Bluetooth – <xliff:g id="STATE">%s</xliff:g>."</string>
     <string name="accessibility_quick_settings_location" msgid="4577282329866813100">"Lokacija <xliff:g id="STATE">%s</xliff:g>."</string>
     <string name="accessibility_quick_settings_alarm" msgid="3959908972897295660">"Vrijeme alarma: <xliff:g id="TIME">%s</xliff:g>."</string>
+    <string name="accessibility_quick_settings_close" msgid="2571790856136835943">"Zatvori ploču"</string>
+    <string name="accessibility_quick_settings_more_time" msgid="5778794273488176726">"Više vremena"</string>
+    <string name="accessibility_quick_settings_less_time" msgid="101026945195230084">"Manje vremena"</string>
     <string name="data_usage_disabled_dialog_3g_title" msgid="5257833881698644687">"Onemogućeni su 2G-3G podaci"</string>
     <string name="data_usage_disabled_dialog_4g_title" msgid="4789143363492682629">"Onemogućeni su 4G podaci"</string>
     <string name="data_usage_disabled_dialog_mobile_title" msgid="1046047248844821202">"Onemogućeni su mobilni podaci"</string>
@@ -207,7 +212,8 @@
     <string name="quick_settings_more_settings" msgid="326112621462813682">"Više  postavki"</string>
     <string name="quick_settings_tethering_label" msgid="7153452060448575549">"Dijeljenje veze"</string>
     <string name="quick_settings_hotspot_label" msgid="6046917934974004879">"Žarišna točka"</string>
-    <string name="recents_empty_message" msgid="2269156590813544104">"NEDAVNO"</string>
+    <string name="quick_settings_notifications_label" msgid="4818156442169154523">"Obavijesti"</string>
+    <string name="recents_empty_message" msgid="7883614615463619450">"Nema nedavnih aplikacija"</string>
     <string name="recents_app_info_button_label" msgid="2890317189376000030">"Informacije o aplikaciji"</string>
     <string name="recents_search_bar_label" msgid="8074997400187836677">"pretraži"</string>
     <string name="ssl_ca_cert_warning" msgid="9005954106902053641">"Mreža se\nmožda prati"</string>
@@ -226,4 +232,13 @@
     <string name="speed_bump_explanation" msgid="1288875699658819755">"Manje hitne obavijesti pri dnu"</string>
     <string name="notification_tap_again" msgid="7590196980943943842">"Dodirnite opet za otvaranje"</string>
     <string name="keyguard_unlock" msgid="8043466894212841998">"Prijeđite prstom prema gore za otključavanje"</string>
+    <string name="zen_mode_forever" msgid="7420011936770086993">"Dok ne isključite"</string>
+  <plurals name="zen_mode_duration_minutes">
+    <item quantity="one" msgid="9040808414992812341">"Jednu minutu"</item>
+    <item quantity="other" msgid="6924190729213550991">"%d min"</item>
+  </plurals>
+  <plurals name="zen_mode_duration_hours">
+    <item quantity="one" msgid="3480040795582254384">"Jedan sat"</item>
+    <item quantity="other" msgid="5408537517529822157">"%d h"</item>
+  </plurals>
 </resources>
diff --git a/packages/SystemUI/res/values-hu/strings.xml b/packages/SystemUI/res/values-hu/strings.xml
index b75c7f3..915f5eb 100644
--- a/packages/SystemUI/res/values-hu/strings.xml
+++ b/packages/SystemUI/res/values-hu/strings.xml
@@ -120,6 +120,8 @@
     <string name="accessibility_desc_on" msgid="2385254693624345265">"Bekapcsolva."</string>
     <string name="accessibility_desc_off" msgid="6475508157786853157">"Kikapcsolva."</string>
     <string name="accessibility_desc_connected" msgid="8366256693719499665">"Csatlakoztatva."</string>
+    <!-- no translation found for accessibility_desc_connecting (3812924520316280149) -->
+    <skip />
     <string name="accessibility_data_connection_gprs" msgid="1606477224486747751">"GPRS"</string>
     <string name="accessibility_data_connection_1x" msgid="994133468120244018">"1 X"</string>
     <string name="accessibility_data_connection_hspa" msgid="2032328855462645198">"HSPA"</string>
@@ -156,6 +158,9 @@
     <string name="accessibility_quick_settings_bluetooth" msgid="5749054971341882340">"Bluetooth <xliff:g id="STATE">%s</xliff:g>."</string>
     <string name="accessibility_quick_settings_location" msgid="4577282329866813100">"Helyadatok: <xliff:g id="STATE">%s</xliff:g>."</string>
     <string name="accessibility_quick_settings_alarm" msgid="3959908972897295660">"Ébresztés időpontja: <xliff:g id="TIME">%s</xliff:g>."</string>
+    <string name="accessibility_quick_settings_close" msgid="2571790856136835943">"Panel bezárása"</string>
+    <string name="accessibility_quick_settings_more_time" msgid="5778794273488176726">"Több idő"</string>
+    <string name="accessibility_quick_settings_less_time" msgid="101026945195230084">"Kevesebb idő"</string>
     <string name="data_usage_disabled_dialog_3g_title" msgid="5257833881698644687">"2G-3G adatforgalom letiltva"</string>
     <string name="data_usage_disabled_dialog_4g_title" msgid="4789143363492682629">"4G adatforgalom letiltva"</string>
     <string name="data_usage_disabled_dialog_mobile_title" msgid="1046047248844821202">"Mobil adatforgalom letiltva"</string>
@@ -207,7 +212,8 @@
     <string name="quick_settings_more_settings" msgid="326112621462813682">"További beállítások"</string>
     <string name="quick_settings_tethering_label" msgid="7153452060448575549">"Megosztás"</string>
     <string name="quick_settings_hotspot_label" msgid="6046917934974004879">"Hotspot"</string>
-    <string name="recents_empty_message" msgid="2269156590813544104">"LEGUTÓBBIAK"</string>
+    <string name="quick_settings_notifications_label" msgid="4818156442169154523">"Értesítések"</string>
+    <string name="recents_empty_message" msgid="7883614615463619450">"Nincs újabb alkalmazás"</string>
     <string name="recents_app_info_button_label" msgid="2890317189376000030">"Az alkalmazás adatai"</string>
     <string name="recents_search_bar_label" msgid="8074997400187836677">"keresés"</string>
     <string name="ssl_ca_cert_warning" msgid="9005954106902053641">"Lehet, hogy a\nhálózat felügyelt"</string>
@@ -226,4 +232,13 @@
     <string name="speed_bump_explanation" msgid="1288875699658819755">"A kevésbé sürgős értesítések lentebb vannak"</string>
     <string name="notification_tap_again" msgid="7590196980943943842">"Koppintson rá ismét a megnyitáshoz"</string>
     <string name="keyguard_unlock" msgid="8043466894212841998">"Húzza felfelé az ujját a feloldáshoz"</string>
+    <string name="zen_mode_forever" msgid="7420011936770086993">"Amíg ki nem kapcsolja ezt"</string>
+  <plurals name="zen_mode_duration_minutes">
+    <item quantity="one" msgid="9040808414992812341">"Egy percen át"</item>
+    <item quantity="other" msgid="6924190729213550991">"%d percen át"</item>
+  </plurals>
+  <plurals name="zen_mode_duration_hours">
+    <item quantity="one" msgid="3480040795582254384">"Egy órán át"</item>
+    <item quantity="other" msgid="5408537517529822157">"%d órán át"</item>
+  </plurals>
 </resources>
diff --git a/packages/SystemUI/res/values-hy-rAM/strings.xml b/packages/SystemUI/res/values-hy-rAM/strings.xml
index 904b998..ca327d8 100644
--- a/packages/SystemUI/res/values-hy-rAM/strings.xml
+++ b/packages/SystemUI/res/values-hy-rAM/strings.xml
@@ -120,6 +120,8 @@
     <string name="accessibility_desc_on" msgid="2385254693624345265">"Միացված է:"</string>
     <string name="accessibility_desc_off" msgid="6475508157786853157">"Անջատված է:"</string>
     <string name="accessibility_desc_connected" msgid="8366256693719499665">"Միացված է:"</string>
+    <!-- no translation found for accessibility_desc_connecting (3812924520316280149) -->
+    <skip />
     <string name="accessibility_data_connection_gprs" msgid="1606477224486747751">"GPRS"</string>
     <string name="accessibility_data_connection_1x" msgid="994133468120244018">"1 X"</string>
     <string name="accessibility_data_connection_hspa" msgid="2032328855462645198">"HSPA"</string>
@@ -156,6 +158,9 @@
     <string name="accessibility_quick_settings_bluetooth" msgid="5749054971341882340">"Bluetooth-ը <xliff:g id="STATE">%s</xliff:g> է:"</string>
     <string name="accessibility_quick_settings_location" msgid="4577282329866813100">"Տեղադրությունը՝ <xliff:g id="STATE">%s</xliff:g>:"</string>
     <string name="accessibility_quick_settings_alarm" msgid="3959908972897295660">"Զարթուցիչը դրված է <xliff:g id="TIME">%s</xliff:g>-ին:"</string>
+    <string name="accessibility_quick_settings_close" msgid="2571790856136835943">"Փակել վահանակը"</string>
+    <string name="accessibility_quick_settings_more_time" msgid="5778794273488176726">"Ավելացնել ժամանակը"</string>
+    <string name="accessibility_quick_settings_less_time" msgid="101026945195230084">"Քչացնել ժամանակը"</string>
     <string name="data_usage_disabled_dialog_3g_title" msgid="5257833881698644687">"2G-3G տվյալները անջատված են"</string>
     <string name="data_usage_disabled_dialog_4g_title" msgid="4789143363492682629">"4G տվյալները անջատված են"</string>
     <string name="data_usage_disabled_dialog_mobile_title" msgid="1046047248844821202">"Շարժական տվյալները անջատված են"</string>
@@ -207,7 +212,9 @@
     <string name="quick_settings_more_settings" msgid="326112621462813682">"Հավելյալ կարգավորումներ"</string>
     <string name="quick_settings_tethering_label" msgid="7153452060448575549">"Միացում"</string>
     <string name="quick_settings_hotspot_label" msgid="6046917934974004879">"Թեժ կետ"</string>
-    <string name="recents_empty_message" msgid="2269156590813544104">"ՎԵՐՋԻՆՆԵՐԸ"</string>
+    <string name="quick_settings_notifications_label" msgid="4818156442169154523">"Ծանուցումներ"</string>
+    <!-- no translation found for recents_empty_message (7883614615463619450) -->
+    <skip />
     <string name="recents_app_info_button_label" msgid="2890317189376000030">"Հավելվածի մասին"</string>
     <string name="recents_search_bar_label" msgid="8074997400187836677">"որոնել"</string>
     <string name="ssl_ca_cert_warning" msgid="9005954106902053641">"Ցանցը կարող է\nվերահսկվել"</string>
@@ -226,4 +233,13 @@
     <string name="speed_bump_explanation" msgid="1288875699658819755">"Պակաս հրատապ ծանուցումները ստորև"</string>
     <string name="notification_tap_again" msgid="7590196980943943842">"Կրկին հպեք՝ բացելու համար"</string>
     <string name="keyguard_unlock" msgid="8043466894212841998">"Սահեցրեք վերև` ապակողպելու համար"</string>
+    <string name="zen_mode_forever" msgid="7420011936770086993">"Քանի դեռ չեք անջատել"</string>
+  <plurals name="zen_mode_duration_minutes">
+    <item quantity="one" msgid="9040808414992812341">"Մեկ րոպե"</item>
+    <item quantity="other" msgid="6924190729213550991">"%d րոպե"</item>
+  </plurals>
+  <plurals name="zen_mode_duration_hours">
+    <item quantity="one" msgid="3480040795582254384">"Մեկ ժամ"</item>
+    <item quantity="other" msgid="5408537517529822157">"%d ժամ"</item>
+  </plurals>
 </resources>
diff --git a/packages/SystemUI/res/values-in/strings.xml b/packages/SystemUI/res/values-in/strings.xml
index 02313b3..48c9a6d 100644
--- a/packages/SystemUI/res/values-in/strings.xml
+++ b/packages/SystemUI/res/values-in/strings.xml
@@ -120,6 +120,8 @@
     <string name="accessibility_desc_on" msgid="2385254693624345265">"Aktif."</string>
     <string name="accessibility_desc_off" msgid="6475508157786853157">"Nonaktif."</string>
     <string name="accessibility_desc_connected" msgid="8366256693719499665">"Tersambung."</string>
+    <!-- no translation found for accessibility_desc_connecting (3812924520316280149) -->
+    <skip />
     <string name="accessibility_data_connection_gprs" msgid="1606477224486747751">"GPRS"</string>
     <string name="accessibility_data_connection_1x" msgid="994133468120244018">"1 X"</string>
     <string name="accessibility_data_connection_hspa" msgid="2032328855462645198">"HSPA"</string>
@@ -156,6 +158,9 @@
     <string name="accessibility_quick_settings_bluetooth" msgid="5749054971341882340">"Bluetooth <xliff:g id="STATE">%s</xliff:g>."</string>
     <string name="accessibility_quick_settings_location" msgid="4577282329866813100">"Lokasi <xliff:g id="STATE">%s</xliff:g>."</string>
     <string name="accessibility_quick_settings_alarm" msgid="3959908972897295660">"Alarm disetel ke <xliff:g id="TIME">%s</xliff:g>."</string>
+    <string name="accessibility_quick_settings_close" msgid="2571790856136835943">"Tutup panel"</string>
+    <string name="accessibility_quick_settings_more_time" msgid="5778794273488176726">"Lebih lama"</string>
+    <string name="accessibility_quick_settings_less_time" msgid="101026945195230084">"Lebih cepat"</string>
     <string name="data_usage_disabled_dialog_3g_title" msgid="5257833881698644687">"Data 2G-3G dinonaktifkan"</string>
     <string name="data_usage_disabled_dialog_4g_title" msgid="4789143363492682629">"Data 4G dinonaktifkan"</string>
     <string name="data_usage_disabled_dialog_mobile_title" msgid="1046047248844821202">"Data seluler dinonaktifkan"</string>
@@ -202,16 +207,13 @@
     <string name="quick_settings_remote_display_no_connection_label" msgid="372107699274391290">"Layar Transmisi"</string>
     <string name="quick_settings_brightness_dialog_title" msgid="8599674057673605368">"Kecerahan"</string>
     <string name="quick_settings_brightness_dialog_auto_brightness_label" msgid="5064982743784071218">"OTOMATIS"</string>
-    <!-- no translation found for quick_settings_inversion_label (8790919884718619648) -->
-    <skip />
+    <string name="quick_settings_inversion_label" msgid="8790919884718619648">"Inversi warna"</string>
     <string name="quick_settings_color_space_label" msgid="853443689745584770">"Mode koreksi warna"</string>
-    <!-- no translation found for quick_settings_more_settings (326112621462813682) -->
-    <skip />
-    <!-- no translation found for quick_settings_tethering_label (7153452060448575549) -->
-    <skip />
-    <!-- no translation found for quick_settings_hotspot_label (6046917934974004879) -->
-    <skip />
-    <string name="recents_empty_message" msgid="2269156590813544104">"TERBARU"</string>
+    <string name="quick_settings_more_settings" msgid="326112621462813682">"Setelan lainnya"</string>
+    <string name="quick_settings_tethering_label" msgid="7153452060448575549">"Menambatkan"</string>
+    <string name="quick_settings_hotspot_label" msgid="6046917934974004879">"Hotspot"</string>
+    <string name="quick_settings_notifications_label" msgid="4818156442169154523">"Pemberitahuan"</string>
+    <string name="recents_empty_message" msgid="7883614615463619450">"Tidak ada aplikasi terkini"</string>
     <string name="recents_app_info_button_label" msgid="2890317189376000030">"Info Aplikasi"</string>
     <string name="recents_search_bar_label" msgid="8074997400187836677">"telusuri"</string>
     <string name="ssl_ca_cert_warning" msgid="9005954106902053641">"Jaringan bisa\ndiawasi"</string>
@@ -227,8 +229,16 @@
   <plurals name="keyguard_more_overflow_text">
     <item quantity="other" msgid="9180696159506883684">"%d lainnya"</item>
   </plurals>
-    <!-- no translation found for speed_bump_explanation (1288875699658819755) -->
-    <skip />
+    <string name="speed_bump_explanation" msgid="1288875699658819755">"Pemberitahuan kurang darurat di bawah"</string>
     <string name="notification_tap_again" msgid="7590196980943943842">"Ketuk lagi untuk membuka"</string>
     <string name="keyguard_unlock" msgid="8043466894212841998">"Gesek ke atas untuk membuka kunci"</string>
+    <string name="zen_mode_forever" msgid="7420011936770086993">"Hingga Anda menonaktifkan ini"</string>
+  <plurals name="zen_mode_duration_minutes">
+    <item quantity="one" msgid="9040808414992812341">"Selama satu menit"</item>
+    <item quantity="other" msgid="6924190729213550991">"Selama %d menit"</item>
+  </plurals>
+  <plurals name="zen_mode_duration_hours">
+    <item quantity="one" msgid="3480040795582254384">"Selama satu jam"</item>
+    <item quantity="other" msgid="5408537517529822157">"Selama %d jam"</item>
+  </plurals>
 </resources>
diff --git a/packages/SystemUI/res/values-it/strings.xml b/packages/SystemUI/res/values-it/strings.xml
index 412d001..8653c8f 100644
--- a/packages/SystemUI/res/values-it/strings.xml
+++ b/packages/SystemUI/res/values-it/strings.xml
@@ -120,6 +120,8 @@
     <string name="accessibility_desc_on" msgid="2385254693624345265">"ON"</string>
     <string name="accessibility_desc_off" msgid="6475508157786853157">"OFF"</string>
     <string name="accessibility_desc_connected" msgid="8366256693719499665">"Connesso."</string>
+    <!-- no translation found for accessibility_desc_connecting (3812924520316280149) -->
+    <skip />
     <string name="accessibility_data_connection_gprs" msgid="1606477224486747751">"GPRS"</string>
     <string name="accessibility_data_connection_1x" msgid="994133468120244018">"1 X"</string>
     <string name="accessibility_data_connection_hspa" msgid="2032328855462645198">"HSPA"</string>
@@ -158,6 +160,9 @@
     <string name="accessibility_quick_settings_bluetooth" msgid="5749054971341882340">"Bluetooth: <xliff:g id="STATE">%s</xliff:g>."</string>
     <string name="accessibility_quick_settings_location" msgid="4577282329866813100">"Posizione: <xliff:g id="STATE">%s</xliff:g>."</string>
     <string name="accessibility_quick_settings_alarm" msgid="3959908972897295660">"Allarme impostato per: <xliff:g id="TIME">%s</xliff:g>."</string>
+    <string name="accessibility_quick_settings_close" msgid="2571790856136835943">"Chiudi riquadro"</string>
+    <string name="accessibility_quick_settings_more_time" msgid="5778794273488176726">"Più tempo"</string>
+    <string name="accessibility_quick_settings_less_time" msgid="101026945195230084">"Meno tempo"</string>
     <string name="data_usage_disabled_dialog_3g_title" msgid="5257833881698644687">"Dati 2G-3G disattivati"</string>
     <string name="data_usage_disabled_dialog_4g_title" msgid="4789143363492682629">"Dati 4G disattivati"</string>
     <string name="data_usage_disabled_dialog_mobile_title" msgid="1046047248844821202">"Dati mobili disattivati"</string>
@@ -209,7 +214,8 @@
     <string name="quick_settings_more_settings" msgid="326112621462813682">"Altre impostazioni"</string>
     <string name="quick_settings_tethering_label" msgid="7153452060448575549">"Tethering"</string>
     <string name="quick_settings_hotspot_label" msgid="6046917934974004879">"Hotspot"</string>
-    <string name="recents_empty_message" msgid="2269156590813544104">"MESSAGGI RECENTI"</string>
+    <string name="quick_settings_notifications_label" msgid="4818156442169154523">"Notifiche"</string>
+    <string name="recents_empty_message" msgid="7883614615463619450">"Nessuna app recente"</string>
     <string name="recents_app_info_button_label" msgid="2890317189376000030">"Informazioni sull\'applicazione"</string>
     <string name="recents_search_bar_label" msgid="8074997400187836677">"cerca"</string>
     <string name="ssl_ca_cert_warning" msgid="9005954106902053641">"La rete potrebbe\nessere monitorata"</string>
@@ -228,4 +234,13 @@
     <string name="speed_bump_explanation" msgid="1288875699658819755">"Notifiche meno urgenti in basso"</string>
     <string name="notification_tap_again" msgid="7590196980943943842">"Tocca ancora per aprire"</string>
     <string name="keyguard_unlock" msgid="8043466894212841998">"Scorri verso l\'alto per sbloccare"</string>
+    <string name="zen_mode_forever" msgid="7420011936770086993">"Fino alla disattivazione"</string>
+  <plurals name="zen_mode_duration_minutes">
+    <item quantity="one" msgid="9040808414992812341">"Per un minuto"</item>
+    <item quantity="other" msgid="6924190729213550991">"Per %d minuti"</item>
+  </plurals>
+  <plurals name="zen_mode_duration_hours">
+    <item quantity="one" msgid="3480040795582254384">"Per un\'ora"</item>
+    <item quantity="other" msgid="5408537517529822157">"Per %d ore"</item>
+  </plurals>
 </resources>
diff --git a/packages/SystemUI/res/values-iw/strings.xml b/packages/SystemUI/res/values-iw/strings.xml
index 33a6701..3279bc1 100644
--- a/packages/SystemUI/res/values-iw/strings.xml
+++ b/packages/SystemUI/res/values-iw/strings.xml
@@ -120,6 +120,8 @@
     <string name="accessibility_desc_on" msgid="2385254693624345265">"פועל."</string>
     <string name="accessibility_desc_off" msgid="6475508157786853157">"כבוי."</string>
     <string name="accessibility_desc_connected" msgid="8366256693719499665">"מחובר."</string>
+    <!-- no translation found for accessibility_desc_connecting (3812924520316280149) -->
+    <skip />
     <string name="accessibility_data_connection_gprs" msgid="1606477224486747751">"GPRS"</string>
     <string name="accessibility_data_connection_1x" msgid="994133468120244018">"‎1 X"</string>
     <string name="accessibility_data_connection_hspa" msgid="2032328855462645198">"HSPA"</string>
@@ -156,6 +158,9 @@
     <string name="accessibility_quick_settings_bluetooth" msgid="5749054971341882340">"‏Bluetooth ‏<xliff:g id="STATE">%s</xliff:g>."</string>
     <string name="accessibility_quick_settings_location" msgid="4577282329866813100">"המיקום <xliff:g id="STATE">%s</xliff:g>."</string>
     <string name="accessibility_quick_settings_alarm" msgid="3959908972897295660">"ההתראה נקבעה ל-<xliff:g id="TIME">%s</xliff:g>."</string>
+    <string name="accessibility_quick_settings_close" msgid="2571790856136835943">"סגור חלונית"</string>
+    <string name="accessibility_quick_settings_more_time" msgid="5778794273488176726">"יותר זמן"</string>
+    <string name="accessibility_quick_settings_less_time" msgid="101026945195230084">"פחות זמן"</string>
     <string name="data_usage_disabled_dialog_3g_title" msgid="5257833881698644687">"‏נתוני 2G-3G מושבתים"</string>
     <string name="data_usage_disabled_dialog_4g_title" msgid="4789143363492682629">"‏נתוני 4G מושבתים"</string>
     <string name="data_usage_disabled_dialog_mobile_title" msgid="1046047248844821202">"נתונים לנייד מושבתים"</string>
@@ -207,7 +212,8 @@
     <string name="quick_settings_more_settings" msgid="326112621462813682">"הגדרות נוספות"</string>
     <string name="quick_settings_tethering_label" msgid="7153452060448575549">"שיתוף אינטרנט בין ניידים"</string>
     <string name="quick_settings_hotspot_label" msgid="6046917934974004879">"נקודה לשיתוף אינטרנט"</string>
-    <string name="recents_empty_message" msgid="2269156590813544104">"אחרונים"</string>
+    <string name="quick_settings_notifications_label" msgid="4818156442169154523">"הודעות"</string>
+    <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="ssl_ca_cert_warning" msgid="9005954106902053641">"ייתכן שהרשת\nמנוטרת"</string>
@@ -226,4 +232,13 @@
     <string name="speed_bump_explanation" msgid="1288875699658819755">"הודעות בדחיפות נמוכה יותר בהמשך"</string>
     <string name="notification_tap_again" msgid="7590196980943943842">"הקש שוב כדי לפתוח"</string>
     <string name="keyguard_unlock" msgid="8043466894212841998">"החלק מעלה כדי לבטל את הנעילה"</string>
+    <string name="zen_mode_forever" msgid="7420011936770086993">"עד שתכבה"</string>
+  <plurals name="zen_mode_duration_minutes">
+    <item quantity="one" msgid="9040808414992812341">"למשך דקה אחת"</item>
+    <item quantity="other" msgid="6924190729213550991">"‏למשך %d דקות"</item>
+  </plurals>
+  <plurals name="zen_mode_duration_hours">
+    <item quantity="one" msgid="3480040795582254384">"למשך שעה אחת"</item>
+    <item quantity="other" msgid="5408537517529822157">"‏למשך %d שעות"</item>
+  </plurals>
 </resources>
diff --git a/packages/SystemUI/res/values-ja/strings.xml b/packages/SystemUI/res/values-ja/strings.xml
index 3ed729b..a1f7b5e 100644
--- a/packages/SystemUI/res/values-ja/strings.xml
+++ b/packages/SystemUI/res/values-ja/strings.xml
@@ -120,6 +120,8 @@
     <string name="accessibility_desc_on" msgid="2385254693624345265">"ON"</string>
     <string name="accessibility_desc_off" msgid="6475508157786853157">"OFF"</string>
     <string name="accessibility_desc_connected" msgid="8366256693719499665">"接続済みです。"</string>
+    <!-- no translation found for accessibility_desc_connecting (3812924520316280149) -->
+    <skip />
     <string name="accessibility_data_connection_gprs" msgid="1606477224486747751">"GPRS"</string>
     <string name="accessibility_data_connection_1x" msgid="994133468120244018">"1 X"</string>
     <string name="accessibility_data_connection_hspa" msgid="2032328855462645198">"HSPA"</string>
@@ -158,6 +160,9 @@
     <string name="accessibility_quick_settings_bluetooth" msgid="5749054971341882340">"Bluetooth <xliff:g id="STATE">%s</xliff:g>"</string>
     <string name="accessibility_quick_settings_location" msgid="4577282329866813100">"現在地: <xliff:g id="STATE">%s</xliff:g>"</string>
     <string name="accessibility_quick_settings_alarm" msgid="3959908972897295660">"アラームは<xliff:g id="TIME">%s</xliff:g>に設定されています。"</string>
+    <string name="accessibility_quick_settings_close" msgid="2571790856136835943">"パネルを閉じる"</string>
+    <string name="accessibility_quick_settings_more_time" msgid="5778794273488176726">"長くする"</string>
+    <string name="accessibility_quick_settings_less_time" msgid="101026945195230084">"短くする"</string>
     <string name="data_usage_disabled_dialog_3g_title" msgid="5257833881698644687">"2G~3Gデータが無効になりました"</string>
     <string name="data_usage_disabled_dialog_4g_title" msgid="4789143363492682629">"4Gデータが無効になりました"</string>
     <string name="data_usage_disabled_dialog_mobile_title" msgid="1046047248844821202">"モバイルデータが無効になりました"</string>
@@ -209,7 +214,8 @@
     <string name="quick_settings_more_settings" msgid="326112621462813682">"詳細設定"</string>
     <string name="quick_settings_tethering_label" msgid="7153452060448575549">"テザリング"</string>
     <string name="quick_settings_hotspot_label" msgid="6046917934974004879">"アクセスポイント"</string>
-    <string name="recents_empty_message" msgid="2269156590813544104">"最近"</string>
+    <string name="quick_settings_notifications_label" msgid="4818156442169154523">"通知"</string>
+    <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="ssl_ca_cert_warning" msgid="9005954106902053641">"ネットワークが監視される\n場合があります"</string>
@@ -228,4 +234,13 @@
     <string name="speed_bump_explanation" msgid="1288875699658819755">"緊急度の低い通知を下に表示"</string>
     <string name="notification_tap_again" msgid="7590196980943943842">"開くにはもう一度タップしてください"</string>
     <string name="keyguard_unlock" msgid="8043466894212841998">"ロック解除するには上にスワイプしてください"</string>
+    <string name="zen_mode_forever" msgid="7420011936770086993">"ユーザーがOFFにするまで"</string>
+  <plurals name="zen_mode_duration_minutes">
+    <item quantity="one" msgid="9040808414992812341">"1分"</item>
+    <item quantity="other" msgid="6924190729213550991">"%d分"</item>
+  </plurals>
+  <plurals name="zen_mode_duration_hours">
+    <item quantity="one" msgid="3480040795582254384">"1時間"</item>
+    <item quantity="other" msgid="5408537517529822157">"%d時間"</item>
+  </plurals>
 </resources>
diff --git a/packages/SystemUI/res/values-ka-rGE/strings.xml b/packages/SystemUI/res/values-ka-rGE/strings.xml
index 506ec3d..a1862da 100644
--- a/packages/SystemUI/res/values-ka-rGE/strings.xml
+++ b/packages/SystemUI/res/values-ka-rGE/strings.xml
@@ -120,6 +120,8 @@
     <string name="accessibility_desc_on" msgid="2385254693624345265">"ჩართული"</string>
     <string name="accessibility_desc_off" msgid="6475508157786853157">"გამორთულია."</string>
     <string name="accessibility_desc_connected" msgid="8366256693719499665">"დაკავშირებულია."</string>
+    <!-- no translation found for accessibility_desc_connecting (3812924520316280149) -->
+    <skip />
     <string name="accessibility_data_connection_gprs" msgid="1606477224486747751">"GPRS"</string>
     <string name="accessibility_data_connection_1x" msgid="994133468120244018">"1 X"</string>
     <string name="accessibility_data_connection_hspa" msgid="2032328855462645198">"HSPA"</string>
@@ -156,6 +158,9 @@
     <string name="accessibility_quick_settings_bluetooth" msgid="5749054971341882340">"Bluetooth <xliff:g id="STATE">%s</xliff:g>."</string>
     <string name="accessibility_quick_settings_location" msgid="4577282329866813100">"მდებარეობა <xliff:g id="STATE">%s</xliff:g>."</string>
     <string name="accessibility_quick_settings_alarm" msgid="3959908972897295660">"მაღვიძარა დაყენებულია: <xliff:g id="TIME">%s</xliff:g>"</string>
+    <string name="accessibility_quick_settings_close" msgid="2571790856136835943">"არეს დახურვა"</string>
+    <string name="accessibility_quick_settings_more_time" msgid="5778794273488176726">"მეტი დრო"</string>
+    <string name="accessibility_quick_settings_less_time" msgid="101026945195230084">"ნაკლები დრო"</string>
     <string name="data_usage_disabled_dialog_3g_title" msgid="5257833881698644687">"2G-3G ინტერნეტი გაითიშა."</string>
     <string name="data_usage_disabled_dialog_4g_title" msgid="4789143363492682629">"4G მონაცემები გათიშულია"</string>
     <string name="data_usage_disabled_dialog_mobile_title" msgid="1046047248844821202">"მობილური ინტერნეტი გაითიშა."</string>
@@ -202,12 +207,14 @@
     <string name="quick_settings_remote_display_no_connection_label" msgid="372107699274391290">"Cast Screen"</string>
     <string name="quick_settings_brightness_dialog_title" msgid="8599674057673605368">"განათება"</string>
     <string name="quick_settings_brightness_dialog_auto_brightness_label" msgid="5064982743784071218">"ავტომატურად"</string>
-    <string name="quick_settings_inversion_label" msgid="8790919884718619648">"ინვერტირება"</string>
+    <string name="quick_settings_inversion_label" msgid="8790919884718619648">"ფერების შებრუნება"</string>
     <string name="quick_settings_color_space_label" msgid="853443689745584770">"ფერთა კორექციის რეჟიმი"</string>
     <string name="quick_settings_more_settings" msgid="326112621462813682">"დამატებითი პარამეტრები"</string>
     <string name="quick_settings_tethering_label" msgid="7153452060448575549">"მოდემის რეჟიმი"</string>
     <string name="quick_settings_hotspot_label" msgid="6046917934974004879">"წვდომის წერტილი"</string>
-    <string name="recents_empty_message" msgid="2269156590813544104">"ბოლო დროის"</string>
+    <string name="quick_settings_notifications_label" msgid="4818156442169154523">"შეტყობინებები"</string>
+    <!-- no translation found for recents_empty_message (7883614615463619450) -->
+    <skip />
     <string name="recents_app_info_button_label" msgid="2890317189376000030">"აპლიკაციის შესახებ"</string>
     <string name="recents_search_bar_label" msgid="8074997400187836677">"ძიება"</string>
     <string name="ssl_ca_cert_warning" msgid="9005954106902053641">"შესაძლოა ქსელზე\nმონიტორინგი ხორციელდებოდეს"</string>
@@ -223,8 +230,16 @@
   <plurals name="keyguard_more_overflow_text">
     <item quantity="other" msgid="9180696159506883684">"%d სხვა"</item>
   </plurals>
-    <!-- no translation found for speed_bump_explanation (1288875699658819755) -->
-    <skip />
+    <string name="speed_bump_explanation" msgid="1288875699658819755">"ქვემოთ მითითებულია ნაკლებად სასწრაფო შეტყობინებები"</string>
     <string name="notification_tap_again" msgid="7590196980943943842">"შეეხეთ ისევ გასახსნელად"</string>
     <string name="keyguard_unlock" msgid="8043466894212841998">"გაასრიალეთ ზევით განსაბლოკად"</string>
+    <string name="zen_mode_forever" msgid="7420011936770086993">"სანამ ამას გამორთავდეთ"</string>
+  <plurals name="zen_mode_duration_minutes">
+    <item quantity="one" msgid="9040808414992812341">"ერთი წუთით"</item>
+    <item quantity="other" msgid="6924190729213550991">"%d წუთით"</item>
+  </plurals>
+  <plurals name="zen_mode_duration_hours">
+    <item quantity="one" msgid="3480040795582254384">"ერთი საათით"</item>
+    <item quantity="other" msgid="5408537517529822157">"%d საათით"</item>
+  </plurals>
 </resources>
diff --git a/packages/SystemUI/res/values-km-rKH/strings.xml b/packages/SystemUI/res/values-km-rKH/strings.xml
index f3b4e9e..3f5b844 100644
--- a/packages/SystemUI/res/values-km-rKH/strings.xml
+++ b/packages/SystemUI/res/values-km-rKH/strings.xml
@@ -120,6 +120,8 @@
     <string name="accessibility_desc_on" msgid="2385254693624345265">"បើក។"</string>
     <string name="accessibility_desc_off" msgid="6475508157786853157">"បិទ"</string>
     <string name="accessibility_desc_connected" msgid="8366256693719499665">"បាន​តភ្ជាប់។"</string>
+    <!-- no translation found for accessibility_desc_connecting (3812924520316280149) -->
+    <skip />
     <string name="accessibility_data_connection_gprs" msgid="1606477224486747751">"GPRS"</string>
     <string name="accessibility_data_connection_1x" msgid="994133468120244018">"1 X"</string>
     <string name="accessibility_data_connection_hspa" msgid="2032328855462645198">"HSPA"</string>
@@ -156,6 +158,9 @@
     <string name="accessibility_quick_settings_bluetooth" msgid="5749054971341882340">"ប៊្លូធូស <xliff:g id="STATE">%s</xliff:g> ។"</string>
     <string name="accessibility_quick_settings_location" msgid="4577282329866813100">"ទីតាំង <xliff:g id="STATE">%s</xliff:g> ។"</string>
     <string name="accessibility_quick_settings_alarm" msgid="3959908972897295660">"កំណត់​សំឡេង​រោទ៍​សម្រាប់ <xliff:g id="TIME">%s</xliff:g> ។"</string>
+    <string name="accessibility_quick_settings_close" msgid="2571790856136835943">"បិទ​បន្ទះ"</string>
+    <string name="accessibility_quick_settings_more_time" msgid="5778794273488176726">"ពេល​ច្រើនជាង"</string>
+    <string name="accessibility_quick_settings_less_time" msgid="101026945195230084">"ពេល​តិចជាង"</string>
     <string name="data_usage_disabled_dialog_3g_title" msgid="5257833881698644687">"បាន​បិទ​ទិន្នន័យ 2G-3G"</string>
     <string name="data_usage_disabled_dialog_4g_title" msgid="4789143363492682629">"បាន​បិទ​ទិន្នន័យ 4G"</string>
     <string name="data_usage_disabled_dialog_mobile_title" msgid="1046047248844821202">"បាន​បិទ​ទិន្នន័យ​ចល័ត"</string>
@@ -207,7 +212,9 @@
     <string name="quick_settings_more_settings" msgid="326112621462813682">"ការ​កំណត់​ច្រើន​ទៀត"</string>
     <string name="quick_settings_tethering_label" msgid="7153452060448575549">"ការ​ភ្ជាប់"</string>
     <string name="quick_settings_hotspot_label" msgid="6046917934974004879">"ហតស្ប៉ត"</string>
-    <string name="recents_empty_message" msgid="2269156590813544104">"ថ្មីៗ"</string>
+    <string name="quick_settings_notifications_label" msgid="4818156442169154523">"ការ​ជូនដំណឹង"</string>
+    <!-- no translation found for recents_empty_message (7883614615463619450) -->
+    <skip />
     <string name="recents_app_info_button_label" msgid="2890317189376000030">"ព័ត៌មាន​កម្មវិធី"</string>
     <string name="recents_search_bar_label" msgid="8074997400187836677">"ស្វែងរក"</string>
     <string name="ssl_ca_cert_warning" msgid="9005954106902053641">"បណ្ដាញ​អាច​\nត្រូវ​បាន​ត្រួតពិនិត្យ​"</string>
@@ -226,4 +233,13 @@
     <string name="speed_bump_explanation" msgid="1288875699658819755">"ការ​ជូន​ដំណឹង​​មិន​សូវ​បន្ទាន់​ខាង​ក្រោម"</string>
     <string name="notification_tap_again" msgid="7590196980943943842">"ប៉ះ​ម្ដង​ទៀត ដើម្បី​បើក"</string>
     <string name="keyguard_unlock" msgid="8043466894212841998">"អូស​ឡើង​លើ ដើម្បី​ដោះ​សោ"</string>
+    <string name="zen_mode_forever" msgid="7420011936770086993">"រហូត​ដល់ពេល​​អ្នក​បិទ​វា"</string>
+  <plurals name="zen_mode_duration_minutes">
+    <item quantity="one" msgid="9040808414992812341">"សម្រាប់​មួយ​នាទី"</item>
+    <item quantity="other" msgid="6924190729213550991">"សម្រាប់ %d នាទី"</item>
+  </plurals>
+  <plurals name="zen_mode_duration_hours">
+    <item quantity="one" msgid="3480040795582254384">"សម្រាប់​មួយ​ម៉ោង"</item>
+    <item quantity="other" msgid="5408537517529822157">"សម្រាប់ %d ម៉ោង"</item>
+  </plurals>
 </resources>
diff --git a/packages/SystemUI/res/values-ko/strings.xml b/packages/SystemUI/res/values-ko/strings.xml
index 2b39e14..4772e54 100644
--- a/packages/SystemUI/res/values-ko/strings.xml
+++ b/packages/SystemUI/res/values-ko/strings.xml
@@ -120,6 +120,8 @@
     <string name="accessibility_desc_on" msgid="2385254693624345265">"사용"</string>
     <string name="accessibility_desc_off" msgid="6475508157786853157">"사용 안함"</string>
     <string name="accessibility_desc_connected" msgid="8366256693719499665">"연결됨"</string>
+    <!-- no translation found for accessibility_desc_connecting (3812924520316280149) -->
+    <skip />
     <string name="accessibility_data_connection_gprs" msgid="1606477224486747751">"GPRS"</string>
     <string name="accessibility_data_connection_1x" msgid="994133468120244018">"1 X"</string>
     <string name="accessibility_data_connection_hspa" msgid="2032328855462645198">"HSPA"</string>
@@ -156,6 +158,9 @@
     <string name="accessibility_quick_settings_bluetooth" msgid="5749054971341882340">"블루투스 <xliff:g id="STATE">%s</xliff:g>"</string>
     <string name="accessibility_quick_settings_location" msgid="4577282329866813100">"위치 <xliff:g id="STATE">%s</xliff:g>"</string>
     <string name="accessibility_quick_settings_alarm" msgid="3959908972897295660">"알람이 <xliff:g id="TIME">%s</xliff:g>(으)로 설정되었습니다."</string>
+    <string name="accessibility_quick_settings_close" msgid="2571790856136835943">"패널 닫기"</string>
+    <string name="accessibility_quick_settings_more_time" msgid="5778794273488176726">"시간 늘리기"</string>
+    <string name="accessibility_quick_settings_less_time" msgid="101026945195230084">"시간 줄이기"</string>
     <string name="data_usage_disabled_dialog_3g_title" msgid="5257833881698644687">"2G-3G 데이터 사용중지됨"</string>
     <string name="data_usage_disabled_dialog_4g_title" msgid="4789143363492682629">"4G 데이터 사용중지됨"</string>
     <string name="data_usage_disabled_dialog_mobile_title" msgid="1046047248844821202">"모바일 데이터 사용중지됨"</string>
@@ -207,7 +212,8 @@
     <string name="quick_settings_more_settings" msgid="326112621462813682">"설정 더보기"</string>
     <string name="quick_settings_tethering_label" msgid="7153452060448575549">"테더링"</string>
     <string name="quick_settings_hotspot_label" msgid="6046917934974004879">"핫스팟"</string>
-    <string name="recents_empty_message" msgid="2269156590813544104">"최근"</string>
+    <string name="quick_settings_notifications_label" msgid="4818156442169154523">"알림"</string>
+    <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="ssl_ca_cert_warning" msgid="9005954106902053641">"네트워크가\n모니터링될 수 있음"</string>
@@ -226,4 +232,13 @@
     <string name="speed_bump_explanation" msgid="1288875699658819755">"아래에 덜 급한 알림 표시"</string>
     <string name="notification_tap_again" msgid="7590196980943943842">"다시 탭하여 열기"</string>
     <string name="keyguard_unlock" msgid="8043466894212841998">"위로 스와이프하여 잠금 해제"</string>
+    <string name="zen_mode_forever" msgid="7420011936770086993">"이 기능을 사용 중지할 때까지"</string>
+  <plurals name="zen_mode_duration_minutes">
+    <item quantity="one" msgid="9040808414992812341">"1분 동안"</item>
+    <item quantity="other" msgid="6924190729213550991">"%d분 동안"</item>
+  </plurals>
+  <plurals name="zen_mode_duration_hours">
+    <item quantity="one" msgid="3480040795582254384">"1시간 동안"</item>
+    <item quantity="other" msgid="5408537517529822157">"%d시간 동안"</item>
+  </plurals>
 </resources>
diff --git a/packages/SystemUI/res/values-land/dimens.xml b/packages/SystemUI/res/values-land/dimens.xml
index 39ce0a2..42c4392 100644
--- a/packages/SystemUI/res/values-land/dimens.xml
+++ b/packages/SystemUI/res/values-land/dimens.xml
@@ -38,6 +38,9 @@
     <dimen name="status_bar_recents_app_icon_left_margin">8dp</dimen>
     <dimen name="status_bar_recents_app_icon_top_margin">8dp</dimen>
 
+    <!-- The side padding for the task stack as a percentage of the width. -->
+    <item name="recents_stack_width_padding_percentage" format="float" type="dimen">0.2229</item>
+
     <!-- Width of the zen mode interstitial dialog. -->
     <dimen name="zen_mode_dialog_width">384dp</dimen>
 </resources>
diff --git a/packages/SystemUI/res/values-lo-rLA/strings.xml b/packages/SystemUI/res/values-lo-rLA/strings.xml
index 5d2ccbc..d5117a9 100644
--- a/packages/SystemUI/res/values-lo-rLA/strings.xml
+++ b/packages/SystemUI/res/values-lo-rLA/strings.xml
@@ -120,6 +120,8 @@
     <string name="accessibility_desc_on" msgid="2385254693624345265">"ເປີດ."</string>
     <string name="accessibility_desc_off" msgid="6475508157786853157">"ປິດ."</string>
     <string name="accessibility_desc_connected" msgid="8366256693719499665">"ເຊື່ອມ​ຕໍ່ແລ້ວ."</string>
+    <!-- no translation found for accessibility_desc_connecting (3812924520316280149) -->
+    <skip />
     <string name="accessibility_data_connection_gprs" msgid="1606477224486747751">"GPRS"</string>
     <string name="accessibility_data_connection_1x" msgid="994133468120244018">"1 X"</string>
     <string name="accessibility_data_connection_hspa" msgid="2032328855462645198">"HSPA"</string>
@@ -156,6 +158,9 @@
     <string name="accessibility_quick_settings_bluetooth" msgid="5749054971341882340">"Bluetooth <xliff:g id="STATE">%s</xliff:g>."</string>
     <string name="accessibility_quick_settings_location" msgid="4577282329866813100">"ສະ​ຖານ​ທີ່ <xliff:g id="STATE">%s</xliff:g>."</string>
     <string name="accessibility_quick_settings_alarm" msgid="3959908972897295660">"ຕັ້ງໂມງປຸກ <xliff:g id="TIME">%s</xliff:g> ແລ້ວ."</string>
+    <string name="accessibility_quick_settings_close" msgid="2571790856136835943">"​ປິດ​ແຖບ"</string>
+    <string name="accessibility_quick_settings_more_time" msgid="5778794273488176726">"​ເພີ່ມ​ເວ​ລາ"</string>
+    <string name="accessibility_quick_settings_less_time" msgid="101026945195230084">"ຫຼຸດ​ເວ​ລາ"</string>
     <string name="data_usage_disabled_dialog_3g_title" msgid="5257833881698644687">"ອິນເຕີເນັດ 2G​, 3G ຖືກປິດແລ້ວ"</string>
     <string name="data_usage_disabled_dialog_4g_title" msgid="4789143363492682629">"ການນຳໃຊ້ຂໍ້ມູນ 4G ຖືກປິດແລ້ວ"</string>
     <string name="data_usage_disabled_dialog_mobile_title" msgid="1046047248844821202">"ອິນເຕີເນັດໃນມືຖືຖືກປິດການນຳໃຊ້ແລ້ວ"</string>
@@ -207,7 +212,8 @@
     <string name="quick_settings_more_settings" msgid="326112621462813682">"​ການ​ຕັ້ງ​ຄ່າ​ເພີ່ມ​ເຕີມ"</string>
     <string name="quick_settings_tethering_label" msgid="7153452060448575549">"​ການ​ປ່ອນ​ສັນ​ຍານ"</string>
     <string name="quick_settings_hotspot_label" msgid="6046917934974004879">"​ຮັອດ​ສະ​ປອດ"</string>
-    <string name="recents_empty_message" msgid="2269156590813544104">"ບໍ່​ດົນ​ມາ​ນີ້"</string>
+    <string name="quick_settings_notifications_label" msgid="4818156442169154523">"ການແຈ້ງເຕືອນ"</string>
+    <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="ssl_ca_cert_warning" msgid="9005954106902053641">"ເຄືອຄ່າຍອາດ\nຖືກຕິດຕາມ"</string>
@@ -226,4 +232,13 @@
     <string name="speed_bump_explanation" msgid="1288875699658819755">"ການ​ແຈ້ງເຕືອນ​ທີ່​ສຳຄັນ​ໜ້ອຍ​ກວ່າ​ຢູ່​ດ້ານ​ລຸ່ມ"</string>
     <string name="notification_tap_again" msgid="7590196980943943842">"ແຕະ​ອີກ​ຄັ້ງ​ເພື່ອ​ເປີດ"</string>
     <string name="keyguard_unlock" msgid="8043466894212841998">"ເລື່ອນ​ຂຶ້ນ​ເພື່ອ​ປົດ​ລັອກ"</string>
+    <string name="zen_mode_forever" msgid="7420011936770086993">"ຈົນກວ່າ​ທ່ານ​ຈະ​ປິດ​"</string>
+  <plurals name="zen_mode_duration_minutes">
+    <item quantity="one" msgid="9040808414992812341">"ເປັນ​ເວລາ​ນຶ່ງ​ນາ​ທີ"</item>
+    <item quantity="other" msgid="6924190729213550991">"ເປັນ​ເວລາ %d ນາ​ທີ"</item>
+  </plurals>
+  <plurals name="zen_mode_duration_hours">
+    <item quantity="one" msgid="3480040795582254384">"ເປັນ​ເວລາ​ນຶ່ງ​ຊົ່ວ​ໂມງ"</item>
+    <item quantity="other" msgid="5408537517529822157">"ເປັນ​ເວລາ %d ຊົ່ວ​ໂມງ"</item>
+  </plurals>
 </resources>
diff --git a/packages/SystemUI/res/values-lt/strings.xml b/packages/SystemUI/res/values-lt/strings.xml
index cede14c..574e559 100644
--- a/packages/SystemUI/res/values-lt/strings.xml
+++ b/packages/SystemUI/res/values-lt/strings.xml
@@ -120,6 +120,8 @@
     <string name="accessibility_desc_on" msgid="2385254693624345265">"Įjungta."</string>
     <string name="accessibility_desc_off" msgid="6475508157786853157">"Išjungta."</string>
     <string name="accessibility_desc_connected" msgid="8366256693719499665">"Prijungta."</string>
+    <!-- no translation found for accessibility_desc_connecting (3812924520316280149) -->
+    <skip />
     <string name="accessibility_data_connection_gprs" msgid="1606477224486747751">"GPRS"</string>
     <string name="accessibility_data_connection_1x" msgid="994133468120244018">"1 X"</string>
     <string name="accessibility_data_connection_hspa" msgid="2032328855462645198">"HSPA"</string>
@@ -156,6 +158,9 @@
     <string name="accessibility_quick_settings_bluetooth" msgid="5749054971341882340">"„Bluetooth“ <xliff:g id="STATE">%s</xliff:g>."</string>
     <string name="accessibility_quick_settings_location" msgid="4577282329866813100">"Vietovė – <xliff:g id="STATE">%s</xliff:g>."</string>
     <string name="accessibility_quick_settings_alarm" msgid="3959908972897295660">"Signalas nustatytas <xliff:g id="TIME">%s</xliff:g>."</string>
+    <string name="accessibility_quick_settings_close" msgid="2571790856136835943">"Uždaryti skydelį"</string>
+    <string name="accessibility_quick_settings_more_time" msgid="5778794273488176726">"Daugiau laiko"</string>
+    <string name="accessibility_quick_settings_less_time" msgid="101026945195230084">"Mažiau laiko"</string>
     <string name="data_usage_disabled_dialog_3g_title" msgid="5257833881698644687">"2G–3G duomenys neleidžiami"</string>
     <string name="data_usage_disabled_dialog_4g_title" msgid="4789143363492682629">"4G duomenys neleidžiami"</string>
     <string name="data_usage_disabled_dialog_mobile_title" msgid="1046047248844821202">"Mobilieji duomenys neleidžiami"</string>
@@ -207,7 +212,8 @@
     <string name="quick_settings_more_settings" msgid="326112621462813682">"Daugiau nustatymų"</string>
     <string name="quick_settings_tethering_label" msgid="7153452060448575549">"Susiejimas"</string>
     <string name="quick_settings_hotspot_label" msgid="6046917934974004879">"Viešosios interneto prieigos taškas"</string>
-    <string name="recents_empty_message" msgid="2269156590813544104">"PASTARIEJI"</string>
+    <string name="quick_settings_notifications_label" msgid="4818156442169154523">"Pranešimai"</string>
+    <string name="recents_empty_message" msgid="7883614615463619450">"Nėra naujausių programų"</string>
     <string name="recents_app_info_button_label" msgid="2890317189376000030">"Programos informacija"</string>
     <string name="recents_search_bar_label" msgid="8074997400187836677">"paieška"</string>
     <string name="ssl_ca_cert_warning" msgid="9005954106902053641">"Tinklas gali\nbūti stebimas"</string>
@@ -226,4 +232,13 @@
     <string name="speed_bump_explanation" msgid="1288875699658819755">"Mažiau skubūs pranešimai toliau"</string>
     <string name="notification_tap_again" msgid="7590196980943943842">"Palieskite dar kartą, kad atidarytumėte"</string>
     <string name="keyguard_unlock" msgid="8043466894212841998">"Perbraukite aukštyn, kad atrakintumėte"</string>
+    <string name="zen_mode_forever" msgid="7420011936770086993">"Kol išjungsite"</string>
+  <plurals name="zen_mode_duration_minutes">
+    <item quantity="one" msgid="9040808414992812341">"1 min."</item>
+    <item quantity="other" msgid="6924190729213550991">"%d min."</item>
+  </plurals>
+  <plurals name="zen_mode_duration_hours">
+    <item quantity="one" msgid="3480040795582254384">"1 val."</item>
+    <item quantity="other" msgid="5408537517529822157">"%d val."</item>
+  </plurals>
 </resources>
diff --git a/packages/SystemUI/res/values-lv/strings.xml b/packages/SystemUI/res/values-lv/strings.xml
index 280ce40..398553d 100644
--- a/packages/SystemUI/res/values-lv/strings.xml
+++ b/packages/SystemUI/res/values-lv/strings.xml
@@ -120,6 +120,8 @@
     <string name="accessibility_desc_on" msgid="2385254693624345265">"Ieslēgts"</string>
     <string name="accessibility_desc_off" msgid="6475508157786853157">"Izslēgts"</string>
     <string name="accessibility_desc_connected" msgid="8366256693719499665">"Savienojums ir izveidots."</string>
+    <!-- no translation found for accessibility_desc_connecting (3812924520316280149) -->
+    <skip />
     <string name="accessibility_data_connection_gprs" msgid="1606477224486747751">"GPRS"</string>
     <string name="accessibility_data_connection_1x" msgid="994133468120244018">"1 X"</string>
     <string name="accessibility_data_connection_hspa" msgid="2032328855462645198">"HSPA"</string>
@@ -156,6 +158,9 @@
     <string name="accessibility_quick_settings_bluetooth" msgid="5749054971341882340">"Bluetooth statuss: <xliff:g id="STATE">%s</xliff:g>"</string>
     <string name="accessibility_quick_settings_location" msgid="4577282329866813100">"Atrašanās vieta: <xliff:g id="STATE">%s</xliff:g>."</string>
     <string name="accessibility_quick_settings_alarm" msgid="3959908972897295660">"Signāls ir iestatīts uz: <xliff:g id="TIME">%s</xliff:g>."</string>
+    <string name="accessibility_quick_settings_close" msgid="2571790856136835943">"Aizvērt paneli"</string>
+    <string name="accessibility_quick_settings_more_time" msgid="5778794273488176726">"Ilgāk"</string>
+    <string name="accessibility_quick_settings_less_time" msgid="101026945195230084">"Mazāks laiks"</string>
     <string name="data_usage_disabled_dialog_3g_title" msgid="5257833881698644687">"2G–3G dati atspējoti"</string>
     <string name="data_usage_disabled_dialog_4g_title" msgid="4789143363492682629">"4G dati atspējoti"</string>
     <string name="data_usage_disabled_dialog_mobile_title" msgid="1046047248844821202">"Mobilie dati atspējoti"</string>
@@ -207,7 +212,9 @@
     <string name="quick_settings_more_settings" msgid="326112621462813682">"Vairāk iestatījumu"</string>
     <string name="quick_settings_tethering_label" msgid="7153452060448575549">"Piesaiste"</string>
     <string name="quick_settings_hotspot_label" msgid="6046917934974004879">"Tīklājs"</string>
-    <string name="recents_empty_message" msgid="2269156590813544104">"JAUNĀKIE"</string>
+    <string name="quick_settings_notifications_label" msgid="4818156442169154523">"Paziņojumi"</string>
+    <!-- no translation found for recents_empty_message (7883614615463619450) -->
+    <skip />
     <string name="recents_app_info_button_label" msgid="2890317189376000030">"Informācija par lietojumprogrammu"</string>
     <string name="recents_search_bar_label" msgid="8074997400187836677">"Meklēt"</string>
     <string name="ssl_ca_cert_warning" msgid="9005954106902053641">"Tīkls var\ntikt uzraudzīts"</string>
@@ -226,4 +233,13 @@
     <string name="speed_bump_explanation" msgid="1288875699658819755">"Mazāk steidzami paziņojumi tiek rādīti tālāk"</string>
     <string name="notification_tap_again" msgid="7590196980943943842">"Pieskarieties vēlreiz, lai atvērtu"</string>
     <string name="keyguard_unlock" msgid="8043466894212841998">"Velciet uz augšu, lai atbloķētu"</string>
+    <string name="zen_mode_forever" msgid="7420011936770086993">"Līdz brīdim, kad izslēgsiet"</string>
+  <plurals name="zen_mode_duration_minutes">
+    <item quantity="one" msgid="9040808414992812341">"Vienu minūti"</item>
+    <item quantity="other" msgid="6924190729213550991">"%d min"</item>
+  </plurals>
+  <plurals name="zen_mode_duration_hours">
+    <item quantity="one" msgid="3480040795582254384">"Vienu stundu"</item>
+    <item quantity="other" msgid="5408537517529822157">"%d h"</item>
+  </plurals>
 </resources>
diff --git a/packages/SystemUI/res/values-mn-rMN/strings.xml b/packages/SystemUI/res/values-mn-rMN/strings.xml
index 773a6fb..6a2e79e 100644
--- a/packages/SystemUI/res/values-mn-rMN/strings.xml
+++ b/packages/SystemUI/res/values-mn-rMN/strings.xml
@@ -120,6 +120,8 @@
     <string name="accessibility_desc_on" msgid="2385254693624345265">"Идэвхижсэн."</string>
     <string name="accessibility_desc_off" msgid="6475508157786853157">"Унтраах"</string>
     <string name="accessibility_desc_connected" msgid="8366256693719499665">"Холбогдсон."</string>
+    <!-- no translation found for accessibility_desc_connecting (3812924520316280149) -->
+    <skip />
     <string name="accessibility_data_connection_gprs" msgid="1606477224486747751">"GPRS"</string>
     <string name="accessibility_data_connection_1x" msgid="994133468120244018">"1 X"</string>
     <string name="accessibility_data_connection_hspa" msgid="2032328855462645198">"HSPA"</string>
@@ -156,6 +158,9 @@
     <string name="accessibility_quick_settings_bluetooth" msgid="5749054971341882340">"Блютүүт <xliff:g id="STATE">%s</xliff:g>."</string>
     <string name="accessibility_quick_settings_location" msgid="4577282329866813100">"Байршил <xliff:g id="STATE">%s</xliff:g>."</string>
     <string name="accessibility_quick_settings_alarm" msgid="3959908972897295660">"Сэрүүлгийг <xliff:g id="TIME">%s</xliff:g>-д тохируулсан."</string>
+    <string name="accessibility_quick_settings_close" msgid="2571790856136835943">"Самбарыг хаах"</string>
+    <string name="accessibility_quick_settings_more_time" msgid="5778794273488176726">"Цаг нэмэх"</string>
+    <string name="accessibility_quick_settings_less_time" msgid="101026945195230084">"Цаг хасах"</string>
     <string name="data_usage_disabled_dialog_3g_title" msgid="5257833881698644687">"2G-3G дата идэвхгүй болов"</string>
     <string name="data_usage_disabled_dialog_4g_title" msgid="4789143363492682629">"4G дата идэвхгүй байна"</string>
     <string name="data_usage_disabled_dialog_mobile_title" msgid="1046047248844821202">"Мобайл дата идэвхгүй болов"</string>
@@ -207,7 +212,8 @@
     <string name="quick_settings_more_settings" msgid="326112621462813682">"Өөр тохиргоо"</string>
     <string name="quick_settings_tethering_label" msgid="7153452060448575549">"Модем болгох"</string>
     <string name="quick_settings_hotspot_label" msgid="6046917934974004879">"Сүлжээний цэг"</string>
-    <string name="recents_empty_message" msgid="2269156590813544104">"СҮҮЛИЙН"</string>
+    <string name="quick_settings_notifications_label" msgid="4818156442169154523">"Мэдэгдэл"</string>
+    <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="ssl_ca_cert_warning" msgid="9005954106902053641">"Сүлжээ хянагдаж\nбайж болзошгүй"</string>
@@ -226,4 +232,13 @@
     <string name="speed_bump_explanation" msgid="1288875699658819755">"Яаралтай биш мэдэгдлүүдийг доор"</string>
     <string name="notification_tap_again" msgid="7590196980943943842">"Нээхийн тулд дахин товшино уу"</string>
     <string name="keyguard_unlock" msgid="8043466894212841998">"Түгжээг тайлах бол шудрана уу"</string>
+    <string name="zen_mode_forever" msgid="7420011936770086993">"Таныг унтраах хүртэл"</string>
+  <plurals name="zen_mode_duration_minutes">
+    <item quantity="one" msgid="9040808414992812341">"Нэг минутын турш"</item>
+    <item quantity="other" msgid="6924190729213550991">"%d минутын турш"</item>
+  </plurals>
+  <plurals name="zen_mode_duration_hours">
+    <item quantity="one" msgid="3480040795582254384">"Нэг цагийн турш"</item>
+    <item quantity="other" msgid="5408537517529822157">"%d цагийн турш"</item>
+  </plurals>
 </resources>
diff --git a/packages/SystemUI/res/values-ms-rMY/strings.xml b/packages/SystemUI/res/values-ms-rMY/strings.xml
index 09e52cc..7ae9b55 100644
--- a/packages/SystemUI/res/values-ms-rMY/strings.xml
+++ b/packages/SystemUI/res/values-ms-rMY/strings.xml
@@ -120,6 +120,8 @@
     <string name="accessibility_desc_on" msgid="2385254693624345265">"Dihidupkan."</string>
     <string name="accessibility_desc_off" msgid="6475508157786853157">"Dimatikan."</string>
     <string name="accessibility_desc_connected" msgid="8366256693719499665">"Disambungkan."</string>
+    <!-- no translation found for accessibility_desc_connecting (3812924520316280149) -->
+    <skip />
     <string name="accessibility_data_connection_gprs" msgid="1606477224486747751">"GPRS"</string>
     <string name="accessibility_data_connection_1x" msgid="994133468120244018">"1 X"</string>
     <string name="accessibility_data_connection_hspa" msgid="2032328855462645198">"HSPA"</string>
@@ -156,6 +158,9 @@
     <string name="accessibility_quick_settings_bluetooth" msgid="5749054971341882340">"Bluetooth <xliff:g id="STATE">%s</xliff:g>."</string>
     <string name="accessibility_quick_settings_location" msgid="4577282329866813100">"Lokasi <xliff:g id="STATE">%s</xliff:g>."</string>
     <string name="accessibility_quick_settings_alarm" msgid="3959908972897295660">"Penggera ditetapkan pada <xliff:g id="TIME">%s</xliff:g>."</string>
+    <string name="accessibility_quick_settings_close" msgid="2571790856136835943">"Tutup panel"</string>
+    <string name="accessibility_quick_settings_more_time" msgid="5778794273488176726">"Lagi masa"</string>
+    <string name="accessibility_quick_settings_less_time" msgid="101026945195230084">"Kurang masa"</string>
     <string name="data_usage_disabled_dialog_3g_title" msgid="5257833881698644687">"Data 2G-3G dilumpuhkan"</string>
     <string name="data_usage_disabled_dialog_4g_title" msgid="4789143363492682629">"Data 4G dilumpuhkan"</string>
     <string name="data_usage_disabled_dialog_mobile_title" msgid="1046047248844821202">"Data mudah alih dilumpuhkan"</string>
@@ -207,7 +212,9 @@
     <string name="quick_settings_more_settings" msgid="326112621462813682">"Lagi tetapan"</string>
     <string name="quick_settings_tethering_label" msgid="7153452060448575549">"Penambatan"</string>
     <string name="quick_settings_hotspot_label" msgid="6046917934974004879">"Tempat liputan"</string>
-    <string name="recents_empty_message" msgid="2269156590813544104">"TERBAHARU"</string>
+    <string name="quick_settings_notifications_label" msgid="4818156442169154523">"Pemberitahuan"</string>
+    <!-- no translation found for recents_empty_message (7883614615463619450) -->
+    <skip />
     <string name="recents_app_info_button_label" msgid="2890317189376000030">"Maklumat Aplikasi"</string>
     <string name="recents_search_bar_label" msgid="8074997400187836677">"cari"</string>
     <string name="ssl_ca_cert_warning" msgid="9005954106902053641">"Rangkaian mungkin\nboleh dipantau"</string>
@@ -223,8 +230,16 @@
   <plurals name="keyguard_more_overflow_text">
     <item quantity="other" msgid="9180696159506883684">"%d lagi"</item>
   </plurals>
-    <!-- no translation found for speed_bump_explanation (1288875699658819755) -->
-    <skip />
+    <string name="speed_bump_explanation" msgid="1288875699658819755">"Pemberitahuan kurang penting di bawah"</string>
     <string name="notification_tap_again" msgid="7590196980943943842">"Ketik lagi untuk membuka"</string>
     <string name="keyguard_unlock" msgid="8043466894212841998">"Leret ke atas untuk membuka kunci"</string>
+    <string name="zen_mode_forever" msgid="7420011936770086993">"Sehingga anda matikan"</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>
+  </plurals>
+  <plurals name="zen_mode_duration_hours">
+    <item quantity="one" msgid="3480040795582254384">"Selama satu jam"</item>
+    <item quantity="other" msgid="5408537517529822157">"Selama %d jam"</item>
+  </plurals>
 </resources>
diff --git a/packages/SystemUI/res/values-nb/strings.xml b/packages/SystemUI/res/values-nb/strings.xml
index 7ffd381..e842c0f 100644
--- a/packages/SystemUI/res/values-nb/strings.xml
+++ b/packages/SystemUI/res/values-nb/strings.xml
@@ -120,6 +120,8 @@
     <string name="accessibility_desc_on" msgid="2385254693624345265">"På."</string>
     <string name="accessibility_desc_off" msgid="6475508157786853157">"Av."</string>
     <string name="accessibility_desc_connected" msgid="8366256693719499665">"Tilkoblet."</string>
+    <!-- no translation found for accessibility_desc_connecting (3812924520316280149) -->
+    <skip />
     <string name="accessibility_data_connection_gprs" msgid="1606477224486747751">"GPRS"</string>
     <string name="accessibility_data_connection_1x" msgid="994133468120244018">"1 X"</string>
     <string name="accessibility_data_connection_hspa" msgid="2032328855462645198">"HSPA"</string>
@@ -156,6 +158,9 @@
     <string name="accessibility_quick_settings_bluetooth" msgid="5749054971341882340">"Bluetooth – <xliff:g id="STATE">%s</xliff:g>."</string>
     <string name="accessibility_quick_settings_location" msgid="4577282329866813100">"Posisjon <xliff:g id="STATE">%s</xliff:g>."</string>
     <string name="accessibility_quick_settings_alarm" msgid="3959908972897295660">"Alarmen ble stilt for <xliff:g id="TIME">%s</xliff:g>."</string>
+    <string name="accessibility_quick_settings_close" msgid="2571790856136835943">"Lukk panelet"</string>
+    <string name="accessibility_quick_settings_more_time" msgid="5778794273488176726">"Mer tid"</string>
+    <string name="accessibility_quick_settings_less_time" msgid="101026945195230084">"Mindre tid"</string>
     <string name="data_usage_disabled_dialog_3g_title" msgid="5257833881698644687">"2G-3G-data er deaktivert"</string>
     <string name="data_usage_disabled_dialog_4g_title" msgid="4789143363492682629">"4G-data er deaktivert"</string>
     <string name="data_usage_disabled_dialog_mobile_title" msgid="1046047248844821202">"Mobildata er deaktivert"</string>
@@ -207,7 +212,8 @@
     <string name="quick_settings_more_settings" msgid="326112621462813682">"Flere innstillinger"</string>
     <string name="quick_settings_tethering_label" msgid="7153452060448575549">"Tilknytning"</string>
     <string name="quick_settings_hotspot_label" msgid="6046917934974004879">"Wi-Fi-sone"</string>
-    <string name="recents_empty_message" msgid="2269156590813544104">"NYLIGE"</string>
+    <string name="quick_settings_notifications_label" msgid="4818156442169154523">"Varsler"</string>
+    <string name="recents_empty_message" msgid="7883614615463619450">"Ingen nylige apper"</string>
     <string name="recents_app_info_button_label" msgid="2890317189376000030">"Appinformasjon"</string>
     <string name="recents_search_bar_label" msgid="8074997400187836677">"Søk"</string>
     <string name="ssl_ca_cert_warning" msgid="9005954106902053641">"Nettverket kan\nvære overvåket"</string>
@@ -226,4 +232,13 @@
     <string name="speed_bump_explanation" msgid="1288875699658819755">"Mindre presserende varsler nedenfor"</string>
     <string name="notification_tap_again" msgid="7590196980943943842">"Trykk på nytt for å åpne"</string>
     <string name="keyguard_unlock" msgid="8043466894212841998">"Sveip oppover for å låse opp"</string>
+    <string name="zen_mode_forever" msgid="7420011936770086993">"Inntil du slår av funksjonen"</string>
+  <plurals name="zen_mode_duration_minutes">
+    <item quantity="one" msgid="9040808414992812341">"I ett minutt"</item>
+    <item quantity="other" msgid="6924190729213550991">"I %d minutter"</item>
+  </plurals>
+  <plurals name="zen_mode_duration_hours">
+    <item quantity="one" msgid="3480040795582254384">"I én time"</item>
+    <item quantity="other" msgid="5408537517529822157">"I %d timer"</item>
+  </plurals>
 </resources>
diff --git a/packages/SystemUI/res/values-nl/strings.xml b/packages/SystemUI/res/values-nl/strings.xml
index e79adf9..360b71d 100644
--- a/packages/SystemUI/res/values-nl/strings.xml
+++ b/packages/SystemUI/res/values-nl/strings.xml
@@ -120,6 +120,8 @@
     <string name="accessibility_desc_on" msgid="2385254693624345265">"Ingeschakeld."</string>
     <string name="accessibility_desc_off" msgid="6475508157786853157">"Uitgeschakeld."</string>
     <string name="accessibility_desc_connected" msgid="8366256693719499665">"Verbonden."</string>
+    <!-- no translation found for accessibility_desc_connecting (3812924520316280149) -->
+    <skip />
     <string name="accessibility_data_connection_gprs" msgid="1606477224486747751">"GPRS"</string>
     <string name="accessibility_data_connection_1x" msgid="994133468120244018">"1 X"</string>
     <string name="accessibility_data_connection_hspa" msgid="2032328855462645198">"HSPA"</string>
@@ -156,6 +158,9 @@
     <string name="accessibility_quick_settings_bluetooth" msgid="5749054971341882340">"Bluetooth: <xliff:g id="STATE">%s</xliff:g>."</string>
     <string name="accessibility_quick_settings_location" msgid="4577282329866813100">"Locatie <xliff:g id="STATE">%s</xliff:g>."</string>
     <string name="accessibility_quick_settings_alarm" msgid="3959908972897295660">"Alarm is ingesteld op <xliff:g id="TIME">%s</xliff:g>."</string>
+    <string name="accessibility_quick_settings_close" msgid="2571790856136835943">"Deelvenster sluiten"</string>
+    <string name="accessibility_quick_settings_more_time" msgid="5778794273488176726">"Meer tijd"</string>
+    <string name="accessibility_quick_settings_less_time" msgid="101026945195230084">"Minder tijd"</string>
     <string name="data_usage_disabled_dialog_3g_title" msgid="5257833881698644687">"2G-/3G-gegevens uitgeschakeld"</string>
     <string name="data_usage_disabled_dialog_4g_title" msgid="4789143363492682629">"4G-gegevens uitgeschakeld"</string>
     <string name="data_usage_disabled_dialog_mobile_title" msgid="1046047248844821202">"Mobiele gegevens uitgeschakeld"</string>
@@ -207,7 +212,8 @@
     <string name="quick_settings_more_settings" msgid="326112621462813682">"Meer instellingen"</string>
     <string name="quick_settings_tethering_label" msgid="7153452060448575549">"Tethering"</string>
     <string name="quick_settings_hotspot_label" msgid="6046917934974004879">"Hotspot"</string>
-    <string name="recents_empty_message" msgid="2269156590813544104">"RECENTE"</string>
+    <string name="quick_settings_notifications_label" msgid="4818156442169154523">"Meldingen"</string>
+    <string name="recents_empty_message" msgid="7883614615463619450">"Geen recente apps"</string>
     <string name="recents_app_info_button_label" msgid="2890317189376000030">"App-informatie"</string>
     <string name="recents_search_bar_label" msgid="8074997400187836677">"zoeken"</string>
     <string name="ssl_ca_cert_warning" msgid="9005954106902053641">"Netwerk kan\nworden gecontroleerd"</string>
@@ -226,4 +232,13 @@
     <string name="speed_bump_explanation" msgid="1288875699658819755">"Minder urgente meldingen onderaan"</string>
     <string name="notification_tap_again" msgid="7590196980943943842">"Tik nogmaals om te openen"</string>
     <string name="keyguard_unlock" msgid="8043466894212841998">"Veeg omhoog om te ontgrendelen"</string>
+    <string name="zen_mode_forever" msgid="7420011936770086993">"Totdat u dit uitschakelt"</string>
+  <plurals name="zen_mode_duration_minutes">
+    <item quantity="one" msgid="9040808414992812341">"Eén minuut"</item>
+    <item quantity="other" msgid="6924190729213550991">"%d minuten"</item>
+  </plurals>
+  <plurals name="zen_mode_duration_hours">
+    <item quantity="one" msgid="3480040795582254384">"Eén uur"</item>
+    <item quantity="other" msgid="5408537517529822157">"%d uur"</item>
+  </plurals>
 </resources>
diff --git a/packages/SystemUI/res/values-pl/strings.xml b/packages/SystemUI/res/values-pl/strings.xml
index aec7e87..25098d8 100644
--- a/packages/SystemUI/res/values-pl/strings.xml
+++ b/packages/SystemUI/res/values-pl/strings.xml
@@ -120,6 +120,8 @@
     <string name="accessibility_desc_on" msgid="2385254693624345265">"Wł."</string>
     <string name="accessibility_desc_off" msgid="6475508157786853157">"Wył."</string>
     <string name="accessibility_desc_connected" msgid="8366256693719499665">"Połączono."</string>
+    <!-- no translation found for accessibility_desc_connecting (3812924520316280149) -->
+    <skip />
     <string name="accessibility_data_connection_gprs" msgid="1606477224486747751">"GPRS"</string>
     <string name="accessibility_data_connection_1x" msgid="994133468120244018">"1 X"</string>
     <string name="accessibility_data_connection_hspa" msgid="2032328855462645198">"HSPA"</string>
@@ -156,6 +158,9 @@
     <string name="accessibility_quick_settings_bluetooth" msgid="5749054971341882340">"Bluetooth: <xliff:g id="STATE">%s</xliff:g>."</string>
     <string name="accessibility_quick_settings_location" msgid="4577282329866813100">"Lokalizacja <xliff:g id="STATE">%s</xliff:g>."</string>
     <string name="accessibility_quick_settings_alarm" msgid="3959908972897295660">"Alarm ustawiony na <xliff:g id="TIME">%s</xliff:g>."</string>
+    <string name="accessibility_quick_settings_close" msgid="2571790856136835943">"Zamknij panel"</string>
+    <string name="accessibility_quick_settings_more_time" msgid="5778794273488176726">"Więcej czasu"</string>
+    <string name="accessibility_quick_settings_less_time" msgid="101026945195230084">"Mniej czasu"</string>
     <string name="data_usage_disabled_dialog_3g_title" msgid="5257833881698644687">"Wyłączono transmisję danych 2G/3G"</string>
     <string name="data_usage_disabled_dialog_4g_title" msgid="4789143363492682629">"Wyłączono transmisję danych 4G"</string>
     <string name="data_usage_disabled_dialog_mobile_title" msgid="1046047248844821202">"Transmisja danych została wyłączona"</string>
@@ -207,7 +212,8 @@
     <string name="quick_settings_more_settings" msgid="326112621462813682">"Więcej ustawień"</string>
     <string name="quick_settings_tethering_label" msgid="7153452060448575549">"Powiązanie"</string>
     <string name="quick_settings_hotspot_label" msgid="6046917934974004879">"Punkt dostępu"</string>
-    <string name="recents_empty_message" msgid="2269156590813544104">"OSTATNIE"</string>
+    <string name="quick_settings_notifications_label" msgid="4818156442169154523">"Powiadomienia"</string>
+    <string name="recents_empty_message" msgid="7883614615463619450">"Brak ostatnio uruchomionych aplikacji"</string>
     <string name="recents_app_info_button_label" msgid="2890317189376000030">"Informacje o aplikacji"</string>
     <string name="recents_search_bar_label" msgid="8074997400187836677">"szukaj"</string>
     <string name="ssl_ca_cert_warning" msgid="9005954106902053641">"Sieć może być\nmonitorowana"</string>
@@ -226,4 +232,13 @@
     <string name="speed_bump_explanation" msgid="1288875699658819755">"Poniżej widać mniej pilne powiadomienia"</string>
     <string name="notification_tap_again" msgid="7590196980943943842">"Kliknij ponownie, by otworzyć"</string>
     <string name="keyguard_unlock" msgid="8043466894212841998">"Przesuń w górę, by odblokować"</string>
+    <string name="zen_mode_forever" msgid="7420011936770086993">"Dopóki nie wyłączysz"</string>
+  <plurals name="zen_mode_duration_minutes">
+    <item quantity="one" msgid="9040808414992812341">"Przez minutę"</item>
+    <item quantity="other" msgid="6924190729213550991">"Przez %d min"</item>
+  </plurals>
+  <plurals name="zen_mode_duration_hours">
+    <item quantity="one" msgid="3480040795582254384">"Przez godzinę"</item>
+    <item quantity="other" msgid="5408537517529822157">"Przez %d godz."</item>
+  </plurals>
 </resources>
diff --git a/packages/SystemUI/res/values-pt-rPT/strings.xml b/packages/SystemUI/res/values-pt-rPT/strings.xml
index 93c7f60..34adc46 100644
--- a/packages/SystemUI/res/values-pt-rPT/strings.xml
+++ b/packages/SystemUI/res/values-pt-rPT/strings.xml
@@ -120,6 +120,8 @@
     <string name="accessibility_desc_on" msgid="2385254693624345265">"Ativado."</string>
     <string name="accessibility_desc_off" msgid="6475508157786853157">"Desativado."</string>
     <string name="accessibility_desc_connected" msgid="8366256693719499665">"Ligado."</string>
+    <!-- no translation found for accessibility_desc_connecting (3812924520316280149) -->
+    <skip />
     <string name="accessibility_data_connection_gprs" msgid="1606477224486747751">"GPRS"</string>
     <string name="accessibility_data_connection_1x" msgid="994133468120244018">"1 X"</string>
     <string name="accessibility_data_connection_hspa" msgid="2032328855462645198">"HSPA"</string>
@@ -156,6 +158,9 @@
     <string name="accessibility_quick_settings_bluetooth" msgid="5749054971341882340">"Bluetooth <xliff:g id="STATE">%s</xliff:g>."</string>
     <string name="accessibility_quick_settings_location" msgid="4577282329866813100">"Localização <xliff:g id="STATE">%s</xliff:g>."</string>
     <string name="accessibility_quick_settings_alarm" msgid="3959908972897295660">"Alarme definido para <xliff:g id="TIME">%s</xliff:g>."</string>
+    <string name="accessibility_quick_settings_close" msgid="2571790856136835943">"Fechar painel"</string>
+    <string name="accessibility_quick_settings_more_time" msgid="5778794273488176726">"Mais tempo"</string>
+    <string name="accessibility_quick_settings_less_time" msgid="101026945195230084">"Menos tempo"</string>
     <string name="data_usage_disabled_dialog_3g_title" msgid="5257833881698644687">"Os dados 2G-3G estão desativados"</string>
     <string name="data_usage_disabled_dialog_4g_title" msgid="4789143363492682629">"Os dados 4G estão desativados"</string>
     <string name="data_usage_disabled_dialog_mobile_title" msgid="1046047248844821202">"Os dados móveis estão desativados"</string>
@@ -207,7 +212,8 @@
     <string name="quick_settings_more_settings" msgid="326112621462813682">"Mais definições"</string>
     <string name="quick_settings_tethering_label" msgid="7153452060448575549">"Associação"</string>
     <string name="quick_settings_hotspot_label" msgid="6046917934974004879">"Zona Wi-Fi"</string>
-    <string name="recents_empty_message" msgid="2269156590813544104">"RECENTES"</string>
+    <string name="quick_settings_notifications_label" msgid="4818156442169154523">"Notificações"</string>
+    <string name="recents_empty_message" msgid="7883614615463619450">"Nenhuma aplicação recente"</string>
     <string name="recents_app_info_button_label" msgid="2890317189376000030">"Informações da aplicação"</string>
     <string name="recents_search_bar_label" msgid="8074997400187836677">"pesquisar"</string>
     <string name="ssl_ca_cert_warning" msgid="9005954106902053641">"A rede pode ser\nmonitorizada"</string>
@@ -226,4 +232,13 @@
     <string name="speed_bump_explanation" msgid="1288875699658819755">"Notificações menos urgentes abaixo"</string>
     <string name="notification_tap_again" msgid="7590196980943943842">"Toque novamente para abrir"</string>
     <string name="keyguard_unlock" msgid="8043466894212841998">"Deslizar rapidamente com o dedo para cima para desbloquear"</string>
+    <string name="zen_mode_forever" msgid="7420011936770086993">"Até que o utilizador desative"</string>
+  <plurals name="zen_mode_duration_minutes">
+    <item quantity="one" msgid="9040808414992812341">"Durante um minuto"</item>
+    <item quantity="other" msgid="6924190729213550991">"Durante %d minutos"</item>
+  </plurals>
+  <plurals name="zen_mode_duration_hours">
+    <item quantity="one" msgid="3480040795582254384">"Durante uma hora"</item>
+    <item quantity="other" msgid="5408537517529822157">"Durante %d horas"</item>
+  </plurals>
 </resources>
diff --git a/packages/SystemUI/res/values-pt/strings.xml b/packages/SystemUI/res/values-pt/strings.xml
index 079c8f0..e3b1f84 100644
--- a/packages/SystemUI/res/values-pt/strings.xml
+++ b/packages/SystemUI/res/values-pt/strings.xml
@@ -120,6 +120,8 @@
     <string name="accessibility_desc_on" msgid="2385254693624345265">"Ligado."</string>
     <string name="accessibility_desc_off" msgid="6475508157786853157">"Desligado."</string>
     <string name="accessibility_desc_connected" msgid="8366256693719499665">"Conectado."</string>
+    <!-- no translation found for accessibility_desc_connecting (3812924520316280149) -->
+    <skip />
     <string name="accessibility_data_connection_gprs" msgid="1606477224486747751">"GPRS"</string>
     <string name="accessibility_data_connection_1x" msgid="994133468120244018">"1 X"</string>
     <string name="accessibility_data_connection_hspa" msgid="2032328855462645198">"HSPA"</string>
@@ -158,6 +160,9 @@
     <string name="accessibility_quick_settings_bluetooth" msgid="5749054971341882340">"Bluetooth <xliff:g id="STATE">%s</xliff:g>."</string>
     <string name="accessibility_quick_settings_location" msgid="4577282329866813100">"Localização <xliff:g id="STATE">%s</xliff:g>."</string>
     <string name="accessibility_quick_settings_alarm" msgid="3959908972897295660">"Alarme definido para <xliff:g id="TIME">%s</xliff:g>."</string>
+    <string name="accessibility_quick_settings_close" msgid="2571790856136835943">"Fechar painel"</string>
+    <string name="accessibility_quick_settings_more_time" msgid="5778794273488176726">"Mais tempo"</string>
+    <string name="accessibility_quick_settings_less_time" msgid="101026945195230084">"Menos tempo"</string>
     <string name="data_usage_disabled_dialog_3g_title" msgid="5257833881698644687">"Dados 2G e 3G desativados"</string>
     <string name="data_usage_disabled_dialog_4g_title" msgid="4789143363492682629">"Dados 4G desativados"</string>
     <string name="data_usage_disabled_dialog_mobile_title" msgid="1046047248844821202">"Dados móveis desativados"</string>
@@ -204,16 +209,14 @@
     <string name="quick_settings_remote_display_no_connection_label" msgid="372107699274391290">"Transmitir tela"</string>
     <string name="quick_settings_brightness_dialog_title" msgid="8599674057673605368">"Brilho"</string>
     <string name="quick_settings_brightness_dialog_auto_brightness_label" msgid="5064982743784071218">"AUTO"</string>
-    <!-- no translation found for quick_settings_inversion_label (8790919884718619648) -->
-    <skip />
+    <string name="quick_settings_inversion_label" msgid="8790919884718619648">"Inverter cores"</string>
     <string name="quick_settings_color_space_label" msgid="853443689745584770">"Modo de correção de cor"</string>
-    <!-- no translation found for quick_settings_more_settings (326112621462813682) -->
+    <string name="quick_settings_more_settings" msgid="326112621462813682">"Mais configurações"</string>
+    <string name="quick_settings_tethering_label" msgid="7153452060448575549">"Tethering"</string>
+    <string name="quick_settings_hotspot_label" msgid="6046917934974004879">"Ponto de acesso"</string>
+    <string name="quick_settings_notifications_label" msgid="4818156442169154523">"Notificações"</string>
+    <!-- no translation found for recents_empty_message (7883614615463619450) -->
     <skip />
-    <!-- no translation found for quick_settings_tethering_label (7153452060448575549) -->
-    <skip />
-    <!-- no translation found for quick_settings_hotspot_label (6046917934974004879) -->
-    <skip />
-    <string name="recents_empty_message" msgid="2269156590813544104">"RECENTES"</string>
     <string name="recents_app_info_button_label" msgid="2890317189376000030">"Informações do aplicativo"</string>
     <string name="recents_search_bar_label" msgid="8074997400187836677">"pesquisar"</string>
     <string name="ssl_ca_cert_warning" msgid="9005954106902053641">"A rede pode estar\nsob monitoração"</string>
@@ -229,8 +232,16 @@
   <plurals name="keyguard_more_overflow_text">
     <item quantity="other" msgid="9180696159506883684">"Mais %d"</item>
   </plurals>
-    <!-- no translation found for speed_bump_explanation (1288875699658819755) -->
-    <skip />
+    <string name="speed_bump_explanation" msgid="1288875699658819755">"Notificações menos urgentes abaixo"</string>
     <string name="notification_tap_again" msgid="7590196980943943842">"Toque novamente para abrir"</string>
     <string name="keyguard_unlock" msgid="8043466894212841998">"Deslize para cima para desbloquear"</string>
+    <string name="zen_mode_forever" msgid="7420011936770086993">"Até você desativar"</string>
+  <plurals name="zen_mode_duration_minutes">
+    <item quantity="one" msgid="9040808414992812341">"Por 1 minuto"</item>
+    <item quantity="other" msgid="6924190729213550991">"Por %d minutos"</item>
+  </plurals>
+  <plurals name="zen_mode_duration_hours">
+    <item quantity="one" msgid="3480040795582254384">"Por 1 hora"</item>
+    <item quantity="other" msgid="5408537517529822157">"Por %d horas"</item>
+  </plurals>
 </resources>
diff --git a/packages/SystemUI/res/values-rm/strings.xml b/packages/SystemUI/res/values-rm/strings.xml
index b9d121a..e6a3872 100644
--- a/packages/SystemUI/res/values-rm/strings.xml
+++ b/packages/SystemUI/res/values-rm/strings.xml
@@ -210,6 +210,8 @@
     <skip />
     <!-- no translation found for accessibility_desc_connected (8366256693719499665) -->
     <skip />
+    <!-- no translation found for accessibility_desc_connecting (3812924520316280149) -->
+    <skip />
     <!-- no translation found for accessibility_data_connection_gprs (1606477224486747751) -->
     <skip />
     <!-- no translation found for accessibility_data_connection_1x (994133468120244018) -->
@@ -282,6 +284,12 @@
     <skip />
     <!-- no translation found for accessibility_quick_settings_alarm (3959908972897295660) -->
     <skip />
+    <!-- no translation found for accessibility_quick_settings_close (2571790856136835943) -->
+    <skip />
+    <!-- no translation found for accessibility_quick_settings_more_time (5778794273488176726) -->
+    <skip />
+    <!-- no translation found for accessibility_quick_settings_less_time (101026945195230084) -->
+    <skip />
     <!-- no translation found for data_usage_disabled_dialog_3g_title (5257833881698644687) -->
     <skip />
     <!-- no translation found for data_usage_disabled_dialog_4g_title (4789143363492682629) -->
@@ -384,7 +392,9 @@
     <skip />
     <!-- no translation found for quick_settings_hotspot_label (6046917934974004879) -->
     <skip />
-    <!-- no translation found for recents_empty_message (2269156590813544104) -->
+    <!-- no translation found for quick_settings_notifications_label (4818156442169154523) -->
+    <skip />
+    <!-- no translation found for recents_empty_message (7883614615463619450) -->
     <skip />
     <!-- no translation found for recents_app_info_button_label (2890317189376000030) -->
     <skip />
@@ -411,4 +421,10 @@
     <skip />
     <!-- no translation found for keyguard_unlock (8043466894212841998) -->
     <skip />
+    <!-- no translation found for zen_mode_forever (7420011936770086993) -->
+    <skip />
+    <!-- no translation found for zen_mode_duration_minutes:one (9040808414992812341) -->
+    <!-- no translation found for zen_mode_duration_minutes:other (6924190729213550991) -->
+    <!-- no translation found for zen_mode_duration_hours:one (3480040795582254384) -->
+    <!-- no translation found for zen_mode_duration_hours:other (5408537517529822157) -->
 </resources>
diff --git a/packages/SystemUI/res/values-ro/strings.xml b/packages/SystemUI/res/values-ro/strings.xml
index 10afe88..9bc9178 100644
--- a/packages/SystemUI/res/values-ro/strings.xml
+++ b/packages/SystemUI/res/values-ro/strings.xml
@@ -120,6 +120,8 @@
     <string name="accessibility_desc_on" msgid="2385254693624345265">"Activat."</string>
     <string name="accessibility_desc_off" msgid="6475508157786853157">"Dezactivat."</string>
     <string name="accessibility_desc_connected" msgid="8366256693719499665">"Conectat."</string>
+    <!-- no translation found for accessibility_desc_connecting (3812924520316280149) -->
+    <skip />
     <string name="accessibility_data_connection_gprs" msgid="1606477224486747751">"GPRS"</string>
     <string name="accessibility_data_connection_1x" msgid="994133468120244018">"1 X"</string>
     <string name="accessibility_data_connection_hspa" msgid="2032328855462645198">"HSPA"</string>
@@ -156,6 +158,9 @@
     <string name="accessibility_quick_settings_bluetooth" msgid="5749054971341882340">"Bluetooth <xliff:g id="STATE">%s</xliff:g>."</string>
     <string name="accessibility_quick_settings_location" msgid="4577282329866813100">"Locație: <xliff:g id="STATE">%s</xliff:g>."</string>
     <string name="accessibility_quick_settings_alarm" msgid="3959908972897295660">"Alarmă setată pentru <xliff:g id="TIME">%s</xliff:g>."</string>
+    <string name="accessibility_quick_settings_close" msgid="2571790856136835943">"Închideți panoul"</string>
+    <string name="accessibility_quick_settings_more_time" msgid="5778794273488176726">"Mai mult timp"</string>
+    <string name="accessibility_quick_settings_less_time" msgid="101026945195230084">"Mai puțin timp"</string>
     <string name="data_usage_disabled_dialog_3g_title" msgid="5257833881698644687">"Datele 2G-3G au fost dezactivate"</string>
     <string name="data_usage_disabled_dialog_4g_title" msgid="4789143363492682629">"Datele 4G au fost dezactivate"</string>
     <string name="data_usage_disabled_dialog_mobile_title" msgid="1046047248844821202">"Datele mobile au fost dezactivate"</string>
@@ -207,7 +212,8 @@
     <string name="quick_settings_more_settings" msgid="326112621462813682">"Mai multe setări"</string>
     <string name="quick_settings_tethering_label" msgid="7153452060448575549">"Tethering"</string>
     <string name="quick_settings_hotspot_label" msgid="6046917934974004879">"Hotspot"</string>
-    <string name="recents_empty_message" msgid="2269156590813544104">"RECENTE"</string>
+    <string name="quick_settings_notifications_label" msgid="4818156442169154523">"Notificări"</string>
+    <string name="recents_empty_message" msgid="7883614615463619450">"Nicio aplicație recentă"</string>
     <string name="recents_app_info_button_label" msgid="2890317189376000030">"Informații despre aplicație"</string>
     <string name="recents_search_bar_label" msgid="8074997400187836677">"căutare"</string>
     <string name="ssl_ca_cert_warning" msgid="9005954106902053641">"Rețeaua poate\nfi monitorizată"</string>
@@ -223,8 +229,16 @@
   <plurals name="keyguard_more_overflow_text">
     <item quantity="other" msgid="9180696159506883684">"Încă %d"</item>
   </plurals>
-    <!-- no translation found for speed_bump_explanation (1288875699658819755) -->
-    <skip />
+    <string name="speed_bump_explanation" msgid="1288875699658819755">"Notificările mai puțin urgente mai jos"</string>
     <string name="notification_tap_again" msgid="7590196980943943842">"Atingeți din nou pentru a deschide"</string>
     <string name="keyguard_unlock" msgid="8043466894212841998">"Glisați în sus pentru a debloca"</string>
+    <string name="zen_mode_forever" msgid="7420011936770086993">"Până la dezactivare"</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>
+  </plurals>
+  <plurals name="zen_mode_duration_hours">
+    <item quantity="one" msgid="3480040795582254384">"Timp de o oră"</item>
+    <item quantity="other" msgid="5408537517529822157">"Timp de %d (de) ore"</item>
+  </plurals>
 </resources>
diff --git a/packages/SystemUI/res/values-ru/strings.xml b/packages/SystemUI/res/values-ru/strings.xml
index c726014..619ebc2 100644
--- a/packages/SystemUI/res/values-ru/strings.xml
+++ b/packages/SystemUI/res/values-ru/strings.xml
@@ -120,6 +120,8 @@
     <string name="accessibility_desc_on" msgid="2385254693624345265">"Вкл."</string>
     <string name="accessibility_desc_off" msgid="6475508157786853157">"Выкл."</string>
     <string name="accessibility_desc_connected" msgid="8366256693719499665">"Подключено"</string>
+    <!-- no translation found for accessibility_desc_connecting (3812924520316280149) -->
+    <skip />
     <string name="accessibility_data_connection_gprs" msgid="1606477224486747751">"GPRS"</string>
     <string name="accessibility_data_connection_1x" msgid="994133468120244018">"1 X"</string>
     <string name="accessibility_data_connection_hspa" msgid="2032328855462645198">"HSPA"</string>
@@ -158,6 +160,9 @@
     <string name="accessibility_quick_settings_bluetooth" msgid="5749054971341882340">"Bluetooth <xliff:g id="STATE">%s</xliff:g>"</string>
     <string name="accessibility_quick_settings_location" msgid="4577282329866813100">"Доступ к геоданным <xliff:g id="STATE">%s</xliff:g>"</string>
     <string name="accessibility_quick_settings_alarm" msgid="3959908972897295660">"Будильник установлен на <xliff:g id="TIME">%s</xliff:g>"</string>
+    <string name="accessibility_quick_settings_close" msgid="2571790856136835943">"Закрыть панель."</string>
+    <string name="accessibility_quick_settings_more_time" msgid="5778794273488176726">"Увеличить продолжительность."</string>
+    <string name="accessibility_quick_settings_less_time" msgid="101026945195230084">"Уменьшить продолжительность."</string>
     <string name="data_usage_disabled_dialog_3g_title" msgid="5257833881698644687">"Передача данных по каналам 2G и 3G отключена"</string>
     <string name="data_usage_disabled_dialog_4g_title" msgid="4789143363492682629">"Передача данных по каналу 4G отключена"</string>
     <string name="data_usage_disabled_dialog_mobile_title" msgid="1046047248844821202">"Моб. Интернет отключен"</string>
@@ -209,7 +214,8 @@
     <string name="quick_settings_more_settings" msgid="326112621462813682">"Дополнительные настройки"</string>
     <string name="quick_settings_tethering_label" msgid="7153452060448575549">"Режим модема"</string>
     <string name="quick_settings_hotspot_label" msgid="6046917934974004879">"Точка доступа"</string>
-    <string name="recents_empty_message" msgid="2269156590813544104">"НЕДАВНИЕ СООБЩЕНИЯ"</string>
+    <string name="quick_settings_notifications_label" msgid="4818156442169154523">"Уведомления"</string>
+    <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="ssl_ca_cert_warning" msgid="9005954106902053641">"Действия в сети\nмогут отслеживаться"</string>
@@ -228,4 +234,13 @@
     <string name="speed_bump_explanation" msgid="1288875699658819755">"Показать менее важные оповещения"</string>
     <string name="notification_tap_again" msgid="7590196980943943842">"Нажмите ещё раз, чтобы открыть"</string>
     <string name="keyguard_unlock" msgid="8043466894212841998">"Для разблокировки проведите пальцем по экрану"</string>
+    <string name="zen_mode_forever" msgid="7420011936770086993">"Пока я не отключу"</string>
+  <plurals name="zen_mode_duration_minutes">
+    <item quantity="one" msgid="9040808414992812341">"1 мин."</item>
+    <item quantity="other" msgid="6924190729213550991">"%d мин."</item>
+  </plurals>
+  <plurals name="zen_mode_duration_hours">
+    <item quantity="one" msgid="3480040795582254384">"1 ч."</item>
+    <item quantity="other" msgid="5408537517529822157">"%d ч."</item>
+  </plurals>
 </resources>
diff --git a/packages/SystemUI/res/values-sk/strings.xml b/packages/SystemUI/res/values-sk/strings.xml
index 45bb2ad..df880cf 100644
--- a/packages/SystemUI/res/values-sk/strings.xml
+++ b/packages/SystemUI/res/values-sk/strings.xml
@@ -120,6 +120,8 @@
     <string name="accessibility_desc_on" msgid="2385254693624345265">"Zapnuté."</string>
     <string name="accessibility_desc_off" msgid="6475508157786853157">"Vypnuté."</string>
     <string name="accessibility_desc_connected" msgid="8366256693719499665">"Pripojené."</string>
+    <!-- no translation found for accessibility_desc_connecting (3812924520316280149) -->
+    <skip />
     <string name="accessibility_data_connection_gprs" msgid="1606477224486747751">"GPRS"</string>
     <string name="accessibility_data_connection_1x" msgid="994133468120244018">"1 X"</string>
     <string name="accessibility_data_connection_hspa" msgid="2032328855462645198">"HSPA"</string>
@@ -158,6 +160,9 @@
     <string name="accessibility_quick_settings_bluetooth" msgid="5749054971341882340">"Bluetooth: <xliff:g id="STATE">%s</xliff:g>."</string>
     <string name="accessibility_quick_settings_location" msgid="4577282329866813100">"Poloha: <xliff:g id="STATE">%s</xliff:g>."</string>
     <string name="accessibility_quick_settings_alarm" msgid="3959908972897295660">"Budík nastavený na <xliff:g id="TIME">%s</xliff:g>."</string>
+    <string name="accessibility_quick_settings_close" msgid="2571790856136835943">"Zavrieť panel"</string>
+    <string name="accessibility_quick_settings_more_time" msgid="5778794273488176726">"Dlhší čas"</string>
+    <string name="accessibility_quick_settings_less_time" msgid="101026945195230084">"Kratší čas"</string>
     <string name="data_usage_disabled_dialog_3g_title" msgid="5257833881698644687">"Dátové prenosy 2G a 3G sú zakázané"</string>
     <string name="data_usage_disabled_dialog_4g_title" msgid="4789143363492682629">"Dátové prenosy 4G sú zakázané"</string>
     <string name="data_usage_disabled_dialog_mobile_title" msgid="1046047248844821202">"Mobilné dátové prenosy sú zakázané"</string>
@@ -209,7 +214,8 @@
     <string name="quick_settings_more_settings" msgid="326112621462813682">"Ďalšie nastavenia"</string>
     <string name="quick_settings_tethering_label" msgid="7153452060448575549">"Zdieľanie dátového pripojenia"</string>
     <string name="quick_settings_hotspot_label" msgid="6046917934974004879">"Hotspot"</string>
-    <string name="recents_empty_message" msgid="2269156590813544104">"NEDÁVNE"</string>
+    <string name="quick_settings_notifications_label" msgid="4818156442169154523">"Upozornenia"</string>
+    <string name="recents_empty_message" msgid="7883614615463619450">"Žiadne nedávne aplikácie"</string>
     <string name="recents_app_info_button_label" msgid="2890317189376000030">"Informácie o aplikácii"</string>
     <string name="recents_search_bar_label" msgid="8074997400187836677">"hľadať"</string>
     <string name="ssl_ca_cert_warning" msgid="9005954106902053641">"Sieť môže byť\nmonitorovaná"</string>
@@ -228,4 +234,13 @@
     <string name="speed_bump_explanation" msgid="1288875699658819755">"Menej naliehavé upozornenia sa nachádzajú nižšie"</string>
     <string name="notification_tap_again" msgid="7590196980943943842">"Upozornenie otvoríte opätovným klepnutím"</string>
     <string name="keyguard_unlock" msgid="8043466894212841998">"Zariadenie odomknete prejdením prstom nahor"</string>
+    <string name="zen_mode_forever" msgid="7420011936770086993">"Dokým túto funkciu nevypnete"</string>
+  <plurals name="zen_mode_duration_minutes">
+    <item quantity="one" msgid="9040808414992812341">"Na jednu minútu"</item>
+    <item quantity="other" msgid="6924190729213550991">"Na %d min"</item>
+  </plurals>
+  <plurals name="zen_mode_duration_hours">
+    <item quantity="one" msgid="3480040795582254384">"Na jednu hodinu"</item>
+    <item quantity="other" msgid="5408537517529822157">"Na %d h"</item>
+  </plurals>
 </resources>
diff --git a/packages/SystemUI/res/values-sl/strings.xml b/packages/SystemUI/res/values-sl/strings.xml
index 99470d6..d1170bd 100644
--- a/packages/SystemUI/res/values-sl/strings.xml
+++ b/packages/SystemUI/res/values-sl/strings.xml
@@ -120,6 +120,8 @@
     <string name="accessibility_desc_on" msgid="2385254693624345265">"Vklopljen."</string>
     <string name="accessibility_desc_off" msgid="6475508157786853157">"Izklopljen."</string>
     <string name="accessibility_desc_connected" msgid="8366256693719499665">"Povezan."</string>
+    <!-- no translation found for accessibility_desc_connecting (3812924520316280149) -->
+    <skip />
     <string name="accessibility_data_connection_gprs" msgid="1606477224486747751">"GPRS"</string>
     <string name="accessibility_data_connection_1x" msgid="994133468120244018">"1 X"</string>
     <string name="accessibility_data_connection_hspa" msgid="2032328855462645198">"HSPA"</string>
@@ -156,6 +158,9 @@
     <string name="accessibility_quick_settings_bluetooth" msgid="5749054971341882340">"Bluetooth: <xliff:g id="STATE">%s</xliff:g>."</string>
     <string name="accessibility_quick_settings_location" msgid="4577282329866813100">"Lokacija: <xliff:g id="STATE">%s</xliff:g>."</string>
     <string name="accessibility_quick_settings_alarm" msgid="3959908972897295660">"Alarm je nastavljen na <xliff:g id="TIME">%s</xliff:g>."</string>
+    <string name="accessibility_quick_settings_close" msgid="2571790856136835943">"Zapri podokno"</string>
+    <string name="accessibility_quick_settings_more_time" msgid="5778794273488176726">"Daljši čas"</string>
+    <string name="accessibility_quick_settings_less_time" msgid="101026945195230084">"Krajši čas"</string>
     <string name="data_usage_disabled_dialog_3g_title" msgid="5257833881698644687">"Podatki 2G-3G so onemogočeni"</string>
     <string name="data_usage_disabled_dialog_4g_title" msgid="4789143363492682629">"Podatki 4G so onemogočeni"</string>
     <string name="data_usage_disabled_dialog_mobile_title" msgid="1046047248844821202">"Mobilni podatki so onemogočeni"</string>
@@ -207,7 +212,8 @@
     <string name="quick_settings_more_settings" msgid="326112621462813682">"Več nastavitev"</string>
     <string name="quick_settings_tethering_label" msgid="7153452060448575549">"Internet prek mobilne naprave"</string>
     <string name="quick_settings_hotspot_label" msgid="6046917934974004879">"Dostopna točka"</string>
-    <string name="recents_empty_message" msgid="2269156590813544104">"NEDAVNI"</string>
+    <string name="quick_settings_notifications_label" msgid="4818156442169154523">"Obvestila"</string>
+    <string name="recents_empty_message" msgid="7883614615463619450">"Ni nedavnih aplikacij"</string>
     <string name="recents_app_info_button_label" msgid="2890317189376000030">"Podatki o aplikaciji"</string>
     <string name="recents_search_bar_label" msgid="8074997400187836677">"iskanje"</string>
     <string name="ssl_ca_cert_warning" msgid="9005954106902053641">"Omrežje je\nlahko spremljano"</string>
@@ -226,4 +232,13 @@
     <string name="speed_bump_explanation" msgid="1288875699658819755">"Manj nujna obvestila spodaj"</string>
     <string name="notification_tap_again" msgid="7590196980943943842">"Znova se dotaknite, da odprete"</string>
     <string name="keyguard_unlock" msgid="8043466894212841998">"Povlecite, da odklenete"</string>
+    <string name="zen_mode_forever" msgid="7420011936770086993">"Dokler tega ne izklopite"</string>
+  <plurals name="zen_mode_duration_minutes">
+    <item quantity="one" msgid="9040808414992812341">"Za eno minuto"</item>
+    <item quantity="other" msgid="6924190729213550991">"Za %d min"</item>
+  </plurals>
+  <plurals name="zen_mode_duration_hours">
+    <item quantity="one" msgid="3480040795582254384">"Za eno uro"</item>
+    <item quantity="other" msgid="5408537517529822157">"Za %d h"</item>
+  </plurals>
 </resources>
diff --git a/packages/SystemUI/res/values-sr/strings.xml b/packages/SystemUI/res/values-sr/strings.xml
index 1e40ed6..cfb6a2c 100644
--- a/packages/SystemUI/res/values-sr/strings.xml
+++ b/packages/SystemUI/res/values-sr/strings.xml
@@ -120,6 +120,8 @@
     <string name="accessibility_desc_on" msgid="2385254693624345265">"Укључено."</string>
     <string name="accessibility_desc_off" msgid="6475508157786853157">"Искључено."</string>
     <string name="accessibility_desc_connected" msgid="8366256693719499665">"Повезано је."</string>
+    <!-- no translation found for accessibility_desc_connecting (3812924520316280149) -->
+    <skip />
     <string name="accessibility_data_connection_gprs" msgid="1606477224486747751">"GPRS"</string>
     <string name="accessibility_data_connection_1x" msgid="994133468120244018">"1 X"</string>
     <string name="accessibility_data_connection_hspa" msgid="2032328855462645198">"HSPA"</string>
@@ -156,6 +158,9 @@
     <string name="accessibility_quick_settings_bluetooth" msgid="5749054971341882340">"Bluetooth: <xliff:g id="STATE">%s</xliff:g>."</string>
     <string name="accessibility_quick_settings_location" msgid="4577282329866813100">"Локација је <xliff:g id="STATE">%s</xliff:g>."</string>
     <string name="accessibility_quick_settings_alarm" msgid="3959908972897295660">"Аларм је подешен за <xliff:g id="TIME">%s</xliff:g>."</string>
+    <string name="accessibility_quick_settings_close" msgid="2571790856136835943">"Затворите таблу"</string>
+    <string name="accessibility_quick_settings_more_time" msgid="5778794273488176726">"Више времена"</string>
+    <string name="accessibility_quick_settings_less_time" msgid="101026945195230084">"Мање времена"</string>
     <string name="data_usage_disabled_dialog_3g_title" msgid="5257833881698644687">"2G–3G подаци су онемогућени"</string>
     <string name="data_usage_disabled_dialog_4g_title" msgid="4789143363492682629">"4G подаци су онемогућени"</string>
     <string name="data_usage_disabled_dialog_mobile_title" msgid="1046047248844821202">"Подаци мобилне мреже су онемогућени"</string>
@@ -207,7 +212,8 @@
     <string name="quick_settings_more_settings" msgid="326112621462813682">"Још подешавања"</string>
     <string name="quick_settings_tethering_label" msgid="7153452060448575549">"Повезивање"</string>
     <string name="quick_settings_hotspot_label" msgid="6046917934974004879">"Хотспот"</string>
-    <string name="recents_empty_message" msgid="2269156590813544104">"НАЈНОВИЈЕ"</string>
+    <string name="quick_settings_notifications_label" msgid="4818156442169154523">"Обавештења"</string>
+    <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="ssl_ca_cert_warning" msgid="9005954106902053641">"Мрежа се можда\nнадгледа"</string>
@@ -226,4 +232,13 @@
     <string name="speed_bump_explanation" msgid="1288875699658819755">"Мање хитна обавештења су у наставку"</string>
     <string name="notification_tap_again" msgid="7590196980943943842">"Додирните поново да бисте отворили"</string>
     <string name="keyguard_unlock" msgid="8043466894212841998">"Превуците нагоре да бисте откључали"</string>
+    <string name="zen_mode_forever" msgid="7420011936770086993">"Док не искључите"</string>
+  <plurals name="zen_mode_duration_minutes">
+    <item quantity="one" msgid="9040808414992812341">"Један минут"</item>
+    <item quantity="other" msgid="6924190729213550991">"%d мин"</item>
+  </plurals>
+  <plurals name="zen_mode_duration_hours">
+    <item quantity="one" msgid="3480040795582254384">"Један сат"</item>
+    <item quantity="other" msgid="5408537517529822157">"%d с"</item>
+  </plurals>
 </resources>
diff --git a/packages/SystemUI/res/values-sv/strings.xml b/packages/SystemUI/res/values-sv/strings.xml
index e4d7471..4e3d72f 100644
--- a/packages/SystemUI/res/values-sv/strings.xml
+++ b/packages/SystemUI/res/values-sv/strings.xml
@@ -120,6 +120,8 @@
     <string name="accessibility_desc_on" msgid="2385254693624345265">"Aktiverad."</string>
     <string name="accessibility_desc_off" msgid="6475508157786853157">"Inaktiverad."</string>
     <string name="accessibility_desc_connected" msgid="8366256693719499665">"Ansluten."</string>
+    <!-- no translation found for accessibility_desc_connecting (3812924520316280149) -->
+    <skip />
     <string name="accessibility_data_connection_gprs" msgid="1606477224486747751">"GPRS"</string>
     <string name="accessibility_data_connection_1x" msgid="994133468120244018">"1 X"</string>
     <string name="accessibility_data_connection_hspa" msgid="2032328855462645198">"HSPA"</string>
@@ -156,6 +158,9 @@
     <string name="accessibility_quick_settings_bluetooth" msgid="5749054971341882340">"Bluetooth <xliff:g id="STATE">%s</xliff:g>."</string>
     <string name="accessibility_quick_settings_location" msgid="4577282329866813100">"Plats <xliff:g id="STATE">%s</xliff:g>."</string>
     <string name="accessibility_quick_settings_alarm" msgid="3959908972897295660">"Alarmet ringer <xliff:g id="TIME">%s</xliff:g>."</string>
+    <string name="accessibility_quick_settings_close" msgid="2571790856136835943">"Stäng panelen"</string>
+    <string name="accessibility_quick_settings_more_time" msgid="5778794273488176726">"Längre tid"</string>
+    <string name="accessibility_quick_settings_less_time" msgid="101026945195230084">"Kortare tid"</string>
     <string name="data_usage_disabled_dialog_3g_title" msgid="5257833881698644687">"Data via 2G-3G har inaktiverats"</string>
     <string name="data_usage_disabled_dialog_4g_title" msgid="4789143363492682629">"Data via 4G har inaktiverats"</string>
     <string name="data_usage_disabled_dialog_mobile_title" msgid="1046047248844821202">"Mobildata har inaktiverats"</string>
@@ -207,7 +212,9 @@
     <string name="quick_settings_more_settings" msgid="326112621462813682">"Fler inställningar"</string>
     <string name="quick_settings_tethering_label" msgid="7153452060448575549">"Internetdelning"</string>
     <string name="quick_settings_hotspot_label" msgid="6046917934974004879">"Trådlös surfzon"</string>
-    <string name="recents_empty_message" msgid="2269156590813544104">"NYA"</string>
+    <string name="quick_settings_notifications_label" msgid="4818156442169154523">"Aviseringar"</string>
+    <!-- no translation found for recents_empty_message (7883614615463619450) -->
+    <skip />
     <string name="recents_app_info_button_label" msgid="2890317189376000030">"Appinformation"</string>
     <string name="recents_search_bar_label" msgid="8074997400187836677">"sök"</string>
     <string name="ssl_ca_cert_warning" msgid="9005954106902053641">"Nätverket kan\nvara övervakat"</string>
@@ -226,4 +233,13 @@
     <string name="speed_bump_explanation" msgid="1288875699658819755">"Mindre brådskande aviseringar nedan"</string>
     <string name="notification_tap_again" msgid="7590196980943943842">"Tryck igen för att öppna"</string>
     <string name="keyguard_unlock" msgid="8043466894212841998">"Dra uppåt om du vill låsa upp"</string>
+    <string name="zen_mode_forever" msgid="7420011936770086993">"Tills du inaktiverar detta"</string>
+  <plurals name="zen_mode_duration_minutes">
+    <item quantity="one" msgid="9040808414992812341">"I en minut"</item>
+    <item quantity="other" msgid="6924190729213550991">"I %d minuter"</item>
+  </plurals>
+  <plurals name="zen_mode_duration_hours">
+    <item quantity="one" msgid="3480040795582254384">"I en timme"</item>
+    <item quantity="other" msgid="5408537517529822157">"I %d timmar"</item>
+  </plurals>
 </resources>
diff --git a/packages/SystemUI/res/values-sw/strings.xml b/packages/SystemUI/res/values-sw/strings.xml
index 1d27854..9bab46e 100644
--- a/packages/SystemUI/res/values-sw/strings.xml
+++ b/packages/SystemUI/res/values-sw/strings.xml
@@ -118,6 +118,8 @@
     <string name="accessibility_desc_on" msgid="2385254693624345265">"Imewashwa."</string>
     <string name="accessibility_desc_off" msgid="6475508157786853157">"Imezimwa."</string>
     <string name="accessibility_desc_connected" msgid="8366256693719499665">"Imeunganishwa."</string>
+    <!-- no translation found for accessibility_desc_connecting (3812924520316280149) -->
+    <skip />
     <string name="accessibility_data_connection_gprs" msgid="1606477224486747751">"GPRS"</string>
     <string name="accessibility_data_connection_1x" msgid="994133468120244018">"1 X"</string>
     <string name="accessibility_data_connection_hspa" msgid="2032328855462645198">"HSPA"</string>
@@ -154,6 +156,9 @@
     <string name="accessibility_quick_settings_bluetooth" msgid="5749054971341882340">"Bluetooth <xliff:g id="STATE">%s</xliff:g>."</string>
     <string name="accessibility_quick_settings_location" msgid="4577282329866813100">"Mahali <xliff:g id="STATE">%s</xliff:g>."</string>
     <string name="accessibility_quick_settings_alarm" msgid="3959908972897295660">"Kengele imewekwa <xliff:g id="TIME">%s</xliff:g>."</string>
+    <string name="accessibility_quick_settings_close" msgid="2571790856136835943">"Funga paneli ya maelezo"</string>
+    <string name="accessibility_quick_settings_more_time" msgid="5778794273488176726">"Muda zaidi"</string>
+    <string name="accessibility_quick_settings_less_time" msgid="101026945195230084">"Muda kidogo"</string>
     <string name="data_usage_disabled_dialog_3g_title" msgid="5257833881698644687">"Data ya 2G-3G imelemazwa"</string>
     <string name="data_usage_disabled_dialog_4g_title" msgid="4789143363492682629">"Data ya 4G imelemazwa"</string>
     <string name="data_usage_disabled_dialog_mobile_title" msgid="1046047248844821202">"Data ya kifaa cha mkononi imelemazwa"</string>
@@ -205,7 +210,8 @@
     <string name="quick_settings_more_settings" msgid="326112621462813682">"Mipangilio zaidi"</string>
     <string name="quick_settings_tethering_label" msgid="7153452060448575549">"Kusambaza mtandao"</string>
     <string name="quick_settings_hotspot_label" msgid="6046917934974004879">"Mtandao-hewa"</string>
-    <string name="recents_empty_message" msgid="2269156590813544104">"YA HIVI KARIBUNI"</string>
+    <string name="quick_settings_notifications_label" msgid="4818156442169154523">"Arifa"</string>
+    <string name="recents_empty_message" msgid="7883614615463619450">"Hakuna programu za karibuni"</string>
     <string name="recents_app_info_button_label" msgid="2890317189376000030">"Maelezo ya Programu"</string>
     <string name="recents_search_bar_label" msgid="8074997400187836677">"tafuta"</string>
     <string name="ssl_ca_cert_warning" msgid="9005954106902053641">"Huenda mtandao\nunafuatiliwa"</string>
@@ -221,7 +227,16 @@
   <plurals name="keyguard_more_overflow_text">
     <item quantity="other" msgid="9180696159506883684">"%d zaidi"</item>
   </plurals>
-    <string name="speed_bump_explanation" msgid="1288875699658819755">"Arifa zisizokuwa za dharura sana hapo chini"</string>
+    <string name="speed_bump_explanation" msgid="1288875699658819755">"Arifa zisizo za dharura sana ziko hapo chini"</string>
     <string name="notification_tap_again" msgid="7590196980943943842">"Gonga tena ili ufungue"</string>
     <string name="keyguard_unlock" msgid="8043466894212841998">"Telezesha kidole ili ufungue"</string>
+    <string name="zen_mode_forever" msgid="7420011936770086993">"Hadi utakapozima hili"</string>
+  <plurals name="zen_mode_duration_minutes">
+    <item quantity="one" msgid="9040808414992812341">"Kwa dakika moja"</item>
+    <item quantity="other" msgid="6924190729213550991">"Kwa dakika %d"</item>
+  </plurals>
+  <plurals name="zen_mode_duration_hours">
+    <item quantity="one" msgid="3480040795582254384">"Kwa saa moja"</item>
+    <item quantity="other" msgid="5408537517529822157">"Kwa saa %d"</item>
+  </plurals>
 </resources>
diff --git a/packages/SystemUI/res/values-sw600dp-land/dimens.xml b/packages/SystemUI/res/values-sw600dp-land/dimens.xml
index b510fef..326f602 100644
--- a/packages/SystemUI/res/values-sw600dp-land/dimens.xml
+++ b/packages/SystemUI/res/values-sw600dp-land/dimens.xml
@@ -19,6 +19,9 @@
     <!-- Recent Applications parameters -->
     <dimen name="status_bar_recents_app_label_width">190dip</dimen>
 
+    <!-- The side padding for the task stack as a percentage of the width. -->
+    <item name="recents_stack_width_padding_percentage" format="float" type="dimen">0.25</item>
+
     <fraction name="keyguard_clock_y_fraction_max">37%</fraction>
     <fraction name="keyguard_clock_y_fraction_min">14%</fraction>
 </resources>
diff --git a/packages/SystemUI/res/values-sw600dp/dimens.xml b/packages/SystemUI/res/values-sw600dp/dimens.xml
index 5750faa..313e2e8 100644
--- a/packages/SystemUI/res/values-sw600dp/dimens.xml
+++ b/packages/SystemUI/res/values-sw600dp/dimens.xml
@@ -46,6 +46,9 @@
     <!-- On tablets this is just the close_handle_height -->
     <dimen name="peek_height">@dimen/close_handle_height</dimen>
 
+    <!-- The side padding for the task stack as a percentage of the width. -->
+    <item name="recents_stack_width_padding_percentage" format="float" type="dimen">0.075</item>
+
     <!-- Width of the zen mode interstitial dialog. -->
     <dimen name="zen_mode_dialog_width">384dp</dimen>
 
diff --git a/packages/SystemUI/res/values-th/strings.xml b/packages/SystemUI/res/values-th/strings.xml
index 306416b..4820071 100644
--- a/packages/SystemUI/res/values-th/strings.xml
+++ b/packages/SystemUI/res/values-th/strings.xml
@@ -120,6 +120,8 @@
     <string name="accessibility_desc_on" msgid="2385254693624345265">"เปิดอยู่"</string>
     <string name="accessibility_desc_off" msgid="6475508157786853157">"ปิดอยู่"</string>
     <string name="accessibility_desc_connected" msgid="8366256693719499665">"เชื่อมต่อแล้ว"</string>
+    <!-- no translation found for accessibility_desc_connecting (3812924520316280149) -->
+    <skip />
     <string name="accessibility_data_connection_gprs" msgid="1606477224486747751">"GPRS"</string>
     <string name="accessibility_data_connection_1x" msgid="994133468120244018">"1 X"</string>
     <string name="accessibility_data_connection_hspa" msgid="2032328855462645198">"HSPA"</string>
@@ -156,6 +158,9 @@
     <string name="accessibility_quick_settings_bluetooth" msgid="5749054971341882340">"บลูทูธ <xliff:g id="STATE">%s</xliff:g>"</string>
     <string name="accessibility_quick_settings_location" msgid="4577282329866813100">"สถานที่ <xliff:g id="STATE">%s</xliff:g>"</string>
     <string name="accessibility_quick_settings_alarm" msgid="3959908972897295660">"ตั้งเวลาปลุกไว้ที่ <xliff:g id="TIME">%s</xliff:g>"</string>
+    <string name="accessibility_quick_settings_close" msgid="2571790856136835943">"ปิดแผงควบคุม"</string>
+    <string name="accessibility_quick_settings_more_time" msgid="5778794273488176726">"เวลามากขึ้น"</string>
+    <string name="accessibility_quick_settings_less_time" msgid="101026945195230084">"เวลาน้อยลง"</string>
     <string name="data_usage_disabled_dialog_3g_title" msgid="5257833881698644687">"ปิดใช้งานข้อมูล 2G-3G แล้ว"</string>
     <string name="data_usage_disabled_dialog_4g_title" msgid="4789143363492682629">"ปิดใช้งานข้อมูล 4G แล้ว"</string>
     <string name="data_usage_disabled_dialog_mobile_title" msgid="1046047248844821202">"ปิดใช้งานข้อมูลมือถือแล้ว"</string>
@@ -207,7 +212,8 @@
     <string name="quick_settings_more_settings" msgid="326112621462813682">"การตั้งค่าเพิ่มเติม"</string>
     <string name="quick_settings_tethering_label" msgid="7153452060448575549">"การปล่อยสัญญาณ"</string>
     <string name="quick_settings_hotspot_label" msgid="6046917934974004879">"ฮอตสปอต"</string>
-    <string name="recents_empty_message" msgid="2269156590813544104">"ล่าสุด"</string>
+    <string name="quick_settings_notifications_label" msgid="4818156442169154523">"การแจ้งเตือน"</string>
+    <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="ssl_ca_cert_warning" msgid="9005954106902053641">"เครือข่ายอาจ\nถูกตรวจสอบ"</string>
@@ -223,7 +229,16 @@
   <plurals name="keyguard_more_overflow_text">
     <item quantity="other" msgid="9180696159506883684">"อีก %d"</item>
   </plurals>
-    <string name="speed_bump_explanation" msgid="1288875699658819755">"การแจ้งเตือนที่เร่งด่วนน้อยทางด้านล่าง"</string>
+    <string name="speed_bump_explanation" msgid="1288875699658819755">"การแจ้งเตือนที่เร่งด่วนน้อยด้านล่าง"</string>
     <string name="notification_tap_again" msgid="7590196980943943842">"แตะอีกครั้งเพื่อเปิด"</string>
     <string name="keyguard_unlock" msgid="8043466894212841998">"กวาดขึ้นเพื่อปลดล็อก"</string>
+    <string name="zen_mode_forever" msgid="7420011936770086993">"จนกว่าคุณจะปิดฟังก์ชันนี้"</string>
+  <plurals name="zen_mode_duration_minutes">
+    <item quantity="one" msgid="9040808414992812341">"1 นาที"</item>
+    <item quantity="other" msgid="6924190729213550991">"%d นาที"</item>
+  </plurals>
+  <plurals name="zen_mode_duration_hours">
+    <item quantity="one" msgid="3480040795582254384">"1 ชั่วโมง"</item>
+    <item quantity="other" msgid="5408537517529822157">"%d ชั่วโมง"</item>
+  </plurals>
 </resources>
diff --git a/packages/SystemUI/res/values-tl/strings.xml b/packages/SystemUI/res/values-tl/strings.xml
index debef15..6c6af51 100644
--- a/packages/SystemUI/res/values-tl/strings.xml
+++ b/packages/SystemUI/res/values-tl/strings.xml
@@ -120,6 +120,8 @@
     <string name="accessibility_desc_on" msgid="2385254693624345265">"Naka-on."</string>
     <string name="accessibility_desc_off" msgid="6475508157786853157">"Naka-off."</string>
     <string name="accessibility_desc_connected" msgid="8366256693719499665">"Nakakonekta."</string>
+    <!-- no translation found for accessibility_desc_connecting (3812924520316280149) -->
+    <skip />
     <string name="accessibility_data_connection_gprs" msgid="1606477224486747751">"GPRS"</string>
     <string name="accessibility_data_connection_1x" msgid="994133468120244018">"1 X"</string>
     <string name="accessibility_data_connection_hspa" msgid="2032328855462645198">"HSPA"</string>
@@ -156,6 +158,9 @@
     <string name="accessibility_quick_settings_bluetooth" msgid="5749054971341882340">"<xliff:g id="STATE">%s</xliff:g> ng Bluetooth."</string>
     <string name="accessibility_quick_settings_location" msgid="4577282329866813100">"Lokasyon <xliff:g id="STATE">%s</xliff:g>."</string>
     <string name="accessibility_quick_settings_alarm" msgid="3959908972897295660">"Alarm set para sa <xliff:g id="TIME">%s</xliff:g>."</string>
+    <string name="accessibility_quick_settings_close" msgid="2571790856136835943">"Isara ang panel"</string>
+    <string name="accessibility_quick_settings_more_time" msgid="5778794273488176726">"Higit pang oras"</string>
+    <string name="accessibility_quick_settings_less_time" msgid="101026945195230084">"Mas kaunting oras"</string>
     <string name="data_usage_disabled_dialog_3g_title" msgid="5257833881698644687">"Di pinapagana ang 2G-3G na data"</string>
     <string name="data_usage_disabled_dialog_4g_title" msgid="4789143363492682629">"Hindi pinapagana ang 4G na data"</string>
     <string name="data_usage_disabled_dialog_mobile_title" msgid="1046047248844821202">"Hindi pinapagana ang data ng mobile"</string>
@@ -202,16 +207,13 @@
     <string name="quick_settings_remote_display_no_connection_label" msgid="372107699274391290">"I-cast ang Screen"</string>
     <string name="quick_settings_brightness_dialog_title" msgid="8599674057673605368">"Brightness"</string>
     <string name="quick_settings_brightness_dialog_auto_brightness_label" msgid="5064982743784071218">"AUTO"</string>
-    <!-- no translation found for quick_settings_inversion_label (8790919884718619648) -->
-    <skip />
+    <string name="quick_settings_inversion_label" msgid="8790919884718619648">"I-invert ang mga kulay"</string>
     <string name="quick_settings_color_space_label" msgid="853443689745584770">"Mode ng pagtatama ng kulay"</string>
-    <!-- no translation found for quick_settings_more_settings (326112621462813682) -->
-    <skip />
-    <!-- no translation found for quick_settings_tethering_label (7153452060448575549) -->
-    <skip />
-    <!-- no translation found for quick_settings_hotspot_label (6046917934974004879) -->
-    <skip />
-    <string name="recents_empty_message" msgid="2269156590813544104">"MGA KAMAKAILAN"</string>
+    <string name="quick_settings_more_settings" msgid="326112621462813682">"Marami pang setting"</string>
+    <string name="quick_settings_tethering_label" msgid="7153452060448575549">"Nagte-tether"</string>
+    <string name="quick_settings_hotspot_label" msgid="6046917934974004879">"Hotspot"</string>
+    <string name="quick_settings_notifications_label" msgid="4818156442169154523">"Mga Notification"</string>
+    <string name="recents_empty_message" msgid="7883614615463619450">"Walang kamakailang mga app"</string>
     <string name="recents_app_info_button_label" msgid="2890317189376000030">"Impormasyon ng Application"</string>
     <string name="recents_search_bar_label" msgid="8074997400187836677">"maghanap"</string>
     <string name="ssl_ca_cert_warning" msgid="9005954106902053641">"Maaaring\nsinusubaybayan ang network"</string>
@@ -227,8 +229,16 @@
   <plurals name="keyguard_more_overflow_text">
     <item quantity="other" msgid="9180696159506883684">"%d pa"</item>
   </plurals>
-    <!-- no translation found for speed_bump_explanation (1288875699658819755) -->
-    <skip />
+    <string name="speed_bump_explanation" msgid="1288875699658819755">"Nasa ibaba ang mga notification na hindi masyadong mahalaga"</string>
     <string name="notification_tap_again" msgid="7590196980943943842">"I-tap ulit upang buksan"</string>
     <string name="keyguard_unlock" msgid="8043466894212841998">"Mag-swipe pataas upang i-unlock"</string>
+    <string name="zen_mode_forever" msgid="7420011936770086993">"Hanggang sa i-off mo ito"</string>
+  <plurals name="zen_mode_duration_minutes">
+    <item quantity="one" msgid="9040808414992812341">"Sa loob ng isang minuto"</item>
+    <item quantity="other" msgid="6924190729213550991">"Sa loob ng %d (na) minuto"</item>
+  </plurals>
+  <plurals name="zen_mode_duration_hours">
+    <item quantity="one" msgid="3480040795582254384">"Sa loob ng isang oras"</item>
+    <item quantity="other" msgid="5408537517529822157">"Sa loob ng %d (na) oras"</item>
+  </plurals>
 </resources>
diff --git a/packages/SystemUI/res/values-tr/strings.xml b/packages/SystemUI/res/values-tr/strings.xml
index 506d27e..85ba8fe 100644
--- a/packages/SystemUI/res/values-tr/strings.xml
+++ b/packages/SystemUI/res/values-tr/strings.xml
@@ -120,6 +120,8 @@
     <string name="accessibility_desc_on" msgid="2385254693624345265">"Açık."</string>
     <string name="accessibility_desc_off" msgid="6475508157786853157">"Kapalı."</string>
     <string name="accessibility_desc_connected" msgid="8366256693719499665">"Bağlandı."</string>
+    <!-- no translation found for accessibility_desc_connecting (3812924520316280149) -->
+    <skip />
     <string name="accessibility_data_connection_gprs" msgid="1606477224486747751">"GPRS"</string>
     <string name="accessibility_data_connection_1x" msgid="994133468120244018">"1 X"</string>
     <string name="accessibility_data_connection_hspa" msgid="2032328855462645198">"HSPA"</string>
@@ -156,6 +158,9 @@
     <string name="accessibility_quick_settings_bluetooth" msgid="5749054971341882340">"Bluetooth <xliff:g id="STATE">%s</xliff:g>."</string>
     <string name="accessibility_quick_settings_location" msgid="4577282329866813100">"Konum: <xliff:g id="STATE">%s</xliff:g>."</string>
     <string name="accessibility_quick_settings_alarm" msgid="3959908972897295660">"Alarm saati: <xliff:g id="TIME">%s</xliff:g>."</string>
+    <string name="accessibility_quick_settings_close" msgid="2571790856136835943">"Paneli kapatın"</string>
+    <string name="accessibility_quick_settings_more_time" msgid="5778794273488176726">"Daha uzun süre"</string>
+    <string name="accessibility_quick_settings_less_time" msgid="101026945195230084">"Daha kısa süre"</string>
     <string name="data_usage_disabled_dialog_3g_title" msgid="5257833881698644687">"2G-3G verileri devre dışı bırakıldı"</string>
     <string name="data_usage_disabled_dialog_4g_title" msgid="4789143363492682629">"4G verileri devre dışı"</string>
     <string name="data_usage_disabled_dialog_mobile_title" msgid="1046047248844821202">"Mobil veriler devre dışı"</string>
@@ -207,7 +212,9 @@
     <string name="quick_settings_more_settings" msgid="326112621462813682">"Diğer ayarlar"</string>
     <string name="quick_settings_tethering_label" msgid="7153452060448575549">"Tethering"</string>
     <string name="quick_settings_hotspot_label" msgid="6046917934974004879">"Hotspot"</string>
-    <string name="recents_empty_message" msgid="2269156590813544104">"SON İLETİLER"</string>
+    <string name="quick_settings_notifications_label" msgid="4818156442169154523">"Bildirimler"</string>
+    <!-- no translation found for recents_empty_message (7883614615463619450) -->
+    <skip />
     <string name="recents_app_info_button_label" msgid="2890317189376000030">"Uygulama Bilgileri"</string>
     <string name="recents_search_bar_label" msgid="8074997400187836677">"ara"</string>
     <string name="ssl_ca_cert_warning" msgid="9005954106902053641">"Ağ izleniyor\nolabilir"</string>
@@ -226,4 +233,13 @@
     <string name="speed_bump_explanation" msgid="1288875699658819755">"Daha az acil bildirimler aşağıdadır"</string>
     <string name="notification_tap_again" msgid="7590196980943943842">"Açmak için tekrar hafifçe vurun"</string>
     <string name="keyguard_unlock" msgid="8043466894212841998">"Kilidi açmak için hızlıca yukarı kaydırın"</string>
+    <string name="zen_mode_forever" msgid="7420011936770086993">"Siz bunu kapatana kadar"</string>
+  <plurals name="zen_mode_duration_minutes">
+    <item quantity="one" msgid="9040808414992812341">"Bir dakika süreyle"</item>
+    <item quantity="other" msgid="6924190729213550991">"%d dakika süreyle"</item>
+  </plurals>
+  <plurals name="zen_mode_duration_hours">
+    <item quantity="one" msgid="3480040795582254384">"Bir saat süreyle"</item>
+    <item quantity="other" msgid="5408537517529822157">"%d saat süreyle"</item>
+  </plurals>
 </resources>
diff --git a/packages/SystemUI/res/values-uk/strings.xml b/packages/SystemUI/res/values-uk/strings.xml
index 5b3c5b2..7f66e72 100644
--- a/packages/SystemUI/res/values-uk/strings.xml
+++ b/packages/SystemUI/res/values-uk/strings.xml
@@ -120,6 +120,8 @@
     <string name="accessibility_desc_on" msgid="2385254693624345265">"Увімкнено."</string>
     <string name="accessibility_desc_off" msgid="6475508157786853157">"Вимкнено."</string>
     <string name="accessibility_desc_connected" msgid="8366256693719499665">"Під’єднано."</string>
+    <!-- no translation found for accessibility_desc_connecting (3812924520316280149) -->
+    <skip />
     <string name="accessibility_data_connection_gprs" msgid="1606477224486747751">"GPRS"</string>
     <string name="accessibility_data_connection_1x" msgid="994133468120244018">"1 X"</string>
     <string name="accessibility_data_connection_hspa" msgid="2032328855462645198">"HSPA"</string>
@@ -156,6 +158,9 @@
     <string name="accessibility_quick_settings_bluetooth" msgid="5749054971341882340">"Bluetooth: <xliff:g id="STATE">%s</xliff:g>."</string>
     <string name="accessibility_quick_settings_location" msgid="4577282329866813100">"Місцезнаходження <xliff:g id="STATE">%s</xliff:g>."</string>
     <string name="accessibility_quick_settings_alarm" msgid="3959908972897295660">"Сигнал установлено на <xliff:g id="TIME">%s</xliff:g>."</string>
+    <string name="accessibility_quick_settings_close" msgid="2571790856136835943">"Закрити панель"</string>
+    <string name="accessibility_quick_settings_more_time" msgid="5778794273488176726">"Більше часу"</string>
+    <string name="accessibility_quick_settings_less_time" msgid="101026945195230084">"Менше часу"</string>
     <string name="data_usage_disabled_dialog_3g_title" msgid="5257833881698644687">"Дані 2G–3G вимкнено"</string>
     <string name="data_usage_disabled_dialog_4g_title" msgid="4789143363492682629">"Дані 4G вимкнено"</string>
     <string name="data_usage_disabled_dialog_mobile_title" msgid="1046047248844821202">"Мобільне передавання даних вимкнено"</string>
@@ -207,7 +212,8 @@
     <string name="quick_settings_more_settings" msgid="326112621462813682">"Більше налаштувань"</string>
     <string name="quick_settings_tethering_label" msgid="7153452060448575549">"Режим модема"</string>
     <string name="quick_settings_hotspot_label" msgid="6046917934974004879">"Точка доступу"</string>
-    <string name="recents_empty_message" msgid="2269156590813544104">"ОСТАННІ"</string>
+    <string name="quick_settings_notifications_label" msgid="4818156442169154523">"Сповіщення"</string>
+    <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="ssl_ca_cert_warning" msgid="9005954106902053641">"Мережа може\nвідстежуватися"</string>
@@ -226,4 +232,13 @@
     <string name="speed_bump_explanation" msgid="1288875699658819755">"Менше термінових сповіщень нижче"</string>
     <string name="notification_tap_again" msgid="7590196980943943842">"Торкніться знову, щоб відкрити"</string>
     <string name="keyguard_unlock" msgid="8043466894212841998">"Проведіть пальцем угору, щоб розблокувати"</string>
+    <string name="zen_mode_forever" msgid="7420011936770086993">"Доки ви не вимкнете"</string>
+  <plurals name="zen_mode_duration_minutes">
+    <item quantity="one" msgid="9040808414992812341">"Протягом хвилини"</item>
+    <item quantity="other" msgid="6924190729213550991">"Протягом %d хв"</item>
+  </plurals>
+  <plurals name="zen_mode_duration_hours">
+    <item quantity="one" msgid="3480040795582254384">"Протягом години"</item>
+    <item quantity="other" msgid="5408537517529822157">"Протягом %d год"</item>
+  </plurals>
 </resources>
diff --git a/packages/SystemUI/res/values-vi/strings.xml b/packages/SystemUI/res/values-vi/strings.xml
index c6fddfe..3ddea96 100644
--- a/packages/SystemUI/res/values-vi/strings.xml
+++ b/packages/SystemUI/res/values-vi/strings.xml
@@ -120,6 +120,8 @@
     <string name="accessibility_desc_on" msgid="2385254693624345265">"Bật."</string>
     <string name="accessibility_desc_off" msgid="6475508157786853157">"Tắt."</string>
     <string name="accessibility_desc_connected" msgid="8366256693719499665">"Đã kết nối."</string>
+    <!-- no translation found for accessibility_desc_connecting (3812924520316280149) -->
+    <skip />
     <string name="accessibility_data_connection_gprs" msgid="1606477224486747751">"GPRS"</string>
     <string name="accessibility_data_connection_1x" msgid="994133468120244018">"1 X"</string>
     <string name="accessibility_data_connection_hspa" msgid="2032328855462645198">"HSPA"</string>
@@ -156,6 +158,9 @@
     <string name="accessibility_quick_settings_bluetooth" msgid="5749054971341882340">"Bluetooth <xliff:g id="STATE">%s</xliff:g>."</string>
     <string name="accessibility_quick_settings_location" msgid="4577282329866813100">"Vị trí <xliff:g id="STATE">%s</xliff:g>."</string>
     <string name="accessibility_quick_settings_alarm" msgid="3959908972897295660">"Báo thức được đặt cho <xliff:g id="TIME">%s</xliff:g>."</string>
+    <string name="accessibility_quick_settings_close" msgid="2571790856136835943">"Đóng bảng điều khiển"</string>
+    <string name="accessibility_quick_settings_more_time" msgid="5778794273488176726">"Nhiều thời gian hơn"</string>
+    <string name="accessibility_quick_settings_less_time" msgid="101026945195230084">"Ít thời gian hơn"</string>
     <string name="data_usage_disabled_dialog_3g_title" msgid="5257833881698644687">"Đã tắt dữ liệu 2G-3G"</string>
     <string name="data_usage_disabled_dialog_4g_title" msgid="4789143363492682629">"Đã tắt dữ liệu 4G"</string>
     <string name="data_usage_disabled_dialog_mobile_title" msgid="1046047248844821202">"Dữ liệu di động bị vô hiệu hóa"</string>
@@ -207,7 +212,8 @@
     <string name="quick_settings_more_settings" msgid="326112621462813682">"Cài đặt khác"</string>
     <string name="quick_settings_tethering_label" msgid="7153452060448575549">"Đang dùng làm điểm truy cập Internet"</string>
     <string name="quick_settings_hotspot_label" msgid="6046917934974004879">"Điểm phát sóng"</string>
-    <string name="recents_empty_message" msgid="2269156590813544104">"GẦN ĐÂY"</string>
+    <string name="quick_settings_notifications_label" msgid="4818156442169154523">"Thông báo"</string>
+    <string name="recents_empty_message" msgid="7883614615463619450">"Không có ứng dụng nào gần đây"</string>
     <string name="recents_app_info_button_label" msgid="2890317189376000030">"Thông tin ứng dụng"</string>
     <string name="recents_search_bar_label" msgid="8074997400187836677">"tìm kiếm"</string>
     <string name="ssl_ca_cert_warning" msgid="9005954106902053641">"Mạng có thể\nđược giám sát"</string>
@@ -226,4 +232,13 @@
     <string name="speed_bump_explanation" msgid="1288875699658819755">"Thông báo ít khẩn cấp hơn bên dưới"</string>
     <string name="notification_tap_again" msgid="7590196980943943842">"Nhấn lại để mở"</string>
     <string name="keyguard_unlock" msgid="8043466894212841998">"Vuốt lên để mở khóa"</string>
+    <string name="zen_mode_forever" msgid="7420011936770086993">"Cho đến khi bạn tắt tính năng này"</string>
+  <plurals name="zen_mode_duration_minutes">
+    <item quantity="one" msgid="9040808414992812341">"Trong một phút"</item>
+    <item quantity="other" msgid="6924190729213550991">"Trong %d phút"</item>
+  </plurals>
+  <plurals name="zen_mode_duration_hours">
+    <item quantity="one" msgid="3480040795582254384">"Trong một giờ"</item>
+    <item quantity="other" msgid="5408537517529822157">"Trong %d giờ"</item>
+  </plurals>
 </resources>
diff --git a/packages/SystemUI/res/values-zh-rCN/strings.xml b/packages/SystemUI/res/values-zh-rCN/strings.xml
index c6da6ac..934ac8c 100644
--- a/packages/SystemUI/res/values-zh-rCN/strings.xml
+++ b/packages/SystemUI/res/values-zh-rCN/strings.xml
@@ -120,6 +120,8 @@
     <string name="accessibility_desc_on" msgid="2385254693624345265">"开启。"</string>
     <string name="accessibility_desc_off" msgid="6475508157786853157">"关闭。"</string>
     <string name="accessibility_desc_connected" msgid="8366256693719499665">"已连接。"</string>
+    <!-- no translation found for accessibility_desc_connecting (3812924520316280149) -->
+    <skip />
     <string name="accessibility_data_connection_gprs" msgid="1606477224486747751">"GPRS"</string>
     <string name="accessibility_data_connection_1x" msgid="994133468120244018">"1 X"</string>
     <string name="accessibility_data_connection_hspa" msgid="2032328855462645198">"HSPA"</string>
@@ -158,6 +160,9 @@
     <string name="accessibility_quick_settings_bluetooth" msgid="5749054971341882340">"蓝牙:<xliff:g id="STATE">%s</xliff:g>。"</string>
     <string name="accessibility_quick_settings_location" msgid="4577282329866813100">"位置信息服务<xliff:g id="STATE">%s</xliff:g>。"</string>
     <string name="accessibility_quick_settings_alarm" msgid="3959908972897295660">"闹钟已设置为:<xliff:g id="TIME">%s</xliff:g>。"</string>
+    <string name="accessibility_quick_settings_close" msgid="2571790856136835943">"关闭面板"</string>
+    <string name="accessibility_quick_settings_more_time" msgid="5778794273488176726">"更长时间"</string>
+    <string name="accessibility_quick_settings_less_time" msgid="101026945195230084">"更短时间"</string>
     <string name="data_usage_disabled_dialog_3g_title" msgid="5257833881698644687">"2G-3G 数据网络已停用"</string>
     <string name="data_usage_disabled_dialog_4g_title" msgid="4789143363492682629">"4G 数据网络已停用"</string>
     <string name="data_usage_disabled_dialog_mobile_title" msgid="1046047248844821202">"移动数据网络已停用"</string>
@@ -209,7 +214,8 @@
     <string name="quick_settings_more_settings" msgid="326112621462813682">"更多设置"</string>
     <string name="quick_settings_tethering_label" msgid="7153452060448575549">"网络共享"</string>
     <string name="quick_settings_hotspot_label" msgid="6046917934974004879">"热点"</string>
-    <string name="recents_empty_message" msgid="2269156590813544104">"最近"</string>
+    <string name="quick_settings_notifications_label" msgid="4818156442169154523">"通知"</string>
+    <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="ssl_ca_cert_warning" msgid="9005954106902053641">"网络可能会\n受到监控"</string>
@@ -225,8 +231,16 @@
   <plurals name="keyguard_more_overflow_text">
     <item quantity="other" msgid="9180696159506883684">"还有%d条"</item>
   </plurals>
-    <!-- no translation found for speed_bump_explanation (1288875699658819755) -->
-    <skip />
+    <string name="speed_bump_explanation" msgid="1288875699658819755">"不太紧急的通知会显示在下方"</string>
     <string name="notification_tap_again" msgid="7590196980943943842">"再次点按即可打开"</string>
     <string name="keyguard_unlock" msgid="8043466894212841998">"向上滑动即可解锁"</string>
+    <string name="zen_mode_forever" msgid="7420011936770086993">"直到您将其关闭"</string>
+  <plurals name="zen_mode_duration_minutes">
+    <item quantity="one" msgid="9040808414992812341">"1分钟"</item>
+    <item quantity="other" msgid="6924190729213550991">"%d分钟"</item>
+  </plurals>
+  <plurals name="zen_mode_duration_hours">
+    <item quantity="one" msgid="3480040795582254384">"1小时"</item>
+    <item quantity="other" msgid="5408537517529822157">"%d小时"</item>
+  </plurals>
 </resources>
diff --git a/packages/SystemUI/res/values-zh-rHK/strings.xml b/packages/SystemUI/res/values-zh-rHK/strings.xml
index ce9e260..40b56fe 100644
--- a/packages/SystemUI/res/values-zh-rHK/strings.xml
+++ b/packages/SystemUI/res/values-zh-rHK/strings.xml
@@ -120,6 +120,8 @@
     <string name="accessibility_desc_on" msgid="2385254693624345265">"開啟。"</string>
     <string name="accessibility_desc_off" msgid="6475508157786853157">"關閉。"</string>
     <string name="accessibility_desc_connected" msgid="8366256693719499665">"已連線。"</string>
+    <!-- no translation found for accessibility_desc_connecting (3812924520316280149) -->
+    <skip />
     <string name="accessibility_data_connection_gprs" msgid="1606477224486747751">"GPRS"</string>
     <string name="accessibility_data_connection_1x" msgid="994133468120244018">"1 X"</string>
     <string name="accessibility_data_connection_hspa" msgid="2032328855462645198">"HSPA"</string>
@@ -158,6 +160,9 @@
     <string name="accessibility_quick_settings_bluetooth" msgid="5749054971341882340">"藍牙:<xliff:g id="STATE">%s</xliff:g>。"</string>
     <string name="accessibility_quick_settings_location" msgid="4577282329866813100">"定位服務<xliff:g id="STATE">%s</xliff:g>。"</string>
     <string name="accessibility_quick_settings_alarm" msgid="3959908972897295660">"鬧鐘已設定為:<xliff:g id="TIME">%s</xliff:g>。"</string>
+    <string name="accessibility_quick_settings_close" msgid="2571790856136835943">"關閉面板"</string>
+    <string name="accessibility_quick_settings_more_time" msgid="5778794273488176726">"更多時間"</string>
+    <string name="accessibility_quick_settings_less_time" msgid="101026945195230084">"較少時間"</string>
     <string name="data_usage_disabled_dialog_3g_title" msgid="5257833881698644687">"已停用 2G-3G 數據"</string>
     <string name="data_usage_disabled_dialog_4g_title" msgid="4789143363492682629">"已停用 4G 數據"</string>
     <string name="data_usage_disabled_dialog_mobile_title" msgid="1046047248844821202">"已停用流動數據"</string>
@@ -209,7 +214,8 @@
     <string name="quick_settings_more_settings" msgid="326112621462813682">"更多設定"</string>
     <string name="quick_settings_tethering_label" msgid="7153452060448575549">"網路共用"</string>
     <string name="quick_settings_hotspot_label" msgid="6046917934974004879">"熱點"</string>
-    <string name="recents_empty_message" msgid="2269156590813544104">"近期"</string>
+    <string name="quick_settings_notifications_label" msgid="4818156442169154523">"通知"</string>
+    <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="ssl_ca_cert_warning" msgid="9005954106902053641">"網絡可能會\n受到監控"</string>
@@ -228,4 +234,13 @@
     <string name="speed_bump_explanation" msgid="1288875699658819755">"不太緊急的通知會在下方顯示"</string>
     <string name="notification_tap_again" msgid="7590196980943943842">"再次輕按即可開啟"</string>
     <string name="keyguard_unlock" msgid="8043466894212841998">"向上快速滑動即可解鎖"</string>
+    <string name="zen_mode_forever" msgid="7420011936770086993">"直至您關閉這項設定"</string>
+  <plurals name="zen_mode_duration_minutes">
+    <item quantity="one" msgid="9040808414992812341">"1 分鐘"</item>
+    <item quantity="other" msgid="6924190729213550991">"%d 分鐘"</item>
+  </plurals>
+  <plurals name="zen_mode_duration_hours">
+    <item quantity="one" msgid="3480040795582254384">"1 小時"</item>
+    <item quantity="other" msgid="5408537517529822157">"%d 小時"</item>
+  </plurals>
 </resources>
diff --git a/packages/SystemUI/res/values-zh-rTW/strings.xml b/packages/SystemUI/res/values-zh-rTW/strings.xml
index cafb688..c1a48c4 100644
--- a/packages/SystemUI/res/values-zh-rTW/strings.xml
+++ b/packages/SystemUI/res/values-zh-rTW/strings.xml
@@ -120,6 +120,8 @@
     <string name="accessibility_desc_on" msgid="2385254693624345265">"開啟。"</string>
     <string name="accessibility_desc_off" msgid="6475508157786853157">"關閉。"</string>
     <string name="accessibility_desc_connected" msgid="8366256693719499665">"已連線。"</string>
+    <!-- no translation found for accessibility_desc_connecting (3812924520316280149) -->
+    <skip />
     <string name="accessibility_data_connection_gprs" msgid="1606477224486747751">"GPRS"</string>
     <string name="accessibility_data_connection_1x" msgid="994133468120244018">"1 X"</string>
     <string name="accessibility_data_connection_hspa" msgid="2032328855462645198">"HSPA"</string>
@@ -158,6 +160,9 @@
     <string name="accessibility_quick_settings_bluetooth" msgid="5749054971341882340">"藍牙:<xliff:g id="STATE">%s</xliff:g>。"</string>
     <string name="accessibility_quick_settings_location" msgid="4577282329866813100">"定位服務<xliff:g id="STATE">%s</xliff:g>。"</string>
     <string name="accessibility_quick_settings_alarm" msgid="3959908972897295660">"鬧鐘已設定為:<xliff:g id="TIME">%s</xliff:g>。"</string>
+    <string name="accessibility_quick_settings_close" msgid="2571790856136835943">"關閉面板"</string>
+    <string name="accessibility_quick_settings_more_time" msgid="5778794273488176726">"更多時間"</string>
+    <string name="accessibility_quick_settings_less_time" msgid="101026945195230084">"更少時間"</string>
     <string name="data_usage_disabled_dialog_3g_title" msgid="5257833881698644687">"已停用 2G-3G 數據"</string>
     <string name="data_usage_disabled_dialog_4g_title" msgid="4789143363492682629">"已停用 4G 數據"</string>
     <string name="data_usage_disabled_dialog_mobile_title" msgid="1046047248844821202">"已停用行動數據"</string>
@@ -209,7 +214,8 @@
     <string name="quick_settings_more_settings" msgid="326112621462813682">"更多設定"</string>
     <string name="quick_settings_tethering_label" msgid="7153452060448575549">"網路共用"</string>
     <string name="quick_settings_hotspot_label" msgid="6046917934974004879">"無線基地台"</string>
-    <string name="recents_empty_message" msgid="2269156590813544104">"近期"</string>
+    <string name="quick_settings_notifications_label" msgid="4818156442169154523">"通知"</string>
+    <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="ssl_ca_cert_warning" msgid="9005954106902053641">"網路可能\n受到監控"</string>
@@ -228,4 +234,13 @@
     <string name="speed_bump_explanation" msgid="1288875699658819755">"較不緊急的通知會顯示在下方"</string>
     <string name="notification_tap_again" msgid="7590196980943943842">"再次輕按即可開啟"</string>
     <string name="keyguard_unlock" msgid="8043466894212841998">"向上滑動即可解鎖"</string>
+    <string name="zen_mode_forever" msgid="7420011936770086993">"手動關閉這項設定前一律啟用"</string>
+  <plurals name="zen_mode_duration_minutes">
+    <item quantity="one" msgid="9040808414992812341">"1 分鐘"</item>
+    <item quantity="other" msgid="6924190729213550991">"%d 分鐘"</item>
+  </plurals>
+  <plurals name="zen_mode_duration_hours">
+    <item quantity="one" msgid="3480040795582254384">"1 小時"</item>
+    <item quantity="other" msgid="5408537517529822157">"%d 小時"</item>
+  </plurals>
 </resources>
diff --git a/packages/SystemUI/res/values-zu/strings.xml b/packages/SystemUI/res/values-zu/strings.xml
index 4865f7b..af49a7f 100644
--- a/packages/SystemUI/res/values-zu/strings.xml
+++ b/packages/SystemUI/res/values-zu/strings.xml
@@ -120,6 +120,8 @@
     <string name="accessibility_desc_on" msgid="2385254693624345265">"Vula."</string>
     <string name="accessibility_desc_off" msgid="6475508157786853157">"Vala."</string>
     <string name="accessibility_desc_connected" msgid="8366256693719499665">"Ixhunyiwe."</string>
+    <!-- no translation found for accessibility_desc_connecting (3812924520316280149) -->
+    <skip />
     <string name="accessibility_data_connection_gprs" msgid="1606477224486747751">"GPRS"</string>
     <string name="accessibility_data_connection_1x" msgid="994133468120244018">"1 X"</string>
     <string name="accessibility_data_connection_hspa" msgid="2032328855462645198">"I-HSPA"</string>
@@ -156,6 +158,9 @@
     <string name="accessibility_quick_settings_bluetooth" msgid="5749054971341882340">"I-Bluetooth <xliff:g id="STATE">%s</xliff:g>."</string>
     <string name="accessibility_quick_settings_location" msgid="4577282329866813100">"Indawo i-<xliff:g id="STATE">%s</xliff:g>."</string>
     <string name="accessibility_quick_settings_alarm" msgid="3959908972897295660">"I-alamu isethiwe ngo-<xliff:g id="TIME">%s</xliff:g>."</string>
+    <string name="accessibility_quick_settings_close" msgid="2571790856136835943">"Vala iphaneli"</string>
+    <string name="accessibility_quick_settings_more_time" msgid="5778794273488176726">"Isikhathi esiningi"</string>
+    <string name="accessibility_quick_settings_less_time" msgid="101026945195230084">"Isikhathi esingaphansi"</string>
     <string name="data_usage_disabled_dialog_3g_title" msgid="5257833881698644687">"idatha ye-2G-3G ivimbelwe"</string>
     <string name="data_usage_disabled_dialog_4g_title" msgid="4789143363492682629">"Idatha ye-4G ivimbelwe"</string>
     <string name="data_usage_disabled_dialog_mobile_title" msgid="1046047248844821202">"Idatha yefoni ivimbelwe"</string>
@@ -207,7 +212,8 @@
     <string name="quick_settings_more_settings" msgid="326112621462813682">"Izilungiselelo eziningi"</string>
     <string name="quick_settings_tethering_label" msgid="7153452060448575549">"Ukusebenzisa njengemodemu"</string>
     <string name="quick_settings_hotspot_label" msgid="6046917934974004879">"I-Hotspot"</string>
-    <string name="recents_empty_message" msgid="2269156590813544104">"OKWAKAMUVA"</string>
+    <string name="quick_settings_notifications_label" msgid="4818156442169154523">"Izaziso"</string>
+    <string name="recents_empty_message" msgid="7883614615463619450">"Azikho izinhlelo zokusebenza zakamuva"</string>
     <string name="recents_app_info_button_label" msgid="2890317189376000030">"Ulwazi lohlelo lokusebenza"</string>
     <string name="recents_search_bar_label" msgid="8074997400187836677">"sesha"</string>
     <string name="ssl_ca_cert_warning" msgid="9005954106902053641">"Kungenzeka inethiwekhi\niqashiwe"</string>
@@ -226,4 +232,13 @@
     <string name="speed_bump_explanation" msgid="1288875699658819755">"Izaziso ezingasheshi kakhulu ezingezansi"</string>
     <string name="notification_tap_again" msgid="7590196980943943842">"Thepha futhi ukuze uvule"</string>
     <string name="keyguard_unlock" msgid="8043466894212841998">"Swayiphela phezulu ukuze uvule"</string>
+    <string name="zen_mode_forever" msgid="7420011936770086993">"Uze uvale lokhu"</string>
+  <plurals name="zen_mode_duration_minutes">
+    <item quantity="one" msgid="9040808414992812341">"Iminithi elilodwa"</item>
+    <item quantity="other" msgid="6924190729213550991">"Amaminithi angu-%d"</item>
+  </plurals>
+  <plurals name="zen_mode_duration_hours">
+    <item quantity="one" msgid="3480040795582254384">"Ihora elilodwa"</item>
+    <item quantity="other" msgid="5408537517529822157">"Amahora angu-%d"</item>
+  </plurals>
 </resources>
diff --git a/packages/SystemUI/res/values/attrs.xml b/packages/SystemUI/res/values/attrs.xml
index 3549689..c453618 100644
--- a/packages/SystemUI/res/values/attrs.xml
+++ b/packages/SystemUI/res/values/attrs.xml
@@ -43,12 +43,6 @@
     <declare-styleable name="BatteryMeterView">
         <attr name="frameColor" format="color" />
     </declare-styleable>
-    <declare-styleable name="SwipeAffordanceView">
-        <attr name="swipeDirection" format="enum">
-            <enum name="start" value="0" />
-            <enum name="end" value="1" />
-        </attr>
-    </declare-styleable>
     <declare-styleable name="Clock">
         <attr name="amPmStyle" format="enum">
             <enum name="normal" value="0" />
diff --git a/packages/SystemUI/res/values/colors.xml b/packages/SystemUI/res/values/colors.xml
index 4e37dbb..757d4ad 100644
--- a/packages/SystemUI/res/values/colors.xml
+++ b/packages/SystemUI/res/values/colors.xml
@@ -67,11 +67,13 @@
     <!-- 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. -->
-    <color name="recents_task_bar_dark_text_color">#ff222222</color>
+    <color name="recents_task_bar_dark_text_color">#ff333333</color>
     <!-- The recents task bar light dismiss icon color to be drawn on top of dark backgrounds. -->
     <color name="recents_task_bar_light_dismiss_color">#ffeeeeee</color>
     <!-- The recents task bar dark dismiss icon color to be drawn on top of light backgrounds. -->
     <color name="recents_task_bar_dark_dismiss_color">#ff333333</color>
+    <!-- The recents task bar highlight color. -->
+    <color name="recents_task_bar_highlight_color">#28ffffff</color>
 
     <color name="keyguard_affordance">#ffffffff</color>
 
diff --git a/packages/SystemUI/res/values/config.xml b/packages/SystemUI/res/values/config.xml
index 0bd4f18..0184df2 100644
--- a/packages/SystemUI/res/values/config.xml
+++ b/packages/SystemUI/res/values/config.xml
@@ -113,9 +113,9 @@
     <!-- The min animation duration for animating views that are newly visible. -->
     <integer name="recents_filter_animate_new_views_min_duration">125</integer>
     <!-- The min animation duration for animating the task bar in. -->
-    <integer name="recents_animate_task_bar_enter_duration">225</integer>
+    <integer name="recents_animate_task_bar_enter_duration">300</integer>
     <!-- The min animation duration for animating the task bar out. -->
-    <integer name="recents_animate_task_bar_exit_duration">175</integer>
+    <integer name="recents_animate_task_bar_exit_duration">150</integer>
     <!-- The animation duration for animating in the info pane. -->
     <integer name="recents_animate_task_view_info_pane_duration">150</integer>
     <!-- The animation duration for animating the removal of a task view. -->
@@ -135,5 +135,8 @@
     <!-- Defines the implementation of the velocity tracker to be used for the panel expansion. Can
          be 'platform' or 'noisy' (i.e. for noisy touch screens). -->
     <string name="velocity_tracker_impl" translatable="false">platform</string>
+
+    <!-- Wait on the touch feedback this long before performing an action. -->
+    <integer name="feedback_start_delay">300</integer>
 </resources>
 
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index bf0cb68..60a5643 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -199,6 +199,9 @@
     <!-- How far the expanded QS panel peeks from the header in collapsed state. -->
     <dimen name="qs_peek_height">8dp</dimen>
 
+    <dimen name="zen_mode_condition_detail_button_padding">8dp</dimen>
+    <dimen name="zen_mode_condition_height">48dp</dimen>
+
     <!-- used by DessertCase -->
     <dimen name="dessert_case_cell_size">192dp</dimen>
 
@@ -209,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">60dp</dimen>
+    <dimen name="recents_task_view_application_icon_size">32dp</dimen>
 
     <!-- The size of the activity icon in the recents task view. -->
     <dimen name="recents_task_view_activity_icon_size">60dp</dimen>
@@ -218,20 +221,32 @@
     <dimen name="recents_task_view_rounded_corners_radius">2dp</dimen>
 
     <!-- The min translation in the Z index for the last task. -->
-    <dimen name="recents_task_view_z_min">3dp</dimen>
+    <dimen name="recents_task_view_z_min">5dp</dimen>
 
     <!-- The translation in the Z index for each task above the last task. -->
-    <dimen name="recents_task_view_z_increment">5dp</dimen>
+    <dimen name="recents_task_view_z_increment">10dp</dimen>
+
+    <!-- The amount of bottom inset in the shadow outline. -->
+    <dimen name="recents_task_view_shadow_outline_bottom_inset">5dp</dimen>
 
     <!-- The amount to translate when animating the removal of a task. -->
     <dimen name="recents_task_view_remove_anim_translation_x">100dp</dimen>
 
+    <!-- The amount of highlight to make on each task view. -->
+    <dimen name="recents_task_view_highlight">1dp</dimen>
+
     <!-- The amount of space a user has to scroll to dismiss any info panes. -->
     <dimen name="recents_task_stack_scroll_dismiss_info_pane_distance">50dp</dimen>
 
     <!-- The height of the search bar space. -->
     <dimen name="recents_search_bar_space_height">64dp</dimen>
 
+    <!-- The side padding for the task stack as a percentage of the width. -->
+    <item name="recents_stack_width_padding_percentage" format="float" type="dimen">0.04444</item>
+
+    <!-- The top offset for the task stack. -->
+    <dimen name="recents_stack_top_padding">16dp</dimen>
+
     <!-- Used to calculate the translation animation duration, the expected amount of movement 
          in dps over one second of time. -->
     <dimen name="recents_animation_movement_in_dps_per_second">800dp</dimen>
@@ -290,5 +305,23 @@
          keyguard_clock_height_fraction_* for the difference between min and max.-->
     <dimen name="keyguard_clock_notifications_margin_min">22dp</dimen>
     <dimen name="keyguard_clock_notifications_margin_max">36dp</dimen>
+    <dimen name="heads_up_window_height">250dp</dimen>
 
+    <!-- The minimum amount the user needs to swipe to go to the camera / phone. -->
+    <dimen name="keyguard_min_swipe_amount">75dp</dimen>
+
+    <!-- Volume panel dialog y offset -->
+    <dimen name="volume_panel_top">16dp</dimen>
+
+    <!-- Volume panel dialog width -->
+    <dimen name="volume_panel_width">300dp</dimen>
+
+    <!-- Volume panel z depth -->
+    <dimen name="volume_panel_z">3dp</dimen>
+
+    <!-- Distance between notifications and header when they are considered to be colliding. -->
+    <dimen name="header_notifications_collide_distance">24dp</dimen>
+
+    <!-- Move distance for the hint animations on the lockscreen (unlock, phone, camera)-->
+    <dimen name="hint_move_distance">75dp</dimen>
 </resources>
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index f5bc353..373f11f 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -290,6 +290,8 @@
     <string name="accessibility_desc_off">Off.</string>
     <!-- Content description of an item that is connected for accessibility (not shown on the screen). [CHAR LIMIT=NONE] -->
     <string name="accessibility_desc_connected">Connected.</string>
+    <!-- Content description of an item that is connecting for accessibility (not shown on the screen). [CHAR LIMIT=NONE] -->
+    <string name="accessibility_desc_connecting">Connecting.</string>
 
     <!-- Content description of the data connection type GPRS for accessibility (not shown on the screen). [CHAR LIMIT=NONE] -->
     <string name="accessibility_data_connection_gprs">GPRS</string>
@@ -388,6 +390,12 @@
     <string name="accessibility_quick_settings_location">Location <xliff:g id="state" example="Off">%s</xliff:g>.</string>
     <!-- Content description of the alarm tile in quick settings (not shown on the screen). [CHAR LIMIT=NONE] -->
     <string name="accessibility_quick_settings_alarm">Alarm set for <xliff:g id="time" example="Wed 3:30 PM">%s</xliff:g>.</string>
+    <!-- Content description of quick settings detail panel close button (not shown on the screen). [CHAR LIMIT=NONE] -->
+    <string name="accessibility_quick_settings_close">Close panel</string>
+    <!-- Content description of zen mode time condition plus button (not shown on the screen). [CHAR LIMIT=NONE] -->
+    <string name="accessibility_quick_settings_more_time">More time</string>
+    <!-- Content description of zen mode time condition minus button (not shown on the screen). [CHAR LIMIT=NONE] -->
+    <string name="accessibility_quick_settings_less_time">Less time</string>
 
     <!-- Title of dialog shown when 2G-3G data usage has exceeded limit and has been disabled. [CHAR LIMIT=48] -->
     <string name="data_usage_disabled_dialog_3g_title">2G-3G data disabled</string>
@@ -512,9 +520,11 @@
     <string name="quick_settings_tethering_label">Tethering</string>
     <!-- QuickSettings: Hotspot. [CHAR LIMIT=NONE] -->
     <string name="quick_settings_hotspot_label">Hotspot</string>
+    <!-- QuickSettings: Notifications [CHAR LIMIT=NONE] -->
+    <string name="quick_settings_notifications_label">Notifications</string>
 
     <!-- Recents: The empty recents string. [CHAR LIMIT=NONE] -->
-    <string name="recents_empty_message">RECENTS</string>
+    <string name="recents_empty_message">No recent apps</string>
     <!-- Recents: The info panel app info button string. [CHAR LIMIT=NONE] -->
     <string name="recents_app_info_button_label">Application Info</string>
     <!-- Recents: Temporary string for the button in the recents search bar. [CHAR LIMIT=NONE] -->
@@ -563,4 +573,19 @@
     <string name="keyguard_unlock">Swipe up to unlock</string>
 
     <string name="bugreport_tile_extended" translatable="false">%s\n%s (%s)</string>
+
+    <!-- Zen mode condition: no exit criteria. [CHAR LIMIT=NONE] -->
+    <string name="zen_mode_forever">Until you turn this off</string>
+
+    <!-- Zen mode condition: time duration in minutes. [CHAR LIMIT=NONE] -->
+    <plurals name="zen_mode_duration_minutes">
+        <item quantity="one">For one minute</item>
+        <item quantity="other">For %d minutes</item>
+    </plurals>
+
+    <!-- Zen mode condition: time duration in hours. [CHAR LIMIT=NONE] -->
+    <plurals name="zen_mode_duration_hours">
+        <item quantity="one">For one hour</item>
+        <item quantity="other">For %d hours</item>
+    </plurals>
 </resources>
diff --git a/packages/SystemUI/res/values/styles.xml b/packages/SystemUI/res/values/styles.xml
index 19888a8..6a12232 100644
--- a/packages/SystemUI/res/values/styles.xml
+++ b/packages/SystemUI/res/values/styles.xml
@@ -133,6 +133,32 @@
         <item name="android:fadingEdge">horizontal</item>
     </style>
 
+    <style name="TextAppearance.QS">
+        <item name="android:textStyle">normal</item>
+        <item name="android:textColor">#ffffff</item>
+        <item name="android:fontFamily">sans-serif</item>
+    </style>
+
+    <style name="TextAppearance.QS.DetailHeader">
+        <item name="android:textSize">20sp</item>
+        <item name="android:fontFamily">sans-serif-medium</item>
+    </style>
+
+    <style name="TextAppearance.QS.DetailItemPrimary">
+        <item name="android:textSize">16sp</item>
+    </style>
+
+    <style name="TextAppearance.QS.DetailItemSecondary">
+        <item name="android:textSize">14sp</item>
+        <item name="android:textColor">#7fcac3</item>
+    </style>
+
+    <style name="TextAppearance.QS.DetailButton">
+        <item name="android:textSize">14sp</item>
+        <item name="android:textAllCaps">true</item>
+        <item name="android:fontFamily">sans-serif-medium</item>
+    </style>
+
     <style name="BaseBrightnessDialogContainer">
         <item name="android:layout_width">match_parent</item>
         <item name="android:layout_height">wrap_content</item>
@@ -192,9 +218,9 @@
         <item name="android:colorControlActivated">@color/system_accent_color</item>
     </style>
 
-    <style name="QSBorderless" parent="@android:style/Widget.Quantum.Button.Borderless" />
+    <style name="BorderlessButton" parent="@android:style/Widget.Quantum.Button.Borderless" />
 
-    <style name="QSBorderless.Tiny">
+    <style name="BorderlessButton.Tiny">
         <item name="android:minHeight">12dip</item>
         <item name="android:minWidth">12dip</item>
     </style>
diff --git a/packages/SystemUI/src/com/android/systemui/SwipeHelper.java b/packages/SystemUI/src/com/android/systemui/SwipeHelper.java
index 1b12cb0..d153d09 100644
--- a/packages/SystemUI/src/com/android/systemui/SwipeHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/SwipeHelper.java
@@ -51,12 +51,12 @@
     private int MAX_DISMISS_VELOCITY = 2000; // dp/sec
     private static final int SNAP_ANIM_LEN = SLOW_ANIMATIONS ? 1000 : 150; // ms
 
-    public static float ALPHA_FADE_START = 0f; // fraction of thumbnail width
+    public static float SWIPE_PROGRESS_FADE_START = 0f; // fraction of thumbnail width
                                                  // where fade starts
-    static final float ALPHA_FADE_END = 0.5f; // fraction of thumbnail width
-                                              // beyond which alpha->0
-    private float mMinAlpha = 0f;
-    private float mMaxAlpha = 1f;
+    static final float SWIPE_PROGRESS_FADE_END = 0.5f; // fraction of thumbnail width
+                                              // beyond which swipe progress->0
+    private float mMinSwipeProgress = 0f;
+    private float mMaxSwipeProgress = 1f;
 
     private float mPagingTouchSlop;
     private Callback mCallback;
@@ -137,36 +137,39 @@
                 v.getMeasuredHeight();
     }
 
-    public void setMinAlpha(float minAlpha) {
-        mMinAlpha = minAlpha;
+    public void setMinSwipeProgress(float minSwipeProgress) {
+        mMinSwipeProgress = minSwipeProgress;
     }
 
-    public void setMaxAlpha(float maxAlpha) {
-        mMaxAlpha = maxAlpha;
+    public void setMaxSwipeProgress(float maxSwipeProgress) {
+        mMaxSwipeProgress = maxSwipeProgress;
     }
 
-    private float getAlphaForOffset(View view) {
+    private float getSwipeProgressForOffset(View view) {
         float viewSize = getSize(view);
-        final float fadeSize = ALPHA_FADE_END * viewSize;
+        final float fadeSize = SWIPE_PROGRESS_FADE_END * viewSize;
         float result = 1.0f;
         float pos = getTranslation(view);
-        if (pos >= viewSize * ALPHA_FADE_START) {
-            result = 1.0f - (pos - viewSize * ALPHA_FADE_START) / fadeSize;
-        } else if (pos < viewSize * (1.0f - ALPHA_FADE_START)) {
-            result = 1.0f + (viewSize * ALPHA_FADE_START + pos) / fadeSize;
+        if (pos >= viewSize * SWIPE_PROGRESS_FADE_START) {
+            result = 1.0f - (pos - viewSize * SWIPE_PROGRESS_FADE_START) / fadeSize;
+        } else if (pos < viewSize * (1.0f - SWIPE_PROGRESS_FADE_START)) {
+            result = 1.0f + (viewSize * SWIPE_PROGRESS_FADE_START + pos) / fadeSize;
         }
-        return Math.min(Math.max(mMinAlpha, result), mMaxAlpha);
+        return Math.min(Math.max(mMinSwipeProgress, result), mMaxSwipeProgress);
     }
 
-    private void updateAlphaFromOffset(View animView, boolean dismissable) {
-        if (FADE_OUT_DURING_SWIPE && dismissable) {
-            float alpha = getAlphaForOffset(animView);
-            if (alpha != 0f && alpha != 1f) {
-                animView.setLayerType(View.LAYER_TYPE_HARDWARE, null);
-            } else {
-                animView.setLayerType(View.LAYER_TYPE_NONE, null);
+    private void updateSwipeProgressFromOffset(View animView, boolean dismissable) {
+        float swipeProgress = getSwipeProgressForOffset(animView);
+        if (!mCallback.updateSwipeProgress(animView, dismissable, swipeProgress)) {
+            if (FADE_OUT_DURING_SWIPE && dismissable) {
+                float alpha = swipeProgress;
+                if (alpha != 0f && alpha != 1f) {
+                    animView.setLayerType(View.LAYER_TYPE_HARDWARE, null);
+                } else {
+                    animView.setLayerType(View.LAYER_TYPE_NONE, null);
+                }
+                animView.setAlpha(getSwipeProgressForOffset(animView));
             }
-            animView.setAlpha(getAlphaForOffset(animView));
         }
         invalidateGlobalRegion(animView);
     }
@@ -307,7 +310,7 @@
         });
         anim.addUpdateListener(new AnimatorUpdateListener() {
             public void onAnimationUpdate(ValueAnimator animation) {
-                updateAlphaFromOffset(animView, canAnimViewBeDismissed);
+                updateSwipeProgressFromOffset(animView, canAnimViewBeDismissed);
             }
         });
         anim.start();
@@ -321,12 +324,12 @@
         anim.setDuration(duration);
         anim.addUpdateListener(new AnimatorUpdateListener() {
             public void onAnimationUpdate(ValueAnimator animation) {
-                updateAlphaFromOffset(animView, canAnimViewBeDismissed);
+                updateSwipeProgressFromOffset(animView, canAnimViewBeDismissed);
             }
         });
         anim.addListener(new AnimatorListenerAdapter() {
             public void onAnimationEnd(Animator animator) {
-                updateAlphaFromOffset(animView, canAnimViewBeDismissed);
+                updateSwipeProgressFromOffset(animView, canAnimViewBeDismissed);
                 mCallback.onChildSnappedBack(animView);
             }
         });
@@ -365,7 +368,7 @@
                     }
                     setTranslation(mCurrAnimView, delta);
 
-                    updateAlphaFromOffset(mCurrAnimView, mCanCurrViewBeDimissed);
+                    updateSwipeProgressFromOffset(mCurrAnimView, mCanCurrViewBeDimissed);
                 }
                 break;
             case MotionEvent.ACTION_UP:
@@ -415,5 +418,12 @@
         void onDragCancelled(View v);
 
         void onChildSnappedBack(View animView);
+
+        /**
+         * Updates the swipe progress on a child.
+         *
+         * @return if true, prevents the default alpha fading.
+         */
+        boolean updateSwipeProgress(View animView, boolean dismissable, float swipeProgress);
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java b/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java
index d7ce255..630ba13 100644
--- a/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java
+++ b/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java
@@ -42,12 +42,12 @@
     private final Class<?>[] SERVICES = new Class[] {
             com.android.systemui.keyguard.KeyguardViewMediator.class,
             com.android.systemui.recent.Recents.class,
+            com.android.systemui.volume.VolumeUI.class,
             com.android.systemui.statusbar.SystemBars.class,
             com.android.systemui.usb.StorageNotification.class,
             com.android.systemui.power.PowerUI.class,
             com.android.systemui.media.RingtonePlayer.class,
             com.android.systemui.settings.SettingsUI.class,
-            com.android.systemui.volume.VolumeUI.class,
     };
 
     /**
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java
index 41c0e78..b280ab7 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java
@@ -202,6 +202,12 @@
             checkPermission();
             mKeyguardViewMediator.onBootCompleted();
         }
+
+        @Override
+        public void startKeyguardExitAnimation(long startTime, long fadeoutDuration) {
+            checkPermission();
+            mKeyguardViewMediator.startKeyguardExitAnimation(startTime, fadeoutDuration);
+        }
     };
 }
 
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
index b2872fa..4837a53 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
@@ -46,7 +46,8 @@
 import android.util.Log;
 import android.util.Slog;
 import android.view.ViewGroup;
-import android.view.WindowManager;
+import android.view.IWindowManager;
+import android.view.WindowManagerGlobal;
 import android.view.WindowManagerPolicy;
 
 import com.android.internal.policy.IKeyguardExitCallback;
@@ -58,8 +59,6 @@
 import com.android.keyguard.KeyguardUpdateMonitorCallback;
 import com.android.keyguard.MultiUserAvatarCache;
 import com.android.keyguard.ViewMediatorCallback;
-import com.android.keyguard.analytics.KeyguardAnalytics;
-import com.android.keyguard.analytics.Session;
 import com.android.systemui.SystemUI;
 import com.android.systemui.statusbar.phone.PhoneStatusBar;
 import com.android.systemui.statusbar.phone.ScrimController;
@@ -69,7 +68,6 @@
 import java.io.File;
 
 import static android.provider.Settings.System.SCREEN_OFF_TIMEOUT;
-import static com.android.keyguard.analytics.KeyguardAnalytics.SessionTypeAdapter;
 
 
 /**
@@ -116,7 +114,6 @@
 public class KeyguardViewMediator extends SystemUI {
     private static final int KEYGUARD_DISPLAY_TIMEOUT_DELAY_DEFAULT = 30000;
     final static boolean DEBUG = false;
-    private static final boolean ENABLE_ANALYTICS = Build.IS_DEBUGGABLE;
     private final static boolean DBG_WAKE = false;
 
     private final static String TAG = "KeyguardViewMediator";
@@ -137,6 +134,7 @@
     private static final int SET_OCCLUDED = 12;
     private static final int KEYGUARD_TIMEOUT = 13;
     private static final int DISMISS = 17;
+    private static final int START_KEYGUARD_EXIT_ANIM = 18;
 
     /**
      * The default amount of time we stay awake (used for all key input)
@@ -180,6 +178,9 @@
     /** High level access to the power manager for WakeLocks */
     private PowerManager mPM;
 
+    /** High level access to the window manager for dismissing keyguard animation */
+    private IWindowManager mWM;
+
     /** UserManager for querying number of users */
     private UserManager mUserManager;
 
@@ -194,8 +195,6 @@
 
     private StatusBarKeyguardViewManager mStatusBarKeyguardViewManager;
 
-    private KeyguardAnalytics mKeyguardAnalytics;
-
     // these are protected by synchronized (this)
 
     /**
@@ -440,6 +439,7 @@
 
     private void setup() {
         mPM = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
+        mWM = WindowManagerGlobal.getWindowManagerService();
         mUserManager = (UserManager) mContext.getSystemService(Context.USER_SERVICE);
         mShowKeyguardWakeLock = mPM.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "show keyguard");
         mShowKeyguardWakeLock.setReferenceCounted(false);
@@ -463,22 +463,6 @@
                 mViewMediatorCallback, mLockPatternUtils);
         final ContentResolver cr = mContext.getContentResolver();
 
-        if (ENABLE_ANALYTICS && !LockPatternUtils.isSafeModeEnabled() &&
-                Settings.Secure.getInt(cr, KEYGUARD_ANALYTICS_SETTING, 0) == 1) {
-            mKeyguardAnalytics = new KeyguardAnalytics(mContext, new SessionTypeAdapter() {
-
-                @Override
-                public int getSessionType() {
-                    return mLockPatternUtils.isSecure() && !mUpdateMonitor.getUserHasTrust(
-                            mLockPatternUtils.getCurrentUser())
-                            ? Session.TYPE_KEYGUARD_SECURE
-                            : Session.TYPE_KEYGUARD_INSECURE;
-                }
-            }, new File(mContext.getCacheDir(), "keyguard_analytics.bin"));
-        } else {
-            mKeyguardAnalytics = null;
-        }
-
         mScreenOn = mPM.isScreenOn();
 
         mLockSounds = new SoundPool(1, AudioManager.STREAM_SYSTEM, 0);
@@ -579,9 +563,6 @@
             } else {
                 doKeyguardLocked(null);
             }
-            if (ENABLE_ANALYTICS && mKeyguardAnalytics != null) {
-                mKeyguardAnalytics.getCallback().onScreenOff();
-            }
         }
         KeyguardUpdateMonitor.getInstance(mContext).dispatchScreenTurndOff(why);
     }
@@ -824,9 +805,6 @@
                 updateActivityLockScreenState();
                 adjustStatusBarLocked();
             }
-            if (ENABLE_ANALYTICS && mKeyguardAnalytics != null) {
-                mKeyguardAnalytics.getCallback().onSetOccluded(isOccluded);
-            }
         }
     }
 
@@ -1076,6 +1054,10 @@
                 case DISMISS:
                     handleDismiss();
                     break;
+                case START_KEYGUARD_EXIT_ANIM:
+                    StartKeyguardExitAnimParams params = (StartKeyguardExitAnimParams) msg.obj;
+                    handleStartKeyguardExitAnimation(params.startTime, params.fadeoutDuration);
+                    break;
             }
         }
     };
@@ -1207,6 +1189,19 @@
     private void handleHide() {
         synchronized (KeyguardViewMediator.this) {
             if (DEBUG) Log.d(TAG, "handleHide");
+            try {
+
+                // Don't actually hide the Keyguard at the moment, wait for window manager until
+                // it tells us it's safe to do so with startKeyguardExitAnimation.
+                mWM.keyguardGoingAway();
+            } catch (RemoteException e) {
+                Log.e(TAG, "Error while calling WindowManager", e);
+            }
+        }
+    }
+
+    private void handleStartKeyguardExitAnimation(long startTime, long fadeoutDuration) {
+        synchronized (KeyguardViewMediator.this) {
 
             // only play "unlock" noises if not on a call (since the incall UI
             // disables the keyguard)
@@ -1214,7 +1209,7 @@
                 playSounds(false);
             }
 
-            mStatusBarKeyguardViewManager.hide();
+            mStatusBarKeyguardViewManager.hide(startTime, fadeoutDuration);
             mShowing = false;
             mKeyguardDonePending = false;
             updateActivityLockScreenState();
@@ -1324,7 +1319,24 @@
         return mStatusBarKeyguardViewManager;
     }
 
+    public void startKeyguardExitAnimation(long startTime, long fadeoutDuration) {
+        Message msg = mHandler.obtainMessage(START_KEYGUARD_EXIT_ANIM,
+                new StartKeyguardExitAnimParams(startTime, fadeoutDuration));
+        mHandler.sendMessage(msg);
+    }
+
     public ViewMediatorCallback getViewMediatorCallback() {
         return mViewMediatorCallback;
     }
+
+    private static class StartKeyguardExitAnimParams {
+
+        long startTime;
+        long fadeoutDuration;
+
+        private StartKeyguardExitAnimParams(long startTime, long fadeoutDuration) {
+            this.startTime = startTime;
+            this.fadeoutDuration = fadeoutDuration;
+        }
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
index bdac7a0..2bf369a 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
@@ -47,8 +47,10 @@
     private int mCellHeight;
     private int mLargeCellWidth;
     private int mLargeCellHeight;
+    private boolean mExpanded;
 
     private TileRecord mDetailRecord;
+    private Callback mCallback;
 
     public QSPanel(Context context) {
         this(context, null);
@@ -59,6 +61,7 @@
         mContext = context;
 
         mDetail = new FrameLayout(mContext);
+        mDetail.setBackgroundColor(mContext.getResources().getColor(R.color.system_primary_color));
         mDetail.setVisibility(GONE);
         mDetail.setClickable(true);
         addView(mDetail);
@@ -66,6 +69,10 @@
         updateResources();
     }
 
+    public void setCallback(Callback callback) {
+        mCallback = callback;
+    }
+
     public void updateResources() {
         final Resources res = mContext.getResources();
         final int columns = Math.max(1, res.getInteger(R.integer.quick_settings_num_columns));
@@ -84,12 +91,14 @@
     }
 
     public void setExpanded(boolean expanded) {
-        if (!expanded) {
+        if (mExpanded == expanded) return;
+        mExpanded = expanded;
+        if (!mExpanded) {
             showDetail(false /*show*/, mDetailRecord);
         }
         for (TileRecord r : mRecords) {
-            r.tile.setListening(expanded);
-            if (expanded) {
+            r.tile.setListening(mExpanded);
+            if (mExpanded) {
                 r.tile.refreshState();
             }
         }
@@ -143,6 +152,7 @@
     }
 
     private void handleShowDetail(TileRecord r, boolean show) {
+        if (r == null) return;
         AnimatorListener listener = null;
         if (show) {
             if (mDetailRecord != null) return;
@@ -156,6 +166,7 @@
             if (mDetailRecord == null) return;
             listener = mTeardownDetailWhenDone;
         }
+        fireShowingDetail(show);
         int x = r.tileView.getLeft() + r.tileView.getWidth() / 2;
         int y = r.tileView.getTop() + r.tileView.getHeight() / 2;
         mClipper.animateCircularClip(x, y, show, listener);
@@ -191,15 +202,23 @@
             final int ch = record.row == 0 ? mLargeCellHeight : mCellHeight;
             record.tileView.measure(exactly(cw), exactly(ch));
         }
-        final int actualHeight = rows == 0 ? 0 : getRowTop(rows);
-        mDetail.measure(exactly(width), exactly(actualHeight));
-        setMeasuredDimension(width, actualHeight);
+        int h = rows == 0 ? 0 : getRowTop(rows);
+        mDetail.measure(exactly(width), unspecified());
+        if (mDetail.getVisibility() == VISIBLE && mDetail.getChildCount() > 0) {
+            final int dmh = mDetail.getMeasuredHeight();
+            if (dmh > 0) h = Math.max(h, dmh);
+        }
+        setMeasuredDimension(width, h);
     }
 
     private static int exactly(int size) {
         return MeasureSpec.makeMeasureSpec(size, MeasureSpec.EXACTLY);
     }
 
+    private static int unspecified() {
+        return MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED);
+    }
+
     @Override
     protected void onLayout(boolean changed, int l, int t, int r, int b) {
         final int w = getWidth();
@@ -214,7 +233,8 @@
                     left + record.tileView.getMeasuredWidth(),
                     top + record.tileView.getMeasuredHeight());
         }
-        mDetail.layout(0, 0, mDetail.getMeasuredWidth(), mDetail.getMeasuredHeight());
+        final int dh = Math.max(mDetail.getMeasuredHeight(), getMeasuredHeight());
+        mDetail.layout(0, 0, mDetail.getMeasuredWidth(), dh);
     }
 
     private int getRowTop(int row) {
@@ -231,6 +251,12 @@
         return cols;
     }
 
+    private void fireShowingDetail(boolean showingDetail) {
+        if (mCallback != null) {
+            mCallback.onShowingDetail(showingDetail);
+        }
+    }
+
     private class H extends Handler {
         private static final int SHOW_DETAIL = 1;
         private static final int SET_TILE_VISIBILITY = 2;
@@ -257,4 +283,8 @@
             mDetailRecord = null;
         };
     };
+
+    public interface Callback {
+        void onShowingDetail(boolean showingDetail);
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSTile.java b/packages/SystemUI/src/com/android/systemui/qs/QSTile.java
index 835a5c4..c76ee8c 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSTile.java
@@ -26,6 +26,7 @@
 import android.view.View;
 import android.view.ViewGroup;
 
+import com.android.systemui.R;
 import com.android.systemui.qs.QSTile.State;
 import com.android.systemui.statusbar.policy.BluetoothController;
 import com.android.systemui.statusbar.policy.CastController;
@@ -35,6 +36,7 @@
 import com.android.systemui.statusbar.policy.RotationLockController;
 import com.android.systemui.statusbar.policy.TetheringController;
 import com.android.systemui.statusbar.policy.ZenModeController;
+import com.android.systemui.volume.VolumeComponent;
 
 import java.util.List;
 import java.util.Objects;
@@ -49,12 +51,12 @@
 public abstract class QSTile<TState extends State> implements Listenable {
     protected final String TAG = "QSTile." + getClass().getSimpleName();
     protected static final boolean DEBUG = false;
-    public static final int FEEDBACK_START_DELAY = 400;
 
     protected final Host mHost;
     protected final Context mContext;
     protected final H mHandler;
     protected final Handler mUiHandler = new Handler(Looper.getMainLooper());
+    private final int mFeedbackStartDelay;
 
     private Callback mCallback;
     protected final TState mState = newTileState();
@@ -68,6 +70,7 @@
         mHost = host;
         mContext = host.getContext();
         mHandler = new H(host.getLooper());
+        mFeedbackStartDelay = mContext.getResources().getInteger(R.integer.feedback_start_delay);
     }
 
     public boolean supportsDualTargets() {
@@ -116,6 +119,10 @@
         mHandler.obtainMessage(H.USER_SWITCH, newUserId).sendToTarget();
     }
 
+    protected void postAfterFeedback(Runnable runnable) {
+        mHandler.postDelayed(runnable, mFeedbackStartDelay);
+    }
+
     // call only on tile worker looper
 
     private void handleSetCallback(Callback callback) {
@@ -213,6 +220,7 @@
         ZenModeController getZenModeController();
         TetheringController getTetheringController();
         CastController getCastController();
+        VolumeComponent getVolumeComponent();
     }
 
     public static class State {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSTileView.java b/packages/SystemUI/src/com/android/systemui/qs/QSTileView.java
index 5eecc20..2edd8d5 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSTileView.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSTileView.java
@@ -84,7 +84,8 @@
             removeView(mLabel);
         }
         final Resources res = mContext.getResources();
-        mLabel = new TextView(mDual ? new ContextThemeWrapper(mContext, R.style.QSBorderless_Tiny)
+        mLabel = new TextView(mDual
+                ? new ContextThemeWrapper(mContext, R.style.BorderlessButton_Tiny)
                 : mContext);
         mLabel.setId(android.R.id.title);
         mLabel.setTextColor(res.getColor(R.color.qs_tile_text));
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/BluetoothTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/BluetoothTile.java
index 7335ab4..d220e1a 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/BluetoothTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/BluetoothTile.java
@@ -19,6 +19,7 @@
 import android.bluetooth.BluetoothAdapter.BluetoothStateChangeCallback;
 import android.content.Intent;
 import android.provider.Settings;
+import android.text.TextUtils;
 
 import com.android.systemui.R;
 import com.android.systemui.qs.QSTile;
@@ -70,18 +71,27 @@
         final boolean supported = mController.isBluetoothSupported();
         final boolean enabled = mController.isBluetoothEnabled();
         final boolean connected = mController.isBluetoothConnected();
+        final boolean connecting = mController.isBluetoothConnecting();
         state.visible = supported;
         state.value = enabled;
         final String stateContentDescription;
         if (enabled) {
+            state.label = null;
             if (connected) {
                 state.iconId = R.drawable.ic_qs_bluetooth_connected;
                 stateContentDescription = mContext.getString(R.string.accessibility_desc_connected);
+                state.label = mController.getLastDeviceName();
+            } else if (connecting) {
+                state.iconId = R.drawable.ic_qs_bluetooth_connecting;
+                stateContentDescription = mContext.getString(R.string.accessibility_desc_connecting);
+                state.label = mController.getLastDeviceName();
             } else {
                 state.iconId = R.drawable.ic_qs_bluetooth_on;
                 stateContentDescription = mContext.getString(R.string.accessibility_desc_on);
             }
-            state.label = mContext.getString(R.string.quick_settings_bluetooth_label);
+            if (TextUtils.isEmpty(state.label)) {
+                state.label = mContext.getString(R.string.quick_settings_bluetooth_label);
+            }
         } else {
             state.iconId = R.drawable.ic_qs_bluetooth_off;
             state.label = mContext.getString(R.string.quick_settings_bluetooth_off_label);
@@ -91,9 +101,9 @@
                 R.string.accessibility_quick_settings_bluetooth, stateContentDescription);
     }
 
-    private final BluetoothStateChangeCallback mCallback = new BluetoothStateChangeCallback() {
+    private final BluetoothController.Callback mCallback = new BluetoothController.Callback() {
         @Override
-        public void onBluetoothStateChange(boolean on) {
+        public void onBluetoothStateChange(boolean enabled, boolean connecting) {
             refreshState();
         }
     };
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/BugreportTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/BugreportTile.java
index fa41837..07ea825 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/BugreportTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/BugreportTile.java
@@ -58,13 +58,13 @@
 
     @Override
     protected void handleClick() {
-        mHandler.postDelayed(new Runnable() {
+        postAfterFeedback(new Runnable() {
             @Override
             public void run() {
                 mHost.collapsePanels();
                 mUiHandler.post(mShowDialog);
             }
-        }, FEEDBACK_START_DELAY);
+        });
     }
 
     @Override
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/CastTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/CastTile.java
index 907c77e..6793051 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/CastTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/CastTile.java
@@ -65,12 +65,12 @@
 
     @Override
     protected void handleClick() {
-        mHandler.postDelayed(new Runnable() {
+        postAfterFeedback(new Runnable() {
             public void run() {
                 mHost.collapsePanels();
                 mUiHandler.post(mShowDialog);
             }
-        }, FEEDBACK_START_DELAY);
+        });
     }
 
     @Override
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/NotificationsTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/NotificationsTile.java
new file mode 100644
index 0000000..20bbf8b
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/NotificationsTile.java
@@ -0,0 +1,168 @@
+/*
+ * 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.qs.tiles;
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.media.AudioManager;
+import android.util.Log;
+import android.view.ContextThemeWrapper;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.View.OnAttachStateChangeListener;
+import android.view.ViewGroup;
+import android.widget.TextView;
+
+import com.android.systemui.R;
+import com.android.systemui.qs.QSTile;
+import com.android.systemui.statusbar.policy.ZenModeController;
+import com.android.systemui.volume.VolumeComponent;
+import com.android.systemui.volume.VolumePanel;
+import com.android.systemui.volume.ZenModePanel;
+
+/** Quick settings tile: Notifications **/
+public class NotificationsTile extends QSTile<NotificationsTile.NotificationsState> {
+    private final ZenModeController mZenController;
+    private final AudioManager mAudioManager;
+
+    public NotificationsTile(Host host) {
+        super(host);
+        mZenController = host.getZenModeController();
+        mAudioManager = (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE);
+    }
+
+    @Override
+    public View createDetailView(Context context, ViewGroup root) {
+        final Context themedContext = new ContextThemeWrapper(mContext, R.style.QSAccentTheme);
+        final View v = LayoutInflater.from(themedContext).inflate(R.layout.qs_detail, root, false);
+        final TextView title = (TextView) v.findViewById(android.R.id.title);
+        title.setText(R.string.quick_settings_notifications_label);
+        final View close = v.findViewById(android.R.id.button1);
+        close.setOnClickListener(new View.OnClickListener() {
+            @Override
+            public void onClick(View v) {
+                showDetail(false);
+            }
+        });
+        final ViewGroup content = (ViewGroup) v.findViewById(android.R.id.content);
+        final VolumeComponent volumeComponent = mHost.getVolumeComponent();
+        final VolumePanel vp = new VolumePanel(mContext, content, mZenController);
+        v.addOnAttachStateChangeListener(new OnAttachStateChangeListener() {
+            @Override
+            public void onViewDetachedFromWindow(View v) {
+                volumeComponent.setVolumePanel(null);
+            }
+
+            @Override
+            public void onViewAttachedToWindow(View v) {
+                volumeComponent.setVolumePanel(vp);
+            }
+        });
+        vp.setZenModePanelCallback(new ZenModePanel.Callback() {
+            @Override
+            public void onMoreSettings() {
+                mHost.startSettingsActivity(ZenModePanel.ZEN_SETTINGS);
+            }
+
+            @Override
+            public void onInteraction() {
+                // noop
+            }
+        });
+        vp.postVolumeChanged(AudioManager.STREAM_RING, AudioManager.FLAG_SHOW_UI);
+        return v;
+    }
+
+    @Override
+    protected NotificationsState newTileState() {
+        return new NotificationsState();
+    }
+
+    @Override
+    public void setListening(boolean listening) {
+        if (listening) {
+            mZenController.addCallback(mCallback);
+            final IntentFilter filter = new IntentFilter(AudioManager.RINGER_MODE_CHANGED_ACTION);
+            mContext.registerReceiver(mReceiver, filter);
+        } else {
+            mZenController.removeCallback(mCallback);
+            mContext.unregisterReceiver(mReceiver);
+        }
+    }
+
+    @Override
+    protected void handleClick() {
+        showDetail(true);
+    }
+
+    @Override
+    protected void handleUpdateState(NotificationsState state, Object arg) {
+        state.visible = true;
+        state.zen = arg instanceof Boolean ? (Boolean) arg : mZenController.isZen();
+        state.ringerMode = mAudioManager.getRingerMode();
+        if (state.zen) {
+            state.iconId = R.drawable.ic_qs_zen_on;
+        } else if (state.ringerMode == AudioManager.RINGER_MODE_VIBRATE) {
+            state.iconId = R.drawable.ic_qs_ringer_vibrate;
+        } else if (state.ringerMode == AudioManager.RINGER_MODE_SILENT) {
+            state.iconId = R.drawable.ic_qs_ringer_silent;
+        } else {
+            state.iconId = R.drawable.ic_qs_ringer_audible;
+        }
+        state.label = mContext.getString(R.string.quick_settings_notifications_label);
+    }
+
+    private final ZenModeController.Callback mCallback = new ZenModeController.Callback() {
+        @Override
+        public void onZenChanged(boolean zen) {
+            if (DEBUG) Log.d(TAG, "onZenChanged " + zen);
+            refreshState(zen);
+        }
+    };
+
+    public static final class NotificationsState extends QSTile.State {
+        public boolean zen;
+        public int ringerMode;
+
+        @Override
+        public boolean copyTo(State other) {
+            final NotificationsState o = (NotificationsState) other;
+            final boolean changed = o.zen != zen || o.ringerMode != ringerMode;
+            o.zen = zen;
+            o.ringerMode = ringerMode;
+            return super.copyTo(other) || changed;
+        }
+
+        @Override
+        protected StringBuilder toStringBuilder() {
+            final StringBuilder rt = super.toStringBuilder();
+            rt.insert(rt.length() - 1, ",zen=" + zen + ",ringerMode=" + ringerMode);
+            return rt;
+        }
+    }
+
+    private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            if (AudioManager.RINGER_MODE_CHANGED_ACTION.equals(intent.getAction())) {
+                refreshState();
+            }
+        }
+    };
+}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/RingerModeTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/RingerModeTile.java
deleted file mode 100644
index c5e9b52..0000000
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/RingerModeTile.java
+++ /dev/null
@@ -1,108 +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.qs.tiles;
-
-import android.content.BroadcastReceiver;
-import android.content.Context;
-import android.content.Intent;
-import android.content.IntentFilter;
-import android.media.AudioManager;
-
-import com.android.systemui.R;
-import com.android.systemui.qs.QSTile;
-
-/** Quick settings tile: Ringer mode **/
-public class RingerModeTile extends QSTile<RingerModeTile.IntState> {
-
-    private final AudioManager mAudioManager;
-
-    public RingerModeTile(Host host) {
-        super(host);
-        mAudioManager = (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE);
-    }
-
-    @Override
-    protected IntState newTileState() {
-        return new IntState();
-    }
-
-    @Override
-    public void setListening(boolean listening) {
-        if (listening) {
-            final IntentFilter filter = new IntentFilter(AudioManager.RINGER_MODE_CHANGED_ACTION);
-            mContext.registerReceiver(mReceiver, filter);
-        } else {
-            mContext.unregisterReceiver(mReceiver);
-        }
-    }
-
-    @Override
-    protected void handleClick() {
-        final int oldValue = (Integer) mState.value;
-        final int newValue =
-                oldValue == AudioManager.RINGER_MODE_NORMAL ? AudioManager.RINGER_MODE_VIBRATE
-              : oldValue == AudioManager.RINGER_MODE_VIBRATE ? AudioManager.RINGER_MODE_SILENT
-              : AudioManager.RINGER_MODE_NORMAL;
-
-        mAudioManager.setRingerMode(newValue);
-    }
-
-    @Override
-    protected void handleUpdateState(IntState state, Object arg) {
-        final int ringerMode = mAudioManager.getRingerMode();
-        state.visible = true;
-        state.value = ringerMode;
-        if (ringerMode == AudioManager.RINGER_MODE_VIBRATE) {
-            state.iconId = R.drawable.ic_qs_ringer_vibrate;
-            state.label = "Vibrate";
-        } else if (ringerMode == AudioManager.RINGER_MODE_SILENT) {
-            state.iconId = R.drawable.ic_qs_ringer_silent;
-            state.label = "Silent";
-        } else {
-            state.iconId = R.drawable.ic_qs_ringer_audible;
-            state.label = "Audible";
-        }
-    }
-
-    private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
-        @Override
-        public void onReceive(Context context, Intent intent) {
-            if (AudioManager.RINGER_MODE_CHANGED_ACTION.equals(intent.getAction())) {
-                refreshState();
-            }
-        }
-    };
-
-    public static class IntState extends QSTile.State {
-        public int value;
-
-        @Override
-        public boolean copyTo(State other) {
-            final IntState o = (IntState) other;
-            final boolean changed = o.value != value;
-            o.value = value;
-            return super.copyTo(other) || changed;
-        }
-
-        @Override
-        protected StringBuilder toStringBuilder() {
-            final StringBuilder rt = super.toStringBuilder();
-            rt.insert(rt.length() - 1, ",value=" + value);
-            return rt;
-        }
-    }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/ZenModeDetail.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/ZenModeDetail.java
deleted file mode 100644
index f30f791..0000000
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/ZenModeDetail.java
+++ /dev/null
@@ -1,273 +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.qs.tiles;
-
-import android.content.Context;
-import android.content.Intent;
-import android.net.Uri;
-import android.os.Handler;
-import android.os.Looper;
-import android.os.Message;
-import android.provider.Settings;
-import android.service.notification.Condition;
-import android.util.AttributeSet;
-import android.view.ContextThemeWrapper;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.ViewGroup;
-import android.widget.ArrayAdapter;
-import android.widget.CompoundButton;
-import android.widget.CompoundButton.OnCheckedChangeListener;
-import android.widget.ImageView;
-import android.widget.ListView;
-import android.widget.RadioButton;
-import android.widget.RelativeLayout;
-import android.widget.Switch;
-import android.widget.TextView;
-
-import com.android.systemui.R;
-import com.android.systemui.qs.QSTile;
-import com.android.systemui.statusbar.policy.ZenModeController;
-
-import java.util.HashSet;
-
-/** Quick settings control panel: Zen mode **/
-public class ZenModeDetail extends RelativeLayout {
-    private static final String TAG = "ZenModeDetail";
-    private static final Intent ZEN_SETTINGS = new Intent(Settings.ACTION_ZEN_MODE_SETTINGS);
-    private static final int[] MINUTES = new int[] { 15, 30, 45, 60, 120, 180, 240, 480 };
-
-    private final H mHandler = new H();
-
-    private int mMinutesIndex = 3;
-    private Context mContext;
-    private ZenModeTile mTile;
-    private QSTile.Host mHost;
-    private ZenModeController mController;
-
-    private Switch mSwitch;
-    private ConditionAdapter mAdapter;
-
-    public ZenModeDetail(Context context, AttributeSet attrs) {
-        super(context, attrs);
-    }
-
-    public void init(ZenModeTile tile) {
-        mTile = tile;
-        mHost = mTile.getHost();
-        mContext = getContext();
-        mController = mHost.getZenModeController();
-
-        final ImageView close = (ImageView) findViewById(android.R.id.button1);
-        close.setOnClickListener(new View.OnClickListener() {
-            @Override
-            public void onClick(View v) {
-                mTile.showDetail(false);
-            }
-        });
-        mSwitch = (Switch) findViewById(android.R.id.checkbox);
-        mSwitch.setOnCheckedChangeListener(new OnCheckedChangeListener() {
-            @Override
-            public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
-                mController.setZen(isChecked);
-            }
-        });
-        mSwitch.setOnClickListener(new View.OnClickListener() {
-            @Override
-            public void onClick(View v) {
-                final boolean isChecked = mSwitch.isChecked();
-                mController.setZen(isChecked);
-                if (!isChecked) {
-                    mTile.showDetail(false);
-                }
-            }
-        });
-
-        final View moreSettings = findViewById(android.R.id.button2);
-        moreSettings.setOnClickListener(new View.OnClickListener() {
-            @Override
-            public void onClick(View v) {
-                mHost.startSettingsActivity(ZEN_SETTINGS);
-                mTile.showDetail(false);
-            }
-        });
-        final ListView conditions = (ListView) findViewById(android.R.id.content);
-        mAdapter = new ConditionAdapter(mContext);
-        conditions.setAdapter(mAdapter);
-        mAdapter.add(updateTimeCondition());
-
-        updateZen(mController.isZen());
-    }
-
-    private Condition updateTimeCondition() {
-        final int minutes = MINUTES[mMinutesIndex];
-        final long millis = System.currentTimeMillis() + minutes * 60 * 1000;
-        final Uri id = new Uri.Builder().scheme(Condition.SCHEME).authority("android")
-                .appendPath("countdown").appendPath(Long.toString(millis)).build();
-        final int num = minutes < 60 ? minutes : minutes / 60;
-        final String units = minutes < 60 ? "minutes" : minutes == 60 ? "hour" : "hours";
-        return new Condition(id, "For " + num + " " + units, "", "", 0, Condition.STATE_TRUE,
-                Condition.FLAG_RELEVANT_NOW);
-    }
-
-    private void editTimeCondition(int delta) {
-        final int i = mMinutesIndex + delta;
-        if (i < 0 || i >= MINUTES.length) return;
-        mMinutesIndex = i;
-        mAdapter.remove(mAdapter.getItem(0));
-        final Condition c = updateTimeCondition();
-        mAdapter.insert(c, 0);
-        select(c);
-    }
-
-    private void select(Condition condition) {
-        mController.select(condition);
-    }
-
-    private void updateZen(boolean zen) {
-        mHandler.obtainMessage(H.UPDATE_ZEN, zen ? 1 : 0, 0).sendToTarget();
-    }
-
-    private void updateConditions(Condition[] conditions) {
-        if (conditions == null) return;
-        mHandler.obtainMessage(H.UPDATE_CONDITIONS, conditions).sendToTarget();
-    }
-
-    private void handleUpdateZen(boolean zen) {
-        mSwitch.setChecked(zen);
-    }
-
-    private void handleUpdateConditions(Condition[] conditions) {
-        for (int i = mAdapter.getCount() - 1; i > 0; i--) {
-            mAdapter.remove(mAdapter.getItem(i));
-        }
-        for (Condition condition : conditions) {
-            mAdapter.add(condition);
-        }
-    }
-
-    @Override
-    protected void onAttachedToWindow() {
-        super.onAttachedToWindow();
-        mController.addCallback(mCallback);
-        mController.requestConditions(true);
-    }
-
-    @Override
-    protected void onDetachedFromWindow() {
-        super.onDetachedFromWindow();
-        mController.removeCallback(mCallback);
-        mController.requestConditions(false);
-    }
-
-    private final class H extends Handler {
-        private static final int UPDATE_ZEN = 1;
-        private static final int UPDATE_CONDITIONS = 2;
-
-        public H() {
-            super(Looper.getMainLooper());
-        }
-
-        @Override
-        public void handleMessage(Message msg) {
-            if (msg.what == UPDATE_ZEN) {
-                handleUpdateZen(msg.arg1 == 1);
-            } else if (msg.what == UPDATE_CONDITIONS) {
-                handleUpdateConditions((Condition[])msg.obj);
-            }
-        }
-    }
-
-    private final ZenModeController.Callback mCallback = new ZenModeController.Callback() {
-        @Override
-        public void onZenChanged(boolean zen) {
-            updateZen(zen);
-        }
-        public void onConditionsChanged(Condition[] conditions) {
-            updateConditions(conditions);
-        }
-    };
-
-    private final class ConditionAdapter extends ArrayAdapter<Condition> {
-        private final LayoutInflater mInflater;
-        private final HashSet<RadioButton> mRadioButtons = new HashSet<RadioButton>();
-
-        public ConditionAdapter(Context context) {
-            super(context, 0);
-            mInflater = LayoutInflater.from(new ContextThemeWrapper(context, R.style.QSWhiteTheme));
-        }
-
-        @Override
-        public View getView(int position, View convertView, ViewGroup parent) {
-            final Condition condition = getItem(position);
-            final boolean enabled = condition.state == Condition.STATE_TRUE;
-
-            final View row = convertView != null ? convertView : mInflater
-                    .inflate(R.layout.qs_zen_mode_detail_condition, parent, false);
-            final RadioButton rb = (RadioButton) row.findViewById(android.R.id.checkbox);
-            mRadioButtons.add(rb);
-            rb.setEnabled(enabled);
-            rb.setOnCheckedChangeListener(new OnCheckedChangeListener() {
-                @Override
-                public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
-                    if (isChecked) {
-                        for (RadioButton otherButton : mRadioButtons) {
-                            if (otherButton == rb) continue;
-                            otherButton.setChecked(false);
-                        }
-                        select(condition);
-                    }
-                }
-            });
-            final TextView title = (TextView) row.findViewById(android.R.id.title);
-            title.setText(condition.summary);
-            title.setEnabled(enabled);
-            title.setAlpha(enabled ? 1 : .5f);
-            final ImageView button1 = (ImageView) row.findViewById(android.R.id.button1);
-            button1.setOnClickListener(new OnClickListener() {
-                @Override
-                public void onClick(View v) {
-                    rb.setChecked(true);
-                    editTimeCondition(-1);
-                }
-            });
-
-            final ImageView button2 = (ImageView) row.findViewById(android.R.id.button2);
-            button2.setOnClickListener(new OnClickListener() {
-                @Override
-                public void onClick(View v) {
-                    rb.setChecked(true);
-                    editTimeCondition(1);
-                }
-            });
-            title.setOnClickListener(new OnClickListener() {
-                @Override
-                public void onClick(View v) {
-                    rb.setChecked(true);
-                }
-            });
-            if (position != 0) {
-                button1.setVisibility(View.GONE);
-                button2.setVisibility(View.GONE);
-            }
-            if (position == 0 && mRadioButtons.size() == 1) {
-                rb.setChecked(true);
-            }
-            return row;
-        }
-    }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/ZenModeTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/ZenModeTile.java
deleted file mode 100644
index bfa9c19..0000000
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/ZenModeTile.java
+++ /dev/null
@@ -1,87 +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.qs.tiles;
-
-import android.content.Context;
-import android.util.Log;
-import android.view.ContextThemeWrapper;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.ViewGroup;
-
-import com.android.systemui.R;
-import com.android.systemui.qs.QSTile;
-import com.android.systemui.statusbar.policy.ZenModeController;
-
-/** Quick settings tile: Zen mode **/
-public class ZenModeTile extends QSTile<QSTile.BooleanState> {
-    private final ZenModeController mController;
-
-    public ZenModeTile(Host host) {
-        super(host);
-        mController = host.getZenModeController();
-    }
-
-    @Override
-    public View createDetailView(Context context, ViewGroup root) {
-        final Context themedContext = new ContextThemeWrapper(mContext, R.style.QSAccentTheme);
-        final ZenModeDetail v = (ZenModeDetail) LayoutInflater.from(themedContext)
-                .inflate(R.layout.qs_zen_mode_detail, root, false);
-        v.init(this);
-        return v;
-    }
-
-    @Override
-    protected BooleanState newTileState() {
-        return new BooleanState();
-    }
-
-    @Override
-    public void setListening(boolean listening) {
-        if (listening) {
-            mController.addCallback(mCallback);
-        } else {
-            mController.removeCallback(mCallback);
-        }
-    }
-
-    @Override
-    protected void handleClick() {
-        final boolean newZen = !mState.value;
-        mController.setZen(newZen);
-        if (newZen) {
-            showDetail(true);
-        }
-    }
-
-    @Override
-    protected void handleUpdateState(BooleanState state, Object arg) {
-        final boolean zen = arg instanceof Boolean ? (Boolean)arg : mController.isZen();
-        state.value = zen;
-        state.visible = true;
-        state.iconId = zen ? R.drawable.ic_qs_zen_on : R.drawable.ic_qs_zen_off;
-        state.label = mContext.getString(R.string.zen_mode_title);
-    }
-
-    private final ZenModeController.Callback mCallback = new ZenModeController.Callback() {
-        @Override
-        public void onZenChanged(boolean zen) {
-            if (DEBUG) Log.d(TAG, "onZenChanged " + zen);
-            refreshState(zen);
-        }
-    };
-}
diff --git a/packages/SystemUI/src/com/android/systemui/recent/RecentsHorizontalScrollView.java b/packages/SystemUI/src/com/android/systemui/recent/RecentsHorizontalScrollView.java
index 0759b8e..72a3341 100644
--- a/packages/SystemUI/src/com/android/systemui/recent/RecentsHorizontalScrollView.java
+++ b/packages/SystemUI/src/com/android/systemui/recent/RecentsHorizontalScrollView.java
@@ -64,7 +64,7 @@
     }
 
     public void setMinSwipeAlpha(float minAlpha) {
-        mSwipeHelper.setMinAlpha(minAlpha);
+        mSwipeHelper.setMinSwipeProgress(minAlpha);
     }
 
     private int scrollPositionOfMostRecent() {
@@ -221,6 +221,11 @@
     public void onChildSnappedBack(View animView) {
     }
 
+    @Override
+    public boolean updateSwipeProgress(View animView, boolean dismissable, float swipeProgress) {
+        return false;
+    }
+
     public View getChildAtPosition(MotionEvent ev) {
         final float x = ev.getX() + getScrollX();
         final float y = ev.getY() + getScrollY();
diff --git a/packages/SystemUI/src/com/android/systemui/recent/RecentsVerticalScrollView.java b/packages/SystemUI/src/com/android/systemui/recent/RecentsVerticalScrollView.java
index c2dde6a..1213375 100644
--- a/packages/SystemUI/src/com/android/systemui/recent/RecentsVerticalScrollView.java
+++ b/packages/SystemUI/src/com/android/systemui/recent/RecentsVerticalScrollView.java
@@ -65,7 +65,7 @@
     }
 
     public void setMinSwipeAlpha(float minAlpha) {
-        mSwipeHelper.setMinAlpha(minAlpha);
+        mSwipeHelper.setMinSwipeProgress(minAlpha);
     }
 
     private int scrollPositionOfMostRecent() {
@@ -229,6 +229,11 @@
     public void onChildSnappedBack(View animView) {
     }
 
+    @Override
+    public boolean updateSwipeProgress(View animView, boolean dismissable, float swipeProgress) {
+        return false;
+    }
+
     public View getChildAtPosition(MotionEvent ev) {
         final float x = ev.getX() + getScrollX();
         final float y = ev.getY() + getScrollY();
diff --git a/packages/SystemUI/src/com/android/systemui/recents/AlternateRecentsComponent.java b/packages/SystemUI/src/com/android/systemui/recents/AlternateRecentsComponent.java
index ffd64d4..ca9bb94 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/AlternateRecentsComponent.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/AlternateRecentsComponent.java
@@ -35,6 +35,7 @@
 import android.os.Message;
 import android.os.Messenger;
 import android.os.RemoteException;
+import android.os.SystemClock;
 import android.os.UserHandle;
 import android.util.DisplayMetrics;
 import android.view.Display;
@@ -49,7 +50,7 @@
 import java.util.concurrent.atomic.AtomicBoolean;
 
 /** A proxy implementation for the recents component */
-public class AlternateRecentsComponent {
+public class AlternateRecentsComponent implements ActivityOptions.OnAnimationStartedListener {
 
     /** A handler for messages from the recents implementation */
     class RecentsMessageHandler extends Handler {
@@ -62,12 +63,15 @@
                 Bundle replyData = msg.getData().getParcelable(KEY_CONFIGURATION_DATA);
                 mSingleCountFirstTaskRect = replyData.getParcelable(KEY_SINGLE_TASK_STACK_RECT);
                 mSingleCountFirstTaskRect.offset(0, (int) statusBarHeight);
+                mTwoCountFirstTaskRect = replyData.getParcelable(KEY_TWO_TASK_STACK_RECT);
+                mTwoCountFirstTaskRect.offset(0, (int) statusBarHeight);
                 mMultipleCountFirstTaskRect = replyData.getParcelable(KEY_MULTIPLE_TASK_STACK_RECT);
                 mMultipleCountFirstTaskRect.offset(0, (int) statusBarHeight);
                 if (Console.Enabled) {
                     Console.log(Constants.Log.App.RecentsComponent,
                             "[RecentsComponent|RecentsMessageHandler|handleMessage]",
                             "singleTaskRect: " + mSingleCountFirstTaskRect +
+                            " twoTaskRect: " + mTwoCountFirstTaskRect +
                             " multipleTaskRect: " + mMultipleCountFirstTaskRect);
                 }
 
@@ -123,6 +127,7 @@
     final public static int MSG_SHOW_RECENTS = 4;
     final public static int MSG_HIDE_RECENTS = 5;
     final public static int MSG_TOGGLE_RECENTS = 6;
+    final public static int MSG_START_ENTER_ANIMATION = 7;
 
     final public static String EXTRA_ANIMATING_WITH_THUMBNAIL = "recents.animatingWithThumbnail";
     final public static String EXTRA_FROM_ALT_TAB = "recents.triggeredFromAltTab";
@@ -130,6 +135,7 @@
     final public static String KEY_WINDOW_RECT = "recents.windowRect";
     final public static String KEY_SYSTEM_INSETS = "recents.systemInsets";
     final public static String KEY_SINGLE_TASK_STACK_RECT = "recents.singleCountTaskRect";
+    final public static String KEY_TWO_TASK_STACK_RECT = "recents.twoCountTaskRect";
     final public static String KEY_MULTIPLE_TASK_STACK_RECT = "recents.multipleCountTaskRect";
 
 
@@ -155,6 +161,7 @@
     boolean mTriggeredFromAltTab;
 
     Rect mSingleCountFirstTaskRect = new Rect();
+    Rect mTwoCountFirstTaskRect = new Rect();
     Rect mMultipleCountFirstTaskRect = new Rect();
     long mLastToggleTime;
 
@@ -261,8 +268,10 @@
     /** Returns whether we have valid task rects to animate to. */
     boolean hasValidTaskRects() {
         return mSingleCountFirstTaskRect != null && mSingleCountFirstTaskRect.width() > 0 &&
-                mSingleCountFirstTaskRect.height() > 0 && mMultipleCountFirstTaskRect != null &&
-                mMultipleCountFirstTaskRect.width() > 0 && mMultipleCountFirstTaskRect.height() > 0;
+                mSingleCountFirstTaskRect.height() > 0 && mTwoCountFirstTaskRect != null &&
+                mTwoCountFirstTaskRect.width() > 0 && mTwoCountFirstTaskRect.height() > 0 &&
+                mMultipleCountFirstTaskRect != null && mMultipleCountFirstTaskRect.width() > 0 &&
+                mMultipleCountFirstTaskRect.height() > 0;
     }
 
     /** Updates each of the task animation rects. */
@@ -303,8 +312,8 @@
         return null;
     }
 
-    /** Returns whether there is are multiple recents tasks */
-    boolean hasMultipleRecentsTask(List<ActivityManager.RecentTaskInfo> tasks) {
+    /** Returns the proper rect to use for the animation, given the number of tasks. */
+    Rect getAnimationTaskRect(List<ActivityManager.RecentTaskInfo> tasks) {
         // NOTE: Currently there's no method to get the number of non-home tasks, so we have to
         // compute this ourselves
         SystemServicesProxy ssp = mSystemServicesProxy;
@@ -318,7 +327,13 @@
                 continue;
             }
         }
-        return (tasks.size() > 1);
+        if (tasks.size() <= 1) {
+            return mSingleCountFirstTaskRect;
+        } else if (tasks.size() <= 2) {
+            return mTwoCountFirstTaskRect;
+        } else {
+            return mMultipleCountFirstTaskRect;
+        }
     }
 
     /** Converts from the device rotation to the degree */
@@ -392,7 +407,7 @@
         }
 
         return ActivityOptions.makeThumbnailScaleDownAnimation(mStatusBarView, thumbnail,
-                taskRect.left, taskRect.top, null);
+                taskRect.left, taskRect.top, this);
     }
 
     /** Returns whether the recents is currently running */
@@ -472,9 +487,8 @@
         // which can differ depending on the number of items in the list.
         SystemServicesProxy ssp = mSystemServicesProxy;
         List<ActivityManager.RecentTaskInfo> recentTasks =
-                ssp.getRecentTasks(2, UserHandle.CURRENT.getIdentifier());
-        Rect taskRect = hasMultipleRecentsTask(recentTasks) ? mMultipleCountFirstTaskRect :
-                mSingleCountFirstTaskRect;
+                ssp.getRecentTasks(3, UserHandle.CURRENT.getIdentifier());
+        Rect taskRect = getAnimationTaskRect(recentTasks);
         boolean useThumbnailTransition = !isTopTaskHome &&
                 hasValidTaskRects();
 
@@ -517,4 +531,18 @@
             mContext.startActivityAsUser(intent, new UserHandle(UserHandle.USER_CURRENT));
         }
     }
+
+
+    /**** OnAnimationStartedListener Implementation ****/
+
+    @Override
+    public void onAnimationStarted() {
+        // Notify recents to start the enter animation
+        try {
+            Message msg = Message.obtain(null, MSG_START_ENTER_ANIMATION, 0, 0);
+            mService.send(msg);
+        } catch (RemoteException re) {
+            re.printStackTrace();
+        }
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/recents/Constants.java b/packages/SystemUI/src/com/android/systemui/recents/Constants.java
index 57957a8..76e88a5 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/Constants.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/Constants.java
@@ -36,7 +36,7 @@
             // Enables the search bar layout
             public static final boolean EnableSearchLayout = true;
             // Enables the dynamic shadows behind each task
-            public static final boolean EnableShadows = false;
+            public static final boolean EnableShadows = true;
             // This disables the bitmap and icon caches
             public static final boolean DisableBackgroundCache = false;
             // For debugging, this enables us to create mock recents tasks
@@ -101,8 +101,6 @@
             public static final int TaskStackOverscrollRange = 150;
             public static final int FilterStartDelay = 25;
 
-            // The padding will be applied to the smallest dimension, and then applied to all sides
-            public static final float StackPaddingPct = 0.1f;
             // The overlap height relative to the task height
             public static final float StackOverlapPct = 0.65f;
             // The height of the peek space relative to the stack height
@@ -113,4 +111,4 @@
             public static final int StackPeekNumCards = 3;
         }
     }
-}
\ No newline at end of file
+}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java
index befe8b4..df387c1 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java
@@ -98,6 +98,9 @@
                     // If there are no filtered stacks, dismiss recents and launch the first task
                     dismissRecentsIfVisible();
                 }
+            } else if (action.equals(RecentsService.ACTION_START_ENTER_ANIMATION)) {
+                // Try and start the enter animation
+                mRecentsView.startOnEnterAnimation();
             }
         }
     };
@@ -345,6 +348,7 @@
         IntentFilter filter = new IntentFilter();
         filter.addAction(RecentsService.ACTION_HIDE_RECENTS_ACTIVITY);
         filter.addAction(RecentsService.ACTION_TOGGLE_RECENTS_ACTIVITY);
+        filter.addAction(RecentsService.ACTION_START_ENTER_ANIMATION);
         registerReceiver(mServiceBroadcastReceiver, filter);
 
         // Register the broadcast receiver to handle messages when the screen is turned off
diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsConfiguration.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsConfiguration.java
index 03f7e36..6391685 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/RecentsConfiguration.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsConfiguration.java
@@ -46,26 +46,37 @@
 
     public float animationPxMovementPerSecond;
 
-    public Interpolator defaultBezierInterpolator;
+    public Interpolator fastOutSlowInInterpolator;
+    public Interpolator fastOutLinearInInterpolator;
+    public Interpolator linearOutSlowInInterpolator;
 
     public int filteringCurrentViewsMinAnimDuration;
     public int filteringNewViewsMinAnimDuration;
-    public int taskBarEnterAnimDuration;
-    public int taskBarExitAnimDuration;
+
     public int taskStackScrollDismissInfoPaneDistance;
     public int taskStackMaxDim;
+    public float taskStackWidthPaddingPct;
+    public int taskStackTopPaddingPx;
+
     public int taskViewInfoPaneAnimDuration;
     public int taskViewRemoveAnimDuration;
     public int taskViewRemoveAnimTranslationXPx;
     public int taskViewTranslationZMinPx;
     public int taskViewTranslationZIncrementPx;
+    public int taskViewShadowOutlineBottomInsetPx;
     public int taskViewRoundedCornerRadiusPx;
+    public int taskViewHighlightPx;
+
     public int searchBarSpaceHeightPx;
 
     public int taskBarViewDefaultBackgroundColor;
     public int taskBarViewDefaultTextColor;
     public int taskBarViewLightTextColor;
     public int taskBarViewDarkTextColor;
+    public int taskBarViewHighlightColor;
+
+    public int taskBarEnterAnimDuration;
+    public int taskBarExitAnimDuration;
 
     public boolean launchedFromAltTab;
     public boolean launchedWithThumbnailAnimation;
@@ -112,13 +123,16 @@
                 res.getInteger(R.integer.recents_filter_animate_current_views_min_duration);
         filteringNewViewsMinAnimDuration =
                 res.getInteger(R.integer.recents_filter_animate_new_views_min_duration);
-        taskBarEnterAnimDuration =
-                res.getInteger(R.integer.recents_animate_task_bar_enter_duration);
-        taskBarExitAnimDuration =
-                res.getInteger(R.integer.recents_animate_task_bar_exit_duration);
+
         taskStackScrollDismissInfoPaneDistance = res.getDimensionPixelSize(
                 R.dimen.recents_task_stack_scroll_dismiss_info_pane_distance);
         taskStackMaxDim = res.getInteger(R.integer.recents_max_task_stack_view_dim);
+
+        TypedValue widthPaddingPctValue = new TypedValue();
+        res.getValue(R.dimen.recents_stack_width_padding_percentage, widthPaddingPctValue, true);
+        taskStackWidthPaddingPct = widthPaddingPctValue.getFloat();
+        taskStackTopPaddingPx = res.getDimensionPixelSize(R.dimen.recents_stack_top_padding);
+
         taskViewInfoPaneAnimDuration =
                 res.getInteger(R.integer.recents_animate_task_view_info_pane_duration);
         taskViewRemoveAnimDuration =
@@ -127,9 +141,13 @@
                 res.getDimensionPixelSize(R.dimen.recents_task_view_remove_anim_translation_x);
         taskViewRoundedCornerRadiusPx =
                 res.getDimensionPixelSize(R.dimen.recents_task_view_rounded_corners_radius);
+        taskViewHighlightPx = res.getDimensionPixelSize(R.dimen.recents_task_view_highlight);
         taskViewTranslationZMinPx = res.getDimensionPixelSize(R.dimen.recents_task_view_z_min);
         taskViewTranslationZIncrementPx =
                 res.getDimensionPixelSize(R.dimen.recents_task_view_z_increment);
+        taskViewShadowOutlineBottomInsetPx =
+                res.getDimensionPixelSize(R.dimen.recents_task_view_shadow_outline_bottom_inset);
+
         searchBarSpaceHeightPx = res.getDimensionPixelSize(R.dimen.recents_search_bar_space_height);
 
         taskBarViewDefaultBackgroundColor =
@@ -140,9 +158,20 @@
                 res.getColor(R.color.recents_task_bar_light_text_color);
         taskBarViewDarkTextColor =
                 res.getColor(R.color.recents_task_bar_dark_text_color);
+        taskBarViewHighlightColor =
+                res.getColor(R.color.recents_task_bar_highlight_color);
 
-        defaultBezierInterpolator = AnimationUtils.loadInterpolator(context,
+        taskBarEnterAnimDuration =
+                res.getInteger(R.integer.recents_animate_task_bar_enter_duration);
+        taskBarExitAnimDuration =
+                res.getInteger(R.integer.recents_animate_task_bar_exit_duration);
+
+        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);
 
         // Check if the developer options are enabled
         ContentResolver cr = context.getContentResolver();
@@ -167,6 +196,13 @@
                 appWidgetId).apply();
     }
 
+    /** Called when the configuration has changed, and we want to reset any configuration specific
+     * members. */
+    public void updateOnConfigurationChange() {
+        launchedFromAltTab = false;
+        launchedWithThumbnailAnimation = false;
+    }
+
     /** Returns whether the search bar app widget exists */
     public boolean hasSearchBarAppWidget() {
         return searchBarAppWidgetId >= 0;
diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsService.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsService.java
index 4bdbb20..0c2c11d 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/RecentsService.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsService.java
@@ -55,7 +55,8 @@
 
         if (msg.what == AlternateRecentsComponent.MSG_UPDATE_FOR_CONFIGURATION) {
             RecentsTaskLoader.initialize(context);
-            RecentsConfiguration.reinitialize(context);
+            RecentsConfiguration config = RecentsConfiguration.reinitialize(context);
+            config.updateOnConfigurationChange();
 
             try {
                 Bundle data = msg.getData();
@@ -73,7 +74,6 @@
 
                 // Get the task stack and search bar bounds
                 Rect taskStackBounds = new Rect();
-                RecentsConfiguration config = RecentsConfiguration.getInstance();
                 config.getTaskStackBounds(windowRect.width(), windowRect.height(), taskStackBounds);
 
                 // Calculate the target task rect for when there is one task.
@@ -83,20 +83,29 @@
                 stack.addTask(new Task());
                 tsv.computeRects(taskStackBounds.width(), taskStackBounds.height() -
                         systemInsets.top - systemInsets.bottom, 0, 0);
-                tsv.boundScroll();
+                tsv.setStackScrollToInitialState();
                 transform = tsv.getStackTransform(0, tsv.getStackScroll());
                 transform.rect.offset(taskStackBounds.left, taskStackBounds.top);
                 replyData.putParcelable(AlternateRecentsComponent.KEY_SINGLE_TASK_STACK_RECT,
                         new Rect(transform.rect));
 
-                // Also calculate the target task rect when there are multiple tasks.
+                // Also calculate the target task rect when there are two tasks.
                 stack.addTask(new Task());
                 tsv.computeRects(taskStackBounds.width(), taskStackBounds.height() -
                         systemInsets.top - systemInsets.bottom, 0, 0);
-                tsv.setStackScrollRaw(Integer.MAX_VALUE);
-                tsv.boundScroll();
+                tsv.setStackScrollToInitialState();
                 transform = tsv.getStackTransform(1, tsv.getStackScroll());
                 transform.rect.offset(taskStackBounds.left, taskStackBounds.top);
+                replyData.putParcelable(AlternateRecentsComponent.KEY_TWO_TASK_STACK_RECT,
+                        new Rect(transform.rect));
+
+                // Also calculate the target task rect when there are two tasks.
+                stack.addTask(new Task());
+                tsv.computeRects(taskStackBounds.width(), taskStackBounds.height() -
+                        systemInsets.top - systemInsets.bottom, 0, 0);
+                tsv.setStackScrollToInitialState();
+                transform = tsv.getStackTransform(2, tsv.getStackScroll());
+                transform.rect.offset(taskStackBounds.left, taskStackBounds.top);
                 replyData.putParcelable(AlternateRecentsComponent.KEY_MULTIPLE_TASK_STACK_RECT,
                         new Rect(transform.rect));
 
@@ -127,6 +136,11 @@
                     Constants.Log.App.TimeRecentsStartupKey, "receivedToggleRecents");
             Console.logTraceTime(Constants.Log.App.TimeRecentsLaunchTask,
                     Constants.Log.App.TimeRecentsLaunchKey, "receivedToggleRecents");
+        } else if (msg.what == AlternateRecentsComponent.MSG_START_ENTER_ANIMATION) {
+            // Send a broadcast to start the enter animation
+            Intent intent = new Intent(RecentsService.ACTION_START_ENTER_ANIMATION);
+            intent.setPackage(context.getPackageName());
+            context.sendBroadcast(intent);
         }
     }
 }
@@ -135,6 +149,7 @@
 public class RecentsService extends Service {
     final static String ACTION_HIDE_RECENTS_ACTIVITY = "action_hide_recents_activity";
     final static String ACTION_TOGGLE_RECENTS_ACTIVITY = "action_toggle_recents_activity";
+    final static String ACTION_START_ENTER_ANIMATION = "action_start_enter_animation";
     final static String EXTRA_TRIGGERED_FROM_ALT_TAB = "extra_triggered_from_alt_tab";
 
     Messenger mSystemUIMessenger = new Messenger(new SystemUIMessageHandler(this));
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 cad9ce5..6005275 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java
@@ -158,6 +158,18 @@
         return false;
     }
 
+    /** Requests all task stacks to start their enter-recents animation */
+    public void startOnEnterAnimation() {
+        int childCount = getChildCount();
+        for (int i = 0; i < childCount; i++) {
+            View child = getChildAt(i);
+            if (child instanceof TaskStackView) {
+                TaskStackView stackView = (TaskStackView) child;
+                stackView.startOnEnterAnimation();
+            }
+        }
+    }
+
     /** Adds the search bar */
     public void setSearchBar(View searchBar) {
         // Create the search bar (and hide it if we have no recent tasks)
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/SwipeHelper.java b/packages/SystemUI/src/com/android/systemui/recents/views/SwipeHelper.java
index 3ee0545..cae6bd7 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/SwipeHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/SwipeHelper.java
@@ -278,6 +278,7 @@
                 if (FADE_OUT_DURING_SWIPE && canAnimViewBeDismissed) {
                     view.setAlpha(getAlphaForOffset(view));
                 }
+                mCallback.onSwipeChanged(mCurrView, view.getTranslationX());
             }
         });
         anim.addListener(new AnimatorListenerAdapter() {
@@ -313,6 +314,7 @@
                 if (mCurrView != null) {
                     float delta = getPos(ev) - mInitialTouchPos;
                     setSwipeAmount(delta);
+                    mCallback.onSwipeChanged(mCurrView, delta);
                 }
                 break;
             case MotionEvent.ACTION_UP:
@@ -393,6 +395,8 @@
 
         void onBeginDrag(View v);
 
+        void onSwipeChanged(View v, float delta);
+
         void onChildDismissed(View v);
 
         void onSnapBackCompleted(View v);
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 07caa1b..c10ddd1 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskBarView.java
@@ -16,9 +16,12 @@
 
 package com.android.systemui.recents.views;
 
-import android.animation.ValueAnimator;
 import android.content.Context;
 import android.content.res.Resources;
+import android.graphics.Canvas;
+import android.graphics.Paint;
+import android.graphics.PorterDuff;
+import android.graphics.PorterDuffXfermode;
 import android.graphics.drawable.Drawable;
 import android.util.AttributeSet;
 import android.widget.FrameLayout;
@@ -42,6 +45,8 @@
     Drawable mLightDismissDrawable;
     Drawable mDarkDismissDrawable;
 
+    static Paint sHighlightPaint;
+
     public TaskBarView(Context context) {
         this(context, null);
     }
@@ -56,9 +61,23 @@
 
     public TaskBarView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
         super(context, attrs, defStyleAttr, defStyleRes);
+        setWillNotDraw(false);
+
+        // Load the dismiss resources
         Resources res = context.getResources();
         mLightDismissDrawable = res.getDrawable(R.drawable.recents_dismiss_light);
         mDarkDismissDrawable = res.getDrawable(R.drawable.recents_dismiss_dark);
+
+        // Configure the highlight paint
+        if (sHighlightPaint == null) {
+            RecentsConfiguration config = RecentsConfiguration.getInstance();
+            sHighlightPaint = new Paint();
+            sHighlightPaint.setStyle(Paint.Style.STROKE);
+            sHighlightPaint.setStrokeWidth(config.taskViewHighlightPx);
+            sHighlightPaint.setColor(config.taskBarViewHighlightColor);
+            sHighlightPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.ADD));
+            sHighlightPaint.setAntiAlias(true);
+        }
     }
 
     @Override
@@ -69,6 +88,17 @@
         mDismissButton = (ImageView) findViewById(R.id.dismiss_task);
     }
 
+    @Override
+    protected void onDraw(Canvas canvas) {
+        RecentsConfiguration config = RecentsConfiguration.getInstance();
+
+        // Draw the highlight at the top edge (but put the bottom edge just out of view)
+        float offset = config.taskViewHighlightPx / 2f;
+        float radius = config.taskViewRoundedCornerRadiusPx;
+        canvas.drawRoundRect(-offset, 0f, (float) getMeasuredWidth() + offset,
+                getMeasuredHeight() + radius, radius, radius, sHighlightPaint);
+    }
+
     /** Synchronizes this bar view's properties with the task's transform */
     void updateViewPropertiesToTaskTransform(TaskViewTransform animateFromTransform,
                                              TaskViewTransform toTransform, int duration) {
@@ -81,7 +111,7 @@
                     .alpha(toTransform.dismissAlpha)
                     .setStartDelay(0)
                     .setDuration(duration)
-                    .setInterpolator(config.defaultBezierInterpolator)
+                    .setInterpolator(config.fastOutSlowInInterpolator)
                     .withLayer()
                     .start();
         } else {
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 2b08b19..053f122 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java
@@ -23,7 +23,6 @@
 import android.app.Activity;
 import android.content.ComponentName;
 import android.content.Context;
-import android.content.Intent;
 import android.graphics.Canvas;
 import android.graphics.Rect;
 import android.graphics.Region;
@@ -88,6 +87,7 @@
     int mStackViewsAnimationDuration;
     boolean mStackViewsDirty = true;
     boolean mAwaitingFirstLayout = true;
+    boolean mStartEnterAnimationRequestedAfterLayout;
     int[] mTmpVisibleRange = new int[2];
     Rect mTmpRect = new Rect();
     Rect mTmpRect2 = new Rect();
@@ -172,7 +172,7 @@
         }
 
         // Set the alphas
-        transform.dismissAlpha = Math.max(-1f, Math.min(0f, t)) + 1f;
+        transform.dismissAlpha = Math.max(-1f, Math.min(0f, t + 1)) + 1f;
 
         // Update the rect and visibility
         transform.rect.set(mTaskRect);
@@ -300,6 +300,15 @@
     public void setStackScrollRaw(int value) {
         mStackScroll = value;
     }
+    /** Sets the current stack scroll to the initial state when you first enter recents */
+    public void setStackScrollToInitialState() {
+        if (mStack.getTaskCount() > 2) {
+            int initialScroll = mMaxScroll - mTaskRect.height() / 2;
+            setStackScroll(initialScroll);
+        } else {
+            setStackScroll(mMaxScroll);
+        }
+    }
 
     /**
      * Returns the scroll to such that the task transform at that index will have t=0. (If the scroll
@@ -344,7 +353,7 @@
         mScrollAnimator = ObjectAnimator.ofInt(this, "stackScroll", curScroll, newScroll);
         mScrollAnimator.setDuration(Utilities.calculateTranslationAnimationDuration(newScroll -
                 curScroll, 250));
-        mScrollAnimator.setInterpolator(RecentsConfiguration.getInstance().defaultBezierInterpolator);
+        mScrollAnimator.setInterpolator(RecentsConfiguration.getInstance().fastOutSlowInInterpolator);
         mScrollAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
             @Override
             public void onAnimationUpdate(ValueAnimator animation) {
@@ -636,6 +645,7 @@
         // Note: We let the stack view be the full height because we want the cards to go under the
         //       navigation bar if possible.  However, the stack rects which we use to calculate
         //       max scroll, etc. need to take the nav bar into account
+        RecentsConfiguration config = RecentsConfiguration.getInstance();
 
         // Compute the stack rects
         mRect.set(0, 0, width, height);
@@ -643,23 +653,21 @@
         mStackRect.left += insetLeft;
         mStackRect.bottom -= insetBottom;
 
-        int smallestDimension = Math.min(width, height);
-        int padding = (int) (Constants.Values.TaskStackView.StackPaddingPct * smallestDimension / 2f);
+        int widthPadding = (int) (config.taskStackWidthPaddingPct * mStackRect.width());
+        int heightPadding = config.taskStackTopPaddingPx;
         if (Constants.DebugFlags.App.EnableSearchLayout) {
-            mStackRect.top += padding;
-            mStackRect.left += padding;
-            mStackRect.right -= padding;
-            mStackRect.bottom -= padding;
+            mStackRect.top += heightPadding;
+            mStackRect.left += widthPadding;
+            mStackRect.right -= widthPadding;
+            mStackRect.bottom -= heightPadding;
         } else {
-            mStackRect.inset(padding, padding);
+            mStackRect.inset(widthPadding, heightPadding);
         }
         mStackRectSansPeek.set(mStackRect);
         mStackRectSansPeek.top += Constants.Values.TaskStackView.StackPeekHeightPct * mStackRect.height();
 
         // Compute the task rect
-        int minHeight = (int) (mStackRect.height() -
-                (Constants.Values.TaskStackView.StackPeekHeightPct * mStackRect.height()));
-        int size = Math.min(minHeight, Math.min(mStackRect.width(), mStackRect.height()));
+        int size = mStackRect.width();
         int left = mStackRect.left + (mStackRect.width() - size) / 2;
         mTaskRect.set(left, mStackRectSansPeek.top,
                 left + size, mStackRectSansPeek.top + size);
@@ -700,22 +708,9 @@
         // If this is the first layout, then scroll to the front of the stack and synchronize the
         // stack views immediately
         if (mAwaitingFirstLayout) {
-            setStackScroll(mMaxScroll);
+            setStackScrollToInitialState();
             requestSynchronizeStackViewsWithModel();
             synchronizeStackViewsWithModel();
-
-            // Update the focused task index to be the next item to the top task
-            if (config.launchedFromAltTab) {
-                focusTask(Math.max(0, mStack.getTaskCount() - 2), false);
-            }
-
-            // Animate the task bar of the first task view
-            if (config.launchedWithThumbnailAnimation) {
-                TaskView tv = (TaskView) getChildAt(getChildCount() - 1);
-                if (tv != null) {
-                    tv.animateOnEnterRecents();
-                }
-            }
         }
 
         // Measure each of the children
@@ -758,7 +753,47 @@
         }
 
         if (mAwaitingFirstLayout) {
+            RecentsConfiguration config = RecentsConfiguration.getInstance();
+
+            // Update the focused task index to be the next item to the top task
+            if (config.launchedFromAltTab) {
+                focusTask(Math.max(0, mStack.getTaskCount() - 2), false);
+            }
+
+            // Prepare the first view for its enter animation
+            if (config.launchedWithThumbnailAnimation) {
+                TaskView tv = (TaskView) getChildAt(getChildCount() - 1);
+                if (tv != null) {
+                    tv.prepareAnimateOnEnterRecents();
+                }
+            }
+
+            // Mark that we have completely the first layout
             mAwaitingFirstLayout = false;
+
+            // If the enter animation started already and we haven't completed a layout yet, do the
+            // enter animation now
+            if (mStartEnterAnimationRequestedAfterLayout) {
+                startOnEnterAnimation();
+            }
+        }
+    }
+
+    /** Requests this task stacks to start it's enter-recents animation */
+    public void startOnEnterAnimation() {
+        RecentsConfiguration config = RecentsConfiguration.getInstance();
+        if (!config.launchedWithThumbnailAnimation) return;
+
+        // If we are still waiting to layout, then just defer until then
+        if (mAwaitingFirstLayout) {
+            mStartEnterAnimationRequestedAfterLayout = true;
+            return;
+        }
+
+        // Animate the task bar of the first task view
+        TaskView tv = (TaskView) getChildAt(getChildCount() - 1);
+        if (tv != null) {
+            tv.animateOnEnterRecents();
         }
     }
 
@@ -1529,6 +1564,11 @@
     }
 
     @Override
+    public void onSwipeChanged(View v, float delta) {
+        // Do nothing
+    }
+
+    @Override
     public void onChildDismissed(View v) {
         TaskView tv = (TaskView) v;
         mSv.onTaskDismissed(tv);
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 8575661..ffa181d 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java
@@ -21,7 +21,6 @@
 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;
@@ -113,7 +112,8 @@
 
         // Update the outline
         Outline o = new Outline();
-        o.setRoundRect(0, 0, getMeasuredWidth(), getMeasuredHeight(), radius);
+        o.setRoundRect(0, 0, getMeasuredWidth(), getMeasuredHeight() -
+                config.taskViewShadowOutlineBottomInsetPx, radius);
         setOutline(o);
     }
 
@@ -167,7 +167,7 @@
                     .scaleY(toTransform.scale)
                     .alpha(toTransform.alpha)
                     .setDuration(duration)
-                    .setInterpolator(config.defaultBezierInterpolator)
+                    .setInterpolator(config.fastOutSlowInInterpolator)
                     .withLayer()
                     .setUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
                         @Override
@@ -221,14 +221,21 @@
         fromTransform.alpha = 0f;
     }
 
+    /** Prepares this task view for the enter-recents animations.  This is called earlier in the
+     * first layout because the actual animation into recents may take a long time. */
+    public void prepareAnimateOnEnterRecents() {
+        mBarView.setVisibility(View.INVISIBLE);
+    }
+
     /** Animates this task view as it enters recents */
     public void animateOnEnterRecents() {
         RecentsConfiguration config = RecentsConfiguration.getInstance();
-        mBarView.setAlpha(0f);
+        mBarView.setVisibility(View.VISIBLE);
+        mBarView.setTranslationY(-mBarView.getMeasuredHeight());
         mBarView.animate()
-                .alpha(1f)
-                .setStartDelay(300)
-                .setInterpolator(config.defaultBezierInterpolator)
+                .translationY(0)
+                .setStartDelay(200)
+                .setInterpolator(config.fastOutSlowInInterpolator)
                 .setDuration(config.taskBarEnterAnimDuration)
                 .withLayer()
                 .start();
@@ -238,9 +245,9 @@
     public void animateOnLeavingRecents(final Runnable r) {
         RecentsConfiguration config = RecentsConfiguration.getInstance();
         mBarView.animate()
-            .alpha(0f)
+            .translationY(-mBarView.getMeasuredHeight())
             .setStartDelay(0)
-            .setInterpolator(config.defaultBezierInterpolator)
+            .setInterpolator(config.fastOutLinearInInterpolator)
             .setDuration(config.taskBarExitAnimDuration)
             .withLayer()
             .withEndAction(new Runnable() {
@@ -261,7 +268,7 @@
         animate().translationX(config.taskViewRemoveAnimTranslationXPx)
             .alpha(0f)
             .setStartDelay(0)
-            .setInterpolator(config.defaultBezierInterpolator)
+            .setInterpolator(config.fastOutSlowInInterpolator)
             .setDuration(config.taskViewRemoveAnimDuration)
             .withLayer()
             .withEndAction(new Runnable() {
diff --git a/packages/SystemUI/src/com/android/systemui/settings/BrightnessDialog.java b/packages/SystemUI/src/com/android/systemui/settings/BrightnessDialog.java
index 27881c4..65e1cc6 100644
--- a/packages/SystemUI/src/com/android/systemui/settings/BrightnessDialog.java
+++ b/packages/SystemUI/src/com/android/systemui/settings/BrightnessDialog.java
@@ -72,8 +72,7 @@
         window.setGravity(Gravity.TOP);
         WindowManager.LayoutParams lp = window.getAttributes();
         // Offset from the top
-        lp.y = getContext().getResources().getDimensionPixelOffset(
-                com.android.internal.R.dimen.volume_panel_top);
+        lp.y = getContext().getResources().getDimensionPixelOffset(R.dimen.volume_panel_top);
         lp.type = WindowManager.LayoutParams.TYPE_VOLUME_OVERLAY;
         lp.privateFlags |=
                 WindowManager.LayoutParams.PRIVATE_FLAG_SHOW_FOR_ALL_USERS;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/ActivatableNotificationView.java b/packages/SystemUI/src/com/android/systemui/statusbar/ActivatableNotificationView.java
index e3dac4a..dcd187c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/ActivatableNotificationView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/ActivatableNotificationView.java
@@ -149,6 +149,7 @@
         super.onFinishInflate();
         mBackgroundNormal = (NotificationBackgroundView) findViewById(R.id.backgroundNormal);
         mBackgroundDimmed = (NotificationBackgroundView) findViewById(R.id.backgroundDimmed);
+        updateBackground();
         updateBackgroundResources();
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/AlphaImageView.java b/packages/SystemUI/src/com/android/systemui/statusbar/AlphaImageView.java
new file mode 100644
index 0000000..06dc4e6
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/AlphaImageView.java
@@ -0,0 +1,48 @@
+/*
+ * 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.util.AttributeSet;
+import android.widget.ImageView;
+
+/**
+ * An ImageView which does not have overlapping renderings commands and therefore does not need a
+ * layer when alpha is changed.
+ */
+public class AlphaImageView extends ImageView {
+    public AlphaImageView(Context context) {
+        super(context);
+    }
+
+    public AlphaImageView(Context context, AttributeSet attrs) {
+        super(context, attrs);
+    }
+
+    public AlphaImageView(Context context, AttributeSet attrs, int defStyleAttr) {
+        super(context, attrs, defStyleAttr);
+    }
+
+    public AlphaImageView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
+        super(context, attrs, defStyleAttr, defStyleRes);
+    }
+
+    @Override
+    public boolean hasOverlappingRendering() {
+        return false;
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
index cda84cf..06cc476 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
@@ -48,6 +48,7 @@
 import android.service.dreams.DreamService;
 import android.service.dreams.IDreamManager;
 import android.service.notification.NotificationListenerService;
+import android.service.notification.NotificationListenerService.Ranking;
 import android.service.notification.StatusBarNotification;
 import android.text.TextUtils;
 import android.util.Log;
@@ -77,11 +78,13 @@
 import com.android.systemui.RecentsComponent;
 import com.android.systemui.SearchPanelView;
 import com.android.systemui.SystemUI;
+import com.android.systemui.statusbar.NotificationData.Entry;
 import com.android.systemui.statusbar.phone.KeyguardTouchDelegate;
 import com.android.systemui.statusbar.stack.NotificationStackScrollLayout;
 
 import java.util.ArrayList;
 import java.util.Arrays;
+import java.util.Collections;
 import java.util.Locale;
 
 import static com.android.keyguard.KeyguardHostView.OnDismissAction;
@@ -91,7 +94,7 @@
     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 = 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;
@@ -115,6 +118,9 @@
     public static final int EXPANDED_LEAVE_ALONE = -10000;
     public static final int EXPANDED_FULL_OPEN = -10001;
 
+    /** If true, delays dismissing the Keyguard until the ActivityManager calls back. */
+    protected static final boolean DELAY_DISMISS_TO_ACTIVITY_LAUNCH = false;
+
     protected CommandQueue mCommandQueue;
     protected IStatusBarService mBarService;
     protected H mHandler = createHandler();
@@ -195,7 +201,7 @@
                     mContext.getContentResolver(), Settings.Global.DEVICE_PROVISIONED, 0);
             if (provisioned != mDeviceProvisioned) {
                 mDeviceProvisioned = provisioned;
-                updateNotificationIcons();
+                updateNotifications();
             }
             final int mode = Settings.Global.getInt(mContext.getContentResolver(),
                     Settings.Global.ZEN_MODE, Settings.Global.ZEN_MODE_OFF);
@@ -213,7 +219,7 @@
             // so we just dump our cache ...
             mUsersAllowingPrivateNotifications.clear();
             // ... and refresh all the notifications
-            updateNotificationIcons();
+            updateNotifications();
         }
     };
 
@@ -226,7 +232,7 @@
             }
             final boolean isActivity = pendingIntent.isActivity();
             if (isActivity) {
-                startNotificationActivity(new OnDismissAction() {
+                dismissKeyguardThenExecute(new OnDismissAction() {
                     @Override
                     public boolean onDismiss() {
                         try {
@@ -248,7 +254,8 @@
                             animateCollapsePanels(CommandQueue.FLAG_EXCLUDE_NONE);
                             visibilityChanged(false);
                         }
-                        return handled; // Wait for activity start.
+                        // Wait for activity start.
+                        return handled && DELAY_DISMISS_TO_ACTIVITY_LAUNCH;
                     }
                 });
                 return true;
@@ -284,11 +291,12 @@
         public void onListenerConnected() {
             if (DEBUG) Log.d(TAG, "onListenerConnected");
             final StatusBarNotification[] notifications = getActiveNotifications();
+            final Ranking currentRanking = getCurrentRanking();
             mHandler.post(new Runnable() {
                 @Override
                 public void run() {
                     for (StatusBarNotification sbn : notifications) {
-                        addNotificationInternal(sbn);
+                        addNotificationInternal(sbn, currentRanking);
                     }
                 }
             });
@@ -297,13 +305,14 @@
         @Override
         public void onNotificationPosted(final StatusBarNotification sbn) {
             if (DEBUG) Log.d(TAG, "onNotificationPosted: " + sbn);
+            final Ranking currentRanking = getCurrentRanking();
             mHandler.post(new Runnable() {
                 @Override
                 public void run() {
                     if (mNotificationData.findByKey(sbn.getKey()) != null) {
-                        updateNotificationInternal(sbn);
+                        updateNotificationInternal(sbn, currentRanking);
                     } else {
-                        addNotificationInternal(sbn);
+                        addNotificationInternal(sbn, currentRanking);
                     }
                 }
             });
@@ -312,10 +321,23 @@
         @Override
         public void onNotificationRemoved(final StatusBarNotification sbn) {
             if (DEBUG) Log.d(TAG, "onNotificationRemoved: " + sbn);
+            final Ranking currentRanking = getCurrentRanking();
             mHandler.post(new Runnable() {
                 @Override
                 public void run() {
-                    removeNotificationInternal(sbn.getKey());
+                    removeNotificationInternal(sbn.getKey(), currentRanking);
+                }
+            });
+        }
+
+        @Override
+        public void onNotificationRankingUpdate() {
+            if (DEBUG) Log.d(TAG, "onRankingUpdate");
+            final Ranking currentRanking = getCurrentRanking();
+            mHandler.post(new Runnable() {
+                @Override
+                public void run() {
+                    updateRankingInternal(currentRanking);
                 }
             });
         }
@@ -461,7 +483,7 @@
      * Takes the necessary steps to prepare the status bar for starting an activity, then starts it.
      * @param action A dismiss action that is called if it's safe to start the activity.
      */
-    protected void startNotificationActivity(OnDismissAction action) {
+    protected void dismissKeyguardThenExecute(OnDismissAction action) {
         action.onDismiss();
     }
 
@@ -1031,7 +1053,7 @@
         }
 
         public void onClick(final View v) {
-            startNotificationActivity(new OnDismissAction() {
+            dismissKeyguardThenExecute(new OnDismissAction() {
                 public boolean onDismiss() {
                     try {
                         // The intent we are sending is for the application, which
@@ -1051,7 +1073,7 @@
                         v.getLocationOnScreen(pos);
                         Intent overlay = new Intent();
                         overlay.setSourceBounds(new Rect(pos[0], pos[1],
-                                pos[0]+v.getWidth(), pos[1]+v.getHeight()));
+                                pos[0] + v.getWidth(), pos[1] + v.getHeight()));
                         try {
                             mIntent.send(mContext, 0, overlay);
                             sent = true;
@@ -1076,7 +1098,7 @@
                     visibilityChanged(false);
 
                     boolean waitForActivityLaunch = sent && mIntent.isActivity();
-                    return waitForActivityLaunch;
+                    return waitForActivityLaunch && DELAY_DISMISS_TO_ACTIVITY_LAUNCH;
                 }
             });
         }
@@ -1120,19 +1142,13 @@
         }
     }
 
-    protected StatusBarNotification removeNotificationViews(String key) {
-        NotificationData.Entry entry = mNotificationData.remove(key);
+    protected StatusBarNotification removeNotificationViews(String key, Ranking ranking) {
+        NotificationData.Entry entry = mNotificationData.remove(key, ranking);
         if (entry == null) {
             Log.w(TAG, "removeNotification for unknown key: " + key);
             return null;
         }
-        // Remove the expanded view.
-        ViewGroup rowParent = (ViewGroup)entry.row.getParent();
-        if (rowParent != null) rowParent.removeView(entry.row);
-        updateRowStates();
-        updateNotificationIcons();
-        updateSpeedBump();
-
+        updateNotifications();
         return entry.notification;
     }
 
@@ -1166,35 +1182,17 @@
         return entry;
     }
 
-    protected void addNotificationViews(NotificationData.Entry entry) {
+    protected void addNotificationViews(Entry entry, Ranking ranking) {
         if (entry == null) {
             return;
         }
         // Add the expanded view and icon.
-        int pos = mNotificationData.add(entry);
-        if (DEBUG) {
-            Log.d(TAG, "addNotificationViews: added at " + pos);
-        }
-        updateRowStates();
-        updateNotificationIcons();
-        updateSpeedBump();
+        mNotificationData.add(entry, ranking);
+        updateNotifications();
     }
 
-    protected void updateSpeedBump() {
-        int n = mNotificationData.size();
-        int speedBumpIndex = -1;
-        for (int i = n-1; i >= 0; i--) {
-            NotificationData.Entry entry = mNotificationData.get(i);
-            if (entry.row.getVisibility() != View.GONE && speedBumpIndex == -1
-                    && entry.row.isBelowSpeedBump() ) {
-                speedBumpIndex = n - 1 - i;
-            }
-        }
-        mStackScroller.updateSpeedBumpIndex(speedBumpIndex);
-    }
-
-    private void addNotificationViews(StatusBarNotification notification) {
-        addNotificationViews(createNotificationViews(notification));
+    private void addNotificationViews(StatusBarNotification notification, Ranking ranking) {
+        addNotificationViews(createNotificationViews(notification), ranking);
     }
 
     /**
@@ -1208,17 +1206,17 @@
     protected void updateRowStates() {
         int maxKeyguardNotifications = getMaxKeyguardNotifications();
         mKeyguardIconOverflowContainer.getIconsView().removeAllViews();
-        int n = mNotificationData.size();
+        final int N = mNotificationData.size();
         int visibleNotifications = 0;
         boolean onKeyguard = mState == StatusBarState.KEYGUARD;
-        for (int i = n-1; i >= 0; i--) {
+        for (int i = 0; i < N; i++) {
             NotificationData.Entry entry = mNotificationData.get(i);
             if (onKeyguard) {
                 entry.row.setExpansionDisabled(true);
             } else {
                 entry.row.setExpansionDisabled(false);
                 if (!entry.row.isUserLocked()) {
-                    boolean top = (i == n-1);
+                    boolean top = (i == 0);
                     entry.row.setSystemExpanded(top);
                 }
             }
@@ -1245,6 +1243,9 @@
         } else {
             mKeyguardIconOverflowContainer.setVisibility(View.GONE);
         }
+        // Move overflow container to last position.
+        mStackScroller.changeViewPosition(mKeyguardIconOverflowContainer,
+                mStackScroller.getChildCount() - 1);
     }
 
     private boolean shouldShowOnKeyguard(StatusBarNotification sbn) {
@@ -1254,7 +1255,7 @@
     protected void setZenMode(int mode) {
         if (!isDeviceProvisioned()) return;
         mZenMode = mode;
-        updateNotificationIcons();
+        updateNotifications();
     }
 
     protected void setShowLockscreenNotifications(boolean show) {
@@ -1263,41 +1264,39 @@
 
     protected abstract void haltTicker();
     protected abstract void setAreThereNotifications();
-    protected abstract void updateNotificationIcons();
+    protected abstract void updateNotifications();
     protected abstract void tick(StatusBarNotification n, boolean firstTime);
     protected abstract void updateExpandedViewPos(int expandedPosition);
     protected abstract boolean shouldDisableNavbarGestures();
 
-    protected boolean isTopNotification(ViewGroup parent, NotificationData.Entry entry) {
-        return parent != null && parent.indexOfChild(entry.row) == 0;
-    }
-
-
     @Override
     public void addNotification(StatusBarNotification notification) {
         if (!USE_NOTIFICATION_LISTENER) {
-            addNotificationInternal(notification);
+            addNotificationInternal(notification, null);
         }
     }
 
-    public abstract void addNotificationInternal(StatusBarNotification notification);
+    public abstract void addNotificationInternal(StatusBarNotification notification,
+            Ranking ranking);
+
+    protected abstract void updateRankingInternal(Ranking ranking);
 
     @Override
     public void removeNotification(String key) {
         if (!USE_NOTIFICATION_LISTENER) {
-            removeNotificationInternal(key);
+            removeNotificationInternal(key, null);
         }
     }
 
-    protected abstract void removeNotificationInternal(String key);
+    public abstract void removeNotificationInternal(String key, Ranking ranking);
 
     public void updateNotification(StatusBarNotification notification) {
         if (!USE_NOTIFICATION_LISTENER) {
-            updateNotificationInternal(notification);
+            updateNotificationInternal(notification, null);
         }
     }
 
-    public void updateNotificationInternal(StatusBarNotification notification) {
+    public void updateNotificationInternal(StatusBarNotification notification, Ranking ranking) {
         if (DEBUG) Log.d(TAG, "updateNotification(" + notification + ")");
 
         final NotificationData.Entry oldEntry = mNotificationData.findByKey(notification.getKey());
@@ -1369,18 +1368,12 @@
                         && oldPublicContentView.getPackage().equals(publicContentView.getPackage())
                         && oldPublicContentView.getLayoutId() == publicContentView.getLayoutId());
 
-        ViewGroup rowParent = (ViewGroup) oldEntry.row.getParent();
-        boolean orderUnchanged =
-                   notification.getNotification().when == oldNotification.getNotification().when
-                && notification.getScore() == oldNotification.getScore();
-        // score now encompasses/supersedes isOngoing()
 
         boolean updateTicker = notification.getNotification().tickerText != null
                 && !TextUtils.equals(notification.getNotification().tickerText,
                 oldEntry.notification.getNotification().tickerText);
-        boolean isTopAnyway = isTopNotification(rowParent, oldEntry);
-        if (contentsUnchanged && bigContentsUnchanged && headsUpContentsUnchanged && publicUnchanged
-                && (orderUnchanged || isTopAnyway)) {
+        if (contentsUnchanged && bigContentsUnchanged && headsUpContentsUnchanged
+                && publicUnchanged) {
             if (DEBUG) Log.d(TAG, "reusing notification for key: " + notification.getKey());
             oldEntry.notification = notification;
             try {
@@ -1408,22 +1401,20 @@
                     handleNotificationError(notification, "Couldn't update icon: " + ic);
                     return;
                 }
-                updateRowStates();
-                updateSpeedBump();
+                mNotificationData.updateRanking(ranking);
+                updateNotifications();
             }
             catch (RuntimeException e) {
                 // It failed to add cleanly.  Log, and remove the view from the panel.
                 Log.w(TAG, "Couldn't reapply views for package " + contentView.getPackage(), e);
-                removeNotificationViews(notification.getKey());
-                addNotificationViews(notification);
+                removeNotificationViews(notification.getKey(), ranking);
+                addNotificationViews(notification, ranking);
             }
         } else {
             if (DEBUG) Log.d(TAG, "not reusing notification for key: " + notification.getKey());
             if (DEBUG) Log.d(TAG, "contents was " + (contentsUnchanged ? "unchanged" : "changed"));
-            if (DEBUG) Log.d(TAG, "order was " + (orderUnchanged ? "unchanged" : "changed"));
-            if (DEBUG) Log.d(TAG, "notification is " + (isTopAnyway ? "top" : "not top"));
-            removeNotificationViews(notification.getKey());
-            addNotificationViews(notification);  // will also replace the heads up
+            removeNotificationViews(notification.getKey(), ranking);
+            addNotificationViews(notification, ranking);  // will also replace the heads up
             final NotificationData.Entry newEntry = mNotificationData.findByKey(
                     notification.getKey());
             final boolean userChangedExpansion = oldEntry.row.hasUserChangedExpansion();
@@ -1570,5 +1561,12 @@
             mWindowManager.removeViewImmediate(mSearchPanelView);
         }
         mContext.unregisterReceiver(mBroadcastReceiver);
+        if (USE_NOTIFICATION_LISTENER) {
+            try {
+                mNotificationListener.unregisterAsSystemService();
+            } catch (RemoteException e) {
+                // Ignore.
+            }
+        }
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/FlingAnimationUtils.java b/packages/SystemUI/src/com/android/systemui/statusbar/FlingAnimationUtils.java
index 7d576cb..5f1325b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/FlingAnimationUtils.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/FlingAnimationUtils.java
@@ -18,6 +18,7 @@
 
 import android.animation.ValueAnimator;
 import android.content.Context;
+import android.view.ViewPropertyAnimator;
 import android.view.animation.AnimationUtils;
 import android.view.animation.Interpolator;
 import android.view.animation.PathInterpolator;
@@ -46,6 +47,8 @@
     private float mMaxLengthSeconds;
     private float mHighVelocityPxPerSecond;
 
+    private AnimatorProperties mAnimatorProperties = new AnimatorProperties();
+
     public FlingAnimationUtils(Context ctx, float maxLengthSeconds) {
         mMaxLengthSeconds = maxLengthSeconds;
         mLinearOutSlowIn = new PathInterpolator(0, 0, LINEAR_OUT_SLOW_IN_X2, 1);
@@ -80,18 +83,59 @@
      * @param currValue the current value
      * @param endValue the end value of the animator
      * @param velocity the current velocity of the motion
+     */
+    public void apply(ViewPropertyAnimator animator, float currValue, float endValue,
+            float velocity) {
+        apply(animator, currValue, endValue, velocity, Math.abs(endValue - currValue));
+    }
+
+    /**
+     * Applies the interpolator and length to the animator, such that the fling animation is
+     * consistent with the finger motion.
+     *
+     * @param animator the animator to apply
+     * @param currValue the current value
+     * @param endValue the end value of the animator
+     * @param velocity the current velocity of the motion
      * @param maxDistance the maximum distance for this interaction; the maximum animation length
      *                    gets multiplied by the ratio between the actual distance and this value
      */
     public void apply(ValueAnimator animator, float currValue, float endValue, float velocity,
             float maxDistance) {
+        AnimatorProperties properties = getProperties(currValue, endValue, velocity,
+                maxDistance);
+        animator.setDuration(properties.duration);
+        animator.setInterpolator(properties.interpolator);
+    }
+
+    /**
+     * Applies the interpolator and length to the animator, such that the fling animation is
+     * consistent with the finger motion.
+     *
+     * @param animator the animator to apply
+     * @param currValue the current value
+     * @param endValue the end value of the animator
+     * @param velocity the current velocity of the motion
+     * @param maxDistance the maximum distance for this interaction; the maximum animation length
+     *                    gets multiplied by the ratio between the actual distance and this value
+     */
+    public void apply(ViewPropertyAnimator animator, float currValue, float endValue,
+            float velocity, float maxDistance) {
+        AnimatorProperties properties = getProperties(currValue, endValue, velocity,
+                maxDistance);
+        animator.setDuration(properties.duration);
+        animator.setInterpolator(properties.interpolator);
+    }
+
+    private AnimatorProperties getProperties(float currValue,
+            float endValue, float velocity, float maxDistance) {
         float maxLengthSeconds = (float) (mMaxLengthSeconds
                 * Math.sqrt(Math.abs(endValue - currValue) / maxDistance));
         float diff = Math.abs(endValue - currValue);
         float velAbs = Math.abs(velocity);
         float durationSeconds = LINEAR_OUT_SLOW_IN_START_GRADIENT * diff / velAbs;
         if (durationSeconds <= maxLengthSeconds) {
-            animator.setInterpolator(mLinearOutSlowIn);
+            mAnimatorProperties.interpolator = mLinearOutSlowIn;
         } else if (velAbs >= mMinVelocityPxPerSecond) {
 
             // Cross fade between fast-out-slow-in and linear interpolator with current velocity.
@@ -100,14 +144,15 @@
                     = new VelocityInterpolator(durationSeconds, velAbs, diff);
             InterpolatorInterpolator superInterpolator = new InterpolatorInterpolator(
                     velocityInterpolator, mLinearOutSlowIn, mLinearOutSlowIn);
-            animator.setInterpolator(superInterpolator);
+            mAnimatorProperties.interpolator = superInterpolator;
         } else {
 
             // Just use a normal interpolator which doesn't take the velocity into account.
             durationSeconds = maxLengthSeconds;
-            animator.setInterpolator(mFastOutSlowIn);
+            mAnimatorProperties.interpolator = mFastOutSlowIn;
         }
-        animator.setDuration((long) (durationSeconds * 1000));
+        mAnimatorProperties.duration = (long) (durationSeconds * 1000);
+        return mAnimatorProperties;
     }
 
     /**
@@ -124,6 +169,34 @@
      */
     public void applyDismissing(ValueAnimator animator, float currValue, float endValue,
             float velocity, float maxDistance) {
+        AnimatorProperties properties = getDismissingProperties(currValue, endValue, velocity,
+                maxDistance);
+        animator.setDuration(properties.duration);
+        animator.setInterpolator(properties.interpolator);
+    }
+
+    /**
+     * Applies the interpolator and length to the animator, such that the fling animation is
+     * consistent with the finger motion for the case when the animation is making something
+     * disappear.
+     *
+     * @param animator the animator to apply
+     * @param currValue the current value
+     * @param endValue the end value of the animator
+     * @param velocity the current velocity of the motion
+     * @param maxDistance the maximum distance for this interaction; the maximum animation length
+     *                    gets multiplied by the ratio between the actual distance and this value
+     */
+    public void applyDismissing(ViewPropertyAnimator animator, float currValue, float endValue,
+            float velocity, float maxDistance) {
+        AnimatorProperties properties = getDismissingProperties(currValue, endValue, velocity,
+                maxDistance);
+        animator.setDuration(properties.duration);
+        animator.setInterpolator(properties.interpolator);
+    }
+
+    private AnimatorProperties getDismissingProperties(float currValue, float endValue,
+            float velocity, float maxDistance) {
         float maxLengthSeconds = (float) (mMaxLengthSeconds
                 * Math.pow(Math.abs(endValue - currValue) / maxDistance, 0.5f));
         float diff = Math.abs(endValue - currValue);
@@ -135,7 +208,7 @@
         Interpolator mLinearOutFasterIn = new PathInterpolator(0, 0, 1, y2);
         float durationSeconds = startGradient * diff / velAbs;
         if (durationSeconds <= maxLengthSeconds) {
-            animator.setInterpolator(mLinearOutFasterIn);
+            mAnimatorProperties.interpolator = mLinearOutFasterIn;
         } else if (velAbs >= mMinVelocityPxPerSecond) {
 
             // Cross fade between linear-out-faster-in and linear interpolator with current
@@ -145,14 +218,15 @@
                     = new VelocityInterpolator(durationSeconds, velAbs, diff);
             InterpolatorInterpolator superInterpolator = new InterpolatorInterpolator(
                     velocityInterpolator, mLinearOutFasterIn, mLinearOutSlowIn);
-            animator.setInterpolator(superInterpolator);
+            mAnimatorProperties.interpolator = superInterpolator;
         } else {
 
             // Just use a normal interpolator which doesn't take the velocity into account.
             durationSeconds = maxLengthSeconds;
-            animator.setInterpolator(mFastOutLinearIn);
+            mAnimatorProperties.interpolator = mFastOutLinearIn;
         }
-        animator.setDuration((long) (durationSeconds * 1000));
+        mAnimatorProperties.duration = (long) (durationSeconds * 1000);
+        return mAnimatorProperties;
     }
 
     /**
@@ -221,4 +295,10 @@
             return time * mVelocity / mDiff;
         }
     }
+
+    private static class AnimatorProperties {
+        Interpolator interpolator;
+        long duration;
+    }
+
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/InterceptedNotifications.java b/packages/SystemUI/src/com/android/systemui/statusbar/InterceptedNotifications.java
index 0555879..de27119 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/InterceptedNotifications.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/InterceptedNotifications.java
@@ -20,8 +20,10 @@
 import android.content.Context;
 import android.os.Process;
 import android.provider.Settings;
+import android.service.notification.NotificationListenerService.Ranking;
 import android.service.notification.StatusBarNotification;
 import android.util.ArrayMap;
+import android.util.ArraySet;
 import android.view.View;
 
 import com.android.systemui.R;
@@ -30,13 +32,13 @@
 
 public class InterceptedNotifications {
     private static final String TAG = "InterceptedNotifications";
-    private static final String EXTRA_INTERCEPT = "android.intercept";
     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;
 
@@ -49,29 +51,49 @@
         final int n = mIntercepted.size();
         for (int i = 0; i < n; i++) {
             final StatusBarNotification sbn = mIntercepted.valueAt(i);
-            sbn.getNotification().extras.putBoolean(EXTRA_INTERCEPT, false);
-            mBar.addNotificationInternal(sbn);
+            mReleased.add(sbn.getKey());
+            mBar.addNotificationInternal(sbn, null);
         }
         mIntercepted.clear();
         updateSyntheticNotification();
     }
 
-    public boolean tryIntercept(StatusBarNotification notification) {
-        if (!notification.getNotification().extras.getBoolean(EXTRA_INTERCEPT)) return false;
+    public boolean tryIntercept(StatusBarNotification notification, Ranking ranking) {
+        if (ranking == null) return false;
         if (shouldDisplayIntercepted()) return false;
+        if (mReleased.contains(notification.getKey())) return false;
+        if (!ranking.isInterceptedByDoNotDisturb(notification.getKey())) return false;
         mIntercepted.put(notification.getKey(), notification);
         updateSyntheticNotification();
         return true;
     }
 
+    public void retryIntercepts(Ranking ranking) {
+        if (ranking == null) return;
+
+        boolean changed = false;
+        final int N = mIntercepted.size();
+        for (int i = 0; i < N; i++) {
+            final StatusBarNotification sbn = mIntercepted.valueAt(i);
+            if (!tryIntercept(sbn, ranking)) {
+                changed = true;
+                mBar.addNotificationInternal(sbn, ranking);
+            }
+        }
+        if (changed) {
+            updateSyntheticNotification();
+        }
+    }
+
     public void remove(String key) {
         if (mIntercepted.remove(key) != null) {
             updateSyntheticNotification();
         }
+        mReleased.remove(key);
     }
 
     public boolean isSyntheticEntry(Entry ent) {
-        return ent.key.equals(SYNTHETIC_KEY);
+        return ent.key.equals(mSynKey);
     }
 
     public void update(StatusBarNotification notification) {
@@ -88,7 +110,7 @@
     private void updateSyntheticNotification() {
         if (mIntercepted.isEmpty()) {
             if (mSynKey != null) {
-                mBar.removeNotificationInternal(mSynKey);
+                mBar.removeNotificationInternal(mSynKey, null);
                 mSynKey = null;
             }
             return;
@@ -107,9 +129,9 @@
                 mBar.getCurrentUserHandle());
         if (mSynKey == null) {
             mSynKey = sbn.getKey();
-            mBar.addNotificationInternal(sbn);
+            mBar.addNotificationInternal(sbn, null);
         } else {
-           mBar.updateNotificationInternal(sbn);
+           mBar.updateNotificationInternal(sbn, null);
         }
         final NotificationData.Entry entry = mBar.mNotificationData.findByKey(mSynKey);
         entry.row.setOnClickListener(mSynClickListener);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationData.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationData.java
index 5696246..d829ac0 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationData.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationData.java
@@ -16,15 +16,19 @@
 
 package com.android.systemui.statusbar;
 
+import android.app.Notification;
+import android.service.notification.NotificationListenerService.Ranking;
 import android.service.notification.StatusBarNotification;
 import android.view.View;
-import android.widget.ImageView;
 
 import java.util.ArrayList;
+import java.util.Collections;
 import java.util.Comparator;
 
 /**
  * The list of currently displaying notifications.
+ *
+ * TODO: Rename to NotificationList.
  */
 public class NotificationData {
     public static final class Entry {
@@ -34,7 +38,6 @@
         public ExpandableNotificationRow row; // the outer expanded view
         public View expanded; // the inflated RemoteViews
         public View expandedPublic; // for insecure lockscreens
-        public ImageView largeIcon;
         public View expandedBig;
         private boolean interruption;
         public Entry() {}
@@ -64,18 +67,23 @@
     }
 
     private final ArrayList<Entry> mEntries = new ArrayList<Entry>();
-    private final Comparator<Entry> mEntryCmp = new Comparator<Entry>() {
-        // sort first by score, then by when
+    private Ranking mRanking;
+    private final Comparator<Entry> mRankingComparator = new Comparator<Entry>() {
+        @Override
         public int compare(Entry a, Entry b) {
+            if (mRanking != null) {
+                return mRanking.getRank(a.key) - mRanking.getRank(b.key);
+            }
+
             final StatusBarNotification na = a.notification;
             final StatusBarNotification nb = b.notification;
-            int d = na.getScore() - nb.getScore();
+            int d = nb.getScore() - na.getScore();
             if (a.interruption != b.interruption) {
-                return a.interruption ? 1 : -1;
+                return a.interruption ? -1 : 1;
             } else if (d != 0) {
                 return d;
             } else {
-                return (int) (na.getNotification().when - nb.getNotification().when);
+                return (int) (nb.getNotification().when - na.getNotification().when);
             }
         }
     };
@@ -97,26 +105,47 @@
         return null;
     }
 
-    public int add(Entry entry) {
-        int i;
-        int N = mEntries.size();
-        for (i = 0; i < N; i++) {
-            if (mEntryCmp.compare(mEntries.get(i), entry) > 0) {
-                break;
-            }
-        }
-        mEntries.add(i, entry);
-        return i;
+    public void add(Entry entry, Ranking ranking) {
+        mEntries.add(entry);
+        updateRankingAndSort(ranking);
     }
 
-    public Entry remove(String key) {
+    public Entry remove(String key, Ranking ranking) {
         Entry e = findByKey(key);
-        if (e != null) {
-            mEntries.remove(e);
+        if (e == null) {
+            return null;
         }
+        mEntries.remove(e);
+        updateRankingAndSort(ranking);
         return e;
     }
 
+    public void updateRanking(Ranking ranking) {
+        updateRankingAndSort(ranking);
+    }
+
+    public boolean isAmbient(String key) {
+        // TODO: Remove when switching to NotificationListener.
+        if (mRanking == null) {
+            for (Entry entry : mEntries) {
+                if (key.equals(entry.key)) {
+                    return entry.notification.getNotification().priority ==
+                            Notification.PRIORITY_MIN;
+                }
+            }
+        } else {
+            return mRanking.isAmbient(key);
+        }
+        return false;
+    }
+
+    private void updateRankingAndSort(Ranking ranking) {
+        if (ranking != null) {
+            mRanking = ranking;
+        }
+        Collections.sort(mEntries, mRankingComparator);
+    }
+
     /**
      * Return whether there are any visible items (i.e. items without an error).
      */
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/BounceInterpolator.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/BounceInterpolator.java
new file mode 100644
index 0000000..367d326
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/BounceInterpolator.java
@@ -0,0 +1,43 @@
+/*
+ * 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 android.view.animation.Interpolator;
+
+/**
+ * An implementation of a bouncer interpolator optimized for unlock hinting.
+ */
+public class BounceInterpolator implements Interpolator {
+
+    private final static float SCALE_FACTOR = 7.5625f;
+
+    @Override
+    public float getInterpolation(float t) {
+        if (t < 4f / 11f) {
+            return SCALE_FACTOR * t * t;
+        } else if (t < 8f / 11f) {
+            float t2 = t - 6f / 11f;
+            return SCALE_FACTOR * t2 * t2 + 3f / 4f;
+        } else if (t < 10f / 11f) {
+            float t2 = t - 9f / 11f;
+            return SCALE_FACTOR * t2 * t2 + 15f / 16f;
+        } else {
+            float t2 = t - 21f / 22f;
+            return SCALE_FACTOR * t2 * t2 + 63f / 64f;
+        }
+    }
+}
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 714ad06..97aa993 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java
@@ -23,7 +23,7 @@
 import android.content.Intent;
 import android.content.IntentFilter;
 import android.content.pm.PackageManager;
-import android.os.PowerManager;
+import android.content.pm.ResolveInfo;
 import android.os.RemoteException;
 import android.os.UserHandle;
 import android.provider.MediaStore;
@@ -34,27 +34,34 @@
 import android.widget.FrameLayout;
 import android.widget.ImageView;
 
+import com.android.internal.widget.LockPatternUtils;
+import com.android.keyguard.KeyguardUpdateMonitor;
+import com.android.keyguard.KeyguardUpdateMonitorCallback;
 import com.android.systemui.R;
 
 /**
  * Implementation for the bottom area of the Keyguard, including camera/phone affordance and status
  * text.
  */
-public class KeyguardBottomAreaView extends FrameLayout
-        implements SwipeAffordanceView.AffordanceListener,
+public class KeyguardBottomAreaView extends FrameLayout implements View.OnClickListener,
         UnlockMethodCache.OnUnlockMethodChangedListener {
 
     final static String TAG = "PhoneStatusBar/KeyguardBottomAreaView";
 
+    private static final Intent SECURE_CAMERA_INTENT =
+            new Intent(MediaStore.INTENT_ACTION_STILL_IMAGE_CAMERA_SECURE)
+                    .addFlags(Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
+    private static final Intent INSECURE_CAMERA_INTENT =
+            new Intent(MediaStore.INTENT_ACTION_STILL_IMAGE_CAMERA);
     private static final Intent PHONE_INTENT = new Intent(Intent.ACTION_DIAL);
 
-    private SwipeAffordanceView mCameraButton;
-    private SwipeAffordanceView mPhoneButton;
+    private ImageView mCameraImageView;
+    private ImageView mPhoneImageView;
     private ImageView mLockIcon;
 
-    private PowerManager mPowerManager;
     private ActivityStarter mActivityStarter;
     private UnlockMethodCache mUnlockMethodCache;
+    private LockPatternUtils mLockPatternUtils;
 
     public KeyguardBottomAreaView(Context context) {
         super(context);
@@ -76,33 +83,42 @@
     @Override
     protected void onFinishInflate() {
         super.onFinishInflate();
-        mCameraButton = (SwipeAffordanceView) findViewById(R.id.camera_button);
-        mPhoneButton = (SwipeAffordanceView) findViewById(R.id.phone_button);
+        mLockPatternUtils = new LockPatternUtils(mContext);
+        mCameraImageView = (ImageView) findViewById(R.id.camera_button);
+        mPhoneImageView = (ImageView) findViewById(R.id.phone_button);
         mLockIcon = (ImageView) findViewById(R.id.lock_icon);
-        mCameraButton.setAffordanceListener(this);
-        mPhoneButton.setAffordanceListener(this);
-        watchForDevicePolicyChanges();
+        watchForCameraPolicyChanges();
         watchForAccessibilityChanges();
         updateCameraVisibility();
         updatePhoneVisibility();
         mUnlockMethodCache = UnlockMethodCache.getInstance(getContext());
         mUnlockMethodCache.addListener(this);
         updateTrust();
-        mPowerManager = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
     }
 
     public void setActivityStarter(ActivityStarter activityStarter) {
         mActivityStarter = activityStarter;
     }
 
+    private Intent getCameraIntent() {
+        KeyguardUpdateMonitor updateMonitor = KeyguardUpdateMonitor.getInstance(mContext);
+        boolean currentUserHasTrust = updateMonitor.getUserHasTrust(
+                mLockPatternUtils.getCurrentUser());
+        return mLockPatternUtils.isSecure() && !currentUserHasTrust
+                ? SECURE_CAMERA_INTENT : INSECURE_CAMERA_INTENT;
+    }
+
     private void updateCameraVisibility() {
-        boolean visible = !isCameraDisabledByDpm();
-        mCameraButton.setVisibility(visible ? View.VISIBLE : View.GONE);
+        ResolveInfo resolved = mContext.getPackageManager().resolveActivityAsUser(getCameraIntent(),
+                PackageManager.MATCH_DEFAULT_ONLY,
+                mLockPatternUtils.getCurrentUser());
+        boolean visible = !isCameraDisabledByDpm() && resolved != null;
+        mCameraImageView.setVisibility(visible ? View.VISIBLE : View.GONE);
     }
 
     private void updatePhoneVisibility() {
         boolean visible = isPhoneVisible();
-        mPhoneButton.setVisibility(visible ? View.VISIBLE : View.GONE);
+        mPhoneImageView.setVisibility(visible ? View.VISIBLE : View.GONE);
     }
 
     private boolean isPhoneVisible() {
@@ -129,19 +145,12 @@
         return false;
     }
 
-    private void watchForDevicePolicyChanges() {
+    private void watchForCameraPolicyChanges() {
         final IntentFilter filter = new IntentFilter();
         filter.addAction(DevicePolicyManager.ACTION_DEVICE_POLICY_MANAGER_STATE_CHANGED);
-        getContext().registerReceiver(new BroadcastReceiver() {
-            public void onReceive(Context context, Intent intent) {
-                post(new Runnable() {
-                    @Override
-                    public void run() {
-                        updateCameraVisibility();
-                    }
-                });
-            }
-        }, filter);
+        getContext().registerReceiverAsUser(mDevicePolicyReceiver,
+                UserHandle.ALL, filter, null, null);
+        KeyguardUpdateMonitor.getInstance(mContext).registerCallback(mUpdateMonitorCallback);
     }
 
     private void watchForAccessibilityChanges() {
@@ -162,39 +171,41 @@
     }
 
     private void enableAccessibility(boolean touchExplorationEnabled) {
-        mCameraButton.enableAccessibility(touchExplorationEnabled);
-        mPhoneButton.enableAccessibility(touchExplorationEnabled);
-    }
-
-    private void launchCamera() {
-        mContext.startActivityAsUser(
-                new Intent(MediaStore.INTENT_ACTION_STILL_IMAGE_CAMERA_SECURE),
-                UserHandle.CURRENT);
-    }
-
-    private void launchPhone() {
-        mActivityStarter.startActivity(PHONE_INTENT);
+        mCameraImageView.setOnClickListener(touchExplorationEnabled ? this : null);
+        mCameraImageView.setClickable(touchExplorationEnabled);
+        mPhoneImageView.setOnClickListener(touchExplorationEnabled ? this : null);
+        mPhoneImageView.setClickable(touchExplorationEnabled);
     }
 
     @Override
-    public void onUserActivity(long when) {
-        mPowerManager.userActivity(when, false);
-    }
-
-    @Override
-    public void onActionPerformed(SwipeAffordanceView view) {
-        if (view == mCameraButton) {
+    public void onClick(View v) {
+        if (v == mCameraImageView) {
             launchCamera();
-        } else if (view == mPhoneButton) {
+        } else if (v == mPhoneImageView) {
             launchPhone();
         }
     }
 
+    public void launchCamera() {
+        Intent intent = getCameraIntent();
+        if (intent == SECURE_CAMERA_INTENT) {
+            mContext.startActivityAsUser(intent, UserHandle.CURRENT);
+        } else {
+            mActivityStarter.startActivity(intent);
+        }
+    }
+
+    public void launchPhone() {
+        mActivityStarter.startActivity(PHONE_INTENT);
+    }
+
+
     @Override
     protected void onVisibilityChanged(View changedView, int visibility) {
         super.onVisibilityChanged(changedView, visibility);
         if (changedView == this && visibility == VISIBLE) {
             updateTrust();
+            updateCameraVisibility();
         }
     }
 
@@ -208,8 +219,40 @@
         mLockIcon.setImageResource(iconRes);
     }
 
+    public ImageView getPhoneImageView() {
+        return mPhoneImageView;
+    }
+
+    public ImageView getCameraImageView() {
+        return mCameraImageView;
+    }
+
+    public ImageView getLockIcon() {
+        return mLockIcon;
+    }
+
     @Override
     public void onMethodSecureChanged(boolean methodSecure) {
         updateTrust();
+        updateCameraVisibility();
     }
+
+    private final BroadcastReceiver mDevicePolicyReceiver = new BroadcastReceiver() {
+        public void onReceive(Context context, Intent intent) {
+            post(new Runnable() {
+                @Override
+                public void run() {
+                    updateCameraVisibility();
+                }
+            });
+        }
+    };
+
+    private final KeyguardUpdateMonitorCallback mUpdateMonitorCallback =
+            new KeyguardUpdateMonitorCallback() {
+        @Override
+        public void onUserSwitchComplete(int userId) {
+            updateCameraVisibility();
+        }
+    };
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java
index 2bb80bf..3aaace4 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java
@@ -17,10 +17,14 @@
 package com.android.systemui.statusbar.phone;
 
 import android.content.Context;
+import android.os.SystemClock;
 import android.view.KeyEvent;
 import android.view.LayoutInflater;
 import android.view.View;
 import android.view.ViewGroup;
+import android.view.animation.AnimationUtils;
+import android.view.animation.Interpolator;
+import android.view.animation.LinearInterpolator;
 
 import com.android.internal.widget.LockPatternUtils;
 import com.android.keyguard.KeyguardViewBase;
@@ -43,6 +47,8 @@
     private StatusBarWindowManager mWindowManager;
     private KeyguardViewBase mKeyguardView;
     private ViewGroup mRoot;
+    private Interpolator mFadeOutInterpolator = new LinearInterpolator();
+    private boolean mFadingOut;
 
     public KeyguardBouncer(Context context, ViewMediatorCallback callback,
             LockPatternUtils lockPatternUtils, StatusBarWindowManager windowManager,
@@ -56,12 +62,16 @@
 
     public void show() {
         ensureView();
+        if (mRoot.getVisibility() == View.VISIBLE) {
+            return;
+        }
 
         // Try to dismiss the Keyguard. If no security pattern is set, this will dismiss the whole
         // Keyguard. If we need to authenticate, show the bouncer.
         if (!mKeyguardView.dismiss()) {
             mRoot.setVisibility(View.VISIBLE);
             mKeyguardView.onResume();
+            mKeyguardView.startAppearAnimation();
         }
     }
 
@@ -82,6 +92,29 @@
         }
     }
 
+    public void animateHide(long delay, long duration) {
+        if (isShowing()) {
+            mFadingOut = true;
+            mKeyguardView.animate()
+                    .alpha(0)
+                    .withLayer()
+
+                    // Make it disappear faster, as the focus should be on the activity behind.
+                    .setDuration(duration / 3)
+                    .setInterpolator(mFadeOutInterpolator)
+                    .setStartDelay(delay)
+                    .withEndAction(new Runnable() {
+                        @Override
+                        public void run() {
+                            mFadingOut = false;
+                            hide(true /* destroyView */);
+                        }
+                    });
+        } else {
+            hide(true /* destroyView */);
+        }
+    }
+
     /**
      * Reset the state of the view.
      */
@@ -106,7 +139,11 @@
     }
 
     public boolean isShowing() {
-        return mRoot != null && mRoot.getVisibility() == View.VISIBLE;
+        return mRoot != null && mRoot.getVisibility() == View.VISIBLE && !mFadingOut;
+    }
+
+    public void prepare() {
+        ensureView();
     }
 
     private void ensureView() {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardIndicationTextView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardIndicationTextView.java
index 769b1b1..c5c3fff 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardIndicationTextView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardIndicationTextView.java
@@ -17,7 +17,9 @@
 package com.android.systemui.statusbar.phone;
 
 import android.content.Context;
+import android.text.TextUtils;
 import android.util.AttributeSet;
+import android.view.View;
 import android.widget.TextView;
 
 /**
@@ -50,7 +52,12 @@
     public void switchIndication(CharSequence text) {
 
         // TODO: Animation, make sure that we will show one indication long enough.
-        setText(text);
+        if (TextUtils.isEmpty(text)) {
+            setVisibility(View.INVISIBLE);
+        } else {
+            setVisibility(View.VISIBLE);
+            setText(text);
+        }
     }
 
     /**
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardPageSwipeHelper.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardPageSwipeHelper.java
new file mode 100644
index 0000000..b4f4865
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardPageSwipeHelper.java
@@ -0,0 +1,398 @@
+/*
+ * 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 android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.animation.ValueAnimator;
+import android.content.Context;
+import android.os.PowerManager;
+import android.view.MotionEvent;
+import android.view.VelocityTracker;
+import android.view.View;
+import android.view.ViewConfiguration;
+import android.view.ViewPropertyAnimator;
+import android.view.animation.AnimationUtils;
+import android.view.animation.Interpolator;
+import com.android.systemui.R;
+import com.android.systemui.statusbar.FlingAnimationUtils;
+
+import java.util.ArrayList;
+
+/**
+ * A touch handler of the Keyguard which is responsible for swiping the content left or right.
+ */
+public class KeyguardPageSwipeHelper {
+
+    private static final float SWIPE_MAX_ICON_SCALE_AMOUNT = 2.0f;
+    private static final float SWIPE_RESTING_ALPHA_AMOUNT = 0.7f;
+    private final Context mContext;
+
+    private FlingAnimationUtils mFlingAnimationUtils;
+    private Callback mCallback;
+    private int mTrackingPointer;
+    private VelocityTracker mVelocityTracker;
+    private boolean mSwipingInProgress;
+    private float mInitialTouchX;
+    private float mInitialTouchY;
+    private float mTranslation;
+    private float mTranslationOnDown;
+    private int mTouchSlop;
+    private int mMinTranslationAmount;
+    private int mMinFlingVelocity;
+    private PowerManager mPowerManager;
+    private final View mLeftIcon;
+    private final View mCenterIcon;
+    private final View mRightIcon;
+    private Interpolator mFastOutSlowIn;
+    private Animator mSwipeAnimator;
+    private boolean mCallbackCalled;
+
+    KeyguardPageSwipeHelper(Callback callback, Context context) {
+        mContext = context;
+        mCallback = callback;
+        mLeftIcon = mCallback.getLeftIcon();
+        mCenterIcon = mCallback.getCenterIcon();
+        mRightIcon = mCallback.getRightIcon();
+        updateIcon(mLeftIcon, 1.0f, SWIPE_RESTING_ALPHA_AMOUNT, false);
+        updateIcon(mCenterIcon, 1.0f, SWIPE_RESTING_ALPHA_AMOUNT, false);
+        updateIcon(mRightIcon, 1.0f, SWIPE_RESTING_ALPHA_AMOUNT, false);
+        mPowerManager = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
+        initDimens();
+    }
+
+    private void initDimens() {
+        final ViewConfiguration configuration = ViewConfiguration.get(mContext);
+        mTouchSlop = configuration.getScaledTouchSlop();
+        mMinFlingVelocity = configuration.getScaledMinimumFlingVelocity();
+        mMinTranslationAmount = mContext.getResources().getDimensionPixelSize(
+                R.dimen.keyguard_min_swipe_amount);
+        mFlingAnimationUtils = new FlingAnimationUtils(mContext, 0.4f);
+        mFastOutSlowIn = AnimationUtils.loadInterpolator(mContext,
+                android.R.interpolator.fast_out_slow_in);
+    }
+
+    public boolean onTouchEvent(MotionEvent event) {
+        int pointerIndex = event.findPointerIndex(mTrackingPointer);
+        if (pointerIndex < 0) {
+            pointerIndex = 0;
+            mTrackingPointer = event.getPointerId(pointerIndex);
+        }
+        final float y = event.getY(pointerIndex);
+        final float x = event.getX(pointerIndex);
+
+        switch (event.getActionMasked()) {
+            case MotionEvent.ACTION_DOWN:
+                if (mSwipingInProgress) {
+                    cancelAnimations();
+                }
+                mInitialTouchY = y;
+                mInitialTouchX = x;
+                mTranslationOnDown = mTranslation;
+                initVelocityTracker();
+                trackMovement(event);
+                break;
+
+            case MotionEvent.ACTION_POINTER_UP:
+                final int upPointer = event.getPointerId(event.getActionIndex());
+                if (mTrackingPointer == upPointer) {
+                    // gesture is ongoing, find a new pointer to track
+                    final int newIndex = event.getPointerId(0) != upPointer ? 0 : 1;
+                    final float newY = event.getY(newIndex);
+                    final float newX = event.getX(newIndex);
+                    mTrackingPointer = event.getPointerId(newIndex);
+                    mInitialTouchY = newY;
+                    mInitialTouchX = newX;
+                    mTranslationOnDown = mTranslation;
+                }
+                break;
+
+            case MotionEvent.ACTION_MOVE:
+                final float w = x - mInitialTouchX;
+                trackMovement(event);
+                if (((leftSwipePossible() && w > mTouchSlop)
+                        || (rightSwipePossible() && w < -mTouchSlop))
+                        && Math.abs(w) > Math.abs(y - mInitialTouchY)
+                        && !mSwipingInProgress) {
+                    cancelAnimations();
+                    mInitialTouchY = y;
+                    mInitialTouchX = x;
+                    mTranslationOnDown = mTranslation;
+                    mSwipingInProgress = true;
+                }
+                if (mSwipingInProgress) {
+                    setTranslation(mTranslationOnDown + x - mInitialTouchX, false);
+                    onUserActivity(event.getEventTime());
+                }
+                break;
+
+            case MotionEvent.ACTION_UP:
+            case MotionEvent.ACTION_CANCEL:
+                mTrackingPointer = -1;
+                trackMovement(event);
+                if (mSwipingInProgress) {
+                    flingWithCurrentVelocity();
+                }
+                if (mVelocityTracker != null) {
+                    mVelocityTracker.recycle();
+                    mVelocityTracker = null;
+                }
+                break;
+        }
+        return true;
+    }
+
+    private boolean rightSwipePossible() {
+        return mRightIcon.getVisibility() == View.VISIBLE;
+    }
+
+    private boolean leftSwipePossible() {
+        return mLeftIcon.getVisibility() == View.VISIBLE;
+    }
+
+    public boolean onInterceptTouchEvent(MotionEvent ev) {
+        return false;
+    }
+
+    private void onUserActivity(long when) {
+        mPowerManager.userActivity(when, false);
+    }
+
+    private void cancelAnimations() {
+        ArrayList<View> targetViews = mCallback.getTranslationViews();
+        for (View target : targetViews) {
+            target.animate().cancel();
+        }
+        View targetView = mTranslation > 0 ? mLeftIcon : mRightIcon;
+        targetView.animate().cancel();
+        if (mSwipeAnimator != null) {
+            mSwipeAnimator.removeAllListeners();
+            mSwipeAnimator.cancel();
+            hideInactiveIcons(true);
+        }
+    }
+
+    private void flingWithCurrentVelocity() {
+        float vel = getCurrentVelocity();
+
+        // We snap back if the current translation is not far enough
+        boolean snapBack = Math.abs(mTranslation) < mMinTranslationAmount;
+
+        // or if the velocity is in the opposite direction.
+        boolean velIsInWrongDirection = vel * mTranslation < 0;
+        snapBack |= Math.abs(vel) > mMinFlingVelocity && velIsInWrongDirection;
+        vel = snapBack ^ velIsInWrongDirection ? 0 : vel;
+        fling(vel, snapBack);
+    }
+
+    private void fling(float vel, final boolean snapBack) {
+        float target = mTranslation < 0 ? -mCallback.getPageWidth() : mCallback.getPageWidth();
+        target = snapBack ? 0 : target;
+
+        // translation Animation
+        startTranslationAnimations(vel, target);
+
+        // animate left / right icon
+        startIconAnimation(vel, snapBack, target);
+
+        ValueAnimator animator = ValueAnimator.ofFloat(mTranslation, target);
+        mFlingAnimationUtils.apply(animator, mTranslation, target, vel);
+        animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
+            @Override
+            public void onAnimationUpdate(ValueAnimator animation) {
+                mTranslation = (float) animation.getAnimatedValue();
+            }
+        });
+        animator.addListener(new AnimatorListenerAdapter() {
+            @Override
+            public void onAnimationEnd(Animator animation) {
+                mSwipeAnimator = null;
+                mSwipingInProgress = false;
+                if (!snapBack && !mCallbackCalled) {
+
+                    // ensure that the callback is called eventually
+                    mCallback.onAnimationToSideStarted(mTranslation < 0);
+                    mCallbackCalled = true;
+                }
+            }
+        });
+        if (!snapBack) {
+            mCallbackCalled = false;
+            animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
+                int frameNumber;
+
+                @Override
+                public void onAnimationUpdate(ValueAnimator animation) {
+                    if (frameNumber == 2 && !mCallbackCalled) {
+
+                        // we have to wait for the second frame for this call,
+                        // until the render thread has definitely kicked in, to avoid a lag.
+                        mCallback.onAnimationToSideStarted(mTranslation < 0);
+                        mCallbackCalled = true;
+                    }
+                    frameNumber++;
+                }
+            });
+        } else {
+            showAllIcons(true);
+        }
+        animator.start();
+        mSwipeAnimator = animator;
+    }
+
+    private void startTranslationAnimations(float vel, float target) {
+        ArrayList<View> targetViews = mCallback.getTranslationViews();
+        for (View targetView : targetViews) {
+            ViewPropertyAnimator animator = targetView.animate();
+            mFlingAnimationUtils.apply(animator, mTranslation, target, vel);
+            animator.translationX(target);
+        }
+    }
+
+    private void startIconAnimation(float vel, boolean snapBack, float target) {
+        float scale = snapBack ? 1.0f : SWIPE_MAX_ICON_SCALE_AMOUNT;
+        float alpha = snapBack ? SWIPE_RESTING_ALPHA_AMOUNT : 1.0f;
+        View targetView = mTranslation > 0
+                ? mLeftIcon
+                : mRightIcon;
+        if (targetView.getVisibility() == View.VISIBLE) {
+            ViewPropertyAnimator iconAnimator = targetView.animate();
+            mFlingAnimationUtils.apply(iconAnimator, mTranslation, target, vel);
+            iconAnimator.scaleX(scale);
+            iconAnimator.scaleY(scale);
+            iconAnimator.alpha(alpha);
+        }
+    }
+
+    private void setTranslation(float translation, boolean isReset) {
+        translation = rightSwipePossible() ? translation : Math.max(0, translation);
+        translation = leftSwipePossible() ? translation : Math.min(0, translation);
+        if (translation != mTranslation) {
+            ArrayList<View> translatedViews = mCallback.getTranslationViews();
+            for (View view : translatedViews) {
+                view.setTranslationX(translation);
+            }
+            if (translation == 0.0f) {
+                boolean animate = !isReset;
+                showAllIcons(animate);
+            } else {
+                View targetView = translation > 0 ? mLeftIcon : mRightIcon;
+                float progress = Math.abs(translation) / mCallback.getPageWidth();
+                progress = Math.min(progress, 1.0f);
+                float alpha = SWIPE_RESTING_ALPHA_AMOUNT * (1.0f - progress) + progress;
+                float scale = (1.0f - progress) + progress * SWIPE_MAX_ICON_SCALE_AMOUNT;
+                updateIcon(targetView, scale, alpha, false);
+                View otherView = translation < 0 ? mLeftIcon : mRightIcon;
+                if (mTranslation * translation <= 0) {
+                    // The sign of the translation has changed so we need to hide the other icons
+                    updateIcon(otherView, 0, 0, true);
+                    updateIcon(mCenterIcon, 0, 0, true);
+                }
+            }
+            mTranslation = translation;
+        }
+    }
+
+    private void showAllIcons(boolean animate) {
+        float scale = 1.0f;
+        float alpha = SWIPE_RESTING_ALPHA_AMOUNT;
+        updateIcon(mRightIcon, scale, alpha, animate);
+        updateIcon(mCenterIcon, scale, alpha, animate);
+        updateIcon(mLeftIcon, scale, alpha, animate);
+    }
+
+    private void hideInactiveIcons(boolean animate){
+        View otherView = mTranslation < 0 ? mLeftIcon : mRightIcon;
+        updateIcon(otherView, 0, 0, animate);
+        updateIcon(mCenterIcon, 0, 0, animate);
+    }
+
+    private void updateIcon(View view, float scale, float alpha, boolean animate) {
+        if (view.getVisibility() != View.VISIBLE) {
+            return;
+        }
+        if (!animate) {
+            view.setAlpha(alpha);
+            view.setScaleX(scale);
+            view.setScaleY(scale);
+            // TODO: remove this invalidate once the property setters invalidate it properly
+            view.invalidate();
+        } else {
+            if (view.getAlpha() != alpha || view.getScaleX() != scale) {
+                view.animate()
+                        .setInterpolator(mFastOutSlowIn)
+                        .alpha(alpha)
+                        .scaleX(scale)
+                        .scaleY(scale);
+            }
+        }
+    }
+
+    private void trackMovement(MotionEvent event) {
+        if (mVelocityTracker != null) {
+            mVelocityTracker.addMovement(event);
+        }
+    }
+
+    private void initVelocityTracker() {
+        if (mVelocityTracker != null) {
+            mVelocityTracker.recycle();
+        }
+        mVelocityTracker = VelocityTracker.obtain();
+    }
+
+    private float getCurrentVelocity() {
+        if (mVelocityTracker == null) {
+            return 0;
+        }
+        mVelocityTracker.computeCurrentVelocity(1000);
+        return mVelocityTracker.getXVelocity();
+    }
+
+    public void onConfigurationChanged() {
+        initDimens();
+    }
+
+    public void reset() {
+        setTranslation(0.0f, true);
+        mSwipingInProgress = false;
+    }
+
+    public boolean isSwipingInProgress() {
+        return mSwipingInProgress;
+    }
+
+    public interface Callback {
+
+        /**
+         * Notifies the callback when an animation to a side page was started.
+         *
+         * @param rightPage Is the page animated to the right page?
+         */
+        void onAnimationToSideStarted(boolean rightPage);
+
+        float getPageWidth();
+
+        ArrayList<View> getTranslationViews();
+
+        View getLeftIcon();
+
+        View getCenterIcon();
+
+        View getRightIcon();
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarTransitions.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarTransitions.java
index c83b479..3753a72 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarTransitions.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarTransitions.java
@@ -30,7 +30,6 @@
 
 public final class NavigationBarTransitions extends BarTransitions {
 
-    private static final float KEYGUARD_QUIESCENT_ALPHA = 0.5f;
     private static final int CONTENT_FADE_DURATION = 200;
 
     private final NavigationBarView mView;
@@ -81,8 +80,6 @@
         setKeyButtonViewQuiescentAlpha(mView.getRecentsButton(), alpha, animate);
         setKeyButtonViewQuiescentAlpha(mView.getMenuButton(), alpha, animate);
 
-        setKeyButtonViewQuiescentAlpha(mView.getSearchLight(), KEYGUARD_QUIESCENT_ALPHA, animate);
-
         applyBackButtonQuiescentAlpha(mode, animate);
 
         // apply to lights out
@@ -96,7 +93,6 @@
 
     public void applyBackButtonQuiescentAlpha(int mode, boolean animate) {
         float backAlpha = 0;
-        backAlpha = maxVisibleQuiescentAlpha(backAlpha, mView.getSearchLight());
         backAlpha = maxVisibleQuiescentAlpha(backAlpha, mView.getHomeButton());
         backAlpha = maxVisibleQuiescentAlpha(backAlpha, mView.getRecentsButton());
         backAlpha = maxVisibleQuiescentAlpha(backAlpha, mView.getMenuButton());
@@ -116,7 +112,6 @@
     public void setContentVisible(boolean visible) {
         final float alpha = visible ? 1 : 0;
         fadeContent(mView.getBackButton(), alpha);
-        fadeContent(mView.getSearchLight(), alpha);
     }
 
     private void fadeContent(View v, float alpha) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
index 089757a..21842bf 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
@@ -133,16 +133,6 @@
         }
     }
 
-    // simplified click handler to be used when device is in accessibility mode
-    private final OnClickListener mAccessibilityClickListener = new OnClickListener() {
-        @Override
-        public void onClick(View v) {
-            if (v.getId() == R.id.search_light) {
-                KeyguardTouchDelegate.getInstance(getContext()).showAssistant();
-            }
-        }
-    };
-
     private final OnClickListener mImeSwitcherClickListener = new OnClickListener() {
         @Override
         public void onClick(View view) {
@@ -246,11 +236,6 @@
         return mCurrentView.findViewById(R.id.ime_switcher);
     }
 
-    // for when home is disabled, but search isn't
-    public View getSearchLight() {
-        return mCurrentView.findViewById(R.id.search_light);
-    }
-
     private void getIcons(Resources res) {
         mBackIcon = res.getDrawable(R.drawable.ic_sysbar_back);
         mBackLandIcon = res.getDrawable(R.drawable.ic_sysbar_back_land);
@@ -345,9 +330,6 @@
         getHomeButton()   .setVisibility(disableHome       ? View.INVISIBLE : View.VISIBLE);
         getRecentsButton().setVisibility(disableRecent     ? View.INVISIBLE : View.VISIBLE);
 
-        final boolean showSearch = disableHome && !disableSearch;
-        setVisibleOrGone(getSearchLight(), showSearch);
-
         mBarTransitions.applyBackButtonQuiescentAlpha(mBarTransitions.getMode(), true /*animate*/);
     }
 
@@ -402,38 +384,6 @@
         mCurrentView = mRotatedViews[Surface.ROTATION_0];
 
         getImeSwitchButton().setOnClickListener(mImeSwitcherClickListener);
-
-        watchForAccessibilityChanges();
-    }
-
-    private void watchForAccessibilityChanges() {
-        final AccessibilityManager am =
-                (AccessibilityManager) getContext().getSystemService(Context.ACCESSIBILITY_SERVICE);
-
-        // Set the initial state
-        enableAccessibility(am.isTouchExplorationEnabled());
-
-        // Watch for changes
-        am.addTouchExplorationStateChangeListener(new TouchExplorationStateChangeListener() {
-            @Override
-            public void onTouchExplorationStateChanged(boolean enabled) {
-                enableAccessibility(enabled);
-            }
-        });
-    }
-
-    private void enableAccessibility(boolean touchEnabled) {
-        Log.v(TAG, "touchEnabled:"  + touchEnabled);
-
-        // Add a touch handler or accessibility click listener for camera and search buttons
-        // for all view orientations.
-        final OnClickListener onClickListener = touchEnabled ? mAccessibilityClickListener : null;
-        for (int i = 0; i < mRotatedViews.length; i++) {
-            final View searchLight = mRotatedViews[i].findViewById(R.id.search_light);
-            if (searchLight != null) {
-                searchLight.setOnClickListener(onClickListener);
-            }
-        }
     }
 
     public boolean isVertical() {
@@ -571,7 +521,6 @@
         dumpButton(pw, "home", getHomeButton());
         dumpButton(pw, "rcnt", getRecentsButton());
         dumpButton(pw, "menu", getMenuButton());
-        dumpButton(pw, "srch", getSearchLight());
 
         pw.println("    }");
     }
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 f5252a3..ee6d369 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
@@ -21,7 +21,9 @@
 import android.animation.ObjectAnimator;
 import android.animation.ValueAnimator;
 import android.content.Context;
+import android.content.res.Configuration;
 import android.util.AttributeSet;
+import android.util.Log;
 import android.view.MotionEvent;
 import android.view.VelocityTracker;
 import android.view.View;
@@ -40,11 +42,16 @@
 import com.android.systemui.statusbar.stack.NotificationStackScrollLayout;
 import com.android.systemui.statusbar.stack.StackStateAnimator;
 
+import java.util.ArrayList;
+
 public class NotificationPanelView extends PanelView implements
         ExpandableView.OnHeightChangedListener, ObservableScrollView.Listener,
-        View.OnClickListener {
+        View.OnClickListener, NotificationStackScrollLayout.OnOverscrollTopChangedListener,
+        KeyguardPageSwipeHelper.Callback {
 
-    PhoneStatusBar mStatusBar;
+    private static float EXPANSION_RUBBER_BAND_EXTRA_FACTOR = 0.6f;
+
+    private KeyguardPageSwipeHelper mPageSwiper;
     private StatusBarHeaderView mHeader;
     private View mQsContainer;
     private View mQsPanel;
@@ -58,7 +65,7 @@
 
     private int mTrackingPointer;
     private VelocityTracker mVelocityTracker;
-    private boolean mTracking;
+    private boolean mQsTracking;
 
     /**
      * Whether we are currently handling a motion gesture in #onInterceptTouchEvent, but haven't
@@ -66,6 +73,7 @@
      */
     private boolean mIntercepting;
     private boolean mQsExpanded;
+    private boolean mQsFullyExpanded;
     private boolean mKeyguardShowing;
     private float mInitialHeightOnTouch;
     private float mInitialTouchX;
@@ -79,12 +87,17 @@
     private int mQsPeekHeight;
     private float mNotificationTranslation;
     private int mStackScrollerIntrinsicPadding;
+    private boolean mStackScrollerOverscrolling;
     private boolean mQsExpansionEnabled = true;
     private ValueAnimator mQsExpansionAnimator;
     private FlingAnimationUtils mFlingAnimationUtils;
     private int mStatusBarMinHeight;
+    private boolean mHeaderHidden;
+    private int mNotificationsHeaderCollideDistance;
 
     private Interpolator mFastOutSlowInInterpolator;
+    private Interpolator mFastOutLinearInterpolator;
+    private Interpolator mLinearOutSlowInInterpolator;
     private ObjectAnimator mClockAnimator;
     private int mClockAnimationTarget = -1;
     private int mTopPaddingAdjustment;
@@ -92,6 +105,11 @@
             new KeyguardClockPositionAlgorithm();
     private KeyguardClockPositionAlgorithm.Result mClockPositionResult =
             new KeyguardClockPositionAlgorithm.Result();
+    private boolean mIsSwipedHorizontally;
+    private boolean mIsExpanding;
+    private KeyguardBottomAreaView mKeyguardBottomArea;
+    private boolean mBlockTouches;
+    private ArrayList<View> mSwipeTranslationViews = new ArrayList<>();
 
     public NotificationPanelView(Context context, AttributeSet attrs) {
         super(context, attrs);
@@ -127,8 +145,17 @@
         mNotificationStackScroller = (NotificationStackScrollLayout)
                 findViewById(R.id.notification_stack_scroller);
         mNotificationStackScroller.setOnHeightChangedListener(this);
+        mNotificationStackScroller.setOverscrollTopChangedListener(this);
         mFastOutSlowInInterpolator = AnimationUtils.loadInterpolator(getContext(),
                 android.R.interpolator.fast_out_slow_in);
+        mLinearOutSlowInInterpolator = AnimationUtils.loadInterpolator(getContext(),
+                android.R.interpolator.linear_out_slow_in);
+        mFastOutLinearInterpolator = AnimationUtils.loadInterpolator(getContext(),
+                android.R.interpolator.fast_out_linear_in);
+        mKeyguardBottomArea = (KeyguardBottomAreaView) findViewById(R.id.keyguard_bottom_area);
+        mSwipeTranslationViews.add(mNotificationStackScroller);
+        mSwipeTranslationViews.add(mKeyguardStatusView);
+        mPageSwiper = new KeyguardPageSwipeHelper(this, getContext());
     }
 
     @Override
@@ -141,6 +168,8 @@
         mStatusBarMinHeight = getResources().getDimensionPixelSize(
                 com.android.internal.R.dimen.status_bar_height);
         mQsPeekHeight = getResources().getDimensionPixelSize(R.dimen.qs_peek_height);
+        mNotificationsHeaderCollideDistance =
+                getResources().getDimensionPixelSize(R.dimen.header_notifications_collide_distance);
         mClockPositionAlgorithm.loadDimens(getResources());
     }
 
@@ -151,8 +180,14 @@
         // Calculate quick setting heights.
         mQsMinExpansionHeight = mHeader.getCollapsedHeight() + mQsPeekHeight;
         mQsMaxExpansionHeight = mHeader.getExpandedHeight() + mQsContainer.getHeight();
-        if (!mQsExpanded) {
-            setQsExpansion(mQsMinExpansionHeight);
+        if (mQsExpanded) {
+            if (mQsFullyExpanded) {
+                setQsStackScrollerPadding(mQsMaxExpansionHeight);
+            }
+        } else {
+            if (!mStackScrollerOverscrolling) {
+                setQsExpansion(mQsMinExpansionHeight);
+            }
             positionClockAndNotifications();
             mNotificationStackScroller.setStackHeight(getExpandedHeight());
         }
@@ -165,7 +200,10 @@
     private void positionClockAndNotifications() {
         boolean animateClock = mNotificationStackScroller.isAddOrRemoveAnimationPending();
         if (mStatusBar.getBarState() != StatusBarState.KEYGUARD) {
-            mStackScrollerIntrinsicPadding = mHeader.getBottom() + mQsPeekHeight
+            int bottom = mStackScrollerOverscrolling
+                    ? mHeader.getCollapsedHeight()
+                    : mHeader.getBottom();
+            mStackScrollerIntrinsicPadding = bottom + mQsPeekHeight
                     + mNotificationTopPadding;
             mTopPaddingAdjustment = 0;
         } else {
@@ -247,6 +285,13 @@
         mQsExpansionEnabled = qsExpansionEnabled;
     }
 
+    @Override
+    public void resetViews() {
+        mBlockTouches = false;
+        mPageSwiper.reset();
+        closeQs();
+    }
+
     public void closeQs() {
         cancelAnimation();
         setQsExpansion(mQsMinExpansionHeight);
@@ -263,9 +308,7 @@
     public void fling(float vel, boolean always) {
         GestureRecorder gr = ((PhoneStatusBarView) mBar).mBar.getGestureRecorder();
         if (gr != null) {
-            gr.tag(
-                "fling " + ((vel > 0) ? "open" : "closed"),
-                "notifications,v=" + vel);
+            gr.tag("fling " + ((vel > 0) ? "open" : "closed"), "notifications,v=" + vel);
         }
         super.fling(vel, always);
     }
@@ -283,6 +326,9 @@
 
     @Override
     public boolean onInterceptTouchEvent(MotionEvent event) {
+        if (mBlockTouches) {
+            return false;
+        }
         int pointerIndex = event.findPointerIndex(mTrackingPointer);
         if (pointerIndex < 0) {
             pointerIndex = 0;
@@ -298,7 +344,7 @@
                 mInitialTouchX = x;
                 initVelocityTracker();
                 trackMovement(event);
-                if (shouldIntercept(mInitialTouchX, mInitialTouchY, 0)) {
+                if (shouldQuickSettingsIntercept(mInitialTouchX, mInitialTouchY, 0)) {
                     getParent().requestDisallowInterceptTouchEvent(true);
                 }
                 break;
@@ -316,7 +362,7 @@
             case MotionEvent.ACTION_MOVE:
                 final float h = y - mInitialTouchY;
                 trackMovement(event);
-                if (mTracking) {
+                if (mQsTracking) {
 
                     // Already tracking because onOverscrolled was called. We need to update here
                     // so we don't stop for a frame until the next touch event gets handled in
@@ -327,12 +373,12 @@
                     return true;
                 }
                 if (Math.abs(h) > mTouchSlop && Math.abs(h) > Math.abs(x - mInitialTouchX)
-                        && shouldIntercept(mInitialTouchX, mInitialTouchY, h)) {
+                        && shouldQuickSettingsIntercept(mInitialTouchX, mInitialTouchY, h)) {
                     onQsExpansionStarted();
                     mInitialHeightOnTouch = mQsExpansionHeight;
                     mInitialTouchY = y;
                     mInitialTouchX = x;
-                    mTracking = true;
+                    mQsTracking = true;
                     mIntercepting = false;
                     return true;
                 }
@@ -341,9 +387,9 @@
             case MotionEvent.ACTION_CANCEL:
             case MotionEvent.ACTION_UP:
                 trackMovement(event);
-                if (mTracking) {
-                    flingWithCurrentVelocity();
-                    mTracking = false;
+                if (mQsTracking) {
+                    flingQsWithCurrentVelocity();
+                    mQsTracking = false;
                 }
                 mIntercepting = false;
                 break;
@@ -362,7 +408,7 @@
         super.requestDisallowInterceptTouchEvent(disallowIntercept);
     }
 
-    private void flingWithCurrentVelocity() {
+    private void flingQsWithCurrentVelocity() {
         float vel = getCurrentVelocity();
 
         // TODO: Better logic whether we should expand or not.
@@ -371,65 +417,83 @@
 
     @Override
     public boolean onTouchEvent(MotionEvent event) {
+        if (mBlockTouches) {
+            return false;
+        }
         // TODO: Handle doublefinger swipe to notifications again. Look at history for a reference
         // implementation.
-        if (mTracking) {
-            int pointerIndex = event.findPointerIndex(mTrackingPointer);
-            if (pointerIndex < 0) {
-                pointerIndex = 0;
-                mTrackingPointer = event.getPointerId(pointerIndex);
+        if (!mIsExpanding && !mQsExpanded && mStatusBar.getBarState() != StatusBarState.SHADE) {
+            mPageSwiper.onTouchEvent(event);
+            if (mPageSwiper.isSwipingInProgress()) {
+                return true;
             }
-            final float y = event.getY(pointerIndex);
-            final float x = event.getX(pointerIndex);
-
-            switch (event.getActionMasked()) {
-                case MotionEvent.ACTION_DOWN:
-                    mTracking = true;
-                    mInitialTouchY = y;
-                    mInitialTouchX = x;
-                    onQsExpansionStarted();
-                    mInitialHeightOnTouch = mQsExpansionHeight;
-                    initVelocityTracker();
-                    trackMovement(event);
-                    break;
-
-                case MotionEvent.ACTION_POINTER_UP:
-                    final int upPointer = event.getPointerId(event.getActionIndex());
-                    if (mTrackingPointer == upPointer) {
-                        // gesture is ongoing, find a new pointer to track
-                        final int newIndex = event.getPointerId(0) != upPointer ? 0 : 1;
-                        final float newY = event.getY(newIndex);
-                        final float newX = event.getX(newIndex);
-                        mTrackingPointer = event.getPointerId(newIndex);
-                        mInitialHeightOnTouch = mQsExpansionHeight;
-                        mInitialTouchY = newY;
-                        mInitialTouchX = newX;
-                    }
-                    break;
-
-                case MotionEvent.ACTION_MOVE:
-                    final float h = y - mInitialTouchY;
-                    setQsExpansion(h + mInitialHeightOnTouch);
-                    trackMovement(event);
-                    break;
-
-                case MotionEvent.ACTION_UP:
-                case MotionEvent.ACTION_CANCEL:
-                    mTracking = false;
-                    mTrackingPointer = -1;
-                    trackMovement(event);
-                    flingWithCurrentVelocity();
-                    if (mVelocityTracker != null) {
-                        mVelocityTracker.recycle();
-                        mVelocityTracker = null;
-                    }
-                    break;
-            }
-            return true;
+        }
+        if (mQsTracking || mQsExpanded) {
+            return onQsTouch(event);
         }
 
-        // Consume touch events when QS are expanded.
-        return mQsExpanded || super.onTouchEvent(event);
+        super.onTouchEvent(event);
+        return true;
+    }
+
+    @Override
+    protected boolean hasConflictingGestures() {
+        return mStatusBar.getBarState() != StatusBarState.SHADE;
+    }
+
+    private boolean onQsTouch(MotionEvent event) {
+        int pointerIndex = event.findPointerIndex(mTrackingPointer);
+        if (pointerIndex < 0) {
+            pointerIndex = 0;
+            mTrackingPointer = event.getPointerId(pointerIndex);
+        }
+        final float y = event.getY(pointerIndex);
+        final float x = event.getX(pointerIndex);
+
+        switch (event.getActionMasked()) {
+            case MotionEvent.ACTION_DOWN:
+                mQsTracking = true;
+                mInitialTouchY = y;
+                mInitialTouchX = x;
+                onQsExpansionStarted();
+                mInitialHeightOnTouch = mQsExpansionHeight;
+                initVelocityTracker();
+                trackMovement(event);
+                break;
+
+            case MotionEvent.ACTION_POINTER_UP:
+                final int upPointer = event.getPointerId(event.getActionIndex());
+                if (mTrackingPointer == upPointer) {
+                    // gesture is ongoing, find a new pointer to track
+                    final int newIndex = event.getPointerId(0) != upPointer ? 0 : 1;
+                    final float newY = event.getY(newIndex);
+                    final float newX = event.getX(newIndex);
+                    mTrackingPointer = event.getPointerId(newIndex);
+                    mInitialHeightOnTouch = mQsExpansionHeight;
+                    mInitialTouchY = newY;
+                    mInitialTouchX = newX;
+                }
+                break;
+
+            case MotionEvent.ACTION_MOVE:
+                final float h = y - mInitialTouchY;
+                setQsExpansion(h + mInitialHeightOnTouch);
+                trackMovement(event);
+                break;
+
+            case MotionEvent.ACTION_UP:
+            case MotionEvent.ACTION_CANCEL:
+                mQsTracking = false;
+                mTrackingPointer = -1;
+                trackMovement(event);
+                flingQsWithCurrentVelocity();
+                if (mVelocityTracker != null) {
+                    mVelocityTracker.recycle();
+                    mVelocityTracker = null;
+                }
+                break;
+        }
+        return true;
     }
 
     @Override
@@ -439,10 +503,20 @@
             mInitialHeightOnTouch = mQsExpansionHeight;
             mInitialTouchY = mLastTouchY;
             mInitialTouchX = mLastTouchX;
-            mTracking = true;
+            mQsTracking = true;
         }
     }
 
+
+    @Override
+    public void onOverscrollTopChanged(float amount) {
+        cancelAnimation();
+        float rounded = amount >= 1f ? amount : 0f;
+        mStackScrollerOverscrolling = rounded != 0f;
+        setQsExpansion(mQsMinExpansionHeight + rounded);
+        updateQsState();
+    }
+
     private void onQsExpansionStarted() {
         onQsExpansionStarted(0);
     }
@@ -470,9 +544,10 @@
     }
 
     private void updateQsState() {
-        mHeader.setExpanded(mQsExpanded);
+        boolean expandVisually = mQsExpanded || mStackScrollerOverscrolling;
+        mHeader.setExpanded(expandVisually, mStackScrollerOverscrolling);
         mNotificationStackScroller.setEnabled(!mQsExpanded);
-        mQsPanel.setVisibility(mQsExpanded ? View.VISIBLE : View.INVISIBLE);
+        mQsPanel.setVisibility(expandVisually ? View.VISIBLE : View.INVISIBLE);
         mQsContainer.setVisibility(mKeyguardShowing && !mQsExpanded
                 ? View.INVISIBLE
                 : View.VISIBLE);
@@ -481,7 +556,8 @@
 
     private void setQsExpansion(float height) {
         height = Math.min(Math.max(height, mQsMinExpansionHeight), mQsMaxExpansionHeight);
-        if (height > mQsMinExpansionHeight && !mQsExpanded) {
+        mQsFullyExpanded = height == mQsMaxExpansionHeight;
+        if (height > mQsMinExpansionHeight && !mQsExpanded && !mStackScrollerOverscrolling) {
             setQsExpanded(true);
         } else if (height <= mQsMinExpansionHeight && mQsExpanded) {
             setQsExpanded(false);
@@ -489,7 +565,9 @@
         mQsExpansionHeight = height;
         mHeader.setExpansion(height - mQsPeekHeight);
         setQsTranslation(height);
-        setQsStackScrollerPadding(height);
+        if (!mStackScrollerOverscrolling) {
+            setQsStackScrollerPadding(height);
+        }
         mStatusBar.userActivity();
     }
 
@@ -569,7 +647,7 @@
     /**
      * @return Whether we should intercept a gesture to open Quick Settings.
      */
-    private boolean shouldIntercept(float x, float y, float yDiff) {
+    private boolean shouldQuickSettingsIntercept(float x, float y, float yDiff) {
         if (!mQsExpansionEnabled) {
             return false;
         }
@@ -641,31 +719,73 @@
             positionClockAndNotifications();
         }
         mNotificationStackScroller.setStackHeight(expandedHeight);
+        updateKeyguardHeaderVisibility();
+    }
+
+    /**
+     * Hides the header when notifications are colliding with it.
+     */
+    private void updateKeyguardHeaderVisibility() {
+        if (mStatusBar.getBarState() == StatusBarState.KEYGUARD
+                || mStatusBar.getBarState() == StatusBarState.SHADE_LOCKED) {
+            boolean hidden = mNotificationStackScroller.getNotificationsTopY()
+                    <= mHeader.getBottom() + mNotificationsHeaderCollideDistance;
+            if (hidden && !mHeaderHidden) {
+                mHeader.animate()
+                        .alpha(0f)
+                        .withLayer()
+                        .translationY(-mHeader.getHeight()/2)
+                        .setInterpolator(mFastOutLinearInterpolator)
+                        .setDuration(200);
+            } else if (!hidden && mHeaderHidden) {
+                mHeader.animate()
+                        .alpha(1f)
+                        .withLayer()
+                        .translationY(0)
+                        .setInterpolator(mLinearOutSlowInInterpolator)
+                        .setDuration(200);
+            }
+            mHeaderHidden = hidden;
+        } else {
+            mHeader.animate().cancel();
+            mHeader.setAlpha(1f);
+            mHeader.setTranslationY(0f);
+            if (mHeader.getLayerType() != LAYER_TYPE_NONE) {
+                mHeader.setLayerType(LAYER_TYPE_NONE, null);
+            }
+            mHeaderHidden = false;
+        }
+
     }
 
     @Override
     protected void onExpandingStarted() {
         super.onExpandingStarted();
         mNotificationStackScroller.onExpansionStarted();
+        mIsExpanding = true;
     }
 
     @Override
     protected void onExpandingFinished() {
         super.onExpandingFinished();
         mNotificationStackScroller.onExpansionStopped();
+        mIsExpanding = false;
     }
 
     @Override
     protected void onOverExpansionChanged(float overExpansion) {
         float currentOverScroll = mNotificationStackScroller.getCurrentOverScrolledPixels(true);
-        mNotificationStackScroller.setOverScrolledPixels(currentOverScroll + overExpansion
-                        - mOverExpansion, true /* onTop */, false /* animate */);
+        float expansionChange = overExpansion - mOverExpansion;
+        expansionChange *= EXPANSION_RUBBER_BAND_EXTRA_FACTOR;
+        mNotificationStackScroller.setOverScrolledPixels(currentOverScroll + expansionChange,
+                true /* onTop */,
+                false /* animate */);
         super.onOverExpansionChanged(overExpansion);
     }
 
     @Override
-    protected void onTrackingStopped() {
-        super.onTrackingStopped();
+    protected void onTrackingStopped(boolean expand) {
+        super.onTrackingStopped(expand);
         mOverExpansion = 0.0f;
         mNotificationStackScroller.setOverScrolledPixels(0.0f, true /* onTop */,
                 true /* animate */);
@@ -686,6 +806,12 @@
     }
 
     @Override
+    protected void onConfigurationChanged(Configuration newConfig) {
+        super.onConfigurationChanged(newConfig);
+        mPageSwiper.onConfigurationChanged();
+    }
+
+    @Override
     public void onClick(View v) {
         if (v == mHeader.getBackgroundView()) {
             onQsExpansionStarted();
@@ -696,4 +822,40 @@
             }
         }
     }
+
+    @Override
+    public void onAnimationToSideStarted(boolean rightPage) {
+        if (rightPage) {
+            mKeyguardBottomArea.launchCamera();
+        } else {
+            mKeyguardBottomArea.launchPhone();
+        }
+        mBlockTouches = true;
+    }
+
+
+    @Override
+    public float getPageWidth() {
+        return getWidth();
+    }
+
+    @Override
+    public ArrayList<View> getTranslationViews() {
+        return mSwipeTranslationViews;
+    }
+
+    @Override
+    public View getLeftIcon() {
+        return mKeyguardBottomArea.getPhoneImageView();
+    }
+
+    @Override
+    public View getCenterIcon() {
+        return mKeyguardBottomArea.getLockIcon();
+    }
+
+    @Override
+    public View getRightIcon() {
+        return mKeyguardBottomArea.getCameraImageView();
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelBar.java
index 8800625..b94f6f3 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelBar.java
@@ -191,6 +191,7 @@
                 pv.setExpandedFraction(0); // just in case
                 pv.setVisibility(View.GONE);
                 pv.cancelPeek();
+                pv.resetViews();
             }
         }
         if (DEBUG) LOG("collapseAllPanels: animate=%s waiting=%s", animate, waiting);
@@ -222,7 +223,11 @@
         }
     }
 
-    public void onTrackingStopped(PanelView panel) {
+    public void onTrackingStopped(PanelView panel, boolean expand) {
         mTracking = false;
     }
+
+    public void onExpandingFinished() {
+
+    }
 }
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 7c1f2cf..4686933 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java
@@ -27,15 +27,18 @@
 import android.util.Log;
 import android.view.MotionEvent;
 import android.view.ViewConfiguration;
+import android.view.animation.AnimationUtils;
+import android.view.animation.Interpolator;
 import android.widget.FrameLayout;
 
 import com.android.systemui.R;
 import com.android.systemui.statusbar.FlingAnimationUtils;
+import com.android.systemui.statusbar.StatusBarState;
 
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
 
-public class PanelView extends FrameLayout {
+public abstract class PanelView extends FrameLayout {
     public static final boolean DEBUG = PanelBar.DEBUG;
     public static final String TAG = PanelView.class.getSimpleName();
     protected float mOverExpansion;
@@ -44,13 +47,16 @@
         Log.v(TAG, (mViewName != null ? (mViewName + ": ") : "") + String.format(fmt, args));
     }
 
+    protected PhoneStatusBar mStatusBar;
     private float mPeekHeight;
+    private float mHintDistance;
     private float mInitialOffsetOnTouch;
     private float mExpandedFraction = 0;
     private float mExpandedHeight = 0;
     private boolean mJustPeeked;
     private boolean mClosing;
     private boolean mTracking;
+    private boolean mTouchSlopExceeded;
     private int mTrackingPointer;
     protected int mTouchSlop;
 
@@ -66,7 +72,11 @@
     private float mInitialTouchY;
     private float mInitialTouchX;
 
+    private Interpolator mLinearOutSlowInInterpolator;
+    private Interpolator mBounceInterpolator;
+
     protected void onExpandingFinished() {
+        mBar.onExpandingFinished();
     }
 
     protected void onExpandingStarted() {
@@ -88,6 +98,9 @@
     public PanelView(Context context, AttributeSet attrs) {
         super(context, attrs);
         mFlingAnimationUtils = new FlingAnimationUtils(context, 0.6f);
+        mLinearOutSlowInInterpolator =
+                AnimationUtils.loadInterpolator(context, android.R.interpolator.fast_out_slow_in);
+        mBounceInterpolator = new BounceInterpolator();
     }
 
     protected void loadDimens() {
@@ -97,6 +110,7 @@
 
         final ViewConfiguration configuration = ViewConfiguration.get(getContext());
         mTouchSlop = configuration.getScaledTouchSlop();
+        mHintDistance = res.getDimension(R.dimen.hint_move_distance);
     }
 
     private void trackMovement(MotionEvent event) {
@@ -129,21 +143,25 @@
         final float y = event.getY(pointerIndex);
         final float x = event.getX(pointerIndex);
 
+        boolean waitForTouchSlop = hasConflictingGestures();
+
         switch (event.getActionMasked()) {
             case MotionEvent.ACTION_DOWN:
-                mTracking = true;
 
                 mInitialTouchY = y;
                 mInitialTouchX = x;
+                mInitialOffsetOnTouch = mExpandedHeight;
+                mTouchSlopExceeded = false;
                 if (mVelocityTracker == null) {
                     initVelocityTracker();
                 }
                 trackMovement(event);
-                if (mHeightAnimator != null) {
-                    mHeightAnimator.cancel(); // end any outstanding animations
+                if (!waitForTouchSlop || mHeightAnimator != null) {
+                    if (mHeightAnimator != null) {
+                        mHeightAnimator.cancel(); // end any outstanding animations
+                    }
+                    onTrackingStarted();
                 }
-                onTrackingStarted();
-                mInitialOffsetOnTouch = mExpandedHeight;
                 if (mExpandedHeight == 0) {
                     mJustPeeked = true;
                     runPeekAnimation();
@@ -165,15 +183,29 @@
                 break;
 
             case MotionEvent.ACTION_MOVE:
-                final float h = y - mInitialTouchY + mInitialOffsetOnTouch;
-                if (h > mPeekHeight) {
+                float h = y - mInitialTouchY;
+                if (Math.abs(h) > mTouchSlop && Math.abs(h) > Math.abs(x - mInitialTouchX)) {
+                    mTouchSlopExceeded = true;
+                    if (waitForTouchSlop && !mTracking) {
+                        mInitialOffsetOnTouch = mExpandedHeight;
+                        mInitialTouchX = x;
+                        mInitialTouchY = y;
+                        if (mHeightAnimator != null) {
+                            mHeightAnimator.cancel(); // end any outstanding animations
+                        }
+                        onTrackingStarted();
+                        h = 0;
+                    }
+                }
+                final float newHeight = h + mInitialOffsetOnTouch;
+                if (newHeight > mPeekHeight) {
                     if (mPeekAnimator != null && mPeekAnimator.isStarted()) {
                         mPeekAnimator.cancel();
                     }
                     mJustPeeked = false;
                 }
-                if (!mJustPeeked) {
-                    setExpandedHeightInternal(h);
+                if (!mJustPeeked && (!waitForTouchSlop || mTracking)) {
+                    setExpandedHeightInternal(newHeight);
                     mBar.panelExpansionChanged(PanelView.this, mExpandedFraction);
                 }
 
@@ -182,25 +214,35 @@
 
             case MotionEvent.ACTION_UP:
             case MotionEvent.ACTION_CANCEL:
-                mTracking = false;
                 mTrackingPointer = -1;
-                onTrackingStopped();
                 trackMovement(event);
-                flingWithCurrentVelocity();
+                if (mTracking && mTouchSlopExceeded) {
+                    float vel = getCurrentVelocity();
+                    boolean expand = flingExpands(vel);
+                    onTrackingStopped(expand);
+                    fling(vel, expand);
+                } else {
+                    boolean expands = onEmptySpaceClick();
+                    onTrackingStopped(expands);
+                }
                 if (mVelocityTracker != null) {
                     mVelocityTracker.recycle();
                     mVelocityTracker = null;
                 }
                 break;
         }
-        return true;
+        return !waitForTouchSlop || mTracking;
     }
 
-    protected void onTrackingStopped() {
-        mBar.onTrackingStopped(PanelView.this);
+    protected abstract boolean hasConflictingGestures();
+
+    protected void onTrackingStopped(boolean expand) {
+        mTracking = false;
+        mBar.onTrackingStopped(PanelView.this, expand);
     }
 
     protected void onTrackingStarted() {
+        mTracking = true;
         mBar.onTrackingStarted(PanelView.this);
         onExpandingStarted();
     }
@@ -243,6 +285,7 @@
                 }
                 mInitialTouchY = y;
                 mInitialTouchX = x;
+                mTouchSlopExceeded = false;
                 initVelocityTracker();
                 trackMovement(event);
                 break;
@@ -266,6 +309,7 @@
                         mInitialTouchY = y;
                         mInitialTouchX = x;
                         mTracking = true;
+                        mTouchSlopExceeded = true;
                         onTrackingStarted();
                         return true;
                     }
@@ -303,21 +347,27 @@
         mMaxPanelHeight = -1;
     }
 
-    private void flingWithCurrentVelocity() {
-        float vel = getCurrentVelocity();
-        boolean expand;
+    /**
+     * @param vel the current velocity of the motion
+     * @return whether a fling should expands the panel; contracts otherwise
+     */
+    private boolean flingExpands(float vel) {
         if (Math.abs(vel) < mFlingAnimationUtils.getMinVelocityPxPerSecond()) {
-            expand = getExpandedFraction() > 0.5f;
+            return getExpandedFraction() > 0.5f;
         } else {
-            expand = vel > 0;
+            return vel > 0;
         }
-        fling(vel, expand);
     }
 
     protected void fling(float vel, boolean expand) {
         cancelPeek();
         float target = expand ? getMaxPanelHeight() : 0.0f;
-        ValueAnimator animator = ValueAnimator.ofFloat(mExpandedHeight, target);
+        if (target == mExpandedHeight) {
+            onExpandingFinished();
+            mBar.panelExpansionChanged(this, mExpandedFraction);
+            return;
+        }
+        ValueAnimator animator = createHeightAnimator(target);
         if (expand) {
             mFlingAnimationUtils.apply(animator, mExpandedHeight, target, vel, getHeight());
         } else {
@@ -329,12 +379,6 @@
                 animator.setDuration((long) (animator.getDuration() / 1.75f));
             }
         }
-        animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
-            @Override
-            public void onAnimationUpdate(ValueAnimator animation) {
-                setExpandedHeight((Float) animation.getAnimatedValue());
-            }
-        });
         animator.addListener(new AnimatorListenerAdapter() {
             @Override
             public void onAnimationEnd(Animator animation) {
@@ -403,7 +447,7 @@
 
     public void setExpandedHeightInternal(float h) {
         float fh = getMaxPanelHeight();
-        mExpandedHeight = Math.min(fh, h);
+        mExpandedHeight = Math.max(0, Math.min(fh, h));
         float overExpansion = h - fh;
         overExpansion = Math.max(0, overExpansion);
         if (overExpansion != mOverExpansion) {
@@ -415,16 +459,14 @@
         }
 
         onHeightUpdated(mExpandedHeight);
-        mExpandedFraction = Math.min(1f, (fh == 0) ? 0 : h / fh);
+        mExpandedFraction = Math.min(1f, (fh == 0) ? 0 : mExpandedHeight / fh);
     }
 
     protected void onOverExpansionChanged(float overExpansion) {
         mOverExpansion = overExpansion;
     }
 
-    protected void onHeightUpdated(float expandedHeight) {
-        requestLayout();
-    }
+    protected abstract void onHeightUpdated(float expandedHeight);
 
     /**
      * This returns the maximum height of the panel. Children should override this if their
@@ -499,6 +541,101 @@
         }
     }
 
+    protected void startUnlockHintAnimation() {
+
+        // We don't need to hint the user if an animation is already running or the user is changing
+        // the expansion.
+        if (mHeightAnimator != null || mTracking) {
+            return;
+        }
+        cancelPeek();
+        onExpandingStarted();
+        startUnlockHintAnimationPhase1();
+        mStatusBar.onUnlockHintStarted();
+    }
+
+    /**
+     * Phase 1: Move everything upwards.
+     */
+    private void startUnlockHintAnimationPhase1() {
+        float target = Math.max(0, getMaxPanelHeight() - mHintDistance);
+        ValueAnimator animator = createHeightAnimator(target);
+        animator.setDuration(250);
+        animator.setInterpolator(mLinearOutSlowInInterpolator);
+        animator.addListener(new AnimatorListenerAdapter() {
+            private boolean mCancelled;
+
+            @Override
+            public void onAnimationCancel(Animator animation) {
+                mCancelled = true;
+            }
+
+            @Override
+            public void onAnimationEnd(Animator animation) {
+                if (mCancelled) {
+                    mHeightAnimator = null;
+                    onExpandingFinished();
+                    mStatusBar.onUnlockHintFinished();
+                } else {
+                    startUnlockHintAnimationPhase2();
+                }
+            }
+        });
+        animator.start();
+        mHeightAnimator = animator;
+    }
+
+    /**
+     * Phase 2: Bounce down.
+     */
+    private void startUnlockHintAnimationPhase2() {
+        ValueAnimator animator = createHeightAnimator(getMaxPanelHeight());
+        animator.setDuration(450);
+        animator.setInterpolator(mBounceInterpolator);
+        animator.addListener(new AnimatorListenerAdapter() {
+            @Override
+            public void onAnimationEnd(Animator animation) {
+                mHeightAnimator = null;
+                onExpandingFinished();
+                mStatusBar.onUnlockHintFinished();
+            }
+        });
+        animator.start();
+        mHeightAnimator = animator;
+    }
+
+    private ValueAnimator createHeightAnimator(float targetHeight) {
+        ValueAnimator animator = ValueAnimator.ofFloat(mExpandedHeight, targetHeight);
+        animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
+            @Override
+            public void onAnimationUpdate(ValueAnimator animation) {
+                setExpandedHeight((Float) animation.getAnimatedValue());
+            }
+        });
+        return animator;
+    }
+
+    /**
+     * Gets called when the user performs a click anywhere in the empty area of the panel.
+     *
+     * @return whether the panel will be expanded after the action performed by this method
+     */
+    private boolean onEmptySpaceClick() {
+        switch (mStatusBar.getBarState()) {
+            case StatusBarState.KEYGUARD:
+                startUnlockHintAnimation();
+                return true;
+            case StatusBarState.SHADE_LOCKED:
+                // TODO: Go to Keyguard again.
+                return true;
+            case StatusBarState.SHADE:
+                collapse();
+                return false;
+            default:
+                return true;
+        }
+    }
+
     public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
         pw.println(String.format("[PanelView(%s): expandedHeight=%f maxPanelHeight=%d closing=%s"
                 + " tracking=%s justPeeked=%s peekAnim=%s%s timeAnim=%s%s"
@@ -513,4 +650,6 @@
                 mHeightAnimator, ((mHeightAnimator !=null && mHeightAnimator.isStarted())?" (started)":"")
         ));
     }
+
+    public abstract void resetViews();
 }
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 dee1333..b1216e6 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
@@ -64,6 +64,7 @@
 import android.os.UserHandle;
 import android.provider.Settings;
 import android.provider.Settings.Global;
+import android.service.notification.NotificationListenerService.Ranking;
 import android.service.notification.StatusBarNotification;
 import android.util.ArraySet;
 import android.util.DisplayMetrics;
@@ -86,7 +87,6 @@
 import android.view.animation.AnimationUtils;
 import android.view.animation.DecelerateInterpolator;
 import android.widget.FrameLayout;
-import android.widget.ImageView;
 import android.widget.LinearLayout;
 import android.widget.TextView;
 
@@ -121,10 +121,12 @@
 import com.android.systemui.statusbar.policy.LocationControllerImpl;
 import com.android.systemui.statusbar.policy.NetworkControllerImpl;
 import com.android.systemui.statusbar.policy.RotationLockControllerImpl;
+import com.android.systemui.statusbar.policy.ZenModeController;
 import com.android.systemui.statusbar.policy.ZenModeControllerImpl;
 import com.android.systemui.statusbar.stack.NotificationStackScrollLayout;
 import com.android.systemui.statusbar.stack.NotificationStackScrollLayout.OnChildLocationsChangedListener;
 import com.android.systemui.statusbar.stack.StackScrollState.ViewState;
+import com.android.systemui.volume.VolumeComponent;
 
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
@@ -196,8 +198,9 @@
     NetworkControllerImpl mNetworkController;
     RotationLockControllerImpl mRotationLockController;
     UserInfoController mUserInfoController;
-    ZenModeControllerImpl mZenModeController;
+    ZenModeController mZenModeController;
     CastControllerImpl mCastController;
+    VolumeComponent mVolumeComponent;
 
     int mNaturalBarHeight = -1;
     int mIconSize = -1;
@@ -209,6 +212,7 @@
     PhoneStatusBarView mStatusBarView;
     private int mStatusBarWindowState = WINDOW_STATE_SHOWING;
     private StatusBarWindowManager mStatusBarWindowManager;
+    private UnlockMethodCache mUnlockMethodCache;
 
     int mPixelFormat;
     Object mQueueLock = new Object();
@@ -529,6 +533,7 @@
                     Settings.Global.getUriFor(SETTING_HEADS_UP_TICKER), true,
                     mHeadsUpObserver);
         }
+        mUnlockMethodCache = UnlockMethodCache.getInstance(mContext);
         startKeyguard();
     }
 
@@ -690,7 +695,8 @@
             mRotationLockController = new RotationLockControllerImpl(mContext);
         }
         mUserInfoController = new UserInfoController(mContext);
-        mZenModeController = new ZenModeControllerImpl(mContext, mHandler);
+        mVolumeComponent = getComponent(VolumeComponent.class);
+        mZenModeController = mVolumeComponent.getZenController();
         mCastController = new CastControllerImpl(mContext);
         final SignalClusterView signalCluster =
                 (SignalClusterView)mStatusBarView.findViewById(R.id.signal_cluster);
@@ -753,7 +759,7 @@
             final QSTileHost qsh = new QSTileHost(mContext, this,
                     mBluetoothController, mLocationController, mRotationLockController,
                     mNetworkController, mZenModeController, null /*tethering*/,
-                    mCastController);
+                    mCastController, mVolumeComponent);
             for (QSTile<?> tile : qsh.getTiles()) {
                 mQSPanel.addTile(tile);
             }
@@ -890,7 +896,7 @@
         }
     };
 
-    View.OnTouchListener mHomeSearchActionListener = new View.OnTouchListener() {
+    View.OnTouchListener mHomeActionListener = new View.OnTouchListener() {
         public boolean onTouch(View v, MotionEvent event) {
             switch(event.getAction()) {
             case MotionEvent.ACTION_DOWN:
@@ -925,8 +931,7 @@
 
         mNavigationBarView.getRecentsButton().setOnClickListener(mRecentsClickListener);
         mNavigationBarView.getRecentsButton().setOnTouchListener(mRecentsPreloadOnTouchListener);
-        mNavigationBarView.getHomeButton().setOnTouchListener(mHomeSearchActionListener);
-        mNavigationBarView.getSearchLight().setOnTouchListener(mHomeSearchActionListener);
+        mNavigationBarView.getHomeButton().setOnTouchListener(mHomeActionListener);
         updateSearchPanel();
     }
 
@@ -975,8 +980,10 @@
     }
 
     private void addHeadsUpView() {
+        int headsUpHeight = mContext.getResources()
+                .getDimensionPixelSize(R.dimen.heads_up_window_height);
         WindowManager.LayoutParams lp = new WindowManager.LayoutParams(
-                LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT,
+                LayoutParams.MATCH_PARENT, headsUpHeight,
                 WindowManager.LayoutParams.TYPE_STATUS_BAR_PANEL, // above the status bar!
                 WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN
                     | WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS
@@ -1039,13 +1046,15 @@
     }
 
     @Override
-    public void addNotificationInternal(StatusBarNotification notification) {
+    public void addNotificationInternal(StatusBarNotification notification, Ranking ranking) {
         if (DEBUG) Log.d(TAG, "addNotification score=" + notification.getScore());
         Entry shadeEntry = createNotificationViews(notification);
         if (shadeEntry == null) {
             return;
         }
-        if (mZenMode != Global.ZEN_MODE_OFF && mIntercepted.tryIntercept(notification)) {
+        if (mZenMode != Global.ZEN_MODE_OFF && mIntercepted.tryIntercept(notification, ranking)) {
+            // Forward the ranking so we can sort the new notification.
+            mNotificationData.updateRanking(ranking);
             return;
         }
         if (mUseHeadsUp && shouldInterrupt(notification)) {
@@ -1085,7 +1094,7 @@
                 tick(notification, true);
             }
         }
-        addNotificationViews(shadeEntry);
+        addNotificationViews(shadeEntry, ranking);
         // Recalculate the position of the sliding windows and the titles.
         setAreThereNotifications();
         updateExpandedViewPos(EXPANDED_LEAVE_ALONE);
@@ -1101,14 +1110,21 @@
     }
 
     @Override
-    public void updateNotification(StatusBarNotification notification) {
-        super.updateNotification(notification);
+    public void updateNotificationInternal(StatusBarNotification notification, Ranking ranking) {
+        super.updateNotificationInternal(notification, ranking);
         mIntercepted.update(notification);
     }
 
     @Override
-    public void removeNotificationInternal(String key) {
-        StatusBarNotification old = removeNotificationViews(key);
+    protected void updateRankingInternal(Ranking ranking) {
+        mNotificationData.updateRanking(ranking);
+        mIntercepted.retryIntercepts(ranking);
+        updateNotifications();
+    }
+
+    @Override
+    public void removeNotificationInternal(String key, Ranking ranking) {
+        StatusBarNotification old = removeNotificationViews(key, ranking);
         if (SPEW) Log.d(TAG, "removeNotification key=" + key + " old=" + old);
 
         if (old != null) {
@@ -1145,7 +1161,7 @@
             R.integer.config_show_search_delay);
     }
 
-    private void loadNotificationShade() {
+    private void updateNotificationShade() {
         if (mStackScroller == null) return;
 
         int N = mNotificationData.size();
@@ -1155,7 +1171,7 @@
         final boolean provisioned = isDeviceProvisioned();
         // If the device hasn't been through Setup, we only show system notifications
         for (int i=0; i<N; i++) {
-            Entry ent = mNotificationData.get(N-i-1);
+            Entry ent = mNotificationData.get(i);
             if (!(provisioned || showNotificationEvenIfUnprovisioned(ent.notification))) continue;
 
             // TODO How do we want to badge notifcations from profiles.
@@ -1186,26 +1202,75 @@
         for (int i=0; i<toShow.size(); i++) {
             View v = toShow.get(i);
             if (v.getParent() == null) {
-                mStackScroller.addView(v, i);
+                mStackScroller.addView(v);
             }
         }
 
+        // So after all this work notifications still aren't sorted correctly.
+        // Let's do that now by advancing through toShow and mStackScroller in
+        // lock-step, making sure mStackScroller matches what we see in toShow.
+        int j = 0;
+        for (int i = 0; i < mStackScroller.getChildCount(); i++) {
+            View child = mStackScroller.getChildAt(i);
+            if (!(child instanceof ExpandableNotificationRow)) {
+                // We don't care about non-notification views.
+                continue;
+            }
+
+            if (child == toShow.get(j)) {
+                // Everything is well, advance both lists.
+                j++;
+                continue;
+            }
+
+            // Oops, wrong notification at this position. Put the right one
+            // here and advance both lists.
+            mStackScroller.changeViewPosition(toShow.get(j), i);
+            j++;
+        }
+        updateRowStates();
+        updateSpeedbump();
         mNotificationPanel.setQsExpansionEnabled(provisioned && mUserSetup);
     }
 
+    private void updateSpeedbump() {
+        int speedbumpIndex = -1;
+        int currentIndex = 0;
+        for (int i = 0; i < mNotificationData.size(); i++) {
+            Entry entry = mNotificationData.get(i);
+            if (entry.row.getParent() == null) {
+                // This view isn't even added, so the stack scroller doesn't
+                // know about it. Ignore completely.
+                continue;
+            }
+            if (entry.row.getVisibility() != View.GONE &&
+                    mNotificationData.isAmbient(entry.key)) {
+                speedbumpIndex = currentIndex;
+                break;
+            }
+            currentIndex++;
+        }
+        mStackScroller.updateSpeedBumpIndex(speedbumpIndex);
+    }
+
     @Override
-    protected void updateNotificationIcons() {
+    protected void updateNotifications() {
+        // TODO: Move this into updateNotificationIcons()?
         if (mNotificationIcons == null) return;
 
-        loadNotificationShade();
+        updateNotificationShade();
+        updateNotificationIcons();
+    }
 
+    private void updateNotificationIcons() {
         final LinearLayout.LayoutParams params
             = new LinearLayout.LayoutParams(mIconSize + 2*mIconHPadding, mNaturalBarHeight);
 
         int N = mNotificationData.size();
 
         if (DEBUG) {
-            Log.d(TAG, "refreshing icons: " + N + " notifications, mNotificationIcons=" + mNotificationIcons);
+            Log.d(TAG, "refreshing icons: " + N + " notifications, mNotificationIcons=" +
+                    mNotificationIcons);
         }
 
         ArrayList<View> toShow = new ArrayList<View>();
@@ -1213,7 +1278,7 @@
         final boolean provisioned = isDeviceProvisioned();
         // If the device hasn't been through Setup, we only show system notifications
         for (int i=0; i<N; i++) {
-            Entry ent = mNotificationData.get(N-i-1);
+            Entry ent = mNotificationData.get(i);
             if (!((provisioned && ent.notification.getScore() >= HIDE_ICONS_BELOW_SCORE)
                     || showNotificationEvenIfUnprovisioned(ent.notification))) continue;
             if (!notificationIsForCurrentProfiles(ent.notification)) continue;
@@ -2292,16 +2357,24 @@
         }
     };
 
-    public void startActivityDismissingKeyguard(Intent intent, boolean onlyProvisioned) {
+    public void startActivityDismissingKeyguard(final Intent intent, boolean onlyProvisioned) {
         if (onlyProvisioned && !isDeviceProvisioned()) return;
-        try {
-            // Dismiss the lock screen when Settings starts.
-            ActivityManagerNative.getDefault().dismissKeyguardOnNextActivity();
-        } catch (RemoteException e) {
-        }
-        intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP);
-        mContext.startActivityAsUser(intent, new UserHandle(UserHandle.USER_CURRENT));
-        animateCollapsePanels();
+
+        dismissKeyguardThenExecute(new OnDismissAction() {
+            @Override
+            public boolean onDismiss() {
+                try {
+                    // Dismiss the lock screen when Settings starts.
+                    ActivityManagerNative.getDefault().dismissKeyguardOnNextActivity();
+                } catch (RemoteException e) {
+                }
+                intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP);
+                mContext.startActivityAsUser(intent, new UserHandle(UserHandle.USER_CURRENT));
+                animateCollapsePanels();
+
+                return DELAY_DISMISS_TO_ACTIVITY_LAUNCH;
+            }
+        });
     }
 
     private View.OnClickListener mClockClickListener = new View.OnClickListener() {
@@ -2362,7 +2435,7 @@
     };
 
     @Override
-    protected void startNotificationActivity(OnDismissAction action) {
+    protected void dismissKeyguardThenExecute(OnDismissAction action) {
         if (mStatusBarKeyguardViewManager.isShowing()) {
             mStatusBarKeyguardViewManager.dismissWithAction(action);
         } else {
@@ -2390,7 +2463,7 @@
     public void userSwitched(int newUserId) {
         if (MULTIUSER_DEBUG) mNotificationPanelDebugText.setText("USER " + newUserId);
         animateCollapsePanels();
-        updateNotificationIcons();
+        updateNotifications();
         resetUserSetupObserver();
     }
 
@@ -2776,7 +2849,7 @@
             mKeyguardStatusView.setVisibility(View.VISIBLE);
             mKeyguardIndicationTextView.setVisibility(View.VISIBLE);
             mKeyguardIndicationTextView.switchIndication(mKeyguardHotwordPhrase);
-            mNotificationPanel.closeQs();
+            mNotificationPanel.resetViews();
         } else {
             mKeyguardStatusView.setVisibility(View.GONE);
             mKeyguardIndicationTextView.setVisibility(View.GONE);
@@ -2795,10 +2868,8 @@
 
         updateStackScrollerState();
         updatePublicMode();
-        updateRowStates();
-        updateSpeedBump();
+        updateNotifications();
         checkBarModes();
-        updateNotificationIcons();
         updateCarrierLabelVisibility(false);
     }
 
@@ -2886,14 +2957,21 @@
     }
 
     public void onTrackingStarted() {
-        if (mState == StatusBarState.KEYGUARD) {
-            mKeyguardIndicationTextView.switchIndication(R.string.keyguard_unlock);
-        }
     }
 
-    public void onTrackingStopped() {
-        if (mState == StatusBarState.KEYGUARD) {
-            mKeyguardIndicationTextView.switchIndication(mKeyguardHotwordPhrase);
+    public void onUnlockHintStarted() {
+        mKeyguardIndicationTextView.switchIndication(R.string.keyguard_unlock);
+    }
+
+    public void onUnlockHintFinished() {
+        mKeyguardIndicationTextView.switchIndication(mKeyguardHotwordPhrase);
+    }
+
+    public void onTrackingStopped(boolean expand) {
+        if (mState == StatusBarState.KEYGUARD || mState == StatusBarState.SHADE_LOCKED) {
+            if (!expand && !mUnlockMethodCache.isMethodInsecure()) {
+                showBouncer();
+            }
         }
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java
index 5fdf5bf..910d88c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java
@@ -148,9 +148,15 @@
     }
 
     @Override
-    public void onTrackingStopped(PanelView panel) {
-        super.onTrackingStopped(panel);
-        mBar.onTrackingStopped();
+    public void onTrackingStopped(PanelView panel, boolean expand) {
+        super.onTrackingStopped(panel, expand);
+        mBar.onTrackingStopped(expand);
+    }
+
+    @Override
+    public void onExpandingFinished() {
+        super.onExpandingFinished();
+        mScrimController.onExpandingFinished();
     }
 
     @Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/QSTileHost.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/QSTileHost.java
index 7029898..7c87580 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/QSTileHost.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/QSTileHost.java
@@ -21,6 +21,7 @@
 import android.os.HandlerThread;
 import android.os.Looper;
 
+import com.android.systemui.R;
 import com.android.systemui.qs.QSTile;
 import com.android.systemui.qs.tiles.AirplaneModeTile;
 import com.android.systemui.qs.tiles.BluetoothTile;
@@ -29,11 +30,10 @@
 import com.android.systemui.qs.tiles.CellularTile;
 import com.android.systemui.qs.tiles.ColorInversionTile;
 import com.android.systemui.qs.tiles.LocationTile;
-import com.android.systemui.qs.tiles.RingerModeTile;
+import com.android.systemui.qs.tiles.NotificationsTile;
 import com.android.systemui.qs.tiles.RotationLockTile;
 import com.android.systemui.qs.tiles.HotspotTile;
 import com.android.systemui.qs.tiles.WifiTile;
-import com.android.systemui.qs.tiles.ZenModeTile;
 import com.android.systemui.settings.CurrentUserTracker;
 import com.android.systemui.statusbar.policy.BluetoothController;
 import com.android.systemui.statusbar.policy.CastController;
@@ -42,6 +42,7 @@
 import com.android.systemui.statusbar.policy.RotationLockController;
 import com.android.systemui.statusbar.policy.TetheringController;
 import com.android.systemui.statusbar.policy.ZenModeController;
+import com.android.systemui.volume.VolumeComponent;
 
 import java.util.ArrayList;
 import java.util.List;
@@ -60,13 +61,15 @@
     private final CastController mCast;
     private final Looper mLooper;
     private final CurrentUserTracker mUserTracker;
+    private final VolumeComponent mVolume;
     private final ArrayList<QSTile<?>> mTiles = new ArrayList<QSTile<?>>();
+    private final int mFeedbackStartDelay;
 
     public QSTileHost(Context context, PhoneStatusBar statusBar,
             BluetoothController bluetooth, LocationController location,
             RotationLockController rotation, NetworkController network,
             ZenModeController zen, TetheringController tethering,
-            CastController cast) {
+            CastController cast, VolumeComponent volume) {
         mContext = context;
         mStatusBar = statusBar;
         mBluetooth = bluetooth;
@@ -76,6 +79,7 @@
         mZen = zen;
         mTethering = tethering;
         mCast = cast;
+        mVolume = volume;
 
         final HandlerThread ht = new HandlerThread(QSTileHost.class.getSimpleName());
         ht.start();
@@ -86,13 +90,11 @@
         mTiles.add(new ColorInversionTile(this));
         mTiles.add(new CellularTile(this));
         mTiles.add(new AirplaneModeTile(this));
-        mTiles.add(new ZenModeTile(this));
-        mTiles.add(new RingerModeTile(this));
+        mTiles.add(new NotificationsTile(this));
         mTiles.add(new RotationLockTile(this));
         mTiles.add(new LocationTile(this));
         mTiles.add(new CastTile(this));
         mTiles.add(new HotspotTile(this));
-        mTiles.add(new BugreportTile(this));
 
         mUserTracker = new CurrentUserTracker(mContext) {
             @Override
@@ -103,6 +105,7 @@
             }
         };
         mUserTracker.startTracking();
+        mFeedbackStartDelay = mContext.getResources().getInteger(R.integer.feedback_start_delay);
     }
 
     @Override
@@ -112,7 +115,7 @@
 
     @Override
     public void startSettingsActivity(final Intent intent) {
-        mStatusBar.postStartSettingsActivity(intent, QSTile.FEEDBACK_START_DELAY);
+        mStatusBar.postStartSettingsActivity(intent, mFeedbackStartDelay);
     }
 
     @Override
@@ -169,4 +172,9 @@
     public CastController getCastController() {
         return mCast;
     }
+
+    @Override
+    public VolumeComponent getVolumeComponent() {
+        return mVolume;
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
index 95255d5..1264d75 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
@@ -19,16 +19,12 @@
 import android.animation.Animator;
 import android.animation.AnimatorListenerAdapter;
 import android.animation.ValueAnimator;
-import android.content.res.Resources;
 import android.graphics.Color;
 import android.graphics.drawable.ColorDrawable;
 import android.view.View;
 import android.view.ViewTreeObserver;
-import android.view.animation.AccelerateInterpolator;
-import android.view.animation.AnimationUtils;
 import android.view.animation.DecelerateInterpolator;
 import android.view.animation.Interpolator;
-import android.view.animation.LinearInterpolator;
 
 /**
  * Controls both the scrim behind the notifications and in front of the notifications (when a
@@ -52,6 +48,12 @@
     private boolean mBouncerShowing;
     private boolean mAnimateChange;
     private boolean mUpdatePending;
+    private boolean mExpanding;
+    private boolean mAnimateKeyguardFadingOut;
+    private long mDurationOverride = -1;
+    private long mAnimationDelay;
+    private Runnable mOnAnimationFinished;
+    private boolean mAnimationStarted;
 
     private final Interpolator mInterpolator = new DecelerateInterpolator();
 
@@ -67,9 +69,14 @@
     }
 
     public void onTrackingStarted() {
+        mExpanding = true;
         mDarkenWhileDragging = !mUnlockMethodCache.isMethodInsecure();
     }
 
+    public void onExpandingFinished() {
+        mExpanding = false;
+    }
+
     public void setPanelExpansion(float fraction) {
         mFraction = fraction;
         scheduleUpdate();
@@ -77,18 +84,30 @@
 
     public void setBouncerShowing(boolean showing) {
         mBouncerShowing = showing;
+        mAnimateChange = !mExpanding;
+        scheduleUpdate();
+    }
+
+    public void animateKeyguardFadingOut(long delay, long duration, Runnable onAnimationFinished) {
+        mAnimateKeyguardFadingOut = true;
+        mDurationOverride = duration;
+        mAnimationDelay = delay;
         mAnimateChange = true;
+        mOnAnimationFinished = onAnimationFinished;
         scheduleUpdate();
     }
 
     private void scheduleUpdate() {
         if (mUpdatePending) return;
+
+        // Make sure that a frame gets scheduled.
+        mScrimBehind.invalidate();
         mScrimBehind.getViewTreeObserver().addOnPreDrawListener(this);
         mUpdatePending = true;
     }
 
     private void updateScrims() {
-        if (!mKeyguardShowing) {
+        if (!mKeyguardShowing || mAnimateKeyguardFadingOut) {
             updateScrimNormal();
             setScrimInFrontColor(0);
         } else {
@@ -98,14 +117,14 @@
     }
 
     private void updateScrimKeyguard() {
-        if (mBouncerShowing) {
-            setScrimInFrontColor(SCRIM_IN_FRONT_ALPHA);
-            setScrimBehindColor(0f);
-        } else if (mDarkenWhileDragging) {
+        if (mExpanding && mDarkenWhileDragging) {
             float behindFraction = Math.max(0, Math.min(mFraction, 1));
             float fraction = 1 - behindFraction;
             setScrimInFrontColor(fraction * SCRIM_IN_FRONT_ALPHA);
             setScrimBehindColor(behindFraction * SCRIM_BEHIND_ALPHA_KEYGUARD);
+        } else if (mBouncerShowing) {
+            setScrimInFrontColor(SCRIM_IN_FRONT_ALPHA);
+            setScrimBehindColor(0f);
         } else {
             setScrimInFrontColor(0f);
             setScrimBehindColor(SCRIM_BEHIND_ALPHA_KEYGUARD);
@@ -164,8 +183,20 @@
             }
         });
         anim.setInterpolator(mInterpolator);
-        anim.setDuration(ANIMATION_DURATION);
+        anim.setStartDelay(mAnimationDelay);
+        anim.setDuration(mDurationOverride != -1 ? mDurationOverride : ANIMATION_DURATION);
+        anim.addListener(new AnimatorListenerAdapter() {
+
+            @Override
+            public void onAnimationEnd(Animator animation) {
+                if (mOnAnimationFinished != null) {
+                    mOnAnimationFinished.run();
+                    mOnAnimationFinished = null;
+                }
+            }
+        });
         anim.start();
+        mAnimationStarted = true;
     }
 
     private int getBackgroundAlpha(View scrim) {
@@ -182,6 +213,16 @@
         mScrimBehind.getViewTreeObserver().removeOnPreDrawListener(this);
         mUpdatePending = false;
         updateScrims();
+        mAnimateKeyguardFadingOut = false;
+        mDurationOverride = -1;
+        mAnimationDelay = 0;
+
+        // Make sure that we always call the listener even if we didn't start an animation.
+        if (!mAnimationStarted && mOnAnimationFinished != null) {
+            mOnAnimationFinished.run();
+            mOnAnimationFinished = null;
+        }
+        mAnimationStarted = false;
         return true;
     }
 }
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 3245f1a..13d3291 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarHeaderView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarHeaderView.java
@@ -44,6 +44,7 @@
     private static final float EXPANSION_RUBBERBAND_FACTOR = 0.35f;
 
     private boolean mExpanded;
+    private boolean mOverscrolled;
     private boolean mKeyguardShowing;
 
     private View mBackground;
@@ -125,10 +126,12 @@
         return mExpandedHeight;
     }
 
-    public void setExpanded(boolean expanded) {
+    public void setExpanded(boolean expanded, boolean overscrolled) {
         boolean changed = expanded != mExpanded;
+        boolean overscrollChanged = overscrolled != mOverscrolled;
         mExpanded = expanded;
-        if (changed) {
+        mOverscrolled = overscrolled;
+        if (changed || overscrollChanged) {
             updateHeights();
             updateVisibilities();
             updateSystemIconsLayoutParams();
@@ -136,7 +139,7 @@
             updateZTranslation();
             updateClickTargets();
             if (mQSPanel != null) {
-                mQSPanel.setExpanded(expanded);
+                mQSPanel.setExpanded(expanded && !overscrolled);
             }
         }
     }
@@ -184,13 +187,13 @@
         mDateTime.setVisibility(onKeyguardAndCollapsed ? View.INVISIBLE : View.VISIBLE);
         mKeyguardCarrierText.setVisibility(onKeyguardAndCollapsed ? View.VISIBLE : View.GONE);
         mDate.setVisibility(mExpanded ? View.VISIBLE : View.GONE);
-        mSettingsButton.setVisibility(mExpanded ? View.VISIBLE : View.GONE);
+        mSettingsButton.setVisibility(mExpanded && !mOverscrolled ? View.VISIBLE : View.GONE);
         mBrightnessContainer.setVisibility(mExpanded ? View.VISIBLE : View.GONE);
         if (mStatusIcons != null) {
-            mStatusIcons.setVisibility(!mExpanded ? View.VISIBLE : View.GONE);
+            mStatusIcons.setVisibility(!mExpanded || mOverscrolled ? View.VISIBLE : View.GONE);
         }
         if (mSignalCluster != null) {
-            mSignalCluster.setVisibility(!mExpanded ? View.VISIBLE : View.GONE);
+            mSignalCluster.setVisibility(!mExpanded || mOverscrolled ? View.VISIBLE : View.GONE);
         }
     }
 
@@ -293,5 +296,15 @@
 
     public void setQSPanel(QSPanel qsp) {
         mQSPanel = qsp;
+        if (mQSPanel != null) {
+            mQSPanel.setCallback(mQsPanelCallback);
+        }
     }
+
+    private final QSPanel.Callback mQsPanelCallback = new QSPanel.Callback() {
+        @Override
+        public void onShowingDetail(boolean showingDetail) {
+            mBrightnessContainer.animate().alpha(showingDetail ? 0 : 1).withLayer().start();
+        }
+    };
 }
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 c3430c3..e3145a6 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
@@ -19,6 +19,7 @@
 import android.content.Context;
 import android.os.Bundle;
 import android.os.RemoteException;
+import android.os.SystemClock;
 import android.util.Slog;
 import android.view.KeyEvent;
 import android.view.View;
@@ -102,6 +103,7 @@
         } else {
             mPhoneStatusBar.showKeyguard();
             mBouncer.hide(false /* destroyView */);
+            mBouncer.prepare();
         }
     }
 
@@ -182,11 +184,23 @@
     /**
      * Hides the keyguard view
      */
-    public void hide() {
+    public void hide(long startTime, long fadeoutDuration) {
         mShowing = false;
         mPhoneStatusBar.hideKeyguard();
+        mStatusBarWindowManager.setKeyguardFadingAway(true);
         mStatusBarWindowManager.setKeyguardShowing(false);
-        mBouncer.hide(true /* destroyView */);
+        long uptimeMillis = SystemClock.uptimeMillis();
+        long delay = startTime - uptimeMillis;
+        if (delay < 0) {
+            delay = 0;
+        }
+        mBouncer.animateHide(delay, fadeoutDuration);
+        mScrimController.animateKeyguardFadingOut(delay, fadeoutDuration, new Runnable() {
+            @Override
+            public void run() {
+                mStatusBarWindowManager.setKeyguardFadingAway(false);
+            }
+        });
         mViewMediatorCallback.keyguardGone();
         updateStates();
     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowManager.java
index b7bf6cd..fe57cef 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowManager.java
@@ -124,7 +124,8 @@
     }
 
     private void applyHeight(State state) {
-        boolean expanded = state.isKeyguardShowingAndNotOccluded() || state.statusBarExpanded;
+        boolean expanded = state.isKeyguardShowingAndNotOccluded() || state.statusBarExpanded
+                || state.keyguardFadingAway;
         if (expanded) {
             mLp.height = ViewGroup.LayoutParams.MATCH_PARENT;
         } else {
@@ -201,6 +202,11 @@
         apply(mCurrentState);
     }
 
+    public void setKeyguardFadingAway(boolean keyguardFadingAway) {
+        mCurrentState.keyguardFadingAway = keyguardFadingAway;
+        apply(mCurrentState);
+    }
+
     /**
      * @param state The {@link StatusBarState} of the status bar.
      */
@@ -217,6 +223,7 @@
         boolean statusBarFocusable;
         long keyguardUserActivityTimeout;
         boolean bouncerShowing;
+        boolean keyguardFadingAway;
 
         /**
          * The {@link BaseStatusBar} state from the status bar.
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/SwipeAffordanceView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/SwipeAffordanceView.java
deleted file mode 100644
index 049c5fc..0000000
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/SwipeAffordanceView.java
+++ /dev/null
@@ -1,222 +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.phone;
-
-import android.content.Context;
-import android.content.res.TypedArray;
-import android.os.SystemClock;
-import android.util.AttributeSet;
-import android.view.MotionEvent;
-import android.view.View;
-import android.view.ViewConfiguration;
-import android.view.animation.AccelerateInterpolator;
-import android.view.animation.DecelerateInterpolator;
-import android.widget.Button;
-
-import com.android.systemui.R;
-import com.android.systemui.statusbar.policy.KeyButtonView;
-
-/**
- * A swipeable button for affordances on the lockscreen. This is used for the camera and phone
- * affordance.
- */
-public class SwipeAffordanceView extends KeyButtonView {
-
-    private static final int SWIPE_DIRECTION_START = 0;
-    private static final int SWIPE_DIRECTION_END = 1;
-
-    private static final int SWIPE_DIRECTION_LEFT = 0;
-    private static final int SWIPE_DIRECTION_RIGHT = 1;
-
-    private AffordanceListener mListener;
-    private int mScaledTouchSlop;
-    private float mDragDistance;
-    private int mResolvedSwipeDirection;
-    private int mSwipeDirection;
-
-    public SwipeAffordanceView(Context context, AttributeSet attrs) {
-        this(context, attrs, 0);
-    }
-
-    public SwipeAffordanceView(Context context, AttributeSet attrs, int defStyleAttr) {
-        super(context, attrs, defStyleAttr);
-        TypedArray a = context.getTheme().obtainStyledAttributes(
-                attrs,
-                R.styleable.SwipeAffordanceView,
-                0, 0);
-        try {
-            mSwipeDirection = a.getInt(R.styleable.SwipeAffordanceView_swipeDirection, 0);
-        } finally {
-            a.recycle();
-        }
-    }
-
-    @Override
-    public void onRtlPropertiesChanged(int layoutDirection) {
-        super.onRtlPropertiesChanged(layoutDirection);
-        if (!isLayoutRtl()) {
-            mResolvedSwipeDirection = mSwipeDirection;
-        } else {
-            mResolvedSwipeDirection = mSwipeDirection == SWIPE_DIRECTION_START
-                    ? SWIPE_DIRECTION_RIGHT
-                    : SWIPE_DIRECTION_LEFT;
-        }
-    }
-
-    @Override
-    protected void onFinishInflate() {
-        super.onFinishInflate();
-        mDragDistance = getResources().getDimension(R.dimen.affordance_drag_distance);
-        mScaledTouchSlop = ViewConfiguration.get(mContext).getScaledTouchSlop();
-    }
-
-    public void enableAccessibility(boolean touchExplorationEnabled) {
-
-        // Add a touch handler or accessibility click listener for camera button.
-        if (touchExplorationEnabled) {
-            setOnTouchListener(null);
-            setOnClickListener(mClickListener);
-        } else {
-            setOnTouchListener(mTouchListener);
-            setOnClickListener(null);
-        }
-    }
-
-    public void setAffordanceListener(AffordanceListener listener) {
-        mListener = listener;
-    }
-
-    private void onActionPerformed() {
-        if (mListener != null) {
-            mListener.onActionPerformed(this);
-        }
-    }
-
-    private void onUserActivity(long when) {
-        if (mListener != null) {
-            mListener.onUserActivity(when);
-        }
-    }
-
-    private final OnClickListener mClickListener = new OnClickListener() {
-        @Override
-        public void onClick(View v) {
-            onActionPerformed();
-        }
-    };
-
-    private final OnTouchListener mTouchListener = new OnTouchListener() {
-        private float mStartX;
-        private boolean mTouchSlopReached;
-        private boolean mSkipCancelAnimation;
-
-        @Override
-        public boolean onTouch(final View view, MotionEvent event) {
-            float realX = event.getRawX();
-            switch (event.getAction()) {
-                case MotionEvent.ACTION_DOWN:
-                    mStartX = realX;
-                    mTouchSlopReached = false;
-                    mSkipCancelAnimation = false;
-                    break;
-                case MotionEvent.ACTION_MOVE:
-                    if (mResolvedSwipeDirection == SWIPE_DIRECTION_LEFT
-                            ? realX > mStartX
-                            : realX < mStartX) {
-                        realX = mStartX;
-                    }
-                    if (mResolvedSwipeDirection == SWIPE_DIRECTION_LEFT
-                            ? realX < mStartX - mDragDistance
-                            : realX > mStartX + mDragDistance) {
-                        view.setPressed(true);
-                        onUserActivity(event.getEventTime());
-                    } else {
-                        view.setPressed(false);
-                    }
-                    if (mResolvedSwipeDirection == SWIPE_DIRECTION_LEFT
-                            ? realX < mStartX - mScaledTouchSlop
-                            : realX > mStartX + mScaledTouchSlop) {
-                        mTouchSlopReached = true;
-                    }
-                    view.setTranslationX(mResolvedSwipeDirection == SWIPE_DIRECTION_LEFT
-                                    ? Math.max(realX - mStartX, -mDragDistance)
-                                    : Math.min(realX - mStartX, mDragDistance));
-                    break;
-                case MotionEvent.ACTION_UP:
-                    if (mResolvedSwipeDirection == SWIPE_DIRECTION_LEFT
-                            ? realX < mStartX - mDragDistance
-                            : realX > mStartX + mDragDistance) {
-                        onActionPerformed();
-                        view.animate().x(mResolvedSwipeDirection == SWIPE_DIRECTION_LEFT
-                                ? -view.getWidth()
-                                : ((View) view.getParent()).getWidth() + view.getWidth())
-                                .setInterpolator(new AccelerateInterpolator(2f)).withEndAction(
-                                new Runnable() {
-                                    @Override
-                                    public void run() {
-                                        view.setTranslationX(0);
-                                    }
-                                });
-                        mSkipCancelAnimation = true;
-                    }
-                    if (mResolvedSwipeDirection == SWIPE_DIRECTION_LEFT
-                            ? realX < mStartX - mScaledTouchSlop
-                            : realX > mStartX + mScaledTouchSlop) {
-                        mTouchSlopReached = true;
-                    }
-                    if (!mTouchSlopReached) {
-                        mSkipCancelAnimation = true;
-                        view.animate().translationX(mResolvedSwipeDirection == SWIPE_DIRECTION_LEFT
-                                ? -mDragDistance / 2
-                                : mDragDistance / 2).
-                                setInterpolator(new DecelerateInterpolator()).withEndAction(
-                                new Runnable() {
-                                    @Override
-                                    public void run() {
-                                        view.animate().translationX(0).
-                                                setInterpolator(new AccelerateInterpolator());
-                                    }
-                                });
-                    }
-                case MotionEvent.ACTION_CANCEL:
-                    view.setPressed(false);
-                    if (!mSkipCancelAnimation) {
-                        view.animate().translationX(0)
-                                .setInterpolator(new AccelerateInterpolator(2f));
-                    }
-                    break;
-            }
-            return true;
-        }
-    };
-
-    public interface AffordanceListener {
-
-        /**
-         * Called when the view would like to report user activity.
-         *
-         * @param when The timestamp of the user activity in {@link SystemClock#uptimeMillis} time
-         *             base.
-         */
-        void onUserActivity(long when);
-
-        /**
-         * Called when the action of the affordance has been performed.
-         */
-        void onActionPerformed(SwipeAffordanceView view);
-    }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BluetoothController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BluetoothController.java
index f4145cd..8e9fb30 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BluetoothController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BluetoothController.java
@@ -16,14 +16,18 @@
 
 package com.android.systemui.statusbar.policy;
 
-import android.bluetooth.BluetoothAdapter.BluetoothStateChangeCallback;
-
 public interface BluetoothController {
-    void addStateChangedCallback(BluetoothStateChangeCallback callback);
-    void removeStateChangedCallback(BluetoothStateChangeCallback callback);
+    void addStateChangedCallback(Callback callback);
+    void removeStateChangedCallback(Callback callback);
 
     boolean isBluetoothSupported();
     boolean isBluetoothEnabled();
     boolean isBluetoothConnected();
+    boolean isBluetoothConnecting();
+    String getLastDeviceName();
     void setBluetoothEnabled(boolean enabled);
+
+    public interface Callback {
+        void onBluetoothStateChange(boolean enabled, boolean connecting);
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BluetoothControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BluetoothControllerImpl.java
index 5a19881..117bf61 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BluetoothControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BluetoothControllerImpl.java
@@ -17,7 +17,6 @@
 package com.android.systemui.statusbar.policy;
 
 import android.bluetooth.BluetoothAdapter;
-import android.bluetooth.BluetoothAdapter.BluetoothStateChangeCallback;
 import android.bluetooth.BluetoothDevice;
 import android.content.BroadcastReceiver;
 import android.content.Context;
@@ -31,14 +30,13 @@
 public class BluetoothControllerImpl extends BroadcastReceiver implements BluetoothController {
     private static final String TAG = "StatusBar.BluetoothController";
 
+    private final ArrayList<Callback> mCallbacks = new ArrayList<Callback>();
+    private final Set<BluetoothDevice> mBondedDevices = new HashSet<BluetoothDevice>();
     private final BluetoothAdapter mAdapter;
 
-    private boolean mEnabled = false;
-
-    private Set<BluetoothDevice> mBondedDevices = new HashSet<BluetoothDevice>();
-
-    private ArrayList<BluetoothStateChangeCallback> mChangeCallbacks =
-            new ArrayList<BluetoothStateChangeCallback>();
+    private boolean mEnabled;
+    private boolean mConnecting;
+    private BluetoothDevice mLastDevice;
 
     public BluetoothControllerImpl(Context context) {
         mAdapter = BluetoothAdapter.getDefaultAdapter();
@@ -57,14 +55,14 @@
         updateBondedBluetoothDevices();
     }
 
-    public void addStateChangedCallback(BluetoothStateChangeCallback cb) {
-        mChangeCallbacks.add(cb);
+    public void addStateChangedCallback(Callback cb) {
+        mCallbacks.add(cb);
         fireCallback(cb);
     }
 
     @Override
-    public void removeStateChangedCallback(BluetoothStateChangeCallback cb) {
-        mChangeCallbacks.remove(cb);
+    public void removeStateChangedCallback(Callback cb) {
+        mCallbacks.remove(cb);
     }
 
     @Override
@@ -79,6 +77,12 @@
     }
 
     @Override
+    public boolean isBluetoothConnecting() {
+        return mAdapter != null
+                && mAdapter.getConnectionState() == BluetoothAdapter.STATE_CONNECTING;
+    }
+
+    @Override
     public void setBluetoothEnabled(boolean enabled) {
         if (mAdapter != null) {
             if (enabled) {
@@ -99,6 +103,13 @@
     }
 
     @Override
+    public String getLastDeviceName() {
+        return mLastDevice != null ? mLastDevice.getName()
+                : mBondedDevices.size() == 1 ? mBondedDevices.iterator().next().getName()
+                : null;
+    }
+
+    @Override
     public void onReceive(Context context, Intent intent) {
         final String action = intent.getAction();
 
@@ -106,6 +117,11 @@
             handleAdapterStateChange(
                     intent.getIntExtra(BluetoothAdapter.EXTRA_STATE, BluetoothAdapter.ERROR));
         }
+        if (action.equals(BluetoothAdapter.ACTION_CONNECTION_STATE_CHANGED)) {
+            mConnecting = intent.getIntExtra(BluetoothAdapter.EXTRA_CONNECTION_STATE, -1)
+                    == BluetoothAdapter.STATE_CONNECTING;
+            mLastDevice = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
+        }
         fireCallbacks();
         updateBondedBluetoothDevices();
     }
@@ -131,12 +147,12 @@
     }
 
     private void fireCallbacks() {
-        for (BluetoothStateChangeCallback cb : mChangeCallbacks) {
+        for (Callback cb : mCallbacks) {
             fireCallback(cb);
         }
     }
 
-    private void fireCallback(BluetoothStateChangeCallback cb) {
-        cb.onBluetoothStateChange(mEnabled);
+    private void fireCallback(Callback cb) {
+        cb.onBluetoothStateChange(mEnabled, mConnecting);
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/DateView.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/DateView.java
index cadb44a..f978833 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/DateView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/DateView.java
@@ -20,6 +20,7 @@
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentFilter;
+import android.text.format.DateFormat;
 import android.util.AttributeSet;
 import android.widget.TextView;
 
@@ -29,8 +30,6 @@
 import java.util.Date;
 import java.util.Locale;
 
-import libcore.icu.ICU;
-
 public class DateView extends TextView {
     private static final String TAG = "DateView";
 
@@ -87,7 +86,7 @@
         if (mDateFormat == null) {
             final String dateFormat = getContext().getString(R.string.system_ui_date_pattern);
             final Locale l = Locale.getDefault();
-            final String fmt = ICU.getBestDateTimePattern(dateFormat, l.toString());
+            final String fmt = DateFormat.getBestDateTimePattern(l, dateFormat);
             mDateFormat = new SimpleDateFormat(fmt, l);
         }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpNotificationView.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpNotificationView.java
index 9271e71..ac26da2 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpNotificationView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpNotificationView.java
@@ -47,7 +47,7 @@
     int[] mTmpTwoArray = new int[2];
 
     private final int mTouchSensitivityDelay;
-    private final float mMaxAlpha = 0.95f;
+    private final float mMaxAlpha = 1f;
     private SwipeHelper mSwipeHelper;
     private EdgeSwipeHelper mEdgeSwipeHelper;
 
@@ -114,7 +114,7 @@
         float pagingTouchSlop = viewConfiguration.getScaledPagingTouchSlop();
         float touchSlop = viewConfiguration.getScaledTouchSlop();
         mSwipeHelper = new SwipeHelper(SwipeHelper.X, this, densityScale, pagingTouchSlop);
-        mSwipeHelper.setMaxAlpha(mMaxAlpha);
+        mSwipeHelper.setMaxSwipeProgress(mMaxAlpha);
         mEdgeSwipeHelper = new EdgeSwipeHelper(touchSlop);
 
         int minHeight = getResources().getDimensionPixelSize(R.dimen.notification_min_height);
@@ -184,7 +184,13 @@
     protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
         super.onLayout(changed, left, top, right, bottom);
         Outline o = new Outline();
-        o.setRect(0, 0, mContentHolder.getWidth(), mContentHolder.getHeight());
+
+        // Apply padding to shadow.
+        int outlineLeft = mContentHolder.getPaddingLeft();
+        int outlineTop = mContentHolder.getPaddingTop();
+        o.setRect(outlineLeft, outlineTop,
+                mContentHolder.getWidth() - outlineLeft - mContentHolder.getPaddingRight(),
+                mContentHolder.getHeight() - outlineTop - mContentHolder.getPaddingBottom());
         mContentHolder.setOutline(o);
     }
 
@@ -246,6 +252,12 @@
     }
 
     @Override
+    public boolean updateSwipeProgress(View animView, boolean dismissable, float swipeProgress) {
+        getBackground().setAlpha((int) (255 * swipeProgress));
+        return false;
+    }
+
+    @Override
     public View getChildAtPosition(MotionEvent ev) {
         return mContentHolder;
     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/AnimationFilter.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/AnimationFilter.java
index 5e2d06b..cf56fa57 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/AnimationFilter.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/AnimationFilter.java
@@ -27,6 +27,7 @@
     boolean animateZ;
     boolean animateScale;
     boolean animateHeight;
+    boolean animateTopInset;
     boolean animateDimmed;
     boolean hasDelays;
 
@@ -60,6 +61,11 @@
         return this;
     }
 
+    public AnimationFilter animateTopInset() {
+        animateTopInset = true;
+        return this;
+    }
+
     public AnimationFilter animateDimmed() {
         animateDimmed = true;
         return this;
@@ -84,6 +90,7 @@
         animateZ |= filter.animateZ;
         animateScale |= filter.animateScale;
         animateHeight |= filter.animateHeight;
+        animateTopInset |= filter.animateTopInset;
         animateDimmed |= filter.animateDimmed;
         hasDelays |= filter.hasDelays;
     }
@@ -94,6 +101,7 @@
         animateZ = false;
         animateScale = false;
         animateHeight = false;
+        animateTopInset = false;
         animateDimmed = false;
         hasDelays = false;
     }
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 079b184..5c98d51 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java
@@ -18,13 +18,10 @@
 
 import android.content.Context;
 import android.content.res.Configuration;
-
 import android.graphics.Canvas;
 import android.graphics.Paint;
-
 import android.util.AttributeSet;
 import android.util.Log;
-
 import android.view.MotionEvent;
 import android.view.VelocityTracker;
 import android.view.View;
@@ -40,8 +37,8 @@
 import com.android.systemui.statusbar.ExpandableNotificationRow;
 import com.android.systemui.statusbar.ExpandableView;
 import com.android.systemui.statusbar.SpeedBumpView;
-import com.android.systemui.statusbar.stack.StackScrollState.ViewState;
 import com.android.systemui.statusbar.policy.ScrollAdapter;
+import com.android.systemui.statusbar.stack.StackScrollState.ViewState;
 
 import java.util.ArrayList;
 
@@ -121,6 +118,7 @@
     private float mOverScrolledBottomPixels;
 
     private OnChildLocationsChangedListener mListener;
+    private OnOverscrollTopChangedListener mOverscrollTopChangedListener;
     private ExpandableView.OnHeightChangedListener mOnHeightChangedListener;
     private boolean mNeedsAnimation;
     private boolean mTopPaddingNeedsAnimation;
@@ -449,6 +447,11 @@
         }
     }
 
+    @Override
+    public boolean updateSwipeProgress(View animView, boolean dismissable, float swipeProgress) {
+        return false;
+    }
+
     public void onBeginDrag(View v) {
         setSwipingInProgress(true);
         mAmbientState.onBeginDrag(v);
@@ -870,9 +873,24 @@
             setOverScrolledPixels(amount / RUBBER_BAND_FACTOR, onTop);
             mAmbientState.setOverScrollAmount(amount, onTop);
             requestChildrenUpdate();
+            if (onTop) {
+                float scrollAmount = mOwnScrollY < 0 ? -mOwnScrollY : 0;
+                notifyOverscrollTopListener(scrollAmount + amount);
+            }
         }
     }
 
+    private void notifyOverscrollTopListener(float amount) {
+        if (mOverscrollTopChangedListener != null) {
+            mOverscrollTopChangedListener.onOverscrollTopChanged(amount);
+        }
+    }
+
+    public void setOverscrollTopChangedListener(
+            OnOverscrollTopChangedListener overscrollTopChangedListener) {
+        mOverscrollTopChangedListener = overscrollTopChangedListener;
+    }
+
     public float getCurrentOverScrollAmount(boolean top) {
         return mAmbientState.getOverScrollAmount(top);
     }
@@ -908,6 +926,12 @@
                 onScrollChanged(mScrollX, mOwnScrollY, oldX, oldY);
                 invalidateParentIfNeeded();
                 updateChildren();
+                float overScrollTop = getCurrentOverScrollAmount(true);
+                if (mOwnScrollY < 0) {
+                    notifyOverscrollTopListener(-mOwnScrollY + overScrollTop);
+                } else {
+                    notifyOverscrollTopListener(overScrollTop);
+                }
             }
         } else {
             customScrollTo(scrollY);
@@ -1585,12 +1609,26 @@
     }
 
     /**
+     * @return the y position of the first notification
+     */
+    public float getNotificationsTopY() {
+        return mTopPadding + getTranslationY();
+    }
+
+    /**
      * A listener that is notified when some child locations might have changed.
      */
     public interface OnChildLocationsChangedListener {
         public void onChildLocationsChanged(NotificationStackScrollLayout stackScrollLayout);
     }
 
+    /**
+     * A listener that gets notified when the overscroll at the top has changed.
+     */
+    public interface OnOverscrollTopChangedListener {
+        public void onOverscrollTopChanged(float amount);
+    }
+
     static class AnimationEvent {
 
         static AnimationFilter[] FILTERS = new AnimationFilter[] {
@@ -1599,6 +1637,7 @@
                 new AnimationFilter()
                         .animateAlpha()
                         .animateHeight()
+                        .animateTopInset()
                         .animateY()
                         .animateZ()
                         .hasDelays(),
@@ -1607,6 +1646,7 @@
                 new AnimationFilter()
                         .animateAlpha()
                         .animateHeight()
+                        .animateTopInset()
                         .animateY()
                         .animateZ()
                         .hasDelays(),
@@ -1615,6 +1655,7 @@
                 new AnimationFilter()
                         .animateAlpha()
                         .animateHeight()
+                        .animateTopInset()
                         .animateY()
                         .animateZ()
                         .hasDelays(),
@@ -1623,6 +1664,7 @@
                 new AnimationFilter()
                         .animateAlpha()
                         .animateHeight()
+                        .animateTopInset()
                         .animateY()
                         .animateDimmed()
                         .animateScale()
@@ -1651,6 +1693,7 @@
                 new AnimationFilter()
                         .animateAlpha()
                         .animateHeight()
+                        .animateTopInset()
                         .animateY()
                         .animateZ()
         };
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 bd2541a..2b52c7e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollAlgorithm.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollAlgorithm.java
@@ -49,6 +49,7 @@
     private int mBottomStackPeekSize;
     private int mZDistanceBetweenElements;
     private int mZBasicHeight;
+    private int mRoundedRectCornerRadius;
 
     private StackIndentationFunctor mTopStackIndentationFunctor;
     private StackIndentationFunctor mBottomStackIndentationFunctor;
@@ -111,6 +112,8 @@
         mZBasicHeight = (MAX_ITEMS_IN_BOTTOM_STACK + 1) * mZDistanceBetweenElements;
         mBottomStackSlowDownLength = context.getResources()
                 .getDimensionPixelSize(R.dimen.bottom_stack_slow_down_length);
+        mRoundedRectCornerRadius = context.getResources().getDimensionPixelSize(
+                com.android.internal.R.dimen.notification_quantum_rounded_rect_radius);
     }
 
 
@@ -146,6 +149,67 @@
 
         handleDraggedViews(ambientState, resultState, algorithmState);
         updateDimmedActivated(ambientState, resultState, algorithmState);
+        updateClipping(resultState, algorithmState);
+    }
+
+    private void updateClipping(StackScrollState resultState,
+            StackScrollAlgorithmState algorithmState) {
+        float previousNotificationEnd = 0;
+        float previousNotificationStart = 0;
+        boolean previousNotificationIsSwiped = false;
+        int childCount = algorithmState.visibleChildren.size();
+        for (int i = 0; i < childCount; i++) {
+            ExpandableView child = algorithmState.visibleChildren.get(i);
+            StackScrollState.ViewState state = resultState.getViewStateForView(child);
+            float newYTranslation = state.yTranslation;
+            int newHeight = state.height;
+            // apply clipping and shadow
+            float newNotificationEnd = newYTranslation + newHeight;
+
+            // In the unlocked shade we have to clip a little bit higher because of the rounded
+            // corners of the notifications.
+            float clippingCorrection = state.dimmed ? 0 : mRoundedRectCornerRadius;
+
+            // When the previous notification is swiped, we don't clip the content to the
+            // bottom of it.
+            float clipHeight = previousNotificationIsSwiped
+                    ? newHeight
+                    : newNotificationEnd - (previousNotificationEnd - clippingCorrection);
+
+            updateChildClippingAndBackground(state, newHeight, clipHeight,
+                    (int) (newHeight - (previousNotificationStart - newYTranslation)));
+
+            if (!child.isTransparent()) {
+                // Only update the previous values if we are not transparent,
+                // otherwise we would clip to a transparent view.
+                previousNotificationStart = newYTranslation + child.getClipTopAmount();
+                previousNotificationEnd = newNotificationEnd;
+                previousNotificationIsSwiped = child.getTranslationX() != 0;
+            }
+        }
+    }
+
+    /**
+     * Updates the shadow outline and the clipping for a view.
+     *
+     * @param state the viewState to update
+     * @param realHeight the currently applied height of the view
+     * @param clipHeight the desired clip height, the rest of the view will be clipped from the top
+     * @param backgroundHeight the desired background height. The shadows of the view will be
+     *                         based on this height and the content will be clipped from the top
+     */
+    private void updateChildClippingAndBackground(StackScrollState.ViewState state, int realHeight,
+            float clipHeight, int backgroundHeight) {
+        if (realHeight > clipHeight) {
+            state.topOverLap = (int) (realHeight - clipHeight);
+        } else {
+            state.topOverLap = 0;
+        }
+        if (realHeight > backgroundHeight) {
+            state.clipTopAmount = (realHeight - backgroundHeight);
+        } else {
+            state.clipTopAmount = 0;
+        }
     }
 
     /**
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 44e10be..94cb16d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollState.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollState.java
@@ -16,7 +16,6 @@
 
 package com.android.systemui.statusbar.stack;
 
-import android.graphics.Outline;
 import android.graphics.Rect;
 import android.util.Log;
 import android.view.View;
@@ -37,15 +36,12 @@
     private static final String CHILD_NOT_FOUND_TAG = "StackScrollStateNoSuchChild";
 
     private final ViewGroup mHostView;
-    private final int mRoundedRectCornerRadius;
     private Map<ExpandableView, ViewState> mStateMap;
     private final Rect mClipRect = new Rect();
 
     public StackScrollState(ViewGroup hostView) {
         mHostView = hostView;
         mStateMap = new HashMap<ExpandableView, ViewState>();
-        mRoundedRectCornerRadius = mHostView.getResources().getDimensionPixelSize(
-                com.android.internal.R.dimen.notification_quantum_rounded_rect_radius);
     }
 
     public ViewGroup getHostView() {
@@ -83,9 +79,6 @@
      */
     public void apply() {
         int numChildren = mHostView.getChildCount();
-        float previousNotificationEnd = 0;
-        float previousNotificationStart = 0;
-        boolean previousNotificationIsSwiped = false;
         for (int i = 0; i < numChildren; i++) {
             ExpandableView child = (ExpandableView) mHostView.getChildAt(i);
             ViewState state = mStateMap.get(child);
@@ -155,39 +148,41 @@
                 // apply dimming
                 child.setDimmed(state.dimmed, false /* animate */);
 
-                // apply clipping and shadow
-                float newNotificationEnd = newYTranslation + newHeight;
+                float oldClipTopAmount = child.getClipTopAmount();
+                if (oldClipTopAmount != state.clipTopAmount) {
+                    child.setClipTopAmount(state.clipTopAmount);
+                }
 
-                // In the unlocked shade we have to clip a little bit higher because of the rounded
-                // corners of the notifications.
-                float clippingCorrection = state.dimmed ? 0 : mRoundedRectCornerRadius;
-
-                // When the previous notification is swiped, we don't clip the content to the
-                // bottom of it.
-                float clipHeight = previousNotificationIsSwiped
-                        ? newHeight
-                        : newNotificationEnd - (previousNotificationEnd - clippingCorrection);
-
-                updateChildClippingAndBackground(child, newHeight,
-                        clipHeight,
-                        (int) (newHeight - (previousNotificationStart - newYTranslation)));
-
-                if (!child.isTransparent()) {
-                    // Only update the previous values if we are not transparent,
-                    // otherwise we would clip to a transparent view.
-                    previousNotificationStart = newYTranslation + child.getClipTopAmount();
-                    previousNotificationEnd = newNotificationEnd;
-                    previousNotificationIsSwiped = child.getTranslationX() != 0;
+                if (state.topOverLap != 0) {
+                    updateChildClip(child, newHeight, state.topOverLap);
+                } else {
+                    child.setClipBounds(null);
                 }
 
                 if(child instanceof SpeedBumpView) {
-                    performSpeedBumpAnimation(i, (SpeedBumpView) child, newNotificationEnd,
+                    float speedBumpEnd = newYTranslation + newHeight;
+                    performSpeedBumpAnimation(i, (SpeedBumpView) child, speedBumpEnd,
                             newYTranslation);
                 }
             }
         }
     }
 
+    /**
+     * Updates the clipping of a view
+     *
+     * @param child the view to update
+     * @param height the currently applied height of the view
+     * @param clipInset how much should this view be clipped from the top
+     */
+    private void updateChildClip(View child, int height, int clipInset) {
+        mClipRect.set(0,
+                clipInset,
+                child.getWidth(),
+                height);
+        child.setClipBounds(mClipRect);
+    }
+
     private void performSpeedBumpAnimation(int i, SpeedBumpView speedBump, float speedBumpEnd,
             float speedBumpStart) {
         View nextChild = getNextChildNotGone(i);
@@ -216,45 +211,6 @@
         return null;
     }
 
-    /**
-     * Updates the shadow outline and the clipping for a view.
-     *
-     * @param child the view to update
-     * @param realHeight the currently applied height of the view
-     * @param clipHeight the desired clip height, the rest of the view will be clipped from the top
-     * @param backgroundHeight the desired background height. The shadows of the view will be
-     *                         based on this height and the content will be clipped from the top
-     */
-    private void updateChildClippingAndBackground(ExpandableView child, int realHeight,
-            float clipHeight, int backgroundHeight) {
-        if (realHeight > clipHeight) {
-            updateChildClip(child, realHeight, clipHeight);
-        } else {
-            child.setClipBounds(null);
-        }
-        if (realHeight > backgroundHeight) {
-            child.setClipTopAmount(realHeight - backgroundHeight);
-        } else {
-            child.setClipTopAmount(0);
-        }
-    }
-
-    /**
-     * Updates the clipping of a view
-     *
-     * @param child the view to update
-     * @param height the currently applied height of the view
-     * @param clipHeight the desired clip height, the rest of the view will be clipped from the top
-     */
-    private void updateChildClip(View child, int height, float clipHeight) {
-        int clipInset = (int) (height - clipHeight);
-        mClipRect.set(0,
-                clipInset,
-                child.getWidth(),
-                height);
-        child.setClipBounds(mClipRect);
-    }
-
     public static class ViewState {
 
         // These are flags such that we can create masks for filtering.
@@ -276,6 +232,18 @@
         boolean dimmed;
 
         /**
+         * The amount which the view should be clipped from the top. This is calculated to
+         * perceive consistent shadows.
+         */
+        int clipTopAmount;
+
+        /**
+         * How much does the child overlap with the previous view on the top? Can be used for
+         * a clipping optimization
+         */
+        int topOverLap;
+
+        /**
          * The index of the view, only accounting for views not equal to GONE
          */
         int notGoneIndex;
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 f019e6c..2edd7d1 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackStateAnimator.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackStateAnimator.java
@@ -133,10 +133,11 @@
         boolean scaleChanging = child.getScaleX() != viewState.scale;
         boolean alphaChanging = alpha != child.getAlpha();
         boolean heightChanging = viewState.height != child.getActualHeight();
+        boolean topInsetChanging = viewState.clipTopAmount != child.getClipTopAmount();
         boolean wasAdded = mNewAddChildren.contains(child);
         boolean hasDelays = mAnimationFilter.hasDelays;
         boolean isDelayRelevant = yTranslationChanging || zTranslationChanging || scaleChanging ||
-                alphaChanging || heightChanging;
+                alphaChanging || heightChanging || topInsetChanging;
         long delay = 0;
         if (hasDelays && isDelayRelevant || wasAdded) {
             delay = calculateChildAnimationDelay(viewState, finalState);
@@ -167,6 +168,11 @@
             startHeightAnimation(child, viewState, delay);
         }
 
+        // start top inset animation
+        if (topInsetChanging) {
+            startInsetAnimation(child, viewState, delay);
+        }
+
         // start dimmed animation
         child.setDimmed(viewState.dimmed, mAnimationFilter.animateDimmed);
 
@@ -280,6 +286,64 @@
         child.setTag(TAG_END_HEIGHT, newEndValue);
     }
 
+    private void startInsetAnimation(final ExpandableView child,
+            StackScrollState.ViewState viewState, long delay) {
+        Integer previousStartValue = getChildTag(child, TAG_START_TOP_INSET);
+        Integer previousEndValue = getChildTag(child, TAG_END_TOP_INSET);
+        int newEndValue = viewState.clipTopAmount;
+        if (previousEndValue != null && previousEndValue == newEndValue) {
+            return;
+        }
+        ValueAnimator previousAnimator = getChildTag(child, TAG_ANIMATOR_TOP_INSET);
+        if (!mAnimationFilter.animateTopInset) {
+            // just a local update was performed
+            if (previousAnimator != null) {
+                // we need to increase all animation keyframes of the previous animator by the
+                // relative change to the end value
+                PropertyValuesHolder[] values = previousAnimator.getValues();
+                int relativeDiff = newEndValue - previousEndValue;
+                int newStartValue = previousStartValue + relativeDiff;
+                values[0].setIntValues(newStartValue, newEndValue);
+                child.setTag(TAG_START_TOP_INSET, newStartValue);
+                child.setTag(TAG_END_TOP_INSET, newEndValue);
+                previousAnimator.setCurrentPlayTime(previousAnimator.getCurrentPlayTime());
+                return;
+            } else {
+                // no new animation needed, let's just apply the value
+                child.setClipTopAmount(newEndValue);
+                return;
+            }
+        }
+
+        ValueAnimator animator = ValueAnimator.ofInt(child.getClipTopAmount(), newEndValue);
+        animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
+            @Override
+            public void onAnimationUpdate(ValueAnimator animation) {
+                child.setClipTopAmount((int) animation.getAnimatedValue());
+            }
+        });
+        animator.setInterpolator(mFastOutSlowInInterpolator);
+        long newDuration = cancelAnimatorAndGetNewDuration(previousAnimator);
+        animator.setDuration(newDuration);
+        if (delay > 0 && (previousAnimator == null || !previousAnimator.isRunning())) {
+            animator.setStartDelay(delay);
+        }
+        animator.addListener(getGlobalAnimationFinishedListener());
+        // remove the tag when the animation is finished
+        animator.addListener(new AnimatorListenerAdapter() {
+            @Override
+            public void onAnimationEnd(Animator animation) {
+                child.setTag(TAG_ANIMATOR_TOP_INSET, null);
+                child.setTag(TAG_START_TOP_INSET, null);
+                child.setTag(TAG_END_TOP_INSET, null);
+            }
+        });
+        startAnimator(animator);
+        child.setTag(TAG_ANIMATOR_TOP_INSET, animator);
+        child.setTag(TAG_START_TOP_INSET, child.getClipTopAmount());
+        child.setTag(TAG_END_TOP_INSET, newEndValue);
+    }
+
     private void startAlphaAnimation(final ExpandableView child,
             final StackScrollState.ViewState viewState, long delay) {
         Float previousStartValue = getChildTag(child,TAG_START_ALPHA);
@@ -651,6 +715,9 @@
 
     public void animateOverScrollToAmount(float targetAmount, final boolean onTop) {
         final float startOverScrollAmount = mHostLayout.getCurrentOverScrollAmount(onTop);
+        if (targetAmount == startOverScrollAmount) {
+            return;
+        }
         cancelOverScrollAnimators(onTop);
         ValueAnimator overScrollAnimator = ValueAnimator.ofFloat(startOverScrollAmount,
                 targetAmount);
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 25147b4..faea8de 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/tv/TvStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/tv/TvStatusBar.java
@@ -17,12 +17,13 @@
 package com.android.systemui.statusbar.tv;
 
 import android.os.IBinder;
+import android.service.notification.NotificationListenerService;
+import android.service.notification.NotificationListenerService.Ranking;
 import android.service.notification.StatusBarNotification;
 import android.view.View;
 import android.view.ViewGroup.LayoutParams;
 import android.view.WindowManager;
 
-import com.android.internal.policy.IKeyguardShowCallback;
 import com.android.internal.statusbar.StatusBarIcon;
 import com.android.systemui.statusbar.BaseStatusBar;
 
@@ -50,7 +51,11 @@
     }
 
     @Override
-    public void addNotificationInternal(StatusBarNotification notification) {
+    public void addNotificationInternal(StatusBarNotification notification, Ranking ranking) {
+    }
+
+    @Override
+    protected void updateRankingInternal(Ranking ranking) {
     }
 
     @Override
@@ -58,7 +63,7 @@
     }
 
     @Override
-    protected void removeNotificationInternal(String key) {
+    public void removeNotificationInternal(String key, Ranking ranking) {
     }
 
     @Override
@@ -117,7 +122,7 @@
     }
 
     @Override
-    protected void updateNotificationIcons() {
+    protected void updateNotifications() {
     }
 
     @Override
diff --git a/core/java/android/tv/ITvInputHardwareCallback.aidl b/packages/SystemUI/src/com/android/systemui/volume/VolumeComponent.java
similarity index 73%
copy from core/java/android/tv/ITvInputHardwareCallback.aidl
copy to packages/SystemUI/src/com/android/systemui/volume/VolumeComponent.java
index 83041be..5ee8925 100644
--- a/core/java/android/tv/ITvInputHardwareCallback.aidl
+++ b/packages/SystemUI/src/com/android/systemui/volume/VolumeComponent.java
@@ -14,14 +14,11 @@
  * limitations under the License.
  */
 
-package android.tv;
+package com.android.systemui.volume;
 
-import android.tv.TvStreamConfig;
+import com.android.systemui.statusbar.policy.ZenModeController;
 
-/**
- * @hide
- */
-oneway interface ITvInputHardwareCallback {
-    void onReleased();
-    void onStreamConfigChanged(in TvStreamConfig[] configs);
+public interface VolumeComponent {
+    ZenModeController getZenController();
+    void setVolumePanel(VolumePanel panel);
 }
diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumePanel.java b/packages/SystemUI/src/com/android/systemui/volume/VolumePanel.java
index 8657e07..898b46e 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/VolumePanel.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/VolumePanel.java
@@ -16,23 +16,26 @@
 
 package com.android.systemui.volume;
 
-import com.android.internal.R;
-
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
 import android.app.AlertDialog;
 import android.app.Dialog;
-import android.content.DialogInterface.OnDismissListener;
 import android.content.BroadcastReceiver;
 import android.content.Context;
 import android.content.DialogInterface;
+import android.content.DialogInterface.OnDismissListener;
 import android.content.Intent;
 import android.content.IntentFilter;
 import android.content.res.Resources;
+import android.graphics.PixelFormat;
+import android.graphics.drawable.ColorDrawable;
 import android.media.AudioManager;
 import android.media.AudioService;
 import android.media.AudioSystem;
 import android.media.RingtoneManager;
 import android.media.ToneGenerator;
 import android.net.Uri;
+import android.os.AsyncTask;
 import android.os.Handler;
 import android.os.Message;
 import android.os.Vibrator;
@@ -41,14 +44,22 @@
 import android.view.LayoutInflater;
 import android.view.MotionEvent;
 import android.view.View;
+import android.view.View.OnClickListener;
+import android.view.View.OnLongClickListener;
 import android.view.ViewGroup;
+import android.view.ViewStub;
 import android.view.Window;
 import android.view.WindowManager;
 import android.view.WindowManager.LayoutParams;
+import android.view.animation.AnimationUtils;
+import android.view.animation.Interpolator;
 import android.widget.ImageView;
 import android.widget.SeekBar;
 import android.widget.SeekBar.OnSeekBarChangeListener;
 
+import com.android.internal.R;
+import com.android.systemui.statusbar.policy.ZenModeController;
+
 import java.util.HashMap;
 
 /**
@@ -57,7 +68,6 @@
  * @hide
  */
 public class VolumePanel extends Handler {
-    private static final String TAG = VolumePanel.class.getSimpleName();
     private static boolean LOGD = false;
 
     private static final int PLAY_SOUND_DELAY = AudioService.PLAY_SOUND_DELAY;
@@ -75,6 +85,8 @@
     private static final int MAX_VOLUME = 100;
     private static final int FREE_DELAY = 10000;
     private static final int TIMEOUT_DELAY = 3000;
+    private static final int TIMEOUT_DELAY_EXPANDED = 10000;
+    private static final float ICON_PULSE_SCALE = 1.3f;
 
     private static final int MSG_VOLUME_CHANGED = 0;
     private static final int MSG_FREE_RESOURCES = 1;
@@ -88,33 +100,50 @@
     private static final int MSG_REMOTE_VOLUME_UPDATE_IF_SHOWN = 9;
     private static final int MSG_SLIDER_VISIBILITY_CHANGED = 10;
     private static final int MSG_DISPLAY_SAFE_VOLUME_WARNING = 11;
+    private static final int MSG_LAYOUT_DIRECTION = 12;
+    private static final int MSG_ZEN_MODE_CHANGED = 13;
 
     // Pseudo stream type for master volume
     private static final int STREAM_MASTER = -100;
     // Pseudo stream type for remote volume is defined in AudioService.STREAM_REMOTE_MUSIC
 
+    private final String mTag;
     protected final Context mContext;
     private final AudioManager mAudioManager;
+    private final ZenModeController mZenController;
+    private final Interpolator mFastOutSlowInInterpolator;
     private boolean mRingIsSilent;
-    private boolean mShowCombinedVolumes;
     private boolean mVoiceCapable;
+    private boolean mZenModeCapable;
+    private int mTimeoutDelay = TIMEOUT_DELAY;
 
     // True if we want to play tones on the system stream when the master stream is specified.
     private final boolean mPlayMasterStreamTones;
 
-    /** Dialog containing all the sliders */
-    private final Dialog mDialog;
-    /** Dialog's content view */
+
+    /** Volume panel content view */
     private final View mView;
+    /** Dialog hosting the panel, if not embedded */
+    private final Dialog mDialog;
+    /** Parent view hosting the panel, if embedded */
+    private final ViewGroup mParent;
 
     /** The visible portion of the volume overlay */
     private final ViewGroup mPanel;
-    /** Contains the sliders and their touchable icons */
-    private final ViewGroup mSliderGroup;
-    /** The button that expands the dialog to show all sliders */
-    private final View mMoreButton;
-    /** Dummy divider icon that needs to vanish with the more button */
-    private final View mDivider;
+    /** Contains the slider and its touchable icons */
+    private final ViewGroup mSliderPanel;
+    /** The button that expands the dialog to show the zen panel */
+    private final ImageView mExpandButton;
+    /** Dummy divider icon that needs to vanish with the expand button */
+    private final View mExpandDivider;
+    /** The zen mode configuration panel view stub */
+    private final ViewStub mZenPanelStub;
+    /** The zen mode configuration panel view, once inflated */
+    private ZenModePanel mZenPanel;
+    /** Dummy divider icon that needs to vanish with the zen panel */
+    private final View mZenPanelDivider;
+
+    private ZenModePanel.Callback mZenPanelCallback;
 
     /** Currently active stream that shows up at the top of the list of sliders */
     private int mActiveStreamType = -1;
@@ -129,8 +158,8 @@
                 false),
         RingerStream(AudioManager.STREAM_RING,
                 R.string.volume_icon_description_ringer,
-                R.drawable.ic_audio_ring_notif,
-                R.drawable.ic_audio_ring_notif_mute,
+                com.android.systemui.R.drawable.ic_ringer_audible,
+                com.android.systemui.R.drawable.ic_ringer_silent,
                 false),
         VoiceStream(AudioManager.STREAM_VOICE_CALL,
                 R.string.volume_icon_description_incall,
@@ -149,8 +178,8 @@
                 true),
         NotificationStream(AudioManager.STREAM_NOTIFICATION,
                 R.string.volume_icon_description_notification,
-                R.drawable.ic_audio_notification,
-                R.drawable.ic_audio_notification_mute,
+                com.android.systemui.R.drawable.ic_ringer_audible,
+                com.android.systemui.R.drawable.ic_ringer_silent,
                 true),
         // for now, use media resources for master volume
         MasterStream(STREAM_MASTER,
@@ -198,6 +227,7 @@
         ViewGroup group;
         ImageView icon;
         SeekBar seekbarView;
+        View seekbarContainer;
         int iconRes;
         int iconMuteRes;
     }
@@ -245,9 +275,14 @@
     }
 
 
-    public VolumePanel(Context context) {
+    public VolumePanel(Context context, ViewGroup parent, ZenModeController zenController) {
+        mTag = String.format("VolumePanel%s.%08x", parent == null ? "Dialog" : "", hashCode());
         mContext = context;
+        mParent = parent;
+        mZenController = zenController;
         mAudioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
+        mFastOutSlowInInterpolator = AnimationUtils.loadInterpolator(mContext,
+                android.R.interpolator.fast_out_slow_in);
 
         // For now, only show master volume if master volume is supported
         final Resources res = context.getResources();
@@ -258,74 +293,84 @@
                 streamRes.show = (streamRes.streamType == STREAM_MASTER);
             }
         }
-
-        mDialog = new Dialog(context) {
-            @Override
-            public boolean onTouchEvent(MotionEvent event) {
-                if (isShowing() && event.getAction() == MotionEvent.ACTION_OUTSIDE &&
-                        sConfirmSafeVolumeDialog == null) {
-                    forceTimeout();
-                    return true;
+        if (LOGD) Log.d(mTag, String.format("new VolumePanel hasParent=%s", parent != null));
+        if (parent == null) {
+            // dialog mode
+            mDialog = new Dialog(context) {
+                @Override
+                public boolean onTouchEvent(MotionEvent event) {
+                    if (isShowing() && event.getAction() == MotionEvent.ACTION_OUTSIDE &&
+                            sConfirmSafeVolumeDialog == null) {
+                        forceTimeout();
+                        return true;
+                    }
+                    return false;
                 }
-                return false;
-            }
-        };
+            };
 
-        // Change some window properties
-        final Window window = mDialog.getWindow();
-        final LayoutParams lp = window.getAttributes();
-        lp.token = null;
-        // Offset from the top
-        lp.y = res.getDimensionPixelOffset(R.dimen.volume_panel_top);
-        lp.type = LayoutParams.TYPE_VOLUME_OVERLAY;
-        lp.windowAnimations = R.style.Animation_VolumePanel;
-        window.setAttributes(lp);
-        window.setGravity(Gravity.TOP);
-        window.clearFlags(WindowManager.LayoutParams.FLAG_DIM_BEHIND);
-        window.requestFeature(Window.FEATURE_NO_TITLE);
-        window.addFlags(LayoutParams.FLAG_NOT_FOCUSABLE
-                | LayoutParams.FLAG_NOT_TOUCH_MODAL
-                | LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH);
+            // Change some window properties
+            final Window window = mDialog.getWindow();
+            final LayoutParams lp = window.getAttributes();
+            lp.token = null;
+            // Offset from the top
+            lp.y = res.getDimensionPixelOffset(com.android.systemui.R.dimen.volume_panel_top);
+            lp.width = res.getDimensionPixelSize(com.android.systemui.R.dimen.volume_panel_width);
+            lp.type = LayoutParams.TYPE_VOLUME_OVERLAY;
+            lp.format = PixelFormat.TRANSLUCENT;
+            lp.windowAnimations = R.style.Animation_VolumePanel;
+            window.setAttributes(lp);
+            window.setGravity(Gravity.TOP);
+            window.clearFlags(WindowManager.LayoutParams.FLAG_DIM_BEHIND);
+            window.requestFeature(Window.FEATURE_NO_TITLE);
+            window.addFlags(LayoutParams.FLAG_NOT_FOCUSABLE
+                    | LayoutParams.FLAG_NOT_TOUCH_MODAL
+                    | LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH
+                    | LayoutParams.FLAG_HARDWARE_ACCELERATED);
+            mDialog.setCanceledOnTouchOutside(true);
+            mDialog.setContentView(com.android.systemui.R.layout.volume_dialog);
+            mDialog.setOnDismissListener(new OnDismissListener() {
+                @Override
+                public void onDismiss(DialogInterface dialog) {
+                    mActiveStreamType = -1;
+                    mAudioManager.forceVolumeControlStream(mActiveStreamType);
+                }
+            });
 
-        mDialog.setCanceledOnTouchOutside(true);
-        mDialog.setContentView(R.layout.volume_adjust);
-        mDialog.setOnDismissListener(new OnDismissListener() {
-            @Override
-            public void onDismiss(DialogInterface dialog) {
-                mActiveStreamType = -1;
-                mAudioManager.forceVolumeControlStream(mActiveStreamType);
-            }
-        });
+            mDialog.create();
+            // temporary workaround, until we support window-level shadows
+            mDialog.getWindow().setBackgroundDrawable(new ColorDrawable(0x00000000));
 
-        mDialog.create();
+            mView = window.findViewById(R.id.content);
+            mView.setOnTouchListener(new View.OnTouchListener() {
+                @Override
+                public boolean onTouch(View v, MotionEvent event) {
+                    resetTimeout();
+                    return false;
+                }
+            });
 
-        mView = window.findViewById(R.id.content);
-        mView.setOnTouchListener(new View.OnTouchListener() {
-            @Override
-            public boolean onTouch(View v, MotionEvent event) {
-                resetTimeout();
-                return false;
-            }
-        });
-
-        mPanel = (ViewGroup) mView.findViewById(R.id.visible_panel);
-        mSliderGroup = (ViewGroup) mView.findViewById(R.id.slider_group);
-        mMoreButton = mView.findViewById(R.id.expand_button);
-        mDivider = mView.findViewById(R.id.expand_button_divider);
+        } else {
+            // embedded mode
+            mDialog = null;
+            mView = LayoutInflater.from(mContext).inflate(
+                    com.android.systemui.R.layout.volume_panel, parent, true);
+        }
+        mPanel = (ViewGroup) mView.findViewById(com.android.systemui.R.id.visible_panel);
+        mSliderPanel = (ViewGroup) mView.findViewById(com.android.systemui.R.id.slider_panel);
+        mExpandButton = (ImageView) mView.findViewById(com.android.systemui.R.id.expand_button);
+        mExpandDivider = mView.findViewById(com.android.systemui.R.id.expand_button_divider);
+        mZenPanelStub = (ViewStub)mView.findViewById(com.android.systemui.R.id.zen_panel_stub);
+        mZenPanelDivider = mView.findViewById(com.android.systemui.R.id.zen_panel_divider);
 
         mToneGenerators = new ToneGenerator[AudioSystem.getNumStreamTypes()];
         mVibrator = (Vibrator) context.getSystemService(Context.VIBRATOR_SERVICE);
         mVoiceCapable = context.getResources().getBoolean(R.bool.config_voice_capable);
 
-        // If we don't want to show multiple volumes, hide the settings button
-        // and divider.
-        mShowCombinedVolumes = !mVoiceCapable && !useMasterVolume;
-        if (!mShowCombinedVolumes) {
-            mMoreButton.setVisibility(View.GONE);
-            mDivider.setVisibility(View.GONE);
-        } else {
-            mMoreButton.setOnClickListener(mClickListener);
-        }
+        mZenModeCapable = !useMasterVolume && mZenController != null;
+        mZenPanelDivider.setVisibility(View.GONE);
+        mExpandButton.setOnClickListener(mClickListener);
+        updateZenMode(mZenController == null ? false : mZenController.isZen());
+        mZenController.addCallback(mZenCallback);
 
         final boolean masterVolumeOnly = res.getBoolean(R.bool.config_useMasterVolume);
         final boolean masterVolumeKeySounds = res.getBoolean(R.bool.config_useVolumeKeySounds);
@@ -334,7 +379,7 @@
         listenToRingerMode();
     }
 
-    public void setLayoutDirection(int layoutDirection) {
+    private void setLayoutDirection(int layoutDirection) {
         mPanel.setLayoutDirection(layoutDirection);
         updateStates();
     }
@@ -406,21 +451,39 @@
             StreamResources streamRes = STREAMS[i];
 
             final int streamType = streamRes.streamType;
-            if (mVoiceCapable && streamRes == StreamResources.NotificationStream) {
-                streamRes = StreamResources.RingerStream;
-            }
 
             final StreamControl sc = new StreamControl();
             sc.streamType = streamType;
-            sc.group = (ViewGroup) inflater.inflate(R.layout.volume_adjust_item, null);
+            sc.group = (ViewGroup) inflater.inflate(
+                    com.android.systemui.R.layout.volume_panel_item, null);
             sc.group.setTag(sc);
-            sc.icon = (ImageView) sc.group.findViewById(R.id.stream_icon);
+            sc.icon = (ImageView) sc.group.findViewById(com.android.systemui.R.id.stream_icon);
             sc.icon.setTag(sc);
             sc.icon.setContentDescription(res.getString(streamRes.descRes));
             sc.iconRes = streamRes.iconRes;
             sc.iconMuteRes = streamRes.iconMuteRes;
             sc.icon.setImageResource(sc.iconRes);
-            sc.seekbarView = (SeekBar) sc.group.findViewById(R.id.seekbar);
+            sc.icon.setClickable(isNotificationOrRing(streamType));
+            if (sc.icon.isClickable()) {
+                sc.icon.setOnClickListener(new OnClickListener() {
+                    @Override
+                    public void onClick(View v) {
+                        resetTimeout();
+                        toggle(sc);
+                    }
+                });
+                sc.icon.setOnLongClickListener(new OnLongClickListener() {
+                    @Override
+                    public boolean onLongClick(View v) {
+                        resetTimeout();
+                        longToggle(sc);
+                        return true;
+                    }
+                });
+            }
+            sc.seekbarContainer =
+                    sc.group.findViewById(com.android.systemui.R.id.seekbar_container);
+            sc.seekbarView = (SeekBar) sc.group.findViewById(com.android.systemui.R.id.seekbar);
             final int plusOne = (streamType == AudioSystem.STREAM_BLUETOOTH_SCO ||
                     streamType == AudioSystem.STREAM_VOICE_CALL) ? 1 : 0;
             sc.seekbarView.setMax(getStreamMaxVolume(streamType) + plusOne);
@@ -430,35 +493,39 @@
         }
     }
 
+    private void toggle(StreamControl sc) {
+        if (mAudioManager.getRingerMode() == AudioManager.RINGER_MODE_NORMAL) {
+            mAudioManager.setRingerMode(AudioManager.RINGER_MODE_VIBRATE);
+            postVolumeChanged(sc.streamType, AudioManager.FLAG_SHOW_UI | AudioManager.FLAG_VIBRATE);
+        } else {
+            mAudioManager.setRingerMode(AudioManager.RINGER_MODE_NORMAL);
+            postVolumeChanged(sc.streamType, AudioManager.FLAG_PLAY_SOUND);
+        }
+    }
+
+    private void longToggle(StreamControl sc) {
+        if (mAudioManager.getRingerMode() == AudioManager.RINGER_MODE_SILENT) {
+            mAudioManager.setRingerMode(AudioManager.RINGER_MODE_NORMAL);
+            postVolumeChanged(sc.streamType, AudioManager.FLAG_PLAY_SOUND);
+        } else {
+            mAudioManager.setRingerMode(AudioManager.RINGER_MODE_SILENT);
+            postVolumeChanged(sc.streamType, AudioManager.FLAG_SHOW_UI); // disable the slider
+        }
+    }
+
     private void reorderSliders(int activeStreamType) {
-        mSliderGroup.removeAllViews();
+        mSliderPanel.removeAllViews();
 
         final StreamControl active = mStreamControls.get(activeStreamType);
         if (active == null) {
             Log.e("VolumePanel", "Missing stream type! - " + activeStreamType);
             mActiveStreamType = -1;
         } else {
-            mSliderGroup.addView(active.group);
+            mSliderPanel.addView(active.group);
             mActiveStreamType = activeStreamType;
             active.group.setVisibility(View.VISIBLE);
             updateSlider(active);
-        }
-
-        addOtherVolumes();
-    }
-
-    private void addOtherVolumes() {
-        if (!mShowCombinedVolumes) return;
-
-        for (int i = 0; i < STREAMS.length; i++) {
-            // Skip the phone specific ones and the active one
-            final int streamType = STREAMS[i].streamType;
-            if (!STREAMS[i].show || streamType == mActiveStreamType) {
-                continue;
-            }
-            StreamControl sc = mStreamControls.get(streamType);
-            mSliderGroup.addView(sc.group);
-            updateSlider(sc);
+            updateZenMode(mZenController == null ? false : mZenController.isZen());
         }
     }
 
@@ -469,49 +536,132 @@
         // Force reloading the image resource
         sc.icon.setImageDrawable(null);
         sc.icon.setImageResource(muted ? sc.iconMuteRes : sc.iconRes);
-        if (((sc.streamType == AudioManager.STREAM_RING) ||
-                (sc.streamType == AudioManager.STREAM_NOTIFICATION)) &&
+        if (isNotificationOrRing(sc.streamType) &&
                 mAudioManager.getRingerMode() == AudioManager.RINGER_MODE_VIBRATE) {
-            sc.icon.setImageResource(R.drawable.ic_audio_ring_notif_vibrate);
+            sc.icon.setImageResource(com.android.systemui.R.drawable.ic_ringer_vibrate);
         }
+        updateSliderEnabled(sc, muted, false);
+    }
+
+    private void updateSliderEnabled(final StreamControl sc, boolean muted, boolean fixedVolume) {
+        final boolean wasEnabled = sc.seekbarView.isEnabled();
         if (sc.streamType == AudioService.STREAM_REMOTE_MUSIC) {
             // never disable touch interactions for remote playback, the muting is not tied to
             // the state of the phone.
             sc.seekbarView.setEnabled(true);
-        } else if ((sc.streamType != mAudioManager.getMasterStreamType() && muted) ||
+        } else if (fixedVolume ||
+                        (sc.streamType != mAudioManager.getMasterStreamType() && muted) ||
                         (sConfirmSafeVolumeDialog != null)) {
             sc.seekbarView.setEnabled(false);
+        } else if (isNotificationOrRing(sc.streamType)
+                && mAudioManager.getRingerMode() == AudioManager.RINGER_MODE_SILENT) {
+            sc.seekbarView.setEnabled(false);
         } else {
             sc.seekbarView.setEnabled(true);
         }
+        // pulse the ringer icon when the disabled slider is touched in silent mode
+        if (sc.icon.isClickable() && wasEnabled != sc.seekbarView.isEnabled()) {
+            if (sc.seekbarView.isEnabled()) {
+                sc.seekbarContainer.setOnTouchListener(null);
+            } else {
+                sc.seekbarContainer.setOnTouchListener(new View.OnTouchListener() {
+                    @Override
+                    public boolean onTouch(View v, MotionEvent event) {
+                        resetTimeout();
+                        pulseIcon(sc.icon);
+                        return false;
+                    }
+                });
+            }
+        }
+    }
+
+    private void pulseIcon(final ImageView icon) {
+        if (icon.getScaleX() != 1) return;  // already running
+        icon.animate().cancel();
+        icon.animate().scaleX(ICON_PULSE_SCALE).scaleY(ICON_PULSE_SCALE)
+                .setInterpolator(mFastOutSlowInInterpolator)
+                .setListener(new AnimatorListenerAdapter() {
+                    @Override
+                    public void onAnimationEnd(Animator animation) {
+                        icon.animate().scaleX(1).scaleY(1).setListener(null);
+                    }
+                });
+    }
+
+    private static boolean isNotificationOrRing(int streamType) {
+        return streamType == AudioManager.STREAM_RING
+                || streamType == AudioManager.STREAM_NOTIFICATION;
+    }
+
+    public void setZenModePanelCallback(ZenModePanel.Callback callback) {
+        mZenPanelCallback = callback;
     }
 
     private void expand() {
-        final int count = mSliderGroup.getChildCount();
-        for (int i = 0; i < count; i++) {
-            mSliderGroup.getChildAt(i).setVisibility(View.VISIBLE);
+        if (LOGD) Log.d(mTag, "expand mZenPanel=" + mZenPanel);
+        if (mZenPanel == null) {
+            mZenPanel = (ZenModePanel) mZenPanelStub.inflate();
+            mZenPanel.init(mZenController);
+            mZenPanel.setCallback(new ZenModePanel.Callback() {
+                @Override
+                public void onMoreSettings() {
+                    if (mZenPanelCallback != null) {
+                        mZenPanelCallback.onMoreSettings();
+                    }
+                }
+
+                @Override
+                public void onInteraction() {
+                    resetTimeout();
+                    if (mZenPanelCallback != null) {
+                        mZenPanelCallback.onInteraction();
+                    }
+                }
+            });
         }
-        mMoreButton.setVisibility(View.INVISIBLE);
-        mDivider.setVisibility(View.INVISIBLE);
+        mZenPanel.setVisibility(View.VISIBLE);
+        mZenPanelDivider.setVisibility(View.VISIBLE);
+        mTimeoutDelay = TIMEOUT_DELAY_EXPANDED;
+        resetTimeout();
     }
 
     private void collapse() {
-        mMoreButton.setVisibility(View.VISIBLE);
-        mDivider.setVisibility(View.VISIBLE);
-        final int count = mSliderGroup.getChildCount();
-        for (int i = 1; i < count; i++) {
-            mSliderGroup.getChildAt(i).setVisibility(View.GONE);
+        if (LOGD) Log.d(mTag, "collapse mZenPanel=" + mZenPanel);
+        if (mZenPanel != null) {
+            mZenPanel.setVisibility(View.GONE);
         }
+        mZenPanelDivider.setVisibility(View.GONE);
+        mTimeoutDelay = TIMEOUT_DELAY;
+        resetTimeout();
     }
 
     public void updateStates() {
-        final int count = mSliderGroup.getChildCount();
+        final int count = mSliderPanel.getChildCount();
         for (int i = 0; i < count; i++) {
-            StreamControl sc = (StreamControl) mSliderGroup.getChildAt(i).getTag();
+            StreamControl sc = (StreamControl) mSliderPanel.getChildAt(i).getTag();
             updateSlider(sc);
         }
     }
 
+    private void updateZenMode(boolean zen) {
+        if (mZenModeCapable) {
+            final boolean show = isNotificationOrRing(mActiveStreamType);
+            mExpandButton.setVisibility(show ? View.VISIBLE : View.GONE);
+            mExpandDivider.setVisibility(show ? View.VISIBLE : View.GONE);
+            mExpandButton.setImageResource(zen ? com.android.systemui.R.drawable.ic_vol_zen_on
+                    : com.android.systemui.R.drawable.ic_vol_zen_off);
+        } else {
+            mExpandButton.setVisibility(View.GONE);
+            mExpandDivider.setVisibility(View.GONE);
+        }
+    }
+
+    public void postZenModeChanged(boolean zen) {
+        removeMessages(MSG_ZEN_MODE_CHANGED);
+        obtainMessage(MSG_ZEN_MODE_CHANGED, zen ? 1 : 0, 0).sendToTarget();
+    }
+
     public void postVolumeChanged(int streamType, int flags) {
         if (hasMessages(MSG_VOLUME_CHANGED)) return;
         synchronized (this) {
@@ -582,8 +732,12 @@
     }
 
     public void postDismiss() {
-        removeMessages(MSG_TIMEOUT);
-        sendEmptyMessage(MSG_TIMEOUT);
+        forceTimeout();
+    }
+
+    public void postLayoutDirection(int layoutDirection) {
+        removeMessages(MSG_LAYOUT_DIRECTION);
+        obtainMessage(MSG_LAYOUT_DIRECTION, layoutDirection, 0).sendToTarget();
     }
 
     /**
@@ -593,7 +747,7 @@
      */
     protected void onVolumeChanged(int streamType, int flags) {
 
-        if (LOGD) Log.d(TAG, "onVolumeChanged(streamType: " + streamType + ", flags: " + flags + ")");
+        if (LOGD) Log.d(mTag, "onVolumeChanged(streamType: " + streamType + ", flags: " + flags + ")");
 
         if ((flags & AudioManager.FLAG_SHOW_UI) != 0) {
             synchronized (this) {
@@ -622,7 +776,7 @@
 
     protected void onMuteChanged(int streamType, int flags) {
 
-        if (LOGD) Log.d(TAG, "onMuteChanged(streamType: " + streamType + ", flags: " + flags + ")");
+        if (LOGD) Log.d(mTag, "onMuteChanged(streamType: " + streamType + ", flags: " + flags + ")");
 
         StreamControl sc = mStreamControls.get(streamType);
         if (sc != null) {
@@ -638,7 +792,7 @@
         mRingIsSilent = false;
 
         if (LOGD) {
-            Log.d(TAG, "onShowVolumeChanged(streamType: " + streamType
+            Log.d(mTag, "onShowVolumeChanged(streamType: " + streamType
                     + ", flags: " + flags + "), index: " + index);
         }
 
@@ -707,7 +861,7 @@
             }
 
             case AudioService.STREAM_REMOTE_MUSIC: {
-                if (LOGD) { Log.d(TAG, "showing remote volume "+index+" over "+ max); }
+                if (LOGD) { Log.d(mTag, "showing remote volume "+index+" over "+ max); }
                 break;
             }
         }
@@ -719,27 +873,22 @@
             }
 
             sc.seekbarView.setProgress(index);
-            if (((flags & AudioManager.FLAG_FIXED_VOLUME) != 0) ||
-                    (streamType != mAudioManager.getMasterStreamType() &&
-                     streamType != AudioService.STREAM_REMOTE_MUSIC &&
-                     isMuted(streamType)) ||
-                     sConfirmSafeVolumeDialog != null) {
-                sc.seekbarView.setEnabled(false);
-            } else {
-                sc.seekbarView.setEnabled(true);
-            }
+            updateSliderEnabled(sc, isMuted(streamType),
+                    (flags & AudioManager.FLAG_FIXED_VOLUME) != 0);
         }
 
-        if (!mDialog.isShowing()) {
+        if (!isShowing()) {
             int stream = (streamType == AudioService.STREAM_REMOTE_MUSIC) ? -1 : streamType;
             // when the stream is for remote playback, use -1 to reset the stream type evaluation
             mAudioManager.forceVolumeControlStream(stream);
 
             // Showing dialog - use collapsed state
-            if (mShowCombinedVolumes) {
+            if (mZenModeCapable) {
                 collapse();
             }
-            mDialog.show();
+            if (mDialog != null) {
+                mDialog.show();
+            }
         }
 
         // Do a little vibrate if applicable (only when going into vibrate mode)
@@ -749,6 +898,15 @@
                 mAudioManager.getRingerMode() == AudioManager.RINGER_MODE_VIBRATE) {
             sendMessageDelayed(obtainMessage(MSG_VIBRATE), VIBRATE_DELAY);
         }
+
+        // Pulse the slider icon if an adjustment was suppressed due to silent mode.
+        if (sc != null && (flags & AudioManager.FLAG_SHOW_SILENT_HINT) != 0) {
+            pulseIcon(sc.icon);
+        }
+    }
+
+    private boolean isShowing() {
+        return mDialog != null ? mDialog.isShowing() : mParent.isAttachedToWindow();
     }
 
     protected void onPlaySound(int streamType, int flags) {
@@ -795,9 +953,9 @@
         // streamType is the real stream type being affected, but for the UI sliders, we
         // refer to AudioService.STREAM_REMOTE_MUSIC. We still play the beeps on the real
         // stream type.
-        if (LOGD) Log.d(TAG, "onRemoteVolumeChanged(stream:"+streamType+", flags: " + flags + ")");
+        if (LOGD) Log.d(mTag, "onRemoteVolumeChanged(stream:"+streamType+", flags: " + flags + ")");
 
-        if (((flags & AudioManager.FLAG_SHOW_UI) != 0) || mDialog.isShowing()) {
+        if (((flags & AudioManager.FLAG_SHOW_UI) != 0) || isShowing()) {
             synchronized (this) {
                 if (mActiveStreamType != AudioService.STREAM_REMOTE_MUSIC) {
                     reorderSliders(AudioService.STREAM_REMOTE_MUSIC);
@@ -805,7 +963,7 @@
                 onShowVolumeChanged(AudioService.STREAM_REMOTE_MUSIC, flags);
             }
         } else {
-            if (LOGD) Log.d(TAG, "not calling onShowVolumeChanged(), no FLAG_SHOW_UI or no UI");
+            if (LOGD) Log.d(mTag, "not calling onShowVolumeChanged(), no FLAG_SHOW_UI or no UI");
         }
 
         if ((flags & AudioManager.FLAG_PLAY_SOUND) != 0 && ! mRingIsSilent) {
@@ -825,8 +983,8 @@
     }
 
     protected void onRemoteVolumeUpdateIfShown() {
-        if (LOGD) Log.d(TAG, "onRemoteVolumeUpdateIfShown()");
-        if (mDialog.isShowing()
+        if (LOGD) Log.d(mTag, "onRemoteVolumeUpdateIfShown()");
+        if (isShowing()
                 && (mActiveStreamType == AudioService.STREAM_REMOTE_MUSIC)
                 && (mStreamControls != null)) {
             onShowVolumeChanged(AudioService.STREAM_REMOTE_MUSIC, 0);
@@ -842,7 +1000,7 @@
      * @param visible
      */
     synchronized protected void onSliderVisibilityChanged(int streamType, int visible) {
-        if (LOGD) Log.d(TAG, "onSliderVisibilityChanged(stream="+streamType+", visi="+visible+")");
+        if (LOGD) Log.d(mTag, "onSliderVisibilityChanged(stream="+streamType+", visi="+visible+")");
         boolean isVisible = (visible == 1);
         for (int i = STREAMS.length - 1 ; i >= 0 ; i--) {
             StreamResources streamRes = STREAMS[i];
@@ -857,7 +1015,7 @@
     }
 
     protected void onDisplaySafeVolumeWarning(int flags) {
-        if ((flags & AudioManager.FLAG_SHOW_UI) != 0 || mDialog.isShowing()) {
+        if ((flags & AudioManager.FLAG_SHOW_UI) != 0 || isShowing()) {
             synchronized (sConfirmSafeVolumeLock) {
                 if (sConfirmSafeVolumeDialog != null) {
                     return;
@@ -907,7 +1065,7 @@
                     mToneGenerators[streamType] = new ToneGenerator(streamType, MAX_VOLUME);
                 } catch (RuntimeException e) {
                     if (LOGD) {
-                        Log.d(TAG, "ToneGenerator constructor failed with "
+                        Log.d(mTag, "ToneGenerator constructor failed with "
                                 + "RuntimeException: " + e);
                     }
                 }
@@ -976,9 +1134,11 @@
             }
 
             case MSG_TIMEOUT: {
-                if (mDialog.isShowing()) {
-                    mDialog.dismiss();
-                    mActiveStreamType = -1;
+                if (isShowing()) {
+                    if (mDialog != null) {
+                        mDialog.dismiss();
+                        mActiveStreamType = -1;
+                    }
                 }
                 synchronized (sConfirmSafeVolumeLock) {
                     if (sConfirmSafeVolumeDialog != null) {
@@ -988,7 +1148,7 @@
                 break;
             }
             case MSG_RINGER_MODE_CHANGED: {
-                if (mDialog.isShowing()) {
+                if (isShowing()) {
                     updateStates();
                 }
                 break;
@@ -1010,17 +1170,30 @@
             case MSG_DISPLAY_SAFE_VOLUME_WARNING:
                 onDisplaySafeVolumeWarning(msg.arg1);
                 break;
+
+            case MSG_LAYOUT_DIRECTION:
+                setLayoutDirection(msg.arg1);
+                break;
+
+            case MSG_ZEN_MODE_CHANGED:
+                updateZenMode(msg.arg1 != 0);
+                break;
         }
     }
 
-    private void resetTimeout() {
+    public void resetTimeout() {
+        if (LOGD) Log.d(mTag, "resetTimeout at " + System.currentTimeMillis());
         removeMessages(MSG_TIMEOUT);
-        sendMessageDelayed(obtainMessage(MSG_TIMEOUT), TIMEOUT_DELAY);
+        sendEmptyMessageDelayed(MSG_TIMEOUT, mTimeoutDelay);
     }
 
     private void forceTimeout() {
         removeMessages(MSG_TIMEOUT);
-        sendMessage(obtainMessage(MSG_TIMEOUT));
+        sendEmptyMessage(MSG_TIMEOUT);
+    }
+
+    public ZenModeController getZenController() {
+        return mZenController;
     }
 
     private final OnSeekBarChangeListener mSeekListener = new OnSeekBarChangeListener() {
@@ -1061,10 +1234,27 @@
     private final View.OnClickListener mClickListener = new View.OnClickListener() {
         @Override
         public void onClick(View v) {
-            if (v == mMoreButton) {
-                expand();
+            if (v == mExpandButton && mZenController != null) {
+                final boolean newZen = !mZenController.isZen();
+                AsyncTask.execute(new Runnable() {
+                    @Override
+                    public void run() {
+                        mZenController.setZen(newZen);
+                    }
+                });
+                if (newZen) {
+                    expand();
+                } else {
+                    collapse();
+                }
             }
             resetTimeout();
         }
     };
+
+    private final ZenModeController.Callback mZenCallback = new ZenModeController.Callback() {
+        public void onZenChanged(boolean zen) {
+            postZenModeChanged(zen);
+        }
+    };
 }
diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumeUI.java b/packages/SystemUI/src/com/android/systemui/volume/VolumeUI.java
index 9bd75b7..7da90d8 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/VolumeUI.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/VolumeUI.java
@@ -1,16 +1,22 @@
 package com.android.systemui.volume;
 
 import android.content.Context;
+import android.content.Intent;
+import android.content.res.Resources;
 import android.database.ContentObserver;
 import android.media.AudioManager;
 import android.media.IVolumeController;
 import android.net.Uri;
 import android.os.Handler;
 import android.os.RemoteException;
+import android.os.UserHandle;
 import android.provider.Settings;
 import android.util.Log;
 
+import com.android.systemui.R;
 import com.android.systemui.SystemUI;
+import com.android.systemui.statusbar.policy.ZenModeController;
+import com.android.systemui.statusbar.policy.ZenModeControllerImpl;
 
 /*
  * Copyright (C) 2014 The Android Open Source Project
@@ -34,21 +40,21 @@
     private static final Uri SETTING_URI = Settings.Global.getUriFor(SETTING);
     private static final int DEFAULT = 1;  // enabled by default
 
+    private final Handler mHandler = new Handler();
     private AudioManager mAudioManager;
     private VolumeController mVolumeController;
 
     @Override
     public void start() {
         mAudioManager = (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE);
+        mVolumeController = new VolumeController(mContext);
+        putComponent(VolumeComponent.class, mVolumeController);
         updateController();
         mContext.getContentResolver().registerContentObserver(SETTING_URI, false, mObserver);
     }
 
     private void updateController() {
         if (Settings.Global.getInt(mContext.getContentResolver(), SETTING, DEFAULT) != 0) {
-            if (mVolumeController == null) {
-                mVolumeController = new VolumeController(mContext);
-            }
             Log.d(TAG, "Registering volume controller");
             mAudioManager.setVolumeController(mVolumeController);
         } else {
@@ -57,7 +63,7 @@
         }
     }
 
-    private final ContentObserver mObserver = new ContentObserver(new Handler()) {
+    private final ContentObserver mObserver = new ContentObserver(mHandler) {
         public void onChange(boolean selfChange, Uri uri) {
             if (SETTING_URI.equals(uri)) {
                 updateController();
@@ -66,13 +72,38 @@
     };
 
     /** For now, simply host an unmodified base volume panel in this process. */
-    private final class VolumeController extends IVolumeController.Stub {
-        private final VolumePanel mPanel;
+    private final class VolumeController extends IVolumeController.Stub implements VolumeComponent {
+        private final VolumePanel mDialogPanel;
+        private VolumePanel mPanel;
 
         public VolumeController(Context context) {
-            mPanel = new VolumePanel(context);
+            mPanel = new VolumePanel(context, null, new ZenModeControllerImpl(mContext, mHandler));
+            final int delay = context.getResources().getInteger(R.integer.feedback_start_delay);
+            mPanel.setZenModePanelCallback(new ZenModePanel.Callback() {
+                @Override
+                public void onMoreSettings() {
+                    mHandler.removeCallbacks(mStartZenSettings);
+                    mHandler.postDelayed(mStartZenSettings, delay);
+                }
+
+                @Override
+                public void onInteraction() {
+                    mDialogPanel.resetTimeout();
+                }
+            });
+            mDialogPanel = mPanel;
         }
 
+        private final Runnable mStartZenSettings = new Runnable() {
+            @Override
+            public void run() {
+                mDialogPanel.postDismiss();
+                final Intent intent = ZenModePanel.ZEN_SETTINGS;
+                intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP);
+                mContext.startActivityAsUser(intent, new UserHandle(UserHandle.USER_CURRENT));
+            }
+        };
+
         @Override
         public void hasNewRemotePlaybackInfo() throws RemoteException {
             mPanel.postHasNewRemotePlaybackInfo();
@@ -114,12 +145,22 @@
         @Override
         public void setLayoutDirection(int layoutDirection)
                 throws RemoteException {
-            mPanel.setLayoutDirection(layoutDirection);
+            mPanel.postLayoutDirection(layoutDirection);
         }
 
         @Override
         public void dismiss() throws RemoteException {
             mPanel.postDismiss();
         }
+
+        @Override
+        public ZenModeController getZenController() {
+            return mDialogPanel.getZenController();
+        }
+
+        @Override
+        public void setVolumePanel(VolumePanel panel) {
+            mPanel = panel == null ? mDialogPanel : panel;
+        }
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/volume/ZenModePanel.java b/packages/SystemUI/src/com/android/systemui/volume/ZenModePanel.java
new file mode 100644
index 0000000..c338563
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/volume/ZenModePanel.java
@@ -0,0 +1,250 @@
+/*
+ * 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.volume;
+
+import android.content.Context;
+import android.content.Intent;
+import android.net.Uri;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.Message;
+import android.provider.Settings;
+import android.service.notification.Condition;
+import android.util.AttributeSet;
+import android.view.ContextThemeWrapper;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.widget.CompoundButton;
+import android.widget.CompoundButton.OnCheckedChangeListener;
+import android.widget.ImageView;
+import android.widget.LinearLayout;
+import android.widget.RadioButton;
+import android.widget.TextView;
+
+import com.android.systemui.R;
+import com.android.systemui.statusbar.policy.ZenModeController;
+
+import java.util.Arrays;
+import java.util.HashSet;
+
+public class ZenModePanel extends LinearLayout {
+    private static final int[] MINUTES = new int[] { 15, 30, 45, 60, 120, 180, 240, 480 };
+    public static final Intent ZEN_SETTINGS = new Intent(Settings.ACTION_ZEN_MODE_SETTINGS);
+
+    private final Context mContext;
+    private final LayoutInflater mInflater;
+    private final HashSet<RadioButton> mRadioButtons = new HashSet<RadioButton>();
+    private final H mHandler = new H();
+    private LinearLayout mConditions;
+    private int mMinutesIndex = Arrays.binarySearch(MINUTES, 60);  // default to one hour
+    private Callback mCallback;
+    private ZenModeController mController;
+    private boolean mRequestingConditions;
+
+    public ZenModePanel(Context context, AttributeSet attrs) {
+        super(context, attrs);
+        mContext = context;
+        mInflater = LayoutInflater.from(new ContextThemeWrapper(context, R.style.QSWhiteTheme));
+    }
+
+    @Override
+    protected void onFinishInflate() {
+        super.onFinishInflate();
+        mConditions = (LinearLayout) findViewById(android.R.id.content);
+        findViewById(android.R.id.button2).setOnClickListener(new View.OnClickListener() {
+            @Override
+            public void onClick(View v) {
+                fireMoreSettings();
+            }
+        });
+    }
+
+    @Override
+    public void setVisibility(int visibility) {
+        super.setVisibility(visibility);
+        setRequestingConditions(visibility == VISIBLE);
+    }
+
+    /** Start or stop requesting relevant zen mode exit conditions */
+    private void setRequestingConditions(boolean requesting) {
+        if (mRequestingConditions == requesting) return;
+        mRequestingConditions = requesting;
+        if (mRequestingConditions) {
+            mController.addCallback(mZenCallback);
+        } else {
+            mController.removeCallback(mZenCallback);
+        }
+        mController.requestConditions(mRequestingConditions);
+    }
+
+    public void init(ZenModeController controller) {
+        mController = controller;
+        mConditions.removeAllViews();
+        bind(updateTimeCondition(), mConditions.getChildAt(0));
+        handleUpdateConditions(new Condition[0]);
+    }
+
+    public void setCallback(Callback callback) {
+        mCallback = callback;
+    }
+
+    private Condition updateTimeCondition() {
+        final int minutes = MINUTES[mMinutesIndex];
+        final long millis = System.currentTimeMillis() + minutes * 60 * 1000;
+        final Uri id = new Uri.Builder().scheme(Condition.SCHEME).authority("android")
+                .appendPath("countdown").appendPath(Long.toString(millis)).build();
+        final int num = minutes < 60 ? minutes : minutes / 60;
+        final int resId = minutes < 60
+                ? R.plurals.zen_mode_duration_minutes
+                : R.plurals.zen_mode_duration_hours;
+        final String caption = mContext.getResources().getQuantityString(resId, num, num);
+        return new Condition(id, caption, "", "", 0, Condition.STATE_TRUE,
+                Condition.FLAG_RELEVANT_NOW);
+    }
+
+    private void handleUpdateConditions(Condition[] conditions) {
+        final int newCount = conditions == null ? 0 : conditions.length;
+        for (int i = mConditions.getChildCount() - 1; i > newCount; i--) {
+            mConditions.removeViewAt(i);
+        }
+        for (int i = 0; i < newCount; i++) {
+            bind(conditions[i], mConditions.getChildAt(i + 1));
+        }
+        bind(null, mConditions.getChildAt(newCount + 1));
+    }
+
+    private void editTimeCondition(int delta) {
+        final int i = mMinutesIndex + delta;
+        if (i < 0 || i >= MINUTES.length) return;
+        mMinutesIndex = i;
+        final Condition c = updateTimeCondition();
+        bind(c, mConditions.getChildAt(0));
+    }
+
+    private void bind(final Condition condition, View convertView) {
+        final boolean enabled = condition == null || condition.state == Condition.STATE_TRUE;
+        final View row;
+        if (convertView == null) {
+            row = mInflater.inflate(R.layout.zen_mode_condition, this, false);
+            mConditions.addView(row);
+        } else {
+            row = convertView;
+        }
+        final int position = mConditions.indexOfChild(row);
+        final RadioButton rb = (RadioButton) row.findViewById(android.R.id.checkbox);
+        mRadioButtons.add(rb);
+        rb.setEnabled(enabled);
+        rb.setOnCheckedChangeListener(new OnCheckedChangeListener() {
+            @Override
+            public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
+                if (isChecked) {
+                    for (RadioButton otherButton : mRadioButtons) {
+                        if (otherButton == rb) continue;
+                        otherButton.setChecked(false);
+                    }
+                    mController.select(condition);
+                    fireInteraction();
+                }
+            }
+        });
+        final TextView title = (TextView) row.findViewById(android.R.id.title);
+        if (condition == null) {
+            title.setText(R.string.zen_mode_forever);
+        } else {
+            title.setText(condition.summary);
+        }
+        title.setEnabled(enabled);
+        title.setAlpha(enabled ? 1 : .5f);
+        final ImageView button1 = (ImageView) row.findViewById(android.R.id.button1);
+        button1.setOnClickListener(new OnClickListener() {
+            @Override
+            public void onClick(View v) {
+                rb.setChecked(true);
+                editTimeCondition(-1);
+                fireInteraction();
+            }
+        });
+
+        final ImageView button2 = (ImageView) row.findViewById(android.R.id.button2);
+        button2.setOnClickListener(new OnClickListener() {
+            @Override
+            public void onClick(View v) {
+                rb.setChecked(true);
+                editTimeCondition(1);
+                fireInteraction();
+            }
+        });
+        title.setOnClickListener(new OnClickListener() {
+            @Override
+            public void onClick(View v) {
+                rb.setChecked(true);
+                fireInteraction();
+            }
+        });
+        if (position == 0) {
+            button1.setEnabled(mMinutesIndex > 0);
+            button2.setEnabled(mMinutesIndex < MINUTES.length - 1);
+            button1.setImageAlpha(button1.isEnabled() ? 0xff : 0x7f);
+            button2.setImageAlpha(button2.isEnabled() ? 0xff : 0x7f);
+        } else {
+            button1.setVisibility(View.GONE);
+            button2.setVisibility(View.GONE);
+        }
+        if (position == 0 &&  mConditions.getChildCount() == 1) {
+            rb.setChecked(true);
+        }
+    }
+
+    private void fireMoreSettings() {
+        if (mCallback != null) {
+            mCallback.onMoreSettings();
+        }
+    }
+
+    private void fireInteraction() {
+        if (mCallback != null) {
+            mCallback.onInteraction();
+        }
+    }
+
+    private final ZenModeController.Callback mZenCallback = new ZenModeController.Callback() {
+        @Override
+        public void onConditionsChanged(Condition[] conditions) {
+            mHandler.obtainMessage(H.UPDATE_CONDITIONS, conditions).sendToTarget();
+        }
+    };
+
+    private final class H extends Handler {
+        private static final int UPDATE_CONDITIONS = 1;
+
+        private H() {
+            super(Looper.getMainLooper());
+        }
+
+        @Override
+        public void handleMessage(Message msg) {
+            if (msg.what == UPDATE_CONDITIONS) {
+                handleUpdateConditions((Condition[])msg.obj);
+            }
+        }
+    }
+
+    public interface Callback {
+        void onMoreSettings();
+        void onInteraction();
+    }
+}
diff --git a/packages/services/PacProcessor/Android.mk b/packages/services/PacProcessor/Android.mk
index 79f8a1f..3c4e951 100644
--- a/packages/services/PacProcessor/Android.mk
+++ b/packages/services/PacProcessor/Android.mk
@@ -27,8 +27,6 @@
 
 LOCAL_JNI_SHARED_LIBRARIES := libjni_pacprocessor
 
-LOCAL_MULTILIB := 32
-
 include $(BUILD_PACKAGE)
 
 include $(call all-makefiles-under,$(LOCAL_PATH))
diff --git a/packages/services/PacProcessor/jni/Android.mk b/packages/services/PacProcessor/jni/Android.mk
index 8a60927..f16c90b 100644
--- a/packages/services/PacProcessor/jni/Android.mk
+++ b/packages/services/PacProcessor/jni/Android.mk
@@ -35,7 +35,6 @@
 
 LOCAL_MODULE := libjni_pacprocessor
 LOCAL_MODULE_TAGS := optional
-LOCAL_32_BIT_ONLY := true
 
 include external/stlport/libstlport.mk
 
diff --git a/policy/src/com/android/internal/policy/impl/GlobalActions.java b/policy/src/com/android/internal/policy/impl/GlobalActions.java
index 673ce0b..0c16b78 100644
--- a/policy/src/com/android/internal/policy/impl/GlobalActions.java
+++ b/policy/src/com/android/internal/policy/impl/GlobalActions.java
@@ -35,6 +35,7 @@
 import android.graphics.drawable.Drawable;
 import android.media.AudioManager;
 import android.net.ConnectivityManager;
+import android.os.Build;
 import android.os.Bundle;
 import android.os.Handler;
 import android.os.Message;
@@ -51,6 +52,7 @@
 import android.telephony.PhoneStateListener;
 import android.telephony.ServiceState;
 import android.telephony.TelephonyManager;
+import android.text.TextUtils;
 import android.util.ArraySet;
 import android.util.Log;
 import android.util.TypedValue;
@@ -185,7 +187,8 @@
 
         // If we only have 1 item and it's a simple press action, just do this action.
         if (mAdapter.getCount() == 1
-                && mAdapter.getItem(0) instanceof SinglePressAction) {
+                && mAdapter.getItem(0) instanceof SinglePressAction
+                && !(mAdapter.getItem(0) instanceof LongPressAction)) {
             ((SinglePressAction) mAdapter.getItem(0)).onPress();
         } else {
             WindowManager.LayoutParams attrs = mDialog.getWindow().getAttributes();
@@ -262,7 +265,7 @@
                 continue;
             }
             if (GLOBAL_ACTION_KEY_POWER.equals(actionKey)) {
-                mItems.add(getPowerAction());
+                mItems.add(new PowerAction());
             } else if (GLOBAL_ACTION_KEY_AIRPLANE.equals(actionKey)) {
                 mItems.add(mAirplaneModeOn);
             } else if (GLOBAL_ACTION_KEY_BUGREPORT.equals(actionKey)
@@ -300,7 +303,11 @@
                     @Override
                     public boolean onItemLongClick(AdapterView<?> parent, View view, int position,
                             long id) {
-                        return mAdapter.getItem(position).onLongPress();
+                        final Action action = mAdapter.getItem(position);
+                        if (action instanceof LongPressAction) {
+                            return ((LongPressAction) action).onLongPress();
+                        }
+                        return false;
                     }
         });
         dialog.getWindow().setType(WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG);
@@ -310,34 +317,38 @@
         return dialog;
     }
 
-    private Action getPowerAction() {
-        return new SinglePressAction(
-                com.android.internal.R.drawable.ic_lock_power_off,
-                R.string.global_action_power_off) {
+    private final class PowerAction extends SinglePressAction implements LongPressAction {
+        private PowerAction() {
+            super(com.android.internal.R.drawable.ic_lock_power_off,
+                R.string.global_action_power_off);
+        }
 
-            public void onPress() {
-                // shutdown by making sure radio and power are handled accordingly.
-                mWindowManagerFuncs.shutdown(true);
-            }
+        @Override
+        public boolean onLongPress() {
+            mWindowManagerFuncs.rebootSafeMode(true);
+            return true;
+        }
 
-            public boolean onLongPress() {
-                mWindowManagerFuncs.rebootSafeMode(true);
-                return true;
-            }
+        @Override
+        public boolean showDuringKeyguard() {
+            return true;
+        }
 
-            public boolean showDuringKeyguard() {
-                return true;
-            }
+        @Override
+        public boolean showBeforeProvisioning() {
+            return true;
+        }
 
-            public boolean showBeforeProvisioning() {
-                return true;
-            }
-        };
+        @Override
+        public void onPress() {
+            // shutdown by making sure radio and power are handled accordingly.
+            mWindowManagerFuncs.shutdown(false /* confirm */);
+        }
     }
 
     private Action getBugReportAction() {
-        return new SinglePressAction(com.android.internal.R.drawable.stat_sys_adb,
-                R.string.global_action_bug_report) {
+        return new SinglePressAction(com.android.internal.R.drawable.ic_lock_bugreport,
+                R.string.bugreport_title) {
 
             public void onPress() {
                 AlertDialog.Builder builder = new AlertDialog.Builder(mContext);
@@ -367,10 +378,6 @@
                 dialog.show();
             }
 
-            public boolean onLongPress() {
-                return false;
-            }
-
             public boolean showDuringKeyguard() {
                 return true;
             }
@@ -378,6 +385,14 @@
             public boolean showBeforeProvisioning() {
                 return false;
             }
+
+            @Override
+            public String getStatus() {
+                return mContext.getString(
+                        com.android.internal.R.string.bugreport_status,
+                        Build.VERSION.RELEASE,
+                        Build.ID);
+            }
         };
     }
 
@@ -393,11 +408,6 @@
             }
 
             @Override
-            public boolean onLongPress() {
-                return false;
-            }
-
-            @Override
             public boolean showDuringKeyguard() {
                 return true;
             }
@@ -583,8 +593,6 @@
 
         void onPress();
 
-        public boolean onLongPress();
-
         /**
          * @return whether this action should appear in the dialog when the keygaurd
          *    is showing.
@@ -601,6 +609,13 @@
     }
 
     /**
+     * An action that also supports long press.
+     */
+    private interface LongPressAction extends Action {
+        boolean onLongPress();
+    }
+
+    /**
      * A single press action maintains no state, just responds to a press
      * and takes an action.
      */
@@ -635,12 +650,12 @@
             return true;
         }
 
-        abstract public void onPress();
-
-        public boolean onLongPress() {
-            return false;
+        public String getStatus() {
+            return null;
         }
 
+        abstract public void onPress();
+
         public View create(
                 Context context, View convertView, ViewGroup parent, LayoutInflater inflater) {
             View v = inflater.inflate(R.layout.global_actions_item, parent, false);
@@ -648,7 +663,13 @@
             ImageView icon = (ImageView) v.findViewById(R.id.icon);
             TextView messageView = (TextView) v.findViewById(R.id.message);
 
-            v.findViewById(R.id.status).setVisibility(View.GONE);
+            TextView statusView = (TextView) v.findViewById(R.id.status);
+            final String status = getStatus();
+            if (!TextUtils.isEmpty(status)) {
+                statusView.setText(status);
+            } else {
+                statusView.setVisibility(View.GONE);
+            }
             if (mIcon != null) {
                 icon.setImageDrawable(mIcon);
                 icon.setScaleType(ScaleType.CENTER_CROP);
@@ -769,10 +790,6 @@
             changeStateFromPress(nowOn);
         }
 
-        public boolean onLongPress() {
-            return false;
-        }
-
         public boolean isEnabled() {
             return !mState.inTransition();
         }
@@ -862,10 +879,6 @@
         public void onPress() {
         }
 
-        public boolean onLongPress() {
-            return false;
-        }
-
         public boolean showDuringKeyguard() {
             return true;
         }
diff --git a/policy/src/com/android/internal/policy/impl/GlobalKeyManager.java b/policy/src/com/android/internal/policy/impl/GlobalKeyManager.java
index 3cf7e82..8c8209f 100644
--- a/policy/src/com/android/internal/policy/impl/GlobalKeyManager.java
+++ b/policy/src/com/android/internal/policy/impl/GlobalKeyManager.java
@@ -30,6 +30,7 @@
 import org.xmlpull.v1.XmlPullParserException;
 
 import java.io.IOException;
+import java.io.PrintWriter;
 
 /**
  * Stores a mapping of global keys.
@@ -123,4 +124,21 @@
             }
         }
     }
+
+    public void dump(String prefix, PrintWriter pw) {
+        final int numKeys = mKeyMapping.size();
+        if (numKeys == 0) {
+            pw.print(prefix); pw.println("mKeyMapping.size=0");
+            return;
+        }
+        pw.print(prefix); pw.println("mKeyMapping={");
+        for (int i = 0; i < numKeys; ++i) {
+            pw.print("  ");
+            pw.print(prefix);
+            pw.print(KeyEvent.keyCodeToString(mKeyMapping.keyAt(i)));
+            pw.print("=");
+            pw.println(mKeyMapping.valueAt(i).flattenToString());
+        }
+        pw.print(prefix); pw.println("}");
+    }
 }
diff --git a/policy/src/com/android/internal/policy/impl/PhoneWindow.java b/policy/src/com/android/internal/policy/impl/PhoneWindow.java
index 2fea785..2eee853 100644
--- a/policy/src/com/android/internal/policy/impl/PhoneWindow.java
+++ b/policy/src/com/android/internal/policy/impl/PhoneWindow.java
@@ -1434,30 +1434,58 @@
         final int features = getLocalFeatures();
         if (value == PROGRESS_VISIBILITY_ON) {
             if ((features & (1 << FEATURE_PROGRESS)) != 0) {
-                int level = horizontalProgressBar.getProgress();
-                int visibility = (horizontalProgressBar.isIndeterminate() || level < 10000) ?
-                        View.VISIBLE : View.INVISIBLE;
-                horizontalProgressBar.setVisibility(visibility);
+                if (horizontalProgressBar != null) {
+                    int level = horizontalProgressBar.getProgress();
+                    int visibility = (horizontalProgressBar.isIndeterminate() || level < 10000) ?
+                            View.VISIBLE : View.INVISIBLE;
+                    horizontalProgressBar.setVisibility(visibility);
+                } else {
+                    Log.e(TAG, "Horizontal progress bar not located in current window decor");
+                }
             }
             if ((features & (1 << FEATURE_INDETERMINATE_PROGRESS)) != 0) {
-                circularProgressBar.setVisibility(View.VISIBLE);
+                if (circularProgressBar != null) {
+                    circularProgressBar.setVisibility(View.VISIBLE);
+                } else {
+                    Log.e(TAG, "Circular progress bar not located in current window decor");
+                }
             }
         } else if (value == PROGRESS_VISIBILITY_OFF) {
             if ((features & (1 << FEATURE_PROGRESS)) != 0) {
-                horizontalProgressBar.setVisibility(View.GONE);
+                if (horizontalProgressBar != null) {
+                    horizontalProgressBar.setVisibility(View.GONE);
+                } else {
+                    Log.e(TAG, "Horizontal progress bar not located in current window decor");
+                }
             }
             if ((features & (1 << FEATURE_INDETERMINATE_PROGRESS)) != 0) {
-                circularProgressBar.setVisibility(View.GONE);
+                if (circularProgressBar != null) {
+                    circularProgressBar.setVisibility(View.GONE);
+                } else {
+                    Log.e(TAG, "Circular progress bar not located in current window decor");
+                }
             }
         } else if (value == PROGRESS_INDETERMINATE_ON) {
-            horizontalProgressBar.setIndeterminate(true);
+            if (horizontalProgressBar != null) {
+                horizontalProgressBar.setIndeterminate(true);
+            } else {
+                Log.e(TAG, "Horizontal progress bar not located in current window decor");
+            }
         } else if (value == PROGRESS_INDETERMINATE_OFF) {
-            horizontalProgressBar.setIndeterminate(false);
+            if (horizontalProgressBar != null) {
+                horizontalProgressBar.setIndeterminate(false);
+            } else {
+                Log.e(TAG, "Horizontal progress bar not located in current window decor");
+            }
         } else if (PROGRESS_START <= value && value <= PROGRESS_END) {
             // We want to set the progress value before testing for visibility
             // so that when the progress bar becomes visible again, it has the
             // correct level.
-            horizontalProgressBar.setProgress(value - PROGRESS_START);
+            if (horizontalProgressBar != null) {
+                horizontalProgressBar.setProgress(value - PROGRESS_START);
+            } else {
+                Log.e(TAG, "Horizontal progress bar not located in current window decor");
+            }
 
             if (value < PROGRESS_END) {
                 showProgressBars(horizontalProgressBar, circularProgressBar);
@@ -1465,7 +1493,11 @@
                 hideProgressBars(horizontalProgressBar, circularProgressBar);
             }
         } else if (PROGRESS_SECONDARY_START <= value && value <= PROGRESS_SECONDARY_END) {
-            horizontalProgressBar.setSecondaryProgress(value - PROGRESS_SECONDARY_START);
+            if (horizontalProgressBar != null) {
+                horizontalProgressBar.setSecondaryProgress(value - PROGRESS_SECONDARY_START);
+            } else {
+                Log.e(TAG, "Horizontal progress bar not located in current window decor");
+            }
 
             showProgressBars(horizontalProgressBar, circularProgressBar);
         }
@@ -1475,11 +1507,11 @@
     private void showProgressBars(ProgressBar horizontalProgressBar, ProgressBar spinnyProgressBar) {
         final int features = getLocalFeatures();
         if ((features & (1 << FEATURE_INDETERMINATE_PROGRESS)) != 0 &&
-                spinnyProgressBar.getVisibility() == View.INVISIBLE) {
+                spinnyProgressBar != null && spinnyProgressBar.getVisibility() == View.INVISIBLE) {
             spinnyProgressBar.setVisibility(View.VISIBLE);
         }
         // Only show the progress bars if the primary progress is not complete
-        if ((features & (1 << FEATURE_PROGRESS)) != 0 &&
+        if ((features & (1 << FEATURE_PROGRESS)) != 0 && horizontalProgressBar != null &&
                 horizontalProgressBar.getProgress() < 10000) {
             horizontalProgressBar.setVisibility(View.VISIBLE);
         }
@@ -1490,11 +1522,12 @@
         Animation anim = AnimationUtils.loadAnimation(getContext(), com.android.internal.R.anim.fade_out);
         anim.setDuration(1000);
         if ((features & (1 << FEATURE_INDETERMINATE_PROGRESS)) != 0 &&
+                spinnyProgressBar != null &&
                 spinnyProgressBar.getVisibility() == View.VISIBLE) {
             spinnyProgressBar.startAnimation(anim);
             spinnyProgressBar.setVisibility(View.INVISIBLE);
         }
-        if ((features & (1 << FEATURE_PROGRESS)) != 0 &&
+        if ((features & (1 << FEATURE_PROGRESS)) != 0 && horizontalProgressBar != null &&
                 horizontalProgressBar.getVisibility() == View.VISIBLE) {
             horizontalProgressBar.startAnimation(anim);
             horizontalProgressBar.setVisibility(View.INVISIBLE);
@@ -3179,7 +3212,9 @@
                         com.android.internal.R.attr.dialogTitleDecorLayout, res, true);
                 layoutResource = res.resourceId;
             } else if ((features & (1 << FEATURE_ACTION_BAR)) != 0) {
-                layoutResource = com.android.internal.R.layout.screen_action_bar;
+                layoutResource = a.getResourceId(
+                        com.android.internal.R.styleable.Window_windowActionBarFullscreenDecorLayout,
+                        com.android.internal.R.layout.screen_action_bar);
             } else {
                 layoutResource = com.android.internal.R.layout.screen_title;
             }
@@ -3384,9 +3419,8 @@
 
     private Drawable loadImageURI(Uri uri) {
         try {
-            final Context context = getContext();
-            return Drawable.createFromStreamThemed(
-                    context.getContentResolver().openInputStream(uri), null, context.getTheme());
+            return Drawable.createFromStream(
+                    getContext().getContentResolver().openInputStream(uri), null);
         } catch (Exception e) {
             Log.w(TAG, "Unable to open content: " + uri);
         }
diff --git a/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java b/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java
index 3c37902..0d39586 100644
--- a/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java
+++ b/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java
@@ -289,6 +289,9 @@
     int mDemoHdmiRotation;
     boolean mDemoHdmiRotationLock;
 
+    boolean mWakeGestureEnabledSetting;
+    MyWakeGestureListener mWakeGestureListener;
+
     // Default display does not rotate, apps that require non-default orientation will have to
     // have the orientation emulated.
     private boolean mForceDefaultOrientation = false;
@@ -355,6 +358,10 @@
     // the same as mCur*, but may be larger if the screen decor has supplied
     // content insets.
     int mContentLeft, mContentTop, mContentRight, mContentBottom;
+    // During layout, the frame in which voice content should be displayed
+    // to the user, accounting for all screen decoration except for any
+    // space they deem as available for other content.
+    int mVoiceContentLeft, mVoiceContentTop, mVoiceContentRight, mVoiceContentBottom;
     // During layout, the current screen borders along which input method
     // windows are placed.
     int mDockLeft, mDockTop, mDockRight, mDockBottom;
@@ -479,7 +486,6 @@
     private static final int MSG_DISABLE_POINTER_LOCATION = 2;
     private static final int MSG_DISPATCH_MEDIA_KEY_WITH_WAKE_LOCK = 3;
     private static final int MSG_DISPATCH_MEDIA_KEY_REPEAT_WITH_WAKE_LOCK = 4;
-    private static final int MSG_DISPATCH_SHOW_RECENTS = 5;
 
     private class PolicyHandler extends Handler {
         @Override
@@ -497,9 +503,6 @@
                 case MSG_DISPATCH_MEDIA_KEY_REPEAT_WITH_WAKE_LOCK:
                     dispatchMediaKeyRepeatWithWakeLock((KeyEvent)msg.obj);
                     break;
-                case MSG_DISPATCH_SHOW_RECENTS:
-                    showRecentApps(false);
-                    break;
             }
         }
     }
@@ -525,6 +528,9 @@
             resolver.registerContentObserver(Settings.Secure.getUriFor(
                     Settings.Secure.INCALL_POWER_BUTTON_BEHAVIOR), false, this,
                     UserHandle.USER_ALL);
+            resolver.registerContentObserver(Settings.Secure.getUriFor(
+                    Settings.Secure.WAKE_GESTURE_ENABLED), false, this,
+                    UserHandle.USER_ALL);
             resolver.registerContentObserver(Settings.System.getUriFor(
                     Settings.System.ACCELEROMETER_ROTATION), false, this,
                     UserHandle.USER_ALL);
@@ -555,6 +561,21 @@
         }
     }
 
+    class MyWakeGestureListener extends WakeGestureListener {
+        MyWakeGestureListener(Context context, Handler handler) {
+            super(context, handler);
+        }
+
+        @Override
+        public void onWakeUp() {
+            synchronized (mLock) {
+                if (shouldEnableWakeGestureLp()) {
+                    mPowerManager.wakeUp(SystemClock.uptimeMillis());
+                }
+            }
+        }
+    }
+
     class MyOrientationListener extends WindowOrientationListener {
         MyOrientationListener(Context context, Handler handler) {
             super(context, handler);
@@ -856,6 +877,7 @@
         mWindowManager = windowManager;
         mWindowManagerFuncs = windowManagerFuncs;
         mHandler = new PolicyHandler();
+        mWakeGestureListener = new MyWakeGestureListener(mContext, mHandler);
         mOrientationListener = new MyOrientationListener(mContext, mHandler);
         try {
             mOrientationListener.setCurrentRotation(windowManager.getRotation());
@@ -1142,6 +1164,15 @@
                     Settings.Secure.INCALL_POWER_BUTTON_BEHAVIOR_DEFAULT,
                     UserHandle.USER_CURRENT);
 
+            // Configure wake gesture.
+            boolean wakeGestureEnabledSetting = Settings.Secure.getIntForUser(resolver,
+                    Settings.Secure.WAKE_GESTURE_ENABLED, 0,
+                    UserHandle.USER_CURRENT) != 0;
+            if (mWakeGestureEnabledSetting != wakeGestureEnabledSetting) {
+                mWakeGestureEnabledSetting = wakeGestureEnabledSetting;
+                updateWakeGestureListenerLp();
+            }
+
             // Configure rotation lock.
             int userRotation = Settings.System.getIntForUser(resolver,
                     Settings.System.USER_ROTATION, Surface.ROTATION_0,
@@ -1189,6 +1220,20 @@
         }
     }
 
+    private void updateWakeGestureListenerLp() {
+        if (shouldEnableWakeGestureLp()) {
+            mWakeGestureListener.requestWakeUpTrigger();
+        } else {
+            mWakeGestureListener.cancelWakeUpTrigger();
+        }
+    }
+
+    private boolean shouldEnableWakeGestureLp() {
+        return mWakeGestureEnabledSetting && !mScreenOnEarly
+                && (!mLidControlsSleep || mLidState != LID_CLOSED)
+                && mWakeGestureListener.isSupported();
+    }
+
     private void enablePointerLocation() {
         if (mPointerLocationView == null) {
             mPointerLocationView = new PointerLocationView(mContext);
@@ -1266,6 +1311,7 @@
             case TYPE_INPUT_METHOD:
             case TYPE_WALLPAPER:
             case TYPE_PRIVATE_PRESENTATION:
+            case TYPE_VOICE_INTERACTION:
                 // The window manager will check these.
                 break;
             case TYPE_PHONE:
@@ -1432,74 +1478,77 @@
             return 3;
         case TYPE_SEARCH_BAR:
             return 4;
+        case TYPE_VOICE_INTERACTION:
+            // voice interaction layer is almost immediately above apps.
+            return 5;
         case TYPE_RECENTS_OVERLAY:
         case TYPE_SYSTEM_DIALOG:
-            return 5;
+            return 6;
         case TYPE_TOAST:
             // toasts and the plugged-in battery thing
-            return 6;
+            return 7;
         case TYPE_PRIORITY_PHONE:
             // SIM errors and unlock.  Not sure if this really should be in a high layer.
-            return 7;
+            return 8;
         case TYPE_DREAM:
             // used for Dreams (screensavers with TYPE_DREAM windows)
-            return 8;
+            return 9;
         case TYPE_SYSTEM_ALERT:
             // like the ANR / app crashed dialogs
-            return 9;
+            return 10;
         case TYPE_INPUT_METHOD:
             // on-screen keyboards and other such input method user interfaces go here.
-            return 10;
+            return 11;
         case TYPE_INPUT_METHOD_DIALOG:
             // on-screen keyboards and other such input method user interfaces go here.
-            return 11;
+            return 12;
         case TYPE_KEYGUARD_SCRIM:
             // the safety window that shows behind keyguard while keyguard is starting
-            return 12;
-        case TYPE_STATUS_BAR_SUB_PANEL:
             return 13;
-        case TYPE_STATUS_BAR:
+        case TYPE_STATUS_BAR_SUB_PANEL:
             return 14;
-        case TYPE_STATUS_BAR_PANEL:
+        case TYPE_STATUS_BAR:
             return 15;
-        case TYPE_KEYGUARD_DIALOG:
+        case TYPE_STATUS_BAR_PANEL:
             return 16;
+        case TYPE_KEYGUARD_DIALOG:
+            return 17;
         case TYPE_VOLUME_OVERLAY:
             // the on-screen volume indicator and controller shown when the user
             // changes the device volume
-            return 17;
+            return 18;
         case TYPE_SYSTEM_OVERLAY:
             // the on-screen volume indicator and controller shown when the user
             // changes the device volume
-            return 18;
+            return 19;
         case TYPE_NAVIGATION_BAR:
             // the navigation bar, if available, shows atop most things
-            return 19;
+            return 20;
         case TYPE_NAVIGATION_BAR_PANEL:
             // some panels (e.g. search) need to show on top of the navigation bar
-            return 20;
+            return 21;
         case TYPE_SYSTEM_ERROR:
             // system-level error dialogs
-            return 21;
+            return 22;
         case TYPE_MAGNIFICATION_OVERLAY:
             // used to highlight the magnified portion of a display
-            return 22;
+            return 23;
         case TYPE_DISPLAY_OVERLAY:
             // used to simulate secondary display devices
-            return 23;
+            return 24;
         case TYPE_DRAG:
             // the drag layer: input for drag-and-drop is associated with this window,
             // which sits above all other focusable windows
-            return 24;
-        case TYPE_SECURE_SYSTEM_OVERLAY:
             return 25;
-        case TYPE_BOOT_PROGRESS:
+        case TYPE_SECURE_SYSTEM_OVERLAY:
             return 26;
+        case TYPE_BOOT_PROGRESS:
+            return 27;
         case TYPE_POINTER:
             // the (mouse) pointer layer
-            return 27;
-        case TYPE_HIDDEN_NAV_CONSUMER:
             return 28;
+        case TYPE_HIDDEN_NAV_CONSUMER:
+            return 29;
         }
         Log.e(TAG, "Unknown window type: " + type);
         return 2;
@@ -1568,11 +1617,16 @@
     }
 
     @Override
-    public boolean doesForceHide(WindowState win, WindowManager.LayoutParams attrs) {
+    public boolean doesForceHide(WindowManager.LayoutParams attrs) {
         return (attrs.privateFlags & PRIVATE_FLAG_KEYGUARD) != 0;
     }
 
     @Override
+    public boolean isKeyguardHostWindow(WindowManager.LayoutParams attrs) {
+        return attrs.type == TYPE_STATUS_BAR;
+    }
+
+    @Override
     public boolean canBeForceHidden(WindowState win, WindowManager.LayoutParams attrs) {
         switch (attrs.type) {
             case TYPE_STATUS_BAR:
@@ -1905,9 +1959,8 @@
 
     @Override
     public Animation createForceHideEnterAnimation(boolean onWallpaper) {
-        return AnimationUtils.loadAnimation(mContext, onWallpaper
-                ? com.android.internal.R.anim.lock_screen_wallpaper_behind_enter
-                : com.android.internal.R.anim.lock_screen_behind_enter);
+        return AnimationUtils.loadAnimation(mContext,
+                com.android.internal.R.anim.lock_screen_behind_enter);
     }
 
     private static void awakenDreams() {
@@ -2459,12 +2512,6 @@
         }
     }
 
-    @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 {
@@ -2711,13 +2758,13 @@
         mRestrictedScreenTop = mUnrestrictedScreenTop;
         mRestrictedScreenWidth = mSystemGestures.screenWidth = mUnrestrictedScreenWidth;
         mRestrictedScreenHeight = mSystemGestures.screenHeight = mUnrestrictedScreenHeight;
-        mDockLeft = mContentLeft = mStableLeft = mStableFullscreenLeft
+        mDockLeft = mContentLeft = mVoiceContentLeft = mStableLeft = mStableFullscreenLeft
                 = mCurLeft = mUnrestrictedScreenLeft;
-        mDockTop = mContentTop = mStableTop = mStableFullscreenTop
+        mDockTop = mContentTop = mVoiceContentTop = mStableTop = mStableFullscreenTop
                 = mCurTop = mUnrestrictedScreenTop;
-        mDockRight = mContentRight = mStableRight = mStableFullscreenRight
+        mDockRight = mContentRight = mVoiceContentRight = mStableRight = mStableFullscreenRight
                 = mCurRight = displayWidth - overscanRight;
-        mDockBottom = mContentBottom = mStableBottom = mStableFullscreenBottom
+        mDockBottom = mContentBottom = mVoiceContentBottom = mStableBottom = mStableFullscreenBottom
                 = mCurBottom = displayHeight - overscanBottom;
         mDockLayer = 0x10000000;
         mStatusBarLayer = -1;
@@ -2828,10 +2875,10 @@
                 }
                 // Make sure the content and current rectangles are updated to
                 // account for the restrictions from the navigation bar.
-                mContentTop = mCurTop = mDockTop;
-                mContentBottom = mCurBottom = mDockBottom;
-                mContentLeft = mCurLeft = mDockLeft;
-                mContentRight = mCurRight = mDockRight;
+                mContentTop = mVoiceContentTop = mCurTop = mDockTop;
+                mContentBottom = mVoiceContentBottom = mCurBottom = mDockBottom;
+                mContentLeft = mVoiceContentLeft = mCurLeft = mDockLeft;
+                mContentRight = mVoiceContentRight = mCurRight = mDockRight;
                 mStatusBarLayer = mNavigationBar.getSurfaceLayer();
                 // And compute the final frame.
                 mNavigationBar.computeFrameLw(mTmpNavigationFrame, mTmpNavigationFrame,
@@ -2878,10 +2925,10 @@
                     // status bar is visible.
                     mDockTop = mUnrestrictedScreenTop + mStatusBarHeight;
 
-                    mContentTop = mCurTop = mDockTop;
-                    mContentBottom = mCurBottom = mDockBottom;
-                    mContentLeft = mCurLeft = mDockLeft;
-                    mContentRight = mCurRight = mDockRight;
+                    mContentTop = mVoiceContentTop = mCurTop = mDockTop;
+                    mContentBottom = mVoiceContentBottom = mCurBottom = mDockBottom;
+                    mContentLeft = mVoiceContentLeft = mCurLeft = mDockLeft;
+                    mContentRight = mVoiceContentRight = mCurRight = mDockRight;
 
                     if (DEBUG_LAYOUT) Slog.v(TAG, "Status bar: " +
                         String.format(
@@ -2952,7 +2999,12 @@
                 // Ungh.  So to deal with that, make sure the content frame
                 // we end up using is not covering the IM dock.
                 cf.set(attached.getContentFrameLw());
-                if (attached.getSurfaceLayer() < mDockLayer) {
+                if (attached.isVoiceInteraction()) {
+                    if (cf.left < mVoiceContentLeft) cf.left = mVoiceContentLeft;
+                    if (cf.top < mVoiceContentTop) cf.top = mVoiceContentTop;
+                    if (cf.right > mVoiceContentRight) cf.right = mVoiceContentRight;
+                    if (cf.bottom > mVoiceContentBottom) cf.bottom = mVoiceContentBottom;
+                } else if (attached.getSurfaceLayer() < mDockLayer) {
                     if (cf.left < mContentLeft) cf.left = mContentLeft;
                     if (cf.top < mContentTop) cf.top = mContentTop;
                     if (cf.right > mContentRight) cf.right = mContentRight;
@@ -3160,16 +3212,23 @@
                     }
 
                     if ((fl & FLAG_FULLSCREEN) == 0) {
-                        if (adjust != SOFT_INPUT_ADJUST_RESIZE) {
-                            cf.left = mDockLeft;
-                            cf.top = mDockTop;
-                            cf.right = mDockRight;
-                            cf.bottom = mDockBottom;
+                        if (win.isVoiceInteraction()) {
+                            cf.left = mVoiceContentLeft;
+                            cf.top = mVoiceContentTop;
+                            cf.right = mVoiceContentRight;
+                            cf.bottom = mVoiceContentBottom;
                         } else {
-                            cf.left = mContentLeft;
-                            cf.top = mContentTop;
-                            cf.right = mContentRight;
-                            cf.bottom = mContentBottom;
+                            if (adjust != SOFT_INPUT_ADJUST_RESIZE) {
+                                cf.left = mDockLeft;
+                                cf.top = mDockTop;
+                                cf.right = mDockRight;
+                                cf.bottom = mDockBottom;
+                            } else {
+                                cf.left = mContentLeft;
+                                cf.top = mContentTop;
+                                cf.right = mContentRight;
+                                cf.bottom = mContentBottom;
+                            }
                         }
                     } else {
                         // Full screen windows are always given a layout that is as if the
@@ -3333,7 +3392,12 @@
                     pf.top = mContentTop;
                     pf.right = mContentRight;
                     pf.bottom = mContentBottom;
-                    if (adjust != SOFT_INPUT_ADJUST_RESIZE) {
+                    if (win.isVoiceInteraction()) {
+                        df.left = of.left = cf.left = mVoiceContentLeft;
+                        df.top = of.top = cf.top = mVoiceContentTop;
+                        df.right = of.right = cf.right = mVoiceContentRight;
+                        df.bottom = of.bottom = cf.bottom = mVoiceContentBottom;
+                    } else if (adjust != SOFT_INPUT_ADJUST_RESIZE) {
                         df.left = of.left = cf.left = mDockLeft;
                         df.top = of.top = cf.top = mDockTop;
                         df.right = of.right = cf.right = mDockRight;
@@ -3381,6 +3445,10 @@
             setLastInputMethodWindowLw(null, null);
             offsetInputMethodWindowLw(win);
         }
+        if (attrs.type == TYPE_VOICE_INTERACTION && win.isVisibleOrBehindKeyguardLw()
+                && !win.getGivenInsetsPendingLw()) {
+            offsetVoiceInputWindowLw(win);
+        }
     }
 
     private void offsetInputMethodWindowLw(WindowState win) {
@@ -3389,6 +3457,9 @@
         if (mContentBottom > top) {
             mContentBottom = top;
         }
+        if (mVoiceContentBottom > top) {
+            mVoiceContentBottom = top;
+        }
         top = win.getVisibleFrameLw().top;
         top += win.getGivenVisibleInsetsLw().top;
         if (mCurBottom > top) {
@@ -3399,6 +3470,40 @@
                 + mContentBottom + " mCurBottom=" + mCurBottom);
     }
 
+    private void offsetVoiceInputWindowLw(WindowState win) {
+        final int gravity = win.getAttrs().gravity;
+        switch (gravity&((Gravity.AXIS_PULL_BEFORE|Gravity.AXIS_PULL_AFTER)
+                << Gravity.AXIS_X_SHIFT)) {
+            case Gravity.AXIS_PULL_BEFORE<<Gravity.AXIS_X_SHIFT: {
+                int right = win.getContentFrameLw().right - win.getGivenContentInsetsLw().right;
+                if (mVoiceContentLeft < right) {
+                    mVoiceContentLeft = right;
+                }
+            } break;
+            case Gravity.AXIS_PULL_AFTER<<Gravity.AXIS_X_SHIFT: {
+                int left = win.getContentFrameLw().left - win.getGivenContentInsetsLw().left;
+                if (mVoiceContentRight < left) {
+                    mVoiceContentRight = left;
+                }
+            } break;
+        }
+        switch (gravity&((Gravity.AXIS_PULL_BEFORE|Gravity.AXIS_PULL_AFTER)
+                << Gravity.AXIS_Y_SHIFT)) {
+            case Gravity.AXIS_PULL_BEFORE<<Gravity.AXIS_Y_SHIFT: {
+                int bottom = win.getContentFrameLw().bottom - win.getGivenContentInsetsLw().bottom;
+                if (mVoiceContentTop < bottom) {
+                    mVoiceContentTop = bottom;
+                }
+            } break;
+            case Gravity.AXIS_PULL_AFTER<<Gravity.AXIS_Y_SHIFT: {
+                int top = win.getContentFrameLw().top - win.getGivenContentInsetsLw().top;
+                if (mVoiceContentBottom < top) {
+                    mVoiceContentBottom = top;
+                }
+            } break;
+        }
+    }
+
     /** {@inheritDoc} */
     @Override
     public void finishLayoutLw() {
@@ -4350,6 +4455,7 @@
             mKeyguardDelegate.onScreenTurnedOff(why);
         }
         synchronized (mLock) {
+            updateWakeGestureListenerLp();
             updateOrientationListenerLp();
             updateLockScreenTimeout();
         }
@@ -4366,6 +4472,7 @@
 
         synchronized (mLock) {
             mScreenOnEarly = true;
+            updateWakeGestureListenerLp();
             updateOrientationListenerLp();
             updateLockScreenTimeout();
         }
@@ -4509,6 +4616,13 @@
         }
     }
 
+    @Override
+    public void startKeyguardExitAnimation(long startTime, long fadeoutDuration) {
+        if (mKeyguardDelegate != null) {
+            mKeyguardDelegate.startKeyguardExitAnimation(startTime, fadeoutDuration);
+        }
+    }
+
     void sendCloseSystemWindows() {
         sendCloseSystemWindows(mContext, null);
     }
@@ -4954,6 +5068,10 @@
                     PowerManager.GO_TO_SLEEP_REASON_USER,
                     PowerManager.GO_TO_SLEEP_FLAG_NO_DOZE);
         }
+
+        synchronized (mLock) {
+            updateWakeGestureListenerLp();
+        }
     }
 
     void updateRotation(boolean alwaysSendConfiguration) {
@@ -5414,6 +5532,9 @@
             pw.print(prefix); pw.print("mLastFocusNeedsMenu=");
                     pw.println(mLastFocusNeedsMenu);
         }
+        pw.print(prefix); pw.print("mWakeGestureEnabledSetting=");
+                pw.println(mWakeGestureEnabledSetting);
+
         pw.print(prefix); pw.print("mSupportAutoRotation="); pw.println(mSupportAutoRotation);
         pw.print(prefix); pw.print("mUiMode="); pw.print(mUiMode);
                 pw.print(" mDockMode="); pw.print(mDockMode);
@@ -5482,6 +5603,10 @@
                 pw.print(","); pw.print(mContentTop);
                 pw.print(")-("); pw.print(mContentRight);
                 pw.print(","); pw.print(mContentBottom); pw.println(")");
+        pw.print(prefix); pw.print("mVoiceContent=("); pw.print(mVoiceContentLeft);
+                pw.print(","); pw.print(mVoiceContentTop);
+                pw.print(")-("); pw.print(mVoiceContentRight);
+                pw.print(","); pw.print(mVoiceContentBottom); pw.println(")");
         pw.print(prefix); pw.print("mDock=("); pw.print(mDockLeft);
                 pw.print(","); pw.print(mDockTop);
                 pw.print(")-("); pw.print(mDockRight);
@@ -5552,10 +5677,14 @@
                 pw.print(" mDemoHdmiRotationLock="); pw.println(mDemoHdmiRotationLock);
         pw.print(prefix); pw.print("mUndockedHdmiRotation="); pw.println(mUndockedHdmiRotation);
 
+        mGlobalKeyManager.dump(prefix, pw);
         mStatusBarController.dump(pw, prefix);
         mNavigationBarController.dump(pw, prefix);
         PolicyControl.dump(prefix, pw);
 
+        if (mWakeGestureListener != null) {
+            mWakeGestureListener.dump(pw, prefix);
+        }
         if (mOrientationListener != null) {
             mOrientationListener.dump(pw, prefix);
         }
diff --git a/policy/src/com/android/internal/policy/impl/WakeGestureListener.java b/policy/src/com/android/internal/policy/impl/WakeGestureListener.java
new file mode 100644
index 0000000..9396c2c
--- /dev/null
+++ b/policy/src/com/android/internal/policy/impl/WakeGestureListener.java
@@ -0,0 +1,100 @@
+/*
+ * 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.policy.impl;
+
+import android.os.Handler;
+import android.content.Context;
+import android.hardware.Sensor;
+import android.hardware.SensorManager;
+import android.hardware.TriggerEvent;
+import android.hardware.TriggerEventListener;
+
+import java.io.PrintWriter;
+
+/**
+ * Watches for wake gesture sensor events then invokes the listener.
+ */
+public abstract class WakeGestureListener {
+    private static final String TAG = "WakeGestureListener";
+
+    private final SensorManager mSensorManager;
+    private final Handler mHandler;
+
+    private final Object mLock = new Object();
+
+    private boolean mTriggerRequested;
+    private Sensor mSensor;
+
+    public WakeGestureListener(Context context, Handler handler) {
+        mSensorManager = (SensorManager)context.getSystemService(Context.SENSOR_SERVICE);
+        mHandler = handler;
+
+        mSensor = mSensorManager.getDefaultSensor(Sensor.TYPE_WAKE_GESTURE);
+    }
+
+    public abstract void onWakeUp();
+
+    public boolean isSupported() {
+        synchronized (mLock) {
+            return mSensor != null;
+        }
+    }
+
+    public void requestWakeUpTrigger() {
+        synchronized (mLock) {
+            if (mSensor != null && !mTriggerRequested) {
+                mTriggerRequested = true;
+                mSensorManager.requestTriggerSensor(mListener, mSensor);
+            }
+        }
+    }
+
+    public void cancelWakeUpTrigger() {
+        synchronized (mLock) {
+            if (mSensor != null && mTriggerRequested) {
+                mTriggerRequested = false;
+                mSensorManager.cancelTriggerSensor(mListener, mSensor);
+            }
+        }
+    }
+
+    public void dump(PrintWriter pw, String prefix) {
+        synchronized (mLock) {
+            pw.println(prefix + TAG);
+            prefix += "  ";
+            pw.println(prefix + "mTriggerRequested=" + mTriggerRequested);
+            pw.println(prefix + "mSensor=" + mSensor);
+        }
+    }
+
+    private final TriggerEventListener mListener = new TriggerEventListener() {
+        @Override
+        public void onTrigger(TriggerEvent event) {
+            synchronized (mLock) {
+                mTriggerRequested = false;
+                mHandler.post(mWakeUpRunnable);
+            }
+        }
+    };
+
+    private final Runnable mWakeUpRunnable = new Runnable() {
+        @Override
+        public void run() {
+            onWakeUp();
+        }
+    };
+}
diff --git a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardServiceDelegate.java b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardServiceDelegate.java
index 966924b..63a5850 100644
--- a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardServiceDelegate.java
+++ b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardServiceDelegate.java
@@ -274,6 +274,12 @@
         mKeyguardState.currentUser = newUserId;
     }
 
+    public void startKeyguardExitAnimation(long startTime, long fadeoutDuration) {
+        if (mKeyguardService != null) {
+            mKeyguardService.startKeyguardExitAnimation(startTime, fadeoutDuration);
+        }
+    }
+
     private static final View createScrim(Context context) {
         View view = new View(context);
 
diff --git a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardServiceWrapper.java b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardServiceWrapper.java
index 7cb48fa..5096bd3 100644
--- a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardServiceWrapper.java
+++ b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardServiceWrapper.java
@@ -190,6 +190,14 @@
         }
     }
 
+    public void startKeyguardExitAnimation(long startTime, long fadeoutDuration) {
+        try {
+            mService.startKeyguardExitAnimation(startTime, fadeoutDuration);
+        } catch (RemoteException e) {
+            Slog.w(TAG , "Remote Exception", e);
+        }
+    }
+
     public void showAssistant() {
         // Not used by PhoneWindowManager
     }
diff --git a/rs/java/android/renderscript/FieldPacker.java b/rs/java/android/renderscript/FieldPacker.java
index 723ab24..0e57232 100644
--- a/rs/java/android/renderscript/FieldPacker.java
+++ b/rs/java/android/renderscript/FieldPacker.java
@@ -231,10 +231,18 @@
 
     public void addObj(BaseObj obj) {
         if (obj != null) {
-            // FIXME: this is fine for 32-bit but needs a path for 64-bit
-            addI32((int)obj.getID(null));
+            if (RenderScript.sPointerSize == 8) {
+                addI64(obj.getID(null));
+            }
+            else {
+                addI32((int)obj.getID(null));
+            }
         } else {
-            addI32(0);
+            if (RenderScript.sPointerSize == 8) {
+                addI64(0);
+            } else {
+                addI32(0);
+            }
         }
     }
 
diff --git a/rs/java/android/renderscript/RenderScript.java b/rs/java/android/renderscript/RenderScript.java
index 2222d2c..8cac22d 100644
--- a/rs/java/android/renderscript/RenderScript.java
+++ b/rs/java/android/renderscript/RenderScript.java
@@ -82,6 +82,12 @@
     */
     public static final long CREATE_FLAG_LOW_POWER = 0x0004;
 
+    /*
+     * Detect the bitness of the VM to allow FieldPacker to do the right thing.
+     */
+    static native int rsnSystemGetPointerSize();
+    static int sPointerSize;
+
     static {
         sInitialized = false;
         if (!SystemProperties.getBoolean("config.disable_renderscript", false)) {
@@ -99,6 +105,7 @@
                 System.loadLibrary("rs_jni");
                 _nInit();
                 sInitialized = true;
+                sPointerSize = rsnSystemGetPointerSize();
             } catch (UnsatisfiedLinkError e) {
                 Log.e(LOG_TAG, "Error loading RS jni library: " + e);
                 throw new RSRuntimeException("Error loading RS jni library: " + e);
diff --git a/rs/java/android/renderscript/ScriptC.java b/rs/java/android/renderscript/ScriptC.java
index 9e76f52..3176e28 100644
--- a/rs/java/android/renderscript/ScriptC.java
+++ b/rs/java/android/renderscript/ScriptC.java
@@ -67,6 +67,26 @@
     }
 
     /**
+     * Only intended for use by the generated derived classes.
+     *
+     * @param rs
+     * @hide
+     */
+    protected ScriptC(RenderScript rs, String resName, byte[] bitcode32, byte[] bitcode64) {
+        super(0, rs);
+        long id = 0;
+        if (RenderScript.sPointerSize == 4) {
+            id = internalStringCreate(rs, resName, bitcode32);
+        } else {
+            id = internalStringCreate(rs, resName, bitcode64);
+        }
+        if (id == 0) {
+            throw new RSRuntimeException("Loading of ScriptC script failed.");
+        }
+        setID(id);
+    }
+
+    /**
      * Name of the file that holds the object cache.
      */
     private static final String CACHE_PATH = "com.android.renderscript.cache";
@@ -113,4 +133,17 @@
         //        Log.v(TAG, "Create script for resource = " + resName);
         return rs.nScriptCCreate(resName, mCachePath, pgm, pgmLength);
     }
+
+    private static synchronized long internalStringCreate(RenderScript rs, String resName, byte[] bitcode) {
+        // Create the RS cache path if we haven't done so already.
+        if (mCachePath == null) {
+            File f = new File(rs.mCacheDir, CACHE_PATH);
+            mCachePath = f.getAbsolutePath();
+            f.mkdirs();
+        }
+        //        Log.v(TAG, "Create script for resource = " + resName);
+        return rs.nScriptCCreate(resName, mCachePath, bitcode, bitcode.length);
+    }
+
+
 }
diff --git a/rs/jni/android_renderscript_RenderScript.cpp b/rs/jni/android_renderscript_RenderScript.cpp
index 18a2e31..ae39b05 100644
--- a/rs/jni/android_renderscript_RenderScript.cpp
+++ b/rs/jni/android_renderscript_RenderScript.cpp
@@ -1573,6 +1573,12 @@
     free(prims);
 }
 
+static jint
+nSystemGetPointerSize(JNIEnv *_env, jobject _this) {
+    return (jint)sizeof(void*);
+}
+
+
 // ---------------------------------------------------------------------------
 
 
@@ -1708,6 +1714,7 @@
 {"rsnMeshGetVertices",               "(JJ[JI)V",                              (void*)nMeshGetVertices },
 {"rsnMeshGetIndices",                "(JJ[J[II)V",                            (void*)nMeshGetIndices },
 
+{"rsnSystemGetPointerSize",          "()I",                                   (void*)nSystemGetPointerSize },
 };
 
 static int registerFuncs(JNIEnv *_env)
diff --git a/services/Android.mk b/services/Android.mk
index 5fcef64..b4de903 100644
--- a/services/Android.mk
+++ b/services/Android.mk
@@ -25,6 +25,7 @@
     backup \
     devicepolicy \
     print \
+    restrictions \
     usb \
     voiceinteraction
 
diff --git a/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java b/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
index 87b1d32..7a67d63 100644
--- a/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
+++ b/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
@@ -843,6 +843,15 @@
                 throw new IllegalArgumentException("Unknown component " + componentName);
             }
 
+            // Ensure that the service specified by the passed intent belongs to the same package
+            // as provides the passed widget id.
+            String widgetIdPackage = id.provider.info.provider.getPackageName();
+            String servicePackage = componentName.getPackageName();
+            if (!servicePackage.equals(widgetIdPackage)) {
+                throw new SecurityException("Specified intent doesn't belong to the same package"
+                        + " as the provided AppWidget id");
+            }
+
             // If there is already a connection made for this service intent, then disconnect from
             // that first. (This does not allow multiple connections to the same service under
             // the same key)
diff --git a/services/backup/java/com/android/server/backup/BackupManagerService.java b/services/backup/java/com/android/server/backup/BackupManagerService.java
index 0082b1e..14c15a7 100644
--- a/services/backup/java/com/android/server/backup/BackupManagerService.java
+++ b/services/backup/java/com/android/server/backup/BackupManagerService.java
@@ -26,6 +26,7 @@
 import android.app.backup.BackupAgent;
 import android.app.backup.BackupDataInput;
 import android.app.backup.BackupDataOutput;
+import android.app.backup.BackupTransport;
 import android.app.backup.FullBackup;
 import android.app.backup.RestoreSet;
 import android.app.backup.IBackupManager;
@@ -82,7 +83,6 @@
 import android.util.SparseArray;
 import android.util.StringBuilderPrinter;
 
-import com.android.internal.backup.BackupConstants;
 import com.android.internal.backup.IBackupTransport;
 import com.android.internal.backup.IObbBackupService;
 import com.android.server.AppWidgetBackupBridge;
@@ -2098,7 +2098,7 @@
             }
 
             mAgentBinder = null;
-            mStatus = BackupConstants.TRANSPORT_OK;
+            mStatus = BackupTransport.TRANSPORT_OK;
 
             // Sanity check: if the queue is empty we have no work to do.
             if (mOriginalQueue.isEmpty()) {
@@ -2121,14 +2121,14 @@
                 EventLog.writeEvent(EventLogTags.BACKUP_START, transportName);
 
                 // If we haven't stored package manager metadata yet, we must init the transport.
-                if (mStatus == BackupConstants.TRANSPORT_OK && pmState.length() <= 0) {
+                if (mStatus == BackupTransport.TRANSPORT_OK && pmState.length() <= 0) {
                     Slog.i(TAG, "Initializing (wiping) backup state and transport storage");
                     addBackupTrace("initializing transport " + transportName);
                     resetBackupState(mStateDir);  // Just to make sure.
                     mStatus = mTransport.initializeDevice();
 
                     addBackupTrace("transport.initializeDevice() == " + mStatus);
-                    if (mStatus == BackupConstants.TRANSPORT_OK) {
+                    if (mStatus == BackupTransport.TRANSPORT_OK) {
                         EventLog.writeEvent(EventLogTags.BACKUP_INITIALIZE);
                     } else {
                         EventLog.writeEvent(EventLogTags.BACKUP_TRANSPORT_FAILURE, "(initialize)");
@@ -2141,7 +2141,7 @@
                 // directly and use a synthetic BackupRequest.  We always run this pass
                 // because it's cheap and this way we guarantee that we don't get out of
                 // step even if we're selecting among various transports at run time.
-                if (mStatus == BackupConstants.TRANSPORT_OK) {
+                if (mStatus == BackupTransport.TRANSPORT_OK) {
                     PackageManagerBackupAgent pmAgent = new PackageManagerBackupAgent(
                             mPackageManager, allAgentPackages());
                     mStatus = invokeAgentForBackup(PACKAGE_MANAGER_SENTINEL,
@@ -2149,7 +2149,7 @@
                     addBackupTrace("PMBA invoke: " + mStatus);
                 }
 
-                if (mStatus == BackupConstants.TRANSPORT_NOT_INITIALIZED) {
+                if (mStatus == BackupTransport.TRANSPORT_NOT_INITIALIZED) {
                     // The backend reports that our dataset has been wiped.  Note this in
                     // the event log; the no-success code below will reset the backup
                     // state as well.
@@ -2158,13 +2158,13 @@
             } catch (Exception e) {
                 Slog.e(TAG, "Error in backup thread", e);
                 addBackupTrace("Exception in backup thread: " + e);
-                mStatus = BackupConstants.TRANSPORT_ERROR;
+                mStatus = BackupTransport.TRANSPORT_ERROR;
             } finally {
                 // If we've succeeded so far, invokeAgentForBackup() will have run the PM
                 // metadata and its completion/timeout callback will continue the state
                 // machine chain.  If it failed that won't happen; we handle that now.
                 addBackupTrace("exiting prelim: " + mStatus);
-                if (mStatus != BackupConstants.TRANSPORT_OK) {
+                if (mStatus != BackupTransport.TRANSPORT_OK) {
                     // if things went wrong at this point, we need to
                     // restage everything and try again later.
                     resetBackupState(mStateDir);  // Just to make sure.
@@ -2176,7 +2176,7 @@
         // Transport has been initialized and the PM metadata submitted successfully
         // if that was warranted.  Now we process the single next thing in the queue.
         void invokeNextAgent() {
-            mStatus = BackupConstants.TRANSPORT_OK;
+            mStatus = BackupTransport.TRANSPORT_OK;
             addBackupTrace("invoke q=" + mQueue.size());
 
             // Sanity check that we have work to do.  If not, skip to the end where
@@ -2236,39 +2236,39 @@
                         // done here as long as we're successful so far.
                     } else {
                         // Timeout waiting for the agent
-                        mStatus = BackupConstants.AGENT_ERROR;
+                        mStatus = BackupTransport.AGENT_ERROR;
                     }
                 } catch (SecurityException ex) {
                     // Try for the next one.
                     Slog.d(TAG, "error in bind/backup", ex);
-                    mStatus = BackupConstants.AGENT_ERROR;
+                    mStatus = BackupTransport.AGENT_ERROR;
                             addBackupTrace("agent SE");
                 }
             } catch (NameNotFoundException e) {
                 Slog.d(TAG, "Package does not exist; skipping");
                 addBackupTrace("no such package");
-                mStatus = BackupConstants.AGENT_UNKNOWN;
+                mStatus = BackupTransport.AGENT_UNKNOWN;
             } finally {
                 mWakelock.setWorkSource(null);
 
                 // If there was an agent error, no timeout/completion handling will occur.
                 // That means we need to direct to the next state ourselves.
-                if (mStatus != BackupConstants.TRANSPORT_OK) {
+                if (mStatus != BackupTransport.TRANSPORT_OK) {
                     BackupState nextState = BackupState.RUNNING_QUEUE;
                     mAgentBinder = null;
 
                     // An agent-level failure means we reenqueue this one agent for
                     // a later retry, but otherwise proceed normally.
-                    if (mStatus == BackupConstants.AGENT_ERROR) {
+                    if (mStatus == BackupTransport.AGENT_ERROR) {
                         if (MORE_DEBUG) Slog.i(TAG, "Agent failure for " + request.packageName
                                 + " - restaging");
                         dataChangedImpl(request.packageName);
-                        mStatus = BackupConstants.TRANSPORT_OK;
+                        mStatus = BackupTransport.TRANSPORT_OK;
                         if (mQueue.isEmpty()) nextState = BackupState.FINAL;
-                    } else if (mStatus == BackupConstants.AGENT_UNKNOWN) {
+                    } else if (mStatus == BackupTransport.AGENT_UNKNOWN) {
                         // Failed lookup of the app, so we couldn't bring up an agent, but
                         // we're otherwise fine.  Just drop it and go on to the next as usual.
-                        mStatus = BackupConstants.TRANSPORT_OK;
+                        mStatus = BackupTransport.TRANSPORT_OK;
                     } else {
                         // Transport-level failure means we reenqueue everything
                         revertAndEndBackup();
@@ -2297,7 +2297,7 @@
             // If everything actually went through and this is the first time we've
             // done a backup, we can now record what the current backup dataset token
             // is.
-            if ((mCurrentToken == 0) && (mStatus == BackupConstants.TRANSPORT_OK)) {
+            if ((mCurrentToken == 0) && (mStatus == BackupTransport.TRANSPORT_OK)) {
                 addBackupTrace("success; recording token");
                 try {
                     mCurrentToken = mTransport.getCurrentRestoreSet();
@@ -2314,7 +2314,7 @@
             // state machine sequence and the wakelock is refcounted.
             synchronized (mQueueLock) {
                 mBackupRunning = false;
-                if (mStatus == BackupConstants.TRANSPORT_NOT_INITIALIZED) {
+                if (mStatus == BackupTransport.TRANSPORT_NOT_INITIALIZED) {
                     // Make sure we back up everything and perform the one-time init
                     clearMetadata();
                     if (DEBUG) Slog.d(TAG, "Server requires init; rerunning");
@@ -2395,7 +2395,7 @@
                 EventLog.writeEvent(EventLogTags.BACKUP_AGENT_FAILURE, packageName,
                         e.toString());
                 agentErrorCleanup();
-                return BackupConstants.AGENT_ERROR;
+                return BackupTransport.AGENT_ERROR;
             }
 
             // At this point the agent is off and running.  The next thing to happen will
@@ -2403,7 +2403,7 @@
             // for transport, or a timeout.  Either way the next phase will happen in
             // response to the TimeoutHandler interface callbacks.
             addBackupTrace("invoke success");
-            return BackupConstants.TRANSPORT_OK;
+            return BackupTransport.TRANSPORT_OK;
         }
 
         public void failAgent(IBackupAgent agent, String message) {
@@ -2484,11 +2484,11 @@
             addBackupTrace("operation complete");
 
             ParcelFileDescriptor backupData = null;
-            mStatus = BackupConstants.TRANSPORT_OK;
+            mStatus = BackupTransport.TRANSPORT_OK;
             try {
                 int size = (int) mBackupDataName.length();
                 if (size > 0) {
-                    if (mStatus == BackupConstants.TRANSPORT_OK) {
+                    if (mStatus == BackupTransport.TRANSPORT_OK) {
                         backupData = ParcelFileDescriptor.open(mBackupDataName,
                                 ParcelFileDescriptor.MODE_READ_ONLY);
                         addBackupTrace("sending data to transport");
@@ -2501,7 +2501,7 @@
                     // renaming *all* the output state files (see below) until that happens.
 
                     addBackupTrace("data delivered: " + mStatus);
-                    if (mStatus == BackupConstants.TRANSPORT_OK) {
+                    if (mStatus == BackupTransport.TRANSPORT_OK) {
                         addBackupTrace("finishing op on transport");
                         mStatus = mTransport.finishBackup();
                         addBackupTrace("finished: " + mStatus);
@@ -2514,7 +2514,7 @@
                 // After successful transport, delete the now-stale data
                 // and juggle the files so that next time we supply the agent
                 // with the new state file it just created.
-                if (mStatus == BackupConstants.TRANSPORT_OK) {
+                if (mStatus == BackupTransport.TRANSPORT_OK) {
                     mBackupDataName.delete();
                     mNewStateName.renameTo(mSavedStateName);
                     EventLog.writeEvent(EventLogTags.BACKUP_PACKAGE, pkgName, size);
@@ -2525,7 +2525,7 @@
             } catch (Exception e) {
                 Slog.e(TAG, "Transport error backing up " + pkgName, e);
                 EventLog.writeEvent(EventLogTags.BACKUP_TRANSPORT_FAILURE, pkgName);
-                mStatus = BackupConstants.TRANSPORT_ERROR;
+                mStatus = BackupTransport.TRANSPORT_ERROR;
             } finally {
                 try { if (backupData != null) backupData.close(); } catch (IOException e) {}
             }
@@ -2533,7 +2533,7 @@
             // If we encountered an error here it's a transport-level failure.  That
             // means we need to halt everything and reschedule everything for next time.
             final BackupState nextState;
-            if (mStatus != BackupConstants.TRANSPORT_OK) {
+            if (mStatus != BackupTransport.TRANSPORT_OK) {
                 revertAndEndBackup();
                 nextState = BackupState.FINAL;
             } else {
@@ -4847,7 +4847,7 @@
             mBackupHandler.removeMessages(MSG_RESTORE_TIMEOUT);
 
             // Assume error until we successfully init everything
-            mStatus = BackupConstants.TRANSPORT_ERROR;
+            mStatus = BackupTransport.TRANSPORT_ERROR;
 
             try {
                 // TODO: Log this before getAvailableRestoreSets, somehow
@@ -4902,7 +4902,7 @@
                 return;
             }
 
-            mStatus = BackupConstants.TRANSPORT_OK;
+            mStatus = BackupTransport.TRANSPORT_OK;
             executeNextState(RestoreState.DOWNLOAD_DATA);
         }
 
@@ -4917,7 +4917,7 @@
             try {
                 mStatus = mTransport.startRestore(mToken,
                         mRestorePackages.toArray(new PackageInfo[0]));
-                if (mStatus != BackupConstants.TRANSPORT_OK) {
+                if (mStatus != BackupTransport.TRANSPORT_OK) {
                     Slog.e(TAG, "Error starting restore operation");
                     EventLog.writeEvent(EventLogTags.RESTORE_TRANSPORT_FAILURE);
                     executeNextState(RestoreState.FINAL);
@@ -4926,7 +4926,7 @@
             } catch (RemoteException e) {
                 Slog.e(TAG, "Error communicating with transport for restore");
                 EventLog.writeEvent(EventLogTags.RESTORE_TRANSPORT_FAILURE);
-                mStatus = BackupConstants.TRANSPORT_ERROR;
+                mStatus = BackupTransport.TRANSPORT_ERROR;
                 executeNextState(RestoreState.FINAL);
                 return;
             }
@@ -4941,14 +4941,14 @@
                 if (packageName == null) {
                     Slog.e(TAG, "Error getting first restore package");
                     EventLog.writeEvent(EventLogTags.RESTORE_TRANSPORT_FAILURE);
-                    mStatus = BackupConstants.TRANSPORT_ERROR;
+                    mStatus = BackupTransport.TRANSPORT_ERROR;
                     executeNextState(RestoreState.FINAL);
                     return;
                 } else if (packageName.equals("")) {
                     Slog.i(TAG, "No restore data available");
                     int millis = (int) (SystemClock.elapsedRealtime() - mStartRealtime);
                     EventLog.writeEvent(EventLogTags.RESTORE_SUCCESS, 0, millis);
-                    mStatus = BackupConstants.TRANSPORT_OK;
+                    mStatus = BackupTransport.TRANSPORT_OK;
                     executeNextState(RestoreState.FINAL);
                     return;
                 } else if (!packageName.equals(PACKAGE_MANAGER_SENTINEL)) {
@@ -4979,7 +4979,7 @@
                     Slog.e(TAG, "No restore metadata available, so not restoring settings");
                     EventLog.writeEvent(EventLogTags.RESTORE_AGENT_FAILURE, PACKAGE_MANAGER_SENTINEL,
                     "Package manager restore metadata missing");
-                    mStatus = BackupConstants.TRANSPORT_ERROR;
+                    mStatus = BackupTransport.TRANSPORT_ERROR;
                     mBackupHandler.removeMessages(MSG_BACKUP_RESTORE_STEP, this);
                     executeNextState(RestoreState.FINAL);
                     return;
@@ -4987,7 +4987,7 @@
             } catch (RemoteException e) {
                 Slog.e(TAG, "Error communicating with transport for restore");
                 EventLog.writeEvent(EventLogTags.RESTORE_TRANSPORT_FAILURE);
-                mStatus = BackupConstants.TRANSPORT_ERROR;
+                mStatus = BackupTransport.TRANSPORT_ERROR;
                 mBackupHandler.removeMessages(MSG_BACKUP_RESTORE_STEP, this);
                 executeNextState(RestoreState.FINAL);
                 return;
@@ -5118,7 +5118,7 @@
                 }
             } catch (RemoteException e) {
                 Slog.e(TAG, "Unable to fetch restore data from transport");
-                mStatus = BackupConstants.TRANSPORT_ERROR;
+                mStatus = BackupTransport.TRANSPORT_ERROR;
                 executeNextState(RestoreState.FINAL);
             }
         }
@@ -5206,7 +5206,7 @@
                     Slog.e(TAG, "SElinux restorecon failed for " + downloadFile);
                 }
 
-                if (mTransport.getRestoreData(stage) != BackupConstants.TRANSPORT_OK) {
+                if (mTransport.getRestoreData(stage) != BackupTransport.TRANSPORT_OK) {
                     // Transport-level failure, so we wind everything up and
                     // terminate the restore operation.
                     Slog.e(TAG, "Error getting restore data for " + packageName);
@@ -5450,12 +5450,12 @@
                     long startRealtime = SystemClock.elapsedRealtime();
                     int status = transport.initializeDevice();
 
-                    if (status == BackupConstants.TRANSPORT_OK) {
+                    if (status == BackupTransport.TRANSPORT_OK) {
                         status = transport.finishBackup();
                     }
 
                     // Okay, the wipe really happened.  Clean up our local bookkeeping.
-                    if (status == BackupConstants.TRANSPORT_OK) {
+                    if (status == BackupTransport.TRANSPORT_OK) {
                         Slog.i(TAG, "Device init successful");
                         int millis = (int) (SystemClock.elapsedRealtime() - startRealtime);
                         EventLog.writeEvent(EventLogTags.BACKUP_INITIALIZE);
diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java
index 1e21e1c..b2b4217 100644
--- a/services/core/java/com/android/server/ConnectivityService.java
+++ b/services/core/java/com/android/server/ConnectivityService.java
@@ -38,7 +38,6 @@
 import static android.net.ConnectivityManager.TYPE_PROXY;
 import static android.net.ConnectivityManager.getNetworkTypeName;
 import static android.net.ConnectivityManager.isNetworkTypeValid;
-import static android.net.ConnectivityServiceProtocol.NetworkFactoryProtocol;
 import static android.net.NetworkPolicyManager.RULE_ALLOW_ALL;
 import static android.net.NetworkPolicyManager.RULE_REJECT_METERED;
 
@@ -80,6 +79,7 @@
 import android.net.NetworkConfig;
 import android.net.NetworkInfo;
 import android.net.NetworkInfo.DetailedState;
+import android.net.NetworkFactory;
 import android.net.NetworkQuotaInfo;
 import android.net.NetworkRequest;
 import android.net.NetworkState;
@@ -258,17 +258,6 @@
      */
     private NetworkStateTracker mNetTrackers[];
 
-    /**
-     * Holds references to all NetworkAgentInfos claiming to support the legacy
-     * NetworkType.  We used to have a static set of of NetworkStateTrackers
-     * for each network type.  This is the new model.
-     * Supports synchronous inspection of state.
-     * These are built out at startup such that an unsupported network
-     * doesn't get an ArrayList instance, making this a tristate:
-     * unsupported, supported but not active and active.
-     */
-    private ArrayList<NetworkAgentInfo> mNetworkAgentInfoForType[];
-
     /* Handles captive portal check on a network */
     private CaptivePortalTracker mCaptivePortalTracker;
 
@@ -514,6 +503,120 @@
     // sequence number of NetworkRequests
     private int mNextNetworkRequestId = 1;
 
+    private static final int UID_UNUSED = -1;
+
+    /**
+     * Implements support for the legacy "one network per network type" model.
+     *
+     * We used to have a static array of NetworkStateTrackers, one for each
+     * network type, but that doesn't work any more now that we can have,
+     * for example, more that one wifi network. This class stores all the
+     * NetworkAgentInfo objects that support a given type, but the legacy
+     * API will only see the first one.
+     *
+     * It serves two main purposes:
+     *
+     * 1. Provide information about "the network for a given type" (since this
+     *    API only supports one).
+     * 2. Send legacy connectivity change broadcasts. Broadcasts are sent if
+     *    the first network for a given type changes, or if the default network
+     *    changes.
+     */
+    private class LegacyTypeTracker {
+        /**
+         * Array of lists, one per legacy network type (e.g., TYPE_MOBILE_MMS).
+         * Each list holds references to all NetworkAgentInfos that are used to
+         * satisfy requests for that network type.
+         *
+         * This array is built out at startup such that an unsupported network
+         * doesn't get an ArrayList instance, making this a tristate:
+         * unsupported, supported but not active and active.
+         *
+         * The actual lists are populated when we scan the network types that
+         * are supported on this device.
+         */
+        private ArrayList<NetworkAgentInfo> mTypeLists[];
+
+        public LegacyTypeTracker() {
+            mTypeLists = (ArrayList<NetworkAgentInfo>[])
+                    new ArrayList[ConnectivityManager.MAX_NETWORK_TYPE + 1];
+        }
+
+        public void addSupportedType(int type) {
+            if (mTypeLists[type] != null) {
+                throw new IllegalStateException(
+                        "legacy list for type " + type + "already initialized");
+            }
+            mTypeLists[type] = new ArrayList<NetworkAgentInfo>();
+        }
+
+        private boolean isDefaultNetwork(NetworkAgentInfo nai) {
+            return mNetworkForRequestId.get(mDefaultRequest.requestId) == nai;
+        }
+
+        public boolean isTypeSupported(int type) {
+            return isNetworkTypeValid(type) && mTypeLists[type] != null;
+        }
+
+        public NetworkAgentInfo getNetworkForType(int type) {
+            if (isTypeSupported(type) && !mTypeLists[type].isEmpty()) {
+                return mTypeLists[type].get(0);
+            } else {
+                return null;
+            }
+        }
+
+        public void add(int type, NetworkAgentInfo nai) {
+            if (!isTypeSupported(type)) {
+                return;  // Invalid network type.
+            }
+            if (VDBG) log("Adding agent " + nai + " for legacy network type " + type);
+
+            ArrayList<NetworkAgentInfo> list = mTypeLists[type];
+            if (list.contains(nai)) {
+                loge("Attempting to register duplicate agent for type " + type + ": " + nai);
+                return;
+            }
+
+            if (list.isEmpty() || isDefaultNetwork(nai)) {
+                if (VDBG) log("Sending connected broadcast for type " + type +
+                              "isDefaultNetwork=" + isDefaultNetwork(nai));
+                sendLegacyNetworkBroadcast(nai, true, type);
+            }
+            list.add(nai);
+        }
+
+        public void remove(NetworkAgentInfo nai) {
+            if (VDBG) log("Removing agent " + nai);
+            for (int type = 0; type < mTypeLists.length; type++) {
+                ArrayList<NetworkAgentInfo> list = mTypeLists[type];
+                if (list == null || list.isEmpty()) {
+                    continue;
+                }
+
+                boolean wasFirstNetwork = false;
+                if (list.get(0).equals(nai)) {
+                    // This network was the first in the list. Send broadcast.
+                    wasFirstNetwork = true;
+                }
+                list.remove(nai);
+
+                if (wasFirstNetwork || isDefaultNetwork(nai)) {
+                    if (VDBG) log("Sending disconnected broadcast for type " + type +
+                                  "isDefaultNetwork=" + isDefaultNetwork(nai));
+                    sendLegacyNetworkBroadcast(nai, false, type);
+                }
+
+                if (!list.isEmpty() && wasFirstNetwork) {
+                    if (VDBG) log("Other network available for type " + type +
+                                  ", sending connected broadcast");
+                    sendLegacyNetworkBroadcast(list.get(0), false, type);
+                }
+            }
+        }
+    }
+    private LegacyTypeTracker mLegacyTypeTracker = new LegacyTypeTracker();
+
     public ConnectivityService(Context context, INetworkManagementService netd,
             INetworkStatsService statsService, INetworkPolicyManager policyManager) {
         // Currently, omitting a NetworkFactory will create one internally
@@ -529,7 +632,7 @@
         NetworkCapabilities netCap = new NetworkCapabilities();
         netCap.addNetworkCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET);
         netCap.addNetworkCapability(NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED);
-        mDefaultRequest = new NetworkRequest(netCap, true, nextNetworkRequestId());
+        mDefaultRequest = new NetworkRequest(netCap, TYPE_NONE, nextNetworkRequestId());
         NetworkRequestInfo nri = new NetworkRequestInfo(null, mDefaultRequest, new Binder(),
                 NetworkRequestInfo.REQUEST);
         mNetworkRequests.put(mDefaultRequest, nri);
@@ -585,9 +688,6 @@
         mNetTransitionWakeLockTimeout = mContext.getResources().getInteger(
                 com.android.internal.R.integer.config_networkTransitionTimeout);
 
-        mNetworkAgentInfoForType = (ArrayList<NetworkAgentInfo>[])
-                new ArrayList[ConnectivityManager.MAX_NETWORK_TYPE + 1];
-
         mNetTrackers = new NetworkStateTracker[
                 ConnectivityManager.MAX_NETWORK_TYPE+1];
         mCurrentLinkProperties = new LinkProperties[ConnectivityManager.MAX_NETWORK_TYPE+1];
@@ -642,7 +742,7 @@
                             "radio " + n.radio + " in network type " + n.type);
                     continue;
                 }
-                mNetworkAgentInfoForType[n.type] = new ArrayList<NetworkAgentInfo>();
+                mLegacyTypeTracker.addSupportedType(n.type);
 
                 mNetConfigs[n.type] = n;
                 mNetworksDefined++;
@@ -1673,10 +1773,12 @@
             }
             return false;
         }
+        final int uid = Binder.getCallingUid();
         final long token = Binder.clearCallingIdentity();
         try {
             LinkProperties lp = tracker.getLinkProperties();
-            boolean ok = addRouteToAddress(lp, addr, exempt, tracker.getNetwork().netId);
+            boolean ok = modifyRouteToAddress(lp, addr, ADD, TO_DEFAULT_TABLE, exempt,
+                    tracker.getNetwork().netId, uid);
             if (DBG) log("requestRouteToHostAddress ok=" + ok);
             return ok;
         } finally {
@@ -1686,24 +1788,15 @@
 
     private boolean addRoute(LinkProperties p, RouteInfo r, boolean toDefaultTable,
             boolean exempt, int netId) {
-        return modifyRoute(p, r, 0, ADD, toDefaultTable, exempt, netId);
+        return modifyRoute(p, r, 0, ADD, toDefaultTable, exempt, netId, false, UID_UNUSED);
     }
 
     private boolean removeRoute(LinkProperties p, RouteInfo r, boolean toDefaultTable, int netId) {
-        return modifyRoute(p, r, 0, REMOVE, toDefaultTable, UNEXEMPT, netId);
-    }
-
-    private boolean addRouteToAddress(LinkProperties lp, InetAddress addr, boolean exempt,
-                                      int netId) {
-        return modifyRouteToAddress(lp, addr, ADD, TO_DEFAULT_TABLE, exempt, netId);
-    }
-
-    private boolean removeRouteToAddress(LinkProperties lp, InetAddress addr, int netId) {
-        return modifyRouteToAddress(lp, addr, REMOVE, TO_DEFAULT_TABLE, UNEXEMPT, netId);
+        return modifyRoute(p, r, 0, REMOVE, toDefaultTable, UNEXEMPT, netId, false, UID_UNUSED);
     }
 
     private boolean modifyRouteToAddress(LinkProperties lp, InetAddress addr, boolean doAdd,
-            boolean toDefaultTable, boolean exempt, int netId) {
+            boolean toDefaultTable, boolean exempt, int netId, int uid) {
         RouteInfo bestRoute = RouteInfo.selectBestRoute(lp.getAllRoutes(), addr);
         if (bestRoute == null) {
             bestRoute = RouteInfo.makeHostRoute(addr, lp.getInterfaceName());
@@ -1718,11 +1811,18 @@
                 bestRoute = RouteInfo.makeHostRoute(addr, bestRoute.getGateway(), iface);
             }
         }
-        return modifyRoute(lp, bestRoute, 0, doAdd, toDefaultTable, exempt, netId);
+        return modifyRoute(lp, bestRoute, 0, doAdd, toDefaultTable, exempt, netId, true, uid);
     }
 
+    /*
+     * TODO: Clean all this stuff up. Once we have UID-based routing, stuff will break due to
+     *       incorrect tracking of mAddedRoutes, so a cleanup becomes necessary and urgent. But at
+     *       the same time, there'll be no more need to track mAddedRoutes or mExemptAddresses,
+     *       or even have the concept of an exempt address, or do things like "selectBestRoute", or
+     *       determine "default" vs "secondary" table, etc., so the cleanup becomes possible.
+     */
     private boolean modifyRoute(LinkProperties lp, RouteInfo r, int cycleCount, boolean doAdd,
-            boolean toDefaultTable, boolean exempt, int netId) {
+            boolean toDefaultTable, boolean exempt, int netId, boolean legacy, int uid) {
         if ((lp == null) || (r == null)) {
             if (DBG) log("modifyRoute got unexpected null: " + lp + ", " + r);
             return false;
@@ -1751,7 +1851,8 @@
                                                         bestRoute.getGateway(),
                                                         ifaceName);
                 }
-                modifyRoute(lp, bestRoute, cycleCount+1, doAdd, toDefaultTable, exempt, netId);
+                modifyRoute(lp, bestRoute, cycleCount+1, doAdd, toDefaultTable, exempt, netId,
+                        legacy, uid);
             }
         }
         if (doAdd) {
@@ -1761,7 +1862,11 @@
                     synchronized (mRoutesLock) {
                         // only track default table - only one apps can effect
                         mAddedRoutes.add(r);
-                        mNetd.addRoute(netId, r);
+                        if (legacy) {
+                            mNetd.addLegacyRouteForNetId(netId, r, uid);
+                        } else {
+                            mNetd.addRoute(netId, r);
+                        }
                         if (exempt) {
                             LinkAddress dest = r.getDestination();
                             if (!mExemptAddresses.contains(dest)) {
@@ -1771,7 +1876,11 @@
                         }
                     }
                 } else {
-                    mNetd.addRoute(netId, r);
+                    if (legacy) {
+                        mNetd.addLegacyRouteForNetId(netId, r, uid);
+                    } else {
+                        mNetd.addRoute(netId, r);
+                    }
                 }
             } catch (Exception e) {
                 // never crash - catch them all
@@ -1787,7 +1896,11 @@
                     if (mAddedRoutes.contains(r) == false) {
                         if (VDBG) log("Removing " + r + " for interface " + ifaceName);
                         try {
-                            mNetd.removeRoute(netId, r);
+                            if (legacy) {
+                                mNetd.removeLegacyRouteForNetId(netId, r, uid);
+                            } else {
+                                mNetd.removeRoute(netId, r);
+                            }
                             LinkAddress dest = r.getDestination();
                             if (mExemptAddresses.contains(dest)) {
                                 mNetd.clearHostExemption(dest);
@@ -1805,7 +1918,11 @@
             } else {
                 if (VDBG) log("Removing " + r + " for interface " + ifaceName);
                 try {
-                    mNetd.removeRoute(netId, r);
+                    if (legacy) {
+                        mNetd.removeLegacyRouteForNetId(netId, r, uid);
+                    } else {
+                        mNetd.removeRoute(netId, r);
+                    }
                 } catch (Exception e) {
                     // never crash - catch them all
                     if (VDBG) loge("Exception trying to remove a route: " + e);
@@ -2824,7 +2941,8 @@
         }
     }
 
-    private int getRestoreDefaultNetworkDelay(int networkType) {
+    @Override
+    public int getRestoreDefaultNetworkDelay(int networkType) {
         String restoreDefaultNetworkDelayStr = SystemProperties.get(
                 NETWORK_RESTORE_DELAY_PROP_NAME);
         if(restoreDefaultNetworkDelayStr != null &&
@@ -2975,6 +3093,16 @@
                     updateNetworkInfo(nai, info);
                     break;
                 }
+                case NetworkAgent.EVENT_NETWORK_SCORE_CHANGED: {
+                    NetworkAgentInfo nai = mNetworkAgentInfos.get(msg.replyTo);
+                    if (nai == null) {
+                        loge("EVENT_NETWORK_SCORE_CHANGED from unknown NetworkAgent");
+                        break;
+                    }
+                    Integer score = (Integer) msg.obj;
+                    updateNetworkScore(nai, score);
+                    break;
+                }
                 case NetworkMonitor.EVENT_NETWORK_VALIDATED: {
                     NetworkAgentInfo nai = (NetworkAgentInfo)msg.obj;
                     handleConnectionValidated(nai);
@@ -3079,7 +3207,7 @@
                 for (NetworkRequestInfo nri : mNetworkRequests.values()) {
                     if (nri.isRequest == false) continue;
                     NetworkAgentInfo nai = mNetworkForRequestId.get(nri.request.requestId);
-                    ac.sendMessage(NetworkFactoryProtocol.CMD_REQUEST_NETWORK,
+                    ac.sendMessage(android.net.NetworkFactory.CMD_REQUEST_NETWORK,
                             (nai != null ? nai.currentScore : 0), 0, nri.request);
                 }
             } else {
@@ -3095,11 +3223,9 @@
             } else {
                 loge("Error connecting NetworkAgent");
                 NetworkAgentInfo nai = mNetworkAgentInfos.remove(msg.replyTo);
-                try {
-                    mNetworkAgentInfoForType[nai.networkInfo.getType()].remove(nai);
-                } catch (NullPointerException e) {}
                 if (nai != null) {
                     mNetworkForNetId.remove(nai.network.netId);
+                    mLegacyTypeTracker.remove(nai);
                 }
             }
         }
@@ -3118,14 +3244,19 @@
             } catch (Exception e) {
                 loge("Exception removing network: " + e);
             }
+            // TODO - if we move the logic to the network agent (have them disconnect
+            // because they lost all their requests or because their score isn't good)
+            // then they would disconnect organically, report their new state and then
+            // disconnect the channel.
+            if (nai.networkInfo.isConnected()) {
+                nai.networkInfo.setDetailedState(NetworkInfo.DetailedState.DISCONNECTED,
+                        null, null);
+            }
             notifyNetworkCallbacks(nai, ConnectivityManager.CALLBACK_LOST);
             nai.networkMonitor.sendMessage(NetworkMonitor.CMD_NETWORK_DISCONNECTED);
             mNetworkAgentInfos.remove(msg.replyTo);
             updateClat(null, nai.linkProperties, nai);
-            try {
-                mNetworkAgentInfoForType[nai.networkInfo.getType()].remove(nai);
-            } catch (NullPointerException e) {}
-
+            mLegacyTypeTracker.remove(nai);
             mNetworkForNetId.remove(nai.network.netId);
             // Since we've lost the network, go through all the requests that
             // it was satisfying and see if any other factory can satisfy them.
@@ -3135,7 +3266,7 @@
                 NetworkAgentInfo currentNetwork = mNetworkForRequestId.get(request.requestId);
                 if (VDBG) {
                     log(" checking request " + request + ", currentNetwork = " +
-                            currentNetwork != null ? currentNetwork.name() : "null");
+                            (currentNetwork != null ? currentNetwork.name() : "null"));
                 }
                 if (currentNetwork != null && currentNetwork.network.netId == nai.network.netId) {
                     mNetworkForRequestId.remove(request.requestId);
@@ -3184,7 +3315,11 @@
         }
         if (bestNetwork != null) {
             if (VDBG) log("using " + bestNetwork.name());
-            bestNetwork.networkRequests.put(nri.request.requestId, nri.request);
+            bestNetwork.addRequest(nri.request);
+            int legacyType = nri.request.legacyType;
+            if (legacyType != TYPE_NONE) {
+                mLegacyTypeTracker.add(legacyType, bestNetwork);
+            }
             notifyNetworkCallback(bestNetwork, nri);
             score = bestNetwork.currentScore;
         }
@@ -3192,7 +3327,8 @@
         if (msg.what == EVENT_REGISTER_NETWORK_REQUEST) {
             if (DBG) log("sending new NetworkRequest to factories");
             for (NetworkFactoryInfo nfi : mNetworkFactoryInfos.values()) {
-                nfi.asyncChannel.sendMessage(NetworkFactoryProtocol.CMD_REQUEST_NETWORK, score, 0, nri.request);
+                nfi.asyncChannel.sendMessage(android.net.NetworkFactory.CMD_REQUEST_NETWORK, score,
+                        0, nri.request);
             }
         }
     }
@@ -3214,7 +3350,8 @@
 
             if (nri.isRequest) {
                 for (NetworkFactoryInfo nfi : mNetworkFactoryInfos.values()) {
-                    nfi.asyncChannel.sendMessage(NetworkFactoryProtocol.CMD_CANCEL_REQUEST, nri.request);
+                    nfi.asyncChannel.sendMessage(android.net.NetworkFactory.CMD_CANCEL_REQUEST,
+                            nri.request);
                 }
 
                 if (affectedNetwork != null) {
@@ -5260,7 +5397,7 @@
 
     @Override
     public NetworkRequest requestNetwork(NetworkCapabilities networkCapabilities,
-            Messenger messenger, int timeoutSec, IBinder binder) {
+            Messenger messenger, int timeoutSec, IBinder binder, int legacyType) {
         if (networkCapabilities.hasCapability(NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED)
                 == false) {
             enforceConnectivityInternalPermission();
@@ -5272,7 +5409,7 @@
             throw new IllegalArgumentException("Bad timeout specified");
         }
         NetworkRequest networkRequest = new NetworkRequest(new NetworkCapabilities(
-                networkCapabilities), false, nextNetworkRequestId());
+                networkCapabilities), legacyType, nextNetworkRequestId());
         if (DBG) log("requestNetwork for " + networkRequest);
         NetworkRequestInfo nri = new NetworkRequestInfo(messenger, networkRequest, binder,
                 NetworkRequestInfo.REQUEST);
@@ -5298,7 +5435,7 @@
         enforceAccessPermission();
 
         NetworkRequest networkRequest = new NetworkRequest(new NetworkCapabilities(
-                networkCapabilities), false, nextNetworkRequestId());
+                networkCapabilities), TYPE_NONE, nextNetworkRequestId());
         if (DBG) log("listenForNetwork for " + networkRequest);
         NetworkRequestInfo nri = new NetworkRequestInfo(messenger, networkRequest, binder,
                 NetworkRequestInfo.LISTEN);
@@ -5373,18 +5510,13 @@
         NetworkAgentInfo nai = new NetworkAgentInfo(messenger, new AsyncChannel(), nextNetId(),
             new NetworkInfo(networkInfo), new LinkProperties(linkProperties),
             new NetworkCapabilities(networkCapabilities), currentScore, mContext, mTrackerHandler);
-
+        if (VDBG) log("registerNetworkAgent " + nai);
         mHandler.sendMessage(mHandler.obtainMessage(EVENT_REGISTER_NETWORK_AGENT, nai));
     }
 
     private void handleRegisterNetworkAgent(NetworkAgentInfo na) {
         if (VDBG) log("Got NetworkAgent Messenger");
         mNetworkAgentInfos.put(na.messenger, na);
-        try {
-            mNetworkAgentInfoForType[na.networkInfo.getType()].add(na);
-        } catch (NullPointerException e) {
-            loge("registered NetworkAgent for unsupported type: " + na);
-        }
         mNetworkForNetId.put(na.network.netId, na);
         na.asyncChannel.connect(mContext, mTrackerHandler, na.messenger);
         NetworkInfo networkInfo = na.networkInfo;
@@ -5420,7 +5552,7 @@
                 mClat.stopClat();
             }
             // If the link requires clat to be running, then start the daemon now.
-            if (newLp != null && na.networkInfo.isConnected()) {
+            if (na.networkInfo.isConnected()) {
                 mClat.startClat(na);
             } else {
                 mClat.stopClat();
@@ -5536,7 +5668,8 @@
     private void sendUpdatedScoreToFactories(NetworkRequest networkRequest, int score) {
         if (VDBG) log("sending new Min Network Score(" + score + "): " + networkRequest.toString());
         for (NetworkFactoryInfo nfi : mNetworkFactoryInfos.values()) {
-            nfi.asyncChannel.sendMessage(NetworkFactoryProtocol.CMD_REQUEST_NETWORK, score, 0, networkRequest);
+            nfi.asyncChannel.sendMessage(android.net.NetworkFactory.CMD_REQUEST_NETWORK, score, 0,
+                    networkRequest);
         }
     }
 
@@ -5606,16 +5739,23 @@
         boolean isNewDefault = false;
         if (DBG) log("handleConnectionValidated for "+newNetwork.name());
         // check if any NetworkRequest wants this NetworkAgent
-        // first check if it satisfies the NetworkCapabilities
         ArrayList<NetworkAgentInfo> affectedNetworks = new ArrayList<NetworkAgentInfo>();
         if (VDBG) log(" new Network has: " + newNetwork.networkCapabilities);
         for (NetworkRequestInfo nri : mNetworkRequests.values()) {
+            NetworkAgentInfo currentNetwork = mNetworkForRequestId.get(nri.request.requestId);
+            if (newNetwork == currentNetwork) {
+                if (VDBG) log("Network " + newNetwork.name() + " was already satisfying" +
+                              " request " + nri.request.requestId + ". No change.");
+                keep = true;
+                continue;
+            }
+
+            // check if it satisfies the NetworkCapabilities
             if (VDBG) log("  checking if request is satisfied: " + nri.request);
             if (nri.request.networkCapabilities.satisfiedByNetworkCapabilities(
                     newNetwork.networkCapabilities)) {
                 // next check if it's better than any current network we're using for
                 // this request
-                NetworkAgentInfo currentNetwork = mNetworkForRequestId.get(nri.request.requestId);
                 if (VDBG) {
                     log("currentScore = " +
                             (currentNetwork != null ? currentNetwork.currentScore : 0) +
@@ -5632,7 +5772,11 @@
                         if (VDBG) log("   accepting network in place of null");
                     }
                     mNetworkForRequestId.put(nri.request.requestId, newNetwork);
-                    newNetwork.networkRequests.put(nri.request.requestId, nri.request);
+                    newNetwork.addRequest(nri.request);
+                    int legacyType = nri.request.legacyType;
+                    if (legacyType != TYPE_NONE) {
+                        mLegacyTypeTracker.add(legacyType, newNetwork);
+                    }
                     keep = true;
                     // TODO - this could get expensive if we have alot of requests for this
                     // network.  Think about if there is a way to reduce this.  Push
@@ -5646,6 +5790,7 @@
                         } else {
                             setDefaultDnsSystemProperties(new ArrayList<InetAddress>());
                         }
+                        mLegacyTypeTracker.add(newNetwork.networkInfo.getType(), newNetwork);
                     }
                 }
             }
@@ -5744,12 +5889,19 @@
         }
 
         if (state == NetworkInfo.State.CONNECTED) {
-            // TODO - check if we want it (optimization)
             try {
+                // This is likely caused by the fact that this network already
+                // exists. An example is when a network goes from CONNECTED to
+                // CONNECTING and back (like wifi on DHCP renew).
+                // TODO: keep track of which networks we've created, or ask netd
+                // to tell us whether we've already created this network or not.
                 mNetd.createNetwork(networkAgent.network.netId);
             } catch (Exception e) {
-                loge("Error creating Network " + networkAgent.network.netId);
+                loge("Error creating network " + networkAgent.network.netId + ": "
+                        + e.getMessage());
+                return;
             }
+
             updateLinkProperties(networkAgent, null);
             notifyNetworkCallbacks(networkAgent, ConnectivityManager.CALLBACK_PRECHECK);
             networkAgent.networkMonitor.sendMessage(NetworkMonitor.CMD_NETWORK_CONNECTED);
@@ -5759,6 +5911,11 @@
         }
     }
 
+    private void updateNetworkScore(NetworkAgentInfo nai, Integer scoreInteger) {
+        int score = scoreInteger.intValue();
+        // TODO
+    }
+
     // notify only this one new request of the current state
     protected void notifyNetworkCallback(NetworkAgentInfo nai, NetworkRequestInfo nri) {
         int notifyType = ConnectivityManager.CALLBACK_AVAILABLE;
@@ -5768,93 +5925,88 @@
 //        } else if (nai.networkMonitor.isEvaluating()) {
 //            notifyType = NetworkCallbacks.callCallbackForRequest(request, nai, notifyType);
 //        }
-        if (nri.request.needsBroadcasts) {
-        // TODO
-//            sendNetworkBroadcast(nai, notifyType);
-        }
         callCallbackForRequest(nri, nai, notifyType);
     }
 
-    protected void notifyNetworkCallbacks(NetworkAgentInfo networkAgent, int notifyType) {
-        if (VDBG) log("notifyType " + notifyType + " for " + networkAgent.name());
-        boolean needsBroadcasts = false;
-        for (int i = 0; i < networkAgent.networkRequests.size(); i++) {
-            NetworkRequest nr = networkAgent.networkRequests.valueAt(i);
-            NetworkRequestInfo nri = mNetworkRequests.get(nr);
-            if (VDBG) log(" sending notification for " + nr);
-            if (nr.needsBroadcasts) needsBroadcasts = true;
-            callCallbackForRequest(nri, networkAgent, notifyType);
-        }
-        if (needsBroadcasts) {
-            if (notifyType == ConnectivityManager.CALLBACK_AVAILABLE) {
-                sendConnectedBroadcastDelayed(networkAgent.networkInfo,
-                        getConnectivityChangeDelay());
-            } else if (notifyType == ConnectivityManager.CALLBACK_LOST) {
-                NetworkInfo info = new NetworkInfo(networkAgent.networkInfo);
-                Intent intent = new Intent(ConnectivityManager.CONNECTIVITY_ACTION);
-                intent.putExtra(ConnectivityManager.EXTRA_NETWORK_INFO, info);
-                intent.putExtra(ConnectivityManager.EXTRA_NETWORK_TYPE, info.getType());
-                if (info.isFailover()) {
-                    intent.putExtra(ConnectivityManager.EXTRA_IS_FAILOVER, true);
-                    networkAgent.networkInfo.setFailover(false);
-                }
-                if (info.getReason() != null) {
-                    intent.putExtra(ConnectivityManager.EXTRA_REASON, info.getReason());
-                }
-                if (info.getExtraInfo() != null) {
-                    intent.putExtra(ConnectivityManager.EXTRA_EXTRA_INFO, info.getExtraInfo());
-                }
-                NetworkAgentInfo newDefaultAgent = null;
-                if (networkAgent.networkRequests.get(mDefaultRequest.requestId) != null) {
-                    newDefaultAgent = mNetworkForRequestId.get(mDefaultRequest.requestId);
-                    if (newDefaultAgent != null) {
-                        intent.putExtra(ConnectivityManager.EXTRA_OTHER_NETWORK_INFO,
-                                newDefaultAgent.networkInfo);
-                    } else {
-                        intent.putExtra(ConnectivityManager.EXTRA_NO_CONNECTIVITY, true);
-                    }
-                }
-                intent.putExtra(ConnectivityManager.EXTRA_INET_CONDITION,
-                        mDefaultInetConditionPublished);
-                final Intent immediateIntent = new Intent(intent);
-                immediateIntent.setAction(CONNECTIVITY_ACTION_IMMEDIATE);
-                sendStickyBroadcast(immediateIntent);
-                sendStickyBroadcastDelayed(intent, getConnectivityChangeDelay());
+    private void sendLegacyNetworkBroadcast(NetworkAgentInfo nai, boolean connected, int type) {
+        if (connected) {
+            NetworkInfo info = new NetworkInfo(nai.networkInfo);
+            info.setType(type);
+            sendConnectedBroadcastDelayed(info, getConnectivityChangeDelay());
+        } else {
+            NetworkInfo info = new NetworkInfo(nai.networkInfo);
+            info.setType(type);
+            Intent intent = new Intent(ConnectivityManager.CONNECTIVITY_ACTION);
+            intent.putExtra(ConnectivityManager.EXTRA_NETWORK_INFO, info);
+            intent.putExtra(ConnectivityManager.EXTRA_NETWORK_TYPE, info.getType());
+            if (info.isFailover()) {
+                intent.putExtra(ConnectivityManager.EXTRA_IS_FAILOVER, true);
+                nai.networkInfo.setFailover(false);
+            }
+            if (info.getReason() != null) {
+                intent.putExtra(ConnectivityManager.EXTRA_REASON, info.getReason());
+            }
+            if (info.getExtraInfo() != null) {
+                intent.putExtra(ConnectivityManager.EXTRA_EXTRA_INFO, info.getExtraInfo());
+            }
+            NetworkAgentInfo newDefaultAgent = null;
+            if (nai.networkRequests.get(mDefaultRequest.requestId) != null) {
+                newDefaultAgent = mNetworkForRequestId.get(mDefaultRequest.requestId);
                 if (newDefaultAgent != null) {
-                    sendConnectedBroadcastDelayed(newDefaultAgent.networkInfo,
-                            getConnectivityChangeDelay());
+                    intent.putExtra(ConnectivityManager.EXTRA_OTHER_NETWORK_INFO,
+                            newDefaultAgent.networkInfo);
+                } else {
+                    intent.putExtra(ConnectivityManager.EXTRA_NO_CONNECTIVITY, true);
                 }
             }
+            intent.putExtra(ConnectivityManager.EXTRA_INET_CONDITION,
+                    mDefaultInetConditionPublished);
+            final Intent immediateIntent = new Intent(intent);
+            immediateIntent.setAction(CONNECTIVITY_ACTION_IMMEDIATE);
+            sendStickyBroadcast(immediateIntent);
+            sendStickyBroadcastDelayed(intent, getConnectivityChangeDelay());
+            if (newDefaultAgent != null) {
+                sendConnectedBroadcastDelayed(newDefaultAgent.networkInfo,
+                getConnectivityChangeDelay());
+            }
         }
     }
 
-    private LinkProperties getLinkPropertiesForTypeInternal(int networkType) {
-        ArrayList<NetworkAgentInfo> list = mNetworkAgentInfoForType[networkType];
-        if (list == null) return null;
-        try {
-            return new LinkProperties(list.get(0).linkProperties);
-        } catch (IndexOutOfBoundsException e) {
-            return new LinkProperties();
+    protected void notifyNetworkCallbacks(NetworkAgentInfo networkAgent, int notifyType) {
+        if (VDBG) log("notifyType " + notifyType + " for " + networkAgent.name());
+        for (int i = 0; i < networkAgent.networkRequests.size(); i++) {
+            NetworkRequest nr = networkAgent.networkRequests.valueAt(i);
+            NetworkRequestInfo nri = mNetworkRequests.get(nr);
+            if (VDBG) log(" sending notification for " + nr);
+            callCallbackForRequest(nri, networkAgent, notifyType);
         }
     }
 
+    private LinkProperties getLinkPropertiesForTypeInternal(int networkType) {
+        NetworkAgentInfo nai = mLegacyTypeTracker.getNetworkForType(networkType);
+        return (nai != null) ?
+                new LinkProperties(nai.linkProperties) :
+                new LinkProperties();
+    }
+
     private NetworkInfo getNetworkInfoForType(int networkType) {
-        ArrayList<NetworkAgentInfo> list = mNetworkAgentInfoForType[networkType];
-        if (list == null) return null;
-        try {
-            return new NetworkInfo(list.get(0).networkInfo);
-        } catch (IndexOutOfBoundsException e) {
-            return new NetworkInfo(networkType, 0, "Unknown", "");
+        if (!mLegacyTypeTracker.isTypeSupported(networkType))
+            return null;
+
+        NetworkAgentInfo nai = mLegacyTypeTracker.getNetworkForType(networkType);
+        if (nai != null) {
+            NetworkInfo result = new NetworkInfo(nai.networkInfo);
+            result.setType(networkType);
+            return result;
+        } else {
+           return new NetworkInfo(networkType, 0, "Unknown", "");
         }
     }
 
     private NetworkCapabilities getNetworkCapabilitiesForType(int networkType) {
-        ArrayList<NetworkAgentInfo> list = mNetworkAgentInfoForType[networkType];
-        if (list == null) return null;
-        try {
-            return new NetworkCapabilities(list.get(0).networkCapabilities);
-        } catch (IndexOutOfBoundsException e) {
-            return new NetworkCapabilities();
-        }
+        NetworkAgentInfo nai = mLegacyTypeTracker.getNetworkForType(networkType);
+        return (nai != null) ?
+                new NetworkCapabilities(nai.networkCapabilities) :
+                new NetworkCapabilities();
     }
 }
diff --git a/services/core/java/com/android/server/DockObserver.java b/services/core/java/com/android/server/DockObserver.java
index 4a8bf72..af38664 100644
--- a/services/core/java/com/android/server/DockObserver.java
+++ b/services/core/java/com/android/server/DockObserver.java
@@ -19,10 +19,12 @@
 import android.content.ContentResolver;
 import android.content.Context;
 import android.content.Intent;
+import android.content.pm.PackageManager;
 import android.media.AudioManager;
 import android.media.Ringtone;
 import android.media.RingtoneManager;
 import android.net.Uri;
+import android.os.Binder;
 import android.os.Handler;
 import android.os.Message;
 import android.os.PowerManager;
@@ -33,62 +35,62 @@
 import android.util.Log;
 import android.util.Slog;
 
+import java.io.FileDescriptor;
 import java.io.FileNotFoundException;
 import java.io.FileReader;
+import java.io.PrintWriter;
 
 /**
- * <p>DockObserver monitors for a docking station.
+ * DockObserver monitors for a docking station.
  */
-final class DockObserver extends UEventObserver {
-    private static final String TAG = DockObserver.class.getSimpleName();
+final class DockObserver extends SystemService {
+    private static final String TAG = "DockObserver";
 
     private static final String DOCK_UEVENT_MATCH = "DEVPATH=/devices/virtual/switch/dock";
     private static final String DOCK_STATE_PATH = "/sys/class/switch/dock/state";
 
     private static final int MSG_DOCK_STATE_CHANGED = 0;
 
-    private final Object mLock = new Object();
-
-    private int mDockState = Intent.EXTRA_DOCK_STATE_UNDOCKED;
-    private int mPreviousDockState = Intent.EXTRA_DOCK_STATE_UNDOCKED;
-
-    private boolean mSystemReady;
-
-    private final Context mContext;
     private final PowerManager mPowerManager;
     private final PowerManager.WakeLock mWakeLock;
 
-    public DockObserver(Context context) {
-        mContext = context;
+    private final Object mLock = new Object();
 
-        mPowerManager = (PowerManager)mContext.getSystemService(Context.POWER_SERVICE);
+    private boolean mSystemReady;
+
+    private int mActualDockState = Intent.EXTRA_DOCK_STATE_UNDOCKED;
+
+    private int mReportedDockState = Intent.EXTRA_DOCK_STATE_UNDOCKED;
+    private int mPreviousDockState = Intent.EXTRA_DOCK_STATE_UNDOCKED;
+
+    private boolean mUpdatesStopped;
+
+    public DockObserver(Context context) {
+        super(context);
+
+        mPowerManager = (PowerManager)context.getSystemService(Context.POWER_SERVICE);
         mWakeLock = mPowerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, TAG);
 
         init();  // set initial status
-        startObserving(DOCK_UEVENT_MATCH);
+
+        mObserver.startObserving(DOCK_UEVENT_MATCH);
     }
 
     @Override
-    public void onUEvent(UEventObserver.UEvent event) {
-        if (Log.isLoggable(TAG, Log.VERBOSE)) {
-            Slog.v(TAG, "Dock UEVENT: " + event.toString());
-        }
+    public void onStart() {
+        publishBinderService(TAG, new BinderService());
+    }
 
-        synchronized (mLock) {
-            try {
-                int newState = Integer.parseInt(event.get("SWITCH_STATE"));
-                if (newState != mDockState) {
-                    mPreviousDockState = mDockState;
-                    mDockState = newState;
-                    if (mSystemReady) {
-                        // Wake up immediately when docked or undocked.
-                        mPowerManager.wakeUp(SystemClock.uptimeMillis());
+    @Override
+    public void onBootPhase(int phase) {
+        if (phase == PHASE_ACTIVITY_MANAGER_READY) {
+            synchronized (mLock) {
+                mSystemReady = true;
 
-                        updateLocked();
-                    }
+                // don't bother broadcasting undocked here
+                if (mReportedDockState != Intent.EXTRA_DOCK_STATE_UNDOCKED) {
+                    updateLocked();
                 }
-            } catch (NumberFormatException e) {
-                Slog.e(TAG, "Could not parse switch state from event " + event);
             }
         }
     }
@@ -100,8 +102,8 @@
                 FileReader file = new FileReader(DOCK_STATE_PATH);
                 try {
                     int len = file.read(buffer, 0, 1024);
-                    mDockState = Integer.valueOf((new String(buffer, 0, len)).trim());
-                    mPreviousDockState = mDockState;
+                    setActualDockStateLocked(Integer.valueOf((new String(buffer, 0, len)).trim()));
+                    mPreviousDockState = mActualDockState;
                 } finally {
                     file.close();
                 }
@@ -113,13 +115,21 @@
         }
     }
 
-    void systemReady() {
-        synchronized (mLock) {
-            // don't bother broadcasting undocked here
-            if (mDockState != Intent.EXTRA_DOCK_STATE_UNDOCKED) {
+    private void setActualDockStateLocked(int newState) {
+        mActualDockState = newState;
+        if (!mUpdatesStopped) {
+            setDockStateLocked(newState);
+        }
+    }
+
+    private void setDockStateLocked(int newState) {
+        if (newState != mReportedDockState) {
+            mReportedDockState = newState;
+            if (mSystemReady) {
+                // Wake up immediately when docked or undocked.
+                mPowerManager.wakeUp(SystemClock.uptimeMillis());
                 updateLocked();
             }
-            mSystemReady = true;
         }
     }
 
@@ -130,10 +140,13 @@
 
     private void handleDockStateChange() {
         synchronized (mLock) {
-            Slog.i(TAG, "Dock state changed: " + mDockState);
+            Slog.i(TAG, "Dock state changed from " + mPreviousDockState + " to "
+                    + mReportedDockState);
+            final int previousDockState = mPreviousDockState;
+            mPreviousDockState = mReportedDockState;
 
             // Skip the dock intent if not yet provisioned.
-            final ContentResolver cr = mContext.getContentResolver();
+            final ContentResolver cr = getContext().getContentResolver();
             if (Settings.Global.getInt(cr,
                     Settings.Global.DEVICE_PROVISIONED, 0) == 0) {
                 Slog.i(TAG, "Device not provisioned, skipping dock broadcast");
@@ -143,27 +156,27 @@
             // Pack up the values and broadcast them to everyone
             Intent intent = new Intent(Intent.ACTION_DOCK_EVENT);
             intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING);
-            intent.putExtra(Intent.EXTRA_DOCK_STATE, mDockState);
+            intent.putExtra(Intent.EXTRA_DOCK_STATE, mReportedDockState);
 
             // Play a sound to provide feedback to confirm dock connection.
             // Particularly useful for flaky contact pins...
             if (Settings.Global.getInt(cr,
                     Settings.Global.DOCK_SOUNDS_ENABLED, 1) == 1) {
                 String whichSound = null;
-                if (mDockState == Intent.EXTRA_DOCK_STATE_UNDOCKED) {
-                    if ((mPreviousDockState == Intent.EXTRA_DOCK_STATE_DESK) ||
-                        (mPreviousDockState == Intent.EXTRA_DOCK_STATE_LE_DESK) ||
-                        (mPreviousDockState == Intent.EXTRA_DOCK_STATE_HE_DESK)) {
+                if (mReportedDockState == Intent.EXTRA_DOCK_STATE_UNDOCKED) {
+                    if ((previousDockState == Intent.EXTRA_DOCK_STATE_DESK) ||
+                        (previousDockState == Intent.EXTRA_DOCK_STATE_LE_DESK) ||
+                        (previousDockState == Intent.EXTRA_DOCK_STATE_HE_DESK)) {
                         whichSound = Settings.Global.DESK_UNDOCK_SOUND;
-                    } else if (mPreviousDockState == Intent.EXTRA_DOCK_STATE_CAR) {
+                    } else if (previousDockState == Intent.EXTRA_DOCK_STATE_CAR) {
                         whichSound = Settings.Global.CAR_UNDOCK_SOUND;
                     }
                 } else {
-                    if ((mDockState == Intent.EXTRA_DOCK_STATE_DESK) ||
-                        (mDockState == Intent.EXTRA_DOCK_STATE_LE_DESK) ||
-                        (mDockState == Intent.EXTRA_DOCK_STATE_HE_DESK)) {
+                    if ((mReportedDockState == Intent.EXTRA_DOCK_STATE_DESK) ||
+                        (mReportedDockState == Intent.EXTRA_DOCK_STATE_LE_DESK) ||
+                        (mReportedDockState == Intent.EXTRA_DOCK_STATE_HE_DESK)) {
                         whichSound = Settings.Global.DESK_DOCK_SOUND;
-                    } else if (mDockState == Intent.EXTRA_DOCK_STATE_CAR) {
+                    } else if (mReportedDockState == Intent.EXTRA_DOCK_STATE_CAR) {
                         whichSound = Settings.Global.CAR_DOCK_SOUND;
                     }
                 }
@@ -173,7 +186,8 @@
                     if (soundPath != null) {
                         final Uri soundUri = Uri.parse("file://" + soundPath);
                         if (soundUri != null) {
-                            final Ringtone sfx = RingtoneManager.getRingtone(mContext, soundUri);
+                            final Ringtone sfx = RingtoneManager.getRingtone(
+                                    getContext(), soundUri);
                             if (sfx != null) {
                                 sfx.setStreamType(AudioManager.STREAM_SYSTEM);
                                 sfx.play();
@@ -186,7 +200,7 @@
             // Send the dock event intent.
             // There are many components in the system watching for this so as to
             // adjust audio routing, screen orientation, etc.
-            mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL);
+            getContext().sendStickyBroadcastAsUser(intent, UserHandle.ALL);
 
             // Release the wake lock that was acquired when the message was posted.
             mWakeLock.release();
@@ -203,4 +217,71 @@
             }
         }
     };
+
+    private final UEventObserver mObserver = new UEventObserver() {
+        @Override
+        public void onUEvent(UEventObserver.UEvent event) {
+            if (Log.isLoggable(TAG, Log.VERBOSE)) {
+                Slog.v(TAG, "Dock UEVENT: " + event.toString());
+            }
+
+            try {
+                synchronized (mLock) {
+                    setActualDockStateLocked(Integer.parseInt(event.get("SWITCH_STATE")));
+                }
+            } catch (NumberFormatException e) {
+                Slog.e(TAG, "Could not parse switch state from event " + event);
+            }
+        }
+    };
+
+    private final class BinderService extends Binder {
+        @Override
+        protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+            if (getContext().checkCallingOrSelfPermission(android.Manifest.permission.DUMP)
+                    != PackageManager.PERMISSION_GRANTED) {
+                pw.println("Permission Denial: can't dump dock observer service from from pid="
+                        + Binder.getCallingPid()
+                        + ", uid=" + Binder.getCallingUid());
+                return;
+            }
+
+            final long ident = Binder.clearCallingIdentity();
+            try {
+                synchronized (mLock) {
+                    if (args == null || args.length == 0 || "-a".equals(args[0])) {
+                        pw.println("Current Dock Observer Service state:");
+                        if (mUpdatesStopped) {
+                            pw.println("  (UPDATES STOPPED -- use 'reset' to restart)");
+                        }
+                        pw.println("  reported state: " + mReportedDockState);
+                        pw.println("  previous state: " + mPreviousDockState);
+                        pw.println("  actual state: " + mActualDockState);
+                    } else if (args.length == 3 && "set".equals(args[0])) {
+                        String key = args[1];
+                        String value = args[2];
+                        try {
+                            if ("state".equals(key)) {
+                                mUpdatesStopped = true;
+                                setDockStateLocked(Integer.parseInt(value));
+                            } else {
+                                pw.println("Unknown set option: " + key);
+                            }
+                        } catch (NumberFormatException ex) {
+                            pw.println("Bad value: " + value);
+                        }
+                    } else if (args.length == 1 && "reset".equals(args[0])) {
+                        mUpdatesStopped = false;
+                        setDockStateLocked(mActualDockState);
+                    } else {
+                        pw.println("Dump current dock state, or:");
+                        pw.println("  set state <value>");
+                        pw.println("  reset");
+                    }
+                }
+            } finally {
+                Binder.restoreCallingIdentity(ident);
+            }
+        }
+    }
 }
diff --git a/services/core/java/com/android/server/InputMethodManagerService.java b/services/core/java/com/android/server/InputMethodManagerService.java
index fb69c86..9e0ff19 100644
--- a/services/core/java/com/android/server/InputMethodManagerService.java
+++ b/services/core/java/com/android/server/InputMethodManagerService.java
@@ -1681,6 +1681,11 @@
             mCurMethodId = null;
             unbindCurrentMethodLocked(true, false);
         }
+        // Here is not the perfect place to reset the switching controller. Ideally
+        // mSwitchingController and mSettings should be able to share the same state.
+        // TODO: Make sure that mSwitchingController and mSettings are sharing the
+        // the same enabled IMEs list.
+        mSwitchingController.resetCircularListLocked(mContext);
     }
 
     /* package */ void setInputMethodLocked(String id, int subtypeId) {
@@ -2080,8 +2085,9 @@
         }
         synchronized (mMethodMap) {
             if (subtype != null) {
-                setInputMethodWithSubtypeId(token, id, InputMethodUtils.getSubtypeIdFromHashCode(
-                        mMethodMap.get(id), subtype.hashCode()));
+                setInputMethodWithSubtypeIdLocked(token, id,
+                        InputMethodUtils.getSubtypeIdFromHashCode(mMethodMap.get(id),
+                                subtype.hashCode()));
             } else {
                 setInputMethod(token, id);
             }
@@ -2168,7 +2174,7 @@
                     Slog.d(TAG, "Switch to: " + lastImi.getId() + ", " + lastIme.second
                             + ", from: " + mCurMethodId + ", " + subtypeId);
                 }
-                setInputMethodWithSubtypeId(token, targetLastImiId, subtypeId);
+                setInputMethodWithSubtypeIdLocked(token, targetLastImiId, subtypeId);
                 return true;
             } else {
                 return false;
@@ -2187,7 +2193,8 @@
             if (nextSubtype == null) {
                 return false;
             }
-            setInputMethodWithSubtypeId(token, nextSubtype.mImi.getId(), nextSubtype.mSubtypeId);
+            setInputMethodWithSubtypeIdLocked(token, nextSubtype.mImi.getId(),
+                    nextSubtype.mSubtypeId);
             return true;
         }
     }
@@ -2312,26 +2319,30 @@
 
     private void setInputMethodWithSubtypeId(IBinder token, String id, int subtypeId) {
         synchronized (mMethodMap) {
-            if (token == null) {
-                if (mContext.checkCallingOrSelfPermission(
-                        android.Manifest.permission.WRITE_SECURE_SETTINGS)
-                        != PackageManager.PERMISSION_GRANTED) {
-                    throw new SecurityException(
-                            "Using null token requires permission "
-                            + android.Manifest.permission.WRITE_SECURE_SETTINGS);
-                }
-            } else if (mCurToken != token) {
-                Slog.w(TAG, "Ignoring setInputMethod of uid " + Binder.getCallingUid()
-                        + " token: " + token);
-                return;
-            }
+            setInputMethodWithSubtypeIdLocked(token, id, subtypeId);
+        }
+    }
 
-            final long ident = Binder.clearCallingIdentity();
-            try {
-                setInputMethodLocked(id, subtypeId);
-            } finally {
-                Binder.restoreCallingIdentity(ident);
+    private void setInputMethodWithSubtypeIdLocked(IBinder token, String id, int subtypeId) {
+        if (token == null) {
+            if (mContext.checkCallingOrSelfPermission(
+                    android.Manifest.permission.WRITE_SECURE_SETTINGS)
+                    != PackageManager.PERMISSION_GRANTED) {
+                throw new SecurityException(
+                        "Using null token requires permission "
+                        + android.Manifest.permission.WRITE_SECURE_SETTINGS);
             }
+        } else if (mCurToken != token) {
+            Slog.w(TAG, "Ignoring setInputMethod of uid " + Binder.getCallingUid()
+                    + " token: " + token);
+            return;
+        }
+
+        final long ident = Binder.clearCallingIdentity();
+        try {
+            setInputMethodLocked(id, subtypeId);
+        } finally {
+            Binder.restoreCallingIdentity(ident);
         }
     }
 
@@ -2650,7 +2661,10 @@
                 setInputMethodEnabledLocked(defaultImiId, true);
             }
         }
-
+        // Here is not the perfect place to reset the switching controller. Ideally
+        // mSwitchingController and mSettings should be able to share the same state.
+        // TODO: Make sure that mSwitchingController and mSettings are sharing the
+        // the same enabled IMEs list.
         mSwitchingController.resetCircularListLocked(mContext);
     }
 
diff --git a/services/core/java/com/android/server/LocationManagerService.java b/services/core/java/com/android/server/LocationManagerService.java
index 11fc941..98fa522 100644
--- a/services/core/java/com/android/server/LocationManagerService.java
+++ b/services/core/java/com/android/server/LocationManagerService.java
@@ -427,8 +427,9 @@
         }
 
         // bind to fused provider if supported
-        if (FlpHardwareProvider.getInstance(mContext).isSupported()) {
-          FlpHardwareProvider flpHardwareProvider = FlpHardwareProvider.getInstance(mContext);
+        if (FlpHardwareProvider.isSupported()) {
+          FlpHardwareProvider flpHardwareProvider =
+              FlpHardwareProvider.getInstance(mContext);
           FusedProxy fusedProxy = FusedProxy.createAndBind(
                   mContext,
                   mLocationHandler,
diff --git a/services/core/java/com/android/server/MountService.java b/services/core/java/com/android/server/MountService.java
index d5f045e..d31fb60 100644
--- a/services/core/java/com/android/server/MountService.java
+++ b/services/core/java/com/android/server/MountService.java
@@ -2372,6 +2372,18 @@
             }
         }
 
+        voldPath = maybeTranslatePathForVold(appPath,
+                userEnv.buildExternalStorageAppMediaDirs(callingPkg),
+                userEnv.buildExternalStorageAppMediaDirsForVold(callingPkg));
+        if (voldPath != null) {
+            try {
+                mConnector.execute("volume", "mkdirs", voldPath);
+                return 0;
+            } catch (NativeDaemonConnectorException e) {
+                return e.getCode();
+            }
+        }
+
         throw new SecurityException("Invalid mkdirs path: " + appPath);
     }
 
diff --git a/services/core/java/com/android/server/NetworkManagementService.java b/services/core/java/com/android/server/NetworkManagementService.java
index 137387e..eefe8da 100644
--- a/services/core/java/com/android/server/NetworkManagementService.java
+++ b/services/core/java/com/android/server/NetworkManagementService.java
@@ -885,7 +885,9 @@
         final LinkAddress la = route.getDestination();
         cmd.appendArg(route.getInterface());
         cmd.appendArg(la.getAddress().getHostAddress() + "/" + la.getNetworkPrefixLength());
-        cmd.appendArg(route.getGateway().getHostAddress());
+        if (route.hasGateway()) {
+            cmd.appendArg(route.getGateway().getHostAddress());
+        }
 
         try {
             mConnector.execute(cmd);
@@ -1993,14 +1995,15 @@
     private void modifyLegacyRouteForNetId(int netId, RouteInfo routeInfo, int uid, String action) {
         mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
 
-        final Command cmd = new Command("network", "legacy", uid, "route", action, netId);
+        final Command cmd = new Command("network", "route", "legacy", uid, action, netId);
 
-        // create quadlet: dest-ip-addr prefixlength gateway-ip-addr iface
+        // create triplet: interface dest-ip-addr/prefixlength gateway-ip-addr
         final LinkAddress la = routeInfo.getDestination();
-        cmd.appendArg(la.getAddress().getHostAddress());
-        cmd.appendArg(la.getNetworkPrefixLength());
-        cmd.appendArg(routeInfo.getGateway().getHostAddress());
         cmd.appendArg(routeInfo.getInterface());
+        cmd.appendArg(la.getAddress().getHostAddress() + "/" + la.getNetworkPrefixLength());
+        if (routeInfo.hasGateway()) {
+            cmd.appendArg(routeInfo.getGateway().getHostAddress());
+        }
 
         try {
             mConnector.execute(cmd);
diff --git a/services/core/java/com/android/server/WiredAccessoryManager.java b/services/core/java/com/android/server/WiredAccessoryManager.java
index cd8c13f..d8ee8a1 100644
--- a/services/core/java/com/android/server/WiredAccessoryManager.java
+++ b/services/core/java/com/android/server/WiredAccessoryManager.java
@@ -246,7 +246,8 @@
     private void setDeviceStateLocked(int headset,
             int headsetState, int prevHeadsetState, String headsetName) {
         if ((headsetState & headset) != (prevHeadsetState & headset)) {
-            int device;
+            int outDevice = 0;
+            int inDevice = 0;
             int state;
 
             if ((headsetState & headset) != 0) {
@@ -256,15 +257,16 @@
             }
 
             if (headset == BIT_HEADSET) {
-                device = AudioManager.DEVICE_OUT_WIRED_HEADSET;
+                outDevice = AudioManager.DEVICE_OUT_WIRED_HEADSET;
+                inDevice = AudioManager.DEVICE_IN_WIRED_HEADSET;
             } else if (headset == BIT_HEADSET_NO_MIC){
-                device = AudioManager.DEVICE_OUT_WIRED_HEADPHONE;
+                outDevice = AudioManager.DEVICE_OUT_WIRED_HEADPHONE;
             } else if (headset == BIT_USB_HEADSET_ANLG) {
-                device = AudioManager.DEVICE_OUT_ANLG_DOCK_HEADSET;
+                outDevice = AudioManager.DEVICE_OUT_ANLG_DOCK_HEADSET;
             } else if (headset == BIT_USB_HEADSET_DGTL) {
-                device = AudioManager.DEVICE_OUT_DGTL_DOCK_HEADSET;
+                outDevice = AudioManager.DEVICE_OUT_DGTL_DOCK_HEADSET;
             } else if (headset == BIT_HDMI_AUDIO) {
-                device = AudioManager.DEVICE_OUT_HDMI;
+                outDevice = AudioManager.DEVICE_OUT_HDMI;
             } else {
                 Slog.e(TAG, "setDeviceState() invalid headset type: "+headset);
                 return;
@@ -273,7 +275,12 @@
             if (LOG)
                 Slog.v(TAG, "device "+headsetName+((state == 1) ? " connected" : " disconnected"));
 
-            mAudioManager.setWiredDeviceConnectionState(device, state, headsetName);
+            if (outDevice != 0) {
+              mAudioManager.setWiredDeviceConnectionState(outDevice, state, headsetName);
+            }
+            if (inDevice != 0) {
+              mAudioManager.setWiredDeviceConnectionState(inDevice, state, headsetName);
+            }
         }
     }
 
diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java
index 033b967..816e022 100755
--- a/services/core/java/com/android/server/am/ActiveServices.java
+++ b/services/core/java/com/android/server/am/ActiveServices.java
@@ -247,7 +247,8 @@
             maxBg = Integer.parseInt(SystemProperties.get("ro.config.max_starting_bg", "0"));
         } catch(RuntimeException e) {
         }
-        mMaxStartingBackground = maxBg > 0 ? maxBg : ActivityManager.isLowRamDeviceStatic() ? 1 : 3;
+        mMaxStartingBackground = maxBg > 0
+                ? maxBg : ActivityManager.isLowRamDeviceStatic() ? 1 : 8;
     }
 
     ServiceRecord getServiceByName(ComponentName name, int callingUser) {
@@ -308,7 +309,7 @@
         }
         ServiceRecord r = res.record;
         NeededUriGrants neededGrants = mAm.checkGrantUriPermissionFromIntentLocked(
-                callingUid, r.packageName, service, service.getFlags(), null);
+                callingUid, r.packageName, service, service.getFlags(), null, r.userId);
         if (unscheduleServiceRestartLocked(r, callingUid, false)) {
             if (DEBUG_SERVICE) Slog.v(TAG, "START SERVICE WHILE RESTART PENDING: " + r);
         }
@@ -988,7 +989,8 @@
                         sInfo.applicationInfo.packageName, sInfo.name);
                 if (userId > 0) {
                     if (mAm.isSingleton(sInfo.processName, sInfo.applicationInfo,
-                            sInfo.name, sInfo.flags)) {
+                            sInfo.name, sInfo.flags)
+                            && mAm.isValidSingletonCall(callingUid, sInfo.applicationInfo.uid)) {
                         userId = 0;
                         smap = getServiceMap(0);
                     }
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index c2518f6..aede797 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -16,6 +16,7 @@
 
 package com.android.server.am;
 
+import static android.Manifest.permission.INTERACT_ACROSS_USERS_FULL;
 import static android.content.pm.PackageManager.PERMISSION_GRANTED;
 import static com.android.internal.util.XmlUtils.readBooleanAttribute;
 import static com.android.internal.util.XmlUtils.readIntAttribute;
@@ -2189,7 +2190,7 @@
         mBatteryStatsService.publish(mContext);
         mUsageStatsService.publish(mContext);
         mAppOpsService.publish(mContext);
-
+        Slog.d("AppOps", "AppOpsService published");
         LocalServices.addService(ActivityManagerInternal.class, new LocalService());
     }
 
@@ -2833,7 +2834,7 @@
             return app;
         }
 
-        startProcessLocked(app, hostingType, hostingNameStr);
+        startProcessLocked(app, hostingType, hostingNameStr, null /* ABI override */);
         return (app.pid != 0) ? app : null;
     }
 
@@ -2842,7 +2843,7 @@
     }
 
     private final void startProcessLocked(ProcessRecord app,
-            String hostingType, String hostingNameStr) {
+            String hostingType, String hostingNameStr, String abiOverride) {
         if (app.pid > 0 && app.pid != MY_PID) {
             synchronized (mPidsSelfLocked) {
                 mPidsSelfLocked.remove(app.pid);
@@ -2927,7 +2928,7 @@
                 debugFlags |= Zygote.DEBUG_ENABLE_ASSERT;
             }
 
-            String requiredAbi = app.info.cpuAbi;
+            String requiredAbi = (abiOverride != null) ? abiOverride : app.info.cpuAbi;
             if (requiredAbi == null) {
                 requiredAbi = Build.SUPPORTED_ABIS[0];
             }
@@ -2977,6 +2978,10 @@
                 }
             }
             buf.append("}");
+            if (requiredAbi != null) {
+                buf.append(" abi=");
+                buf.append(requiredAbi);
+            }
             Slog.i(TAG, buf.toString());
             app.setPid(startResult.pid);
             app.usingWrapper = startResult.usingWrapper;
@@ -5019,7 +5024,7 @@
 
             if (app.persistent && !app.isolated) {
                 if (!callerWillRestart) {
-                    addAppLocked(app.info, false);
+                    addAppLocked(app.info, false, null /* ABI override */);
                 } else {
                     needRestart = true;
                 }
@@ -5132,7 +5137,7 @@
             app.deathRecipient = adr;
         } catch (RemoteException e) {
             app.resetPackageList(mProcessStats);
-            startProcessLocked(app, "link fail", processName);
+            startProcessLocked(app, "link fail", processName, null /* ABI override */);
             return false;
         }
 
@@ -5225,7 +5230,7 @@
 
             app.resetPackageList(mProcessStats);
             app.unlinkDeathRecipient();
-            startProcessLocked(app, "bind fail", processName);
+            startProcessLocked(app, "bind fail", processName, null /* ABI override */);
             return false;
         }
 
@@ -5376,7 +5381,7 @@
                 for (int ip=0; ip<NP; ip++) {
                     if (DEBUG_PROCESSES) Slog.v(TAG, "Starting process on hold: "
                             + procs.get(ip));
-                    startProcessLocked(procs.get(ip), "on-hold", null);
+                    startProcessLocked(procs.get(ip), "on-hold", null, null /* ABI override */);
                 }
             }
             
@@ -6380,7 +6385,7 @@
      * Like checkGrantUriPermissionLocked, but takes an Intent.
      */
     NeededUriGrants checkGrantUriPermissionFromIntentLocked(int callingUid,
-            String targetPkg, Intent intent, int mode, NeededUriGrants needed) {
+            String targetPkg, Intent intent, int mode, NeededUriGrants needed, int targetUserId) {
         if (DEBUG_URI_PERMISSION) Slog.v(TAG,
                 "Checking URI perm to data=" + (intent != null ? intent.getData() : null)
                 + " clip=" + (intent != null ? intent.getClipData() : null)
@@ -6399,11 +6404,28 @@
         if (data == null && clip == null) {
             return null;
         }
-
+        final IPackageManager pm = AppGlobals.getPackageManager();
+        int targetUid;
+        if (needed != null) {
+            targetUid = needed.targetUid;
+        } else {
+            try {
+                targetUid = pm.getPackageUid(targetPkg, targetUserId);
+            } catch (RemoteException ex) {
+                return null;
+            }
+            if (targetUid < 0) {
+                if (DEBUG_URI_PERMISSION) {
+                    Slog.v(TAG, "Can't grant URI permission no uid for: " + targetPkg
+                            + " on user " + targetUserId);
+                }
+                return null;
+            }
+        }
         if (data != null) {
             GrantUri grantUri = GrantUri.resolve(UserHandle.getUserId(callingUid), data);
-            int targetUid = checkGrantUriPermissionLocked(callingUid, targetPkg, grantUri, mode,
-                    needed != null ? needed.targetUid : -1);
+            targetUid = checkGrantUriPermissionLocked(callingUid, targetPkg, grantUri, mode,
+                    targetUid);
             if (targetUid > 0) {
                 if (needed == null) {
                     needed = new NeededUriGrants(targetPkg, targetUid, mode);
@@ -6415,10 +6437,9 @@
             for (int i=0; i<clip.getItemCount(); i++) {
                 Uri uri = clip.getItemAt(i).getUri();
                 if (uri != null) {
-                    int targetUid = -1;
                     GrantUri grantUri = GrantUri.resolve(UserHandle.getUserId(callingUid), uri);
                     targetUid = checkGrantUriPermissionLocked(callingUid, targetPkg, grantUri, mode,
-                            needed != null ? needed.targetUid : -1);
+                            targetUid);
                     if (targetUid > 0) {
                         if (needed == null) {
                             needed = new NeededUriGrants(targetPkg, targetUid, mode);
@@ -6429,7 +6450,7 @@
                     Intent clipIntent = clip.getItemAt(i).getIntent();
                     if (clipIntent != null) {
                         NeededUriGrants newNeeded = checkGrantUriPermissionFromIntentLocked(
-                                callingUid, targetPkg, clipIntent, mode, needed);
+                                callingUid, targetPkg, clipIntent, mode, needed, targetUserId);
                         if (newNeeded != null) {
                             needed = newNeeded;
                         }
@@ -6456,9 +6477,9 @@
     }
 
     void grantUriPermissionFromIntentLocked(int callingUid,
-            String targetPkg, Intent intent, UriPermissionOwner owner) {
+            String targetPkg, Intent intent, UriPermissionOwner owner, int targetUserId) {
         NeededUriGrants needed = checkGrantUriPermissionFromIntentLocked(callingUid, targetPkg,
-                intent, intent != null ? intent.getFlags() : 0, null);
+                intent, intent != null ? intent.getFlags() : 0, null, targetUserId);
         if (needed == null) {
             return;
         }
@@ -7088,6 +7109,10 @@
      * Creates a new RecentTaskInfo from a TaskRecord.
      */
     private ActivityManager.RecentTaskInfo createRecentTaskInfoFromTaskRecord(TaskRecord tr) {
+        // Update the task description to reflect any changes in the task stack
+        tr.updateTaskDescription();
+
+        // Compose the recent task info
         ActivityManager.RecentTaskInfo rti
                 = new ActivityManager.RecentTaskInfo();
         rti.id = tr.mActivities.isEmpty() ? -1 : tr.taskId;
@@ -7733,7 +7758,7 @@
      * in {@link ContentProvider}.
      */
     private final String checkContentProviderPermissionLocked(
-            ProviderInfo cpi, ProcessRecord r, int userId) {
+            ProviderInfo cpi, ProcessRecord r, int userId, boolean checkUser) {
         final int callingPid = (r != null) ? r.pid : Binder.getCallingPid();
         final int callingUid = (r != null) ? r.uid : Binder.getCallingUid();
         final ArrayMap<GrantUri, UriPermission> perms = mGrantedUriPermissions.get(callingUid);
@@ -7750,8 +7775,10 @@
                 }
             }
         }
-        userId = handleIncomingUser(callingPid, callingUid, userId,
-                false, true, "checkContentProviderPermissionLocked", null);
+        if (checkUser) {
+            userId = handleIncomingUser(callingPid, callingUid, userId,
+                    false, true, "checkContentProviderPermissionLocked " + cpi.authority, null);
+        }
         if (checkComponentPermission(cpi.readPermission, callingPid, callingUid,
                 cpi.applicationInfo.uid, cpi.exported)
                 == PackageManager.PERMISSION_GRANTED) {
@@ -7886,13 +7913,34 @@
                 }
             }
 
+            boolean checkCrossUser = true;
+
             // First check if this content provider has been published...
             cpr = mProviderMap.getProviderByName(name, userId);
+            // If that didn't work, check if it exists for user 0 and then
+            // verify that it's a singleton provider before using it.
+            if (cpr == null && userId != UserHandle.USER_OWNER) {
+                cpr = mProviderMap.getProviderByName(name, UserHandle.USER_OWNER);
+                if (cpr != null) {
+                    cpi = cpr.info;
+                    if (isSingleton(cpi.processName, cpi.applicationInfo,
+                            cpi.name, cpi.flags)
+                            && isValidSingletonCall(r.uid, cpi.applicationInfo.uid)) {
+                        userId = UserHandle.USER_OWNER;
+                        checkCrossUser = false;
+                    } else {
+                        cpr = null;
+                        cpi = null;
+                    }
+                }
+            }
+
             boolean providerRunning = cpr != null;
             if (providerRunning) {
                 cpi = cpr.info;
                 String msg;
-                if ((msg=checkContentProviderPermissionLocked(cpi, r, userId)) != null) {
+                if ((msg = checkContentProviderPermissionLocked(cpi, r, userId, checkCrossUser))
+                        != null) {
                     throw new SecurityException(msg);
                 }
 
@@ -7972,15 +8020,21 @@
                 if (cpi == null) {
                     return null;
                 }
+                // If the provider is a singleton AND
+                // (it's a call within the same user || the provider is a
+                // privileged app)
+                // Then allow connecting to the singleton provider
                 singleton = isSingleton(cpi.processName, cpi.applicationInfo,
-                        cpi.name, cpi.flags); 
+                        cpi.name, cpi.flags)
+                        && isValidSingletonCall(r.uid, cpi.applicationInfo.uid);
                 if (singleton) {
-                    userId = 0;
+                    userId = UserHandle.USER_OWNER;
                 }
                 cpi.applicationInfo = getAppInfoForUser(cpi.applicationInfo, userId);
 
                 String msg;
-                if ((msg=checkContentProviderPermissionLocked(cpi, r, userId)) != null) {
+                if ((msg = checkContentProviderPermissionLocked(cpi, r, userId, !singleton))
+                        != null) {
                     throw new SecurityException(msg);
                 }
 
@@ -8516,7 +8570,8 @@
         return new ProcessRecord(stats, info, proc, uid);
     }
 
-    final ProcessRecord addAppLocked(ApplicationInfo info, boolean isolated) {
+    final ProcessRecord addAppLocked(ApplicationInfo info, boolean isolated,
+            String abiOverride) {
         ProcessRecord app;
         if (!isolated) {
             app = getProcessRecordLocked(info.processName, info.uid, true);
@@ -8551,7 +8606,8 @@
         }
         if (app.thread == null && mPersistentStartingProcesses.indexOf(app) < 0) {
             mPersistentStartingProcesses.add(app);
-            startProcessLocked(app, "added application", app.processName);
+            startProcessLocked(app, "added application", app.processName,
+                    abiOverride);
         }
 
         return app;
@@ -9577,11 +9633,13 @@
                 return;
             }
 
-            mRecentTasks = mTaskPersister.restoreTasksLocked();
-            if (!mRecentTasks.isEmpty()) {
-                mStackSupervisor.createStackForRestoredTaskHistory(mRecentTasks);
+            if (mRecentTasks == null) {
+                mRecentTasks = mTaskPersister.restoreTasksLocked();
+                if (!mRecentTasks.isEmpty()) {
+                    mStackSupervisor.createStackForRestoredTaskHistory(mRecentTasks);
+                }
+                mTaskPersister.startPersisting();
             }
-            mTaskPersister.startPersisting();
 
             // Check to see if there are any update receivers to run.
             if (!mDidUpdate) {
@@ -9766,7 +9824,7 @@
                                 = (ApplicationInfo)apps.get(i);
                             if (info != null &&
                                     !info.packageName.equals("android")) {
-                                addAppLocked(info, false);
+                                addAppLocked(info, false, null /* ABI override */);
                             }
                         }
                     }
@@ -10557,8 +10615,7 @@
         // assume our apps are happy - lazy create the list
         List<ActivityManager.ProcessErrorStateInfo> errList = null;
 
-        final boolean allUsers = ActivityManager.checkUidPermission(
-                android.Manifest.permission.INTERACT_ACROSS_USERS_FULL,
+        final boolean allUsers = ActivityManager.checkUidPermission(INTERACT_ACROSS_USERS_FULL,
                 Binder.getCallingUid()) == PackageManager.PERMISSION_GRANTED;
         int userId = UserHandle.getUserId(Binder.getCallingUid());
 
@@ -10647,8 +10704,7 @@
         enforceNotIsolatedCaller("getRunningAppProcesses");
         // Lazy instantiation of list
         List<ActivityManager.RunningAppProcessInfo> runList = null;
-        final boolean allUsers = ActivityManager.checkUidPermission(
-                android.Manifest.permission.INTERACT_ACROSS_USERS_FULL,
+        final boolean allUsers = ActivityManager.checkUidPermission(INTERACT_ACROSS_USERS_FULL,
                 Binder.getCallingUid()) == PackageManager.PERMISSION_GRANTED;
         int userId = UserHandle.getUserId(Binder.getCallingUid());
         synchronized (this) {
@@ -12948,7 +13004,7 @@
             // We have components that still need to be running in the
             // process, so re-launch it.
             mProcessNames.put(app.processName, app.uid, app);
-            startProcessLocked(app, "restart", app.processName);
+            startProcessLocked(app, "restart", app.processName, null /* ABI override */);
         } else if (app.pid > 0 && app.pid != MY_PID) {
             // Goodbye!
             boolean removed;
@@ -13088,8 +13144,7 @@
                 if ((requireFull || checkComponentPermission(
                         android.Manifest.permission.INTERACT_ACROSS_USERS,
                         callingPid, callingUid, -1, true) != PackageManager.PERMISSION_GRANTED)
-                        && checkComponentPermission(
-                                android.Manifest.permission.INTERACT_ACROSS_USERS_FULL,
+                        && checkComponentPermission(INTERACT_ACROSS_USERS_FULL,
                                 callingPid, callingUid, -1, true)
                                 != PackageManager.PERMISSION_GRANTED) {
                     if (userId == UserHandle.USER_CURRENT_OR_SELF) {
@@ -13109,7 +13164,7 @@
                         builder.append(" but is calling from user ");
                         builder.append(UserHandle.getUserId(callingUid));
                         builder.append("; this requires ");
-                        builder.append(android.Manifest.permission.INTERACT_ACROSS_USERS_FULL);
+                        builder.append(INTERACT_ACROSS_USERS_FULL);
                         if (!requireFull) {
                             builder.append(" or ");
                             builder.append(android.Manifest.permission.INTERACT_ACROSS_USERS);
@@ -13139,8 +13194,9 @@
     boolean isSingleton(String componentProcessName, ApplicationInfo aInfo,
             String className, int flags) {
         boolean result = false;
+        // For apps that don't have pre-defined UIDs, check for permission
         if (UserHandle.getAppId(aInfo.uid) >= Process.FIRST_APPLICATION_UID) {
-            if ((flags&ServiceInfo.FLAG_SINGLE_USER) != 0) {
+            if ((flags & ServiceInfo.FLAG_SINGLE_USER) != 0) {
                 if (ActivityManager.checkUidPermission(
                         android.Manifest.permission.INTERACT_ACROSS_USERS,
                         aInfo.uid) != PackageManager.PERMISSION_GRANTED) {
@@ -13151,12 +13207,14 @@
                     Slog.w(TAG, msg);
                     throw new SecurityException(msg);
                 }
+                // Permission passed
                 result = true;
             }
-        } else if (componentProcessName == aInfo.packageName) {
-            result = (aInfo.flags & ApplicationInfo.FLAG_PERSISTENT) != 0;
         } else if ("system".equals(componentProcessName)) {
             result = true;
+        } else {
+            // App with pre-defined UID, check if it's a persistent app
+            result = (aInfo.flags & ApplicationInfo.FLAG_PERSISTENT) != 0;
         }
         if (DEBUG_MU) {
             Slog.v(TAG, "isSingleton(" + componentProcessName + ", " + aInfo
@@ -13165,6 +13223,21 @@
         return result;
     }
 
+    /**
+     * Checks to see if the caller is in the same app as the singleton
+     * component, or the component is in a special app. It allows special apps
+     * to export singleton components but prevents exporting singleton
+     * components for regular apps.
+     */
+    boolean isValidSingletonCall(int callingUid, int componentUid) {
+        int componentAppId = UserHandle.getAppId(componentUid);
+        return UserHandle.isSameApp(callingUid, componentUid)
+                || componentAppId == Process.SYSTEM_UID
+                || componentAppId == Process.PHONE_UID
+                || ActivityManager.checkUidPermission(INTERACT_ACROSS_USERS_FULL, componentUid)
+                        == PackageManager.PERMISSION_GRANTED;
+    }
+
     public int bindService(IApplicationThread caller, IBinder token,
             Intent service, String resolvedType,
             IServiceConnection connection, int flags, int userId) {
@@ -13711,8 +13784,8 @@
          */
         int callingAppId = UserHandle.getAppId(callingUid);
         if (callingAppId == Process.SYSTEM_UID || callingAppId == Process.PHONE_UID
-            || callingAppId == Process.SHELL_UID || callingAppId == Process.BLUETOOTH_UID ||
-            callingUid == 0) {
+                || callingAppId == Process.SHELL_UID || callingAppId == Process.BLUETOOTH_UID
+                || callingUid == 0) {
             // Always okay.
         } else if (callerApp == null || !callerApp.persistent) {
             try {
@@ -14245,7 +14318,7 @@
     public boolean startInstrumentation(ComponentName className,
             String profileFile, int flags, Bundle arguments,
             IInstrumentationWatcher watcher, IUiAutomationConnection uiAutomationConnection,
-            int userId) {
+            int userId, String abiOverride) {
         enforceNotIsolatedCaller("startInstrumentation");
         userId = handleIncomingUser(Binder.getCallingPid(), Binder.getCallingUid(),
                 userId, false, true, "startInstrumentation", null);
@@ -14294,7 +14367,7 @@
             // Instrumentation can kill and relaunch even persistent processes
             forceStopPackageLocked(ii.targetPackage, -1, true, false, true, true, false, userId,
                     "start instr");
-            ProcessRecord app = addAppLocked(ai, false);
+            ProcessRecord app = addAppLocked(ai, false, abiOverride);
             app.instrumentationClass = className;
             app.instrumentationInfo = ai;
             app.instrumentationProfileFile = profileFile;
@@ -16258,7 +16331,7 @@
                     
                     if (app.persistent) {
                         if (app.persistent) {
-                            addAppLocked(app.info, false);
+                            addAppLocked(app.info, false, null /* ABI override */);
                         }
                     }
                 }
@@ -16522,12 +16595,12 @@
     }
 
     private boolean startUser(final int userId, boolean foreground) {
-        if (checkCallingPermission(android.Manifest.permission.INTERACT_ACROSS_USERS_FULL)
+        if (checkCallingPermission(INTERACT_ACROSS_USERS_FULL)
                 != PackageManager.PERMISSION_GRANTED) {
             String msg = "Permission Denial: switchUser() from pid="
                     + Binder.getCallingPid()
                     + ", uid=" + Binder.getCallingUid()
-                    + " requires " + android.Manifest.permission.INTERACT_ACROSS_USERS_FULL;
+                    + " requires " + INTERACT_ACROSS_USERS_FULL;
             Slog.w(TAG, msg);
             throw new SecurityException(msg);
         }
@@ -16893,12 +16966,12 @@
 
     @Override
     public int stopUser(final int userId, final IStopUserCallback callback) {
-        if (checkCallingPermission(android.Manifest.permission.INTERACT_ACROSS_USERS_FULL)
+        if (checkCallingPermission(INTERACT_ACROSS_USERS_FULL)
                 != PackageManager.PERMISSION_GRANTED) {
             String msg = "Permission Denial: switchUser() from pid="
                     + Binder.getCallingPid()
                     + ", uid=" + Binder.getCallingUid()
-                    + " requires " + android.Manifest.permission.INTERACT_ACROSS_USERS_FULL;
+                    + " requires " + INTERACT_ACROSS_USERS_FULL;
             Slog.w(TAG, msg);
             throw new SecurityException(msg);
         }
@@ -17036,7 +17109,7 @@
     public UserInfo getCurrentUser() {
         if ((checkCallingPermission(android.Manifest.permission.INTERACT_ACROSS_USERS)
                 != PackageManager.PERMISSION_GRANTED) && (
-                checkCallingPermission(android.Manifest.permission.INTERACT_ACROSS_USERS_FULL)
+                checkCallingPermission(INTERACT_ACROSS_USERS_FULL)
                 != PackageManager.PERMISSION_GRANTED)) {
             String msg = "Permission Denial: getCurrentUser() from pid="
                     + Binder.getCallingPid()
@@ -17122,12 +17195,12 @@
 
     @Override
     public void registerUserSwitchObserver(IUserSwitchObserver observer) {
-        if (checkCallingPermission(android.Manifest.permission.INTERACT_ACROSS_USERS_FULL)
+        if (checkCallingPermission(INTERACT_ACROSS_USERS_FULL)
                 != PackageManager.PERMISSION_GRANTED) {
             String msg = "Permission Denial: registerUserSwitchObserver() from pid="
                     + Binder.getCallingPid()
                     + ", uid=" + Binder.getCallingUid()
-                    + " requires " + android.Manifest.permission.INTERACT_ACROSS_USERS_FULL;
+                    + " requires " + INTERACT_ACROSS_USERS_FULL;
             Slog.w(TAG, msg);
             throw new SecurityException(msg);
         }
diff --git a/services/core/java/com/android/server/am/ActivityRecord.java b/services/core/java/com/android/server/am/ActivityRecord.java
index b948c41..32722bc 100755
--- a/services/core/java/com/android/server/am/ActivityRecord.java
+++ b/services/core/java/com/android/server/am/ActivityRecord.java
@@ -635,7 +635,7 @@
     final void deliverNewIntentLocked(int callingUid, Intent intent) {
         // The activity now gets access to the data associated with this Intent.
         service.grantUriPermissionFromIntentLocked(callingUid, packageName,
-                intent, getUriPermissionsLocked());
+                intent, getUriPermissionsLocked(), userId);
         // We want to immediately deliver the intent to the activity if
         // it is currently the top resumed activity...  however, if the
         // device is sleeping, then all activities are stopped, so in that
@@ -1154,13 +1154,15 @@
         }
 
         if (intent == null) {
-            Slog.e(TAG, "restoreActivity error intent=" + intent);
-            return null;
+            throw new XmlPullParserException("restoreActivity error intent=" + intent);
         }
 
         final ActivityManagerService service = stackSupervisor.mService;
         final ActivityInfo aInfo = stackSupervisor.resolveActivity(intent, resolvedType, 0, null,
                 null, userId);
+        if (aInfo == null) {
+            throw new XmlPullParserException("restoreActivity resolver error.");
+        }
         final ActivityRecord r = new ActivityRecord(service, /*caller*/null, launchedFromUid,
                 launchedFromPackage, intent, resolvedType, aInfo, service.getConfiguration(),
                 null, null, 0, componentSpecified, stackSupervisor, null, null);
diff --git a/services/core/java/com/android/server/am/ActivityStack.java b/services/core/java/com/android/server/am/ActivityStack.java
index a0440cb..ba12374 100755
--- a/services/core/java/com/android/server/am/ActivityStack.java
+++ b/services/core/java/com/android/server/am/ActivityStack.java
@@ -30,10 +30,6 @@
 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_SAVED_STATE;
@@ -1067,6 +1063,40 @@
         }
     }
 
+    /**
+     * 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);
@@ -1096,8 +1126,7 @@
         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 TaskRecord task = tasks.get(taskNdx);
-                final ArrayList<ActivityRecord> activities = task.mActivities;
+                final ArrayList<ActivityRecord> activities = tasks.get(taskNdx).mActivities;
                 for (int activityNdx = 0; activityNdx < activities.size(); activityNdx++) {
                     final ActivityRecord r = activities.get(activityNdx);
 
@@ -1108,7 +1137,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 && task.isOverHomeStack()))) {
+                            (!isHomeStack() && r.frontOfTask && tasks.get(taskNdx).mOnTopOfHome))) {
                         return false;
                     }
                 }
@@ -1236,7 +1265,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.isOverHomeStack()) {
+                    } else if (!isHomeStack() && r.frontOfTask && task.mOnTopOfHome) {
                         if (DEBUG_VISBILITY) Slog.v(TAG, "Showing home: at " + r);
                         behindFullscreen = true;
                     }
@@ -1390,7 +1419,6 @@
         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...
@@ -1398,10 +1426,7 @@
             if (DEBUG_STATES) Slog.d(TAG, "resumeTopActivityLocked: No more activities go home");
             if (DEBUG_STACK) mStackSupervisor.validateTopActivitiesLocked();
             // Only resume home if on home display
-            final int returnTaskType = prevTask == null || !prevTask.isOverHomeStack() ?
-                    HOME_ACTIVITY_TYPE : prevTask.getTaskToReturnTo();
-            return isOnHomeDisplay() &&
-                    mStackSupervisor.resumeHomeStackTask(returnTaskType, prev);
+            return isOnHomeDisplay() && mStackSupervisor.resumeHomeActivity(prev);
         }
 
         next.delayedResume = false;
@@ -1420,24 +1445,22 @@
         }
 
         final TaskRecord nextTask = next.task;
+        final TaskRecord prevTask = prev != null ? prev.task : null;
         if (prevTask != null && prevTask.stack == this &&
-                prevTask.isOverHomeStack() && prev.finishing && prev.frontOfTask) {
+                prevTask.mOnTopOfHome && 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 stack.
+                // This task is going away but it was supposed to return to the home task.
                 // Now the task above it has to return to the home task instead.
                 final int taskNdx = mTaskHistory.indexOf(prevTask) + 1;
-                mTaskHistory.get(taskNdx).setTaskToReturnTo(HOME_ACTIVITY_TYPE);
+                mTaskHistory.get(taskNdx).mOnTopOfHome = true;
             } else {
                 if (DEBUG_STATES && isOnHomeDisplay()) Slog.d(TAG,
                         "resumeTopActivityLocked: Launching home next");
                 // Only resume home if on home display
-                final int returnTaskType = prevTask == null || !prevTask.isOverHomeStack() ?
-                        HOME_ACTIVITY_TYPE : prevTask.getTaskToReturnTo();
-                return isOnHomeDisplay() &&
-                        mStackSupervisor.resumeHomeStackTask(returnTaskType, prev);
+                return isOnHomeDisplay() && mStackSupervisor.resumeHomeActivity(prev);
             }
         }
 
@@ -1808,11 +1831,10 @@
             ActivityStack lastStack = mStackSupervisor.getLastStack();
             final boolean fromHome = lastStack.isHomeStack();
             if (!isHomeStack() && (fromHome || topTask() != task)) {
-                task.setTaskToReturnTo(fromHome ?
-                        lastStack.topTask().taskType : APPLICATION_ACTIVITY_TYPE);
+                task.mOnTopOfHome = fromHome;
             }
         } else {
-            task.setTaskToReturnTo(APPLICATION_ACTIVITY_TYPE);
+            task.mOnTopOfHome = false;
         }
 
         mTaskHistory.remove(task);
@@ -1860,7 +1882,7 @@
                         mWindowManager.addAppToken(task.mActivities.indexOf(r), r.appToken,
                                 r.task.taskId, mStackId, r.info.screenOrientation, r.fullscreen,
                                 (r.info.flags & ActivityInfo.FLAG_SHOW_ON_LOCK_SCREEN) != 0,
-                                r.userId, r.info.configChanges);
+                                r.userId, r.info.configChanges, task.voiceSession != null);
                         if (VALIDATE_TOKENS) {
                             validateAppTokensLocked();
                         }
@@ -1921,7 +1943,7 @@
             mWindowManager.addAppToken(task.mActivities.indexOf(r),
                     r.appToken, r.task.taskId, mStackId, r.info.screenOrientation, r.fullscreen,
                     (r.info.flags & ActivityInfo.FLAG_SHOW_ON_LOCK_SCREEN) != 0, r.userId,
-                    r.info.configChanges);
+                    r.info.configChanges, task.voiceSession != null);
             boolean doShow = true;
             if (newTask) {
                 // Even though this activity is starting fresh, we still need
@@ -1966,7 +1988,7 @@
             mWindowManager.addAppToken(task.mActivities.indexOf(r), r.appToken,
                     r.task.taskId, mStackId, r.info.screenOrientation, r.fullscreen,
                     (r.info.flags & ActivityInfo.FLAG_SHOW_ON_LOCK_SCREEN) != 0, r.userId,
-                    r.info.configChanges);
+                    r.info.configChanges, task.voiceSession != null);
             ActivityOptions.abort(options);
             options = null;
         }
@@ -2331,7 +2353,7 @@
 
         if (callingUid > 0) {
             mService.grantUriPermissionFromIntentLocked(callingUid, r.packageName,
-                    data, r.getUriPermissionsLocked());
+                    data, r.getUriPermissionsLocked(), r.userId);
         }
 
         if (DEBUG_RESULTS) Slog.v(TAG, "Send activity result to " + r
@@ -2357,8 +2379,8 @@
             ActivityRecord next = topRunningActivityLocked(null);
             if (next != r) {
                 final TaskRecord task = r.task;
-                if (r.frontOfTask && task == topTask() && task.isOverHomeStack()) {
-                    mStackSupervisor.moveHomeStackTaskToTop(task.getTaskToReturnTo());
+                if (r.frontOfTask && task == topTask() && task.mOnTopOfHome) {
+                    mStackSupervisor.moveHomeToTop();
                 }
             }
             ActivityRecord top = mStackSupervisor.topRunningActivityLocked();
@@ -2514,10 +2536,15 @@
             if (DEBUG_RESULTS) Slog.v(TAG, "Adding result to " + resultTo
                     + " who=" + r.resultWho + " req=" + r.requestCode
                     + " res=" + resultCode + " data=" + resultData);
+            if (resultTo.userId != r.userId) {
+                if (resultData != null) {
+                    resultData.prepareToLeaveUser(r.userId);
+                }
+            }
             if (r.info.applicationInfo.uid > 0) {
                 mService.grantUriPermissionFromIntentLocked(r.info.applicationInfo.uid,
                         resultTo.packageName, resultData,
-                        resultTo.getUriPermissionsLocked());
+                        resultTo.getUriPermissionsLocked(), resultTo.userId);
             }
             resultTo.addResultLocked(r, r.resultWho, r.requestCode, resultCode,
                                      resultData);
@@ -2842,9 +2869,8 @@
         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.isOverHomeStack()) {
-                mStackSupervisor.moveHomeStackTaskToTop(task.getTaskToReturnTo());
+            if (mStackSupervisor.isFrontStack(this) && task == topTask() && task.mOnTopOfHome) {
+                mStackSupervisor.moveHomeToTop();
             }
             removeTask(task);
         }
@@ -3159,13 +3185,12 @@
         }
     }
 
-    void moveHomeStackTaskToTop(int homeStackTaskType) {
+    void moveHomeTaskToTop() {
         final int top = mTaskHistory.size() - 1;
         for (int taskNdx = top; taskNdx >= 0; --taskNdx) {
             final TaskRecord task = mTaskHistory.get(taskNdx);
-            if (task.taskType == homeStackTaskType) {
-                if (DEBUG_TASKS || DEBUG_STACK)
-                    Slog.d(TAG, "moveHomeStackTaskToTop: moving " + task);
+            if (task.isHomeTask()) {
+                if (DEBUG_TASKS || DEBUG_STACK) Slog.d(TAG, "moveHomeTaskToTop: moving " + task);
                 mTaskHistory.remove(taskNdx);
                 mTaskHistory.add(top, task);
                 updateTaskMovement(task, true);
@@ -3277,12 +3302,12 @@
         int numTasks = mTaskHistory.size();
         for (int taskNdx = numTasks - 1; taskNdx >= 1; --taskNdx) {
             final TaskRecord task = mTaskHistory.get(taskNdx);
-            if (task.isOverHomeStack()) {
+            if (task.mOnTopOfHome) {
                 break;
             }
             if (taskNdx == 1) {
                 // Set the last task before tr to go to home.
-                task.setTaskToReturnTo(HOME_ACTIVITY_TYPE);
+                task.mOnTopOfHome = true;
             }
         }
 
@@ -3303,10 +3328,9 @@
         }
 
         final TaskRecord task = mResumedActivity != null ? mResumedActivity.task : null;
-        if (task == tr && tr.isOverHomeStack() || numTasks <= 1 && isOnHomeDisplay()) {
-            final int taskToReturnTo = tr.getTaskToReturnTo();
-            tr.setTaskToReturnTo(APPLICATION_ACTIVITY_TYPE);
-            return mStackSupervisor.resumeHomeStackTask(taskToReturnTo, null);
+        if (task == tr && tr.mOnTopOfHome || numTasks <= 1 && isOnHomeDisplay()) {
+            tr.mOnTopOfHome = false;
+            return mStackSupervisor.resumeHomeActivity(null);
         }
 
         mStackSupervisor.resumeTopActivitiesLocked();
@@ -3747,11 +3771,8 @@
 
         final int taskNdx = mTaskHistory.indexOf(task);
         final int topTaskNdx = mTaskHistory.size() - 1;
-        if (task.isOverHomeStack() && taskNdx < topTaskNdx) {
-            final TaskRecord nextTask = mTaskHistory.get(taskNdx + 1);
-            if (!nextTask.isOverHomeStack()) {
-                nextTask.setTaskToReturnTo(HOME_ACTIVITY_TYPE);
-            }
+        if (task.mOnTopOfHome && taskNdx < topTaskNdx) {
+            mTaskHistory.get(taskNdx + 1).mOnTopOfHome = true;
         }
         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 c7ae6bc..dc4ad48 100644
--- a/services/core/java/com/android/server/am/ActivityStackSupervisor.java
+++ b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
@@ -31,9 +31,6 @@
 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;
@@ -217,6 +214,10 @@
     /** Set when we have taken too long waiting to go to sleep. */
     boolean mSleepTimeout = false;
 
+    /** Indicates if we are running on a Leanback-only (TV) device. Only initialized after
+     * setWindowManager is called. **/
+    private boolean mLeanbackOnlyDevice;
+
     /**
      * We don't want to allow the device to go to sleep while in the process
      * of launching an activity.  This is primarily to allow alarm intent
@@ -296,6 +297,9 @@
             mHomeStack = mFocusedStack = mLastFocusedStack = getStack(HOME_STACK_ID);
 
             mInputManagerInternal = LocalServices.getService(InputManagerInternal.class);
+
+            // Initialize this here, now that we can get a valid reference to PackageManager.
+            mLeanbackOnlyDevice = isLeanbackOnlyDevice();
         }
     }
 
@@ -347,27 +351,18 @@
         }
     }
 
-    void moveHomeStackTaskToTop(int homeStackTaskType) {
-        if (homeStackTaskType == RECENTS_ACTIVITY_TYPE) {
-            mWindowManager.showRecentApps();
-            return;
-        }
+    void moveHomeToTop() {
         moveHomeStack(true);
-        mHomeStack.moveHomeStackTaskToTop(homeStackTaskType);
+        mHomeStack.moveHomeTaskToTop();
     }
 
-    boolean resumeHomeStackTask(int homeStackTaskType, ActivityRecord prev) {
-        if (homeStackTaskType == RECENTS_ACTIVITY_TYPE) {
-            mWindowManager.showRecentApps();
-            return false;
-        }
-        moveHomeStackTaskToTop(homeStackTaskType);
+    boolean resumeHomeActivity(ActivityRecord prev) {
+        moveHomeToTop();
         if (prev != null) {
-            prev.task.setTaskToReturnTo(APPLICATION_ACTIVITY_TYPE);
+            prev.task.mOnTopOfHome = false;
         }
-
         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);
         }
@@ -721,7 +716,7 @@
     }
 
     void startHomeActivity(Intent intent, ActivityInfo aInfo) {
-        moveHomeStackTaskToTop(HOME_ACTIVITY_TYPE);
+        moveHomeToTop();
         startActivityLocked(null, intent, null, aInfo, null, null, null, null, 0, 0, 0, null, 0,
                 null, false, null, null);
     }
@@ -1238,8 +1233,7 @@
             requestCode = sourceRecord.requestCode;
             sourceRecord.resultTo = null;
             if (resultRecord != null) {
-                resultRecord.removeResultsLocked(
-                    sourceRecord, resultWho, requestCode);
+                resultRecord.removeResultsLocked(sourceRecord, resultWho, requestCode);
             }
             if (sourceRecord.launchedFromUid == callingUid) {
                 // The new activity is being launched from the same uid as the previous
@@ -1411,9 +1405,12 @@
         return err;
     }
 
-    ActivityStack adjustStackFocus(ActivityRecord r) {
+    ActivityStack adjustStackFocus(ActivityRecord r, boolean newTask) {
         final TaskRecord task = r.task;
-        if (r.isApplicationActivity() || (task != null && task.isApplicationTask())) {
+
+        // On leanback only devices we should keep all activities in the same stack.
+        if (!mLeanbackOnlyDevice &&
+                (r.isApplicationActivity() || (task != null && task.isApplicationTask()))) {
             if (task != null) {
                 final ActivityStack taskStack = task.stack;
                 if (taskStack.isOnHomeDisplay()) {
@@ -1436,7 +1433,8 @@
                 return container.mStack;
             }
 
-            if (mFocusedStack != mHomeStack) {
+            if (mFocusedStack != mHomeStack && (!newTask ||
+                    mFocusedStack.mActivityContainer.isEligibleForNewTasks())) {
                 if (DEBUG_FOCUS || DEBUG_STACK) Slog.d(TAG,
                         "adjustStackFocus: Have a focused stack=" + mFocusedStack);
                 return mFocusedStack;
@@ -1489,7 +1487,7 @@
 
         // We'll invoke onUserLeaving before onPause only if the launching
         // activity did not explicitly state that this is an automated launch.
-        mUserLeaving = (launchFlags&Intent.FLAG_ACTIVITY_NO_USER_ACTION) == 0;
+        mUserLeaving = (launchFlags & Intent.FLAG_ACTIVITY_NO_USER_ACTION) == 0;
         if (DEBUG_USER_LEAVING) Slog.v(TAG, "startActivity() => mUserLeaving=" + mUserLeaving);
 
         // If the caller has asked not to resume at this point, we make note
@@ -1499,7 +1497,8 @@
             r.delayedResume = true;
         }
 
-        ActivityRecord notTop = (launchFlags&Intent.FLAG_ACTIVITY_PREVIOUS_IS_TOP) != 0 ? r : null;
+        ActivityRecord notTop =
+                (launchFlags & Intent.FLAG_ACTIVITY_PREVIOUS_IS_TOP) != 0 ? r : null;
 
         // If the onlyIfNeeded flag is set, then we can do this if the activity
         // being launched is the same as the one making the call...  or, as
@@ -1522,9 +1521,11 @@
             case ActivityInfo.DOCUMENT_LAUNCH_ALWAYS:
                 intent.addFlags(
                         Intent.FLAG_ACTIVITY_NEW_DOCUMENT | Intent.FLAG_ACTIVITY_MULTIPLE_TASK);
+                launchFlags = intent.getFlags();
                 break;
             case ActivityInfo.DOCUMENT_LAUNCH_INTO_EXISTING:
                 intent.addFlags(Intent.FLAG_ACTIVITY_NEW_DOCUMENT);
+                launchFlags = intent.getFlags();
                 break;
         }
         final boolean newDocument = intent.isDocument();
@@ -1653,7 +1654,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.setTaskToReturnTo(HOME_ACTIVITY_TYPE);
+                                intentActivity.task.mOnTopOfHome = true;
                             }
                             options = null;
                         }
@@ -1830,31 +1831,26 @@
                 Slog.e(TAG, "Attempted Lock Task Mode violation r=" + r);
                 return ActivityManager.START_RETURN_LOCK_TASK_MODE_VIOLATION;
             }
-            targetStack = adjustStackFocus(r);
+            newTask = true;
+            targetStack = adjustStackFocus(r, newTask);
             targetStack.moveToFront();
             if (reuseTask == null) {
                 r.setTask(targetStack.createTaskRecord(getNextTaskId(),
                         newTaskInfo != null ? newTaskInfo : r.info,
                         newTaskIntent != null ? newTaskIntent : intent,
                         voiceSession, voiceInteractor, true), null, true);
-                if (sourceRecord == null) {
-                    // Launched from a service or notification or task that is finishing.
-                    r.task.setTaskToReturnTo(isFrontStack(mHomeStack) ?
-                            mHomeStack.topTask().taskType : RECENTS_ACTIVITY_TYPE);
-                }
                 if (DEBUG_TASKS) Slog.v(TAG, "Starting new activity " + r + " in new task " +
                         r.task);
             } else {
                 r.setTask(reuseTask, reuseTask, true);
             }
-            newTask = true;
             if (!movedHome) {
                 if ((launchFlags &
                         (Intent.FLAG_ACTIVITY_NEW_TASK|Intent.FLAG_ACTIVITY_TASK_ON_HOME))
                         == (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.setTaskToReturnTo(HOME_ACTIVITY_TYPE);
+                    r.task.mOnTopOfHome = r.task.stack.isOnHomeDisplay();
                 }
             }
         } else if (sourceRecord != null) {
@@ -1915,7 +1911,7 @@
             // This not being started from an existing activity, and not part
             // of a new task...  just put it in the top task, though these days
             // this case should never happen.
-            targetStack = adjustStackFocus(r);
+            targetStack = adjustStackFocus(r, newTask);
             targetStack.moveToFront();
             ActivityRecord prev = targetStack.topActivity();
             r.setTask(prev != null ? prev.task
@@ -1927,7 +1923,7 @@
         }
 
         mService.grantUriPermissionFromIntentLocked(callingUid, r.packageName,
-                intent, r.getUriPermissionsLocked());
+                intent, r.getUriPermissionsLocked(), r.userId);
 
         if (newTask) {
             EventLog.writeEvent(EventLogTags.AM_CREATE_TASK, r.userId, r.task.taskId);
@@ -2205,7 +2201,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.setTaskToReturnTo(HOME_ACTIVITY_TYPE);
+            task.mOnTopOfHome = true;
         }
         task.stack.moveTaskToFrontLocked(task, null, options);
         if (DEBUG_STACK) Slog.d(TAG, "findTaskToMoveToFront: moved to front of stack="
@@ -2312,11 +2308,11 @@
                 mWindowManager.addAppToken(0, r.appToken, taskId, stackId,
                         r.info.screenOrientation, r.fullscreen,
                         (r.info.flags & ActivityInfo.FLAG_SHOW_ON_LOCK_SCREEN) != 0,
-                        r.userId, r.info.configChanges);
+                        r.userId, r.info.configChanges, task.voiceSession != null);
             }
             mWindowManager.addTask(taskId, stackId, false);
         }
-        resumeHomeStackTask(HOME_ACTIVITY_TYPE, null);
+        resumeHomeActivity(null);
     }
 
     void moveTaskToStack(int taskId, int stackId, boolean toTop) {
@@ -2342,7 +2338,12 @@
             for (int stackNdx = stacks.size() - 1; stackNdx >= 0; --stackNdx) {
                 final ActivityStack stack = stacks.get(stackNdx);
                 if (!r.isApplicationActivity() && !stack.isHomeStack()) {
-                    if (DEBUG_TASKS) Slog.d(TAG, "Skipping stack: " + stack);
+                    if (DEBUG_TASKS) Slog.d(TAG, "Skipping stack: (home activity) " + stack);
+                    continue;
+                }
+                if (!stack.mActivityContainer.isEligibleForNewTasks()) {
+                    if (DEBUG_TASKS) Slog.d(TAG, "Skipping stack: (new task not allowed) " +
+                            stack);
                     continue;
                 }
                 final ActivityRecord ar = stack.findTaskLocked(r);
@@ -2573,7 +2574,7 @@
             }
         } else {
             // Stack was moved to another display while user was swapped out.
-            resumeHomeStackTask(HOME_ACTIVITY_TYPE, null);
+            resumeHomeActivity(null);
         }
         return homeInFront;
     }
@@ -3304,6 +3305,11 @@
         void setDrawn() {
         }
 
+        // You can always start a new task on a regular ActivityStack.
+        boolean isEligibleForNewTasks() {
+            return true;
+        }
+
         @Override
         public String toString() {
             return mIdString + (mActivityDisplay == null ? "N" : "A");
@@ -3384,6 +3390,12 @@
             }
         }
 
+        // Never start a new task on an ActivityView if it isn't explicitly specified.
+        @Override
+        boolean isEligibleForNewTasks() {
+            return false;
+        }
+
         private void setSurfaceIfReady() {
             if (DEBUG_STACK) Slog.v(TAG, "setSurfaceIfReady: mDrawn=" + mDrawn +
                     " mContainerState=" + mContainerState + " mSurface=" + mSurface);
@@ -3477,4 +3489,16 @@
             return "VirtualActivityDisplay={" + mDisplayId + "}";
         }
     }
+
+    private boolean isLeanbackOnlyDevice() {
+        boolean onLeanbackOnly = false;
+        try {
+            onLeanbackOnly = AppGlobals.getPackageManager().hasSystemFeature(
+                    PackageManager.FEATURE_LEANBACK_ONLY);
+        } catch (RemoteException e) {
+            // noop
+        }
+
+        return onLeanbackOnly;
+    }
 }
diff --git a/services/core/java/com/android/server/am/BatteryStatsService.java b/services/core/java/com/android/server/am/BatteryStatsService.java
index 249422b..b492edd 100644
--- a/services/core/java/com/android/server/am/BatteryStatsService.java
+++ b/services/core/java/com/android/server/am/BatteryStatsService.java
@@ -623,10 +623,14 @@
         pw.println("  --charged: only output data since last charged.");
         pw.println("  --reset: reset the stats, clearing all current data.");
         pw.println("  --write: force write current collected stats to disk.");
-        pw.println("  --enable: enable an option: full-wake-history, no-auto-reset.");
-        pw.println("  --disable: disable an option: full-wake-history, no-auto-reset.");
-        pw.println("  -h: print this help text.");
         pw.println("  <package.name>: optional name of package to filter output by.");
+        pw.println("  -h: print this help text.");
+        pw.println("Battery stats (batterystats) commands:");
+        pw.println("  enable|disable <option>");
+        pw.println("    Enable or disable a running option.  Option state is not saved across boots.");
+        pw.println("    Options are:");
+        pw.println("      full-wake-history: include wake_lock_in battery history, full wake details.");
+        pw.println("      no-auto-reset: don't automatically reset stats when unplugged");
     }
 
     private int doEnableOrDisable(PrintWriter pw, int i, String[] args, boolean enable) {
@@ -702,14 +706,14 @@
                         pw.println("Battery stats written.");
                         noOutput = true;
                     }
-                } else if ("--enable".equals(arg)) {
+                } else if ("--enable".equals(arg) || "enable".equals(arg)) {
                     i = doEnableOrDisable(pw, i, args, true);
                     if (i < 0) {
                         return;
                     }
                     pw.println("Enabled: " + args[i]);
                     return;
-                } else if ("--disable".equals(arg)) {
+                } else if ("--disable".equals(arg) || "disable".equals(arg)) {
                     i = doEnableOrDisable(pw, i, args, false);
                     if (i < 0) {
                         return;
diff --git a/services/core/java/com/android/server/am/BroadcastQueue.java b/services/core/java/com/android/server/am/BroadcastQueue.java
index 9d6481a..7b2b04d 100644
--- a/services/core/java/com/android/server/am/BroadcastQueue.java
+++ b/services/core/java/com/android/server/am/BroadcastQueue.java
@@ -27,6 +27,7 @@
 import android.content.IIntentReceiver;
 import android.content.Intent;
 import android.content.pm.ActivityInfo;
+import android.content.pm.ApplicationInfo;
 import android.content.pm.PackageManager;
 import android.content.pm.ResolveInfo;
 import android.os.Bundle;
@@ -858,7 +859,10 @@
             r.state = BroadcastRecord.APP_RECEIVE;
             String targetProcess = info.activityInfo.processName;
             r.curComponent = component;
-            if (r.callingUid != Process.SYSTEM_UID && isSingleton) {
+            final int receiverUid = info.activityInfo.applicationInfo.uid;
+            // If it's a singleton, it needs to be the same app or a special app
+            if (r.callingUid != Process.SYSTEM_UID && isSingleton
+                    && mService.isValidSingletonCall(r.callingUid, receiverUid)) {
                 info.activityInfo = mService.getActivityInfoForUser(info.activityInfo, 0);
             }
             r.curReceiver = info.activityInfo;
diff --git a/services/core/java/com/android/server/am/TaskPersister.java b/services/core/java/com/android/server/am/TaskPersister.java
index ba3f2fe..3bfaca9 100644
--- a/services/core/java/com/android/server/am/TaskPersister.java
+++ b/services/core/java/com/android/server/am/TaskPersister.java
@@ -155,6 +155,7 @@
             File taskFile = recentFiles[taskNdx];
             if (DEBUG) Slog.d(TAG, "restoreTasksLocked: taskFile=" + taskFile.getName());
             BufferedReader reader = null;
+            boolean deleteFile = false;
             try {
                 reader = new BufferedReader(new FileReader(taskFile));
                 final XmlPullParser in = Xml.newPullParser();
@@ -183,10 +184,9 @@
                     }
                     XmlUtils.skipCurrentTag(in);
                 }
-            } catch (IOException e) {
-                Slog.e(TAG, "Unable to parse " + taskFile + ". Error " + e);
-            } catch (XmlPullParserException e) {
-                Slog.e(TAG, "Unable to parse " + taskFile + ". Error " + e);
+            } catch (Exception e) {
+                Slog.wtf(TAG, "Unable to parse " + taskFile + ". Error " + e);
+                deleteFile = true;
             } finally {
                 if (reader != null) {
                     try {
@@ -194,6 +194,9 @@
                     } catch (IOException e) {
                     }
                 }
+                if (!DEBUG && deleteFile) {
+                    taskFile.delete();
+                }
             }
         }
 
@@ -220,7 +223,7 @@
         return new ArrayList<TaskRecord>(Arrays.asList(tasksArray));
     }
 
-    private void removeObsoleteFiles(ArraySet<Integer> persistentTaskIds, File[] files) {
+    private static void removeObsoleteFiles(ArraySet<Integer> persistentTaskIds, File[] files) {
         for (int fileNdx = 0; fileNdx < files.length; ++fileNdx) {
             File file = files[fileNdx];
             String filename = file.getName();
@@ -285,8 +288,7 @@
                 synchronized(mService) {
                     final ArrayList<TaskRecord> tasks = mService.mRecentTasks;
                     persistentTaskIds.clear();
-                    int taskNdx;
-                    for (taskNdx = tasks.size() - 1; taskNdx >= 0; --taskNdx) {
+                    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);
diff --git a/services/core/java/com/android/server/am/TaskRecord.java b/services/core/java/com/android/server/am/TaskRecord.java
index c07bc1e..ce83ae6 100644
--- a/services/core/java/com/android/server/am/TaskRecord.java
+++ b/services/core/java/com/android/server/am/TaskRecord.java
@@ -17,9 +17,6 @@
 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;
@@ -57,6 +54,7 @@
     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";
 
@@ -106,11 +104,9 @@
 
     /** True if persistable, has changed, and has not yet been persisted */
     boolean needsPersisting = 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;
+    /** Launch the home activity when leaving this task. Will be false for tasks that are not on
+     * Display.DEFAULT_DISPLAY. */
+    boolean mOnTopOfHome = false;
 
     final ActivityManagerService mService;
 
@@ -127,8 +123,9 @@
 
     TaskRecord(ActivityManagerService service, int _taskId, Intent _intent, Intent _affinityIntent,
             String _affinity, ComponentName _realActivity, ComponentName _origActivity,
-            boolean _rootWasReset, boolean _askedCompatMode, int _taskType, int _userId,
-            String _lastDescription, ArrayList<ActivityRecord> activities, long lastTimeMoved) {
+            boolean _rootWasReset, boolean _askedCompatMode, int _taskType, boolean _onTopOfHome,
+            int _userId, String _lastDescription, ArrayList<ActivityRecord> activities,
+            long lastTimeMoved) {
         mService = service;
         taskId = _taskId;
         intent = _intent;
@@ -141,7 +138,7 @@
         rootWasReset = _rootWasReset;
         askedCompatMode = _askedCompatMode;
         taskType = _taskType;
-        mTaskToReturnTo = HOME_ACTIVITY_TYPE;
+        mOnTopOfHome = _onTopOfHome;
         userId = _userId;
         lastDescription = _lastDescription;
         mActivities = activities;
@@ -209,14 +206,6 @@
         }
     }
 
-    void setTaskToReturnTo(int taskToReturnTo) {
-        mTaskToReturnTo = taskToReturnTo;
-    }
-
-    int getTaskToReturnTo() {
-        return mTaskToReturnTo;
-    }
-
     void disposeThumbnail() {
         super.disposeThumbnail();
         for (int i=mActivities.size()-1; i>=0; i--) {
@@ -488,15 +477,11 @@
     }
 
     boolean isHomeTask() {
-        return taskType == HOME_ACTIVITY_TYPE;
+        return taskType == ActivityRecord.HOME_ACTIVITY_TYPE;
     }
 
     boolean isApplicationTask() {
-        return taskType == APPLICATION_ACTIVITY_TYPE;
-    }
-
-    boolean isOverHomeStack() {
-        return mTaskToReturnTo == HOME_ACTIVITY_TYPE || mTaskToReturnTo == RECENTS_ACTIVITY_TYPE;
+        return taskType == ActivityRecord.APPLICATION_ACTIVITY_TYPE;
     }
 
     public TaskAccessInfo getTaskAccessInfoLocked() {
@@ -638,6 +623,7 @@
         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));
         if (lastDescription != null) {
             out.attribute(null, ATTR_LASTDESCRIPTION, lastDescription.toString());
@@ -683,6 +669,7 @@
         boolean rootHasReset = false;
         boolean askedCompatMode = false;
         int taskType = ActivityRecord.APPLICATION_ACTIVITY_TYPE;
+        boolean onTopOfHome = true;
         int userId = 0;
         String lastDescription = null;
         long lastTimeOnTop = 0;
@@ -710,6 +697,8 @@
                 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)) {
@@ -747,7 +736,8 @@
 
         final TaskRecord task = new TaskRecord(stackSupervisor.mService, taskId, intent,
                 affinityIntent, affinity, realActivity, origActivity, rootHasReset,
-                askedCompatMode, taskType, userId, lastDescription, activities, lastTimeOnTop);
+                askedCompatMode, taskType, onTopOfHome, userId, lastDescription, activities,
+                lastTimeOnTop);
 
         for (int activityNdx = activities.size() - 1; activityNdx >=0; --activityNdx) {
             final ActivityRecord r = activities.get(activityNdx);
@@ -766,7 +756,7 @@
                     pw.print(" userId="); pw.print(userId);
                     pw.print(" taskType="); pw.print(taskType);
                     pw.print(" numFullscreen="); pw.print(numFullscreen);
-                    pw.print(" mTaskToReturnTo="); pw.println(mTaskToReturnTo);
+                    pw.print(" mOnTopOfHome="); pw.println(mOnTopOfHome);
         }
         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 8102591..b03c247 100644
--- a/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java
+++ b/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java
@@ -45,7 +45,6 @@
     public int currentScore;
     public final NetworkMonitor networkMonitor;
 
-
     // The list of NetworkRequests being satisfied by this Network.
     public final SparseArray<NetworkRequest> networkRequests = new SparseArray<NetworkRequest>();
     public final ArrayList<NetworkRequest> networkLingered = new ArrayList<NetworkRequest>();
@@ -66,6 +65,10 @@
         networkMonitor = new NetworkMonitor(context, handler, this);
     }
 
+    public void addRequest(NetworkRequest networkRequest) {
+        networkRequests.put(networkRequest.requestId, networkRequest);
+    }
+
     public String toString() {
         return "NetworkAgentInfo{ ni{" + networkInfo + "}  network{" +
                 network + "}  lp{" +
diff --git a/services/core/java/com/android/server/connectivity/Tethering.java b/services/core/java/com/android/server/connectivity/Tethering.java
index 92b5f52..c93f85d 100644
--- a/services/core/java/com/android/server/connectivity/Tethering.java
+++ b/services/core/java/com/android/server/connectivity/Tethering.java
@@ -489,8 +489,10 @@
     }
 
     private class StateReceiver extends BroadcastReceiver {
+        @Override
         public void onReceive(Context content, Intent intent) {
             String action = intent.getAction();
+            if (action == null) { return; }
             if (action.equals(UsbManager.ACTION_USB_STATE)) {
                 synchronized (Tethering.this.mPublicSync) {
                     boolean usbConnected = intent.getBooleanExtra(UsbManager.USB_CONNECTED, false);
diff --git a/services/core/java/com/android/server/content/ContentService.java b/services/core/java/com/android/server/content/ContentService.java
index c7d2871..8fd55e7 100644
--- a/services/core/java/com/android/server/content/ContentService.java
+++ b/services/core/java/com/android/server/content/ContentService.java
@@ -230,7 +230,7 @@
         // Notify for any user other than the caller's own requires permission.
         final int callingUserHandle = UserHandle.getCallingUserId();
         if (userHandle != callingUserHandle) {
-            mContext.enforceCallingOrSelfPermission(Manifest.permission.INTERACT_ACROSS_USERS_FULL,
+            mContext.enforceCallingOrSelfPermission(Manifest.permission.INTERACT_ACROSS_USERS,
                     "no permission to notify other users");
         }
 
diff --git a/services/core/java/com/android/server/hdmi/DeviceDiscoveryAction.java b/services/core/java/com/android/server/hdmi/DeviceDiscoveryAction.java
new file mode 100644
index 0000000..579f89f
--- /dev/null
+++ b/services/core/java/com/android/server/hdmi/DeviceDiscoveryAction.java
@@ -0,0 +1,369 @@
+/*
+ * 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.HdmiCecDeviceInfo;
+import android.hardware.hdmi.HdmiCecMessage;
+import android.util.Slog;
+
+import com.android.internal.util.Preconditions;
+import com.android.server.hdmi.HdmiControlService.DevicePollingCallback;
+
+import java.io.UnsupportedEncodingException;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Feature action that handles device discovery sequences.
+ * Device discovery is launched when TV device is woken from "Standby" state
+ * or enabled "Control for Hdmi" from disabled state.
+ *
+ * <p>Device discovery goes through the following steps.
+ * <ol>
+ *   <li>Poll all non-local devices by sending &lt;Polling Message&gt;
+ *   <li>Gather "Physical address" and "device type" of all acknowledged devices
+ *   <li>Gather "OSD (display) name" of all acknowledge devices
+ *   <li>Gather "Vendor id" of all acknowledge devices
+ * </ol>
+ */
+final class DeviceDiscoveryAction extends FeatureAction {
+    private static final String TAG = "DeviceDiscoveryAction";
+
+    // State in which the action is waiting for device polling.
+    private static final int STATE_WAITING_FOR_DEVICE_POLLING = 1;
+    // State in which the action is waiting for gathering physical address of non-local devices.
+    private static final int STATE_WAITING_FOR_PHYSICAL_ADDRESS = 2;
+    // State in which the action is waiting for gathering osd name of non-local devices.
+    private static final int STATE_WAITING_FOR_OSD_NAME = 3;
+    // State in which the action is waiting for gathering vendor id of non-local devices.
+    private static final int STATE_WAITING_FOR_VENDOR_ID = 4;
+
+    private static final int DEVICE_POLLING_RETRY = 1;
+
+    // TODO: Move this to common place
+    private static final int INVALID_PHYSICAL_ADDRESS = 0xFFFF;
+
+    /**
+     * Interface used to report result of device discovery.
+     */
+    interface DeviceDiscoveryCallback {
+        /**
+         * Called when device discovery is done.
+         *
+         * @param deviceInfos a list of all non-local devices. It can be empty list.
+         */
+        void onDeviceDiscoveryDone(List<HdmiCecDeviceInfo> deviceInfos);
+    }
+
+    // An internal container used to keep track of device information during
+    // this action.
+    private static final class DeviceInfo {
+        private final int mLogicalAddress;
+
+        private int mPhysicalAddress = INVALID_PHYSICAL_ADDRESS;
+        private int mVendorId = HdmiCec.UNKNOWN_VENDOR_ID;
+        private String mDisplayName = "";
+        private int mDeviceType = HdmiCec.DEVICE_INACTIVE;
+
+        private DeviceInfo(int logicalAddress) {
+            mLogicalAddress = logicalAddress;
+        }
+
+        private HdmiCecDeviceInfo toHdmiCecDeviceInfo() {
+            return new HdmiCecDeviceInfo(mLogicalAddress, mPhysicalAddress, mDeviceType, mVendorId,
+                    mDisplayName);
+        }
+    }
+
+    private final ArrayList<DeviceInfo> mDevices = new ArrayList<>();
+    private final DeviceDiscoveryCallback mCallback;
+    private int mProcessedDeviceCount = 0;
+
+    /**
+     * @Constructor
+     *
+     * @param service
+     * @param sourceAddress
+     */
+    DeviceDiscoveryAction(HdmiControlService service, int sourceAddress,
+            DeviceDiscoveryCallback callback) {
+        super(service, sourceAddress);
+        mCallback = Preconditions.checkNotNull(callback);
+    }
+
+    @Override
+    boolean start() {
+        mDevices.clear();
+        mState = STATE_WAITING_FOR_DEVICE_POLLING;
+
+        mService.pollDevices(new DevicePollingCallback() {
+            @Override
+            public void onPollingFinished(List<Integer> ackedAddress) {
+                if (ackedAddress.isEmpty()) {
+                    Slog.v(TAG, "No device is detected.");
+                    finish();
+                    return;
+                }
+
+                Slog.v(TAG, "Device detected: " + ackedAddress);
+                allocateDevices(ackedAddress);
+                startPhysicalAddressStage();
+            }
+        }, DEVICE_POLLING_RETRY);
+        return true;
+    }
+
+    private void allocateDevices(List<Integer> addresses) {
+        for (Integer i : addresses) {
+            DeviceInfo info = new DeviceInfo(i);
+            mDevices.add(info);
+        }
+    }
+
+    private void startPhysicalAddressStage() {
+        Slog.v(TAG, "Start [Physical Address Stage]:" + mDevices.size());
+        mProcessedDeviceCount = 0;
+        mState = STATE_WAITING_FOR_PHYSICAL_ADDRESS;
+
+        checkAndProceedStage();
+    }
+
+    private boolean verifyValidLogicalAddress(int address) {
+        return address >= HdmiCec.ADDR_TV && address < HdmiCec.ADDR_UNREGISTERED;
+    }
+
+    private void queryPhysicalAddress(int address) {
+        if (!verifyValidLogicalAddress(address)) {
+            checkAndProceedStage();
+            return;
+        }
+
+        mActionTimer.clearTimerMessage();
+        sendCommand(HdmiCecMessageBuilder.buildGivePhysicalAddress(mSourceAddress, address));
+        addTimer(mState, TIMEOUT_MS);
+    }
+
+    private void startOsdNameStage() {
+        Slog.v(TAG, "Start [Osd Name Stage]:" + mDevices.size());
+        mProcessedDeviceCount = 0;
+        mState = STATE_WAITING_FOR_OSD_NAME;
+
+        checkAndProceedStage();
+    }
+
+    private void queryOsdName(int address) {
+        if (!verifyValidLogicalAddress(address)) {
+            checkAndProceedStage();
+            return;
+        }
+
+        mActionTimer.clearTimerMessage();
+        sendCommand(HdmiCecMessageBuilder.buildGiveOsdNameCommand(mSourceAddress, address));
+        addTimer(mState, TIMEOUT_MS);
+    }
+
+    private void startVendorIdStage() {
+        Slog.v(TAG, "Start [Vendor Id Stage]:" + mDevices.size());
+
+        mProcessedDeviceCount = 0;
+        mState = STATE_WAITING_FOR_VENDOR_ID;
+
+        checkAndProceedStage();
+    }
+
+    private void queryVendorId(int address) {
+        if (!verifyValidLogicalAddress(address)) {
+            checkAndProceedStage();
+            return;
+        }
+
+        mActionTimer.clearTimerMessage();
+        sendCommand(HdmiCecMessageBuilder.buildGiveDeviceVendorIdCommand(mSourceAddress, address));
+        addTimer(mState, TIMEOUT_MS);
+    }
+
+    @Override
+    boolean processCommand(HdmiCecMessage cmd) {
+        switch (mState) {
+            case STATE_WAITING_FOR_PHYSICAL_ADDRESS:
+                if (cmd.getOpcode() == HdmiCec.MESSAGE_REPORT_PHYSICAL_ADDRESS) {
+                    handleReportPhysicalAddress(cmd);
+                    return true;
+                }
+                return false;
+            case STATE_WAITING_FOR_OSD_NAME:
+                if (cmd.getOpcode() == HdmiCec.MESSAGE_SET_OSD_NAME) {
+                    handleSetOsdName(cmd);
+                    return true;
+                }
+                return false;
+            case STATE_WAITING_FOR_VENDOR_ID:
+                if (cmd.getOpcode() == HdmiCec.MESSAGE_DEVICE_VENDOR_ID) {
+                    handleVendorId(cmd);
+                    return true;
+                }
+                return false;
+            case STATE_WAITING_FOR_DEVICE_POLLING:
+                // Fall through.
+            default:
+                return false;
+        }
+    }
+
+    private void handleReportPhysicalAddress(HdmiCecMessage cmd) {
+        Preconditions.checkState(mProcessedDeviceCount < mDevices.size());
+
+        DeviceInfo current = mDevices.get(mProcessedDeviceCount);
+        if (current.mLogicalAddress != cmd.getSource()) {
+            Slog.w(TAG, "Unmatched address[expected:" + current.mLogicalAddress + ", actual:" +
+                    cmd.getSource());
+            return;
+        }
+
+        byte params[] = cmd.getParams();
+        if (params.length == 3) {
+            current.mPhysicalAddress = ((params[0] & 0xFF) << 8) | (params[1] & 0xFF);
+            current.mDeviceType = params[2] & 0xFF;
+
+            increaseProcessedDeviceCount();
+            checkAndProceedStage();
+        } else {
+            // Physical address is a critical element in device info.
+            // If failed, remove device from device list and proceed to the next device.
+            removeDevice(mProcessedDeviceCount);
+            checkAndProceedStage();
+        }
+    }
+
+    private void handleSetOsdName(HdmiCecMessage cmd) {
+        Preconditions.checkState(mProcessedDeviceCount < mDevices.size());
+
+        DeviceInfo current = mDevices.get(mProcessedDeviceCount);
+        if (current.mLogicalAddress != cmd.getSource()) {
+            Slog.w(TAG, "Unmatched address[expected:" + current.mLogicalAddress + ", actual:" +
+                    cmd.getSource());
+            return;
+        }
+
+        String displayName = null;
+        try {
+            displayName = new String(cmd.getParams(), "US-ASCII");
+        } catch (UnsupportedEncodingException e) {
+            Slog.w(TAG, "Failed to decode display name: " + cmd.toString());
+            // If failed to get display name, use the default name of device.
+            displayName = HdmiCec.getDefaultDeviceName(current.mLogicalAddress);
+        }
+        current.mDisplayName = displayName;
+        increaseProcessedDeviceCount();
+        checkAndProceedStage();
+    }
+
+    private void handleVendorId(HdmiCecMessage cmd) {
+        Preconditions.checkState(mProcessedDeviceCount < mDevices.size());
+
+        DeviceInfo current = mDevices.get(mProcessedDeviceCount);
+        if (current.mLogicalAddress != cmd.getSource()) {
+            Slog.w(TAG, "Unmatched address[expected:" + current.mLogicalAddress + ", actual:" +
+                    cmd.getSource());
+            return;
+        }
+
+        byte[] params = cmd.getParams();
+        if (params.length == 3) {
+            int vendorId = ((params[0] & 0xFF) << 16)
+                    | ((params[1] & 0xFF) << 8)
+                    | (params[2] & 0xFF);
+            current.mVendorId = vendorId;
+        } else {
+            Slog.w(TAG, "Invalid vendor id: " + cmd.toString());
+        }
+        increaseProcessedDeviceCount();
+        checkAndProceedStage();
+    }
+
+    private void increaseProcessedDeviceCount() {
+        mProcessedDeviceCount++;
+    }
+
+    private void removeDevice(int index) {
+        mDevices.remove(index);
+    }
+
+    private void wrapUpAndFinish() {
+        Slog.v(TAG, "---------Wrap up Device Discovery:[" + mDevices.size() + "]---------");
+        ArrayList<HdmiCecDeviceInfo> result = new ArrayList<>();
+        for (DeviceInfo info : mDevices) {
+            HdmiCecDeviceInfo cecDeviceInfo = info.toHdmiCecDeviceInfo();
+            Slog.v(TAG, " DeviceInfo: " + cecDeviceInfo);
+            result.add(cecDeviceInfo);
+        }
+        Slog.v(TAG, "--------------------------------------------");
+        mCallback.onDeviceDiscoveryDone(result);
+        finish();
+    }
+
+    private void checkAndProceedStage() {
+        if (mDevices.isEmpty()) {
+            wrapUpAndFinish();
+            return;
+        }
+
+        // If finished current stage, move on to next stage.
+        if (mProcessedDeviceCount == mDevices.size()) {
+            mProcessedDeviceCount = 0;
+            switch (mState) {
+                case STATE_WAITING_FOR_PHYSICAL_ADDRESS:
+                    startOsdNameStage();
+                    return;
+                case STATE_WAITING_FOR_OSD_NAME:
+                    startVendorIdStage();
+                    return;
+                case STATE_WAITING_FOR_VENDOR_ID:
+                    wrapUpAndFinish();
+                    return;
+                default:
+                    return;
+            }
+        } else {
+            int address = mDevices.get(mProcessedDeviceCount).mLogicalAddress;
+            switch (mState) {
+                case STATE_WAITING_FOR_PHYSICAL_ADDRESS:
+                    queryPhysicalAddress(address);
+                    return;
+                case STATE_WAITING_FOR_OSD_NAME:
+                    queryOsdName(address);
+                    return;
+                case STATE_WAITING_FOR_VENDOR_ID:
+                    queryVendorId(address);
+                default:
+                    return;
+            }
+        }
+    }
+
+    @Override
+    void handleTimerEvent(int state) {
+        if (mState == STATE_NONE || mState != state) {
+            return;
+        }
+
+        Slog.v(TAG, "Timeout[State=" + mState + ", Processed=" + mProcessedDeviceCount);
+        removeDevice(mProcessedDeviceCount);
+        checkAndProceedStage();
+    }
+}
diff --git a/services/core/java/com/android/server/hdmi/FeatureAction.java b/services/core/java/com/android/server/hdmi/FeatureAction.java
index f8e9b7b..0ba7773 100644
--- a/services/core/java/com/android/server/hdmi/FeatureAction.java
+++ b/services/core/java/com/android/server/hdmi/FeatureAction.java
@@ -133,7 +133,8 @@
 
         @Override
         public void sendTimerMessage(int state, long delayMillis) {
-            sendMessageDelayed(obtainMessage(MSG_TIMEOUT, state), delayMillis);
+            // The third argument(0) is not used.
+            sendMessageDelayed(obtainMessage(MSG_TIMEOUT, state, 0), delayMillis);
         }
 
         @Override
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecController.java b/services/core/java/com/android/server/hdmi/HdmiCecController.java
index 3c18a59..5141d16 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecController.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecController.java
@@ -21,6 +21,7 @@
 import android.hardware.hdmi.HdmiCecMessage;
 import android.os.Handler;
 import android.os.Looper;
+import android.os.MessageQueue;
 import android.util.Slog;
 import android.util.SparseArray;
 
@@ -29,7 +30,6 @@
 import libcore.util.EmptyArray;
 
 import java.util.ArrayList;
-import java.util.Arrays;
 import java.util.List;
 
 /**
@@ -99,7 +99,7 @@
      */
     static HdmiCecController create(HdmiControlService service) {
         HdmiCecController controller = new HdmiCecController();
-        long nativePtr = nativeInit(controller);
+        long nativePtr = nativeInit(controller, service.getServiceLooper().getQueue());
         if (nativePtr == 0L) {
             controller = null;
             return null;
@@ -121,10 +121,11 @@
      *
      * @param deviceTypes array of device types
      */
-    void initializeLocalDevices(int[] deviceTypes) {
+    void initializeLocalDevices(int[] deviceTypes,
+            HdmiCecLocalDevice.AddressAllocationCallback callback) {
         assertRunOnServiceThread();
         for (int type : deviceTypes) {
-            HdmiCecLocalDevice device = HdmiCecLocalDevice.create(this, type);
+            HdmiCecLocalDevice device = HdmiCecLocalDevice.create(this, type, callback);
             if (device == null) {
                 continue;
             }
@@ -261,18 +262,23 @@
     }
 
     /**
+     * Clear all device info.
+     *
+     * <p>Declared as package-private. accessed by {@link HdmiControlService} only.
+     */
+    void clearDeviceInfoList() {
+        assertRunOnServiceThread();
+        mDeviceInfos.clear();
+    }
+
+    /**
      * Return a list of all {@link HdmiCecDeviceInfo}.
      *
      * <p>Declared as package-private. accessed by {@link HdmiControlService} only.
      */
     List<HdmiCecDeviceInfo> getDeviceInfoList() {
         assertRunOnServiceThread();
-
-        List<HdmiCecDeviceInfo> deviceInfoList = new ArrayList<>(mDeviceInfos.size());
-        for (int i = 0; i < mDeviceInfos.size(); ++i) {
-            deviceInfoList.add(mDeviceInfos.valueAt(i));
-        }
-        return deviceInfoList;
+        return sparseArrayToList(mDeviceInfos);
     }
 
     /**
@@ -389,6 +395,24 @@
         runDevicePolling(pollingCandidates, retryCount, callback);
     }
 
+    /**
+     * Return a list of all {@link HdmiCecLocalDevice}s.
+     *
+     * <p>Declared as package-private. accessed by {@link HdmiControlService} only.
+     */
+    List<HdmiCecLocalDevice> getLocalDeviceList() {
+        assertRunOnServiceThread();
+        return sparseArrayToList(mLocalDevices);
+    }
+
+    private static <T> List<T> sparseArrayToList(SparseArray<T> array) {
+        ArrayList<T> list = new ArrayList<>();
+        for (int i = 0; i < array.size(); ++i) {
+            list.add(array.valueAt(i));
+        }
+        return list;
+    }
+
     private boolean isAllocatedLocalDeviceAddress(int address) {
         for (int i = 0; i < mLocalDevices.size(); ++i) {
             if (mLocalDevices.valueAt(i).isAddressOf(address)) {
@@ -471,19 +495,19 @@
 
     private void onReceiveCommand(HdmiCecMessage message) {
         assertRunOnServiceThread();
-        if (isAcceptableAddress(message.getDestination()) &&
-                mService.handleCecCommand(message)) {
+        if (isAcceptableAddress(message.getDestination())
+                && mService.handleCecCommand(message)) {
             return;
         }
 
-        // TODO: Use device's source address for broadcast message.
-        int sourceAddress = message.getDestination() != HdmiCec.ADDR_BROADCAST ?
-                message.getDestination() : 0;
-        // Reply <Feature Abort> to initiator (source) for all requests.
-        HdmiCecMessage cecMessage = HdmiCecMessageBuilder.buildFeatureAbortCommand
-                (sourceAddress, message.getSource(), message.getOpcode(),
-                        HdmiCecMessageBuilder.ABORT_REFUSED);
-        sendCommand(cecMessage, null);
+        if (message.getDestination() != HdmiCec.ADDR_BROADCAST) {
+            int sourceAddress = message.getDestination();
+            // Reply <Feature Abort> to initiator (source) for all requests.
+            HdmiCecMessage cecMessage = HdmiCecMessageBuilder.buildFeatureAbortCommand(
+                    sourceAddress, message.getSource(), message.getOpcode(),
+                    HdmiConstants.ABORT_REFUSED);
+            sendCommand(cecMessage, null);
+        }
     }
 
     void sendCommand(HdmiCecMessage cecMessage) {
@@ -517,17 +541,8 @@
      * Called by native when incoming CEC message arrived.
      */
     private void handleIncomingCecCommand(int srcAddress, int dstAddress, byte[] body) {
-        byte opcode = body[0];
-        byte params[] = Arrays.copyOfRange(body, 1, body.length);
-        final HdmiCecMessage cecMessage = new HdmiCecMessage(srcAddress, dstAddress, opcode, params);
-
-        // Delegate message to main handler so that it handles in main thread.
-        runOnServiceThread(new Runnable() {
-            @Override
-            public void run() {
-                onReceiveCommand(cecMessage);
-            }
-        });
+        assertRunOnServiceThread();
+        onReceiveCommand(HdmiCecMessageBuilder.of(srcAddress, dstAddress, body));
     }
 
     /**
@@ -539,7 +554,7 @@
         mService.onHotplug(0, connected);
     }
 
-    private static native long nativeInit(HdmiCecController handler);
+    private static native long nativeInit(HdmiCecController handler, MessageQueue messageQueue);
     private static native int nativeSendCecCommand(long controllerPtr, int srcAddress,
             int dstAddress, byte[] body);
     private static native int nativeAddLogicalAddress(long controllerPtr, int logicalAddress);
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDevice.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDevice.java
index e65e5fa..aac2a15 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDevice.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDevice.java
@@ -29,23 +29,41 @@
 
     protected final HdmiCecController mController;
     protected final int mDeviceType;
+    protected final AddressAllocationCallback mAllocationCallback;
     protected int mAddress;
     protected int mPreferredAddress;
     protected HdmiCecDeviceInfo mDeviceInfo;
 
-    protected HdmiCecLocalDevice(HdmiCecController controller, int deviceType) {
+    /**
+     * Callback interface to notify newly allocated logical address of the given
+     * local device.
+     */
+    interface AddressAllocationCallback {
+        /**
+         * Called when a logical address of the given device is allocated.
+         *
+         * @param deviceType original device type
+         * @param logicalAddress newly allocated logical address
+         */
+        void onAddressAllocated(int deviceType, int logicalAddress);
+    }
+
+    protected HdmiCecLocalDevice(HdmiCecController controller, int deviceType,
+            AddressAllocationCallback callback) {
         mController = controller;
         mDeviceType = deviceType;
+        mAllocationCallback = callback;
         mAddress = HdmiCec.ADDR_UNREGISTERED;
     }
 
     // Factory method that returns HdmiCecLocalDevice of corresponding type.
-    static HdmiCecLocalDevice create(HdmiCecController controller, int deviceType) {
+    static HdmiCecLocalDevice create(HdmiCecController controller, int deviceType,
+            AddressAllocationCallback callback) {
         switch (deviceType) {
         case HdmiCec.DEVICE_TV:
-            return new HdmiCecLocalDeviceTv(controller);
+            return new HdmiCecLocalDeviceTv(controller, callback);
         case HdmiCec.DEVICE_PLAYBACK:
-            return new HdmiCecLocalDevicePlayback(controller);
+            return new HdmiCecLocalDevicePlayback(controller, callback);
         default:
             return null;
         }
@@ -53,6 +71,12 @@
 
     abstract void init();
 
+    /**
+     * Called when a logical address of the local device is allocated.
+     * Note that internal variables are updated before it's called.
+     */
+    protected abstract void onAddressAllocated(int logicalAddress);
+
     protected void allocateAddress(int type) {
         mController.allocateLogicalAddress(type, mPreferredAddress,
                 new AllocateLogicalAddressCallback() {
@@ -66,6 +90,10 @@
                 mController.addDeviceInfo(deviceInfo);
 
                 mController.addLogicalAddress(logicalAddress);
+                onAddressAllocated(logicalAddress);
+                if (mAllocationCallback != null) {
+                    mAllocationCallback.onAddressAllocated(deviceType, logicalAddress);
+                }
             }
         });
     }
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java
index a953467..3347725 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java
@@ -23,13 +23,17 @@
  */
 final class HdmiCecLocalDevicePlayback extends HdmiCecLocalDevice {
 
-    HdmiCecLocalDevicePlayback(HdmiCecController controller) {
-        super(controller, HdmiCec.DEVICE_PLAYBACK);
+    HdmiCecLocalDevicePlayback(HdmiCecController controller, AddressAllocationCallback callback) {
+        super(controller, HdmiCec.DEVICE_PLAYBACK, callback);
     }
 
     @Override
     void init() {
         allocateAddress(mDeviceType);
+    }
+
+    @Override
+    protected void onAddressAllocated(int logicalAddress) {
         mController.sendCommand(HdmiCecMessageBuilder.buildReportPhysicalAddressCommand(
                 mAddress, mController.getPhysicalAddress(), mDeviceType));
     }
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java
index 01ea685..93761ab 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java
@@ -23,14 +23,17 @@
  */
 final class HdmiCecLocalDeviceTv extends HdmiCecLocalDevice {
 
-    HdmiCecLocalDeviceTv(HdmiCecController controller) {
-        super(controller, HdmiCec.DEVICE_TV);
+    HdmiCecLocalDeviceTv(HdmiCecController controller, AddressAllocationCallback callback) {
+        super(controller, HdmiCec.DEVICE_TV, callback);
     }
 
     @Override
     void init() {
         allocateAddress(mDeviceType);
+    }
 
+    @Override
+    protected void onAddressAllocated(int logicalAddress) {
         // TODO: vendor-specific initialization here.
 
         mController.sendCommand(HdmiCecMessageBuilder.buildReportPhysicalAddressCommand(
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecMessageBuilder.java b/services/core/java/com/android/server/hdmi/HdmiCecMessageBuilder.java
index be270b9..6c2be34 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecMessageBuilder.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecMessageBuilder.java
@@ -20,25 +20,31 @@
 import android.hardware.hdmi.HdmiCecMessage;
 
 import java.io.UnsupportedEncodingException;
+import java.util.Arrays;
 
 /**
  * A helper class to build {@link HdmiCecMessage} from various cec commands.
  */
 public class HdmiCecMessageBuilder {
-    // TODO: move these values to HdmiCec.java once make it internal constant class.
-    // CEC's ABORT reason values.
-    static final int ABORT_UNRECOGNIZED_MODE = 0;
-    static final int ABORT_NOT_IN_CORRECT_MODE = 1;
-    static final int ABORT_CANNOT_PROVIDE_SOURCE = 2;
-    static final int ABORT_INVALID_OPERAND = 3;
-    static final int ABORT_REFUSED = 4;
-    static final int ABORT_UNABLE_TO_DETERMINE = 5;
-
     private static final int OSD_NAME_MAX_LENGTH = 13;
 
     private HdmiCecMessageBuilder() {}
 
     /**
+     * Build {@link HdmiCecMessage} from raw data.
+     *
+     * @param src source address of command
+     * @param dest destination address of command
+     * @param body body of message. It includes opcode.
+     * @return newly created {@link HdmiCecMessage}
+     */
+    static HdmiCecMessage of(int src, int dest, byte[] body) {
+        byte opcode = body[0];
+        byte params[] = Arrays.copyOfRange(body, 1, body.length);
+        return new HdmiCecMessage(src, dest, opcode, params);
+    }
+
+    /**
      * Build &lt;Feature Abort&gt; command. &lt;Feature Abort&gt; consists of
      * 1 byte original opcode and 1 byte reason fields with basic fields.
      *
@@ -58,6 +64,17 @@
     }
 
     /**
+     * Build &lt;Give Physical Address&gt; command.
+     *
+     * @param src source address of command
+     * @param dest destination address of command
+     * @return newly created {@link HdmiCecMessage}
+     */
+    static HdmiCecMessage buildGivePhysicalAddress(int src, int dest) {
+        return buildCommand(src, dest, HdmiCec.MESSAGE_GIVE_PHYSICAL_ADDRESS);
+    }
+
+    /**
      * Build &lt;Give Osd Name&gt; command.
      *
      * @param src source address of command
@@ -264,6 +281,64 @@
     }
 
     /**
+     * Build &lt;System Audio Mode Request&gt; command.
+     *
+     * @param src source address of command
+     * @param avr destination address of command, it should be AVR
+     * @param avrPhysicalAddress physical address of AVR
+     * @param enableSystemAudio whether to enable System Audio Mode or not
+     * @return newly created {@link HdmiCecMessage}
+     */
+    static HdmiCecMessage buildSystemAudioModeRequest(int src, int avr, int avrPhysicalAddress,
+            boolean enableSystemAudio) {
+        if (enableSystemAudio) {
+            return buildCommand(src, avr, HdmiCec.MESSAGE_SYSTEM_AUDIO_MODE_REQUEST,
+                    physicalAddressToParam(avrPhysicalAddress));
+        } else {
+            return buildCommand(src, avr, HdmiCec.MESSAGE_SYSTEM_AUDIO_MODE_REQUEST);
+        }
+    }
+
+    /**
+     * Build &lt;Give Audio Status&gt; command.
+     *
+     * @param src source address of command
+     * @param dest destination address of command
+     * @return newly created {@link HdmiCecMessage}
+     */
+    static HdmiCecMessage buildGiveAudioStatus(int src, int dest) {
+        return buildCommand(src, dest, HdmiCec.MESSAGE_GIVE_AUDIO_STATUS);
+    }
+
+    /**
+     * Build &lt;User Control Pressed&gt; command.
+     *
+     * @param src source address of command
+     * @param dest destination address of command
+     * @param uiCommand keycode that user pressed
+     * @return newly created {@link HdmiCecMessage}
+     */
+    static HdmiCecMessage buildUserControlPressed(int src, int dest, int uiCommand) {
+        byte[] params = new byte[] {
+                (byte) uiCommand
+        };
+        return buildCommand(src, dest, HdmiCec.MESSAGE_USER_CONTROL_PRESSED, params);
+    }
+
+    /**
+     * Build &lt;User Control Released&gt; command.
+     *
+     * @param src source address of command
+     * @param dest destination address of command
+     * @return newly created {@link HdmiCecMessage}
+     */
+    static HdmiCecMessage buildUserControlReleased(int src, int dest) {
+        return buildCommand(src, dest, HdmiCec.MESSAGE_USER_CONTROL_RELEASED);
+    }
+
+    /***** Please ADD new buildXXX() methods above. ******/
+
+    /**
      * Build a {@link HdmiCecMessage} without extra parameter.
      *
      * @param src source address of command
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecService.java b/services/core/java/com/android/server/hdmi/HdmiCecService.java
index 0a4c719..98dc72f 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecService.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecService.java
@@ -75,13 +75,8 @@
 
     @Override
     public void onStart() {
-        mNativePtr = nativeInit(this);
-        if (mNativePtr != 0) {
-            // TODO: Consider using a dedicated, configurable identifier for OSD name, maybe from
-            //       Settings. It should be ASCII only, not a very long one (limited to 15 chars).
-            setOsdNameLocked(Build.MODEL);
-            publishBinderService(Context.HDMI_CEC_SERVICE, new BinderService());
-        }
+        // Stop publishing the service. Soon to be deprecated.
+        Log.w(TAG, "In transition to HdmiControlService. May not work.");
     }
 
     /**
diff --git a/services/core/java/com/android/server/hdmi/HdmiConstants.java b/services/core/java/com/android/server/hdmi/HdmiConstants.java
new file mode 100644
index 0000000..a83d1ed
--- /dev/null
+++ b/services/core/java/com/android/server/hdmi/HdmiConstants.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;
+
+/**
+ * Defines constants related to HDMI-CEC protocol internal implementation.
+ * If a constant will be used in the public api, it should be located in
+ * {@link android.hardware.hdmi.HdmiCec}.
+ */
+final class HdmiConstants {
+
+    // Constants related to operands of HDMI CEC commands.
+    // Refer to CEC Table 29 in HDMI Spec v1.4b.
+    // [Abort Reason]
+    static final int ABORT_UNRECOGNIZED_MODE = 0;
+    static final int ABORT_NOT_IN_CORRECT_MODE = 1;
+    static final int ABORT_CANNOT_PROVIDE_SOURCE = 2;
+    static final int ABORT_INVALID_OPERAND = 3;
+    static final int ABORT_REFUSED = 4;
+    static final int ABORT_UNABLE_TO_DETERMINE = 5;
+
+    // [Audio Status]
+    static final int SYSTEM_AUDIO_STATUS_OFF = 0;
+    static final int SYSTEM_AUDIO_STATUS_ON = 1;
+
+    // Constants related to UI Command Codes.
+    // Refer to CEC Table 30 in HDMI Spec v1.4b.
+    static final int UI_COMMAND_MUTE = 0x43;
+    static final int UI_COMMAND_MUTE_FUNCTION = 0x65;
+    static final int UI_COMMAND_RESTORE_VOLUME_FUNCTION = 0x66;
+
+    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 475852f..0f3fc21 100644
--- a/services/core/java/com/android/server/hdmi/HdmiControlService.java
+++ b/services/core/java/com/android/server/hdmi/HdmiControlService.java
@@ -30,9 +30,12 @@
 import android.os.Looper;
 import android.os.RemoteException;
 import android.util.Slog;
+import android.util.SparseIntArray;
 
 import com.android.internal.annotations.GuardedBy;
 import com.android.server.SystemService;
+import com.android.server.hdmi.DeviceDiscoveryAction.DeviceDiscoveryCallback;
+import com.android.server.hdmi.HdmiCecLocalDevice.AddressAllocationCallback;
 
 import java.util.ArrayList;
 import java.util.Iterator;
@@ -115,8 +118,11 @@
     // TODO: it may need to hold lock if it's accessed from others.
     private boolean mArcStatusEnabled = false;
 
+    // Whether SystemAudioMode is "On" or not.
+    private boolean mSystemAudioMode;
+
     // Handler running on service thread. It's used to run a task in service thread.
-    private Handler mHandler = new Handler();
+    private final Handler mHandler = new Handler();
 
     public HdmiControlService(Context context) {
         super(context);
@@ -129,7 +135,22 @@
         mIoThread.start();
         mCecController = HdmiCecController.create(this);
         if (mCecController != null) {
-            mCecController.initializeLocalDevices(mLocalDevices);
+            mCecController.initializeLocalDevices(mLocalDevices, new AddressAllocationCallback() {
+                private final SparseIntArray mAllocated = new SparseIntArray();
+
+                @Override
+                public void onAddressAllocated(int deviceType, int logicalAddress) {
+                    mAllocated.append(deviceType, logicalAddress);
+                    // TODO: get HdmiLCecLocalDevice and call onAddressAllocated here.
+
+                    // Once all logical allocation is done, launch device discovery
+                    // action if one of local device is TV.
+                    int tvAddress = mAllocated.get(HdmiCec.DEVICE_TV, -1);
+                    if (mLocalDevices.length == mAllocated.size() && tvAddress != -1) {
+                        launchDeviceDiscovery(tvAddress);
+                    }
+                }
+            });
         } else {
             Slog.i(TAG, "Device does not support HDMI-CEC.");
         }
@@ -139,8 +160,10 @@
             Slog.i(TAG, "Device does not support MHL-control.");
         }
 
-        // TODO: Publish the BinderService
-        // publishBinderService(Context.HDMI_CONTROL_SERVICE, new BinderService());
+        publishBinderService(Context.HDMI_CONTROL_SERVICE, new BinderService());
+
+        // TODO: Read the preference for SystemAudioMode and initialize mSystemAudioMode and
+        // start to monitor the preference value and invoke SystemAudioActionFromTv if needed.
     }
 
     /**
@@ -194,35 +217,43 @@
      * @param action {@link FeatureAction} to remove
      */
     void removeAction(final FeatureAction action) {
-        runOnServiceThread(new Runnable() {
-            @Override
-            public void run() {
-                mActions.remove(action);
-            }
-        });
+        assertRunOnServiceThread();
+        mActions.remove(action);
     }
 
     // Remove all actions matched with the given Class type.
     private <T extends FeatureAction> void removeAction(final Class<T> clazz) {
-        runOnServiceThread(new Runnable() {
-            @Override
-            public void run() {
-                Iterator<FeatureAction> iter = mActions.iterator();
-                while (iter.hasNext()) {
-                    FeatureAction action = iter.next();
-                    if (action.getClass().equals(clazz)) {
-                        action.clear();
-                        mActions.remove(action);
-                    }
-                }
+        removeActionExcept(clazz, null);
+    }
+
+    // Remove all actions matched with the given Class type besides |exception|.
+    <T extends FeatureAction> void removeActionExcept(final Class<T> clazz,
+            final FeatureAction exception) {
+        assertRunOnServiceThread();
+        Iterator<FeatureAction> iter = mActions.iterator();
+        while (iter.hasNext()) {
+            FeatureAction action = iter.next();
+            if (action != exception && action.getClass().equals(clazz)) {
+                action.clear();
+                mActions.remove(action);
             }
-        });
+        }
     }
 
     private void runOnServiceThread(Runnable runnable) {
         mHandler.post(runnable);
     }
 
+    void runOnServiceThreadAtFrontOfQueue(Runnable runnable) {
+        mHandler.postAtFrontOfQueue(runnable);
+    }
+
+    private void assertRunOnServiceThread() {
+        if (Looper.myLooper() != mHandler.getLooper()) {
+            throw new IllegalStateException("Should run on service thread.");
+        }
+    }
+
     /**
      * Change ARC status into the given {@code enabled} status.
      *
@@ -286,18 +317,24 @@
             case HdmiCec.MESSAGE_TERMINATE_ARC:
                 handleTerminateArc(message);
                 return true;
-            // TODO: Add remaining system information query such as
-            // <Give Device Power Status> and <Request Active Source> handler.
+            case HdmiCec.MESSAGE_REPORT_PHYSICAL_ADDRESS:
+                handleReportPhysicalAddress(message);
+                return true;
+            case HdmiCec.MESSAGE_SET_SYSTEM_AUDIO_MODE:
+                handleSetSystemAudioMode(message);
+                return true;
+            case HdmiCec.MESSAGE_SYSTEM_AUDIO_MODE_STATUS:
+                handleSystemAudioModeStatus(message);
+                return true;
             default:
-                Slog.w(TAG, "Unsupported cec command:" + message.toString());
-                return false;
+                return dispatchMessageToAction(message);
         }
     }
 
     /**
      * Called when a new hotplug event is issued.
      *
-     * @param port hdmi port number where hot plug event issued.
+     * @param portNo hdmi port number where hot plug event issued.
      * @param connected whether to be plugged in or not
      */
     void onHotplug(int portNo, boolean connected) {
@@ -315,6 +352,22 @@
         mCecController.pollDevices(callback, retryCount);
     }
 
+    private void handleReportPhysicalAddress(HdmiCecMessage message) {
+        // At first, try to consume it.
+        if (dispatchMessageToAction(message)) {
+            return;
+        }
+
+        // Ignore if [Device Discovery Action] is on going ignore message.
+        if (hasAction(DeviceDiscoveryAction.class)) {
+            Slog.i(TAG, "Ignore unrecognizable <Report Physical Address> "
+                    + "because Device Discovery Action is on-going:" + message);
+            return;
+        }
+
+        // TODO: start new device action.
+    }
+
     private void handleInitiateArc(HdmiCecMessage message){
         // In case where <Initiate Arc> is started by <Request ARC Initiation>
         // need to clean up RequestArcInitiationAction.
@@ -378,7 +431,7 @@
             sendCecCommand(
                     HdmiCecMessageBuilder.buildFeatureAbortCommand(message.getDestination(),
                             message.getSource(), HdmiCec.MESSAGE_GET_MENU_LANGUAGE,
-                            HdmiCecMessageBuilder.ABORT_UNRECOGNIZED_MODE));
+                            HdmiConstants.ABORT_UNRECOGNIZED_MODE));
             return;
         }
 
@@ -393,6 +446,43 @@
         }
     }
 
+    private boolean dispatchMessageToAction(HdmiCecMessage message) {
+        for (FeatureAction action : mActions) {
+            if (action.processCommand(message)) {
+                return true;
+            }
+        }
+        Slog.w(TAG, "Unsupported cec command:" + message);
+        return false;
+    }
+
+    private void handleSetSystemAudioMode(HdmiCecMessage message) {
+        if (dispatchMessageToAction(message) || !isMessageForSystemAudio(message)) {
+            return;
+        }
+        SystemAudioActionFromAvr action = new SystemAudioActionFromAvr(this,
+                message.getDestination(), message.getSource(),
+                HdmiUtils.parseCommandParamSystemAudioStatus(message));
+        addAndStartAction(action);
+    }
+
+    private void handleSystemAudioModeStatus(HdmiCecMessage message) {
+        if (!isMessageForSystemAudio(message)) {
+            return;
+        }
+        setSystemAudioMode(HdmiUtils.parseCommandParamSystemAudioStatus(message));
+    }
+
+    private boolean isMessageForSystemAudio(HdmiCecMessage message) {
+        if (message.getSource() != HdmiCec.ADDR_AUDIO_SYSTEM
+                || message.getDestination() != HdmiCec.ADDR_TV
+                || getAvrDeviceInfo() == null) {
+            Slog.w(TAG, "Skip abnormal CecMessage: " + message);
+            return false;
+        }
+        return true;
+    }
+
     // Record class that monitors the event of the caller of being killed. Used to clean up
     // the listener list and record list accordingly.
     private final class HotplugEventListenerRecord implements IBinder.DeathRecipient {
@@ -411,6 +501,38 @@
         }
     }
 
+    void addCecDevice(HdmiCecDeviceInfo info) {
+        mCecController.addDeviceInfo(info);
+    }
+
+    // Launch device discovery sequence.
+    // It starts with clearing the existing device info list.
+    // Note that it assumes that logical address of all local devices is already allocated.
+    private void launchDeviceDiscovery(int sourceAddress) {
+        // At first, clear all existing device infos.
+        mCecController.clearDeviceInfoList();
+
+        // TODO: check whether TV is one of local devices.
+        DeviceDiscoveryAction action = new DeviceDiscoveryAction(this, sourceAddress,
+                new DeviceDiscoveryCallback() {
+                    @Override
+                    public void onDeviceDiscoveryDone(List<HdmiCecDeviceInfo> deviceInfos) {
+                        for (HdmiCecDeviceInfo info : deviceInfos) {
+                            mCecController.addDeviceInfo(info);
+                        }
+
+                        // Add device info of all local devices.
+                        for (HdmiCecLocalDevice device : mCecController.getLocalDeviceList()) {
+                            mCecController.addDeviceInfo(device.getDeviceInfo());
+                        }
+
+                        // TODO: start hot-plug detection sequence here.
+                        // addAndStartAction(new HotplugDetectionAction());
+                    }
+                });
+        addAndStartAction(action);
+    }
+
     private void enforceAccessPermission() {
         getContext().enforceCallingOrSelfPermission(PERMISSION, TAG);
     }
@@ -550,4 +672,32 @@
             Slog.e(TAG, "Invoking callback failed:" + e);
         }
     }
+
+    HdmiCecDeviceInfo getAvrDeviceInfo() {
+        return mCecController.getDeviceInfo(HdmiCec.ADDR_AUDIO_SYSTEM);
+    }
+
+    void setSystemAudioMode(boolean newMode) {
+        assertRunOnServiceThread();
+        if (newMode != mSystemAudioMode) {
+            // 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.
+            mSystemAudioMode = newMode;
+        }
+    }
+
+    boolean getSystemAudioMode() {
+        assertRunOnServiceThread();
+        return mSystemAudioMode;
+    }
+
+    void setAudioStatus(boolean mute, int volume) {
+        // TODO: Hook up with AudioManager.
+    }
+
+    boolean isInPresetInstallationMode() {
+        // TODO: Implement this.
+        return false;
+    }
 }
diff --git a/services/core/java/com/android/server/hdmi/HdmiUtils.java b/services/core/java/com/android/server/hdmi/HdmiUtils.java
new file mode 100644
index 0000000..ef128ed1
--- /dev/null
+++ b/services/core/java/com/android/server/hdmi/HdmiUtils.java
@@ -0,0 +1,74 @@
+/*
+ * 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 android.util.Slog;
+
+/**
+ * Various utilities to handle HDMI CEC messages.
+ */
+final class HdmiUtils {
+
+    private HdmiUtils() { /* cannot be instantiated */ }
+
+    /**
+     * Verify if the given address is for the given device type.  If not it will throw
+     * {@link IllegalArgumentException}.
+     *
+     * @param logicalAddress the logical address to verify
+     * @param deviceType the device type to check
+     * @throw IllegalArgumentException
+     */
+    static void verifyAddressType(int logicalAddress, int deviceType) {
+        int actualDeviceType = HdmiCec.getTypeFromAddress(logicalAddress);
+        if (actualDeviceType != deviceType) {
+            throw new IllegalArgumentException("Device type missmatch:[Expected:" + deviceType
+                    + ", Actual:" + actualDeviceType);
+        }
+    }
+
+    /**
+     * Check if the given CEC message come from the given address.
+     *
+     * @param cmd the CEC message to check
+     * @param expectedAddress the expected source address of the given message
+     * @param tag the tag of caller module (for log message)
+     * @return true if the CEC message comes from the given address
+     */
+    static boolean checkCommandSource(HdmiCecMessage cmd, int expectedAddress, String tag) {
+        int src = cmd.getSource();
+        if (src != expectedAddress) {
+            Slog.w(tag, "Invalid source [Expected:" + expectedAddress + ", Actual:" + src + "]");
+            return false;
+        }
+        return true;
+    }
+
+    /**
+     * Parse the parameter block of CEC message as [System Audio Status].
+     *
+     * @param cmd the CEC message to parse
+     * @return true if the given parameter has [ON] value
+     */
+    static boolean parseCommandParamSystemAudioStatus(HdmiCecMessage cmd) {
+        // TODO: Handle the exception when the length is wrong.
+        return cmd.getParams().length > 0
+                && cmd.getParams()[0] == HdmiConstants.SYSTEM_AUDIO_STATUS_ON;
+    }
+}
diff --git a/services/core/java/com/android/server/hdmi/RequestArcAction.java b/services/core/java/com/android/server/hdmi/RequestArcAction.java
index 05614a4..08ca306 100644
--- a/services/core/java/com/android/server/hdmi/RequestArcAction.java
+++ b/services/core/java/com/android/server/hdmi/RequestArcAction.java
@@ -44,28 +44,15 @@
      */
     RequestArcAction(HdmiControlService service, int sourceAddress, int avrAddress) {
         super(service, sourceAddress);
-        verifyAddressType(sourceAddress, HdmiCec.DEVICE_TV);
-        verifyAddressType(avrAddress, HdmiCec.DEVICE_AUDIO_SYSTEM);
+        HdmiUtils.verifyAddressType(sourceAddress, HdmiCec.DEVICE_TV);
+        HdmiUtils.verifyAddressType(avrAddress, HdmiCec.DEVICE_AUDIO_SYSTEM);
         mAvrAddress = avrAddress;
     }
 
-    private static void verifyAddressType(int logicalAddress, int deviceType) {
-        int actualDeviceType = HdmiCec.getTypeFromAddress(logicalAddress);
-        if (actualDeviceType != deviceType) {
-            throw new IllegalArgumentException("Device type missmatch:[Expected:" + deviceType
-                    + ", Actual:" + actualDeviceType);
-        }
-    }
-
     @Override
     boolean processCommand(HdmiCecMessage cmd) {
-        if (mState != STATE_WATING_FOR_REQUEST_ARC_REQUEST_RESPONSE) {
-            return false;
-        }
-
-        int src = cmd.getSource();
-        if (src != mAvrAddress) {
-            Slog.w(TAG, "Invalid source [Expected:" + mAvrAddress + ", Actual:" + src + "]");
+        if (mState != STATE_WATING_FOR_REQUEST_ARC_REQUEST_RESPONSE
+                || !HdmiUtils.checkCommandSource(cmd, mAvrAddress, TAG)) {
             return false;
         }
         int opcode = cmd.getOpcode();
diff --git a/services/core/java/com/android/server/hdmi/SetArcTransmissionStateAction.java b/services/core/java/com/android/server/hdmi/SetArcTransmissionStateAction.java
index e3525d8..d53d88d 100644
--- a/services/core/java/com/android/server/hdmi/SetArcTransmissionStateAction.java
+++ b/services/core/java/com/android/server/hdmi/SetArcTransmissionStateAction.java
@@ -46,21 +46,12 @@
     SetArcTransmissionStateAction(HdmiControlService service, int sourceAddress, int avrAddress,
             boolean enabled) {
         super(service, sourceAddress);
-        verifyAddressType(sourceAddress, HdmiCec.DEVICE_TV);
-        verifyAddressType(avrAddress, HdmiCec.DEVICE_AUDIO_SYSTEM);
+        HdmiUtils.verifyAddressType(sourceAddress, HdmiCec.DEVICE_TV);
+        HdmiUtils.verifyAddressType(avrAddress, HdmiCec.DEVICE_AUDIO_SYSTEM);
         mAvrAddress = avrAddress;
         mEnabled = enabled;
     }
 
-    // TODO: extract it as separate utility class.
-    private static void verifyAddressType(int logicalAddress, int deviceType) {
-        int actualDeviceType = HdmiCec.getTypeFromAddress(logicalAddress);
-        if (actualDeviceType != deviceType) {
-            throw new IllegalArgumentException("Device type missmatch:[Expected:" + deviceType
-                    + ", Actual:" + actualDeviceType);
-        }
-    }
-
     @Override
     boolean start() {
         if (mEnabled) {
diff --git a/services/core/java/com/android/server/hdmi/SystemAudioAction.java b/services/core/java/com/android/server/hdmi/SystemAudioAction.java
new file mode 100644
index 0000000..dde3342
--- /dev/null
+++ b/services/core/java/com/android/server/hdmi/SystemAudioAction.java
@@ -0,0 +1,192 @@
+/*
+ * 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;
+
+/**
+ * Base feature action class for SystemAudioActionFromTv and SystemAudioActionFromAvr.
+ */
+abstract class SystemAudioAction extends FeatureAction {
+    private static final String TAG = "SystemAudioAction";
+
+    // 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;
+    private static final int OFF_TIMEOUT_MS = TIMEOUT_MS;
+
+    // Logical address of AV Receiver.
+    protected final int mAvrLogicalAddress;
+
+    // The target audio status of the action, whether to enable the system audio mode or not.
+    protected boolean mTargetAudioStatus;
+
+    private int mSendRetryCount = 0;
+
+    /**
+     * Constructor
+     *
+     * @param service {@link HdmiControlService} instance
+     * @param sourceAddress logical address of source device (TV or STB).
+     * @param avrAddress logical address of AVR device
+     * @param targetStatus Whether to enable the system audio mode or not
+     * @throw IllegalArugmentException if device type of sourceAddress and avrAddress is invalid
+     */
+    SystemAudioAction(HdmiControlService service, int sourceAddress, int avrAddress,
+            boolean targetStatus) {
+        super(service, sourceAddress);
+        HdmiUtils.verifyAddressType(avrAddress, HdmiCec.DEVICE_AUDIO_SYSTEM);
+        mAvrLogicalAddress = avrAddress;
+        mTargetAudioStatus = targetStatus;
+    }
+
+    protected void sendSystemAudioModeRequest() {
+        int avrPhysicalAddress = mService.getAvrDeviceInfo().getPhysicalAddress();
+        HdmiCecMessage command = HdmiCecMessageBuilder.buildSystemAudioModeRequest(mSourceAddress,
+                mAvrLogicalAddress, avrPhysicalAddress, mTargetAudioStatus);
+        sendCommand(command, new HdmiControlService.SendMessageCallback() {
+            @Override
+            public void onSendCompleted(int error) {
+                if (error == HdmiControlService.SEND_RESULT_SUCCESS) {
+                    mState = STATE_WAIT_FOR_SET_SYSTEM_AUDIO_MODE;
+                    addTimer(mState, mTargetAudioStatus ? ON_TIMEOUT_MS : OFF_TIMEOUT_MS);
+                } else {
+                    setSystemAudioMode(false);
+                    finish();
+                }
+            }
+        });
+    }
+
+    private void handleSendSystemAudioModeRequestTimeout() {
+        if (!mTargetAudioStatus  // Don't retry for Off case.
+                || mSendRetryCount++ >= MAX_SEND_RETRY_COUNT) {
+            setSystemAudioMode(false);
+            finish();
+            return;
+        }
+        sendSystemAudioModeRequest();
+    }
+
+    protected void setSystemAudioMode(boolean mode) {
+        mService.setSystemAudioMode(mode);
+    }
+
+    protected void sendGiveAudioStatus() {
+        HdmiCecMessage command = HdmiCecMessageBuilder.buildGiveAudioStatus(mSourceAddress,
+                mAvrLogicalAddress);
+        sendCommand(command, new HdmiControlService.SendMessageCallback() {
+            @Override
+            public void onSendCompleted(int error) {
+                if (error == HdmiControlService.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 = mService.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(
+                mSourceAddress, mAvrLogicalAddress, uiCommand));
+        sendCommand(HdmiCecMessageBuilder.buildUserControlReleased(
+                mSourceAddress, mAvrLogicalAddress));
+    }
+
+    @Override
+    final boolean processCommand(HdmiCecMessage cmd) {
+        switch (mState) {
+            case STATE_WAIT_FOR_SET_SYSTEM_AUDIO_MODE:
+                // TODO: Handle <FeatureAbort> of <SystemAudioModeRequest>
+                if (cmd.getOpcode() != HdmiCec.MESSAGE_SET_SYSTEM_AUDIO_MODE
+                        || !HdmiUtils.checkCommandSource(cmd, mAvrLogicalAddress, TAG)) {
+                    return false;
+                }
+                boolean receivedStatus = HdmiUtils.parseCommandParamSystemAudioStatus(cmd);
+                if (receivedStatus == mTargetAudioStatus) {
+                    setSystemAudioMode(receivedStatus);
+                    sendGiveAudioStatus();
+                } else {
+                    // Unexpected response, consider the request is newly initiated by AVR.
+                    // To return 'false' will initiate new SystemAudioActionFromAvr by the control
+                    // service.
+                    finish();
+                    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;
+                    mService.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;
+        }
+        return false;
+    }
+
+    protected void removeSystemAudioActionInProgress() {
+        mService.removeActionExcept(SystemAudioActionFromTv.class, this);
+        mService.removeActionExcept(SystemAudioActionFromAvr.class, this);
+    }
+
+    @Override
+    final void handleTimerEvent(int state) {
+        if (mState != state) {
+            return;
+        }
+        switch (mState) {
+            case STATE_WAIT_FOR_SET_SYSTEM_AUDIO_MODE:
+                handleSendSystemAudioModeRequestTimeout();
+                return;
+            case STATE_WAIT_FOR_REPORT_AUDIO_STATUS:
+                handleSendGiveAudioStatusFailure();
+                return;
+        }
+    }
+}
diff --git a/services/core/java/com/android/server/hdmi/SystemAudioActionFromAvr.java b/services/core/java/com/android/server/hdmi/SystemAudioActionFromAvr.java
new file mode 100644
index 0000000..c5eb44b
--- /dev/null
+++ b/services/core/java/com/android/server/hdmi/SystemAudioActionFromAvr.java
@@ -0,0 +1,69 @@
+/*
+ * 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;
+
+/**
+ * Feature action that handles System Audio initiated by AVR devices.
+ */
+final class SystemAudioActionFromAvr extends SystemAudioAction {
+    /**
+     * Constructor
+     *
+     * @param service {@link HdmiControlService} instance
+     * @param tvAddress logical address of TV device
+     * @param avrAddress logical address of AVR device
+     * @param targetStatus Whether to enable the system audio mode or not
+     * @throw IllegalArugmentException if device type of tvAddress and avrAddress is invalid
+     */
+    SystemAudioActionFromAvr(HdmiControlService service, int tvAddress, int avrAddress,
+            boolean targetStatus) {
+        super(service, tvAddress, avrAddress, targetStatus);
+        HdmiUtils.verifyAddressType(tvAddress, HdmiCec.DEVICE_TV);
+    }
+
+    @Override
+    boolean start() {
+        removeSystemAudioActionInProgress();
+        handleSystemAudioActionFromAvr();
+        return true;
+    }
+
+    private void handleSystemAudioActionFromAvr() {
+        if (mTargetAudioStatus == mService.getSystemAudioMode()) {
+            finish();
+            return;
+        }
+        if (mService.isInPresetInstallationMode()) {
+            sendCommand(HdmiCecMessageBuilder.buildFeatureAbortCommand(
+                    mSourceAddress, mAvrLogicalAddress,
+                    HdmiCec.MESSAGE_SET_SYSTEM_AUDIO_MODE, HdmiConstants.ABORT_REFUSED));
+            mTargetAudioStatus = false;
+            sendSystemAudioModeRequest();
+            return;
+        }
+        // TODO: Stop the action for System Audio Mode initialization if it is running.
+        if (mTargetAudioStatus) {
+            setSystemAudioMode(true);
+            sendGiveAudioStatus();
+        } else {
+            setSystemAudioMode(false);
+            finish();
+        }
+    }
+}
diff --git a/services/core/java/com/android/server/hdmi/SystemAudioActionFromTv.java b/services/core/java/com/android/server/hdmi/SystemAudioActionFromTv.java
new file mode 100644
index 0000000..9994de6
--- /dev/null
+++ b/services/core/java/com/android/server/hdmi/SystemAudioActionFromTv.java
@@ -0,0 +1,50 @@
+/*
+ * 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;
+
+
+/**
+ * Feature action that handles System Audio initiated by TV devices.
+ */
+final class SystemAudioActionFromTv extends SystemAudioAction {
+    /**
+     * Constructor
+     *
+     * @param service {@link HdmiControlService} instance
+     * @param tvAddress logical address of TV device
+     * @param avrAddress logical address of AVR device
+     * @param targetStatus Whether to enable the system audio mode or not
+     * @throw IllegalArugmentException if device type of tvAddress is invalid
+     */
+    SystemAudioActionFromTv(HdmiControlService service, int tvAddress, int avrAddress,
+            boolean targetStatus) {
+        super(service, tvAddress, avrAddress, targetStatus);
+        HdmiUtils.verifyAddressType(tvAddress, HdmiCec.DEVICE_TV);
+    }
+
+    @Override
+    boolean start() {
+        // TODO: Check HDMI-CEC is enabled.
+        // TODO: Move to the waiting state if currently a routing change is in progress.
+
+        removeSystemAudioActionInProgress();
+        sendSystemAudioModeRequest();
+        return true;
+    }
+}
diff --git a/services/core/java/com/android/server/location/FlpHardwareProvider.java b/services/core/java/com/android/server/location/FlpHardwareProvider.java
index 51ee93b..2699fea 100644
--- a/services/core/java/com/android/server/location/FlpHardwareProvider.java
+++ b/services/core/java/com/android/server/location/FlpHardwareProvider.java
@@ -96,7 +96,7 @@
                 Looper.myLooper());
     }
 
-    public boolean isSupported() {
+    public static boolean isSupported() {
         return nativeIsSupported();
     }
 
diff --git a/services/core/java/com/android/server/media/MediaSessionRecord.java b/services/core/java/com/android/server/media/MediaSessionRecord.java
index 030e3ed..737ffda 100644
--- a/services/core/java/com/android/server/media/MediaSessionRecord.java
+++ b/services/core/java/com/android/server/media/MediaSessionRecord.java
@@ -25,6 +25,7 @@
 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;
@@ -33,6 +34,7 @@
 import android.media.session.MediaSessionInfo;
 import android.media.session.RouteInterface;
 import android.media.session.PlaybackState;
+import android.media.AudioManager;
 import android.media.MediaMetadata;
 import android.media.Rating;
 import android.os.Bundle;
@@ -66,13 +68,13 @@
      * These are the playback states that count as currently active.
      */
     private static final int[] ACTIVE_STATES = {
-            PlaybackState.PLAYSTATE_FAST_FORWARDING,
-            PlaybackState.PLAYSTATE_REWINDING,
-            PlaybackState.PLAYSTATE_SKIPPING_BACKWARDS,
-            PlaybackState.PLAYSTATE_SKIPPING_FORWARDS,
-            PlaybackState.PLAYSTATE_BUFFERING,
-            PlaybackState.PLAYSTATE_CONNECTING,
-            PlaybackState.PLAYSTATE_PLAYING };
+            PlaybackState.STATE_FAST_FORWARDING,
+            PlaybackState.STATE_REWINDING,
+            PlaybackState.STATE_SKIPPING_TO_PREVIOUS,
+            PlaybackState.STATE_SKIPPING_TO_NEXT,
+            PlaybackState.STATE_BUFFERING,
+            PlaybackState.STATE_CONNECTING,
+            PlaybackState.STATE_PLAYING };
 
     /**
      * The length of time a session will still be considered active after
@@ -112,6 +114,14 @@
     private long mLastActiveTime;
     // End TransportPerformer fields
 
+    // Volume handling fields
+    private int mPlaybackType = MediaSession.VOLUME_TYPE_LOCAL;
+    private int mAudioStream = AudioManager.STREAM_MUSIC;
+    private int mVolumeControlType = RemoteVolumeProvider.VOLUME_CONTROL_ABSOLUTE;
+    private int mMaxVolume = 0;
+    private int mCurrentVolume = 0;
+    // End volume handling fields
+
     private boolean mIsActive = false;
     private boolean mDestroyed = false;
 
@@ -248,6 +258,27 @@
     }
 
     /**
+     * Send a volume adjustment to the session owner.
+     *
+     * @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;
+        }
+        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;
+        }
+        mSessionCb.setVolumeTo(value);
+    }
+
+    /**
      * Set the connection to use for the selected route and notify the app it is
      * now connected.
      *
@@ -294,14 +325,16 @@
      * Check if the session is currently performing playback. This will also
      * return true if the session was recently paused.
      *
+     * @param includeRecentlyActive True if playback that was recently paused
+     *            should count, false if it shouldn't.
      * @return True if the session is performing playback, false otherwise.
      */
-    public boolean isPlaybackActive() {
+    public boolean isPlaybackActive(boolean includeRecentlyActive) {
         int state = mPlaybackState == null ? 0 : mPlaybackState.getState();
         if (isActiveState(state)) {
             return true;
         }
-        if (state == mPlaybackState.PLAYSTATE_PAUSED) {
+        if (includeRecentlyActive && state == mPlaybackState.STATE_PAUSED) {
             long inactiveTime = SystemClock.uptimeMillis() - mLastActiveTime;
             if (inactiveTime < ACTIVE_BUFFER) {
                 return true;
@@ -311,6 +344,54 @@
     }
 
     /**
+     * Get the type of playback, either local or remote.
+     *
+     * @return The current type of playback.
+     */
+    public int getPlaybackType() {
+        return mPlaybackType;
+    }
+
+    /**
+     * Get the local audio stream being used. Only valid if playback type is
+     * local.
+     *
+     * @return The audio stream the session is using.
+     */
+    public int getAudioStream() {
+        return mAudioStream;
+    }
+
+    /**
+     * Get the type of volume control. Only valid if playback type is remote.
+     *
+     * @return The volume control type being used.
+     */
+    public int getVolumeControl() {
+        return mVolumeControlType;
+    }
+
+    /**
+     * Get the max volume that can be set. Only valid if playback type is
+     * remote.
+     *
+     * @return The max volume that can be set.
+     */
+    public int getMaxVolume() {
+        return mMaxVolume;
+    }
+
+    /**
+     * Get the current volume for this session. Only valid if playback type is
+     * remote.
+     *
+     * @return The current volume of the remote playback.
+     */
+    public int getCurrentVolume() {
+        return mCurrentVolume;
+    }
+
+    /**
      * @return True if this session is currently connected to a route.
      */
     public boolean isConnected() {
@@ -509,12 +590,12 @@
         }
         PlaybackState result = null;
         if (state != null) {
-            if (state.getState() == PlaybackState.PLAYSTATE_PLAYING
-                    || state.getState() == PlaybackState.PLAYSTATE_FAST_FORWARDING
-                    || state.getState() == PlaybackState.PLAYSTATE_REWINDING) {
+            if (state.getState() == PlaybackState.STATE_PLAYING
+                    || state.getState() == PlaybackState.STATE_FAST_FORWARDING
+                    || state.getState() == PlaybackState.STATE_REWINDING) {
                 long updateTime = state.getLastPositionUpdateTime();
                 if (updateTime > 0) {
-                    long position = (long) (state.getRate()
+                    long position = (long) (state.getPlaybackRate()
                             * (SystemClock.elapsedRealtime() - updateTime)) + state.getPosition();
                     if (duration >= 0 && position > duration) {
                         position = duration;
@@ -522,7 +603,7 @@
                         position = 0;
                     }
                     result = new PlaybackState(state);
-                    result.setState(state.getState(), position, state.getRate());
+                    result.setState(state.getState(), position, state.getPlaybackRate());
                 }
             }
         }
@@ -588,7 +669,7 @@
         public void setPlaybackState(PlaybackState state) {
             int oldState = mPlaybackState == null ? 0 : mPlaybackState.getState();
             int newState = state == null ? 0 : state.getState();
-            if (isActiveState(oldState) && newState == PlaybackState.PLAYSTATE_PAUSED) {
+            if (isActiveState(oldState) && newState == PlaybackState.STATE_PAUSED) {
                 mLastActiveTime = SystemClock.elapsedRealtime();
             }
             mPlaybackState = state;
@@ -640,6 +721,40 @@
                 mRequests.add(request);
             }
         }
+
+        @Override
+        public void setCurrentVolume(int volume) {
+            mCurrentVolume = volume;
+        }
+
+        @Override
+        public void configureVolumeHandling(int type, int arg1, int arg2) throws RemoteException {
+            switch(type) {
+                case MediaSession.VOLUME_TYPE_LOCAL:
+                    mPlaybackType = type;
+                    int audioStream = arg1;
+                    if (isValidStream(audioStream)) {
+                        mAudioStream = audioStream;
+                    } else {
+                        Log.e(TAG, "Cannot set stream to " + audioStream + ". Using music stream");
+                        mAudioStream = AudioManager.STREAM_MUSIC;
+                    }
+                    break;
+                case MediaSession.VOLUME_TYPE_REMOTE:
+                    mPlaybackType = type;
+                    mVolumeControlType = arg1;
+                    mMaxVolume = arg2;
+                    break;
+                default:
+                    throw new IllegalArgumentException("Volume handling type " + type
+                            + " not recognized.");
+            }
+        }
+
+        private boolean isValidStream(int stream) {
+            return stream >= AudioManager.STREAM_VOICE_CALL
+                    && stream <= AudioManager.STREAM_NOTIFICATION;
+        }
     }
 
     class SessionCb {
@@ -649,14 +764,16 @@
             mCb = cb;
         }
 
-        public void sendMediaButton(KeyEvent keyEvent, int sequenceId, ResultReceiver cb) {
+        public boolean sendMediaButton(KeyEvent keyEvent, int sequenceId, ResultReceiver cb) {
             Intent mediaButtonIntent = new Intent(Intent.ACTION_MEDIA_BUTTON);
             mediaButtonIntent.putExtra(Intent.EXTRA_KEY_EVENT, keyEvent);
             try {
                 mCb.onMediaButton(mediaButtonIntent, sequenceId, cb);
+                return true;
             } catch (RemoteException e) {
                 Slog.e(TAG, "Remote failure in sendMediaRequest.", e);
             }
+            return false;
         }
 
         public void sendCommand(String command, Bundle extras, ResultReceiver cb) {
@@ -778,6 +895,22 @@
                 Slog.e(TAG, "Remote failure in rate.", e);
             }
         }
+
+        public void adjustVolumeBy(int delta) {
+            try {
+                mCb.onAdjustVolumeBy(delta);
+            } catch (RemoteException e) {
+                Slog.e(TAG, "Remote failure in adjustVolumeBy.", e);
+            }
+        }
+
+        public void setVolumeTo(int value) {
+            try {
+                mCb.onSetVolumeTo(value);
+            } catch (RemoteException e) {
+                Slog.e(TAG, "Remote failure in adjustVolumeBy.", e);
+            }
+        }
     }
 
     class ControllerStub extends ISessionController.Stub {
@@ -788,8 +921,8 @@
         }
 
         @Override
-        public void sendMediaButton(KeyEvent mediaButtonIntent) {
-            mSessionCb.sendMediaButton(mediaButtonIntent, 0, null);
+        public boolean sendMediaButton(KeyEvent mediaButtonIntent) {
+            return mSessionCb.sendMediaButton(mediaButtonIntent, 0, null);
         }
 
         @Override
diff --git a/services/core/java/com/android/server/media/MediaSessionService.java b/services/core/java/com/android/server/media/MediaSessionService.java
index 9d85167..87665e1 100644
--- a/services/core/java/com/android/server/media/MediaSessionService.java
+++ b/services/core/java/com/android/server/media/MediaSessionService.java
@@ -26,6 +26,8 @@
 import android.content.Context;
 import android.content.Intent;
 import android.content.pm.PackageManager;
+import android.media.AudioManager;
+import android.media.IAudioService;
 import android.media.routeprovider.RouteRequest;
 import android.media.session.ISession;
 import android.media.session.ISessionCallback;
@@ -40,6 +42,7 @@
 import android.os.PowerManager;
 import android.os.RemoteException;
 import android.os.ResultReceiver;
+import android.os.ServiceManager;
 import android.os.UserHandle;
 import android.provider.Settings;
 import android.speech.RecognizerIntent;
@@ -79,6 +82,7 @@
     private final PowerManager.WakeLock mMediaEventWakeLock;
 
     private KeyguardManager mKeyguardManager;
+    private IAudioService mAudioService;
 
     private MediaSessionRecord mPrioritySession;
     private int mCurrentUserId = -1;
@@ -105,6 +109,12 @@
         updateUser();
         mKeyguardManager =
                 (KeyguardManager) getContext().getSystemService(Context.KEYGUARD_SERVICE);
+        mAudioService = getAudioService();
+    }
+
+    private IAudioService getAudioService() {
+        IBinder b = ServiceManager.getService(Context.AUDIO_SERVICE);
+        return IAudioService.Stub.asInterface(b);
     }
 
     /**
@@ -703,6 +713,23 @@
         }
 
         @Override
+        public void dispatchAdjustVolumeBy(int suggestedStream, int delta, int flags)
+                throws RemoteException {
+            final int pid = Binder.getCallingPid();
+            final int uid = Binder.getCallingUid();
+            final long token = Binder.clearCallingIdentity();
+            try {
+                synchronized (mLock) {
+                    MediaSessionRecord session = mPriorityStack
+                            .getDefaultVolumeSession(mCurrentUserId);
+                    dispatchAdjustVolumeByLocked(suggestedStream, delta, flags, session);
+                }
+            } finally {
+                Binder.restoreCallingIdentity(token);
+            }
+        }
+
+        @Override
         public void dump(FileDescriptor fd, final PrintWriter pw, String[] args) {
             if (getContext().checkCallingOrSelfPermission(Manifest.permission.DUMP)
                     != PackageManager.PERMISSION_GRANTED) {
@@ -737,6 +764,49 @@
             }
         }
 
+        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
+                        + ", suggestedStream=" + suggestedStream);
+
+            }
+            if (session == null) {
+                for (int i = 0; i < steps; i++) {
+                    try {
+                        mAudioService.adjustSuggestedStreamVolume(direction, suggestedStream,
+                                flags, getContext().getOpPackageName());
+                    } catch (RemoteException e) {
+                        Log.e(TAG, "Error adjusting default volume.", e);
+                    }
+                }
+            } else {
+                if (session.getPlaybackType() == MediaSession.VOLUME_TYPE_LOCAL) {
+                    for (int i = 0; i < steps; i++) {
+                        try {
+                            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);
+                }
+            }
+        }
+
         private void handleVoiceKeyEventLocked(KeyEvent keyEvent, boolean needWakeLock,
                 MediaSessionRecord session) {
             if (session != null && session.hasFlag(MediaSession.FLAG_EXCLUSIVE_GLOBAL_PRIORITY)) {
diff --git a/services/core/java/com/android/server/media/MediaSessionStack.java b/services/core/java/com/android/server/media/MediaSessionStack.java
index 7ba9212..803dee2 100644
--- a/services/core/java/com/android/server/media/MediaSessionStack.java
+++ b/services/core/java/com/android/server/media/MediaSessionStack.java
@@ -33,18 +33,18 @@
      * bump priority regardless of the old state.
      */
     private static final int[] ALWAYS_PRIORITY_STATES = {
-            PlaybackState.PLAYSTATE_FAST_FORWARDING,
-            PlaybackState.PLAYSTATE_REWINDING,
-            PlaybackState.PLAYSTATE_SKIPPING_BACKWARDS,
-            PlaybackState.PLAYSTATE_SKIPPING_FORWARDS };
+            PlaybackState.STATE_FAST_FORWARDING,
+            PlaybackState.STATE_REWINDING,
+            PlaybackState.STATE_SKIPPING_TO_PREVIOUS,
+            PlaybackState.STATE_SKIPPING_TO_NEXT };
     /**
      * These are states that usually indicate the user took an action if they
      * were entered from a non-priority state.
      */
     private static final int[] TRANSITION_PRIORITY_STATES = {
-            PlaybackState.PLAYSTATE_BUFFERING,
-            PlaybackState.PLAYSTATE_CONNECTING,
-            PlaybackState.PLAYSTATE_PLAYING };
+            PlaybackState.STATE_BUFFERING,
+            PlaybackState.STATE_CONNECTING,
+            PlaybackState.STATE_PLAYING };
 
     private final ArrayList<MediaSessionRecord> mSessions = new ArrayList<MediaSessionRecord>();
 
@@ -52,6 +52,7 @@
 
     private MediaSessionRecord mCachedButtonReceiver;
     private MediaSessionRecord mCachedDefault;
+    private MediaSessionRecord mCachedVolumeDefault;
     private ArrayList<MediaSessionRecord> mCachedActiveList;
     private ArrayList<MediaSessionRecord> mCachedTransportControlList;
 
@@ -93,6 +94,9 @@
             mSessions.remove(record);
             mSessions.add(0, record);
             clearCache();
+        } else if (newState == PlaybackState.STATE_PAUSED) {
+            // Just clear the volume cache in this case
+            mCachedVolumeDefault = null;
         }
     }
 
@@ -177,6 +181,25 @@
         return mCachedButtonReceiver;
     }
 
+    public MediaSessionRecord getDefaultVolumeSession(int userId) {
+        if (mGlobalPrioritySession != null && mGlobalPrioritySession.isActive()) {
+            return mGlobalPrioritySession;
+        }
+        if (mCachedVolumeDefault != null) {
+            return mCachedVolumeDefault;
+        }
+        ArrayList<MediaSessionRecord> records = getPriorityListLocked(true, 0, userId);
+        int size = records.size();
+        for (int i = 0; i < size; i++) {
+            MediaSessionRecord record = records.get(i);
+            if (record.isPlaybackActive(false)) {
+                mCachedVolumeDefault = record;
+                return record;
+            }
+        }
+        return null;
+    }
+
     public void dump(PrintWriter pw, String prefix) {
         ArrayList<MediaSessionRecord> sortedSessions = getPriorityListLocked(false, 0,
                 UserHandle.USER_ALL);
@@ -237,7 +260,7 @@
                 lastLocalIndex++;
                 lastActiveIndex++;
                 lastPublishedIndex++;
-            } else if (session.isPlaybackActive()) {
+            } else if (session.isPlaybackActive(true)) {
                 // TODO replace getRoute() == null with real local route check
                 if(session.getRoute() == null) {
                     // Active local sessions get top priority
@@ -284,6 +307,7 @@
 
     private void clearCache() {
         mCachedDefault = null;
+        mCachedVolumeDefault = null;
         mCachedButtonReceiver = null;
         mCachedActiveList = null;
         mCachedTransportControlList = null;
diff --git a/services/core/java/com/android/server/notification/ManagedServices.java b/services/core/java/com/android/server/notification/ManagedServices.java
index b94ea62..1b1fc8b 100644
--- a/services/core/java/com/android/server/notification/ManagedServices.java
+++ b/services/core/java/com/android/server/notification/ManagedServices.java
@@ -259,13 +259,17 @@
                     userIds[i]));
         }
 
-        ManagedServiceInfo[] toRemove = new ManagedServiceInfo[mServices.size()];
+        ArrayList<ManagedServiceInfo> toRemove = new ArrayList<ManagedServiceInfo>();
         final SparseArray<ArrayList<ComponentName>> toAdd
                 = new SparseArray<ArrayList<ComponentName>>();
 
         synchronized (mMutex) {
-            // unbind and remove all existing services
-            toRemove = mServices.toArray(toRemove);
+            // Unbind automatically bound services, retain system services.
+            for (ManagedServiceInfo service : mServices) {
+                if (!service.isSystem) {
+                    toRemove.add(service);
+                }
+            }
 
             final ArraySet<ComponentName> newEnabled = new ArraySet<ComponentName>();
             final ArraySet<String> newPackages = new ArraySet<String>();
diff --git a/services/core/java/com/android/server/notification/NotificationComparator.java b/services/core/java/com/android/server/notification/NotificationComparator.java
index 49293d3..b30baea 100644
--- a/services/core/java/com/android/server/notification/NotificationComparator.java
+++ b/services/core/java/com/android/server/notification/NotificationComparator.java
@@ -21,11 +21,10 @@
  * Sorts notificaitons into attention-relelvant order.
  */
 public class NotificationComparator
-        implements Comparator<NotificationManagerService.NotificationRecord> {
+        implements Comparator<NotificationRecord> {
 
     @Override
-    public int compare(NotificationManagerService.NotificationRecord lhs,
-            NotificationManagerService.NotificationRecord rhs) {
+    public int compare(NotificationRecord lhs, NotificationRecord rhs) {
         if (lhs.isRecentlyIntrusive() != rhs.isRecentlyIntrusive()) {
             return lhs.isRecentlyIntrusive() ? -1 : 1;
         }
diff --git a/services/core/java/com/android/server/notification/NotificationIntrusivenessExtractor.java b/services/core/java/com/android/server/notification/NotificationIntrusivenessExtractor.java
index db17f3a..d8ab9d7 100644
--- a/services/core/java/com/android/server/notification/NotificationIntrusivenessExtractor.java
+++ b/services/core/java/com/android/server/notification/NotificationIntrusivenessExtractor.java
@@ -20,8 +20,6 @@
 import android.content.Context;
 import android.util.Slog;
 
-import com.android.server.notification.NotificationManagerService.NotificationRecord;
-
 /**
  * This {@link com.android.server.notification.NotificationSignalExtractor} noticies noisy
  * notifications and marks them to get a temporary ranking bump.
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index bbc3091..cb78a45 100644
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -43,7 +43,6 @@
 import android.content.pm.ParceledListSlice;
 import android.content.res.Resources;
 import android.database.ContentObserver;
-import android.graphics.Bitmap;
 import android.media.AudioManager;
 import android.media.IRingtonePlayer;
 import android.net.Uri;
@@ -60,13 +59,13 @@
 import android.os.UserHandle;
 import android.os.Vibrator;
 import android.provider.Settings;
-import android.service.notification.INotificationListener;
+import android.service.notification.Condition;
 import android.service.notification.IConditionListener;
 import android.service.notification.IConditionProvider;
+import android.service.notification.INotificationListener;
 import android.service.notification.NotificationListenerService;
 import android.service.notification.NotificationRankingUpdate;
 import android.service.notification.StatusBarNotification;
-import android.service.notification.Condition;
 import android.service.notification.ZenModeConfig;
 import android.telephony.TelephonyManager;
 import android.text.TextUtils;
@@ -87,7 +86,6 @@
 import com.android.server.lights.LightsManager;
 import com.android.server.notification.ManagedServices.ManagedServiceInfo;
 import com.android.server.notification.ManagedServices.UserProfiles;
-import com.android.server.notification.NotificationUsageStats.SingleNotificationStats;
 import com.android.server.statusbar.StatusBarManagerInternal;
 
 import libcore.io.IoUtils;
@@ -103,10 +101,8 @@
 import java.io.FileOutputStream;
 import java.io.IOException;
 import java.io.PrintWriter;
-import java.lang.reflect.Array;
 import java.util.ArrayDeque;
 import java.util.ArrayList;
-import java.util.Arrays;
 import java.util.Collections;
 import java.util.HashSet;
 import java.util.Iterator;
@@ -211,8 +207,6 @@
     private ConditionProviders mConditionProviders;
     private NotificationUsageStats mUsageStats;
 
-    private static final String EXTRA_INTERCEPT = "android.intercept";
-
     private static final int MY_UID = Process.myUid();
     private static final int MY_PID = Process.myPid();
     private static final int REASON_DELEGATE_CLICK = 1;
@@ -425,144 +419,6 @@
         return true;
     }
 
-    private static String idDebugString(Context baseContext, String packageName, int id) {
-        Context c = null;
-
-        if (packageName != null) {
-            try {
-                c = baseContext.createPackageContext(packageName, 0);
-            } catch (NameNotFoundException e) {
-                c = baseContext;
-            }
-        } else {
-            c = baseContext;
-        }
-
-        String pkg;
-        String type;
-        String name;
-
-        Resources r = c.getResources();
-        try {
-            return r.getResourceName(id);
-        } catch (Resources.NotFoundException e) {
-            return "<name unknown>";
-        }
-    }
-
-
-
-    public static final class NotificationRecord {
-        final StatusBarNotification sbn;
-        SingleNotificationStats stats;
-        boolean isCanceled;
-
-        // These members are used by NotificationSignalExtractors
-        // to communicate with the ranking module.
-        private float mContactAffinity;
-        private boolean mRecentlyIntrusive;
-
-        NotificationRecord(StatusBarNotification sbn)
-        {
-            this.sbn = sbn;
-        }
-
-        public Notification getNotification() { return sbn.getNotification(); }
-        public int getFlags() { return sbn.getNotification().flags; }
-        public int getUserId() { return sbn.getUserId(); }
-        public String getKey() { return sbn.getKey(); }
-
-        void dump(PrintWriter pw, String prefix, Context baseContext) {
-            final Notification notification = sbn.getNotification();
-            pw.println(prefix + this);
-            pw.println(prefix + "  uid=" + sbn.getUid() + " userId=" + sbn.getUserId());
-            pw.println(prefix + "  icon=0x" + Integer.toHexString(notification.icon)
-                    + " / " + idDebugString(baseContext, sbn.getPackageName(), notification.icon));
-            pw.println(prefix + "  pri=" + notification.priority + " score=" + sbn.getScore());
-            pw.println(prefix + "  key=" + sbn.getKey());
-            pw.println(prefix + "  contentIntent=" + notification.contentIntent);
-            pw.println(prefix + "  deleteIntent=" + notification.deleteIntent);
-            pw.println(prefix + "  tickerText=" + notification.tickerText);
-            pw.println(prefix + "  contentView=" + notification.contentView);
-            pw.println(prefix + String.format("  defaults=0x%08x flags=0x%08x",
-                    notification.defaults, notification.flags));
-            pw.println(prefix + "  sound=" + notification.sound);
-            pw.println(prefix + String.format("  color=0x%08x", notification.color));
-            pw.println(prefix + "  vibrate=" + Arrays.toString(notification.vibrate));
-            pw.println(prefix + String.format("  led=0x%08x onMs=%d offMs=%d",
-                    notification.ledARGB, notification.ledOnMS, notification.ledOffMS));
-            if (notification.actions != null && notification.actions.length > 0) {
-                pw.println(prefix + "  actions={");
-                final int N = notification.actions.length;
-                for (int i=0; i<N; i++) {
-                    final Notification.Action action = notification.actions[i];
-                    pw.println(String.format("%s    [%d] \"%s\" -> %s",
-                            prefix,
-                            i,
-                            action.title,
-                            action.actionIntent.toString()
-                            ));
-                }
-                pw.println(prefix + "  }");
-            }
-            if (notification.extras != null && notification.extras.size() > 0) {
-                pw.println(prefix + "  extras={");
-                for (String key : notification.extras.keySet()) {
-                    pw.print(prefix + "    " + key + "=");
-                    Object val = notification.extras.get(key);
-                    if (val == null) {
-                        pw.println("null");
-                    } else {
-                        pw.print(val.getClass().getSimpleName());
-                        if (val instanceof CharSequence || val instanceof String) {
-                            // redact contents from bugreports
-                        } else if (val instanceof Bitmap) {
-                            pw.print(String.format(" (%dx%d)",
-                                    ((Bitmap) val).getWidth(),
-                                    ((Bitmap) val).getHeight()));
-                        } else if (val.getClass().isArray()) {
-                            final int N = Array.getLength(val);
-                            pw.println(" (" + N + ")");
-                        } else {
-                            pw.print(" (" + String.valueOf(val) + ")");
-                        }
-                        pw.println();
-                    }
-                }
-                pw.println(prefix + "  }");
-            }
-            pw.println(prefix + "  stats=" + stats.toString());
-            pw.println(prefix + "  mContactAffinity=" + mContactAffinity);
-            pw.println(prefix + "  mRecentlyIntrusive=" + mRecentlyIntrusive);
-        }
-
-        @Override
-        public final String toString() {
-            return String.format(
-                    "NotificationRecord(0x%08x: pkg=%s user=%s id=%d tag=%s score=%d key=%s: %s)",
-                    System.identityHashCode(this),
-                    this.sbn.getPackageName(), this.sbn.getUser(), this.sbn.getId(),
-                    this.sbn.getTag(), this.sbn.getScore(), this.sbn.getKey(),
-                    this.sbn.getNotification());
-        }
-
-        public void setContactAffinity(float contactAffinity) {
-            mContactAffinity = contactAffinity;
-        }
-
-        public float getContactAffinity() {
-            return mContactAffinity;
-        }
-
-        public void setRecentlyIntusive(boolean recentlyIntrusive) {
-            mRecentlyIntrusive = recentlyIntrusive;
-        }
-
-        public boolean isRecentlyIntrusive() {
-            return mRecentlyIntrusive;
-        }
-    }
-
     private static final class ToastRecord
     {
         final int pid;
@@ -1657,15 +1513,15 @@
                     return;
                 }
 
-                // Is this notification intercepted by zen mode?
-                final boolean intercept = mZenModeHelper.shouldIntercept(pkg, notification);
-                notification.extras.putBoolean(EXTRA_INTERCEPT, intercept);
-
-                // Should this notification make noise, vibe, or use the LED?
-                final boolean canInterrupt = (score >= SCORE_INTERRUPTION_THRESHOLD) && !intercept;
-                if (DBG || intercept) Slog.v(TAG,
-                        "pkg=" + pkg + " canInterrupt=" + canInterrupt + " intercept=" + intercept);
                 synchronized (mNotificationList) {
+                    applyZenModeLocked(r);
+
+                    // Should this notification make noise, vibe, or use the LED?
+                    final boolean canInterrupt = (score >= SCORE_INTERRUPTION_THRESHOLD) &&
+                            !r.isIntercepted();
+                    if (DBG || r.isIntercepted()) Slog.v(TAG,
+                            "pkg=" + pkg + " canInterrupt=" + canInterrupt +
+                                    " intercept=" + r.isIntercepted());
                     NotificationRecord old = null;
                     int index = indexOfNotificationLocked(n.getKey());
                     if (index < 0) {
@@ -1678,6 +1534,8 @@
                         // Make sure we don't lose the foreground service state.
                         notification.flags |=
                             old.getNotification().flags & Notification.FLAG_FOREGROUND_SERVICE;
+                        // Retain ranking information from previous record
+                        r.copyRankingInformation(old);
                         mNotificationsByKey.remove(old.sbn.getKey());
                     }
                     mNotificationsByKey.put(n.getKey(), r);
@@ -1724,7 +1582,7 @@
                             sendAccessibilityEvent(notification, pkg);
                         }
 
-                        mListeners.notifyPostedLocked(r.sbn, cloneNotificationListLocked());
+                        mListeners.notifyPostedLocked(r.sbn);
                     } else {
                         Slog.e(TAG, "Not posting notification with icon==0: " + notification);
                         if (old != null && !old.isCanceled) {
@@ -1735,7 +1593,7 @@
                                 Binder.restoreCallingIdentity(identity);
                             }
 
-                            mListeners.notifyRemovedLocked(r.sbn, cloneNotificationListLocked());
+                            mListeners.notifyRemovedLocked(r.sbn);
                         }
                         // ATTENTION: in a future release we will bail out here
                         // so that we do not play sounds, show lights, etc. for invalid
@@ -1992,23 +1850,32 @@
         if (!(message.obj instanceof RankingReconsideration)) return;
         RankingReconsideration recon = (RankingReconsideration) message.obj;
         recon.run();
-        boolean orderChanged;
+        boolean changed;
         synchronized (mNotificationList) {
             final NotificationRecord record = mNotificationsByKey.get(recon.getKey());
             if (record == null) {
                 return;
             }
-            int before = findNotificationRecordIndexLocked(record);
+            int indexBefore = findNotificationRecordIndexLocked(record);
+            boolean interceptBefore = record.isIntercepted();
             recon.applyChangesLocked(record);
+            applyZenModeLocked(record);
             Collections.sort(mNotificationList, mRankingComparator);
-            int after = findNotificationRecordIndexLocked(record);
-            orderChanged = before != after;
+            int indexAfter = findNotificationRecordIndexLocked(record);
+            boolean interceptAfter = record.isIntercepted();
+            changed = indexBefore != indexAfter || interceptBefore != interceptAfter;
         }
-        if (orderChanged) {
+        if (changed) {
             scheduleSendRankingUpdate();
         }
     }
 
+    // let zen mode evaluate this record and then make note of that for the future
+    private void applyZenModeLocked(NotificationRecord record) {
+        record.setIntercepted(mZenModeHelper.shouldIntercept(record, record.wasTouchedByZen()));
+        record.setTouchedByZen();
+    }
+
     // lock on mNotificationList
     private int findNotificationRecordIndexLocked(NotificationRecord target) {
         return Collections.binarySearch(mNotificationList, target, mRankingComparator);
@@ -2022,19 +1889,10 @@
 
     private void handleSendRankingUpdate() {
         synchronized (mNotificationList) {
-            mListeners.notifyRankingUpdateLocked(cloneNotificationListLocked());
+            mListeners.notifyRankingUpdateLocked();
         }
     }
 
-    private ArrayList<StatusBarNotification> cloneNotificationListLocked() {
-        final int N = mNotificationList.size();
-        ArrayList<StatusBarNotification> sbns = new ArrayList<StatusBarNotification>(N);
-        for (int i = 0; i < N; i++) {
-            sbns.add(mNotificationList.get(i).sbn);
-        }
-        return sbns;
-    }
-
     private final class WorkerHandler extends Handler
     {
         @Override
@@ -2120,7 +1978,7 @@
                 Binder.restoreCallingIdentity(identity);
             }
             r.isCanceled = true;
-            mListeners.notifyRemovedLocked(r.sbn, cloneNotificationListLocked());
+            mListeners.notifyRemovedLocked(r.sbn);
         }
 
         // sound
@@ -2441,22 +2299,25 @@
     /**
      * Generates a NotificationRankingUpdate from 'sbns', considering only
      * notifications visible to the given listener.
+     *
+     * <p>Caller must hold a lock on mNotificationList.</p>
      */
-    private static NotificationRankingUpdate makeRankingUpdateForListener(ManagedServiceInfo info,
-            ArrayList<StatusBarNotification> sbns) {
+    private NotificationRankingUpdate makeRankingUpdateLocked(ManagedServiceInfo info) {
         int speedBumpIndex = -1;
-        ArrayList<String> keys = new ArrayList<String>(sbns.size());
-        ArrayList<String> dndKeys = new ArrayList<String>(sbns.size());
-        for (StatusBarNotification sbn: sbns) {
-            if (!info.enabledAndUserMatches(sbn.getUserId())) {
+        final int N = mNotificationList.size();
+        ArrayList<String> keys = new ArrayList<String>(N);
+        ArrayList<String> dndKeys = new ArrayList<String>(N);
+        for (int i = 0; i < N; i++) {
+            NotificationRecord record = mNotificationList.get(i);
+            if (!info.enabledAndUserMatches(record.sbn.getUserId())) {
                 continue;
             }
-            keys.add(sbn.getKey());
-            if (sbn.getNotification().extras.getBoolean(EXTRA_INTERCEPT)) {
-                dndKeys.add(sbn.getKey());
+            keys.add(record.sbn.getKey());
+            if (record.isIntercepted()) {
+                dndKeys.add(record.sbn.getKey());
             }
             if (speedBumpIndex == -1 &&
-                    sbn.getNotification().priority == Notification.PRIORITY_MIN) {
+                    record.sbn.getNotification().priority == Notification.PRIORITY_MIN) {
                 speedBumpIndex = keys.size() - 1;
             }
         }
@@ -2491,12 +2352,12 @@
         @Override
         public void onServiceAdded(ManagedServiceInfo info) {
             final INotificationListener listener = (INotificationListener) info.service;
-            final ArrayList<StatusBarNotification> sbns;
+            final NotificationRankingUpdate update;
             synchronized (mNotificationList) {
-                sbns = cloneNotificationListLocked();
+                update = makeRankingUpdateLocked(info);
             }
             try {
-                listener.onListenerConnected(makeRankingUpdateForListener(info, sbns));
+                listener.onListenerConnected(update);
             } catch (RemoteException e) {
                 // we tried
             }
@@ -2505,15 +2366,14 @@
         /**
          * asynchronously notify all listeners about a new notification
          */
-        public void notifyPostedLocked(StatusBarNotification sbn,
-                final ArrayList<StatusBarNotification> sbns) {
+        public void notifyPostedLocked(StatusBarNotification sbn) {
             // make a copy in case changes are made to the underlying Notification object
             final StatusBarNotification sbnClone = sbn.clone();
             for (final ManagedServiceInfo info : mServices) {
                 if (!info.isEnabledForCurrentProfiles()) {
                     continue;
                 }
-                final NotificationRankingUpdate update = makeRankingUpdateForListener(info, sbns);
+                final NotificationRankingUpdate update = makeRankingUpdateLocked(info);
                 if (update.getOrderedKeys().length == 0) {
                     continue;
                 }
@@ -2529,8 +2389,7 @@
         /**
          * asynchronously notify all listeners about a removed notification
          */
-        public void notifyRemovedLocked(StatusBarNotification sbn,
-                final ArrayList<StatusBarNotification> sbns) {
+        public void notifyRemovedLocked(StatusBarNotification sbn) {
             // make a copy in case changes are made to the underlying Notification object
             // NOTE: this copy is lightweight: it doesn't include heavyweight parts of the
             // notification
@@ -2539,11 +2398,11 @@
                 if (!info.isEnabledForCurrentProfiles()) {
                     continue;
                 }
+                final NotificationRankingUpdate update = makeRankingUpdateLocked(info);
                 mHandler.post(new Runnable() {
                     @Override
                     public void run() {
-                        notifyRemovedIfUserMatch(info, sbnLight,
-                                makeRankingUpdateForListener(info, sbns));
+                        notifyRemovedIfUserMatch(info, sbnLight, update);
                     }
                 });
             }
@@ -2551,20 +2410,18 @@
 
         /**
          * asynchronously notify all listeners about a reordering of notifications
-         * @param sbns an array of {@link StatusBarNotification}s to consider.  This code
-         *             must not rely on mutable members of these objects, such as the
-         *             {@link Notification}.
          */
-        public void notifyRankingUpdateLocked(final ArrayList<StatusBarNotification> sbns) {
+        public void notifyRankingUpdateLocked() {
             for (final ManagedServiceInfo serviceInfo : mServices) {
                 if (!serviceInfo.isEnabledForCurrentProfiles()) {
                     continue;
                 }
+                final NotificationRankingUpdate update =
+                        makeRankingUpdateLocked(serviceInfo);
                 mHandler.post(new Runnable() {
                     @Override
                     public void run() {
-                        notifyRankingUpdate(serviceInfo,
-                                makeRankingUpdateForListener(serviceInfo, sbns));
+                        notifyRankingUpdate(serviceInfo, update);
                     }
                 });
             }
diff --git a/services/core/java/com/android/server/notification/NotificationRecord.java b/services/core/java/com/android/server/notification/NotificationRecord.java
new file mode 100644
index 0000000..08f8eb4
--- /dev/null
+++ b/services/core/java/com/android/server/notification/NotificationRecord.java
@@ -0,0 +1,204 @@
+/*
+ * 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.notification;
+
+import android.app.Notification;
+import android.content.Context;
+import android.content.pm.PackageManager.NameNotFoundException;
+import android.content.res.Resources;
+import android.graphics.Bitmap;
+import android.service.notification.StatusBarNotification;
+
+import java.io.PrintWriter;
+import java.lang.reflect.Array;
+import java.util.Arrays;
+
+/**
+ * Holds data about notifications that should not be shared with the
+ * {@link android.service.notification.NotificationListenerService}s.
+ *
+ * <p>These objects should not be mutated unless the code is synchronized
+ * on {@link NotificationManagerService#mNotificationList}, and any
+ * modification should be followed by a sorting of that list.</p>
+ *
+ * <p>Is sortable by {@link NotificationComparator}.</p>
+ *
+ * {@hide}
+ */
+public final class NotificationRecord {
+    final StatusBarNotification sbn;
+    NotificationUsageStats.SingleNotificationStats stats;
+    boolean isCanceled;
+
+    // These members are used by NotificationSignalExtractors
+    // to communicate with the ranking module.
+    private float mContactAffinity;
+    private boolean mRecentlyIntrusive;
+
+    // is this notification currently being intercepted by Zen Mode?
+    private boolean mIntercept;
+    // InterceptedNotifications needs to know if this has been previously evaluated.
+    private boolean mTouchedByZen;
+
+    NotificationRecord(StatusBarNotification sbn)
+    {
+        this.sbn = sbn;
+    }
+
+    // copy any notes that the ranking system may have made before the update
+    public void copyRankingInformation(NotificationRecord previous) {
+        mContactAffinity = previous.mContactAffinity;
+        mRecentlyIntrusive = previous.mRecentlyIntrusive;
+        mTouchedByZen = previous.mTouchedByZen;
+        mIntercept = previous.mIntercept;
+    }
+
+    public Notification getNotification() { return sbn.getNotification(); }
+    public int getFlags() { return sbn.getNotification().flags; }
+    public int getUserId() { return sbn.getUserId(); }
+    public String getKey() { return sbn.getKey(); }
+
+    void dump(PrintWriter pw, String prefix, Context baseContext) {
+        final Notification notification = sbn.getNotification();
+        pw.println(prefix + this);
+        pw.println(prefix + "  uid=" + sbn.getUid() + " userId=" + sbn.getUserId());
+        pw.println(prefix + "  icon=0x" + Integer.toHexString(notification.icon)
+                + " / " + idDebugString(baseContext, sbn.getPackageName(), notification.icon));
+        pw.println(prefix + "  pri=" + notification.priority + " score=" + sbn.getScore());
+        pw.println(prefix + "  key=" + sbn.getKey());
+        pw.println(prefix + "  contentIntent=" + notification.contentIntent);
+        pw.println(prefix + "  deleteIntent=" + notification.deleteIntent);
+        pw.println(prefix + "  tickerText=" + notification.tickerText);
+        pw.println(prefix + "  contentView=" + notification.contentView);
+        pw.println(prefix + String.format("  defaults=0x%08x flags=0x%08x",
+                notification.defaults, notification.flags));
+        pw.println(prefix + "  sound=" + notification.sound);
+        pw.println(prefix + String.format("  color=0x%08x", notification.color));
+        pw.println(prefix + "  vibrate=" + Arrays.toString(notification.vibrate));
+        pw.println(prefix + String.format("  led=0x%08x onMs=%d offMs=%d",
+                notification.ledARGB, notification.ledOnMS, notification.ledOffMS));
+        if (notification.actions != null && notification.actions.length > 0) {
+            pw.println(prefix + "  actions={");
+            final int N = notification.actions.length;
+            for (int i=0; i<N; i++) {
+                final Notification.Action action = notification.actions[i];
+                pw.println(String.format("%s    [%d] \"%s\" -> %s",
+                        prefix,
+                        i,
+                        action.title,
+                        action.actionIntent.toString()
+                        ));
+            }
+            pw.println(prefix + "  }");
+        }
+        if (notification.extras != null && notification.extras.size() > 0) {
+            pw.println(prefix + "  extras={");
+            for (String key : notification.extras.keySet()) {
+                pw.print(prefix + "    " + key + "=");
+                Object val = notification.extras.get(key);
+                if (val == null) {
+                    pw.println("null");
+                } else {
+                    pw.print(val.getClass().getSimpleName());
+                    if (val instanceof CharSequence || val instanceof String) {
+                        // redact contents from bugreports
+                    } else if (val instanceof Bitmap) {
+                        pw.print(String.format(" (%dx%d)",
+                                ((Bitmap) val).getWidth(),
+                                ((Bitmap) val).getHeight()));
+                    } else if (val.getClass().isArray()) {
+                        final int N = Array.getLength(val);
+                        pw.println(" (" + N + ")");
+                    } else {
+                        pw.print(" (" + String.valueOf(val) + ")");
+                    }
+                    pw.println();
+                }
+            }
+            pw.println(prefix + "  }");
+        }
+        pw.println(prefix + "  stats=" + stats.toString());
+        pw.println(prefix + "  mContactAffinity=" + mContactAffinity);
+        pw.println(prefix + "  mRecentlyIntrusive=" + mRecentlyIntrusive);
+        pw.println(prefix + "  mIntercept=" + mIntercept);
+    }
+
+
+    static String idDebugString(Context baseContext, String packageName, int id) {
+        Context c;
+
+        if (packageName != null) {
+            try {
+                c = baseContext.createPackageContext(packageName, 0);
+            } catch (NameNotFoundException e) {
+                c = baseContext;
+            }
+        } else {
+            c = baseContext;
+        }
+
+        Resources r = c.getResources();
+        try {
+            return r.getResourceName(id);
+        } catch (Resources.NotFoundException e) {
+            return "<name unknown>";
+        }
+    }
+
+    @Override
+    public final String toString() {
+        return String.format(
+                "NotificationRecord(0x%08x: pkg=%s user=%s id=%d tag=%s score=%d key=%s: %s)",
+                System.identityHashCode(this),
+                this.sbn.getPackageName(), this.sbn.getUser(), this.sbn.getId(),
+                this.sbn.getTag(), this.sbn.getScore(), this.sbn.getKey(),
+                this.sbn.getNotification());
+    }
+
+    public void setContactAffinity(float contactAffinity) {
+        mContactAffinity = contactAffinity;
+    }
+
+    public float getContactAffinity() {
+        return mContactAffinity;
+    }
+
+    public void setRecentlyIntusive(boolean recentlyIntrusive) {
+        mRecentlyIntrusive = recentlyIntrusive;
+    }
+
+    public boolean isRecentlyIntrusive() {
+        return mRecentlyIntrusive;
+    }
+
+    public boolean setIntercepted(boolean intercept) {
+        mIntercept = intercept;
+        return mIntercept;
+    }
+
+    public boolean isIntercepted() {
+        return mIntercept;
+    }
+
+    public boolean wasTouchedByZen() {
+        return mTouchedByZen;
+    }
+
+    public void setTouchedByZen() {
+        mTouchedByZen = true;
+    }
+
+}
diff --git a/services/core/java/com/android/server/notification/NotificationSignalExtractor.java b/services/core/java/com/android/server/notification/NotificationSignalExtractor.java
index 71c819e..1537ea9 100644
--- a/services/core/java/com/android/server/notification/NotificationSignalExtractor.java
+++ b/services/core/java/com/android/server/notification/NotificationSignalExtractor.java
@@ -18,11 +18,9 @@
 
 import android.content.Context;
 
-import com.android.server.notification.NotificationManagerService.NotificationRecord;
-
 /**
  * Extracts signals that will be useful to the {@link NotificationComparator} and caches them
- *  on the {@link NotificationManagerService.NotificationRecord} object. These annotations will
+ *  on the {@link NotificationRecord} object. These annotations will
  *  not be passed on to {@link android.service.notification.NotificationListenerService}s.
  */
 public interface NotificationSignalExtractor {
diff --git a/services/core/java/com/android/server/notification/NotificationUsageStats.java b/services/core/java/com/android/server/notification/NotificationUsageStats.java
index d81a25e..5081bf7 100644
--- a/services/core/java/com/android/server/notification/NotificationUsageStats.java
+++ b/services/core/java/com/android/server/notification/NotificationUsageStats.java
@@ -16,8 +16,6 @@
 
 package com.android.server.notification;
 
-import com.android.server.notification.NotificationManagerService.NotificationRecord;
-
 import android.content.ContentValues;
 import android.content.Context;
 import android.database.Cursor;
@@ -46,12 +44,14 @@
  * {@hide}
  */
 public class NotificationUsageStats {
+    private static final boolean ENABLE_SQLITE_LOG = true;
+
     // Guarded by synchronized(this).
     private final Map<String, AggregatedStats> mStats = new HashMap<String, AggregatedStats>();
     private final SQLiteLog mSQLiteLog;
 
     public NotificationUsageStats(Context context) {
-        mSQLiteLog = new SQLiteLog(context);
+        mSQLiteLog = ENABLE_SQLITE_LOG ? new SQLiteLog(context) : null;
     }
 
     /**
@@ -63,7 +63,9 @@
         for (AggregatedStats stats : getAggregatedStatsLocked(notification)) {
             stats.numPostedByApp++;
         }
-        mSQLiteLog.logPosted(notification);
+        if (ENABLE_SQLITE_LOG) {
+            mSQLiteLog.logPosted(notification);
+        }
     }
 
     /**
@@ -85,7 +87,9 @@
             stats.numRemovedByApp++;
             stats.collect(notification.stats);
         }
-        mSQLiteLog.logRemoved(notification);
+        if (ENABLE_SQLITE_LOG) {
+            mSQLiteLog.logRemoved(notification);
+        }
     }
 
     /**
@@ -97,7 +101,9 @@
             stats.numDismissedByUser++;
             stats.collect(notification.stats);
         }
-        mSQLiteLog.logDismissed(notification);
+        if (ENABLE_SQLITE_LOG) {
+            mSQLiteLog.logDismissed(notification);
+        }
     }
 
     /**
@@ -108,7 +114,9 @@
         for (AggregatedStats stats : getAggregatedStatsLocked(notification)) {
             stats.numClickedByUser++;
         }
-        mSQLiteLog.logClicked(notification);
+        if (ENABLE_SQLITE_LOG) {
+            mSQLiteLog.logClicked(notification);
+        }
     }
 
     /**
@@ -164,7 +172,9 @@
         for (AggregatedStats as : mStats.values()) {
             as.dump(pw, indent);
         }
-        mSQLiteLog.dump(pw, indent);
+        if (ENABLE_SQLITE_LOG) {
+            mSQLiteLog.dump(pw, indent);
+        }
     }
 
     /**
diff --git a/services/core/java/com/android/server/notification/RankingReconsideration.java b/services/core/java/com/android/server/notification/RankingReconsideration.java
index cf5e210..057f0f1 100644
--- a/services/core/java/com/android/server/notification/RankingReconsideration.java
+++ b/services/core/java/com/android/server/notification/RankingReconsideration.java
@@ -15,8 +15,6 @@
  */
 package com.android.server.notification;
 
-import com.android.server.notification.NotificationManagerService.NotificationRecord;
-
 import java.util.concurrent.TimeUnit;
 
 /**
diff --git a/services/core/java/com/android/server/notification/ValidateNotificationPeople.java b/services/core/java/com/android/server/notification/ValidateNotificationPeople.java
index 157d749..4ac2dcc 100644
--- a/services/core/java/com/android/server/notification/ValidateNotificationPeople.java
+++ b/services/core/java/com/android/server/notification/ValidateNotificationPeople.java
@@ -28,14 +28,14 @@
 import android.util.LruCache;
 import android.util.Slog;
 
-import com.android.server.notification.NotificationManagerService.NotificationRecord;
-
 import java.util.ArrayList;
 import java.util.LinkedList;
 
 /**
  * This {@link NotificationSignalExtractor} attempts to validate
  * people references. Also elevates the priority of real people.
+ *
+ * {@hide}
  */
 public class ValidateNotificationPeople implements NotificationSignalExtractor {
     private static final String TAG = "ValidateNotificationPeople";
@@ -49,9 +49,20 @@
     private static final int MAX_PEOPLE = 10;
     private static final int PEOPLE_CACHE_SIZE = 200;
 
-    private static final float NONE = 0f;
-    private static final float VALID_CONTACT = 0.5f;
-    private static final float STARRED_CONTACT = 1f;
+    /** Indicates that the notification does not reference any valid contacts. */
+    static final float NONE = 0f;
+
+    /**
+     * Affinity will be equal to or greater than this value on notifications
+     * that reference a valid contact.
+     */
+    static final float VALID_CONTACT = 0.5f;
+
+    /**
+     * Affinity will be equal to or greater than this value on notifications
+     * that reference a starred contact.
+     */
+    static final float STARRED_CONTACT = 1f;
 
     protected boolean mEnabled;
     private Context mContext;
@@ -138,57 +149,66 @@
         };
     }
 
-    private String[] getExtraPeople(Bundle extras) {
-        String[] people = extras.getStringArray(Notification.EXTRA_PEOPLE);
-        if (people != null) {
-            return people;
+    // VisibleForTesting
+    public static String[] getExtraPeople(Bundle extras) {
+        Object people = extras.get(Notification.EXTRA_PEOPLE);
+        if (people instanceof String[]) {
+            return (String[]) people;
         }
 
-        ArrayList<String> stringArray = extras.getStringArrayList(Notification.EXTRA_PEOPLE);
-        if (stringArray != null) {
-            return (String[]) stringArray.toArray();
+        if (people instanceof ArrayList) {
+            ArrayList arrayList = (ArrayList) people;
+
+            if (arrayList.isEmpty()) {
+                return null;
+            }
+
+            if (arrayList.get(0) instanceof String) {
+                ArrayList<String> stringArray = (ArrayList<String>) arrayList;
+                return stringArray.toArray(new String[stringArray.size()]);
+            }
+
+            if (arrayList.get(0) instanceof CharSequence) {
+                ArrayList<CharSequence> charSeqList = (ArrayList<CharSequence>) arrayList;
+                final int N = charSeqList.size();
+                String[] array = new String[N];
+                for (int i = 0; i < N; i++) {
+                    array[i] = charSeqList.get(i).toString();
+                }
+                return array;
+            }
+
+            return null;
         }
 
-        String string = extras.getString(Notification.EXTRA_PEOPLE);
-        if (string != null) {
-            people = new String[1];
-            people[0] = string;
-            return people;
-        }
-        char[] charArray = extras.getCharArray(Notification.EXTRA_PEOPLE);
-        if (charArray != null) {
-            people = new String[1];
-            people[0] = new String(charArray);
-            return people;
+        if (people instanceof String) {
+            String[] array = new String[1];
+            array[0] = (String) people;
+            return array;
         }
 
-        CharSequence charSeq = extras.getCharSequence(Notification.EXTRA_PEOPLE);
-        if (charSeq != null) {
-            people = new String[1];
-            people[0] = charSeq.toString();
-            return people;
+        if (people instanceof char[]) {
+            String[] array = new String[1];
+            array[0] = new String((char[]) people);
+            return array;
         }
 
-        CharSequence[] charSeqArray = extras.getCharSequenceArray(Notification.EXTRA_PEOPLE);
-        if (charSeqArray != null) {
+        if (people instanceof CharSequence) {
+            String[] array = new String[1];
+            array[0] = ((CharSequence) people).toString();
+            return array;
+        }
+
+        if (people instanceof CharSequence[]) {
+            CharSequence[] charSeqArray = (CharSequence[]) people;
             final int N = charSeqArray.length;
-            people = new String[N];
+            String[] array = new String[N];
             for (int i = 0; i < N; i++) {
-                people[i] = charSeqArray[i].toString();
+                array[i] = charSeqArray[i].toString();
             }
-            return people;
+            return array;
         }
 
-        ArrayList<CharSequence> charSeqList =
-                extras.getCharSequenceArrayList(Notification.EXTRA_PEOPLE);
-        if (charSeqList != null) {
-            final int N = charSeqList.size();
-            people = new String[N];
-            for (int i = 0; i < N; i++) {
-                people[i] = charSeqList.get(i).toString();
-            }
-            return people;
-        }
         return null;
     }
 
diff --git a/services/core/java/com/android/server/notification/ZenModeHelper.java b/services/core/java/com/android/server/notification/ZenModeHelper.java
index 154ac96..50a32c4 100644
--- a/services/core/java/com/android/server/notification/ZenModeHelper.java
+++ b/services/core/java/com/android/server/notification/ZenModeHelper.java
@@ -18,7 +18,6 @@
 
 import android.app.AlarmManager;
 import android.app.AppOpsManager;
-import android.app.Notification;
 import android.app.PendingIntent;
 import android.content.BroadcastReceiver;
 import android.content.ContentResolver;
@@ -78,11 +77,13 @@
     // temporary, until we update apps to provide metadata
     private static final Set<String> CALL_PACKAGES = new HashSet<String>(Arrays.asList(
             "com.google.android.dialer",
-            "com.android.phone"
+            "com.android.phone",
+            "com.android.example.notificationshowcase"
             ));
     private static final Set<String> MESSAGE_PACKAGES = new HashSet<String>(Arrays.asList(
             "com.google.android.talk",
-            "com.android.mms"
+            "com.android.mms",
+            "com.android.example.notificationshowcase"
             ));
     private static final Set<String> ALARM_PACKAGES = new HashSet<String>(Arrays.asList(
             "com.google.android.deskclock"
@@ -123,15 +124,23 @@
         mCallbacks.add(callback);
     }
 
-    public boolean shouldIntercept(String pkg, Notification n) {
+    public boolean shouldIntercept(NotificationRecord record, boolean previouslySeen) {
         if (mZenMode != Global.ZEN_MODE_OFF) {
-            if (isAlarm(pkg, n)) {
+            if (previouslySeen && !record.isIntercepted()) {
+                // notifications never transition from not intercepted to intercepted
                 return false;
             }
-            if (isCall(pkg, n)) {
+            if (isAlarm(record)) {
+                return false;
+            }
+            // audience has veto power over all following rules
+            if (!audienceMatches(record)) {
+                return true;
+            }
+            if (isCall(record)) {
                 return !mConfig.allowCalls;
             }
-            if (isMessage(pkg, n)) {
+            if (isMessage(record)) {
                 return !mConfig.allowMessages;
             }
             return true;
@@ -176,7 +185,8 @@
     }
 
     public boolean allowDisable(int what, IBinder token, String pkg) {
-        if (isCall(pkg, null)) {
+        // TODO(cwren): delete this API before the next release. Bug:15344099
+        if (CALL_PACKAGES.contains(pkg)) {
             return mZenMode == Global.ZEN_MODE_OFF || mConfig.allowCalls;
         }
         return true;
@@ -229,16 +239,30 @@
         }
     }
 
-    private boolean isAlarm(String pkg, Notification n) {
-        return ALARM_PACKAGES.contains(pkg);
+    private boolean isAlarm(NotificationRecord record) {
+        return ALARM_PACKAGES.contains(record.sbn.getPackageName());
     }
 
-    private boolean isCall(String pkg, Notification n) {
-        return CALL_PACKAGES.contains(pkg);
+    private boolean isCall(NotificationRecord record) {
+        return CALL_PACKAGES.contains(record.sbn.getPackageName());
     }
 
-    private boolean isMessage(String pkg, Notification n) {
-        return MESSAGE_PACKAGES.contains(pkg);
+    private boolean isMessage(NotificationRecord record) {
+        return MESSAGE_PACKAGES.contains(record.sbn.getPackageName());
+    }
+
+    private boolean audienceMatches(NotificationRecord record) {
+        switch (mConfig.allowFrom) {
+            case ZenModeConfig.SOURCE_ANYONE:
+                return true;
+            case ZenModeConfig.SOURCE_CONTACT:
+                return record.getContactAffinity() >= ValidateNotificationPeople.VALID_CONTACT;
+            case ZenModeConfig.SOURCE_STAR:
+                return record.getContactAffinity() >= ValidateNotificationPeople.STARRED_CONTACT;
+            default:
+                Slog.w(TAG, "Encountered unknown source: " + mConfig.allowFrom);
+                return true;
+        }
     }
 
     private void updateAlarms() {
diff --git a/services/core/java/com/android/server/pm/LauncherAppsService.java b/services/core/java/com/android/server/pm/LauncherAppsService.java
index 5e3325c..25ebfc0 100644
--- a/services/core/java/com/android/server/pm/LauncherAppsService.java
+++ b/services/core/java/com/android/server/pm/LauncherAppsService.java
@@ -40,6 +40,7 @@
 import android.util.Slog;
 
 import com.android.internal.content.PackageMonitor;
+import com.android.server.SystemService;
 
 import java.util.ArrayList;
 import java.util.List;
@@ -48,358 +49,374 @@
  * Service that manages requests and callbacks for launchers that support
  * managed profiles. 
  */
-public class LauncherAppsService extends ILauncherApps.Stub {
-    private static final boolean DEBUG = false;
-    private static final String TAG = "LauncherAppsService";
-    private final Context mContext;
-    private final PackageManager mPm;
-    private final UserManager mUm;
-    private final PackageCallbackList<IOnAppsChangedListener> mListeners
-            = new PackageCallbackList<IOnAppsChangedListener>();
 
-    private MyPackageMonitor mPackageMonitor = new MyPackageMonitor();
+public class LauncherAppsService extends SystemService {
+
+    private final LauncherAppsImpl mLauncherAppsImpl;
 
     public LauncherAppsService(Context context) {
-        mContext = context;
-        mPm = mContext.getPackageManager();
-        mUm = (UserManager) mContext.getSystemService(Context.USER_SERVICE);
+        super(context);
+        mLauncherAppsImpl = new LauncherAppsImpl(context);
     }
 
-    /*
-     * @see android.content.pm.ILauncherApps#addOnAppsChangedListener(
-     *          android.content.pm.IOnAppsChangedListener)
-     */
     @Override
-    public void addOnAppsChangedListener(IOnAppsChangedListener listener) throws RemoteException {
-        synchronized (mListeners) {
-            if (DEBUG) {
-                Log.d(TAG, "Adding listener from " + Binder.getCallingUserHandle());
-            }
-            if (mListeners.getRegisteredCallbackCount() == 0) {
+    public void onStart() {
+        publishBinderService(Context.LAUNCHER_APPS_SERVICE, mLauncherAppsImpl);
+    }
+
+    class LauncherAppsImpl extends ILauncherApps.Stub {
+        private static final boolean DEBUG = false;
+        private static final String TAG = "LauncherAppsService";
+        private final Context mContext;
+        private final PackageManager mPm;
+        private final UserManager mUm;
+        private final PackageCallbackList<IOnAppsChangedListener> mListeners
+                = new PackageCallbackList<IOnAppsChangedListener>();
+
+        private MyPackageMonitor mPackageMonitor = new MyPackageMonitor();
+
+        public LauncherAppsImpl(Context context) {
+            mContext = context;
+            mPm = mContext.getPackageManager();
+            mUm = (UserManager) mContext.getSystemService(Context.USER_SERVICE);
+        }
+
+        /*
+         * @see android.content.pm.ILauncherApps#addOnAppsChangedListener(
+         *          android.content.pm.IOnAppsChangedListener)
+         */
+        @Override
+        public void addOnAppsChangedListener(IOnAppsChangedListener listener) throws RemoteException {
+            synchronized (mListeners) {
                 if (DEBUG) {
-                    Log.d(TAG, "Starting package monitoring");
+                    Log.d(TAG, "Adding listener from " + Binder.getCallingUserHandle());
                 }
-                startWatchingPackageBroadcasts();
+                if (mListeners.getRegisteredCallbackCount() == 0) {
+                    if (DEBUG) {
+                        Log.d(TAG, "Starting package monitoring");
+                    }
+                    startWatchingPackageBroadcasts();
+                }
+                mListeners.unregister(listener);
+                mListeners.register(listener, Binder.getCallingUserHandle());
             }
-            mListeners.unregister(listener);
-            mListeners.register(listener, Binder.getCallingUserHandle());
         }
-    }
 
-    /*
-     * @see android.content.pm.ILauncherApps#removeOnAppsChangedListener(
-     *          android.content.pm.IOnAppsChangedListener)
-     */
-    @Override
-    public void removeOnAppsChangedListener(IOnAppsChangedListener listener)
-            throws RemoteException {
-        synchronized (mListeners) {
+        /*
+         * @see android.content.pm.ILauncherApps#removeOnAppsChangedListener(
+         *          android.content.pm.IOnAppsChangedListener)
+         */
+        @Override
+        public void removeOnAppsChangedListener(IOnAppsChangedListener listener)
+                throws RemoteException {
+            synchronized (mListeners) {
+                if (DEBUG) {
+                    Log.d(TAG, "Removing listener from " + Binder.getCallingUserHandle());
+                }
+                mListeners.unregister(listener);
+                if (mListeners.getRegisteredCallbackCount() == 0) {
+                    stopWatchingPackageBroadcasts();
+                }
+            }
+        }
+
+        /**
+         * Register a receiver to watch for package broadcasts
+         */
+        private void startWatchingPackageBroadcasts() {
+            mPackageMonitor.register(mContext, null, UserHandle.ALL, true);
+        }
+
+        /**
+         * Unregister package broadcast receiver
+         */
+        private void stopWatchingPackageBroadcasts() {
             if (DEBUG) {
-                Log.d(TAG, "Removing listener from " + Binder.getCallingUserHandle());
+                Log.d(TAG, "Stopped watching for packages");
             }
-            mListeners.unregister(listener);
-            if (mListeners.getRegisteredCallbackCount() == 0) {
-                stopWatchingPackageBroadcasts();
+            mPackageMonitor.unregister();
+        }
+
+        void checkCallbackCount() {
+            synchronized (mListeners) {
+                if (DEBUG) {
+                    Log.d(TAG, "Callback count = " + mListeners.getRegisteredCallbackCount());
+                }
+                if (mListeners.getRegisteredCallbackCount() == 0) {
+                    stopWatchingPackageBroadcasts();
+                }
             }
         }
-    }
 
-    /**
-     * Register a receiver to watch for package broadcasts
-     */
-    private void startWatchingPackageBroadcasts() {
-        mPackageMonitor.register(mContext, null, UserHandle.ALL, true);
-    }
+        /**
+         * Checks if the caller is in the same group as the userToCheck.
+         */
+        private void ensureInUserProfiles(UserHandle userToCheck, String message) {
+            final int callingUserId = UserHandle.getCallingUserId();
+            final int targetUserId = userToCheck.getIdentifier();
 
-    /**
-     * Unregister package broadcast receiver
-     */
-    private void stopWatchingPackageBroadcasts() {
-        if (DEBUG) {
-            Log.d(TAG, "Stopped watching for packages");
-        }
-        mPackageMonitor.unregister();
-    }
+            if (targetUserId == callingUserId) return;
 
-    void checkCallbackCount() {
-        synchronized (mListeners) {
-            if (DEBUG) {
-                Log.d(TAG, "Callback count = " + mListeners.getRegisteredCallbackCount());
-            }
-            if (mListeners.getRegisteredCallbackCount() == 0) {
-                stopWatchingPackageBroadcasts();
-            }
-        }
-    }
-
-    /**
-     * Checks if the caller is in the same group as the userToCheck.
-     */
-    private void ensureInUserProfiles(UserHandle userToCheck, String message) {
-        final int callingUserId = UserHandle.getCallingUserId();
-        final int targetUserId = userToCheck.getIdentifier();
-
-        if (targetUserId == callingUserId) return;
-
-        long ident = Binder.clearCallingIdentity();
-        try {
-            UserInfo callingUserInfo = mUm.getUserInfo(callingUserId);
-            UserInfo targetUserInfo = mUm.getUserInfo(targetUserId);
-            if (targetUserInfo == null
-                    || targetUserInfo.profileGroupId == UserInfo.NO_PROFILE_GROUP_ID
-                    || targetUserInfo.profileGroupId != callingUserInfo.profileGroupId) {
-                throw new SecurityException(message);
-            }
-        } finally {
-            Binder.restoreCallingIdentity(ident);
-        }
-    }
-
-    /**
-     * Checks if the user is enabled.
-     */
-    private boolean isUserEnabled(UserHandle user) {
-        long ident = Binder.clearCallingIdentity();
-        try {
-            UserInfo targetUserInfo = mUm.getUserInfo(user.getIdentifier());
-            return targetUserInfo != null && targetUserInfo.isEnabled();
-        } finally {
-            Binder.restoreCallingIdentity(ident);
-        }
-    }
-
-    @Override
-    public List<ResolveInfo> getLauncherActivities(String packageName, UserHandle user)
-            throws RemoteException {
-        ensureInUserProfiles(user, "Cannot retrieve activities for unrelated profile " + user);
-        if (!isUserEnabled(user)) {
-            return new ArrayList<ResolveInfo>();
-        }
-
-        final Intent mainIntent = new Intent(Intent.ACTION_MAIN, null);
-        mainIntent.addCategory(Intent.CATEGORY_LAUNCHER);
-        mainIntent.setPackage(packageName);
-        long ident = Binder.clearCallingIdentity();
-        try {
-            List<ResolveInfo> apps = mPm.queryIntentActivitiesAsUser(mainIntent, 0,
-                    user.getIdentifier());
-            return apps;
-        } finally {
-            Binder.restoreCallingIdentity(ident);
-        }
-    }
-
-    @Override
-    public ResolveInfo resolveActivity(Intent intent, UserHandle user)
-            throws RemoteException {
-        ensureInUserProfiles(user, "Cannot resolve activity for unrelated profile " + user);
-        if (!isUserEnabled(user)) {
-            return null;
-        }
-
-        long ident = Binder.clearCallingIdentity();
-        try {
-            ResolveInfo app = mPm.resolveActivityAsUser(intent, 0, user.getIdentifier());
-            return app;
-        } finally {
-            Binder.restoreCallingIdentity(ident);
-        }
-    }
-
-    @Override
-    public boolean isPackageEnabled(String packageName, UserHandle user)
-            throws RemoteException {
-        ensureInUserProfiles(user, "Cannot check package for unrelated profile " + user);
-        if (!isUserEnabled(user)) {
-            return false;
-        }
-
-        long ident = Binder.clearCallingIdentity();
-        try {
-            IPackageManager pm = AppGlobals.getPackageManager();
-            PackageInfo info = pm.getPackageInfo(packageName, 0, user.getIdentifier());
-            return info != null && info.applicationInfo.enabled;
-        } finally {
-            Binder.restoreCallingIdentity(ident);
-        }
-    }
-
-    @Override
-    public boolean isActivityEnabled(ComponentName component, UserHandle user)
-            throws RemoteException {
-        ensureInUserProfiles(user, "Cannot check component for unrelated profile " + user);
-        if (!isUserEnabled(user)) {
-            return false;
-        }
-
-        long ident = Binder.clearCallingIdentity();
-        try {
-            IPackageManager pm = AppGlobals.getPackageManager();
-            ActivityInfo info = pm.getActivityInfo(component, 0, user.getIdentifier());
-            return info != null && info.isEnabled();
-        } finally {
-            Binder.restoreCallingIdentity(ident);
-        }
-    }
-
-    @Override
-    public void startActivityAsUser(ComponentName component, Rect sourceBounds,
-            Bundle opts, UserHandle user) throws RemoteException {
-        ensureInUserProfiles(user, "Cannot start activity for unrelated profile " + user);
-        if (!isUserEnabled(user)) {
-            throw new IllegalStateException("Cannot start activity for disabled profile "  + user);
-        }
-
-        Intent launchIntent = new Intent(Intent.ACTION_MAIN);
-        launchIntent.addCategory(Intent.CATEGORY_LAUNCHER);
-        launchIntent.setComponent(component);
-        launchIntent.setSourceBounds(sourceBounds);
-        launchIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
-
-        long ident = Binder.clearCallingIdentity();
-        try {
-            mContext.startActivityAsUser(launchIntent, opts, user);
-        } finally {
-            Binder.restoreCallingIdentity(ident);
-        }
-    }
-
-    private class MyPackageMonitor extends PackageMonitor {
-
-        /** Checks if user is a profile of or same as listeningUser.
-          * and the user is enabled. */
-        private boolean isEnabledProfileOf(UserHandle user, UserHandle listeningUser,
-                String debugMsg) {
-            if (user.getIdentifier() == listeningUser.getIdentifier()) {
-                if (DEBUG) Log.d(TAG, "Delivering msg to same user " + debugMsg);
-                return true;
-            }
             long ident = Binder.clearCallingIdentity();
             try {
-                UserInfo userInfo = mUm.getUserInfo(user.getIdentifier());
-                UserInfo listeningUserInfo = mUm.getUserInfo(listeningUser.getIdentifier());
-                if (userInfo == null || listeningUserInfo == null
-                        || userInfo.profileGroupId == UserInfo.NO_PROFILE_GROUP_ID
-                        || userInfo.profileGroupId != listeningUserInfo.profileGroupId
-                        || !userInfo.isEnabled()) {
-                    if (DEBUG) {
-                        Log.d(TAG, "Not delivering msg from " + user + " to " + listeningUser + ":"
-                                + debugMsg);
-                    }
-                    return false;
-                } else {
-                    if (DEBUG) {
-                        Log.d(TAG, "Delivering msg from " + user + " to " + listeningUser + ":"
-                                + debugMsg);
-                    }
-                    return true;
+                UserInfo callingUserInfo = mUm.getUserInfo(callingUserId);
+                UserInfo targetUserInfo = mUm.getUserInfo(targetUserId);
+                if (targetUserInfo == null
+                        || targetUserInfo.profileGroupId == UserInfo.NO_PROFILE_GROUP_ID
+                        || targetUserInfo.profileGroupId != callingUserInfo.profileGroupId) {
+                    throw new SecurityException(message);
                 }
             } finally {
                 Binder.restoreCallingIdentity(ident);
             }
         }
 
-        @Override
-        public void onPackageAdded(String packageName, int uid) {
-            UserHandle user = new UserHandle(getChangingUserId());
-            final int n = mListeners.beginBroadcast();
-            for (int i = 0; i < n; i++) {
-                IOnAppsChangedListener listener = mListeners.getBroadcastItem(i);
-                UserHandle listeningUser = (UserHandle) mListeners.getBroadcastCookie(i);
-                if (!isEnabledProfileOf(user, listeningUser, "onPackageAdded")) continue;
-                try {
-                    listener.onPackageAdded(user, packageName);
-                } catch (RemoteException re) {
-                    Slog.d(TAG, "Callback failed ", re);
-                }
+        /**
+         * Checks if the user is enabled.
+         */
+        private boolean isUserEnabled(UserHandle user) {
+            long ident = Binder.clearCallingIdentity();
+            try {
+                UserInfo targetUserInfo = mUm.getUserInfo(user.getIdentifier());
+                return targetUserInfo != null && targetUserInfo.isEnabled();
+            } finally {
+                Binder.restoreCallingIdentity(ident);
             }
-            mListeners.finishBroadcast();
-
-            super.onPackageAdded(packageName, uid);
         }
 
         @Override
-        public void onPackageRemoved(String packageName, int uid) {
-            UserHandle user = new UserHandle(getChangingUserId());
-            final int n = mListeners.beginBroadcast();
-            for (int i = 0; i < n; i++) {
-                IOnAppsChangedListener listener = mListeners.getBroadcastItem(i);
-                UserHandle listeningUser = (UserHandle) mListeners.getBroadcastCookie(i);
-                if (!isEnabledProfileOf(user, listeningUser, "onPackageRemoved")) continue;
-                try {
-                    listener.onPackageRemoved(user, packageName);
-                } catch (RemoteException re) {
-                    Slog.d(TAG, "Callback failed ", re);
-                }
+        public List<ResolveInfo> getLauncherActivities(String packageName, UserHandle user)
+                throws RemoteException {
+            ensureInUserProfiles(user, "Cannot retrieve activities for unrelated profile " + user);
+            if (!isUserEnabled(user)) {
+                return new ArrayList<ResolveInfo>();
             }
-            mListeners.finishBroadcast();
 
-            super.onPackageRemoved(packageName, uid);
+            final Intent mainIntent = new Intent(Intent.ACTION_MAIN, null);
+            mainIntent.addCategory(Intent.CATEGORY_LAUNCHER);
+            mainIntent.setPackage(packageName);
+            long ident = Binder.clearCallingIdentity();
+            try {
+                List<ResolveInfo> apps = mPm.queryIntentActivitiesAsUser(mainIntent, 0,
+                        user.getIdentifier());
+                return apps;
+            } finally {
+                Binder.restoreCallingIdentity(ident);
+            }
         }
 
         @Override
-        public void onPackageModified(String packageName) {
-            UserHandle user = new UserHandle(getChangingUserId());
-            final int n = mListeners.beginBroadcast();
-            for (int i = 0; i < n; i++) {
-                IOnAppsChangedListener listener = mListeners.getBroadcastItem(i);
-                UserHandle listeningUser = (UserHandle) mListeners.getBroadcastCookie(i);
-                if (!isEnabledProfileOf(user, listeningUser, "onPackageModified")) continue;
-                try {
-                    listener.onPackageChanged(user, packageName);
-                } catch (RemoteException re) {
-                    Slog.d(TAG, "Callback failed ", re);
-                }
+        public ResolveInfo resolveActivity(Intent intent, UserHandle user)
+                throws RemoteException {
+            ensureInUserProfiles(user, "Cannot resolve activity for unrelated profile " + user);
+            if (!isUserEnabled(user)) {
+                return null;
             }
-            mListeners.finishBroadcast();
 
-            super.onPackageModified(packageName);
+            long ident = Binder.clearCallingIdentity();
+            try {
+                ResolveInfo app = mPm.resolveActivityAsUser(intent, 0, user.getIdentifier());
+                return app;
+            } finally {
+                Binder.restoreCallingIdentity(ident);
+            }
         }
 
         @Override
-        public void onPackagesAvailable(String[] packages) {
-            UserHandle user = new UserHandle(getChangingUserId());
-            final int n = mListeners.beginBroadcast();
-            for (int i = 0; i < n; i++) {
-                IOnAppsChangedListener listener = mListeners.getBroadcastItem(i);
-                UserHandle listeningUser = (UserHandle) mListeners.getBroadcastCookie(i);
-                if (!isEnabledProfileOf(user, listeningUser, "onPackagesAvailable")) continue;
-                try {
-                    listener.onPackagesAvailable(user, packages, isReplacing());
-                } catch (RemoteException re) {
-                    Slog.d(TAG, "Callback failed ", re);
-                }
+        public boolean isPackageEnabled(String packageName, UserHandle user)
+                throws RemoteException {
+            ensureInUserProfiles(user, "Cannot check package for unrelated profile " + user);
+            if (!isUserEnabled(user)) {
+                return false;
             }
-            mListeners.finishBroadcast();
 
-            super.onPackagesAvailable(packages);
+            long ident = Binder.clearCallingIdentity();
+            try {
+                IPackageManager pm = AppGlobals.getPackageManager();
+                PackageInfo info = pm.getPackageInfo(packageName, 0, user.getIdentifier());
+                return info != null && info.applicationInfo.enabled;
+            } finally {
+                Binder.restoreCallingIdentity(ident);
+            }
         }
 
         @Override
-        public void onPackagesUnavailable(String[] packages) {
-            UserHandle user = new UserHandle(getChangingUserId());
-            final int n = mListeners.beginBroadcast();
-            for (int i = 0; i < n; i++) {
-                IOnAppsChangedListener listener = mListeners.getBroadcastItem(i);
-                UserHandle listeningUser = (UserHandle) mListeners.getBroadcastCookie(i);
-                if (!isEnabledProfileOf(user, listeningUser, "onPackagesUnavailable")) continue;
-                try {
-                    listener.onPackagesUnavailable(user, packages, isReplacing());
-                } catch (RemoteException re) {
-                    Slog.d(TAG, "Callback failed ", re);
-                }
+        public boolean isActivityEnabled(ComponentName component, UserHandle user)
+                throws RemoteException {
+            ensureInUserProfiles(user, "Cannot check component for unrelated profile " + user);
+            if (!isUserEnabled(user)) {
+                return false;
             }
-            mListeners.finishBroadcast();
 
-            super.onPackagesUnavailable(packages);
+            long ident = Binder.clearCallingIdentity();
+            try {
+                IPackageManager pm = AppGlobals.getPackageManager();
+                ActivityInfo info = pm.getActivityInfo(component, 0, user.getIdentifier());
+                return info != null && info.isEnabled();
+            } finally {
+                Binder.restoreCallingIdentity(ident);
+            }
         }
 
-    }
-
-    class PackageCallbackList<T extends IInterface> extends RemoteCallbackList<T> {
         @Override
-        public void onCallbackDied(T callback, Object cookie) {
-            checkCallbackCount();
+        public void startActivityAsUser(ComponentName component, Rect sourceBounds,
+                Bundle opts, UserHandle user) throws RemoteException {
+            ensureInUserProfiles(user, "Cannot start activity for unrelated profile " + user);
+            if (!isUserEnabled(user)) {
+                throw new IllegalStateException("Cannot start activity for disabled profile "  + user);
+            }
+
+            Intent launchIntent = new Intent(Intent.ACTION_MAIN);
+            launchIntent.addCategory(Intent.CATEGORY_LAUNCHER);
+            launchIntent.setComponent(component);
+            launchIntent.setSourceBounds(sourceBounds);
+            launchIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+
+            long ident = Binder.clearCallingIdentity();
+            try {
+                mContext.startActivityAsUser(launchIntent, opts, user);
+            } finally {
+                Binder.restoreCallingIdentity(ident);
+            }
+        }
+
+        private class MyPackageMonitor extends PackageMonitor {
+
+            /** Checks if user is a profile of or same as listeningUser.
+              * and the user is enabled. */
+            private boolean isEnabledProfileOf(UserHandle user, UserHandle listeningUser,
+                    String debugMsg) {
+                if (user.getIdentifier() == listeningUser.getIdentifier()) {
+                    if (DEBUG) Log.d(TAG, "Delivering msg to same user " + debugMsg);
+                    return true;
+                }
+                long ident = Binder.clearCallingIdentity();
+                try {
+                    UserInfo userInfo = mUm.getUserInfo(user.getIdentifier());
+                    UserInfo listeningUserInfo = mUm.getUserInfo(listeningUser.getIdentifier());
+                    if (userInfo == null || listeningUserInfo == null
+                            || userInfo.profileGroupId == UserInfo.NO_PROFILE_GROUP_ID
+                            || userInfo.profileGroupId != listeningUserInfo.profileGroupId
+                            || !userInfo.isEnabled()) {
+                        if (DEBUG) {
+                            Log.d(TAG, "Not delivering msg from " + user + " to " + listeningUser + ":"
+                                    + debugMsg);
+                        }
+                        return false;
+                    } else {
+                        if (DEBUG) {
+                            Log.d(TAG, "Delivering msg from " + user + " to " + listeningUser + ":"
+                                    + debugMsg);
+                        }
+                        return true;
+                    }
+                } finally {
+                    Binder.restoreCallingIdentity(ident);
+                }
+            }
+
+            @Override
+            public void onPackageAdded(String packageName, int uid) {
+                UserHandle user = new UserHandle(getChangingUserId());
+                final int n = mListeners.beginBroadcast();
+                for (int i = 0; i < n; i++) {
+                    IOnAppsChangedListener listener = mListeners.getBroadcastItem(i);
+                    UserHandle listeningUser = (UserHandle) mListeners.getBroadcastCookie(i);
+                    if (!isEnabledProfileOf(user, listeningUser, "onPackageAdded")) continue;
+                    try {
+                        listener.onPackageAdded(user, packageName);
+                    } catch (RemoteException re) {
+                        Slog.d(TAG, "Callback failed ", re);
+                    }
+                }
+                mListeners.finishBroadcast();
+
+                super.onPackageAdded(packageName, uid);
+            }
+
+            @Override
+            public void onPackageRemoved(String packageName, int uid) {
+                UserHandle user = new UserHandle(getChangingUserId());
+                final int n = mListeners.beginBroadcast();
+                for (int i = 0; i < n; i++) {
+                    IOnAppsChangedListener listener = mListeners.getBroadcastItem(i);
+                    UserHandle listeningUser = (UserHandle) mListeners.getBroadcastCookie(i);
+                    if (!isEnabledProfileOf(user, listeningUser, "onPackageRemoved")) continue;
+                    try {
+                        listener.onPackageRemoved(user, packageName);
+                    } catch (RemoteException re) {
+                        Slog.d(TAG, "Callback failed ", re);
+                    }
+                }
+                mListeners.finishBroadcast();
+
+                super.onPackageRemoved(packageName, uid);
+            }
+
+            @Override
+            public void onPackageModified(String packageName) {
+                UserHandle user = new UserHandle(getChangingUserId());
+                final int n = mListeners.beginBroadcast();
+                for (int i = 0; i < n; i++) {
+                    IOnAppsChangedListener listener = mListeners.getBroadcastItem(i);
+                    UserHandle listeningUser = (UserHandle) mListeners.getBroadcastCookie(i);
+                    if (!isEnabledProfileOf(user, listeningUser, "onPackageModified")) continue;
+                    try {
+                        listener.onPackageChanged(user, packageName);
+                    } catch (RemoteException re) {
+                        Slog.d(TAG, "Callback failed ", re);
+                    }
+                }
+                mListeners.finishBroadcast();
+
+                super.onPackageModified(packageName);
+            }
+
+            @Override
+            public void onPackagesAvailable(String[] packages) {
+                UserHandle user = new UserHandle(getChangingUserId());
+                final int n = mListeners.beginBroadcast();
+                for (int i = 0; i < n; i++) {
+                    IOnAppsChangedListener listener = mListeners.getBroadcastItem(i);
+                    UserHandle listeningUser = (UserHandle) mListeners.getBroadcastCookie(i);
+                    if (!isEnabledProfileOf(user, listeningUser, "onPackagesAvailable")) continue;
+                    try {
+                        listener.onPackagesAvailable(user, packages, isReplacing());
+                    } catch (RemoteException re) {
+                        Slog.d(TAG, "Callback failed ", re);
+                    }
+                }
+                mListeners.finishBroadcast();
+
+                super.onPackagesAvailable(packages);
+            }
+
+            @Override
+            public void onPackagesUnavailable(String[] packages) {
+                UserHandle user = new UserHandle(getChangingUserId());
+                final int n = mListeners.beginBroadcast();
+                for (int i = 0; i < n; i++) {
+                    IOnAppsChangedListener listener = mListeners.getBroadcastItem(i);
+                    UserHandle listeningUser = (UserHandle) mListeners.getBroadcastCookie(i);
+                    if (!isEnabledProfileOf(user, listeningUser, "onPackagesUnavailable")) continue;
+                    try {
+                        listener.onPackagesUnavailable(user, packages, isReplacing());
+                    } catch (RemoteException re) {
+                        Slog.d(TAG, "Callback failed ", re);
+                    }
+                }
+                mListeners.finishBroadcast();
+
+                super.onPackagesUnavailable(packages);
+            }
+
+        }
+
+        class PackageCallbackList<T extends IInterface> extends RemoteCallbackList<T> {
+            @Override
+            public void onCallbackDied(T callback, Object cookie) {
+                checkCallbackCount();
+            }
         }
     }
-}
+}
\ No newline at end of file
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 6d75ee5..bb93663 100755
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -40,6 +40,7 @@
 import com.android.internal.app.IMediaContainerService;
 import com.android.internal.app.ResolverActivity;
 import com.android.internal.content.NativeLibraryHelper;
+import com.android.internal.content.NativeLibraryHelper.ApkHandle;
 import com.android.internal.content.PackageHelper;
 import com.android.internal.util.FastPrintWriter;
 import com.android.internal.util.FastXmlSerializer;
@@ -4148,7 +4149,7 @@
                 continue;
             }
             PackageParser.Package pkg = scanPackageLI(file,
-                    flags|PackageParser.PARSE_MUST_BE_APK, scanMode, currentTime, null);
+                    flags|PackageParser.PARSE_MUST_BE_APK, scanMode, currentTime, null, null);
             // Don't mess around with apps in system partition.
             if (pkg == null && (flags & PackageParser.PARSE_IS_SYSTEM) == 0 &&
                     mLastScanError == PackageManager.INSTALL_FAILED_INVALID_APK) {
@@ -4215,7 +4216,7 @@
      *  Returns null in case of errors and the error code is stored in mLastScanError
      */
     private PackageParser.Package scanPackageLI(File scanFile,
-            int parseFlags, int scanMode, long currentTime, UserHandle user) {
+            int parseFlags, int scanMode, long currentTime, UserHandle user, String abiOverride) {
         mLastScanError = PackageManager.INSTALL_SUCCEEDED;
         String scanPath = scanFile.getPath();
         if (DEBUG_INSTALL) Slog.d(TAG, "Parsing: " + scanPath);
@@ -4283,7 +4284,7 @@
                     mLastScanError = PackageManager.INSTALL_FAILED_DUPLICATE_PACKAGE;
                     return null;
                 } else {
-                    // The current app on the system partion is better than
+                    // The current app on the system partition is better than
                     // what we have updated to on the data partition; switch
                     // back to the system partition version.
                     // At this point, its safely assumed that package installation for
@@ -4402,7 +4403,7 @@
         setApplicationInfoPaths(pkg, codePath, resPath);
         // 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);
+                | SCAN_UPDATE_SIGNATURE, currentTime, user, abiOverride);
 
         /*
          * If the system app should be overridden by a previously installed
@@ -4945,7 +4946,7 @@
     }
 
     private PackageParser.Package scanPackageLI(PackageParser.Package pkg,
-            int parseFlags, int scanMode, long currentTime, UserHandle user) {
+            int parseFlags, int scanMode, long currentTime, UserHandle user, String abiOverride) {
         File scanFile = new File(pkg.mScanPath);
         if (scanFile == null || pkg.applicationInfo.sourceDir == null ||
                 pkg.applicationInfo.publicSourceDir == null) {
@@ -5395,7 +5396,22 @@
          *        only for non-system apps and system app upgrades.
          */
         if (pkg.applicationInfo.nativeLibraryDir != null) {
+            final NativeLibraryHelper.ApkHandle handle = new NativeLibraryHelper.ApkHandle(scanFile);
             try {
+                // Enable gross and lame hacks for apps that are built with old
+                // SDK tools. We must scan their APKs for renderscript bitcode and
+                // not launch them if it's present. Don't bother checking on devices
+                // that don't have 64 bit support.
+                String[] abiList = Build.SUPPORTED_ABIS;
+                boolean hasLegacyRenderscriptBitcode = false;
+                if (abiOverride != null) {
+                    abiList = new String[] { abiOverride };
+                } else if (Build.SUPPORTED_64_BIT_ABIS.length > 0 &&
+                        NativeLibraryHelper.hasRenderscriptBitcode(handle)) {
+                    abiList = Build.SUPPORTED_32_BIT_ABIS;
+                    hasLegacyRenderscriptBitcode = true;
+                }
+
                 File nativeLibraryDir = new File(pkg.applicationInfo.nativeLibraryDir);
                 final String dataPathString = dataPath.getCanonicalPath();
 
@@ -5411,21 +5427,26 @@
                         Log.i(TAG, "removed obsolete native libraries for system package "
                                 + path);
                     }
-
-                    setInternalAppAbi(pkg, pkgSetting);
+                    if (abiOverride != null || hasLegacyRenderscriptBitcode) {
+                        pkg.applicationInfo.cpuAbi = abiList[0];
+                        pkgSetting.cpuAbiString = abiList[0];
+                    } else {
+                        setInternalAppAbi(pkg, pkgSetting);
+                    }
                 } else {
                     if (!isForwardLocked(pkg) && !isExternal(pkg)) {
                         /*
-                         * Update native library dir if it starts with
-                         * /data/data
-                         */
+                        * Update native library dir if it starts with
+                        * /data/data
+                        */
                         if (nativeLibraryDir.getPath().startsWith(dataPathString)) {
                             setInternalAppNativeLibraryPath(pkg, pkgSetting);
                             nativeLibraryDir = new File(pkg.applicationInfo.nativeLibraryDir);
                         }
 
                         try {
-                            int copyRet = copyNativeLibrariesForInternalApp(scanFile, nativeLibraryDir);
+                            int copyRet = copyNativeLibrariesForInternalApp(handle,
+                                    nativeLibraryDir, abiList);
                             if (copyRet < 0 && copyRet != PackageManager.NO_NATIVE_LIBRARIES) {
                                 Slog.e(TAG, "Unable to copy native libraries");
                                 mLastScanError = PackageManager.INSTALL_FAILED_INTERNAL_ERROR;
@@ -5435,7 +5456,9 @@
                             // We've successfully copied native libraries across, so we make a
                             // note of what ABI we're using
                             if (copyRet != PackageManager.NO_NATIVE_LIBRARIES) {
-                                pkg.applicationInfo.cpuAbi = Build.SUPPORTED_ABIS[copyRet];
+                                pkg.applicationInfo.cpuAbi = abiList[copyRet];
+                            } else if (abiOverride != null || hasLegacyRenderscriptBitcode) {
+                                pkg.applicationInfo.cpuAbi = abiList[0];
                             } else {
                                 pkg.applicationInfo.cpuAbi = null;
                             }
@@ -5452,20 +5475,22 @@
                         // to clean this up but we'll need to change the interface between this service
                         // and IMediaContainerService (but doing so will spread this logic out, rather
                         // than centralizing it).
-                        final NativeLibraryHelper.ApkHandle handle = new NativeLibraryHelper.ApkHandle(scanFile);
-                        final int abi = NativeLibraryHelper.findSupportedAbi(handle, Build.SUPPORTED_ABIS);
+                        final int abi = NativeLibraryHelper.findSupportedAbi(handle, abiList);
                         if (abi >= 0) {
-                            pkg.applicationInfo.cpuAbi = Build.SUPPORTED_ABIS[abi];
+                            pkg.applicationInfo.cpuAbi = abiList[abi];
                         } else if (abi == PackageManager.NO_NATIVE_LIBRARIES) {
                             // Note that (non upgraded) system apps will not have any native
                             // libraries bundled in their APK, but we're guaranteed not to be
                             // such an app at this point.
-                            pkg.applicationInfo.cpuAbi = null;
+                            if (abiOverride != null || hasLegacyRenderscriptBitcode) {
+                                pkg.applicationInfo.cpuAbi = abiList[0];
+                            } else {
+                                pkg.applicationInfo.cpuAbi = null;
+                            }
                         } else {
                             mLastScanError = PackageManager.INSTALL_FAILED_INTERNAL_ERROR;
                             return null;
                         }
-                        handle.close();
                     }
 
                     if (DEBUG_INSTALL) Slog.i(TAG, "Linking native library dir for " + path);
@@ -5482,8 +5507,12 @@
                         }
                     }
                 }
+
+                pkgSetting.cpuAbiString = pkg.applicationInfo.cpuAbi;
             } catch (IOException ioe) {
                 Slog.e(TAG, "Unable to get canonical file " + ioe.toString());
+            } finally {
+                handle.close();
             }
         }
         pkg.mScanPath = path;
@@ -6175,8 +6204,8 @@
         }
     }
 
-    private static int copyNativeLibrariesForInternalApp(File scanFile, final File nativeLibraryDir)
-            throws IOException {
+    private static int copyNativeLibrariesForInternalApp(ApkHandle handle,
+            final File nativeLibraryDir, String[] abiList) throws IOException {
         if (!nativeLibraryDir.isDirectory()) {
             nativeLibraryDir.delete();
 
@@ -6198,21 +6227,16 @@
          * If this is an internal application or our nativeLibraryPath points to
          * the app-lib directory, unpack the libraries if necessary.
          */
-        final NativeLibraryHelper.ApkHandle handle = new NativeLibraryHelper.ApkHandle(scanFile);
-        try {
-            int abi = NativeLibraryHelper.findSupportedAbi(handle, Build.SUPPORTED_ABIS);
-            if (abi >= 0) {
-                int copyRet = NativeLibraryHelper.copyNativeBinariesIfNeededLI(handle,
-                        nativeLibraryDir, Build.SUPPORTED_ABIS[abi]);
-                if (copyRet != PackageManager.INSTALL_SUCCEEDED) {
-                    return copyRet;
-                }
+        int abi = NativeLibraryHelper.findSupportedAbi(handle, abiList);
+        if (abi >= 0) {
+            int copyRet = NativeLibraryHelper.copyNativeBinariesIfNeededLI(handle,
+                    nativeLibraryDir, Build.SUPPORTED_ABIS[abi]);
+            if (copyRet != PackageManager.INSTALL_SUCCEEDED) {
+                return copyRet;
             }
-
-            return abi;
-        } finally {
-            handle.close();
         }
+
+        return abi;
     }
 
     private void killApplication(String pkgName, int appId, String reason) {
@@ -7536,7 +7560,7 @@
                         }
                         p = scanPackageLI(fullPath, flags,
                                 SCAN_MONITOR | SCAN_NO_PATHS | SCAN_UPDATE_TIME,
-                                System.currentTimeMillis(), UserHandle.ALL);
+                                System.currentTimeMillis(), UserHandle.ALL, null);
                         if (p != null) {
                             /*
                              * TODO this seems dangerous as the package may have
@@ -7657,6 +7681,16 @@
         if (observer == null && observer2 == null) {
             throw new IllegalArgumentException("No install observer supplied");
         }
+        installPackageWithVerificationEncryptionAndAbiOverrideEtc(packageURI, observer, observer2,
+                flags, installerPackageName, verificationParams, encryptionParams, null);
+    }
+
+    @Override
+    public void installPackageWithVerificationEncryptionAndAbiOverrideEtc(Uri packageURI,
+            IPackageInstallObserver observer, IPackageInstallObserver2 observer2,
+            int flags, String installerPackageName,
+            VerificationParams verificationParams, ContainerEncryptionParams encryptionParams,
+            String packageAbiOverride) {
         mContext.enforceCallingOrSelfPermission(android.Manifest.permission.INSTALL_PACKAGES,
                 null);
 
@@ -7696,7 +7730,8 @@
 
         final Message msg = mHandler.obtainMessage(INIT_COPY);
         msg.obj = new InstallParams(packageURI, observer, observer2, filteredFlags,
-                installerPackageName, verificationParams, encryptionParams, user);
+                installerPackageName, verificationParams, encryptionParams, user,
+                packageAbiOverride);
         mHandler.sendMessage(msg);
     }
 
@@ -8405,11 +8440,14 @@
         private int mRet;
         private File mTempPackage;
         final ContainerEncryptionParams encryptionParams;
+        final String packageAbiOverride;
+        final String packageInstructionSetOverride;
 
         InstallParams(Uri packageURI,
                 IPackageInstallObserver observer, IPackageInstallObserver2 observer2,
                 int flags, String installerPackageName, VerificationParams verificationParams,
-                ContainerEncryptionParams encryptionParams, UserHandle user) {
+                ContainerEncryptionParams encryptionParams, UserHandle user,
+                String packageAbiOverride) {
             super(user);
             this.mPackageURI = packageURI;
             this.flags = flags;
@@ -8418,6 +8456,9 @@
             this.installerPackageName = installerPackageName;
             this.verificationParams = verificationParams;
             this.encryptionParams = encryptionParams;
+            this.packageAbiOverride = packageAbiOverride;
+            this.packageInstructionSetOverride = (packageAbiOverride == null) ?
+                    packageAbiOverride : VMRuntime.getInstructionSet(packageAbiOverride);
         }
 
         @Override
@@ -8563,7 +8604,7 @@
                         // Remote call to find out default install location
                         final String packageFilePath = packageFile.getAbsolutePath();
                         pkgLite = mContainerService.getMinimalPackageInfo(packageFilePath, flags,
-                                lowThreshold);
+                                lowThreshold, packageAbiOverride);
 
                         /*
                          * If we have too little free space, try to free cache
@@ -8572,10 +8613,10 @@
                         if (pkgLite.recommendedInstallLocation
                                 == PackageHelper.RECOMMEND_FAILED_INSUFFICIENT_STORAGE) {
                             final long size = mContainerService.calculateInstalledSize(
-                                    packageFilePath, isForwardLocked());
+                                    packageFilePath, isForwardLocked(), packageAbiOverride);
                             if (mInstaller.freeCache(size + lowThreshold) >= 0) {
                                 pkgLite = mContainerService.getMinimalPackageInfo(packageFilePath,
-                                        flags, lowThreshold);
+                                        flags, lowThreshold, packageAbiOverride);
                             }
                             /*
                              * The cache free must have deleted the file we
@@ -8995,11 +9036,12 @@
         final ManifestDigest manifestDigest;
         final UserHandle user;
         final String instructionSet;
+        final String abiOverride;
 
         InstallArgs(Uri packageURI,
                 IPackageInstallObserver observer, IPackageInstallObserver2 observer2,
                 int flags, String installerPackageName, ManifestDigest manifestDigest,
-                UserHandle user, String instructionSet) {
+                UserHandle user, String instructionSet, String abiOverride) {
             this.packageURI = packageURI;
             this.flags = flags;
             this.observer = observer;
@@ -9008,6 +9050,7 @@
             this.manifestDigest = manifestDigest;
             this.user = user;
             this.instructionSet = instructionSet;
+            this.abiOverride = abiOverride;
         }
 
         abstract void createCopyFile();
@@ -9063,12 +9106,13 @@
         FileInstallArgs(InstallParams params) {
             super(params.getPackageUri(), params.observer, params.observer2, params.flags,
                     params.installerPackageName, params.getManifestDigest(),
-                    params.getUser(), null /* instruction set */);
+                    params.getUser(), params.packageInstructionSetOverride,
+                    params.packageAbiOverride);
         }
 
         FileInstallArgs(String fullCodePath, String fullResourcePath, String nativeLibraryPath,
                 String instructionSet) {
-            super(null, null, null, 0, null, null, null, instructionSet);
+            super(null, null, null, 0, null, null, null, instructionSet, null);
             File codeFile = new File(fullCodePath);
             installDir = codeFile.getParentFile();
             codeFileName = fullCodePath;
@@ -9077,7 +9121,7 @@
         }
 
         FileInstallArgs(Uri packageURI, String pkgName, String dataDir, String instructionSet) {
-            super(packageURI, null, null, 0, null, null, null, instructionSet);
+            super(packageURI, null, null, 0, null, null, null, instructionSet, null);
             installDir = isFwdLocked() ? mDrmAppPrivateInstallDir : mAppInstallDir;
             String apkName = getNextCodePath(null, pkgName, ".apk");
             codeFileName = new File(installDir, apkName + ".apk").getPath();
@@ -9181,14 +9225,26 @@
                 NativeLibraryHelper.removeNativeBinariesFromDirLI(nativeLibraryFile);
                 nativeLibraryFile.delete();
             }
+
+            final NativeLibraryHelper.ApkHandle handle = new NativeLibraryHelper.ApkHandle(codeFile);
+            String[] abiList = (abiOverride != null) ?
+                    new String[] { abiOverride } : Build.SUPPORTED_ABIS;
             try {
-                int copyRet = copyNativeLibrariesForInternalApp(codeFile, nativeLibraryFile);
+                if (Build.SUPPORTED_64_BIT_ABIS.length > 0 &&
+                        abiOverride == null &&
+                        NativeLibraryHelper.hasRenderscriptBitcode(handle)) {
+                    abiList = Build.SUPPORTED_32_BIT_ABIS;
+                }
+
+                int copyRet = copyNativeLibrariesForInternalApp(handle, nativeLibraryFile, abiList);
                 if (copyRet < 0 && copyRet != PackageManager.NO_NATIVE_LIBRARIES) {
                     return copyRet;
                 }
             } catch (IOException e) {
                 Slog.e(TAG, "Copying native libraries failed", e);
                 ret = PackageManager.INSTALL_FAILED_INTERNAL_ERROR;
+            } finally {
+                handle.close();
             }
 
             return ret;
@@ -9403,14 +9459,15 @@
         AsecInstallArgs(InstallParams params) {
             super(params.getPackageUri(), params.observer, params.observer2, params.flags,
                     params.installerPackageName, params.getManifestDigest(),
-                    params.getUser(), null /* instruction set */);
+                    params.getUser(), params.packageInstructionSetOverride,
+                    params.packageAbiOverride);
         }
 
         AsecInstallArgs(String fullCodePath, String fullResourcePath, String nativeLibraryPath,
                 String instructionSet, boolean isExternal, boolean isForwardLocked) {
             super(null, null, null, (isExternal ? PackageManager.INSTALL_EXTERNAL : 0)
                     | (isForwardLocked ? PackageManager.INSTALL_FORWARD_LOCK : 0),
-                    null, null, null, instructionSet);
+                    null, null, null, instructionSet, null);
             // Extract cid from fullCodePath
             int eidx = fullCodePath.lastIndexOf("/");
             String subStr1 = fullCodePath.substring(0, eidx);
@@ -9422,7 +9479,7 @@
         AsecInstallArgs(String cid, String instructionSet, boolean isForwardLocked) {
             super(null, null, null, (isAsecExternal(cid) ? PackageManager.INSTALL_EXTERNAL : 0)
                     | (isForwardLocked ? PackageManager.INSTALL_FORWARD_LOCK : 0),
-                    null, null, null, instructionSet);
+                    null, null, null, instructionSet, null);
             this.cid = cid;
             setCachePath(PackageHelper.getSdDir(cid));
         }
@@ -9431,7 +9488,7 @@
                 boolean isExternal, boolean isForwardLocked) {
             super(packageURI, null, null, (isExternal ? PackageManager.INSTALL_EXTERNAL : 0)
                     | (isForwardLocked ? PackageManager.INSTALL_FORWARD_LOCK : 0),
-                    null, null, null, instructionSet);
+                    null, null, null, instructionSet, null);
             this.cid = cid;
         }
 
@@ -9443,7 +9500,7 @@
             try {
                 mContext.grantUriPermission(DEFAULT_CONTAINER_PACKAGE, packageURI,
                         Intent.FLAG_GRANT_READ_URI_PERMISSION);
-                return imcs.checkExternalFreeStorage(packageURI, isFwdLocked());
+                return imcs.checkExternalFreeStorage(packageURI, isFwdLocked(), abiOverride);
             } finally {
                 mContext.revokeUriPermission(packageURI, Intent.FLAG_GRANT_READ_URI_PERMISSION);
             }
@@ -9469,7 +9526,8 @@
                 mContext.grantUriPermission(DEFAULT_CONTAINER_PACKAGE, packageURI,
                         Intent.FLAG_GRANT_READ_URI_PERMISSION);
                 newCachePath = imcs.copyResourceToContainer(packageURI, cid, getEncryptKey(),
-                        RES_FILE_NAME, PUBLIC_RES_FILE_NAME, isExternal(), isFwdLocked());
+                        RES_FILE_NAME, PUBLIC_RES_FILE_NAME, isExternal(), isFwdLocked(),
+                        abiOverride);
             } finally {
                 mContext.revokeUriPermission(packageURI, Intent.FLAG_GRANT_READ_URI_PERMISSION);
             }
@@ -9777,7 +9835,7 @@
      */
     private void installNewPackageLI(PackageParser.Package pkg,
             int parseFlags, int scanMode, UserHandle user,
-            String installerPackageName, PackageInstalledInfo res) {
+            String installerPackageName, PackageInstalledInfo res, String abiOverride) {
         // Remember this for later, in case we need to rollback this install
         String pkgName = pkg.packageName;
 
@@ -9805,7 +9863,7 @@
         }
         mLastScanError = PackageManager.INSTALL_SUCCEEDED;
         PackageParser.Package newPackage = scanPackageLI(pkg, parseFlags, scanMode,
-                System.currentTimeMillis(), user);
+                System.currentTimeMillis(), user, abiOverride);
         if (newPackage == null) {
             Slog.w(TAG, "Package couldn't be installed in " + pkg.mPath);
             if ((res.returnCode=mLastScanError) == PackageManager.INSTALL_SUCCEEDED) {
@@ -9832,7 +9890,7 @@
 
     private void replacePackageLI(PackageParser.Package pkg,
             int parseFlags, int scanMode, UserHandle user,
-            String installerPackageName, PackageInstalledInfo res) {
+            String installerPackageName, PackageInstalledInfo res, String abiOverride) {
 
         PackageParser.Package oldPackage;
         String pkgName = pkg.packageName;
@@ -9861,17 +9919,19 @@
         boolean sysPkg = (isSystemApp(oldPackage));
         if (sysPkg) {
             replaceSystemPackageLI(oldPackage, pkg, parseFlags, scanMode,
-                    user, allUsers, perUserInstalled, installerPackageName, res);
+                    user, allUsers, perUserInstalled, installerPackageName, res,
+                    abiOverride);
         } else {
             replaceNonSystemPackageLI(oldPackage, pkg, parseFlags, scanMode,
-                    user, allUsers, perUserInstalled, installerPackageName, res);
+                    user, allUsers, perUserInstalled, installerPackageName, res,
+                    abiOverride);
         }
     }
 
     private void replaceNonSystemPackageLI(PackageParser.Package deletedPackage,
             PackageParser.Package pkg, int parseFlags, int scanMode, UserHandle user,
             int[] allUsers, boolean[] perUserInstalled,
-            String installerPackageName, PackageInstalledInfo res) {
+            String installerPackageName, PackageInstalledInfo res, String abiOverride) {
         PackageParser.Package newPackage = null;
         String pkgName = deletedPackage.packageName;
         boolean deletedPkg = true;
@@ -9896,7 +9956,7 @@
             // Successfully deleted the old package. Now proceed with re-installation
             mLastScanError = PackageManager.INSTALL_SUCCEEDED;
             newPackage = scanPackageLI(pkg, parseFlags, scanMode | SCAN_UPDATE_TIME,
-                    System.currentTimeMillis(), user);
+                    System.currentTimeMillis(), user, abiOverride);
             if (newPackage == null) {
                 Slog.w(TAG, "Package couldn't be installed in " + pkg.mPath);
                 if ((res.returnCode=mLastScanError) == PackageManager.INSTALL_SUCCEEDED) {
@@ -9925,7 +9985,7 @@
             }
             // Since we failed to install the new package we need to restore the old
             // package that we deleted.
-            if(deletedPkg) {
+            if (deletedPkg) {
                 if (DEBUG_INSTALL) Slog.d(TAG, "Install failed, reinstalling: " + deletedPackage);
                 File restoreFile = new File(deletedPackage.mPath);
                 // Parse old package
@@ -9936,7 +9996,7 @@
                 int oldScanMode = (oldOnSd ? 0 : SCAN_MONITOR) | SCAN_UPDATE_SIGNATURE
                         | SCAN_UPDATE_TIME;
                 if (scanPackageLI(restoreFile, oldParseFlags, oldScanMode,
-                        origUpdateTime, null) == null) {
+                        origUpdateTime, null, null) == null) {
                     Slog.e(TAG, "Failed to restore package : " + pkgName + " after failed upgrade");
                     return;
                 }
@@ -9956,7 +10016,7 @@
     private void replaceSystemPackageLI(PackageParser.Package deletedPackage,
             PackageParser.Package pkg, int parseFlags, int scanMode, UserHandle user,
             int[] allUsers, boolean[] perUserInstalled,
-            String installerPackageName, PackageInstalledInfo res) {
+            String installerPackageName, PackageInstalledInfo res, String abiOverride) {
         if (DEBUG_INSTALL) Slog.d(TAG, "replaceSystemPackageLI: new=" + pkg
                 + ", old=" + deletedPackage);
         PackageParser.Package newPackage = null;
@@ -10010,7 +10070,7 @@
         // Successfully disabled the old package. Now proceed with re-installation
         res.returnCode = mLastScanError = PackageManager.INSTALL_SUCCEEDED;
         pkg.applicationInfo.flags |= ApplicationInfo.FLAG_UPDATED_SYSTEM_APP;
-        newPackage = scanPackageLI(pkg, parseFlags, scanMode, 0, user);
+        newPackage = scanPackageLI(pkg, parseFlags, scanMode, 0, user, abiOverride);
         if (newPackage == null) {
             Slog.w(TAG, "Package couldn't be installed in " + pkg.mPath);
             if ((res.returnCode=mLastScanError) == PackageManager.INSTALL_SUCCEEDED) {
@@ -10044,7 +10104,7 @@
                 removeInstalledPackageLI(newPackage, true);
             }
             // Add back the old system package
-            scanPackageLI(oldPkg, parseFlags, SCAN_MONITOR | SCAN_UPDATE_SIGNATURE, 0, user);
+            scanPackageLI(oldPkg, parseFlags, SCAN_MONITOR | SCAN_UPDATE_SIGNATURE, 0, user, null);
             // Restore the old system information in Settings
             synchronized(mPackages) {
                 if (updatedSettings) {
@@ -10278,10 +10338,10 @@
         pkg.applicationInfo.nativeLibraryDir = args.getNativeLibraryPath();
         if (replace) {
             replacePackageLI(pkg, parseFlags, scanMode, args.user,
-                    installerPackageName, res);
+                    installerPackageName, res, args.abiOverride);
         } else {
             installNewPackageLI(pkg, parseFlags, scanMode | SCAN_DELETE_DATA_ON_FAILURES, args.user,
-                    installerPackageName, res);
+                    installerPackageName, res, args.abiOverride);
         }
         synchronized (mPackages) {
             final PackageSetting ps = mSettings.mPackages.get(pkgName);
@@ -10698,7 +10758,7 @@
             parseFlags |= PackageParser.PARSE_IS_PRIVILEGED;
         }
         PackageParser.Package newPkg = scanPackageLI(disabledPs.codePath,
-                parseFlags, SCAN_MONITOR | SCAN_NO_PATHS, 0, null);
+                parseFlags, SCAN_MONITOR | SCAN_NO_PATHS, 0, null, null);
 
         if (newPkg == null) {
             Slog.w(TAG, "Failed to restore system package:" + newPs.name
@@ -11778,8 +11838,8 @@
                     }
                 }
                 if (removed.size() > 0) {
-                    for (int j=0; j<removed.size(); j++) {
-                        PreferredActivity pa = removed.get(i);
+                    for (int r=0; r<removed.size(); r++) {
+                        PreferredActivity pa = removed.get(r);
                         Slog.w(TAG, "Removing dangling preferred activity: "
                                 + pa.mPref.mComponent);
                         pir.removeFilter(pa);
@@ -12511,7 +12571,7 @@
                 doGc = true;
                 synchronized (mInstallLock) {
                     final PackageParser.Package pkg = scanPackageLI(new File(codePath), parseFlags,
-                            0, 0, null);
+                            0, 0, null, null);
                     // Scan the package
                     if (pkg != null) {
                         /*
diff --git a/services/core/java/com/android/server/pm/SELinuxMMAC.java b/services/core/java/com/android/server/pm/SELinuxMMAC.java
index d70c725..c78249b 100644
--- a/services/core/java/com/android/server/pm/SELinuxMMAC.java
+++ b/services/core/java/com/android/server/pm/SELinuxMMAC.java
@@ -52,25 +52,49 @@
     private static final boolean DEBUG_POLICY_INSTALL = DEBUG_POLICY || false;
 
     // Signature seinfo values read from policy.
-    private static HashMap<Signature, Policy> sSigSeinfo =
-        new HashMap<Signature, Policy>();
+    private static HashMap<Signature, Policy> sSigSeinfo = new HashMap<Signature, Policy>();
 
     // Default seinfo read from policy.
     private static String sDefaultSeinfo = null;
 
-    // Locations of potential install policy files.
-    private static final File[] INSTALL_POLICY_FILE = {
-        new File(Environment.getDataDirectory(), "security/mac_permissions.xml"),
-        new File(Environment.getRootDirectory(), "etc/security/mac_permissions.xml"),
-        null};
+    // Data policy override version file.
+    private static final String DATA_VERSION_FILE =
+            Environment.getDataDirectory() + "/security/current/selinux_version";
 
-    // Location of seapp_contexts policy file.
-    private static final String SEAPP_CONTEXTS_FILE = "/seapp_contexts";
+    // Base policy version file.
+    private static final String BASE_VERSION_FILE = "/selinux_version";
+
+    // Whether override security policies should be loaded.
+    private static final boolean USE_OVERRIDE_POLICY = useOverridePolicy();
+
+    // Data override mac_permissions.xml policy file.
+    private static final String DATA_MAC_PERMISSIONS =
+            Environment.getDataDirectory() + "/security/current/mac_permissions.xml";
+
+    // Base mac_permissions.xml policy file.
+    private static final String BASE_MAC_PERMISSIONS =
+            Environment.getRootDirectory() + "/etc/security/mac_permissions.xml";
+
+    // Determine which mac_permissions.xml file to use.
+    private static final String MAC_PERMISSIONS = USE_OVERRIDE_POLICY ?
+            DATA_MAC_PERMISSIONS : BASE_MAC_PERMISSIONS;
+
+    // Data override seapp_contexts policy file.
+    private static final String DATA_SEAPP_CONTEXTS =
+            Environment.getDataDirectory() + "/security/current/seapp_contexts";
+
+    // Base seapp_contexts policy file.
+    private static final String BASE_SEAPP_CONTEXTS = "/seapp_contexts";
+
+    // Determine which seapp_contexts file to use.
+    private static final String SEAPP_CONTEXTS = USE_OVERRIDE_POLICY ?
+            DATA_SEAPP_CONTEXTS : BASE_SEAPP_CONTEXTS;
 
     // Stores the hash of the last used seapp_contexts file.
     private static final String SEAPP_HASH_FILE =
             Environment.getDataDirectory().toString() + "/system/seapp_hash";
 
+
     // Signature policy stanzas
     static class Policy {
         private String seinfo;
@@ -112,51 +136,17 @@
         sDefaultSeinfo = null;
     }
 
-    /**
-     * Parses an MMAC install policy from a predefined list of locations.
-     * @return boolean indicating whether an install policy was correctly parsed.
-     */
     public static boolean readInstallPolicy() {
-
-        return readInstallPolicy(INSTALL_POLICY_FILE);
-    }
-
-    /**
-     * Parses an MMAC install policy given as an argument.
-     * @param policyFile object representing the path of the policy.
-     * @return boolean indicating whether the install policy was correctly parsed.
-     */
-    public static boolean readInstallPolicy(File policyFile) {
-
-        return readInstallPolicy(new File[]{policyFile,null});
-    }
-
-    private static boolean readInstallPolicy(File[] policyFiles) {
         // Temp structures to hold the rules while we parse the xml file.
         // We add all the rules together once we know there's no structural problems.
         HashMap<Signature, Policy> sigSeinfo = new HashMap<Signature, Policy>();
         String defaultSeinfo = null;
 
         FileReader policyFile = null;
-        int i = 0;
-        while (policyFile == null && policyFiles != null && policyFiles[i] != null) {
-            try {
-                policyFile = new FileReader(policyFiles[i]);
-                break;
-            } catch (FileNotFoundException e) {
-                Slog.d(TAG,"Couldn't find install policy " + policyFiles[i].getPath());
-            }
-            i++;
-        }
-
-        if (policyFile == null) {
-            Slog.d(TAG, "No policy file found. All seinfo values will be null.");
-            return false;
-        }
-
-        Slog.d(TAG, "Using install policy file " + policyFiles[i].getPath());
-
         try {
+            policyFile = new FileReader(MAC_PERMISSIONS);
+            Slog.d(TAG, "Using policy file " + MAC_PERMISSIONS);
+
             XmlPullParser parser = Xml.newPullParser();
             parser.setInput(policyFile);
 
@@ -199,20 +189,14 @@
                     XmlUtils.skipCurrentTag(parser);
                 }
             }
-        } catch (XmlPullParserException e) {
-            // An error outside of a stanza means a structural problem
-            // with the xml file. So ignore it.
-            Slog.w(TAG, "Got exception parsing ", e);
+        } catch (XmlPullParserException xpe) {
+            Slog.w(TAG, "Got exception parsing " + MAC_PERMISSIONS, xpe);
             return false;
-        } catch (IOException e) {
-            Slog.w(TAG, "Got exception parsing ", e);
+        } catch (IOException ioe) {
+            Slog.w(TAG, "Got exception parsing " + MAC_PERMISSIONS, ioe);
             return false;
         } finally {
-            try {
-                policyFile.close();
-            } catch (IOException e) {
-                //omit
-            }
+            IoUtils.closeQuietly(policyFile);
         }
 
         flushInstallPolicy();
@@ -412,7 +396,7 @@
         // Any error with the seapp_contexts file should be fatal
         byte[] currentHash = null;
         try {
-            currentHash = returnHash(SEAPP_CONTEXTS_FILE);
+            currentHash = returnHash(SEAPP_CONTEXTS);
         } catch (IOException ioe) {
             Slog.e(TAG, "Error with hashing seapp_contexts.", ioe);
             return false;
@@ -434,7 +418,7 @@
      */
     public static void setRestoreconDone() {
         try {
-            final byte[] currentHash = returnHash(SEAPP_CONTEXTS_FILE);
+            final byte[] currentHash = returnHash(SEAPP_CONTEXTS);
             dumpHash(new File(SEAPP_HASH_FILE), currentHash);
         } catch (IOException ioe) {
             Slog.e(TAG, "Error with saving hash to " + SEAPP_HASH_FILE, ioe);
@@ -485,4 +469,21 @@
             throw new RuntimeException(nsae);  // impossible
         }
     }
+
+    private static boolean useOverridePolicy() {
+        try {
+            final String overrideVersion = IoUtils.readFileAsString(DATA_VERSION_FILE);
+            final String baseVersion = IoUtils.readFileAsString(BASE_VERSION_FILE);
+            if (overrideVersion.equals(baseVersion)) {
+                return true;
+            }
+            Slog.e(TAG, "Override policy version '" + overrideVersion + "' doesn't match " +
+                   "base version '" + baseVersion + "'. Skipping override policy files.");
+        } catch (FileNotFoundException fnfe) {
+            // Override version file doesn't have to exist so silently ignore.
+        } catch (IOException ioe) {
+            Slog.w(TAG, "Skipping override policy files.", ioe);
+        }
+        return false;
+    }
 }
diff --git a/services/core/java/com/android/server/pm/Settings.java b/services/core/java/com/android/server/pm/Settings.java
index bb92611..3483fae 100644
--- a/services/core/java/com/android/server/pm/Settings.java
+++ b/services/core/java/com/android/server/pm/Settings.java
@@ -1944,7 +1944,8 @@
                     // TODO: check whether this is okay! as it is very
                     // similar to how preferred-activities are treated
                     readPersistentPreferredActivitiesLPw(parser, 0);
-                } else if (tagName.equals(TAG_FORWARDING_INTENT_FILTERS)) {
+                } else if (tagName.equals(TAG_FORWARDING_INTENT_FILTERS)
+                        || tagName.equals(TAG_CROSS_PROFILE_INTENT_FILTERS)) {
                     // TODO: check whether this is okay! as it is very
                     // similar to how preferred-activities are treated
                     readCrossProfileIntentFiltersLPw(parser, 0);
@@ -2939,6 +2940,26 @@
         file.delete();
         file = getUserPackagesStateBackupFile(userId);
         file.delete();
+        removeCrossProfileIntentFiltersToUserLPr(userId);
+    }
+
+    void removeCrossProfileIntentFiltersToUserLPr(int targetUserId) {
+        for (int i = 0; i < mCrossProfileIntentResolvers.size(); i++) {
+            int sourceUserId = mCrossProfileIntentResolvers.keyAt(i);
+            CrossProfileIntentResolver cpir = mCrossProfileIntentResolvers.get(sourceUserId);
+            boolean needsWriting = false;
+            HashSet<CrossProfileIntentFilter> cpifs =
+                    new HashSet<CrossProfileIntentFilter>(cpir.filterSet());
+            for (CrossProfileIntentFilter cpif : cpifs) {
+                if (cpif.getTargetUserId() == targetUserId) {
+                    needsWriting = true;
+                    cpir.removeFilter(cpif);
+                }
+            }
+            if (needsWriting) {
+                writePackageRestrictionsLPr(sourceUserId);
+            }
+        }
     }
 
     // This should be called (at least) whenever an application is removed
diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java
index 1bf40e0..7162683 100644
--- a/services/core/java/com/android/server/pm/UserManagerService.java
+++ b/services/core/java/com/android/server/pm/UserManagerService.java
@@ -107,6 +107,7 @@
     private static final String ATTR_TYPE_STRING_ARRAY = "sa";
     private static final String ATTR_TYPE_STRING = "s";
     private static final String ATTR_TYPE_BOOLEAN = "b";
+    private static final String ATTR_TYPE_INTEGER = "i";
 
     private static final String USER_INFO_DIR = "system" + File.separator + "users";
     private static final String USER_LIST_FILENAME = "userlist.xml";
@@ -1532,16 +1533,18 @@
                         String [] valueStrings = new String[values.size()];
                         values.toArray(valueStrings);
                         restrictions.putStringArray(key, valueStrings);
-                    } else if (ATTR_TYPE_BOOLEAN.equals(valType)) {
-                        restrictions.putBoolean(key, Boolean.parseBoolean(
-                                parser.nextText().trim()));
                     } else {
                         String value = parser.nextText().trim();
-                        restrictions.putString(key, value);
+                        if (ATTR_TYPE_BOOLEAN.equals(valType)) {
+                            restrictions.putBoolean(key, Boolean.parseBoolean(value));
+                        } else if (ATTR_TYPE_INTEGER.equals(valType)) {
+                            restrictions.putInt(key, Integer.parseInt(value));
+                        } else {
+                            restrictions.putString(key, value);
+                        }
                     }
                 }
             }
-
         } catch (IOException ioe) {
         } catch (XmlPullParserException pe) {
         } finally {
@@ -1581,6 +1584,9 @@
                 if (value instanceof Boolean) {
                     serializer.attribute(null, ATTR_VALUE_TYPE, ATTR_TYPE_BOOLEAN);
                     serializer.text(value.toString());
+                } else if (value instanceof Integer) {
+                    serializer.attribute(null, ATTR_VALUE_TYPE, ATTR_TYPE_INTEGER);
+                    serializer.text(value.toString());
                 } else if (value == null || value instanceof String) {
                     serializer.attribute(null, ATTR_VALUE_TYPE, ATTR_TYPE_STRING);
                     serializer.text(value != null ? (String) value : "");
diff --git a/services/core/java/com/android/server/task/StateChangedListener.java b/services/core/java/com/android/server/task/StateChangedListener.java
index db2d4ee..b1a4636 100644
--- a/services/core/java/com/android/server/task/StateChangedListener.java
+++ b/services/core/java/com/android/server/task/StateChangedListener.java
@@ -27,9 +27,8 @@
     /**
      * Called by the controller to notify the TaskManager that it should check on the state of a
      * task.
-     * @param taskStatus The state of the task which has changed.
      */
-    public void onTaskStateChanged(TaskStatus taskStatus);
+    public void onControllerStateChanged();
 
     /**
      * Called by the controller to notify the TaskManager that regardless of the state of the task,
diff --git a/services/core/java/com/android/server/task/TaskCompletedListener.java b/services/core/java/com/android/server/task/TaskCompletedListener.java
index 0210442..c53f5ca 100644
--- a/services/core/java/com/android/server/task/TaskCompletedListener.java
+++ b/services/core/java/com/android/server/task/TaskCompletedListener.java
@@ -16,6 +16,8 @@
 
 package com.android.server.task;
 
+import com.android.server.task.controllers.TaskStatus;
+
 /**
  * Used for communication between {@link com.android.server.task.TaskServiceContext} and the
  * {@link com.android.server.task.TaskManagerService}.
@@ -26,13 +28,5 @@
      * Callback for when a task is completed.
      * @param needsReschedule Whether the implementing class should reschedule this task.
      */
-    public void onTaskCompleted(int serviceToken, int taskId, boolean needsReschedule);
-
-    /**
-     * Callback for when the implementing class needs to clean up the
-     * {@link com.android.server.task.TaskServiceContext}. The scheduler can get this callback
-     * several times if the TaskServiceContext got into a bad state (for e.g. the client crashed
-     * and it needs to clean up).
-     */
-    public void onAllTasksCompleted(int serviceToken);
+    public void onTaskCompleted(TaskStatus taskStatus, boolean needsReschedule);
 }
diff --git a/services/core/java/com/android/server/task/TaskManagerService.java b/services/core/java/com/android/server/task/TaskManagerService.java
index 80030b4..d5b70e6 100644
--- a/services/core/java/com/android/server/task/TaskManagerService.java
+++ b/services/core/java/com/android/server/task/TaskManagerService.java
@@ -19,10 +19,12 @@
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
 import java.util.ArrayList;
+import java.util.Iterator;
 import java.util.List;
 
 import android.app.task.ITaskManager;
 import android.app.task.Task;
+import android.app.task.TaskManager;
 import android.content.Context;
 import android.content.pm.PackageManager;
 import android.os.Binder;
@@ -30,11 +32,17 @@
 import android.os.Looper;
 import android.os.Message;
 import android.os.RemoteException;
-import android.os.UserHandle;
+import android.os.SystemClock;
 import android.util.Slog;
 import android.util.SparseArray;
 
+import com.android.server.task.controllers.ConnectivityController;
+import com.android.server.task.controllers.IdleController;
+import com.android.server.task.controllers.StateController;
 import com.android.server.task.controllers.TaskStatus;
+import com.android.server.task.controllers.TimeController;
+
+import java.util.LinkedList;
 
 /**
  * Responsible for taking tasks representing work to be performed by a client app, and determining
@@ -44,61 +52,148 @@
  */
 public class TaskManagerService extends com.android.server.SystemService
         implements StateChangedListener, TaskCompletedListener {
+    // TODO: Switch this off for final version.
+    private static final boolean DEBUG = true;
+    /** The number of concurrent tasks we run at one time. */
+    private static final int MAX_TASK_CONTEXTS_COUNT = 3;
     static final String TAG = "TaskManager";
+    /**
+     * When a task fails, it gets rescheduled according to its backoff policy. To be nice, we allow
+     * this amount of time from the rescheduled time by which the retry must occur.
+     */
+    private static final long RESCHEDULE_WINDOW_SLOP_MILLIS = 5000L;
 
     /** Master list of tasks. */
     private final TaskStore mTasks;
 
+    static final int MSG_TASK_EXPIRED = 0;
+    static final int MSG_CHECK_TASKS = 1;
+
+    // Policy constants
+    /**
+     * Minimum # of idle tasks that must be ready in order to force the TM to schedule things
+     * early.
+     */
+    private static final int MIN_IDLE_COUNT = 1;
+    /**
+     * Minimum # of connectivity tasks that must be ready in order to force the TM to schedule
+     * things early.
+     */
+    private static final int MIN_CONNECTIVITY_COUNT = 2;
+    /**
+     * Minimum # of tasks (with no particular constraints) for which the TM will be happy running
+     * some work early.
+     */
+    private static final int MIN_READY_TASKS_COUNT = 4;
+
     /**
      * Track Services that have currently active or pending tasks. The index is provided by
      * {@link TaskStatus#getServiceToken()}
      */
-    private final SparseArray<TaskServiceContext> mActiveServices =
-            new SparseArray<TaskServiceContext>();
+    private final List<TaskServiceContext> mActiveServices = new LinkedList<TaskServiceContext>();
+    /** List of controllers that will notify this service of updates to tasks. */
+    private List<StateController> mControllers;
+    /**
+     * Queue of pending tasks. The TaskServiceContext class will receive tasks from this list
+     * when ready to execute them.
+     */
+    private final LinkedList<TaskStatus> mPendingTasks = new LinkedList<TaskStatus>();
 
     private final TaskHandler mHandler;
     private final TaskManagerStub mTaskManagerStub;
 
-    /** Check the pending queue and start any tasks. */
-    static final int MSG_RUN_PENDING = 0;
-    /** Initiate the stop task flow. */
-    static final int MSG_STOP_TASK = 1;
-    /** */
-    static final int MSG_CHECK_TASKS = 2;
+    /**
+     * Entry point from client to schedule the provided task.
+     * This will add the task to the
+     * @param task Task object containing execution parameters
+     * @param uId The package identifier of the application this task is for.
+     * @param canPersistTask Whether or not the client has the appropriate permissions for persisting
+     *                    of this task.
+     * @return Result of this operation. See <code>TaskManager#RESULT_*</code> return codes.
+     */
+    public int schedule(Task task, int uId, boolean canPersistTask) {
+        TaskStatus taskStatus = new TaskStatus(task, uId, canPersistTask);
+        return startTrackingTask(taskStatus) ?
+                TaskManager.RESULT_SUCCESS : TaskManager.RESULT_FAILURE;
+    }
 
-    private class TaskHandler extends Handler {
-
-        public TaskHandler(Looper looper) {
-            super(looper);
-        }
-
-        @Override
-        public void handleMessage(Message message) {
-            switch (message.what) {
-                case MSG_RUN_PENDING:
-
-                    break;
-                case MSG_STOP_TASK:
-
-                    break;
-                case MSG_CHECK_TASKS:
-                    checkTasks();
-                    break;
+    public List<Task> getPendingTasks(int uid) {
+        ArrayList<Task> outList = new ArrayList<Task>();
+        synchronized (mTasks) {
+            for (TaskStatus ts : mTasks.getTasks()) {
+                if (ts.getUid() == uid) {
+                    outList.add(ts.getTask());
+                }
             }
         }
+        return outList;
+    }
 
-        /**
-         * Called when we need to run through the list of all tasks and start/stop executing one or
-         * more of them.
-         */
-        private void checkTasks() {
-            synchronized (mTasks) {
-                final SparseArray<TaskStatus> tasks = mTasks.getTasks();
-                for (int i = 0; i < tasks.size(); i++) {
-                    TaskStatus ts = tasks.valueAt(i);
-                    if (ts.isReady() && ! isCurrentlyActive(ts)) {
-                        assignTaskToServiceContext(ts);
-                    }
+    /**
+     * Entry point from client to cancel all tasks originating from their uid.
+     * This will remove the task from the master list, and cancel the task if it was staged for
+     * execution or being executed.
+     * @param uid To check against for removal of a task.
+     */
+    public void cancelTaskForUid(int uid) {
+        // Remove from master list.
+        synchronized (mTasks) {
+            if (!mTasks.removeAllByUid(uid)) {
+                // If it's not in the master list, it's nowhere.
+                return;
+            }
+        }
+        // Remove from pending queue.
+        synchronized (mPendingTasks) {
+            Iterator<TaskStatus> it = mPendingTasks.iterator();
+            while (it.hasNext()) {
+                TaskStatus ts = it.next();
+                if (ts.getUid() == uid) {
+                    it.remove();
+                }
+            }
+        }
+        // Cancel if running.
+        synchronized (mActiveServices) {
+            for (TaskServiceContext tsc : mActiveServices) {
+                if (tsc.getRunningTask().getUid() == uid) {
+                    tsc.cancelExecutingTask();
+                }
+            }
+        }
+    }
+
+    /**
+     * Entry point from client to cancel the task corresponding to the taskId provided.
+     * This will remove the task from the master list, and cancel the task if it was staged for
+     * execution or being executed.
+     * @param uid Uid of the calling client.
+     * @param taskId Id of the task, provided at schedule-time.
+     */
+    public void cancelTask(int uid, int taskId) {
+        synchronized (mTasks) {
+            if (!mTasks.remove(uid, taskId)) {
+                // If it's not in the master list, it's nowhere.
+                return;
+            }
+        }
+        synchronized (mPendingTasks) {
+            Iterator<TaskStatus> it = mPendingTasks.iterator();
+            while (it.hasNext()) {
+                TaskStatus ts = it.next();
+                if (ts.getUid() == uid && ts.getTaskId() == taskId) {
+                    it.remove();
+                    // If we got it from pending, it didn't make it to active so return.
+                    return;
+                }
+            }
+        }
+        synchronized (mActiveServices) {
+            for (TaskServiceContext tsc : mActiveServices) {
+                if (tsc.getRunningTask().getUid() == uid &&
+                        tsc.getRunningTask().getTaskId() == taskId) {
+                    tsc.cancelExecutingTask();
+                    return;
                 }
             }
         }
@@ -118,6 +213,17 @@
         mTasks = new TaskStore(context);
         mHandler = new TaskHandler(context.getMainLooper());
         mTaskManagerStub = new TaskManagerStub();
+        // Create the "runners".
+        for (int i = 0; i < MAX_TASK_CONTEXTS_COUNT; i++) {
+            mActiveServices.add(
+                    new TaskServiceContext(this, context.getMainLooper()));
+        }
+
+        mControllers = new LinkedList<StateController>();
+        mControllers.add(ConnectivityController.get(this));
+        mControllers.add(TimeController.get(this));
+        mControllers.add(IdleController.get(this));
+        // TODO: Add BatteryStateController when implemented.
     }
 
     @Override
@@ -126,33 +232,156 @@
     }
 
     /**
-     * Entry point from client to schedule the provided task.
-     * This will add the task to the
-     * @param task Task object containing execution parameters
-     * @param userId The id of the user this task is for.
-     * @param uId The package identifier of the application this task is for.
-     * @param canPersistTask Whether or not the client has the appropriate permissions for
-     *     persisting of this task.
-     * @return Result of this operation. See <code>TaskManager#RESULT_*</code> return codes.
+     * Called when we have a task status object that we need to insert in our
+     * {@link com.android.server.task.TaskStore}, and make sure all the relevant controllers know
+     * about.
      */
-    public int schedule(Task task, int userId, int uId, boolean canPersistTask) {
-        TaskStatus taskStatus = mTasks.addNewTaskForUser(task, userId, uId, canPersistTask);
-        return 0;
-    }
-
-    public List<Task> getPendingTasks(int uid) {
-        ArrayList<Task> outList = new ArrayList<Task>(3);
+    private boolean startTrackingTask(TaskStatus taskStatus) {
+        boolean added = false;
         synchronized (mTasks) {
-            final SparseArray<TaskStatus> tasks = mTasks.getTasks();
-            final int N = tasks.size();
-            for (int i = 0; i < N; i++) {
-                TaskStatus ts = tasks.get(i);
-                if (ts.getUid() == uid) {
-                    outList.add(ts.getTask());
-                }
+            added = mTasks.add(taskStatus);
+        }
+        if (added) {
+            for (StateController controller : mControllers) {
+                controller.maybeStartTrackingTask(taskStatus);
             }
         }
-        return outList;
+        return added;
+    }
+
+    /**
+     * Called when we want to remove a TaskStatus object that we've finished executing. Returns the
+     * object removed.
+     */
+    private boolean stopTrackingTask(TaskStatus taskStatus) {
+        boolean removed;
+        synchronized (mTasks) {
+            // Remove from store as well as controllers.
+            removed = mTasks.remove(taskStatus);
+        }
+        if (removed) {
+            for (StateController controller : mControllers) {
+                controller.maybeStopTrackingTask(taskStatus);
+            }
+        }
+        return removed;
+    }
+
+    private boolean cancelTaskOnServiceContext(TaskStatus ts) {
+        synchronized (mActiveServices) {
+            for (TaskServiceContext tsc : mActiveServices) {
+                if (tsc.getRunningTask() == ts) {
+                    tsc.cancelExecutingTask();
+                    return true;
+                }
+            }
+            return false;
+        }
+    }
+
+    /**
+     * @param ts TaskStatus we are querying against.
+     * @return Whether or not the task represented by the status object is currently being run or
+     * is pending.
+     */
+    private boolean isCurrentlyActive(TaskStatus ts) {
+        synchronized (mActiveServices) {
+            for (TaskServiceContext serviceContext : mActiveServices) {
+                if (serviceContext.getRunningTask() == ts) {
+                    return true;
+                }
+            }
+            return false;
+        }
+    }
+
+    /**
+     * A task is rescheduled with exponential back-off if the client requests this from their
+     * execution logic.
+     * A caveat is for idle-mode tasks, for which the idle-mode constraint will usurp the
+     * timeliness of the reschedule. For an idle-mode task, no deadline is given.
+     * @param failureToReschedule Provided task status that we will reschedule.
+     * @return A newly instantiated TaskStatus with the same constraints as the last task except
+     * with adjusted timing constraints.
+     */
+    private TaskStatus getRescheduleTaskForFailure(TaskStatus failureToReschedule) {
+        final long elapsedNowMillis = SystemClock.elapsedRealtime();
+        final Task task = failureToReschedule.getTask();
+
+        final long initialBackoffMillis = task.getInitialBackoffMillis();
+        final int backoffAttempt = failureToReschedule.getNumFailures() + 1;
+        long newEarliestRuntimeElapsed = elapsedNowMillis;
+
+        switch (task.getBackoffPolicy()) {
+            case Task.BackoffPolicy.LINEAR:
+                newEarliestRuntimeElapsed += initialBackoffMillis * backoffAttempt;
+                break;
+            default:
+                if (DEBUG) {
+                    Slog.v(TAG, "Unrecognised back-off policy, defaulting to exponential.");
+                }
+            case Task.BackoffPolicy.EXPONENTIAL:
+                newEarliestRuntimeElapsed += Math.pow(initialBackoffMillis, backoffAttempt);
+                break;
+        }
+        long newLatestRuntimeElapsed = failureToReschedule.hasIdleConstraint() ? Long.MAX_VALUE
+                : newEarliestRuntimeElapsed + RESCHEDULE_WINDOW_SLOP_MILLIS;
+        return new TaskStatus(failureToReschedule, newEarliestRuntimeElapsed,
+                newLatestRuntimeElapsed, backoffAttempt);
+    }
+
+    /**
+     * Called after a periodic has executed so we can to re-add it. We take the last execution time
+     * of the task to be the time of completion (i.e. the time at which this function is called).
+     * This could be inaccurate b/c the task can run for as long as
+     * {@link com.android.server.task.TaskServiceContext#EXECUTING_TIMESLICE_MILLIS}, but will lead
+     * to underscheduling at least, rather than if we had taken the last execution time to be the
+     * start of the execution.
+     * @return A new task representing the execution criteria for this instantiation of the
+     * recurring task.
+     */
+    private TaskStatus getRescheduleTaskForPeriodic(TaskStatus periodicToReschedule) {
+        final long elapsedNow = SystemClock.elapsedRealtime();
+        // Compute how much of the period is remaining.
+        long runEarly = Math.max(periodicToReschedule.getLatestRunTimeElapsed() - elapsedNow, 0);
+        long newEarliestRunTimeElapsed = elapsedNow + runEarly;
+        long period = periodicToReschedule.getTask().getIntervalMillis();
+        long newLatestRuntimeElapsed = newEarliestRunTimeElapsed + period;
+
+        if (DEBUG) {
+            Slog.v(TAG, "Rescheduling executed periodic. New execution window [" +
+                    newEarliestRunTimeElapsed/1000 + ", " + newLatestRuntimeElapsed/1000 + "]s");
+        }
+        return new TaskStatus(periodicToReschedule, newEarliestRunTimeElapsed,
+                newLatestRuntimeElapsed, 0 /* backoffAttempt */);
+    }
+
+    // TaskCompletedListener implementations.
+
+    /**
+     * A task just finished executing. We fetch the
+     * {@link com.android.server.task.controllers.TaskStatus} from the store and depending on
+     * whether we want to reschedule we readd it to the controllers.
+     * @param taskStatus Completed task.
+     * @param needsReschedule Whether the implementing class should reschedule this task.
+     */
+    @Override
+    public void onTaskCompleted(TaskStatus taskStatus, boolean needsReschedule) {
+        if (!stopTrackingTask(taskStatus)) {
+            if (DEBUG) {
+                Slog.e(TAG, "Error removing task: could not find task to remove. Was task" +
+                        "removed while executing?");
+            }
+            return;
+        }
+        if (needsReschedule) {
+            TaskStatus rescheduled = getRescheduleTaskForFailure(taskStatus);
+            startTrackingTask(rescheduled);
+        } else if (taskStatus.getTask().isPeriodic()) {
+            TaskStatus rescheduledPeriodic = getRescheduleTaskForPeriodic(taskStatus);
+            startTrackingTask(rescheduledPeriodic);
+        }
+        mHandler.obtainMessage(MSG_CHECK_TASKS).sendToTarget();
     }
 
     // StateChangedListener implementations.
@@ -165,70 +394,123 @@
      * see which are ready. This will further decouple the controllers from the execution logic.
      */
     @Override
-    public void onTaskStateChanged(TaskStatus taskStatus) {
-        postCheckTasksMessage();
-
+    public void onControllerStateChanged() {
+        // Post a message to to run through the list of tasks and start/stop any that are eligible.
+        mHandler.obtainMessage(MSG_CHECK_TASKS).sendToTarget();
     }
 
     @Override
     public void onTaskDeadlineExpired(TaskStatus taskStatus) {
-
+        mHandler.obtainMessage(MSG_TASK_EXPIRED, taskStatus);
     }
 
-    // TaskCompletedListener implementations.
+    private class TaskHandler extends Handler {
 
-    /**
-     * A task just finished executing. We fetch the
-     * {@link com.android.server.task.controllers.TaskStatus} from the store and depending on
-     * whether we want to reschedule we readd it to the controllers.
-     * @param serviceToken key for the service context in {@link #mActiveServices}.
-     * @param taskId Id of the task that is complete.
-     * @param needsReschedule Whether the implementing class should reschedule this task.
-     */
-    @Override
-    public void onTaskCompleted(int serviceToken, int taskId, boolean needsReschedule) {
-        final TaskServiceContext serviceContext = mActiveServices.get(serviceToken);
-        if (serviceContext == null) {
-            Slog.e(TAG, "Task completed for invalid service context; " + serviceToken);
-            return;
+        public TaskHandler(Looper looper) {
+            super(looper);
         }
 
-    }
-
-    @Override
-    public void onAllTasksCompleted(int serviceToken) {
-        
-    }
-
-    private void assignTaskToServiceContext(TaskStatus ts) {
-        TaskServiceContext serviceContext =
-                mActiveServices.get(ts.getServiceToken());
-        if (serviceContext == null) {
-            serviceContext = new TaskServiceContext(this, mHandler.getLooper(), ts);
-            mActiveServices.put(ts.getServiceToken(), serviceContext);
+        @Override
+        public void handleMessage(Message message) {
+            switch (message.what) {
+                case MSG_TASK_EXPIRED:
+                    final TaskStatus expired = (TaskStatus) message.obj;  // Unused for now.
+                    queueReadyTasksForExecutionH();
+                    break;
+                case MSG_CHECK_TASKS:
+                    // Check the list of tasks and run some of them if we feel inclined.
+                    maybeQueueReadyTasksForExecutionH();
+                    break;
+            }
+            maybeRunNextPendingTaskH();
+            // Don't remove TASK_EXPIRED in case one came along while processing the queue.
+            removeMessages(MSG_CHECK_TASKS);
         }
-        serviceContext.addPendingTask(ts);
-    }
 
-    /**
-     * @param ts TaskStatus we are querying against.
-     * @return Whether or not the task represented by the status object is currently being run or
-     * is pending.
-     */
-    private boolean isCurrentlyActive(TaskStatus ts) {
-        TaskServiceContext serviceContext = mActiveServices.get(ts.getServiceToken());
-        if (serviceContext == null) {
-            return false;
+        /**
+         * Run through list of tasks and execute all possible - at least one is expired so we do
+         * as many as we can.
+         */
+        private void queueReadyTasksForExecutionH() {
+            synchronized (mTasks) {
+                for (TaskStatus ts : mTasks.getTasks()) {
+                    final boolean criteriaSatisfied = ts.isReady();
+                    final boolean isRunning = isCurrentlyActive(ts);
+                    if (criteriaSatisfied && !isRunning) {
+                        synchronized (mPendingTasks) {
+                            mPendingTasks.add(ts);
+                        }
+                    } else if (!criteriaSatisfied && isRunning) {
+                        cancelTaskOnServiceContext(ts);
+                    }
+                }
+            }
         }
-        return serviceContext.hasTaskPending(ts);
-    }
 
-    /**
-     * Post a message to {@link #mHandler} to run through the list of tasks and start/stop any that
-     * are eligible.
-     */
-    private void postCheckTasksMessage() {
-        mHandler.obtainMessage(MSG_CHECK_TASKS).sendToTarget();
+        /**
+         * The state of at least one task has changed. Here is where we could enforce various
+         * policies on when we want to execute tasks.
+         * Right now the policy is such:
+         *      If >1 of the ready tasks is idle mode we send all of them off
+         *      if more than 2 network connectivity tasks are ready we send them all off.
+         *      If more than 4 tasks total are ready we send them all off.
+         *      TODO: It would be nice to consolidate these sort of high-level policies somewhere.
+         */
+        private void maybeQueueReadyTasksForExecutionH() {
+            synchronized (mTasks) {
+                int idleCount = 0;
+                int connectivityCount = 0;
+                List<TaskStatus> runnableTasks = new ArrayList<TaskStatus>();
+                for (TaskStatus ts : mTasks.getTasks()) {
+                    final boolean criteriaSatisfied = ts.isReady();
+                    final boolean isRunning = isCurrentlyActive(ts);
+                    if (criteriaSatisfied && !isRunning) {
+                        if (ts.hasIdleConstraint()) {
+                            idleCount++;
+                        }
+                        if (ts.hasConnectivityConstraint() || ts.hasMeteredConstraint()) {
+                            connectivityCount++;
+                        }
+                        runnableTasks.add(ts);
+                    } else if (!criteriaSatisfied && isRunning) {
+                        cancelTaskOnServiceContext(ts);
+                    }
+                }
+                if (idleCount >= MIN_IDLE_COUNT || connectivityCount >= MIN_CONNECTIVITY_COUNT ||
+                        runnableTasks.size() >= MIN_READY_TASKS_COUNT) {
+                    for (TaskStatus ts : runnableTasks) {
+                        synchronized (mPendingTasks) {
+                            mPendingTasks.add(ts);
+                        }
+                    }
+                }
+            }
+        }
+
+        /**
+         * Checks the state of the pending queue against any available
+         * {@link com.android.server.task.TaskServiceContext} that can run a new task.
+         * {@link com.android.server.task.TaskServiceContext}.
+         */
+        private void maybeRunNextPendingTaskH() {
+            TaskStatus nextPending;
+            synchronized (mPendingTasks) {
+                nextPending = mPendingTasks.poll();
+            }
+            if (nextPending == null) {
+                return;
+            }
+
+            synchronized (mActiveServices) {
+                for (TaskServiceContext tsc : mActiveServices) {
+                    if (tsc.isAvailable()) {
+                        if (tsc.executeRunnableTask(nextPending)) {
+                            return;
+                        }
+                    }
+                }
+            }
+        }
     }
 
     /**
@@ -268,11 +550,10 @@
         public int schedule(Task task) throws RemoteException {
             final boolean canPersist = canCallerPersistTasks();
             final int uid = Binder.getCallingUid();
-            final int userId = UserHandle.getCallingUserId();
 
             long ident = Binder.clearCallingIdentity();
             try {
-                return TaskManagerService.this.schedule(task, userId, uid, canPersist);
+                return TaskManagerService.this.schedule(task, uid, canPersist);
             } finally {
                 Binder.restoreCallingIdentity(ident);
             }
@@ -280,15 +561,38 @@
 
         @Override
         public List<Task> getAllPendingTasks() throws RemoteException {
-            return null;
+            final int uid = Binder.getCallingUid();
+
+            long ident = Binder.clearCallingIdentity();
+            try {
+                return TaskManagerService.this.getPendingTasks(uid);
+            } finally {
+                Binder.restoreCallingIdentity(ident);
+            }
         }
 
         @Override
         public void cancelAll() throws RemoteException {
+            final int uid = Binder.getCallingUid();
+
+            long ident = Binder.clearCallingIdentity();
+            try {
+                TaskManagerService.this.cancelTaskForUid(uid);
+            } finally {
+                Binder.restoreCallingIdentity(ident);
+            }
         }
 
         @Override
         public void cancel(int taskId) throws RemoteException {
+            final int uid = Binder.getCallingUid();
+
+            long ident = Binder.clearCallingIdentity();
+            try {
+                TaskManagerService.this.cancelTask(uid, taskId);
+            } finally {
+                Binder.restoreCallingIdentity(ident);
+            }
         }
 
         /**
@@ -311,9 +615,7 @@
         synchronized (mTasks) {
             pw.print("Registered tasks:");
             if (mTasks.size() > 0) {
-                SparseArray<TaskStatus> tasks = mTasks.getTasks();
-                for (int i = 0; i < tasks.size(); i++) {
-                    TaskStatus ts = tasks.get(i);
+                for (TaskStatus ts : mTasks.getTasks()) {
                     pw.println();
                     ts.dump(pw, "  ");
                 }
diff --git a/services/core/java/com/android/server/task/TaskServiceContext.java b/services/core/java/com/android/server/task/TaskServiceContext.java
index 165445a..75e9212 100644
--- a/services/core/java/com/android/server/task/TaskServiceContext.java
+++ b/services/core/java/com/android/server/task/TaskServiceContext.java
@@ -24,6 +24,7 @@
 import android.content.Context;
 import android.content.Intent;
 import android.content.ServiceConnection;
+import android.os.Binder;
 import android.os.Handler;
 import android.os.IBinder;
 import android.os.Looper;
@@ -36,20 +37,18 @@
 import android.util.Slog;
 import android.util.SparseArray;
 
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.annotations.VisibleForTesting;
 import com.android.server.task.controllers.TaskStatus;
 
 import java.util.concurrent.atomic.AtomicBoolean;
 
 /**
- * Maintains information required to bind to a {@link android.app.task.TaskService}. This binding
- * is reused to start concurrent tasks on the TaskService. Information here is unique
- * to the service.
- * Functionality provided by this class:
- *     - Manages wakelock for the service.
- *     - Sends onStartTask() and onStopTask() messages to client app, and handles callbacks.
- *     -
+ * Handles client binding and lifecycle of a task. A task will only execute one at a time on an
+ * instance of this class.
  */
 public class TaskServiceContext extends ITaskCallback.Stub implements ServiceConnection {
+    private static final boolean DEBUG = true;
     private static final String TAG = "TaskServiceContext";
     /** Define the maximum # of tasks allowed to run on a service at once. */
     private static final int defaultMaxActiveTasksPerService =
@@ -66,10 +65,10 @@
     };
 
     // States that a task occupies while interacting with the client.
-    private static final int VERB_STARTING = 0;
-    private static final int VERB_EXECUTING = 1;
-    private static final int VERB_STOPPING = 2;
-    private static final int VERB_PENDING = 3;
+    static final int VERB_BINDING = 0;
+    static final int VERB_STARTING = 1;
+    static final int VERB_EXECUTING = 2;
+    static final int VERB_STOPPING = 3;
 
     // Messages that result from interactions with the client service.
     /** System timed out waiting for a response. */
@@ -77,178 +76,173 @@
     /** Received a callback from client. */
     private static final int MSG_CALLBACK = 1;
     /** Run through list and start any ready tasks.*/
-    private static final int MSG_CHECK_PENDING = 2;
-    /** Cancel an active task. */
+    private static final int MSG_SERVICE_BOUND = 2;
+    /** Cancel a task. */
     private static final int MSG_CANCEL = 3;
-    /** Add a pending task. */
-    private static final int MSG_ADD_PENDING = 4;
-    /** Client crashed, so we need to wind things down. */
-    private static final int MSG_SHUTDOWN = 5;
+    /** Shutdown the Task. Used when the client crashes and we can't die gracefully.*/
+    private static final int MSG_SHUTDOWN_EXECUTION = 4;
 
-    /** Used to identify this task service context when communicating with the TaskManager. */
-    final int token;
-    final ComponentName component;
-    final int userId;
-    ITaskService service;
     private final Handler mCallbackHandler;
-    /** Tasks that haven't been sent to the client for execution yet. */
-    private final SparseArray<ActiveTask> mPending;
+    /** Make callbacks to {@link TaskManagerService} to inform on task completion status. */
+    private final TaskCompletedListener mCompletedListener;
     /** Used for service binding, etc. */
     private final Context mContext;
-    /** Make callbacks to {@link TaskManagerService} to inform on task completion status. */
-    final private TaskCompletedListener mCompletedListener;
-    private final PowerManager.WakeLock mWakeLock;
+    private PowerManager.WakeLock mWakeLock;
 
-    /** Whether this service is actively bound. */
-    boolean mBound;
+    // Execution state.
+    private TaskParams mParams;
+    @VisibleForTesting
+    int mVerb;
+    private AtomicBoolean mCancelled = new AtomicBoolean();
 
-    TaskServiceContext(TaskManagerService taskManager, Looper looper, TaskStatus taskStatus) {
-        mContext = taskManager.getContext();
-        this.component = taskStatus.getServiceComponent();
-        this.token = taskStatus.getServiceToken();
-        this.userId = taskStatus.getUserId();
+    /** All the information maintained about the task currently being executed. */
+    private TaskStatus mRunningTask;
+    /** Binder to the client service. */
+    ITaskService service;
+
+    private final Object mAvailableLock = new Object();
+    /** Whether this context is free. */
+    @GuardedBy("mAvailableLock")
+    private boolean mAvailable;
+
+    TaskServiceContext(TaskManagerService service, Looper looper) {
+        this(service.getContext(), service, looper);
+    }
+
+    @VisibleForTesting
+    TaskServiceContext(Context context, TaskCompletedListener completedListener, Looper looper) {
+        mContext = context;
         mCallbackHandler = new TaskServiceHandler(looper);
-        mPending = new SparseArray<ActiveTask>();
-        mCompletedListener = taskManager;
-        final PowerManager pm = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
+        mCompletedListener = completedListener;
+    }
+
+    /**
+     * Give a task to this context for execution. Callers must first check {@link #isAvailable()}
+     * to make sure this is a valid context.
+     * @param ts The status of the task that we are going to run.
+     * @return True if the task was accepted and is going to run.
+     */
+    boolean executeRunnableTask(TaskStatus ts) {
+        synchronized (mAvailableLock) {
+            if (!mAvailable) {
+                Slog.e(TAG, "Starting new runnable but context is unavailable > Error.");
+                return false;
+            }
+            mAvailable = false;
+        }
+
+        final PowerManager pm =
+                (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
         mWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK,
-                TM_WAKELOCK_PREFIX + component.getPackageName());
-        mWakeLock.setWorkSource(new WorkSource(taskStatus.getUid()));
+                TM_WAKELOCK_PREFIX + ts.getServiceComponent().getPackageName());
+        mWakeLock.setWorkSource(new WorkSource(ts.getUid()));
         mWakeLock.setReferenceCounted(false);
+
+        mRunningTask = ts;
+        mParams = new TaskParams(ts.getTaskId(), ts.getExtras(), this);
+
+        mVerb = VERB_BINDING;
+        final Intent intent = new Intent().setComponent(ts.getServiceComponent());
+        boolean binding = mContext.bindServiceAsUser(intent, this,
+                Context.BIND_AUTO_CREATE | Context.BIND_NOT_FOREGROUND,
+                new UserHandle(ts.getUserId()));
+        if (!binding) {
+            if (DEBUG) {
+                Slog.d(TAG, ts.getServiceComponent().getShortClassName() + " unavailable.");
+            }
+            return false;
+        }
+
+        return true;
+    }
+
+    /** Used externally to query the running task. Will return null if there is no task running. */
+    TaskStatus getRunningTask() {
+        return mRunningTask;
+    }
+
+    /** Called externally when a task that was scheduled for execution should be cancelled. */
+    void cancelExecutingTask() {
+        mCallbackHandler.obtainMessage(MSG_CANCEL).sendToTarget();
+    }
+
+    /**
+     * @return Whether this context is available to handle incoming work.
+     */
+    boolean isAvailable() {
+        synchronized (mAvailableLock) {
+            return mAvailable;
+        }
     }
 
     @Override
     public void taskFinished(int taskId, boolean reschedule) {
+        if (!verifyCallingUid()) {
+            return;
+        }
         mCallbackHandler.obtainMessage(MSG_CALLBACK, taskId, reschedule ? 1 : 0)
                 .sendToTarget();
     }
 
     @Override
     public void acknowledgeStopMessage(int taskId, boolean reschedule) {
+        if (!verifyCallingUid()) {
+            return;
+        }
         mCallbackHandler.obtainMessage(MSG_CALLBACK, taskId, reschedule ? 1 : 0)
                 .sendToTarget();
     }
 
     @Override
     public void acknowledgeStartMessage(int taskId, boolean ongoing) {
+        if (!verifyCallingUid()) {
+            return;
+        }
         mCallbackHandler.obtainMessage(MSG_CALLBACK, taskId, ongoing ? 1 : 0).sendToTarget();
     }
 
     /**
-     * Queue up this task to run on the client. This will execute the task as quickly as possible.
-     * @param ts Status of the task to run.
-     */
-    public void addPendingTask(TaskStatus ts) {
-        final TaskParams params = new TaskParams(ts.getTaskId(), ts.getExtras(), this);
-        final ActiveTask newTask = new ActiveTask(params, VERB_PENDING);
-        mCallbackHandler.obtainMessage(MSG_ADD_PENDING, newTask).sendToTarget();
-        if (!mBound) {
-            Intent intent = new Intent().setComponent(component);
-            boolean binding = mContext.bindServiceAsUser(intent, this,
-                    Context.BIND_AUTO_CREATE | Context.BIND_NOT_FOREGROUND,
-                    new UserHandle(userId));
-            if (!binding) {
-                Log.e(TAG, component.getShortClassName() + " unavailable.");
-                cancelPendingTask(ts);
-            }
-        }
-    }
-
-    /**
-     * Called externally when a task that was scheduled for execution should be cancelled.
-     * @param ts The status of the task to cancel.
-     */
-    public void cancelPendingTask(TaskStatus ts) {
-        mCallbackHandler.obtainMessage(MSG_CANCEL, ts.getTaskId(), -1 /* arg2 */)
-                .sendToTarget();
-    }
-
-    /**
-     * MSG_TIMEOUT is sent with the {@link com.android.server.task.TaskServiceContext.ActiveTask}
-     * set in the {@link Message#obj} field. This makes it easier to remove timeouts for a given
-     * ActiveTask.
-     * @param op Operation that is taking place.
-     */
-    private void scheduleOpTimeOut(ActiveTask op) {
-        mCallbackHandler.removeMessages(MSG_TIMEOUT, op);
-
-        final long timeoutMillis = (op.verb == VERB_EXECUTING) ?
-                EXECUTING_TIMESLICE_MILLIS : OP_TIMEOUT_MILLIS;
-        if (Log.isLoggable(TaskManagerService.TAG, Log.DEBUG)) {
-            Slog.d(TAG, "Scheduling time out for '" + component.getShortClassName() + "' tId: " +
-                    op.params.getTaskId() + ", in " + (timeoutMillis / 1000) + " s");
-        }
-        Message m = mCallbackHandler.obtainMessage(MSG_TIMEOUT, op);
-        mCallbackHandler.sendMessageDelayed(m, timeoutMillis);
-    }
-
-    /**
-     * @return true if this task is pending or active within this context.
-     */
-    public boolean hasTaskPending(TaskStatus taskStatus) {
-        synchronized (mPending) {
-            return mPending.get(taskStatus.getTaskId()) != null;
-        }
-    }
-
-    public boolean isBound() {
-        return mBound;
-    }
-
-    /**
-     * We acquire/release the wakelock on onServiceConnected/unbindService. This mirrors the work
+     * We acquire/release a wakelock on onServiceConnected/unbindService. This mirrors the work
      * we intend to send to the client - we stop sending work when the service is unbound so until
      * then we keep the wakelock.
-     * @param name The concrete component name of the service that has
-     * been connected.
+     * @param name The concrete component name of the service that has been connected.
      * @param service The IBinder of the Service's communication channel,
      */
     @Override
     public void onServiceConnected(ComponentName name, IBinder service) {
-        mBound = true;
+        if (!name.equals(mRunningTask.getServiceComponent())) {
+            mCallbackHandler.obtainMessage(MSG_SHUTDOWN_EXECUTION).sendToTarget();
+            return;
+        }
         this.service = ITaskService.Stub.asInterface(service);
-        // Remove all timeouts. We've just connected to the client so there are no other
-        // MSG_TIMEOUTs at this point.
+        // Remove all timeouts.
         mCallbackHandler.removeMessages(MSG_TIMEOUT);
         mWakeLock.acquire();
-        mCallbackHandler.obtainMessage(MSG_CHECK_PENDING).sendToTarget();
+        mCallbackHandler.obtainMessage(MSG_SERVICE_BOUND).sendToTarget();
     }
 
     /**
-     * When the client service crashes we can have a couple tasks executing, in various stages of
-     * undress. We'll cancel all of them and request that they be rescheduled.
+     * If the client service crashes we reschedule this task and clean up.
      * @param name The concrete component name of the service whose
      */
     @Override
     public void onServiceDisconnected(ComponentName name) {
-        // Service disconnected... probably client crashed.
-        startShutdown();
+        mCallbackHandler.obtainMessage(MSG_SHUTDOWN_EXECUTION).sendToTarget();
     }
 
     /**
-     * We don't just shutdown outright - we make sure the scheduler isn't going to send us any more
-     * tasks, then we do the shutdown.
+     * This class is reused across different clients, and passes itself in as a callback. Check
+     * whether the client exercising the callback is the client we expect.
+     * @return True if the binder calling is coming from the client we expect.
      */
-    private void startShutdown() {
-        mCompletedListener.onAllTasksCompleted(token);
-        mCallbackHandler.obtainMessage(MSG_SHUTDOWN).sendToTarget();
-    }
-
-    /** Tracks a task across its various state changes. */
-    private static class ActiveTask {
-        final TaskParams params;
-        int verb;
-        AtomicBoolean cancelled = new AtomicBoolean();
-
-        ActiveTask(TaskParams params, int verb) {
-            this.params = params;
-            this.verb = verb;
+    private boolean verifyCallingUid() {
+        if (mRunningTask == null || Binder.getCallingUid() != mRunningTask.getUid()) {
+            if (DEBUG) {
+                Slog.d(TAG, "Stale callback received, ignoring.");
+            }
+            return false;
         }
-
-        @Override
-        public String toString() {
-            return params.getTaskId() + " " + VERB_STRINGS[verb];
-        }
+        return true;
     }
 
     /**
@@ -264,52 +258,67 @@
         @Override
         public void handleMessage(Message message) {
             switch (message.what) {
-                case MSG_ADD_PENDING:
-                    if (message.obj != null) {
-                        ActiveTask pendingTask = (ActiveTask) message.obj;
-                        mPending.put(pendingTask.params.getTaskId(), pendingTask);
-                    }
-                    // fall through.
-                case MSG_CHECK_PENDING:
-                    checkPendingTasksH();
+                case MSG_SERVICE_BOUND:
+                    handleServiceBoundH();
                     break;
                 case MSG_CALLBACK:
-                    ActiveTask receivedCallback = mPending.get(message.arg1);
-                    removeMessages(MSG_TIMEOUT, receivedCallback);
-
-                    if (Log.isLoggable(TaskManagerService.TAG, Log.DEBUG)) {
-                        Log.d(TAG, "MSG_CALLBACK of : " + receivedCallback);
+                    if (DEBUG) {
+                        Slog.d(TAG, "MSG_CALLBACK of : " + mRunningTask);
                     }
+                    removeMessages(MSG_TIMEOUT);
 
-                    if (receivedCallback.verb == VERB_STARTING) {
+                    if (mVerb == VERB_STARTING) {
                         final boolean workOngoing = message.arg2 == 1;
-                        handleStartedH(receivedCallback, workOngoing);
-                    } else if (receivedCallback.verb == VERB_EXECUTING ||
-                            receivedCallback.verb == VERB_STOPPING) {
+                        handleStartedH(workOngoing);
+                    } else if (mVerb == VERB_EXECUTING ||
+                            mVerb == VERB_STOPPING) {
                         final boolean reschedule = message.arg2 == 1;
-                        handleFinishedH(receivedCallback, reschedule);
+                        handleFinishedH(reschedule);
                     } else {
-                        if (Log.isLoggable(TaskManagerService.TAG, Log.DEBUG)) {
-                            Log.d(TAG, "Unrecognised callback: " + receivedCallback);
+                        if (DEBUG) {
+                            Slog.d(TAG, "Unrecognised callback: " + mRunningTask);
                         }
                     }
                     break;
                 case MSG_CANCEL:
-                    ActiveTask cancelled = mPending.get(message.arg1);
-                    handleCancelH(cancelled);
+                    handleCancelH();
                     break;
                 case MSG_TIMEOUT:
-                    // Timeout msgs have the ActiveTask ref so we can remove them easily.
-                    handleOpTimeoutH((ActiveTask) message.obj);
+                    handleOpTimeoutH();
                     break;
-                case MSG_SHUTDOWN:
-                    handleShutdownH();
-                    break;
+                case MSG_SHUTDOWN_EXECUTION:
+                    closeAndCleanupTaskH(true /* needsReschedule */);
                 default:
                     Log.e(TAG, "Unrecognised message: " + message);
             }
         }
 
+        /** Start the task on the service. */
+        private void handleServiceBoundH() {
+            if (mVerb != VERB_BINDING) {
+                Slog.e(TAG, "Sending onStartTask for a task that isn't pending. "
+                        + VERB_STRINGS[mVerb]);
+                closeAndCleanupTaskH(false /* reschedule */);
+                return;
+            }
+            if (mCancelled.get()) {
+                if (DEBUG) {
+                    Slog.d(TAG, "Task cancelled while waiting for bind to complete. "
+                            + mRunningTask);
+                }
+                closeAndCleanupTaskH(true /* reschedule */);
+                return;
+            }
+            try {
+                mVerb = VERB_STARTING;
+                scheduleOpTimeOut();
+                service.startTask(mParams);
+            } catch (RemoteException e) {
+                Log.e(TAG, "Error sending onStart message to '" +
+                        mRunningTask.getServiceComponent().getShortClassName() + "' ", e);
+            }
+        }
+
         /**
          * State behaviours.
          * VERB_STARTING   -> Successful start, change task to VERB_EXECUTING and post timeout.
@@ -317,24 +326,25 @@
          *     _EXECUTING  -> Error
          *     _STOPPING   -> Error
          */
-        private void handleStartedH(ActiveTask started, boolean workOngoing) {
-            switch (started.verb) {
+        private void handleStartedH(boolean workOngoing) {
+            switch (mVerb) {
                 case VERB_STARTING:
-                    started.verb = VERB_EXECUTING;
+                    mVerb = VERB_EXECUTING;
                     if (!workOngoing) {
                         // Task is finished already so fast-forward to handleFinished.
-                        handleFinishedH(started, false);
+                        handleFinishedH(false);
                         return;
-                    } else if (started.cancelled.get()) {
-                        // Cancelled *while* waiting for acknowledgeStartMessage from client.
-                        handleCancelH(started);
-                        return;
-                    } else {
-                        scheduleOpTimeOut(started);
                     }
+                    if (mCancelled.get()) {
+                        // Cancelled *while* waiting for acknowledgeStartMessage from client.
+                        handleCancelH();
+                        return;
+                    }
+                    scheduleOpTimeOut();
                     break;
                 default:
-                    Log.e(TAG, "Handling started task but task wasn't starting! " + started);
+                    Log.e(TAG, "Handling started task but task wasn't starting! Was "
+                            + VERB_STRINGS[mVerb] + ".");
                     return;
             }
         }
@@ -345,155 +355,104 @@
          *     _STARTING   -> Error
          *     _PENDING    -> Error
          */
-        private void handleFinishedH(ActiveTask executedTask, boolean reschedule) {
-            switch (executedTask.verb) {
+        private void handleFinishedH(boolean reschedule) {
+            switch (mVerb) {
                 case VERB_EXECUTING:
                 case VERB_STOPPING:
-                    closeAndCleanupTaskH(executedTask, reschedule);
+                    closeAndCleanupTaskH(reschedule);
                     break;
                 default:
-                    Log.e(TAG, "Got an execution complete message for a task that wasn't being" +
-                            "executed. " + executedTask);
+                    Slog.e(TAG, "Got an execution complete message for a task that wasn't being" +
+                            "executed. Was " + VERB_STRINGS[mVerb] + ".");
             }
         }
 
         /**
          * A task can be in various states when a cancel request comes in:
-         * VERB_PENDING    -> Remove from queue.
-         *     _STARTING   -> Mark as cancelled and wait for {@link #acknowledgeStartMessage(int)}.
+         * VERB_BINDING    -> Cancelled before bind completed. Mark as cancelled and wait for
+         *                    {@link #onServiceConnected(android.content.ComponentName, android.os.IBinder)}
+         *     _STARTING   -> Mark as cancelled and wait for
+         *                    {@link TaskServiceContext#acknowledgeStartMessage(int, boolean)}
          *     _EXECUTING  -> call {@link #sendStopMessageH}}.
          *     _ENDING     -> No point in doing anything here, so we ignore.
          */
-        private void handleCancelH(ActiveTask cancelledTask) {
-            switch (cancelledTask.verb) {
-                case VERB_PENDING:
-                    mPending.remove(cancelledTask.params.getTaskId());
-                    break;
+        private void handleCancelH() {
+            switch (mVerb) {
+                case VERB_BINDING:
                 case VERB_STARTING:
-                    cancelledTask.cancelled.set(true);
+                    mCancelled.set(true);
                     break;
                 case VERB_EXECUTING:
-                    cancelledTask.verb = VERB_STOPPING;
-                    sendStopMessageH(cancelledTask);
+                    sendStopMessageH();
                     break;
                 case VERB_STOPPING:
                     // Nada.
                     break;
                 default:
-                    Log.e(TAG, "Cancelling a task without a valid verb: " + cancelledTask);
+                    Slog.e(TAG, "Cancelling a task without a valid verb: " + mVerb);
                     break;
             }
         }
 
-        /**
-         * This TaskServiceContext is shutting down. Remove all the tasks from the pending queue
-         * and reschedule them as if they had failed.
-         * Before posting this message, caller must invoke
-         * {@link com.android.server.task.TaskCompletedListener#onAllTasksCompleted(int)}.
-         */
-        private void handleShutdownH() {
-            for (int i = 0; i < mPending.size(); i++) {
-                ActiveTask at = mPending.valueAt(i);
-                closeAndCleanupTaskH(at, true /* needsReschedule */);
-            }
-            mWakeLock.release();
-            mContext.unbindService(TaskServiceContext.this);
-            service = null;
-            mBound = false;
-        }
-
-        /**
-         * MSG_TIMEOUT gets processed here.
-         * @param timedOutTask The task that timed out.
-         */
-        private void handleOpTimeoutH(ActiveTask timedOutTask) {
+        /** Process MSG_TIMEOUT here. */
+        private void handleOpTimeoutH() {
             if (Log.isLoggable(TaskManagerService.TAG, Log.DEBUG)) {
-                Log.d(TAG, "MSG_TIMEOUT of " + component.getShortClassName() + " : "
-                        + timedOutTask.params.getTaskId());
+                Log.d(TAG, "MSG_TIMEOUT of " +
+                        mRunningTask.getServiceComponent().getShortClassName() + " : "
+                        + mParams.getTaskId());
             }
 
-            final int taskId = timedOutTask.params.getTaskId();
-            switch (timedOutTask.verb) {
+            final int taskId = mParams.getTaskId();
+            switch (mVerb) {
                 case VERB_STARTING:
                     // Client unresponsive - wedged or failed to respond in time. We don't really
                     // know what happened so let's log it and notify the TaskManager
                     // FINISHED/NO-RETRY.
                     Log.e(TAG, "No response from client for onStartTask '" +
-                            component.getShortClassName() + "' tId: " + taskId);
-                    closeAndCleanupTaskH(timedOutTask, false /* needsReschedule */);
+                            mRunningTask.getServiceComponent().getShortClassName() + "' tId: "
+                            + taskId);
+                    closeAndCleanupTaskH(false /* needsReschedule */);
                     break;
                 case VERB_STOPPING:
                     // At least we got somewhere, so fail but ask the TaskManager to reschedule.
                     Log.e(TAG, "No response from client for onStopTask, '" +
-                            component.getShortClassName() + "' tId: " + taskId);
-                    closeAndCleanupTaskH(timedOutTask, true /* needsReschedule */);
+                            mRunningTask.getServiceComponent().getShortClassName() + "' tId: "
+                            + taskId);
+                    closeAndCleanupTaskH(true /* needsReschedule */);
                     break;
                 case VERB_EXECUTING:
                     // Not an error - client ran out of time.
                     Log.i(TAG, "Client timed out while executing (no taskFinished received)." +
                             " Reporting failure and asking for reschedule. "  +
-                            component.getShortClassName() + "' tId: " + taskId);
-                    sendStopMessageH(timedOutTask);
+                            mRunningTask.getServiceComponent().getShortClassName() + "' tId: "
+                            + taskId);
+                    sendStopMessageH();
                     break;
                 default:
                     Log.e(TAG, "Handling timeout for an unknown active task state: "
-                            + timedOutTask);
+                            + mRunningTask);
                     return;
             }
         }
 
         /**
-         * Called on the handler thread. Checks the state of the pending queue and starts the task
-         * if it can. The task only starts if there is capacity on the service.
+         * Already running, need to stop. Will switch {@link #mVerb} from VERB_EXECUTING ->
+         * VERB_STOPPING.
          */
-        private void checkPendingTasksH() {
-            if (!mBound) {
-                return;
-            }
-            for (int i = 0; i < mPending.size() && i < defaultMaxActiveTasksPerService; i++) {
-                ActiveTask at = mPending.valueAt(i);
-                if (at.verb != VERB_PENDING) {
-                    continue;
-                }
-                sendStartMessageH(at);
-            }
-        }
-
-        /**
-         * Already running, need to stop. Rund on handler.
-         * @param stoppingTask Task we are sending onStopMessage for. This task will be moved from
-         *                     VERB_EXECUTING -> VERB_STOPPING.
-         */
-        private void sendStopMessageH(ActiveTask stoppingTask) {
-            mCallbackHandler.removeMessages(MSG_TIMEOUT, stoppingTask);
-            if (stoppingTask.verb != VERB_EXECUTING) {
-                Log.e(TAG, "Sending onStopTask for a task that isn't started. " + stoppingTask);
-                // TODO: Handle error?
+        private void sendStopMessageH() {
+            mCallbackHandler.removeMessages(MSG_TIMEOUT);
+            if (mVerb != VERB_EXECUTING) {
+                Log.e(TAG, "Sending onStopTask for a task that isn't started. " + mRunningTask);
+                closeAndCleanupTaskH(false /* reschedule */);
                 return;
             }
             try {
-                service.stopTask(stoppingTask.params);
-                stoppingTask.verb = VERB_STOPPING;
-                scheduleOpTimeOut(stoppingTask);
+                mVerb = VERB_STOPPING;
+                scheduleOpTimeOut();
+                service.stopTask(mParams);
             } catch (RemoteException e) {
                 Log.e(TAG, "Error sending onStopTask to client.", e);
-                closeAndCleanupTaskH(stoppingTask, false);
-            }
-        }
-
-        /** Start the task on the service. */
-        private void sendStartMessageH(ActiveTask pendingTask) {
-            if (pendingTask.verb != VERB_PENDING) {
-                Log.e(TAG, "Sending onStartTask for a task that isn't pending. " + pendingTask);
-                // TODO: Handle error?
-            }
-            try {
-                service.startTask(pendingTask.params);
-                pendingTask.verb = VERB_STARTING;
-                scheduleOpTimeOut(pendingTask);
-            } catch (RemoteException e) {
-                Log.e(TAG, "Error sending onStart message to '" + component.getShortClassName()
-                        + "' ", e);
+                closeAndCleanupTaskH(false);
             }
         }
 
@@ -503,13 +462,42 @@
          * or from acknowledging the stop message we sent. Either way, we're done tracking it and
          * we want to clean up internally.
          */
-        private void closeAndCleanupTaskH(ActiveTask completedTask, boolean reschedule) {
-            removeMessages(MSG_TIMEOUT, completedTask);
-            mPending.remove(completedTask.params.getTaskId());
-            if (mPending.size() == 0) {
-                startShutdown();
+        private void closeAndCleanupTaskH(boolean reschedule) {
+            removeMessages(MSG_TIMEOUT);
+            mWakeLock.release();
+            mContext.unbindService(TaskServiceContext.this);
+            mWakeLock = null;
+
+            mRunningTask = null;
+            mParams = null;
+            mVerb = -1;
+            mCancelled.set(false);
+
+            service = null;
+
+            mCompletedListener.onTaskCompleted(mRunningTask, reschedule);
+            synchronized (mAvailableLock) {
+                mAvailable = true;
             }
-            mCompletedListener.onTaskCompleted(token, completedTask.params.getTaskId(), reschedule);
+        }
+
+        /**
+         * Called when sending a message to the client, over whose execution we have no control. If we
+         * haven't received a response in a certain amount of time, we want to give up and carry on
+         * with life.
+         */
+        private void scheduleOpTimeOut() {
+            mCallbackHandler.removeMessages(MSG_TIMEOUT);
+
+            final long timeoutMillis = (mVerb == VERB_EXECUTING) ?
+                    EXECUTING_TIMESLICE_MILLIS : OP_TIMEOUT_MILLIS;
+            if (DEBUG) {
+                Slog.d(TAG, "Scheduling time out for '" +
+                        mRunningTask.getServiceComponent().getShortClassName() + "' tId: " +
+                        mParams.getTaskId() + ", in " + (timeoutMillis / 1000) + " s");
+            }
+            Message m = mCallbackHandler.obtainMessage(MSG_TIMEOUT);
+            mCallbackHandler.sendMessageDelayed(m, timeoutMillis);
         }
     }
 }
diff --git a/services/core/java/com/android/server/task/TaskStore.java b/services/core/java/com/android/server/task/TaskStore.java
index 81187c8..f72ab22 100644
--- a/services/core/java/com/android/server/task/TaskStore.java
+++ b/services/core/java/com/android/server/task/TaskStore.java
@@ -18,10 +18,16 @@
 
 import android.app.task.Task;
 import android.content.Context;
+import android.util.ArraySet;
+import android.util.Slog;
 import android.util.SparseArray;
 
 import com.android.server.task.controllers.TaskStatus;
 
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.Set;
+
 /**
  * Maintain a list of classes, and accessor methods/logic for these tasks.
  * This class offers the following functionality:
@@ -35,53 +41,122 @@
  *       - This class is <strong>not</strong> thread-safe.
  */
 public class TaskStore {
-
-    /**
-     * Master list, indexed by {@link com.android.server.task.controllers.TaskStatus#hashCode()}.
-     */
-    final SparseArray<TaskStatus> mTasks;
+    private static final String TAG = "TaskManagerStore";
+    /** Threshold to adjust how often we want to write to the db. */
+    private static final int MAX_OPS_BEFORE_WRITE = 1;
+    final ArraySet<TaskStatus> mTasks;
     final Context mContext;
 
+    private int mDirtyOperations;
+
     TaskStore(Context context) {
-        mTasks = intialiseTaskMapFromDisk();
+        mTasks = intialiseTasksFromDisk();
         mContext = context;
+        mDirtyOperations = 0;
     }
 
     /**
-     * Add a task to the master list, persisting it if necessary.
-     * Will first check to see if the task already exists. If so, it will replace it.
-     * {@link android.content.pm.PackageManager} is queried to see if the calling package has
-     * permission to
-     * @param task Task to add.
-     * @return The initialised TaskStatus object if this operation was successful, null if it
-     * failed.
+     * Add a task to the master list, persisting it if necessary. If the TaskStatus already exists,
+     * it will be replaced.
+     * @param taskStatus Task to add.
+     * @return true if the operation succeeded.
      */
-    public TaskStatus addNewTaskForUser(Task task, int userId, int uId,
-                                                     boolean canPersistTask) {
-        TaskStatus taskStatus = TaskStatus.getForTaskAndUser(task, userId, uId);
-        if (canPersistTask && task.isPeriodic()) {
-            if (writeStatusToDisk()) {
-                mTasks.put(taskStatus.hashCode(), taskStatus);
+    public boolean add(TaskStatus taskStatus) {
+        if (taskStatus.isPersisted()) {
+            if (!maybeWriteStatusToDisk()) {
+                return false;
             }
         }
-        return taskStatus;
+        mTasks.remove(taskStatus);
+        mTasks.add(taskStatus);
+        return true;
+    }
+
+    public int size() {
+        return mTasks.size();
     }
 
     /**
-     * Remove the provided task. Will also delete the task if it was persisted. Note that this
-     * function does not return the validity of the operation, as we assume a delete will always
-     * succeed.
-     * @param task Task to remove.
+     * Remove the provided task. Will also delete the task if it was persisted.
+     * @return The TaskStatus that was removed, or null if an invalid token was provided.
      */
-    public void remove(Task task) {
+    public boolean remove(TaskStatus taskStatus) {
+        boolean removed = mTasks.remove(taskStatus);
+        if (!removed) {
+            Slog.e(TAG, "Error removing task: " + taskStatus);
+            return false;
+        } else {
+            maybeWriteStatusToDisk();
+        }
+        return true;
+    }
 
+    /**
+     * Removes all TaskStatus objects for a given uid from the master list. Note that it is
+     * possible to remove a task that is pending/active. This operation will succeed, and the
+     * removal will take effect when the task has completed executing.
+     * @param uid Uid of the requesting app.
+     * @return True if at least one task was removed, false if nothing matching the provided uId
+     * was found.
+     */
+    public boolean removeAllByUid(int uid) {
+        Iterator<TaskStatus> it = mTasks.iterator();
+        boolean removed = false;
+        while (it.hasNext()) {
+            TaskStatus ts = it.next();
+            if (ts.getUid() == uid) {
+                it.remove();
+                removed = true;
+            }
+        }
+        if (removed) {
+            maybeWriteStatusToDisk();
+        }
+        return removed;
+    }
+
+    /**
+     * Remove the TaskStatus that matches the provided uId and taskId.  Note that it is possible
+     * to remove a task that is pending/active. This operation will succeed, and the removal will
+     * take effect when the task has completed executing.
+     * @param uid Uid of the requesting app.
+     * @param taskId Task id, specified at schedule-time.
+     * @return true if a removal occurred, false if the provided parameters didn't match anything.
+     */
+    public boolean remove(int uid, int taskId) {
+        Iterator<TaskStatus> it = mTasks.iterator();
+        while (it.hasNext()) {
+            TaskStatus ts = it.next();
+            if (ts.getUid() == uid && ts.getTaskId() == taskId) {
+                it.remove();
+                maybeWriteStatusToDisk();
+                return true;
+            }
+        }
+        return false;
+    }
+
+    /**
+     * @return The live array of TaskStatus objects.
+     */
+    public Set<TaskStatus> getTasks() {
+        return mTasks;
     }
 
     /**
      * Every time the state changes we write all the tasks in one swathe, instead of trying to
      * track incremental changes.
+     * @return Whether the operation was successful. This will only fail for e.g. if the system is
+     * low on storage. If this happens, we continue as normal
      */
-    private boolean writeStatusToDisk() {
+    private boolean maybeWriteStatusToDisk() {
+        mDirtyOperations++;
+        if (mDirtyOperations > MAX_OPS_BEFORE_WRITE) {
+            for (TaskStatus ts : mTasks) {
+                //
+            }
+            mDirtyOperations = 0;
+        }
         return true;
     }
 
@@ -90,21 +165,7 @@
      * @return
      */
     // TODO: Implement this.
-    private SparseArray<TaskStatus> intialiseTaskMapFromDisk() {
-        return new SparseArray<TaskStatus>();
-    }
-
-    /**
-     * @return the number of tasks in the store
-     */
-    public int size() {
-        return mTasks.size();
-    }
-
-    /**
-     * @return The live array of TaskStatus objects.
-     */
-    public SparseArray<TaskStatus> getTasks() {
-        return mTasks;
+    private ArraySet<TaskStatus> intialiseTasksFromDisk() {
+        return new ArraySet<TaskStatus>();
     }
 }
diff --git a/services/core/java/com/android/server/task/controllers/ConnectivityController.java b/services/core/java/com/android/server/task/controllers/ConnectivityController.java
index a0038c5..474af8f 100644
--- a/services/core/java/com/android/server/task/controllers/ConnectivityController.java
+++ b/services/core/java/com/android/server/task/controllers/ConnectivityController.java
@@ -25,6 +25,7 @@
 import android.net.NetworkInfo;
 import android.os.UserHandle;
 import android.util.Log;
+import android.util.Slog;
 
 import com.android.server.task.TaskManagerService;
 
@@ -32,21 +33,33 @@
 import java.util.List;
 
 /**
- *
+ * Handles changes in connectivity.
+ * We are only interested in metered vs. unmetered networks, and we're interested in them on a
+ * per-user basis.
  */
 public class ConnectivityController extends StateController {
     private static final String TAG = "TaskManager.Connectivity";
+    private static final boolean DEBUG = true;
 
     private final List<TaskStatus> mTrackedTasks = new LinkedList<TaskStatus>();
     private final BroadcastReceiver mConnectivityChangedReceiver =
             new ConnectivityChangedReceiver();
+    /** Singleton. */
+    private static ConnectivityController mSingleton;
 
     /** Track whether the latest active network is metered. */
     private boolean mMetered;
     /** Track whether the latest active network is connected. */
     private boolean mConnectivity;
 
-    public ConnectivityController(TaskManagerService service) {
+    public static synchronized ConnectivityController get(TaskManagerService taskManager) {
+        if (mSingleton == null) {
+            mSingleton = new ConnectivityController(taskManager);
+        }
+        return mSingleton;
+    }
+
+    private ConnectivityController(TaskManagerService service) {
         super(service);
         // Register connectivity changed BR.
         IntentFilter intentFilter = new IntentFilter();
@@ -56,7 +69,7 @@
     }
 
     @Override
-    public void maybeTrackTaskState(TaskStatus taskStatus) {
+    public synchronized void maybeStartTrackingTask(TaskStatus taskStatus) {
         if (taskStatus.hasConnectivityConstraint() || taskStatus.hasMeteredConstraint()) {
             taskStatus.connectivityConstraintSatisfied.set(mConnectivity);
             taskStatus.meteredConstraintSatisfied.set(mMetered);
@@ -65,21 +78,28 @@
     }
 
     @Override
-    public void removeTaskStateIfTracked(TaskStatus taskStatus) {
+    public synchronized void maybeStopTrackingTask(TaskStatus taskStatus) {
         mTrackedTasks.remove(taskStatus);
     }
 
     /**
-     *
+     * @param userId Id of the user for whom we are updating the connectivity state.
      */
-    private void updateTrackedTasks() {
+    private void updateTrackedTasks(int userId) {
+        boolean changed = false;
         for (TaskStatus ts : mTrackedTasks) {
+            if (ts.getUserId() != userId) {
+                continue;
+            }
             boolean prevIsConnected = ts.connectivityConstraintSatisfied.getAndSet(mConnectivity);
             boolean prevIsMetered = ts.meteredConstraintSatisfied.getAndSet(mMetered);
             if (prevIsConnected != mConnectivity || prevIsMetered != mMetered) {
-                    mStateChangedListener.onTaskStateChanged(ts);
+                    changed = true;
             }
         }
+        if (changed) {
+            mStateChangedListener.onControllerStateChanged();
+        }
     }
 
     class ConnectivityChangedReceiver extends BroadcastReceiver {
@@ -103,17 +123,20 @@
                         context.getSystemService(Context.CONNECTIVITY_SERVICE);
                 final NetworkInfo activeNetwork = connManager.getActiveNetworkInfo();
                 // This broadcast gets sent a lot, only update if the active network has changed.
-                if (activeNetwork.getType() == networkType) {
+                if (activeNetwork != null && activeNetwork.getType() == networkType) {
+                    final int userid = context.getUserId();
                     mMetered = false;
                     mConnectivity =
                             !intent.getBooleanExtra(ConnectivityManager.EXTRA_NO_CONNECTIVITY, false);
                     if (mConnectivity) {  // No point making the call if we know there's no conn.
                         mMetered = connManager.isActiveNetworkMetered();
                     }
-                    updateTrackedTasks();
+                    updateTrackedTasks(userid);
                 }
             } else {
-                Log.w(TAG, "Unrecognised action in intent: " + action);
+                if (DEBUG) {
+                    Slog.d(TAG, "Unrecognised action in intent: " + action);
+                }
             }
         }
     };
diff --git a/services/core/java/com/android/server/task/controllers/IdleController.java b/services/core/java/com/android/server/task/controllers/IdleController.java
index a319a31..9489644 100644
--- a/services/core/java/com/android/server/task/controllers/IdleController.java
+++ b/services/core/java/com/android/server/task/controllers/IdleController.java
@@ -49,7 +49,7 @@
     private static Object sCreationLock = new Object();
     private static volatile IdleController sController;
 
-    public IdleController getController(TaskManagerService service) {
+    public static IdleController get(TaskManagerService service) {
         synchronized (sCreationLock) {
             if (sController == null) {
                 sController = new IdleController(service);
@@ -67,7 +67,7 @@
      * StateController interface
      */
     @Override
-    public void maybeTrackTaskState(TaskStatus taskStatus) {
+    public void maybeStartTrackingTask(TaskStatus taskStatus) {
         if (taskStatus.hasIdleConstraint()) {
             synchronized (mTrackedTasks) {
                 mTrackedTasks.add(taskStatus);
@@ -77,7 +77,7 @@
     }
 
     @Override
-    public void removeTaskStateIfTracked(TaskStatus taskStatus) {
+    public void maybeStopTrackingTask(TaskStatus taskStatus) {
         synchronized (mTrackedTasks) {
             mTrackedTasks.remove(taskStatus);
         }
@@ -90,9 +90,9 @@
         synchronized (mTrackedTasks) {
             for (TaskStatus task : mTrackedTasks) {
                 task.idleConstraintSatisfied.set(isIdle);
-                mStateChangedListener.onTaskStateChanged(task);
             }
         }
+        mStateChangedListener.onControllerStateChanged();
     }
 
     /**
diff --git a/services/core/java/com/android/server/task/controllers/StateController.java b/services/core/java/com/android/server/task/controllers/StateController.java
index e1cd662..ed31eac 100644
--- a/services/core/java/com/android/server/task/controllers/StateController.java
+++ b/services/core/java/com/android/server/task/controllers/StateController.java
@@ -42,10 +42,10 @@
      * Also called when updating a task, so implementing controllers have to be aware of
      * preexisting tasks.
      */
-    public abstract void maybeTrackTaskState(TaskStatus taskStatus);
+    public abstract void maybeStartTrackingTask(TaskStatus taskStatus);
     /**
      * Remove task - this will happen if the task is cancelled, completed, etc.
      */
-    public abstract void removeTaskStateIfTracked(TaskStatus taskStatus);
+    public abstract void maybeStopTrackingTask(TaskStatus taskStatus);
 
 }
diff --git a/services/core/java/com/android/server/task/controllers/TaskStatus.java b/services/core/java/com/android/server/task/controllers/TaskStatus.java
index d270016..b7f84ec 100644
--- a/services/core/java/com/android/server/task/controllers/TaskStatus.java
+++ b/services/core/java/com/android/server/task/controllers/TaskStatus.java
@@ -18,7 +18,6 @@
 
 import android.app.task.Task;
 import android.content.ComponentName;
-import android.content.pm.PackageParser;
 import android.os.Bundle;
 import android.os.SystemClock;
 import android.os.UserHandle;
@@ -39,65 +38,67 @@
  */
 public class TaskStatus {
     final Task task;
-    final int taskId;
     final int uId;
-    final Bundle extras;
 
+    /** At reschedule time we need to know whether to update task on disk. */
+    final boolean persisted;
+
+    // Constraints.
     final AtomicBoolean chargingConstraintSatisfied = new AtomicBoolean();
-    final AtomicBoolean timeConstraintSatisfied = new AtomicBoolean();
+    final AtomicBoolean timeDelayConstraintSatisfied = new AtomicBoolean();
+    final AtomicBoolean deadlineConstraintSatisfied = new AtomicBoolean();
     final AtomicBoolean idleConstraintSatisfied = new AtomicBoolean();
     final AtomicBoolean meteredConstraintSatisfied = new AtomicBoolean();
     final AtomicBoolean connectivityConstraintSatisfied = new AtomicBoolean();
 
-    private final boolean hasChargingConstraint;
-    private final boolean hasTimingConstraint;
-    private final boolean hasIdleConstraint;
-    private final boolean hasMeteredConstraint;
-    private final boolean hasConnectivityConstraint;
-
+    /**
+     * Earliest point in the future at which this task will be eligible to run. A value of 0
+     * indicates there is no delay constraint. See {@link #hasTimingDelayConstraint()}.
+     */
     private long earliestRunTimeElapsedMillis;
+    /**
+     * Latest point in the future at which this task must be run. A value of {@link Long#MAX_VALUE}
+     * indicates there is no deadline constraint. See {@link #hasDeadlineConstraint()}.
+     */
     private long latestRunTimeElapsedMillis;
 
+    private final int numFailures;
+
     /** Provide a handle to the service that this task will be run on. */
     public int getServiceToken() {
         return uId;
     }
 
-    /** Generate a TaskStatus object for a given task and uid. */
-    // TODO: reimplement this to reuse these objects instead of creating a new one each time?
-    public static TaskStatus getForTaskAndUser(Task task, int userId, int uId) {
-        return new TaskStatus(task, userId, uId);
-    }
-
-    /** Set up the state of a newly scheduled task. */
-    TaskStatus(Task task, int userId, int uId) {
+    /** Create a newly scheduled task. */
+    public TaskStatus(Task task, int uId, boolean persisted) {
         this.task = task;
-        this.taskId = task.getTaskId();
-        this.extras = task.getExtras();
         this.uId = uId;
+        this.numFailures = 0;
+        this.persisted = persisted;
 
-        hasChargingConstraint = task.isRequireCharging();
-        hasIdleConstraint = task.isRequireDeviceIdle();
-
+        final long elapsedNow = SystemClock.elapsedRealtime();
         // Timing constraints
         if (task.isPeriodic()) {
-            long elapsedNow = SystemClock.elapsedRealtime();
             earliestRunTimeElapsedMillis = elapsedNow;
             latestRunTimeElapsedMillis = elapsedNow + task.getIntervalMillis();
-            hasTimingConstraint = true;
-        } else if (task.getMinLatencyMillis() != 0L || task.getMaxExecutionDelayMillis() != 0L) {
-            earliestRunTimeElapsedMillis = task.getMinLatencyMillis() > 0L ?
-                    task.getMinLatencyMillis() : Long.MAX_VALUE;
-            latestRunTimeElapsedMillis = task.getMaxExecutionDelayMillis() > 0L ?
-                    task.getMaxExecutionDelayMillis() : Long.MAX_VALUE;
-            hasTimingConstraint = true;
         } else {
-            hasTimingConstraint = false;
+            earliestRunTimeElapsedMillis = task.hasEarlyConstraint() ?
+                    elapsedNow + task.getMinLatencyMillis() : 0L;
+            latestRunTimeElapsedMillis = task.hasLateConstraint() ?
+                    elapsedNow + task.getMaxExecutionDelayMillis() : Long.MAX_VALUE;
         }
+    }
 
-        // Networking constraints
-        hasMeteredConstraint = task.getNetworkCapabilities() == Task.NetworkType.UNMETERED;
-        hasConnectivityConstraint = task.getNetworkCapabilities() == Task.NetworkType.ANY;
+    public TaskStatus(TaskStatus rescheduling, long newEarliestRuntimeElapsed,
+                      long newLatestRuntimeElapsed, int backoffAttempt) {
+        this.task = rescheduling.task;
+
+        this.uId = rescheduling.getUid();
+        this.persisted = rescheduling.isPersisted();
+        this.numFailures = backoffAttempt;
+
+        earliestRunTimeElapsedMillis = newEarliestRuntimeElapsed;
+        latestRunTimeElapsedMillis = newLatestRuntimeElapsed;
     }
 
     public Task getTask() {
@@ -105,7 +106,11 @@
     }
 
     public int getTaskId() {
-        return taskId;
+        return task.getId();
+    }
+
+    public int getNumFailures() {
+        return numFailures;
     }
 
     public ComponentName getServiceComponent() {
@@ -121,52 +126,60 @@
     }
 
     public Bundle getExtras() {
-        return extras;
+        return task.getExtras();
     }
 
-    boolean hasConnectivityConstraint() {
-        return hasConnectivityConstraint;
+    public boolean hasConnectivityConstraint() {
+        return task.getNetworkCapabilities() == Task.NetworkType.ANY;
     }
 
-    boolean hasMeteredConstraint() {
-        return hasMeteredConstraint;
+    public boolean hasMeteredConstraint() {
+        return task.getNetworkCapabilities() == Task.NetworkType.UNMETERED;
     }
 
-    boolean hasChargingConstraint() {
-        return hasChargingConstraint;
+    public boolean hasChargingConstraint() {
+        return task.isRequireCharging();
     }
 
-    boolean hasTimingConstraint() {
-        return hasTimingConstraint;
+    public boolean hasTimingDelayConstraint() {
+        return earliestRunTimeElapsedMillis != 0L;
     }
 
-    boolean hasIdleConstraint() {
-        return hasIdleConstraint;
+    public boolean hasDeadlineConstraint() {
+        return latestRunTimeElapsedMillis != Long.MAX_VALUE;
     }
 
-    long getEarliestRunTime() {
+    public boolean hasIdleConstraint() {
+        return task.isRequireDeviceIdle();
+    }
+
+    public long getEarliestRunTime() {
         return earliestRunTimeElapsedMillis;
     }
 
-    long getLatestRunTime() {
+    public long getLatestRunTimeElapsed() {
         return latestRunTimeElapsedMillis;
     }
 
+    public boolean isPersisted() {
+        return persisted;
+    }
     /**
-     * @return whether this task is ready to run, based on its requirements.
+     * @return Whether or not this task is ready to run, based on its requirements.
      */
     public synchronized boolean isReady() {
-        return (!hasChargingConstraint || chargingConstraintSatisfied.get())
-                && (!hasTimingConstraint || timeConstraintSatisfied.get())
-                && (!hasConnectivityConstraint || connectivityConstraintSatisfied.get())
-                && (!hasMeteredConstraint || meteredConstraintSatisfied.get())
-                && (!hasIdleConstraint || idleConstraintSatisfied.get());
+        return (!hasChargingConstraint() || chargingConstraintSatisfied.get())
+                && (!hasTimingDelayConstraint() || timeDelayConstraintSatisfied.get())
+                && (!hasConnectivityConstraint() || connectivityConstraintSatisfied.get())
+                && (!hasMeteredConstraint() || meteredConstraintSatisfied.get())
+                && (!hasIdleConstraint() || idleConstraintSatisfied.get())
+                && (!hasDeadlineConstraint() || deadlineConstraintSatisfied.get());
     }
 
     @Override
     public int hashCode() {
         int result = getServiceComponent().hashCode();
-        result = 31 * result + taskId;
+        result = 31 * result + task.getId();
         result = 31 * result + uId;
         return result;
     }
@@ -177,14 +190,14 @@
         if (!(o instanceof TaskStatus)) return false;
 
         TaskStatus that = (TaskStatus) o;
-        return ((taskId == that.taskId)
+        return ((task.getId() == that.task.getId())
                 && (uId == that.uId)
                 && (getServiceComponent().equals(that.getServiceComponent())));
     }
 
     // Dumpsys infrastructure
     public void dump(PrintWriter pw, String prefix) {
-        pw.print(prefix); pw.print("Task "); pw.println(taskId);
+        pw.print(prefix); pw.print("Task "); pw.println(task.getId());
         pw.print(prefix); pw.print("uid="); pw.println(uId);
         pw.print(prefix); pw.print("component="); pw.println(task.getService());
     }
diff --git a/services/core/java/com/android/server/task/controllers/TimeController.java b/services/core/java/com/android/server/task/controllers/TimeController.java
index 6d97a53..72f312c 100644
--- a/services/core/java/com/android/server/task/controllers/TimeController.java
+++ b/services/core/java/com/android/server/task/controllers/TimeController.java
@@ -23,7 +23,6 @@
 import android.content.Intent;
 import android.content.IntentFilter;
 import android.os.SystemClock;
-import android.util.Log;
 
 import com.android.server.task.TaskManagerService;
 
@@ -54,8 +53,17 @@
     private AlarmManager mAlarmService = null;
     /** List of tracked tasks, sorted asc. by deadline */
     private final List<TaskStatus> mTrackedTasks = new LinkedList<TaskStatus>();
+    /** Singleton. */
+    private static TimeController mSingleton;
 
-    public TimeController(TaskManagerService service) {
+    public static synchronized TimeController get(TaskManagerService taskManager) {
+        if (mSingleton == null) {
+            mSingleton = new TimeController(taskManager);
+        }
+        return mSingleton;
+    }
+
+    private TimeController(TaskManagerService service) {
         super(service);
         mTaskExpiredAlarmIntent =
                 PendingIntent.getBroadcast(mContext, 0 /* ignored */,
@@ -75,8 +83,8 @@
      * list.
      */
     @Override
-    public synchronized void maybeTrackTaskState(TaskStatus task) {
-        if (task.hasTimingConstraint()) {
+    public synchronized void maybeStartTrackingTask(TaskStatus task) {
+        if (task.hasTimingDelayConstraint()) {
             ListIterator<TaskStatus> it = mTrackedTasks.listIterator(mTrackedTasks.size());
             while (it.hasPrevious()) {
                 TaskStatus ts = it.previous();
@@ -85,13 +93,13 @@
                     it.remove();
                     it.add(task);
                     break;
-                } else if (ts.getLatestRunTime() < task.getLatestRunTime()) {
+                } else if (ts.getLatestRunTimeElapsed() < task.getLatestRunTimeElapsed()) {
                     // Insert
                     it.add(task);
                     break;
                 }
             }
-            maybeUpdateAlarms(task.getEarliestRunTime(), task.getLatestRunTime());
+            maybeUpdateAlarms(task.getEarliestRunTime(), task.getLatestRunTimeElapsed());
         }
     }
 
@@ -100,12 +108,12 @@
      * so, update them.
      */
     @Override
-    public synchronized void removeTaskStateIfTracked(TaskStatus taskStatus) {
+    public synchronized void maybeStopTrackingTask(TaskStatus taskStatus) {
         if (mTrackedTasks.remove(taskStatus)) {
             if (mNextDelayExpiredElapsedMillis <= taskStatus.getEarliestRunTime()) {
                 handleTaskDelayExpired();
             }
-            if (mNextTaskExpiredElapsedMillis <= taskStatus.getLatestRunTime()) {
+            if (mNextTaskExpiredElapsedMillis <= taskStatus.getLatestRunTimeElapsed()) {
                 handleTaskDeadlineExpired();
             }
         }
@@ -140,10 +148,10 @@
      * back and forth.
      */
     private boolean canStopTrackingTask(TaskStatus taskStatus) {
-        final long elapsedNowMillis = SystemClock.elapsedRealtime();
-        return taskStatus.timeConstraintSatisfied.get() &&
-                (taskStatus.getLatestRunTime() == Long.MAX_VALUE ||
-                        taskStatus.getLatestRunTime() < elapsedNowMillis);
+        return (!taskStatus.hasTimingDelayConstraint() ||
+                taskStatus.timeDelayConstraintSatisfied.get()) &&
+                (!taskStatus.hasDeadlineConstraint() ||
+                        taskStatus.deadlineConstraintSatisfied.get());
     }
 
     private void maybeUpdateAlarms(long delayExpiredElapsed, long deadlineExpiredElapsed) {
@@ -174,10 +182,10 @@
         Iterator<TaskStatus> it = mTrackedTasks.iterator();
         while (it.hasNext()) {
             TaskStatus ts = it.next();
-            final long taskDeadline = ts.getLatestRunTime();
+            final long taskDeadline = ts.getLatestRunTimeElapsed();
 
             if (taskDeadline <= nowElapsedMillis) {
-                ts.timeConstraintSatisfied.set(true);
+                ts.deadlineConstraintSatisfied.set(true);
                 mStateChangedListener.onTaskDeadlineExpired(ts);
                 it.remove();
             } else {  // Sorted by expiry time, so take the next one and stop.
@@ -199,10 +207,12 @@
         Iterator<TaskStatus> it = mTrackedTasks.iterator();
         while (it.hasNext()) {
             final TaskStatus ts = it.next();
+            if (!ts.hasTimingDelayConstraint()) {
+                continue;
+            }
             final long taskDelayTime = ts.getEarliestRunTime();
             if (taskDelayTime < nowElapsedMillis) {
-                ts.timeConstraintSatisfied.set(true);
-                mStateChangedListener.onTaskStateChanged(ts);
+                ts.timeDelayConstraintSatisfied.set(true);
                 if (canStopTrackingTask(ts)) {
                     it.remove();
                 }
@@ -212,6 +222,7 @@
                 }
             }
         }
+        mStateChangedListener.onControllerStateChanged();
         maybeUpdateAlarms(nextDelayTime, Long.MAX_VALUE);
     }
 
diff --git a/services/core/java/com/android/server/trust/TrustAgentWrapper.java b/services/core/java/com/android/server/trust/TrustAgentWrapper.java
index 47ce3b6..f18939f 100644
--- a/services/core/java/com/android/server/trust/TrustAgentWrapper.java
+++ b/services/core/java/com/android/server/trust/TrustAgentWrapper.java
@@ -63,6 +63,11 @@
         public void handleMessage(Message msg) {
             switch (msg.what) {
                 case MSG_GRANT_TRUST:
+                    if (!isConnected()) {
+                        Log.w(TAG, "Agent is not connected, cannot grant trust: "
+                                + mName.flattenToShortString());
+                        return;
+                    }
                     mTrusted = true;
                     mMessage = (CharSequence) msg.obj;
                     boolean initiatedByUser = msg.arg1 != 0;
@@ -119,6 +124,7 @@
         public void onServiceConnected(ComponentName name, IBinder service) {
             if (DEBUG) Log.v(TAG, "TrustAgent started : " + name.flattenToString());
             mTrustAgentService = ITrustAgentService.Stub.asInterface(service);
+            mTrustManagerService.mArchive.logAgentConnected(mUserId, name);
             setCallback(mCallback);
         }
 
@@ -179,7 +185,10 @@
 
     public void unbind() {
         if (DEBUG) Log.v(TAG, "TrustAgent unbound : " + mName.flattenToShortString());
+        mTrustManagerService.mArchive.logAgentStopped(mUserId, mName);
         mContext.unbindService(mConnection);
+        mTrustAgentService = null;
+        mHandler.sendEmptyMessage(MSG_REVOKE_TRUST);
     }
 
     public boolean isConnected() {
diff --git a/services/core/java/com/android/server/trust/TrustArchive.java b/services/core/java/com/android/server/trust/TrustArchive.java
index aad156c..56950d2 100644
--- a/services/core/java/com/android/server/trust/TrustArchive.java
+++ b/services/core/java/com/android/server/trust/TrustArchive.java
@@ -33,6 +33,8 @@
     private static final int TYPE_REVOKE_TRUST = 1;
     private static final int TYPE_TRUST_TIMEOUT = 2;
     private static final int TYPE_AGENT_DIED = 3;
+    private static final int TYPE_AGENT_CONNECTED = 4;
+    private static final int TYPE_AGENT_STOPPED = 5;
 
     private static final int HISTORY_LIMIT = 200;
 
@@ -79,6 +81,14 @@
         addEvent(new Event(TYPE_AGENT_DIED, userId, agent, null, 0, false));
     }
 
+    public void logAgentConnected(int userId, ComponentName agent) {
+        addEvent(new Event(TYPE_AGENT_CONNECTED, userId, agent, null, 0, false));
+    }
+
+    public void logAgentStopped(int userId, ComponentName agent) {
+        addEvent(new Event(TYPE_AGENT_STOPPED, userId, agent, null, 0, false));
+    }
+
     private void addEvent(Event e) {
         if (mEvents.size() >= HISTORY_LIMIT) {
             mEvents.removeFirst();
@@ -152,6 +162,10 @@
                 return "TrustTimeout";
             case TYPE_AGENT_DIED:
                 return "AgentDied";
+            case TYPE_AGENT_CONNECTED:
+                return "AgentConnected";
+            case TYPE_AGENT_STOPPED:
+                return "AgentStopped";
             default:
                 return "Unknown(" + type + ")";
         }
diff --git a/services/core/java/com/android/server/tv/TvInputHal.java b/services/core/java/com/android/server/tv/TvInputHal.java
index 4bdd2be..34168a8 100644
--- a/services/core/java/com/android/server/tv/TvInputHal.java
+++ b/services/core/java/com/android/server/tv/TvInputHal.java
@@ -16,10 +16,10 @@
 
 package com.android.server.tv;
 
+import android.media.tv.TvInputHardwareInfo;
+import android.media.tv.TvStreamConfig;
 import android.os.Handler;
 import android.os.HandlerThread;
-import android.tv.TvInputHardwareInfo;
-import android.tv.TvStreamConfig;
 import android.view.Surface;
 
 /**
diff --git a/services/core/java/com/android/server/tv/TvInputHardwareManager.java b/services/core/java/com/android/server/tv/TvInputHardwareManager.java
index b95b0f0..e34f42b 100644
--- a/services/core/java/com/android/server/tv/TvInputHardwareManager.java
+++ b/services/core/java/com/android/server/tv/TvInputHardwareManager.java
@@ -17,12 +17,12 @@
 package com.android.server.tv;
 
 import android.content.Context;
+import android.media.tv.ITvInputHardware;
+import android.media.tv.ITvInputHardwareCallback;
+import android.media.tv.TvInputHardwareInfo;
+import android.media.tv.TvStreamConfig;
 import android.os.IBinder;
 import android.os.RemoteException;
-import android.tv.ITvInputHardware;
-import android.tv.ITvInputHardwareCallback;
-import android.tv.TvInputHardwareInfo;
-import android.tv.TvStreamConfig;
 import android.util.Slog;
 import android.util.SparseArray;
 import android.view.KeyEvent;
diff --git a/services/core/java/com/android/server/tv/TvInputManagerService.java b/services/core/java/com/android/server/tv/TvInputManagerService.java
index 6c38a4c..5eb3305 100644
--- a/services/core/java/com/android/server/tv/TvInputManagerService.java
+++ b/services/core/java/com/android/server/tv/TvInputManagerService.java
@@ -31,8 +31,21 @@
 import android.content.pm.ServiceInfo;
 import android.database.Cursor;
 import android.graphics.Rect;
+import android.media.tv.ITvInputClient;
+import android.media.tv.ITvInputHardware;
+import android.media.tv.ITvInputHardwareCallback;
+import android.media.tv.ITvInputManager;
+import android.media.tv.ITvInputService;
+import android.media.tv.ITvInputServiceCallback;
+import android.media.tv.ITvInputSession;
+import android.media.tv.ITvInputSessionCallback;
+import android.media.tv.TvContract;
+import android.media.tv.TvInputHardwareInfo;
+import android.media.tv.TvInputInfo;
+import android.media.tv.TvInputService;
 import android.net.Uri;
 import android.os.Binder;
+import android.os.Bundle;
 import android.os.Handler;
 import android.os.IBinder;
 import android.os.Looper;
@@ -40,18 +53,6 @@
 import android.os.Process;
 import android.os.RemoteException;
 import android.os.UserHandle;
-import android.provider.TvContract;
-import android.tv.ITvInputClient;
-import android.tv.ITvInputHardware;
-import android.tv.ITvInputHardwareCallback;
-import android.tv.ITvInputManager;
-import android.tv.ITvInputService;
-import android.tv.ITvInputServiceCallback;
-import android.tv.ITvInputSession;
-import android.tv.ITvInputSessionCallback;
-import android.tv.TvInputHardwareInfo;
-import android.tv.TvInputInfo;
-import android.tv.TvInputService;
 import android.util.Slog;
 import android.util.SparseArray;
 import android.view.InputChannel;
@@ -62,6 +63,10 @@
 import com.android.server.IoThread;
 import com.android.server.SystemService;
 
+import org.xmlpull.v1.XmlPullParserException;
+
+import java.io.IOException;
+
 import java.util.ArrayList;
 import java.util.HashMap;
 import java.util.List;
@@ -144,7 +149,8 @@
         if (DEBUG) Slog.d(TAG, "buildTvInputList");
         PackageManager pm = mContext.getPackageManager();
         List<ResolveInfo> services = pm.queryIntentServices(
-                new Intent(TvInputService.SERVICE_INTERFACE), PackageManager.GET_SERVICES);
+                new Intent(TvInputService.SERVICE_INTERFACE),
+                PackageManager.GET_SERVICES | PackageManager.GET_META_DATA);
         for (ResolveInfo ri : services) {
             ServiceInfo si = ri.serviceInfo;
             if (!android.Manifest.permission.BIND_TV_INPUT.equals(si.permission)) {
@@ -152,9 +158,13 @@
                         + android.Manifest.permission.BIND_TV_INPUT);
                 continue;
             }
-            TvInputInfo info = new TvInputInfo(ri);
-            if (DEBUG) Slog.d(TAG, "add " + info.getId());
-            userState.inputMap.put(info.getId(), info);
+            try {
+                TvInputInfo info = TvInputInfo.createTvInputInfo(mContext, ri);
+                if (DEBUG) Slog.d(TAG, "add " + info.getId());
+                userState.inputMap.put(info.getId(), info);
+            } catch (IOException | XmlPullParserException e) {
+                Slog.e(TAG, "Can't load TV input " + si.name, e);
+            }
         }
     }
 
@@ -203,11 +213,13 @@
                         Slog.e(TAG, "error in unregisterCallback", e);
                     }
                 }
-                serviceState.mClients.clear();
+                serviceState.mClientTokens.clear();
                 mContext.unbindService(serviceState.mConnection);
             }
             userState.serviceStateMap.clear();
 
+            userState.clientStateMap.clear();
+
             mUserStates.remove(userId);
         }
     }
@@ -272,7 +284,7 @@
             }
             serviceState.mReconnecting = false;
         }
-        boolean isStateEmpty = serviceState.mClients.isEmpty()
+        boolean isStateEmpty = serviceState.mClientTokens.isEmpty()
                 && serviceState.mSessionTokens.isEmpty();
         if (serviceState.mService == null && !isStateEmpty && userId == mCurrentUserId) {
             // This means that the service is not yet connected but its state indicates that we
@@ -303,10 +315,22 @@
         }
     }
 
+    private ClientState createClientStateLocked(IBinder clientToken, int userId) {
+        UserState userState = getUserStateLocked(userId);
+        ClientState clientState = new ClientState(clientToken, userId);
+        try {
+            clientToken.linkToDeath(clientState, 0);
+        } catch (RemoteException e) {
+            Slog.e(TAG, "Client is already died.");
+        }
+        userState.clientStateMap.put(clientToken, clientState);
+        return clientState;
+    }
+
     private void createSessionInternalLocked(ITvInputService service, final IBinder sessionToken,
             final int userId) {
-        final SessionState sessionState =
-                getUserStateLocked(userId).sessionStateMap.get(sessionToken);
+        final UserState userState = getUserStateLocked(userId);
+        final SessionState sessionState = userState.sessionStateMap.get(sessionToken);
         if (DEBUG) {
             Slog.d(TAG, "createSessionInternalLocked(inputId=" + sessionState.mInputId + ")");
         }
@@ -332,12 +356,55 @@
                         } catch (RemoteException e) {
                             Slog.e(TAG, "Session is already died.");
                         }
+
+                        IBinder clientToken = sessionState.mClient.asBinder();
+                        ClientState clientState = userState.clientStateMap.get(clientToken);
+                        if (clientState == null) {
+                            clientState = createClientStateLocked(clientToken, userId);
+                        }
+                        clientState.mSessionTokens.add(sessionState.mSessionToken);
+
                         sendSessionTokenToClientLocked(sessionState.mClient, sessionState.mInputId,
                                 sessionToken, channels[0], sessionState.mSeq, userId);
                     }
                     channels[0].dispose();
                 }
             }
+
+            @Override
+            public void onVideoSizeChanged(int width, int height) throws RemoteException {
+                synchronized (mLock) {
+                    if (DEBUG) {
+                        Slog.d(TAG, "onVideoSizeChanged(" + width + ", " + height + ")");
+                    }
+                    if (sessionState.mSession == null || sessionState.mClient == null) {
+                        return;
+                    }
+                    try {
+                        sessionState.mClient.onVideoSizeChanged(width, height, sessionState.mSeq);
+                    } catch (RemoteException e) {
+                        Slog.e(TAG, "error in onSessionEvent");
+                    }
+                }
+            }
+
+            @Override
+            public void onSessionEvent(String eventType, Bundle eventArgs) {
+                synchronized (mLock) {
+                    if (DEBUG) {
+                        Slog.d(TAG, "onEvent(what=" + eventType + ", data=" + eventArgs + ")");
+                    }
+                    if (sessionState.mSession == null || sessionState.mClient == null) {
+                        return;
+                    }
+                    try {
+                        sessionState.mClient.onSessionEvent(eventType, eventArgs,
+                                sessionState.mSeq);
+                    } catch (RemoteException e) {
+                        Slog.e(TAG, "error in onSessionEvent");
+                    }
+                }
+            }
         };
 
         // Create a session. When failed, send a null token immediately.
@@ -387,7 +454,16 @@
             mLogHandler.obtainMessage(LogHandler.MSG_CLOSE_ENTRY, args).sendToTarget();
         }
 
-        // Also remove the session token from the session token list of the current service.
+        // Also remove the session token from the session token list of the current client and
+        // service.
+        ClientState clientState = userState.clientStateMap.get(sessionState.mClient.asBinder());
+        if (clientState != null) {
+            clientState.mSessionTokens.remove(sessionToken);
+            if (clientState.isEmpty()) {
+                userState.clientStateMap.remove(sessionState.mClient.asBinder());
+            }
+        }
+
         ServiceState serviceState = userState.serviceStateMap.get(sessionState.mInputId);
         if (serviceState != null) {
             serviceState.mSessionTokens.remove(sessionToken);
@@ -395,11 +471,45 @@
         updateServiceConnectionLocked(sessionState.mInputId, userId);
     }
 
+    private void unregisterCallbackInternalLocked(IBinder clientToken, String inputId,
+            int userId) {
+        UserState userState = getUserStateLocked(userId);
+        ClientState clientState = userState.clientStateMap.get(clientToken);
+        if (clientState != null) {
+            clientState.mInputIds.remove(inputId);
+            if (clientState.isEmpty()) {
+                userState.clientStateMap.remove(clientToken);
+            }
+        }
+
+        ServiceState serviceState = userState.serviceStateMap.get(inputId);
+        if (serviceState == null) {
+            return;
+        }
+
+        // Remove this client from the client list and unregister the callback.
+        serviceState.mClientTokens.remove(clientToken);
+        if (!serviceState.mClientTokens.isEmpty()) {
+            // We have other clients who want to keep the callback. Do this later.
+            return;
+        }
+        if (serviceState.mService == null || serviceState.mCallback == null) {
+            return;
+        }
+        try {
+            serviceState.mService.unregisterCallback(serviceState.mCallback);
+        } catch (RemoteException e) {
+            Slog.e(TAG, "error in unregisterCallback", e);
+        } finally {
+            serviceState.mCallback = null;
+            updateServiceConnectionLocked(inputId, userId);
+        }
+    }
+
     private void broadcastServiceAvailabilityChangedLocked(ServiceState serviceState) {
-        for (IBinder iBinder : serviceState.mClients) {
-            ITvInputClient client = ITvInputClient.Stub.asInterface(iBinder);
+        for (IBinder clientToken : serviceState.mClientTokens) {
             try {
-                client.onAvailabilityChanged(
+                ITvInputClient.Stub.asInterface(clientToken).onAvailabilityChanged(
                         serviceState.mTvInputInfo.getId(), serviceState.mAvailable);
             } catch (RemoteException e) {
                 Slog.e(TAG, "error in onAvailabilityChanged", e);
@@ -462,10 +572,19 @@
                                 userState.inputMap.get(inputId), resolvedUserId);
                         userState.serviceStateMap.put(inputId, serviceState);
                     }
-                    IBinder iBinder = client.asBinder();
-                    if (!serviceState.mClients.contains(iBinder)) {
-                        serviceState.mClients.add(iBinder);
+                    IBinder clientToken = client.asBinder();
+                    if (!serviceState.mClientTokens.contains(clientToken)) {
+                        serviceState.mClientTokens.add(clientToken);
                     }
+
+                    ClientState clientState = userState.clientStateMap.get(clientToken);
+                    if (clientState == null) {
+                        clientState = createClientStateLocked(clientToken, resolvedUserId);
+                    }
+                    if (!clientState.mInputIds.contains(inputId)) {
+                        clientState.mInputIds.add(inputId);
+                    }
+
                     if (serviceState.mService != null) {
                         if (serviceState.mCallback != null) {
                             // We already handled.
@@ -493,29 +612,7 @@
             final long identity = Binder.clearCallingIdentity();
             try {
                 synchronized (mLock) {
-                    UserState userState = getUserStateLocked(resolvedUserId);
-                    ServiceState serviceState = userState.serviceStateMap.get(inputId);
-                    if (serviceState == null) {
-                        return;
-                    }
-
-                    // Remove this client from the client list and unregister the callback.
-                    serviceState.mClients.remove(client.asBinder());
-                    if (!serviceState.mClients.isEmpty()) {
-                        // We have other clients who want to keep the callback. Do this later.
-                        return;
-                    }
-                    if (serviceState.mService == null || serviceState.mCallback == null) {
-                        return;
-                    }
-                    try {
-                        serviceState.mService.unregisterCallback(serviceState.mCallback);
-                    } catch (RemoteException e) {
-                        Slog.e(TAG, "error in unregisterCallback", e);
-                    } finally {
-                        serviceState.mCallback = null;
-                        updateServiceConnectionLocked(inputId, resolvedUserId);
-                    }
+                    unregisterCallbackInternalLocked(client.asBinder(), inputId, resolvedUserId);
                 }
             } finally {
                 Binder.restoreCallingIdentity(identity);
@@ -546,8 +643,8 @@
 
                     // Create a new session token and a session state.
                     IBinder sessionToken = new Binder();
-                    SessionState sessionState = new SessionState(
-                            sessionToken, inputId, client, seq, callingUid, resolvedUserId);
+                    SessionState sessionState = new SessionState(sessionToken, inputId, client,
+                            seq, callingUid, resolvedUserId);
 
                     // Add them to the global session state map of the current user.
                     userState.sessionStateMap.put(sessionToken, sessionState);
@@ -799,6 +896,10 @@
         // A mapping from the TV input id to its TvInputInfo.
         private final Map<String, TvInputInfo> inputMap = new HashMap<String,TvInputInfo>();
 
+        // A mapping from the token of a client to its state.
+        private final Map<IBinder, ClientState> clientStateMap =
+                new HashMap<IBinder, ClientState>();
+
         // A mapping from the name of a TV input service to its state.
         private final Map<String, ServiceState> serviceStateMap =
                 new HashMap<String, ServiceState>();
@@ -808,9 +909,46 @@
                 new HashMap<IBinder, SessionState>();
     }
 
+    private final class ClientState implements IBinder.DeathRecipient {
+        private final List<String> mInputIds = new ArrayList<String>();
+        private final List<IBinder> mSessionTokens = new ArrayList<IBinder>();
+
+        private IBinder mClientToken;
+        private final int mUserId;
+
+        ClientState(IBinder clientToken, int userId) {
+            mClientToken = clientToken;
+            mUserId = userId;
+        }
+
+        public boolean isEmpty() {
+            return mInputIds.isEmpty() && mSessionTokens.isEmpty();
+        }
+
+        @Override
+        public void binderDied() {
+            synchronized (mLock) {
+                UserState userState = getUserStateLocked(mUserId);
+                // DO NOT remove the client state of clientStateMap in this method. It will be
+                // removed in releaseSessionLocked() or unregisterCallbackInternalLocked().
+                ClientState clientState = userState.clientStateMap.get(mClientToken);
+                if (clientState != null) {
+                    while (clientState.mSessionTokens.size() > 0) {
+                        releaseSessionLocked(
+                                clientState.mSessionTokens.get(0), Process.SYSTEM_UID, mUserId);
+                    }
+                    while (clientState.mInputIds.size() > 0) {
+                        unregisterCallbackInternalLocked(
+                                mClientToken, clientState.mInputIds.get(0), mUserId);
+                    }
+                }
+                mClientToken = null;
+            }
+        }
+    }
+
     private final class ServiceState {
-        // TODO: need to implement DeathRecipient for clients.
-        private final List<IBinder> mClients = new ArrayList<IBinder>();
+        private final List<IBinder> mClientTokens = new ArrayList<IBinder>();
         private final List<IBinder> mSessionTokens = new ArrayList<IBinder>();
         private final ServiceConnection mConnection;
         private final TvInputInfo mTvInputInfo;
@@ -833,13 +971,13 @@
         private final int mSeq;
         private final int mCallingUid;
         private final int mUserId;
-        private final IBinder mToken;
+        private final IBinder mSessionToken;
         private ITvInputSession mSession;
         private Uri mLogUri;
 
-        private SessionState(IBinder token, String inputId, ITvInputClient client, int seq,
+        private SessionState(IBinder sessionToken, String inputId, ITvInputClient client, int seq,
                 int callingUid, int userId) {
-            mToken = token;
+            mSessionToken = sessionToken;
             mInputId = inputId;
             mClient = client;
             mSeq = seq;
@@ -858,7 +996,7 @@
                         Slog.e(TAG, "error in onSessionReleased", e);
                     }
                 }
-                removeSessionStateLocked(mToken, mUserId);
+                removeSessionStateLocked(mSessionToken, mUserId);
             }
         }
     }
@@ -882,7 +1020,7 @@
                 serviceState.mService = ITvInputService.Stub.asInterface(service);
 
                 // Register a callback, if we need to.
-                if (!serviceState.mClients.isEmpty() && serviceState.mCallback == null) {
+                if (!serviceState.mClientTokens.isEmpty() && serviceState.mCallback == null) {
                     serviceState.mCallback = new ServiceCallback(mUserId);
                     try {
                         serviceState.mService.registerCallback(serviceState.mCallback);
@@ -1010,7 +1148,7 @@
                     TvContract.Programs.COLUMN_TITLE,
                     TvContract.Programs.COLUMN_START_TIME_UTC_MILLIS,
                     TvContract.Programs.COLUMN_END_TIME_UTC_MILLIS,
-                    TvContract.Programs.COLUMN_DESCRIPTION
+                    TvContract.Programs.COLUMN_SHORT_DESCRIPTION
             };
             String selection = TvContract.Programs.COLUMN_CHANNEL_ID + "=? AND "
                     + TvContract.Programs.COLUMN_START_TIME_UTC_MILLIS + "<=? AND "
diff --git a/services/core/java/com/android/server/updates/SELinuxPolicyInstallReceiver.java b/services/core/java/com/android/server/updates/SELinuxPolicyInstallReceiver.java
index 3c960c7..55dd4ab 100644
--- a/services/core/java/com/android/server/updates/SELinuxPolicyInstallReceiver.java
+++ b/services/core/java/com/android/server/updates/SELinuxPolicyInstallReceiver.java
@@ -40,12 +40,20 @@
     private static final String fileContextsPath = "file_contexts";
     private static final String propertyContextsPath = "property_contexts";
     private static final String seappContextsPath = "seapp_contexts";
+    private static final String versionPath = "selinux_version";
+    private static final String macPermissionsPath = "mac_permissions.xml";
 
     public SELinuxPolicyInstallReceiver() {
         super("/data/security/bundle", "sepolicy_bundle", "metadata/", "version");
     }
 
     private void backupContexts(File contexts) {
+        new File(contexts, versionPath).renameTo(
+                new File(contexts, versionPath + "_backup"));
+
+        new File(contexts, macPermissionsPath).renameTo(
+                new File(contexts, macPermissionsPath + "_backup"));
+
         new File(contexts, seappContextsPath).renameTo(
                 new File(contexts, seappContextsPath + "_backup"));
 
@@ -60,6 +68,8 @@
     }
 
     private void copyUpdate(File contexts) {
+        new File(updateDir, versionPath).renameTo(new File(contexts, versionPath));
+        new File(updateDir, macPermissionsPath).renameTo(new File(contexts, macPermissionsPath));
         new File(updateDir, seappContextsPath).renameTo(new File(contexts, seappContextsPath));
         new File(updateDir, propertyContextsPath).renameTo(new File(contexts, propertyContextsPath));
         new File(updateDir, fileContextsPath).renameTo(new File(contexts, fileContextsPath));
@@ -75,11 +85,13 @@
     }
 
     private int[] readChunkLengths(BufferedInputStream bundle) throws IOException {
-        int[] chunks = new int[4];
+        int[] chunks = new int[6];
         chunks[0] = readInt(bundle);
         chunks[1] = readInt(bundle);
         chunks[2] = readInt(bundle);
         chunks[3] = readInt(bundle);
+        chunks[4] = readInt(bundle);
+        chunks[5] = readInt(bundle);
         return chunks;
     }
 
@@ -94,10 +106,12 @@
         BufferedInputStream stream = new BufferedInputStream(new FileInputStream(updateContent));
         try {
             int[] chunkLengths = readChunkLengths(stream);
-            installFile(new File(updateDir, seappContextsPath), stream, chunkLengths[0]);
-            installFile(new File(updateDir, propertyContextsPath), stream, chunkLengths[1]);
-            installFile(new File(updateDir, fileContextsPath), stream, chunkLengths[2]);
-            installFile(new File(updateDir, sepolicyPath), stream, chunkLengths[3]);
+            installFile(new File(updateDir, versionPath), stream, chunkLengths[0]);
+            installFile(new File(updateDir, macPermissionsPath), stream, chunkLengths[1]);
+            installFile(new File(updateDir, seappContextsPath), stream, chunkLengths[2]);
+            installFile(new File(updateDir, propertyContextsPath), stream, chunkLengths[3]);
+            installFile(new File(updateDir, fileContextsPath), stream, chunkLengths[4]);
+            installFile(new File(updateDir, sepolicyPath), stream, chunkLengths[5]);
         } finally {
             IoUtils.closeQuietly(stream);
         }
diff --git a/services/core/java/com/android/server/wm/AppTransition.java b/services/core/java/com/android/server/wm/AppTransition.java
index e2d2ac6..e007600 100644
--- a/services/core/java/com/android/server/wm/AppTransition.java
+++ b/services/core/java/com/android/server/wm/AppTransition.java
@@ -24,9 +24,7 @@
 import android.os.Debug;
 import android.os.Handler;
 import android.os.IRemoteCallback;
-import android.os.SystemProperties;
 import android.util.Slog;
-import android.view.View;
 import android.view.WindowManager;
 import android.view.animation.AlphaAnimation;
 import android.view.animation.Animation;
@@ -117,7 +115,7 @@
     private static final float RECENTS_THUMBNAIL_FADEOUT_FRACTION = 0.25f;
 
     private static final int DEFAULT_APP_TRANSITION_DURATION = 250;
-    private static final int THUMBNAIL_APP_TRANSITION_DURATION = 225;
+    private static final int THUMBNAIL_APP_TRANSITION_DURATION = 275;
 
     private final Context mContext;
     private final Handler mH;
@@ -299,7 +297,7 @@
         return null;
     }
 
-    Animation loadAnimation(WindowManager.LayoutParams lp, int animAttr) {
+    Animation loadAnimationAttr(WindowManager.LayoutParams lp, int animAttr) {
         int anim = 0;
         Context context = mContext;
         if (animAttr >= 0) {
@@ -315,7 +313,19 @@
         return null;
     }
 
-    private Animation loadAnimation(String packageName, int resId) {
+    Animation loadAnimationRes(WindowManager.LayoutParams lp, int resId) {
+        Context context = mContext;
+        if (resId >= 0) {
+            AttributeCache.Entry ent = getCachedAnimations(lp);
+            if (ent != null) {
+                context = ent.context;
+            }
+            return AnimationUtils.loadAnimation(context, resId);
+        }
+        return null;
+    }
+
+    private Animation loadAnimationRes(String packageName, int resId) {
         int anim = 0;
         Context context = mContext;
         if (resId >= 0) {
@@ -695,11 +705,31 @@
 
 
     Animation loadAnimation(WindowManager.LayoutParams lp, int transit, boolean enter,
-                            int appWidth, int appHeight, int orientation,
-                            Rect containingFrame, Rect contentInsets, boolean isFullScreen) {
+            int appWidth, int appHeight, int orientation, Rect containingFrame, Rect contentInsets,
+            boolean isFullScreen, boolean isVoiceInteraction) {
         Animation a;
-        if (mNextAppTransitionType == NEXT_TRANSIT_TYPE_CUSTOM) {
-            a = loadAnimation(mNextAppTransitionPackage, enter ?
+        if (isVoiceInteraction && (transit == TRANSIT_ACTIVITY_OPEN
+                || transit == TRANSIT_TASK_OPEN
+                || transit == TRANSIT_TASK_TO_FRONT)) {
+            a = loadAnimationRes(lp, enter
+                    ? com.android.internal.R.anim.voice_activity_open_enter
+                    : com.android.internal.R.anim.voice_activity_open_exit);
+            if (DEBUG_APP_TRANSITIONS || DEBUG_ANIM) Slog.v(TAG,
+                    "applyAnimation voice:"
+                    + " anim=" + a + " transit=" + transit + " isEntrance=" + enter
+                    + " Callers=" + Debug.getCallers(3));
+        } else if (isVoiceInteraction && (transit == TRANSIT_ACTIVITY_CLOSE
+                || transit == TRANSIT_TASK_CLOSE
+                || transit == TRANSIT_TASK_TO_BACK)) {
+            a = loadAnimationRes(lp, enter
+                    ? com.android.internal.R.anim.voice_activity_close_enter
+                    : com.android.internal.R.anim.voice_activity_close_exit);
+            if (DEBUG_APP_TRANSITIONS || DEBUG_ANIM) Slog.v(TAG,
+                    "applyAnimation voice:"
+                    + " anim=" + a + " transit=" + transit + " isEntrance=" + enter
+                    + " Callers=" + Debug.getCallers(3));
+        } else if (mNextAppTransitionType == NEXT_TRANSIT_TYPE_CUSTOM) {
+            a = loadAnimationRes(mNextAppTransitionPackage, enter ?
                     mNextAppTransitionEnter : mNextAppTransitionExit);
             if (DEBUG_APP_TRANSITIONS || DEBUG_ANIM) Slog.v(TAG,
                     "applyAnimation:"
@@ -782,7 +812,7 @@
                             : WindowAnimation_wallpaperIntraCloseExitAnimation;
                     break;
             }
-            a = animAttr != 0 ? loadAnimation(lp, animAttr) : null;
+            a = animAttr != 0 ? loadAnimationAttr(lp, animAttr) : null;
             if (DEBUG_APP_TRANSITIONS || DEBUG_ANIM) Slog.v(TAG,
                     "applyAnimation:"
                     + " anim=" + a
diff --git a/services/core/java/com/android/server/wm/AppWindowToken.java b/services/core/java/com/android/server/wm/AppWindowToken.java
index ca4ad8a..12c15e2 100644
--- a/services/core/java/com/android/server/wm/AppWindowToken.java
+++ b/services/core/java/com/android/server/wm/AppWindowToken.java
@@ -50,6 +50,8 @@
 
     final WindowAnimator mAnimator;
 
+    final boolean voiceInteraction;
+
     int groupId = -1;
     boolean appFullscreen;
     int requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
@@ -107,11 +109,13 @@
 
     boolean mDeferRemoval;
 
-    AppWindowToken(WindowManagerService _service, IApplicationToken _token) {
+    AppWindowToken(WindowManagerService _service, IApplicationToken _token,
+            boolean _voiceInteraction) {
         super(_service, _token.asBinder(),
                 WindowManager.LayoutParams.TYPE_APPLICATION, true);
         appWindowToken = this;
         appToken = _token;
+        voiceInteraction = _voiceInteraction;
         mInputApplicationHandle = new InputApplicationHandle(this);
         mAnimator = service.mAnimator;
         mAppAnimator = new AppWindowAnimator(this);
@@ -249,7 +253,7 @@
     void dump(PrintWriter pw, String prefix) {
         super.dump(pw, prefix);
         if (appToken != null) {
-            pw.print(prefix); pw.println("app=true");
+            pw.print(prefix); pw.print("app=true voiceInteraction="); pw.println(voiceInteraction);
         }
         if (allAppWindows.size() > 0) {
             pw.print(prefix); pw.print("allAppWindows="); pw.println(allAppWindows);
diff --git a/services/core/java/com/android/server/wm/CircularDisplayMask.java b/services/core/java/com/android/server/wm/CircularDisplayMask.java
index 35d19c1..29bab22 100644
--- a/services/core/java/com/android/server/wm/CircularDisplayMask.java
+++ b/services/core/java/com/android/server/wm/CircularDisplayMask.java
@@ -21,6 +21,7 @@
 import android.graphics.Color;
 import android.graphics.Paint;
 import android.graphics.PixelFormat;
+import android.graphics.PorterDuff;
 import android.graphics.Rect;
 import android.view.Display;
 import android.view.Surface;
@@ -32,6 +33,10 @@
     private static final String TAG = "CircularDisplayMask";
 
     private static final int STROKE_WIDTH = 2;
+    // half the screen size
+    private static final int CIRCLE_RADIUS = 160;
+    // size of the chin
+    private static final int SCREEN_OFFSET = 30;
 
     private final SurfaceControl mSurfaceControl;
     private final Surface mSurface = new Surface();
@@ -40,12 +45,13 @@
     private boolean mDrawNeeded;
     private Paint mPaint;
     private int mRotation;
+    private boolean mVisible;
 
     public CircularDisplayMask(Display display, SurfaceSession session, int zOrder) {
         SurfaceControl ctrl = null;
         try {
             ctrl = new SurfaceControl(session, "CircularDisplayMask",
-                320, 290, PixelFormat.TRANSLUCENT, SurfaceControl.HIDDEN);
+                320, 320, PixelFormat.TRANSLUCENT, SurfaceControl.HIDDEN);
             ctrl.setLayerStack(display.getLayerStack());
             ctrl.setLayer(zOrder);
             ctrl.setPosition(0, 0);
@@ -63,12 +69,12 @@
     }
 
     private void drawIfNeeded() {
-        if (!mDrawNeeded) {
+        if (!mDrawNeeded || !mVisible) {
             return;
         }
         mDrawNeeded = false;
 
-        Rect dirty = new Rect(0, 0, mLastDW, mLastDH);
+        Rect dirty = new Rect(0, 0, 320, 320);
         Canvas c = null;
         try {
             c = mSurface.lockCanvas(dirty);
@@ -78,27 +84,23 @@
         if (c == null) {
             return;
         }
-        int cx = 160;
-        int cy = 160;
+        c.drawColor(Color.TRANSPARENT, PorterDuff.Mode.SRC);
         switch (mRotation) {
-            case Surface.ROTATION_0:
-            case Surface.ROTATION_90:
-                // chin bottom or right
-                cx = 160;
-                cy = 160;
-                break;
-            case Surface.ROTATION_180:
-                // chin top
-                cx = 160;
-                cy = 145;
-                break;
-            case Surface.ROTATION_270:
-                cx = 145;
-                cy = 160;
-                break;
+        case Surface.ROTATION_0:
+        case Surface.ROTATION_90:
+            // chin bottom or right
+            mSurfaceControl.setPosition(0, 0);
+            break;
+        case Surface.ROTATION_180:
+            // chin top
+            mSurfaceControl.setPosition(0, -SCREEN_OFFSET);
+            break;
+        case Surface.ROTATION_270:
+            // chin left
+            mSurfaceControl.setPosition(-SCREEN_OFFSET, 0);
+            break;
         }
-        c.drawCircle(cx, cy, 160, mPaint);
-
+        c.drawCircle(CIRCLE_RADIUS, CIRCLE_RADIUS, CIRCLE_RADIUS, mPaint);
         mSurface.unlockCanvasAndPost(c);
     }
 
@@ -108,6 +110,7 @@
         if (mSurfaceControl == null) {
             return;
         }
+        mVisible = on;
         drawIfNeeded();
         if (on) {
             mSurfaceControl.show();
@@ -117,14 +120,14 @@
     }
 
     void positionSurface(int dw, int dh, int rotation) {
-        if (mLastDW == dw && mLastDH == dh) {
+        if (mLastDW == dw && mLastDH == dh && mRotation == rotation) {
             return;
         }
         mLastDW = dw;
         mLastDH = dh;
-        mSurfaceControl.setSize(dw, dh);
         mDrawNeeded = true;
         mRotation = rotation;
+        drawIfNeeded();
     }
 
 }
diff --git a/services/core/java/com/android/server/wm/TaskStack.java b/services/core/java/com/android/server/wm/TaskStack.java
index 81db8b3..a354c45 100644
--- a/services/core/java/com/android/server/wm/TaskStack.java
+++ b/services/core/java/com/android/server/wm/TaskStack.java
@@ -174,14 +174,14 @@
             stackNdx = 0;
         } else {
             stackNdx = mTasks.size();
-            final int currentUserId = mService.mCurrentUserId;
-            if (task.mUserId != currentUserId) {
+            if (!mService.isCurrentProfileLocked(task.mUserId)) {
                 // Place the task below all current user tasks.
                 while (--stackNdx >= 0) {
-                    if (currentUserId != mTasks.get(stackNdx).mUserId) {
+                    if (!mService.isCurrentProfileLocked(mTasks.get(stackNdx).mUserId)) {
                         break;
                     }
                 }
+                // Put it above first non-current user task.
                 ++stackNdx;
             }
         }
@@ -352,7 +352,7 @@
         int top = mTasks.size();
         for (int taskNdx = 0; taskNdx < top; ++taskNdx) {
             Task task = mTasks.get(taskNdx);
-            if (task.mUserId == userId) {
+            if (mService.isCurrentProfileLocked(task.mUserId)) {
                 mTasks.remove(taskNdx);
                 mTasks.add(task);
                 --top;
diff --git a/services/core/java/com/android/server/wm/WindowAnimator.java b/services/core/java/com/android/server/wm/WindowAnimator.java
index 266527d..008d2fc 100644
--- a/services/core/java/com/android/server/wm/WindowAnimator.java
+++ b/services/core/java/com/android/server/wm/WindowAnimator.java
@@ -36,6 +36,7 @@
 import android.view.Display;
 import android.view.SurfaceControl;
 import android.view.WindowManagerPolicy;
+import android.view.animation.AlphaAnimation;
 import android.view.animation.Animation;
 
 import com.android.server.wm.WindowManagerService.LayoutFields;
@@ -50,6 +51,9 @@
 public class WindowAnimator {
     private static final String TAG = "WindowAnimator";
 
+    /** How long to give statusbar to clear the private keyguard flag when animating out */
+    private static final long KEYGUARD_ANIM_TIMEOUT_MS = 1000;
+
     final WindowManagerService mService;
     final Context mContext;
     final WindowManagerPolicy mPolicy;
@@ -82,6 +86,8 @@
 
     boolean mInitialized = false;
 
+    boolean mKeyguardGoingAway;
+
     // forceHiding states.
     static final int KEYGUARD_NOT_SHOWN     = 0;
     static final int KEYGUARD_ANIMATING_IN  = 1;
@@ -213,6 +219,29 @@
         final WindowList windows = mService.getWindowListLocked(displayId);
         ArrayList<WindowStateAnimator> unForceHiding = null;
         boolean wallpaperInUnForceHiding = false;
+
+        if (mKeyguardGoingAway) {
+            for (int i = windows.size() - 1; i >= 0; i--) {
+                WindowState win = windows.get(i);
+                if (!mPolicy.isKeyguardHostWindow(win.mAttrs)) {
+                    continue;
+                }
+                final WindowStateAnimator winAnimator = win.mWinAnimator;
+                if (mPolicy.doesForceHide(win.mAttrs)) {
+                    if (!winAnimator.mAnimating) {
+                        // Create a new animation to delay until keyguard is gone on its own.
+                        winAnimator.mAnimation = new AlphaAnimation(1.0f, 1.0f);
+                        winAnimator.mAnimation.setDuration(KEYGUARD_ANIM_TIMEOUT_MS);
+                        winAnimator.mAnimationIsEntrance = false;
+                    }
+                } else {
+                    mKeyguardGoingAway = false;
+                    winAnimator.clearAnimation();
+                }
+                break;
+            }
+        }
+
         mForceHiding = KEYGUARD_NOT_SHOWN;
 
         for (int i = windows.size() - 1; i >= 0; i--) {
@@ -239,7 +268,7 @@
                     }
                 }
 
-                if (mPolicy.doesForceHide(win, win.mAttrs)) {
+                if (mPolicy.doesForceHide(win.mAttrs)) {
                     if (!wasAnimating && nowAnimating) {
                         if (WindowManagerService.DEBUG_ANIM ||
                                 WindowManagerService.DEBUG_VISIBILITY) Slog.v(TAG,
@@ -252,6 +281,11 @@
                                     getPendingLayoutChanges(displayId));
                         }
                         mService.mFocusMayChange = true;
+                    } else if (mKeyguardGoingAway && !nowAnimating) {
+                        // Timeout!!
+                        Slog.e(TAG, "Timeout waiting for animation to startup");
+                        mPolicy.startKeyguardExitAnimation(0, 0);
+                        mKeyguardGoingAway = false;
                     }
                     if (win.isReadyForDisplay()) {
                         if (nowAnimating) {
@@ -265,7 +299,7 @@
                         }
                     }
                     if (WindowManagerService.DEBUG_VISIBILITY) Slog.v(TAG,
-                            "Force hide " + mForceHiding
+                            "Force hide " + forceHidingToString()
                             + " hasSurface=" + win.mHasSurface
                             + " policyVis=" + win.mPolicyVisibility
                             + " destroying=" + win.mDestroying
@@ -349,12 +383,20 @@
         // If we have windows that are being show due to them no longer
         // being force-hidden, apply the appropriate animation to them.
         if (unForceHiding != null) {
+            boolean startKeyguardExit = true;
             for (int i=unForceHiding.size()-1; i>=0; i--) {
                 Animation a = mPolicy.createForceHideEnterAnimation(wallpaperInUnForceHiding);
                 if (a != null) {
                     final WindowStateAnimator winAnimator = unForceHiding.get(i);
                     winAnimator.setAnimation(a);
                     winAnimator.mAnimationIsEntrance = true;
+                    if (startKeyguardExit) {
+                        // Do one time only.
+                        mPolicy.startKeyguardExitAnimation(mCurrentTime + a.getStartOffset(),
+                                a.getDuration());
+                        mKeyguardGoingAway = false;
+                        startKeyguardExit = false;
+                    }
                 }
             }
         }
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index c23d1ea..05502cf 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -2215,6 +2215,11 @@
                           + attrs.token + ".  Aborting.");
                     return WindowManagerGlobal.ADD_BAD_APP_TOKEN;
                 }
+                if (type == TYPE_VOICE_INTERACTION) {
+                    Slog.w(TAG, "Attempted to add voice interaction window with unknown token "
+                          + attrs.token + ".  Aborting.");
+                    return WindowManagerGlobal.ADD_BAD_APP_TOKEN;
+                }
                 if (type == TYPE_WALLPAPER) {
                     Slog.w(TAG, "Attempted to add wallpaper window with unknown token "
                           + attrs.token + ".  Aborting.");
@@ -2250,6 +2255,12 @@
                             + attrs.token + ".  Aborting.");
                       return WindowManagerGlobal.ADD_BAD_APP_TOKEN;
                 }
+            } else if (type == TYPE_VOICE_INTERACTION) {
+                if (token.windowType != TYPE_VOICE_INTERACTION) {
+                    Slog.w(TAG, "Attempted to add voice interaction window with bad token "
+                            + attrs.token + ".  Aborting.");
+                      return WindowManagerGlobal.ADD_BAD_APP_TOKEN;
+                }
             } else if (type == TYPE_WALLPAPER) {
                 if (token.windowType != TYPE_WALLPAPER) {
                     Slog.w(TAG, "Attempted to add wallpaper window with bad token "
@@ -3173,7 +3184,7 @@
     }
 
     private boolean applyAnimationLocked(AppWindowToken atoken,
-            WindowManager.LayoutParams lp, int transit, boolean enter) {
+            WindowManager.LayoutParams lp, int transit, boolean enter, boolean isVoiceInteraction) {
         // Only apply an animation if the display isn't frozen.  If it is
         // frozen, there is no reason to animate and it can cause strange
         // artifacts when we unfreeze the display if some different animation
@@ -3203,7 +3214,8 @@
             }
 
             Animation a = mAppTransition.loadAnimation(lp, transit, enter, width, height,
-                    mCurConfiguration.orientation, containingFrame, contentInsets, isFullScreen);
+                    mCurConfiguration.orientation, containingFrame, contentInsets, isFullScreen,
+                    isVoiceInteraction);
             if (a != null) {
                 if (DEBUG_ANIM) {
                     RuntimeException e = null;
@@ -3423,7 +3435,7 @@
     @Override
     public void addAppToken(int addPos, IApplicationToken token, int taskId, int stackId,
             int requestedOrientation, boolean fullscreen, boolean showWhenLocked, int userId,
-            int configChanges) {
+            int configChanges, boolean voiceInteraction) {
         if (!checkCallingPermission(android.Manifest.permission.MANAGE_APP_TOKENS,
                 "addAppToken()")) {
             throw new SecurityException("Requires MANAGE_APP_TOKENS permission");
@@ -3449,7 +3461,7 @@
                 Slog.w(TAG, "Attempted to add existing app token: " + token);
                 return;
             }
-            atoken = new AppWindowToken(this, token);
+            atoken = new AppWindowToken(this, token, voiceInteraction);
             atoken.inputDispatchingTimeoutNanos = inputDispatchingTimeoutNanos;
             atoken.groupId = taskId;
             atoken.appFullscreen = fullscreen;
@@ -4201,7 +4213,7 @@
     }
 
     boolean setTokenVisibilityLocked(AppWindowToken wtoken, WindowManager.LayoutParams lp,
-            boolean visible, int transit, boolean performLayout) {
+            boolean visible, int transit, boolean performLayout, boolean isVoiceInteraction) {
         boolean delayed = false;
 
         if (wtoken.clientHidden == visible) {
@@ -4222,7 +4234,7 @@
                 if (wtoken.mAppAnimator.animation == AppWindowAnimator.sDummyAnimation) {
                     wtoken.mAppAnimator.animation = null;
                 }
-                if (applyAnimationLocked(wtoken, lp, transit, visible)) {
+                if (applyAnimationLocked(wtoken, lp, transit, visible, isVoiceInteraction)) {
                     delayed = runningAppAnimation = true;
                 }
                 WindowState window = wtoken.findMainWindow();
@@ -4400,7 +4412,7 @@
 
             final long origId = Binder.clearCallingIdentity();
             setTokenVisibilityLocked(wtoken, null, visible, AppTransition.TRANSIT_UNSET,
-                    true);
+                    true, wtoken.voiceInteraction);
             wtoken.updateReportedVisibilityLocked();
             Binder.restoreCallingIdentity(origId);
         }
@@ -4547,7 +4559,7 @@
             if (basewtoken != null && (wtoken=basewtoken.appWindowToken) != null) {
                 if (DEBUG_APP_TRANSITIONS) Slog.v(TAG, "Removing app token: " + wtoken);
                 delayed = setTokenVisibilityLocked(wtoken, null, false,
-                        AppTransition.TRANSIT_UNSET, true);
+                        AppTransition.TRANSIT_UNSET, true, wtoken.voiceInteraction);
                 wtoken.inPendingTransaction = false;
                 mOpeningApps.remove(wtoken);
                 wtoken.waitingToShow = false;
@@ -5127,6 +5139,18 @@
     }
 
     @Override
+    public void keyguardGoingAway() {
+        if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DISABLE_KEYGUARD)
+                != PackageManager.PERMISSION_GRANTED) {
+            throw new SecurityException("Requires DISABLE_KEYGUARD permission");
+        }
+        synchronized (mWindowMap) {
+            mAnimator.mKeyguardGoingAway = true;
+            requestTraversalLocked();
+        }
+    }
+
+    @Override
     public void closeSystemDialogs(String reason) {
         synchronized(mWindowMap) {
             final int numDisplays = mDisplayContents.size();
@@ -5802,7 +5826,9 @@
                 // whether the screenshot should use the identity transformation matrix
                 // (e.g., enable it when taking a screenshot for recents, since we might be in
                 // the middle of the rotation animation, but don't want a rotated recent image).
-                rawss = SurfaceControl.screenshot(dw, dh, minLayer, maxLayer, false);
+                // TODO: Replace 'new Rect()' with the portion of the screen to capture for the
+                // screenshot.
+                rawss = SurfaceControl.screenshot(new Rect(), dw, dh, minLayer, maxLayer, false);
             }
         } while (!screenshotReady && retryCount <= MAX_SCREENSHOT_RETRIES);
         if (retryCount > MAX_SCREENSHOT_RETRIES)  Slog.i(TAG, "Screenshot max retries " +
@@ -7149,9 +7175,7 @@
         public static final int TAP_OUTSIDE_STACK = 31;
         public static final int NOTIFY_ACTIVITY_DRAWN = 32;
 
-        public static final int REMOVE_STARTING_TIMEOUT = 33;
-
-        public static final int SHOW_DISPLAY_MASK = 34;
+        public static final int SHOW_DISPLAY_MASK = 33;
 
         @Override
         public void handleMessage(Message msg) {
@@ -8528,6 +8552,7 @@
             LayoutParams animLp = null;
             int bestAnimLayer = -1;
             boolean fullscreenAnim = false;
+            boolean voiceInteraction = false;
 
             if (DEBUG_APP_TRANSITIONS) Slog.v(TAG,
                     "New wallpaper target=" + mWallpaperTarget
@@ -8572,6 +8597,8 @@
                     }
                 }
 
+                voiceInteraction |= wtoken.voiceInteraction;
+
                 if (wtoken.appFullscreen) {
                     WindowState ws = wtoken.findMainWindow();
                     if (ws != null) {
@@ -8644,7 +8671,7 @@
                 appAnimator.clearThumbnail();
                 wtoken.inPendingTransaction = false;
                 appAnimator.animation = null;
-                setTokenVisibilityLocked(wtoken, animLp, true, transit, false);
+                setTokenVisibilityLocked(wtoken, animLp, true, transit, false, voiceInteraction);
                 wtoken.updateReportedVisibilityLocked();
                 wtoken.waitingToShow = false;
 
@@ -8676,7 +8703,7 @@
                 wtoken.mAppAnimator.clearThumbnail();
                 wtoken.inPendingTransaction = false;
                 wtoken.mAppAnimator.animation = null;
-                setTokenVisibilityLocked(wtoken, animLp, false, transit, false);
+                setTokenVisibilityLocked(wtoken, animLp, false, transit, false, voiceInteraction);
                 wtoken.updateReportedVisibilityLocked();
                 wtoken.waitingToHide = false;
                 // Force the allDrawn flag, because we want to start
@@ -10266,10 +10293,6 @@
         mPolicy.lockNow(options);
     }
 
-    public void showRecentApps() {
-        mPolicy.showRecentApps();
-    }
-
     @Override
     public boolean isSafeModeEnabled() {
         return mSafeMode;
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index c88382c..4a80e3e 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -714,6 +714,11 @@
         return mAppToken != null ? mAppToken.appToken : null;
     }
 
+    @Override
+    public boolean isVoiceInteraction() {
+        return mAppToken != null ? mAppToken.voiceInteraction : false;
+    }
+
     boolean setInsetsChanged() {
         mOverscanInsetsChanged |= !mLastOverscanInsets.equals(mOverscanInsets);
         mContentInsetsChanged |= !mLastContentInsets.equals(mContentInsets);
diff --git a/services/core/java/com/android/server/wm/WindowStateAnimator.java b/services/core/java/com/android/server/wm/WindowStateAnimator.java
index 1e79dcb..e257ebc 100644
--- a/services/core/java/com/android/server/wm/WindowStateAnimator.java
+++ b/services/core/java/com/android/server/wm/WindowStateAnimator.java
@@ -1658,7 +1658,7 @@
                         break;
                 }
                 if (attr >= 0) {
-                    a = mService.mAppTransition.loadAnimation(mWin.mAttrs, attr);
+                    a = mService.mAppTransition.loadAnimationAttr(mWin.mAttrs, attr);
                 }
             }
             if (WindowManagerService.DEBUG_ANIM) Slog.v(TAG,
diff --git a/services/core/jni/com_android_server_AssetAtlasService.cpp b/services/core/jni/com_android_server_AssetAtlasService.cpp
index 163225e..9a5079d 100644
--- a/services/core/jni/com_android_server_AssetAtlasService.cpp
+++ b/services/core/jni/com_android_server_AssetAtlasService.cpp
@@ -18,6 +18,7 @@
 
 #include "jni.h"
 #include "JNIHelp.h"
+#include "android/graphics/GraphicsJNI.h"
 
 #include <android_view_GraphicBuffer.h>
 #include <cutils/log.h>
@@ -46,7 +47,7 @@
 // ----------------------------------------------------------------------------
 
 static struct {
-    jmethodID safeCanvasSwap;
+    jmethodID setNativeBitmap;
 } gCanvasClassInfo;
 
 #define INVOKEV(object, method, ...) \
@@ -63,9 +64,7 @@
     bitmap->setConfig(SkBitmap::kARGB_8888_Config, width, height);
     bitmap->allocPixels();
     bitmap->eraseColor(0);
-
-    SkCanvas* nativeCanvas = SkNEW_ARGS(SkCanvas, (*bitmap));
-    INVOKEV(canvas, gCanvasClassInfo.safeCanvasSwap, (jlong)nativeCanvas, false);
+    INVOKEV(canvas, gCanvasClassInfo.setNativeBitmap, reinterpret_cast<jlong>(bitmap));
 
     return reinterpret_cast<jlong>(bitmap);
 }
@@ -74,8 +73,7 @@
         jobject canvas, jlong bitmapHandle) {
 
     SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(bitmapHandle);
-    SkCanvas* nativeCanvas = SkNEW(SkCanvas);
-    INVOKEV(canvas, gCanvasClassInfo.safeCanvasSwap, (jlong)nativeCanvas, false);
+    INVOKEV(canvas, gCanvasClassInfo.setNativeBitmap, (jlong)0);
 
     delete bitmap;
 }
@@ -244,7 +242,7 @@
     jclass clazz;
 
     FIND_CLASS(clazz, "android/graphics/Canvas");
-    GET_METHOD_ID(gCanvasClassInfo.safeCanvasSwap, clazz, "safeCanvasSwap", "(JZ)V");
+    GET_METHOD_ID(gCanvasClassInfo.setNativeBitmap, clazz, "setNativeBitmap", "(J)V");
 
     return jniRegisterNativeMethods(env, kClassPathName, gMethods, NELEM(gMethods));
 }
diff --git a/services/core/jni/com_android_server_hdmi_HdmiCecController.cpp b/services/core/jni/com_android_server_hdmi_HdmiCecController.cpp
index 27c8876..a734026 100644
--- a/services/core/jni/com_android_server_hdmi_HdmiCecController.cpp
+++ b/services/core/jni/com_android_server_hdmi_HdmiCecController.cpp
@@ -21,12 +21,15 @@
 #include "JNIHelp.h"
 #include "ScopedPrimitiveArray.h"
 
-#include <string>
+#include <cstring>
 
+#include <android_os_MessageQueue.h>
 #include <android_runtime/AndroidRuntime.h>
 #include <android_runtime/Log.h>
 #include <hardware/hdmi_cec.h>
 #include <sys/param.h>
+#include <utils/Looper.h>
+#include <utils/RefBase.h>
 
 namespace android {
 
@@ -37,7 +40,8 @@
 
 class HdmiCecController {
 public:
-    HdmiCecController(hdmi_cec_device_t* device, jobject callbacksObj);
+    HdmiCecController(hdmi_cec_device_t* device, jobject callbacksObj,
+            const sp<Looper>& looper);
 
     void init();
 
@@ -54,51 +58,137 @@
     // Get vendor id used for vendor command.
     uint32_t getVendorId();
 
-private:
-    // Propagate the message up to Java layer.
-    void propagateCecCommand(const cec_message_t& message);
-    void propagateHotplugEvent(const hotplug_event_t& event);
+    jobject getCallbacksObj() const {
+        return mCallbacksObj;
+    }
 
+private:
     static void onReceived(const hdmi_event_t* event, void* arg);
-    static void checkAndClearExceptionFromCallback(JNIEnv* env, const char* methodName);
 
     hdmi_cec_device_t* mDevice;
     jobject mCallbacksObj;
+    sp<Looper> mLooper;
 };
 
-HdmiCecController::HdmiCecController(hdmi_cec_device_t* device, jobject callbacksObj) :
+// RefBase wrapper for hdmi_event_t. As hdmi_event_t coming from HAL
+// may keep its own lifetime, we need to copy it in order to delegate
+// it to service thread.
+class CecEventWrapper : public LightRefBase<CecEventWrapper> {
+public:
+    CecEventWrapper(const hdmi_event_t& event) {
+        // Copy message.
+        switch (event.type) {
+        case HDMI_EVENT_CEC_MESSAGE:
+            mEvent.cec.initiator = event.cec.initiator;
+            mEvent.cec.destination = event.cec.destination;
+            mEvent.cec.length = event.cec.length;
+            std::memcpy(mEvent.cec.body, event.cec.body, event.cec.length);
+            break;
+        case HDMI_EVENT_HOT_PLUG:
+            mEvent.hotplug.connected = event.hotplug.connected;
+            mEvent.hotplug.port = event.hotplug.port;
+            break;
+        case HDMI_EVENT_TX_STATUS:
+            mEvent.tx_status.status = event.tx_status.status;
+            mEvent.tx_status.opcode = event.tx_status.opcode;
+            break;
+        default:
+            // TODO: add more type whenever new type is introduced.
+            break;
+        }
+    }
+
+    const cec_message_t& cec() const {
+        return mEvent.cec;
+    }
+
+    const hotplug_event_t& hotplug() const {
+        return mEvent.hotplug;
+    }
+
+    virtual ~CecEventWrapper() {}
+
+private:
+    hdmi_event_t mEvent;
+};
+
+// Handler class to delegate incoming message to service thread.
+class HdmiCecEventHandler : public MessageHandler {
+public:
+    HdmiCecEventHandler(HdmiCecController* controller, const sp<CecEventWrapper>& event)
+        : mController(controller),
+          mEventWrapper(event) {
+    }
+
+    virtual ~HdmiCecEventHandler() {}
+
+    void handleMessage(const Message& message) {
+        switch (message.what) {
+        case HDMI_EVENT_CEC_MESSAGE:
+            propagateCecCommand(mEventWrapper->cec());
+            break;
+        case HDMI_EVENT_HOT_PLUG:
+            propagateHotplugEvent(mEventWrapper->hotplug());
+            break;
+        case HDMI_EVENT_TX_STATUS:
+            // TODO: propagate this to controller.
+        default:
+            // TODO: add more type whenever new type is introduced.
+            break;
+        }
+    }
+
+private:
+    // Propagate the message up to Java layer.
+    void propagateCecCommand(const cec_message_t& message) {
+        jint srcAddr = message.initiator;
+        jint dstAddr = message.destination;
+        JNIEnv* env = AndroidRuntime::getJNIEnv();
+        jbyteArray body = env->NewByteArray(message.length);
+        const jbyte* bodyPtr = reinterpret_cast<const jbyte *>(message.body);
+        env->SetByteArrayRegion(body, 0, message.length, bodyPtr);
+
+        env->CallVoidMethod(mController->getCallbacksObj(),
+                gHdmiCecControllerClassInfo.handleIncomingCecCommand, srcAddr,
+                dstAddr, body);
+        env->DeleteLocalRef(body);
+
+        checkAndClearExceptionFromCallback(env, __FUNCTION__);
+    }
+
+    void propagateHotplugEvent(const hotplug_event_t& event) {
+        // Note that this method should be called in service thread.
+        JNIEnv* env = AndroidRuntime::getJNIEnv();
+        env->CallVoidMethod(mController->getCallbacksObj(),
+                gHdmiCecControllerClassInfo.handleHotplug, event.connected);
+
+        checkAndClearExceptionFromCallback(env, __FUNCTION__);
+    }
+
+    // static
+    static void checkAndClearExceptionFromCallback(JNIEnv* env, const char* methodName) {
+        if (env->ExceptionCheck()) {
+            ALOGE("An exception was thrown by callback '%s'.", methodName);
+            LOGE_EX(env);
+            env->ExceptionClear();
+        }
+    }
+
+    HdmiCecController* mController;
+    sp<CecEventWrapper> mEventWrapper;
+};
+
+HdmiCecController::HdmiCecController(hdmi_cec_device_t* device,
+        jobject callbacksObj, const sp<Looper>& looper) :
     mDevice(device),
-    mCallbacksObj(callbacksObj) {
+    mCallbacksObj(callbacksObj),
+    mLooper(looper) {
 }
 
 void HdmiCecController::init() {
     mDevice->register_event_callback(mDevice, HdmiCecController::onReceived, this);
 }
 
-void HdmiCecController::propagateCecCommand(const cec_message_t& message) {
-    jint srcAddr = message.initiator;
-    jint dstAddr = message.destination;
-    JNIEnv* env = AndroidRuntime::getJNIEnv();
-    jbyteArray body = env->NewByteArray(message.length);
-    const jbyte* bodyPtr = reinterpret_cast<const jbyte *>(message.body);
-    env->SetByteArrayRegion(body, 0, message.length, bodyPtr);
-
-    env->CallVoidMethod(mCallbacksObj,
-            gHdmiCecControllerClassInfo.handleIncomingCecCommand,
-            srcAddr, dstAddr, body);
-    env->DeleteLocalRef(body);
-
-    checkAndClearExceptionFromCallback(env, __FUNCTION__);
-}
-
-void HdmiCecController::propagateHotplugEvent(const hotplug_event_t& event) {
-    JNIEnv* env = AndroidRuntime::getJNIEnv();
-    env->CallVoidMethod(mCallbacksObj,
-            gHdmiCecControllerClassInfo.handleHotplug, event.connected);
-
-    checkAndClearExceptionFromCallback(env, __FUNCTION__);
-}
-
 int HdmiCecController::sendMessage(const cec_message_t& message) {
     // TODO: propagate send_message's return value.
     return mDevice->send_message(mDevice, &message);
@@ -132,15 +222,6 @@
     return vendorId;
 }
 
-// static
-void HdmiCecController::checkAndClearExceptionFromCallback(JNIEnv* env,
-        const char* methodName) {
-    if (env->ExceptionCheck()) {
-        ALOGE("An exception was thrown by callback '%s'.", methodName);
-        LOGE_EX(env);
-        env->ExceptionClear();
-    }
-}
 
 // static
 void HdmiCecController::onReceived(const hdmi_event_t* event, void* arg) {
@@ -149,17 +230,9 @@
         return;
     }
 
-    switch (event->type) {
-    case HDMI_EVENT_CEC_MESSAGE:
-        controller->propagateCecCommand(event->cec);
-        break;
-    case HDMI_EVENT_HOT_PLUG:
-        controller->propagateHotplugEvent(event->hotplug);
-        break;
-    default:
-        ALOGE("Unsupported event type: %d", event->type);
-        break;
-    }
+    sp<CecEventWrapper> spEvent(new CecEventWrapper(*event));
+    sp<HdmiCecEventHandler> handler(new HdmiCecEventHandler(controller, spEvent));
+    controller->mLooper->sendMessage(handler, event->type);
 }
 
 //------------------------------------------------------------------------------
@@ -167,31 +240,31 @@
         var = env->GetMethodID(clazz, methodName, methodDescriptor); \
         LOG_FATAL_IF(! var, "Unable to find method " methodName);
 
-static jlong nativeInit(JNIEnv* env, jclass clazz, jobject callbacksObj) {
+static jlong nativeInit(JNIEnv* env, jclass clazz, jobject callbacksObj,
+        jobject messageQueueObj) {
     int err;
-    // If use same hardware module id between HdmiCecService and
-    // HdmiControlSservice it may conflict and cause abnormal state of HAL.
-    // TODO: use HDMI_CEC_HARDWARE_MODULE_ID of hdmi_cec.h for module id
-    //       once migration to HdmiControlService is done.
     hw_module_t* module;
-    err = hw_get_module("hdmi_cec_module",
+    err = hw_get_module(HDMI_CEC_HARDWARE_MODULE_ID,
             const_cast<const hw_module_t **>(&module));
     if (err != 0) {
         ALOGE("Error acquiring hardware module: %d", err);
         return 0;
     }
+
     hw_device_t* device;
-    // TODO: use HDMI_CEC_HARDWARE_INTERFACE of hdmi_cec.h for interface name
-    //       once migration to HdmiControlService is done.
-    err = module->methods->open(module, "hdmi_cec_module_hw_if", &device);
+    err = module->methods->open(module, HDMI_CEC_HARDWARE_INTERFACE, &device);
     if (err != 0) {
         ALOGE("Error opening hardware module: %d", err);
         return 0;
     }
 
+    sp<MessageQueue> messageQueue =
+            android_os_MessageQueue_getMessageQueue(env, messageQueueObj);
+
     HdmiCecController* controller = new HdmiCecController(
             reinterpret_cast<hdmi_cec_device*>(device),
-            env->NewGlobalRef(callbacksObj));
+            env->NewGlobalRef(callbacksObj),
+            messageQueue->getLooper());
     controller->init();
 
     GET_METHOD_ID(gHdmiCecControllerClassInfo.handleIncomingCecCommand, clazz,
@@ -255,8 +328,9 @@
 
 static JNINativeMethod sMethods[] = {
     /* name, signature, funcPtr */
-    { "nativeInit", "(Lcom/android/server/hdmi/HdmiCecController;)J",
-            (void *) nativeInit },
+    { "nativeInit",
+      "(Lcom/android/server/hdmi/HdmiCecController;Landroid/os/MessageQueue;)J",
+      (void *) nativeInit },
     { "nativeSendCecCommand", "(JII[B)I", (void *) nativeSendCecCommand },
     { "nativeAddLogicalAddress", "(JI)I", (void *) nativeAddLogicalAddress },
     { "nativeClearLogicalAddress", "(J)V", (void *) nativeClearLogicalAddress },
@@ -268,7 +342,8 @@
 #define CLASS_PATH "com/android/server/hdmi/HdmiCecController"
 
 int register_android_server_hdmi_HdmiCecController(JNIEnv* env) {
-    int res = jniRegisterNativeMethods(env, CLASS_PATH, sMethods, NELEM(sMethods));
+    int res = jniRegisterNativeMethods(env, CLASS_PATH, sMethods,
+            NELEM(sMethods));
     LOG_FATAL_IF(res < 0, "Unable to register native methods.");
     return 0;
 }
diff --git a/services/core/jni/com_android_server_location_FlpHardwareProvider.cpp b/services/core/jni/com_android_server_location_FlpHardwareProvider.cpp
index 6c14887..c2fccc1 100644
--- a/services/core/jni/com_android_server_location_FlpHardwareProvider.cpp
+++ b/services/core/jni/com_android_server_location_FlpHardwareProvider.cpp
@@ -139,6 +139,8 @@
  * the HW module and obtaining the proper interfaces.
  */
 static void ClassInit(JNIEnv* env, jclass clazz) {
+  sFlpInterface = NULL;
+
   // get references to the Java provider methods
   sOnLocationReport = env->GetMethodID(
       clazz,
@@ -163,6 +165,38 @@
   sOnGeofenceRemove = env->GetMethodID(clazz, "onGeofenceRemove", "(II)V");
   sOnGeofencePause = env->GetMethodID(clazz, "onGeofencePause", "(II)V");
   sOnGeofenceResume = env->GetMethodID(clazz, "onGeofenceResume", "(II)V");
+
+  // open the hardware module
+  const hw_module_t* module = NULL;
+  int err = hw_get_module(FUSED_LOCATION_HARDWARE_MODULE_ID, &module);
+  if (err != 0) {
+    ALOGE("Error hw_get_module '%s': %d", FUSED_LOCATION_HARDWARE_MODULE_ID, err);
+    return;
+  }
+
+  err = module->methods->open(
+      module,
+      FUSED_LOCATION_HARDWARE_MODULE_ID,
+      &sHardwareDevice);
+  if (err != 0) {
+    ALOGE("Error opening device '%s': %d", FUSED_LOCATION_HARDWARE_MODULE_ID, err);
+    return;
+  }
+
+  // acquire the interfaces pointers
+  flp_device_t* flp_device = reinterpret_cast<flp_device_t*>(sHardwareDevice);
+  sFlpInterface = flp_device->get_flp_interface(flp_device);
+
+  if (sFlpInterface != NULL) {
+    sFlpDiagnosticInterface = reinterpret_cast<const FlpDiagnosticInterface*>(
+        sFlpInterface->get_extension(FLP_DIAGNOSTIC_INTERFACE));
+
+    sFlpGeofencingInterface = reinterpret_cast<const FlpGeofencingInterface*>(
+        sFlpInterface->get_extension(FLP_GEOFENCING_INTERFACE));
+
+    sFlpDeviceContextInterface = reinterpret_cast<const FlpDeviceContextInterface*>(
+        sFlpInterface->get_extension(FLP_DEVICE_CONTEXT_INTERFACE));
+  }
 }
 
 /*
@@ -637,44 +671,6 @@
  * the Flp interfaces are initialized properly.
  */
 static void Init(JNIEnv* env, jobject obj) {
-  if(sHardwareDevice != NULL) {
-    ALOGD("Hardware Device already opened.");
-    return;
-  }
-
-  const hw_module_t* module = NULL;
-  int err = hw_get_module(FUSED_LOCATION_HARDWARE_MODULE_ID, &module);
-  if(err != 0) {
-    ALOGE("Error hw_get_module '%s': %d", FUSED_LOCATION_HARDWARE_MODULE_ID, err);
-    return;
-  }
-
-  err = module->methods->open(
-        module,
-        FUSED_LOCATION_HARDWARE_MODULE_ID, &sHardwareDevice);
-  if(err != 0) {
-    ALOGE("Error opening device '%s': %d", FUSED_LOCATION_HARDWARE_MODULE_ID, err);
-    return;
-  }
-
-  sFlpInterface = NULL;
-  flp_device_t* flp_device = reinterpret_cast<flp_device_t*>(sHardwareDevice);
-  sFlpInterface = flp_device->get_flp_interface(flp_device);
-
-  if(sFlpInterface != NULL) {
-    sFlpDiagnosticInterface = reinterpret_cast<const FlpDiagnosticInterface*>(
-        sFlpInterface->get_extension(FLP_DIAGNOSTIC_INTERFACE)
-        );
-
-    sFlpGeofencingInterface = reinterpret_cast<const FlpGeofencingInterface*>(
-        sFlpInterface->get_extension(FLP_GEOFENCING_INTERFACE)
-        );
-
-    sFlpDeviceContextInterface = reinterpret_cast<const FlpDeviceContextInterface*>(
-        sFlpInterface->get_extension(FLP_DEVICE_CONTEXT_INTERFACE)
-        );
-  }
-
   if(sCallbacksObj == NULL) {
     sCallbacksObj = env->NewGlobalRef(obj);
   }
@@ -696,7 +692,10 @@
 }
 
 static jboolean IsSupported(JNIEnv* env, jclass clazz) {
-  return sFlpInterface != NULL;
+  if (sFlpInterface == NULL) {
+    return JNI_FALSE;
+  }
+  return JNI_TRUE;
 }
 
 static jint GetBatchSize(JNIEnv* env, jobject object) {
diff --git a/services/core/jni/com_android_server_tv_TvInputHal.cpp b/services/core/jni/com_android_server_tv_TvInputHal.cpp
index f0c4f3a..afe629d 100644
--- a/services/core/jni/com_android_server_tv_TvInputHal.cpp
+++ b/services/core/jni/com_android_server_tv_TvInputHal.cpp
@@ -316,7 +316,7 @@
             (void*) nativeOpen },
     { "nativeSetSurface", "(JIILandroid/view/Surface;)I",
             (void*) nativeSetSurface },
-    { "nativeGetStreamConfigs", "(JII)[Landroid/tv/TvStreamConfig;",
+    { "nativeGetStreamConfigs", "(JII)[Landroid/media/tv/TvStreamConfig;",
             (void*) nativeGetStreamConfigs },
     { "nativeClose", "(J)V",
             (void*) nativeClose },
@@ -346,10 +346,10 @@
             gTvInputHalClassInfo.streamConfigsChanged, clazz,
             "streamConfigsChangedFromNative", "(I)V");
 
-    FIND_CLASS(gTvStreamConfigClassInfo.clazz, "android/tv/TvStreamConfig");
+    FIND_CLASS(gTvStreamConfigClassInfo.clazz, "android/media/tv/TvStreamConfig");
     gTvStreamConfigClassInfo.clazz = jclass(env->NewGlobalRef(gTvStreamConfigClassInfo.clazz));
 
-    FIND_CLASS(gTvStreamConfigBuilderClassInfo.clazz, "android/tv/TvStreamConfig$Builder");
+    FIND_CLASS(gTvStreamConfigBuilderClassInfo.clazz, "android/media/tv/TvStreamConfig$Builder");
     gTvStreamConfigBuilderClassInfo.clazz =
             jclass(env->NewGlobalRef(gTvStreamConfigBuilderClassInfo.clazz));
 
@@ -360,27 +360,27 @@
     GET_METHOD_ID(
             gTvStreamConfigBuilderClassInfo.streamId,
             gTvStreamConfigBuilderClassInfo.clazz,
-            "streamId", "(I)Landroid/tv/TvStreamConfig$Builder;");
+            "streamId", "(I)Landroid/media/tv/TvStreamConfig$Builder;");
     GET_METHOD_ID(
             gTvStreamConfigBuilderClassInfo.type,
             gTvStreamConfigBuilderClassInfo.clazz,
-            "type", "(I)Landroid/tv/TvStreamConfig$Builder;");
+            "type", "(I)Landroid/media/tv/TvStreamConfig$Builder;");
     GET_METHOD_ID(
             gTvStreamConfigBuilderClassInfo.maxWidth,
             gTvStreamConfigBuilderClassInfo.clazz,
-            "maxWidth", "(I)Landroid/tv/TvStreamConfig$Builder;");
+            "maxWidth", "(I)Landroid/media/tv/TvStreamConfig$Builder;");
     GET_METHOD_ID(
             gTvStreamConfigBuilderClassInfo.maxHeight,
             gTvStreamConfigBuilderClassInfo.clazz,
-            "maxHeight", "(I)Landroid/tv/TvStreamConfig$Builder;");
+            "maxHeight", "(I)Landroid/media/tv/TvStreamConfig$Builder;");
     GET_METHOD_ID(
             gTvStreamConfigBuilderClassInfo.generation,
             gTvStreamConfigBuilderClassInfo.clazz,
-            "generation", "(I)Landroid/tv/TvStreamConfig$Builder;");
+            "generation", "(I)Landroid/media/tv/TvStreamConfig$Builder;");
     GET_METHOD_ID(
             gTvStreamConfigBuilderClassInfo.build,
             gTvStreamConfigBuilderClassInfo.clazz,
-            "build", "()Landroid/tv/TvStreamConfig;");
+            "build", "()Landroid/media/tv/TvStreamConfig;");
 
     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 53f3ddb..043fab4 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -130,6 +130,8 @@
 
     private static final boolean DBG = false;
 
+    private static final String ATTR_PERMISSION_PROVIDER = "permission-provider";
+
     final Context mContext;
     final UserManager mUserManager;
     final PowerManager.WakeLock mWakeLock;
@@ -190,6 +192,8 @@
         // This is the list of component allowed to start lock task mode.
         final List<ComponentName> mLockTaskComponents = new ArrayList<ComponentName>();
 
+        ComponentName mRestrictionsProvider;
+
         public DevicePolicyData(int userHandle) {
             mUserHandle = userHandle;
         }
@@ -944,6 +948,10 @@
             out.startDocument(null, true);
 
             out.startTag(null, "policies");
+            if (policy.mRestrictionsProvider != null) {
+                out.attribute(null, ATTR_PERMISSION_PROVIDER,
+                        policy.mRestrictionsProvider.flattenToString());
+            }
 
             final int N = policy.mAdminList.size();
             for (int i=0; i<N; i++) {
@@ -1039,6 +1047,13 @@
                 throw new XmlPullParserException(
                         "Settings do not start with policies tag: found " + tag);
             }
+
+            // Extract the permission provider component name if available
+            String permissionProvider = parser.getAttributeValue(null, ATTR_PERMISSION_PROVIDER);
+            if (permissionProvider != null) {
+                policy.mRestrictionsProvider = ComponentName.unflattenFromString(permissionProvider);
+            }
+
             type = parser.next();
             int outerDepth = parser.getDepth();
             policy.mLockTaskComponents.clear();
@@ -3303,6 +3318,32 @@
         }
     }
 
+    @Override
+    public void setRestrictionsProvider(ComponentName who, ComponentName permissionProvider) {
+        synchronized (this) {
+            if (who == null) {
+                throw new NullPointerException("ComponentName is null");
+            }
+            getActiveAdminForCallerLocked(who, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER);
+
+            int userHandle = UserHandle.getCallingUserId();
+            DevicePolicyData userData = getUserData(userHandle);
+            userData.mRestrictionsProvider = permissionProvider;
+            saveSettingsLocked(userHandle);
+        }
+    }
+
+    @Override
+    public ComponentName getRestrictionsProvider(int userHandle) {
+        synchronized (this) {
+            if (Binder.getCallingUid() != Process.SYSTEM_UID) {
+                throw new SecurityException("Only the system can query the permission provider");
+            }
+            DevicePolicyData userData = getUserData(userHandle);
+            return userData != null ? userData.mRestrictionsProvider : null;
+        }
+    }
+
     public void addCrossProfileIntentFilter(ComponentName who, IntentFilter filter, int flags) {
         int callingUserId = UserHandle.getCallingUserId();
         synchronized (this) {
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index bc34e0e..18ece5b 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -29,6 +29,7 @@
 import android.content.pm.PackageManager;
 import android.content.res.Configuration;
 import android.media.AudioService;
+import android.media.tv.TvInputManager;
 import android.os.Build;
 import android.os.Environment;
 import android.os.FactoryTest;
@@ -43,7 +44,6 @@
 import android.os.SystemProperties;
 import android.os.UserHandle;
 import android.service.dreams.DreamService;
-import android.tv.TvInputManager;
 import android.util.DisplayMetrics;
 import android.util.EventLog;
 import android.util.Log;
@@ -80,6 +80,7 @@
 import com.android.server.pm.UserManagerService;
 import com.android.server.power.PowerManagerService;
 import com.android.server.power.ShutdownThread;
+import com.android.server.restrictions.RestrictionsManagerService;
 import com.android.server.search.SearchManagerService;
 import com.android.server.statusbar.StatusBarManagerService;
 import com.android.server.storage.DeviceStorageMonitorService;
@@ -126,7 +127,7 @@
     private static final String WIFI_SERVICE_CLASS =
             "com.android.server.wifi.WifiService";
     private static final String WIFI_PASSPOINT_SERVICE_CLASS =
-            "com.android.server.wifi.passpoint.PasspointService";
+            "com.android.server.wifi.passpoint.WifiPasspointService";
     private static final String WIFI_P2P_SERVICE_CLASS =
             "com.android.server.wifi.p2p.WifiP2pService";
     private static final String HDMI_CEC_SERVICE_CLASS =
@@ -186,7 +187,7 @@
         // had to fallback to a different runtime because it is
         // running as root and we need to be the system user to set
         // the property. http://b/11463182
-        SystemProperties.set("persist.sys.dalvik.vm.lib.1", VMRuntime.getRuntime().vmLibrary());
+        SystemProperties.set("persist.sys.dalvik.vm.lib.2", VMRuntime.getRuntime().vmLibrary());
 
         // Enable the sampling profiler.
         if (SamplingProfilerIntegration.isEnabled()) {
@@ -330,7 +331,6 @@
         IPackageManager pm = null;
         WindowManagerService wm = null;
         BluetoothManagerService bluetooth = null;
-        DockObserver dock = null;
         UsbService usb = null;
         SerialService serial = null;
         RecognitionManagerService recognition = null;
@@ -644,18 +644,18 @@
                 }
 
                 try {
-                    mSystemServiceManager.startService(WIFI_SERVICE_CLASS);
-                } catch (Throwable e) {
-                    reportWtf("starting Wi-Fi Service", e);
-                }
-
-                try {
                     mSystemServiceManager.startService(WIFI_PASSPOINT_SERVICE_CLASS);
                 } catch (Throwable e) {
                     reportWtf("starting Wi-Fi PasspointService", e);
                 }
 
                 try {
+                    mSystemServiceManager.startService(WIFI_SERVICE_CLASS);
+                } catch (Throwable e) {
+                    reportWtf("starting Wi-Fi Service", e);
+                }
+
+                try {
                     Slog.i(TAG, "Wi-Fi Scanning Service");
                     mSystemServiceManager.startService(
                             "com.android.server.wifi.WifiScanningService");
@@ -789,13 +789,7 @@
             }
 
             if (!disableNonCoreServices) {
-                try {
-                    Slog.i(TAG, "Dock Observer");
-                    // Listen for dock station changes
-                    dock = new DockObserver(context);
-                } catch (Throwable e) {
-                    reportWtf("starting DockObserver", e);
-                }
+                mSystemServiceManager.startService(DockObserver.class);
             }
 
             if (!disableMedia) {
@@ -948,6 +942,12 @@
             }
 
             try {
+                mSystemServiceManager.startService(RestrictionsManagerService.class);
+            } catch (Throwable e) {
+                reportWtf("starting RestrictionsManagerService", e);
+            }
+
+            try {
                 mSystemServiceManager.startService(MediaSessionService.class);
             } catch (Throwable e) {
                 reportWtf("starting MediaSessionService", e);
@@ -998,8 +998,7 @@
 
             try {
                 Slog.i(TAG, "LauncherAppsService");
-                LauncherAppsService las = new LauncherAppsService(context);
-                ServiceManager.addService(Context.LAUNCHER_APPS_SERVICE, las);
+                mSystemServiceManager.startService(LauncherAppsService.class);
             } catch (Throwable t) {
                 reportWtf("starting LauncherAppsService", t);
             }
@@ -1085,7 +1084,6 @@
         final NetworkPolicyManagerService networkPolicyF = networkPolicy;
         final ConnectivityService connectivityF = connectivity;
         final NetworkScoreService networkScoreF = networkScore;
-        final DockObserver dockF = dock;
         final WallpaperManagerService wallpaperF = wallpaper;
         final InputMethodManagerService immF = imm;
         final RecognitionManagerService recognitionF = recognition;
@@ -1159,11 +1157,6 @@
                     reportWtf("making Connectivity Service ready", e);
                 }
                 try {
-                    if (dockF != null) dockF.systemReady();
-                } catch (Throwable e) {
-                    reportWtf("making Dock Service ready", e);
-                }
-                try {
                     if (recognitionF != null) recognitionF.systemReady();
                 } catch (Throwable e) {
                     reportWtf("making Recognition Service ready", e);
diff --git a/services/restrictions/Android.mk b/services/restrictions/Android.mk
new file mode 100644
index 0000000..fcf8626
--- /dev/null
+++ b/services/restrictions/Android.mk
@@ -0,0 +1,10 @@
+LOCAL_PATH := $(call my-dir)
+
+include $(CLEAR_VARS)
+
+LOCAL_MODULE := services.restrictions
+
+LOCAL_SRC_FILES += \
+      $(call all-java-files-under,java)
+
+include $(BUILD_STATIC_JAVA_LIBRARY)
diff --git a/services/restrictions/java/com/android/server/restrictions/RestrictionsManagerService.java b/services/restrictions/java/com/android/server/restrictions/RestrictionsManagerService.java
new file mode 100644
index 0000000..e1f77b3
--- /dev/null
+++ b/services/restrictions/java/com/android/server/restrictions/RestrictionsManagerService.java
@@ -0,0 +1,171 @@
+/*
+ * 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.restrictions;
+
+import android.Manifest;
+import android.app.AppGlobals;
+import android.app.Notification;
+import android.app.NotificationManager;
+import android.app.PendingIntent;
+import android.app.admin.IDevicePolicyManager;
+import android.content.BroadcastReceiver;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.IRestrictionsManager;
+import android.content.RestrictionsManager;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
+import android.content.pm.ServiceInfo;
+import android.content.pm.UserInfo;
+import android.database.ContentObserver;
+import android.net.Uri;
+import android.os.Binder;
+import android.os.Bundle;
+import android.os.IUserManager;
+import android.os.Process;
+import android.os.RemoteException;
+import android.os.UserHandle;
+import android.os.UserManager;
+
+import com.android.internal.util.ArrayUtils;
+import com.android.server.SystemService;
+
+/**
+ * SystemService wrapper for the RestrictionsManager implementation. Publishes the
+ * Context.RESTRICTIONS_SERVICE.
+ */
+
+public final class RestrictionsManagerService extends SystemService {
+    private final RestrictionsManagerImpl mRestrictionsManagerImpl;
+
+    public RestrictionsManagerService(Context context) {
+        super(context);
+        mRestrictionsManagerImpl = new RestrictionsManagerImpl(context);
+    }
+
+    @Override
+    public void onStart() {
+        publishBinderService(Context.RESTRICTIONS_SERVICE, mRestrictionsManagerImpl);
+    }
+
+    class RestrictionsManagerImpl extends IRestrictionsManager.Stub {
+        private final Context mContext;
+        private final IUserManager mUm;
+        private final IDevicePolicyManager mDpm;
+
+        public RestrictionsManagerImpl(Context context) {
+            mContext = context;
+            mUm = (IUserManager) getBinderService(Context.USER_SERVICE);
+            mDpm = (IDevicePolicyManager) getBinderService(Context.DEVICE_POLICY_SERVICE);
+        }
+
+        @Override
+        public Bundle getApplicationRestrictions(String packageName) throws RemoteException {
+            return mUm.getApplicationRestrictions(packageName);
+        }
+
+        @Override
+        public boolean hasRestrictionsProvider() throws RemoteException {
+            int userHandle = UserHandle.getCallingUserId();
+            if (mDpm != null) {
+                long ident = Binder.clearCallingIdentity();
+                try {
+                    return mDpm.getRestrictionsProvider(userHandle) != null;
+                } finally {
+                    Binder.restoreCallingIdentity(ident);
+                }
+            } else {
+                return false;
+            }
+        }
+
+        @Override
+        public void requestPermission(String packageName, String requestTemplate,
+                Bundle requestData) throws RemoteException {
+            int callingUid = Binder.getCallingUid();
+            int userHandle = UserHandle.getUserId(callingUid);
+            if (mDpm != null) {
+                long ident = Binder.clearCallingIdentity();
+                try {
+                    ComponentName restrictionsProvider =
+                            mDpm.getRestrictionsProvider(userHandle);
+                    // Check if there is a restrictions provider
+                    if (restrictionsProvider == null) {
+                        throw new IllegalStateException(
+                            "Cannot request permission without a restrictions provider registered");
+                    }
+                    // Check that the packageName matches the caller.
+                    enforceCallerMatchesPackage(callingUid, packageName, "Package name does not" +
+                            " match caller ");
+                    // Prepare and broadcast the intent to the provider
+                    Intent intent = new Intent(RestrictionsManager.ACTION_REQUEST_PERMISSION);
+                    intent.setComponent(restrictionsProvider);
+                    intent.putExtra(RestrictionsManager.EXTRA_PACKAGE_NAME, packageName);
+                    intent.putExtra(RestrictionsManager.EXTRA_TEMPLATE_ID, requestTemplate);
+                    intent.putExtra(RestrictionsManager.EXTRA_REQUEST_BUNDLE, requestData);
+                    mContext.sendBroadcastAsUser(intent, new UserHandle(userHandle));
+                } finally {
+                    Binder.restoreCallingIdentity(ident);
+                }
+            }
+        }
+
+        private void enforceCallerMatchesPackage(int callingUid, String packageName,
+                String message) {
+            try {
+                String[] pkgs = AppGlobals.getPackageManager().getPackagesForUid(callingUid);
+                if (pkgs != null) {
+                    if (!ArrayUtils.contains(pkgs, packageName)) {
+                        throw new SecurityException(message + callingUid);
+                    }
+                }
+            } catch (RemoteException re) {
+                // Shouldn't happen
+            }
+        }
+
+        @Override
+        public void notifyPermissionResponse(String packageName, Bundle response)
+                throws RemoteException {
+            // Check caller
+            int callingUid = Binder.getCallingUid();
+            int userHandle = UserHandle.getUserId(callingUid);
+            if (mDpm != null) {
+                long ident = Binder.clearCallingIdentity();
+                try {
+                    ComponentName permProvider = mDpm.getRestrictionsProvider(userHandle);
+                    if (permProvider == null) {
+                        throw new SecurityException("No restrictions provider registered for user");
+                    }
+                    enforceCallerMatchesPackage(callingUid, permProvider.getPackageName(),
+                            "Restrictions provider does not match caller ");
+
+                    // Post the response to target package
+                    Intent responseIntent = new Intent(
+                            RestrictionsManager.ACTION_PERMISSION_RESPONSE_RECEIVED);
+                    responseIntent.setPackage(packageName);
+                    responseIntent.putExtra(RestrictionsManager.EXTRA_RESPONSE_BUNDLE, response);
+                    mContext.sendBroadcastAsUser(responseIntent, new UserHandle(userHandle));
+                } finally {
+                    Binder.restoreCallingIdentity(ident);
+                }
+            }
+        }
+    }
+}
diff --git a/services/tests/servicestests/AndroidManifest.xml b/services/tests/servicestests/AndroidManifest.xml
index 7848b1d..636dd4d 100644
--- a/services/tests/servicestests/AndroidManifest.xml
+++ b/services/tests/servicestests/AndroidManifest.xml
@@ -35,7 +35,8 @@
     <uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
     <uses-permission android:name="android.permission.MANAGE_USERS" />
     <uses-permission android:name="android.permission.INTERACT_ACROSS_USERS_FULL" />
-    
+    <uses-permission android:name="android.permission.MANAGE_DEVICE_ADMINS" />
+
     <application>
         <uses-library android:name="android.test.runner" />
 
@@ -53,6 +54,15 @@
           </intent-filter>
         </service>
 
+        <receiver android:name="com.android.server.devicepolicy.ApplicationRestrictionsTest$AdminReceiver"
+                android:permission="android.permission.BIND_DEVICE_ADMIN">
+            <meta-data android:name="android.app.device_admin"
+                       android:resource="@xml/device_admin_sample" />
+            <intent-filter>
+                <action android:name="android.app.action.DEVICE_ADMIN_ENABLED" />
+            </intent-filter>
+        </receiver>
+
     </application>
 
     <instrumentation
diff --git a/core/res/res/drawable/list_selector_quantum.xml b/services/tests/servicestests/res/xml/device_admin_sample.xml
similarity index 63%
copy from core/res/res/drawable/list_selector_quantum.xml
copy to services/tests/servicestests/res/xml/device_admin_sample.xml
index 6cd59e5..032debb 100644
--- a/core/res/res/drawable/list_selector_quantum.xml
+++ b/services/tests/servicestests/res/xml/device_admin_sample.xml
@@ -14,9 +14,16 @@
      limitations under the License.
 -->
 
-<ripple xmlns:android="http://schemas.android.com/apk/res/android"
-    android:tint="?attr/colorControlHighlight">
-    <item android:id="@id/mask">
-        <color android:color="@color/white" />
-    </item>
-</ripple>
+<device-admin xmlns:android="http://schemas.android.com/apk/res/android">
+    <uses-policies>
+        <limit-password />
+        <watch-login />
+        <reset-password />
+        <force-lock />
+        <wipe-data />
+        <expire-password />
+        <encrypted-storage />
+        <disable-camera />
+        <disable-keyguard-features />
+    </uses-policies>
+</device-admin>
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/ApplicationRestrictionsTest.java b/services/tests/servicestests/src/com/android/server/devicepolicy/ApplicationRestrictionsTest.java
new file mode 100644
index 0000000..8e8e4e6
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/ApplicationRestrictionsTest.java
@@ -0,0 +1,135 @@
+/*
+ * 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.devicepolicy;
+
+import android.app.admin.DeviceAdminReceiver;
+import android.app.admin.DevicePolicyManager;
+import android.content.BroadcastReceiver;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.os.Bundle;
+import android.os.UserHandle;
+import android.provider.Settings;
+import android.test.AndroidTestCase;
+import android.test.suitebuilder.annotation.SmallTest;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+
+/**
+ * Tests for application restrictions persisting via profile owner:
+ *   make -j FrameworksServicesTests
+ *   runtest --path frameworks/base/services/tests/servicestests/ \
+ *       src/com/android/server/devicepolicy/ApplicationRestrictionsTest.java
+ */
+public class ApplicationRestrictionsTest extends AndroidTestCase {
+
+    static DevicePolicyManager sDpm;
+    static ComponentName sAdminReceiver;
+    private static final String RESTRICTED_APP = "com.example.restrictedApp";
+    static boolean sAddBack = false;
+
+    public static class AdminReceiver extends DeviceAdminReceiver {
+
+        @Override
+        public void onDisabled(Context context, Intent intent) {
+            if (sAddBack) {
+                sDpm.setActiveAdmin(sAdminReceiver, false);
+                sAddBack = false;
+            }
+
+            super.onDisabled(context, intent);
+        }
+    }
+
+    @Override
+    public void setUp() {
+        final Context context = getContext();
+        sAdminReceiver = new ComponentName(mContext.getPackageName(),
+                AdminReceiver.class.getName());
+        sDpm = (DevicePolicyManager) context.getSystemService(Context.DEVICE_POLICY_SERVICE);
+        Settings.Secure.putInt(context.getContentResolver(),
+                Settings.Secure.USER_SETUP_COMPLETE, 0);
+        sDpm.setProfileOwner(context.getPackageName(), "Test", UserHandle.myUserId());
+        Settings.Secure.putInt(context.getContentResolver(),
+                Settings.Secure.USER_SETUP_COMPLETE, 1);
+        // Remove the admin if already registered. It's async, so add it back
+        // when the admin gets a broadcast. Otherwise add it back right away.
+        if (sDpm.isAdminActive(sAdminReceiver)) {
+            sAddBack = true;
+            sDpm.removeActiveAdmin(sAdminReceiver);
+        } else {
+            sDpm.setActiveAdmin(sAdminReceiver, false);
+        }
+    }
+
+    @Override
+    public void tearDown() {
+        Settings.Secure.putInt(getContext().getContentResolver(),
+                Settings.Secure.USER_SETUP_COMPLETE, 0);
+        sDpm.removeActiveAdmin(sAdminReceiver);
+        Settings.Secure.putInt(getContext().getContentResolver(),
+                Settings.Secure.USER_SETUP_COMPLETE, 1);
+    }
+
+    public void testSettingRestrictions() {
+        Bundle restrictions = new Bundle();
+        restrictions.putString("KEY_STRING", "Foo");
+        assertNotNull(sDpm.getApplicationRestrictions(sAdminReceiver, RESTRICTED_APP));
+        sDpm.setApplicationRestrictions(sAdminReceiver, RESTRICTED_APP, restrictions);
+        Bundle returned = sDpm.getApplicationRestrictions(sAdminReceiver, RESTRICTED_APP);
+        assertNotNull(returned);
+        assertEquals(returned.size(), 1);
+        assertEquals(returned.get("KEY_STRING"), "Foo");
+        sDpm.setApplicationRestrictions(sAdminReceiver, RESTRICTED_APP, new Bundle());
+        returned = sDpm.getApplicationRestrictions(sAdminReceiver, RESTRICTED_APP);
+        assertEquals(returned.size(), 0);
+    }
+
+    public void testRestrictionTypes() {
+        Bundle restrictions = new Bundle();
+        restrictions.putString("KEY_STRING", "Foo");
+        restrictions.putInt("KEY_INT", 7);
+        restrictions.putBoolean("KEY_BOOLEAN", true);
+        restrictions.putBoolean("KEY_BOOLEAN_2", false);
+        restrictions.putString("KEY_STRING_2", "Bar");
+        restrictions.putStringArray("KEY_STR_ARRAY", new String[] { "Foo", "Bar" });
+        sDpm.setApplicationRestrictions(sAdminReceiver, RESTRICTED_APP, restrictions);
+        Bundle returned = sDpm.getApplicationRestrictions(sAdminReceiver, RESTRICTED_APP);
+        assertTrue(returned.getBoolean("KEY_BOOLEAN"));
+        assertFalse(returned.getBoolean("KEY_BOOLEAN_2"));
+        assertFalse(returned.getBoolean("KEY_BOOLEAN_3"));
+        assertEquals(returned.getInt("KEY_INT"), 7);
+        assertTrue(returned.get("KEY_BOOLEAN") instanceof Boolean);
+        assertTrue(returned.get("KEY_INT") instanceof Integer);
+        assertEquals(returned.get("KEY_STRING"), "Foo");
+        assertEquals(returned.get("KEY_STRING_2"), "Bar");
+        assertTrue(returned.getStringArray("KEY_STR_ARRAY") instanceof String[]);
+        sDpm.setApplicationRestrictions(sAdminReceiver, RESTRICTED_APP, new Bundle());
+    }
+
+    public void testTextEscaping() {
+        String fancyText = "<This contains XML/> <JSON> "
+                + "{ \"One\": { \"OneOne\": \"11\", \"OneTwo\": \"12\" }, \"Two\": \"2\" } <JSON/>";
+        Bundle restrictions = new Bundle();
+        restrictions.putString("KEY_FANCY_TEXT", fancyText);
+        sDpm.setApplicationRestrictions(sAdminReceiver, RESTRICTED_APP, restrictions);
+        Bundle returned = sDpm.getApplicationRestrictions(sAdminReceiver, RESTRICTED_APP);
+        assertEquals(returned.getString("KEY_FANCY_TEXT"), fancyText);
+    }
+}
\ No newline at end of file
diff --git a/services/tests/servicestests/src/com/android/server/notification/ValidateNotificationPeopleTest.java b/services/tests/servicestests/src/com/android/server/notification/ValidateNotificationPeopleTest.java
new file mode 100644
index 0000000..a6fdee9
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/notification/ValidateNotificationPeopleTest.java
@@ -0,0 +1,152 @@
+/*
+ * 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.notification;
+
+import android.app.Notification;
+import android.os.Bundle;
+import android.test.AndroidTestCase;
+import android.test.suitebuilder.annotation.SmallTest;
+import android.text.SpannableString;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+
+public class ValidateNotificationPeopleTest extends AndroidTestCase {
+
+    @SmallTest
+    public void testNoExtra() throws Exception {
+        Bundle bundle = new Bundle();
+        String[] result = ValidateNotificationPeople.getExtraPeople(bundle);
+        assertNull("lack of extra should return null", result);
+    }
+
+    @SmallTest
+    public void testSingleString() throws Exception {
+        String[] expected = { "foobar" };
+        Bundle bundle = new Bundle();
+        bundle.putString(Notification.EXTRA_PEOPLE, expected[0]);
+        String[] result = ValidateNotificationPeople.getExtraPeople(bundle);
+        assertStringArrayEquals("string should be in result[0]", expected, result);
+    }
+
+    @SmallTest
+    public void testSingleCharArray() throws Exception {
+        String[] expected = { "foobar" };
+        Bundle bundle = new Bundle();
+        bundle.putCharArray(Notification.EXTRA_PEOPLE, expected[0].toCharArray());
+        String[] result = ValidateNotificationPeople.getExtraPeople(bundle);
+        assertStringArrayEquals("char[] should be in result[0]", expected, result);
+    }
+
+    @SmallTest
+    public void testSingleCharSequence() throws Exception {
+        String[] expected = { "foobar" };
+        Bundle bundle = new Bundle();
+        bundle.putCharSequence(Notification.EXTRA_PEOPLE, new SpannableString(expected[0]));
+        String[] result = ValidateNotificationPeople.getExtraPeople(bundle);
+        assertStringArrayEquals("charSequence should be in result[0]", expected, result);
+    }
+
+    @SmallTest
+    public void testStringArraySingle() throws Exception {
+        Bundle bundle = new Bundle();
+        String[] expected = { "foobar" };
+        bundle.putStringArray(Notification.EXTRA_PEOPLE, expected);
+        String[] result = ValidateNotificationPeople.getExtraPeople(bundle);
+        assertStringArrayEquals("wrapped string should be in result[0]", expected, result);
+    }
+
+    @SmallTest
+    public void testStringArrayMultiple() throws Exception {
+        Bundle bundle = new Bundle();
+        String[] expected = { "foo", "bar", "baz" };
+        bundle.putStringArray(Notification.EXTRA_PEOPLE, expected);
+        String[] result = ValidateNotificationPeople.getExtraPeople(bundle);
+        assertStringArrayEquals("testStringArrayMultiple", expected, result);
+    }
+
+    @SmallTest
+    public void testStringArrayNulls() throws Exception {
+        Bundle bundle = new Bundle();
+        String[] expected = { "foo", null, "baz" };
+        bundle.putStringArray(Notification.EXTRA_PEOPLE, expected);
+        String[] result = ValidateNotificationPeople.getExtraPeople(bundle);
+        assertStringArrayEquals("testStringArrayNulls", expected, result);
+    }
+
+    @SmallTest
+    public void testCharSequenceArrayMultiple() throws Exception {
+        Bundle bundle = new Bundle();
+        String[] expected = { "foo", "bar", "baz" };
+        CharSequence[] charSeqArray = new CharSequence[expected.length];
+        for (int i = 0; i < expected.length; i++) {
+            charSeqArray[i] = new SpannableString(expected[i]);
+        }
+        bundle.putCharSequenceArray(Notification.EXTRA_PEOPLE, charSeqArray);
+        String[] result = ValidateNotificationPeople.getExtraPeople(bundle);
+        assertStringArrayEquals("testCharSequenceArrayMultiple", expected, result);
+    }
+
+    @SmallTest
+    public void testMixedCharSequenceArrayList() throws Exception {
+        Bundle bundle = new Bundle();
+        String[] expected = { "foo", "bar", "baz" };
+        CharSequence[] charSeqArray = new CharSequence[expected.length];
+        for (int i = 0; i < expected.length; i++) {
+            if (i % 2 == 0) {
+                charSeqArray[i] = expected[i];
+            } else {
+                charSeqArray[i] = new SpannableString(expected[i]);
+            }
+        }
+        bundle.putCharSequenceArray(Notification.EXTRA_PEOPLE, charSeqArray);
+        String[] result = ValidateNotificationPeople.getExtraPeople(bundle);
+        assertStringArrayEquals("testMixedCharSequenceArrayList", expected, result);
+    }
+
+    @SmallTest
+    public void testStringArrayList() throws Exception {
+        Bundle bundle = new Bundle();
+        String[] expected = { "foo", null, "baz" };
+        final ArrayList<String> stringArrayList = new ArrayList<String>(expected.length);
+        for (int i = 0; i < expected.length; i++) {
+            stringArrayList.add(expected[i]);
+        }
+        bundle.putStringArrayList(Notification.EXTRA_PEOPLE, stringArrayList);
+        String[] result = ValidateNotificationPeople.getExtraPeople(bundle);
+        assertStringArrayEquals("testStringArrayList", expected, result);
+    }
+
+    @SmallTest
+    public void testCharSequenceArrayList() throws Exception {
+        Bundle bundle = new Bundle();
+        String[] expected = { "foo", "bar", "baz" };
+        final ArrayList<CharSequence> stringArrayList =
+                new ArrayList<CharSequence>(expected.length);
+        for (int i = 0; i < expected.length; i++) {
+            stringArrayList.add(new SpannableString(expected[i]));
+        }
+        bundle.putCharSequenceArrayList(Notification.EXTRA_PEOPLE, stringArrayList);
+        String[] result = ValidateNotificationPeople.getExtraPeople(bundle);
+        assertStringArrayEquals("testCharSequenceArrayList", expected, result);
+    }
+
+    private void assertStringArrayEquals(String message, String[] expected, String[] result) {
+        String expectedString = Arrays.toString(expected);
+        String resultString = Arrays.toString(result);
+        assertEquals(message + ": arrays differ", expectedString, resultString);
+    }
+}
diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerServiceImpl.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerServiceImpl.java
index 9b6daad..62ff121 100644
--- a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerServiceImpl.java
+++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerServiceImpl.java
@@ -113,7 +113,7 @@
             if (mBound) {
                 try {
                     mIWindowManager.addWindowToken(mToken,
-                            WindowManager.LayoutParams.TYPE_INPUT_METHOD);
+                            WindowManager.LayoutParams.TYPE_VOICE_INTERACTION);
                 } catch (RemoteException e) {
                     Slog.w(TAG, "Failed adding window token", e);
                 }
diff --git a/telecomm/java/android/telecomm/CallService.java b/telecomm/java/android/telecomm/CallService.java
index 51f10c1..d452172 100644
--- a/telecomm/java/android/telecomm/CallService.java
+++ b/telecomm/java/android/telecomm/CallService.java
@@ -27,6 +27,8 @@
 import com.android.internal.telecomm.ICallService;
 import com.android.internal.telecomm.ICallServiceAdapter;
 
+import java.util.List;
+
 /**
  * Base implementation of CallService which can be used to provide calls for the system
  * in-call UI. CallService is a one-way service from the framework's CallsManager to any app
@@ -59,6 +61,8 @@
     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_ADD_TO_CONFERENCE = 14;
+    private static final int MSG_SPLIT_FROM_CONFERENCE = 15;
 
     /**
      * Default Handler used to consolidate binder method calls onto a single thread.
@@ -123,6 +127,29 @@
                 case MSG_STOP_DTMF_TONE:
                     stopDtmfTone((String) msg.obj);
                     break;
+                case MSG_ADD_TO_CONFERENCE: {
+                    SomeArgs args = (SomeArgs) msg.obj;
+                    try {
+                        @SuppressWarnings("unchecked")
+                        List<String> callIds = (List<String>) args.arg2;
+                        String conferenceCallId = (String) args.arg1;
+                        addToConference(conferenceCallId, callIds);
+                    } finally {
+                        args.recycle();
+                    }
+                    break;
+                }
+                case MSG_SPLIT_FROM_CONFERENCE: {
+                    SomeArgs args = (SomeArgs) msg.obj;
+                    try {
+                        String conferenceCallId = (String) args.arg1;
+                        String callId = (String) args.arg2;
+                        splitFromConference(conferenceCallId, callId);
+                    } finally {
+                        args.recycle();
+                    }
+                    break;
+                }
                 default:
                     break;
             }
@@ -204,6 +231,22 @@
             args.arg2 = audioState;
             mMessageHandler.obtainMessage(MSG_ON_AUDIO_STATE_CHANGED, args).sendToTarget();
         }
+
+        @Override
+        public void addToConference(String conferenceCallId, List<String> callsToConference) {
+            SomeArgs args = SomeArgs.obtain();
+            args.arg1 = conferenceCallId;
+            args.arg2 = callsToConference;
+            mMessageHandler.obtainMessage(MSG_ADD_TO_CONFERENCE, args).sendToTarget();
+        }
+
+        @Override
+        public void splitFromConference(String conferenceCallId, String callId) {
+            SomeArgs args = SomeArgs.obtain();
+            args.arg1 = conferenceCallId;
+            args.arg2 = callId;
+            mMessageHandler.obtainMessage(MSG_SPLIT_FROM_CONFERENCE, args).sendToTarget();
+        }
     }
 
     /**
@@ -359,4 +402,24 @@
      * @param audioState The new {@link CallAudioState}.
      */
     public abstract void onAudioStateChanged(String activeCallId, CallAudioState audioState);
+
+    /**
+     * Adds the specified calls to the specified conference call.
+     *
+     * @param conferenceCallId The unique ID of the conference call onto which the specified calls
+     *         should be added.
+     * @param callIds The calls to add to the conference call.
+     * @hide
+     */
+    public abstract void addToConference(String conferenceCallId, List<String> callIds);
+
+    /**
+     * Removes the specified call from the specified conference call. This is a no-op if the call
+     * is not already part of the conference call.
+     *
+     * @param conferenceCallId The conference call.
+     * @param callId The call to remove from the conference call
+     * @hide
+     */
+    public abstract void splitFromConference(String conferenceCallId, String callId);
 }
diff --git a/telecomm/java/android/telecomm/CallServiceAdapter.java b/telecomm/java/android/telecomm/CallServiceAdapter.java
index d5bb989..7396808 100644
--- a/telecomm/java/android/telecomm/CallServiceAdapter.java
+++ b/telecomm/java/android/telecomm/CallServiceAdapter.java
@@ -20,6 +20,8 @@
 
 import com.android.internal.telecomm.ICallServiceAdapter;
 
+import java.util.List;
+
 /**
  * Provides methods for ICallService implementations to interact with the system phone app.
  * TODO(santoscordon): Need final public-facing comments in this file.
@@ -156,5 +158,59 @@
         }
     }
 
+    /**
+     * Asks Telecomm to start or stop a ringback tone for a call.
+     *
+     * @param callId The unique ID of the call whose ringback is being changed.
+     * @param ringback Whether Telecomm should start playing a ringback tone.
+     */
+    public void setRequestingRingback(String callId, boolean ringback) {
+        try {
+            mAdapter.setRequestingRingback(callId, ringback);
+        } catch (RemoteException e) {
+        }
+    }
 
+    /**
+     * Indicates that the specified call can conference with any of the specified list of calls.
+     *
+     * @param callId The unique ID of the call.
+     * @param conferenceCapableCallIds The unique IDs of the calls which can be conferenced.
+     * @hide
+     */
+    public void setCanConferenceWith(String callId, List<String> conferenceCapableCallIds) {
+        try {
+            mAdapter.setCanConferenceWith(callId, conferenceCapableCallIds);
+        } catch (RemoteException ignored) {
+        }
+    }
+
+    /**
+     * Indicates whether or not the specified call is currently conferenced into the specified
+     * conference call.
+     *
+     * @param conferenceCallId The unique ID of the conference call.
+     * @param callId The unique ID of the call being conferenced.
+     * @hide
+     */
+    public void setIsConferenced(String conferenceCallId, String callId, boolean isConferenced) {
+        try {
+            mAdapter.setIsConferenced(conferenceCallId, callId, isConferenced);
+        } catch (RemoteException ignored) {
+        }
+    }
+
+    /**
+     * Indicates that the call no longer exists. Can be used with either a call or a conference
+     * call.
+     *
+     * @param callId The unique ID of the call.
+     * @hide
+     */
+    public void removeCall(String callId) {
+        try {
+            mAdapter.removeCall(callId);
+        } catch (RemoteException ignored) {
+        }
+    }
 }
diff --git a/telecomm/java/android/telecomm/Connection.java b/telecomm/java/android/telecomm/Connection.java
index 88de17a..8cce8e6 100644
--- a/telecomm/java/android/telecomm/Connection.java
+++ b/telecomm/java/android/telecomm/Connection.java
@@ -33,6 +33,7 @@
         void onHandleChanged(Connection c, Uri newHandle);
         void onSignalChanged(Connection c, Bundle details);
         void onDisconnected(Connection c, int cause, String message);
+        void onRequestingRingback(Connection c, boolean ringback);
         void onDestroyed(Connection c);
     }
 
@@ -60,6 +61,10 @@
         /** {@inheritDoc} */
         @Override
         public void onDestroyed(Connection c) {}
+
+        /** {@inheritDoc} */
+        @Override
+        public void onRequestingRingback(Connection c, boolean ringback) {}
     }
 
     public final class State {
@@ -77,6 +82,7 @@
     private int mState = State.NEW;
     private CallAudioState mCallAudioState;
     private Uri mHandle;
+    private boolean mRequestingRingback = false;
 
     /**
      * Create a new Connection.
@@ -268,6 +274,14 @@
     }
 
     /**
+     * @return Whether this connection is requesting that the system play a ringback tone
+     * on its behalf.
+     */
+    public boolean isRequestingRingback() {
+        return mRequestingRingback;
+    }
+
+    /**
      * Sets the value of the {@link #getHandle()} property and notifies listeners.
      *
      * @param handle The new handle.
@@ -286,6 +300,7 @@
      * communicate).
      */
     protected void setActive() {
+        setRequestingRingback(false);
         setState(State.ACTIVE);
     }
 
@@ -329,6 +344,21 @@
     }
 
     /**
+     * Requests that the framework play a ringback tone. This is to be invoked by implementations
+     * that do not play a ringback tone themselves in the call's audio stream.
+     *
+     * @param ringback Whether the ringback tone is to be played.
+     */
+    protected void setRequestingRingback(boolean ringback) {
+        if (mRequestingRingback != ringback) {
+            mRequestingRingback = ringback;
+            for (Listener l : mListeners) {
+                l.onRequestingRingback(this, ringback);
+            }
+        }
+    }
+
+    /**
      * Notifies this Connection and listeners that the {@link #getCallAudioState()} property
      * has a new value.
      *
@@ -336,7 +366,7 @@
      */
     protected void onSetAudioState(CallAudioState state) {
         // TODO: Enforce super called
-        this.mCallAudioState = state;
+        mCallAudioState = state;
         for (Listener l : mListeners) {
             l.onAudioStateChanged(this, state);
         }
@@ -356,6 +386,21 @@
     }
 
     /**
+     * Notifies this Connection of an internal state change. This method is called before the
+     * state is actually changed. Overriding implementations must call
+     * {@code super.onSetState(state)}.
+     *
+     * @param state The new state, a {@link Connection.State} member.
+     */
+    protected void onSetState(int state) {
+        // TODO: Enforce super called
+        this.mState = state;
+        for (Listener l : mListeners) {
+            l.onStateChanged(this, state);
+        }
+    }
+
+    /**
      * Notifies this Connection of a request to play a DTMF tone.
      *
      * @param c A DTMF character.
@@ -401,9 +446,6 @@
 
     private void setState(int state) {
         Log.d(this, "setState: %s", stateToString(state));
-        this.mState = state;
-        for (Listener l : mListeners) {
-            l.onStateChanged(this, state);
-        }
+        onSetState(state);
     }
 }
diff --git a/telecomm/java/android/telecomm/ConnectionService.java b/telecomm/java/android/telecomm/ConnectionService.java
index 492b08e..aeb1c33 100644
--- a/telecomm/java/android/telecomm/ConnectionService.java
+++ b/telecomm/java/android/telecomm/ConnectionService.java
@@ -20,6 +20,8 @@
 import android.os.Bundle;
 
 import java.util.HashMap;
+import java.util.LinkedList;
+import java.util.List;
 import java.util.Map;
 
 /**
@@ -89,6 +91,13 @@
         public void onDestroyed(Connection c) {
             removeConnection(c);
         }
+
+        @Override
+        public void onRequestingRingback(Connection c, boolean ringback) {
+            String id = mIdByConnection.get(c);
+            Log.d(this, "Adapter onRingback %b", ringback);
+            getAdapter().setRequestingRingback(id, ringback);
+        }
     };
 
     @Override
@@ -241,6 +250,39 @@
         findConnectionForAction(callId, "onAudioStateChanged").setAudioState(audioState);
     }
 
+    /** @hide */
+    @Override
+    public final void addToConference(String conferenceCallId, List<String> callIds) {
+        Log.d(this, "addToConference %s, %s", conferenceCallId, callIds);
+
+        List<Connection> connections = new LinkedList<>();
+        for (String id : callIds) {
+            Connection connection = findConnectionForAction(id, "addToConference");
+            if (connection == NULL_CONNECTION) {
+                Log.w(this, "Connection missing in conference request %s.", id);
+                return;
+            }
+            connections.add(connection);
+        }
+
+        // TODO(santoscordon): Find an existing conference call or create a new one. Then call
+        // conferenceWith on it.
+    }
+
+    /** @hide */
+    @Override
+    public final void splitFromConference(String conferenceCallId, String callId) {
+        Log.d(this, "splitFromConference(%s, %s)", conferenceCallId, callId);
+
+        Connection connection = findConnectionForAction(callId, "splitFromConference");
+        if (connection == NULL_CONNECTION) {
+            Log.w(this, "Connection missing in conference request %s.", callId);
+            return;
+        }
+
+        // TODO(santoscordon): Find existing conference call and invoke split(connection).
+    }
+
     /**
      * Find a set of Subscriptions matching a given handle (e.g. phone number).
      *
@@ -335,4 +377,4 @@
         Log.w(this, "%s - Cannot find Connection %s", action, callId);
         return NULL_CONNECTION;
     }
-}
\ No newline at end of file
+}
diff --git a/telecomm/java/android/telecomm/InCallAdapter.java b/telecomm/java/android/telecomm/InCallAdapter.java
index e41d3f6..6838ede 100644
--- a/telecomm/java/android/telecomm/InCallAdapter.java
+++ b/telecomm/java/android/telecomm/InCallAdapter.java
@@ -196,4 +196,32 @@
         } catch (RemoteException e) {
         }
     }
+
+    /**
+     * Instructs Telecomm to conference the specified calls together.
+     *
+     * @param callId The unique ID of the call.
+     * @param callIdToConference The unique ID of the call to conference with.
+     * @hide
+     */
+    void conferenceWith(String callId, String callIdToConference) {
+        try {
+            mAdapter.conferenceWith(callId, callIdToConference);
+        } catch (RemoteException ignored) {
+        }
+    }
+
+    /**
+     * Instructs Telecomm to split the specified call from any conference call with which it may be
+     * connected.
+     *
+     * @param callId The unique ID of the call.
+     * @hide
+     */
+    void splitFromConference(String callId) {
+        try {
+            mAdapter.splitFromConference(callId);
+        } catch (RemoteException ignored) {
+        }
+    }
 }
diff --git a/telecomm/java/android/telecomm/InCallCall.java b/telecomm/java/android/telecomm/InCallCall.java
index c3b2ae7..346d207 100644
--- a/telecomm/java/android/telecomm/InCallCall.java
+++ b/telecomm/java/android/telecomm/InCallCall.java
@@ -17,13 +17,13 @@
 package android.telecomm;
 
 import android.net.Uri;
-import android.os.Bundle;
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.telephony.DisconnectCause;
 
-import java.util.Date;
-import java.util.UUID;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
 
 /**
  * Information about a call that is used between InCallService and Telecomm.
@@ -38,6 +38,26 @@
     private final GatewayInfo mGatewayInfo;
     private final CallServiceDescriptor mCurrentCallServiceDescriptor;
     private final CallServiceDescriptor mHandoffCallServiceDescriptor;
+    private final List<String> mConferenceCapableCallIds;
+    private final String mParentCallId;
+    private final List<String> mChildCallIds;
+
+    /** @hide */
+    @SuppressWarnings("unchecked")
+    public InCallCall(
+            String id,
+            CallState state,
+            int disconnectCause,
+            int capabilities,
+            long connectTimeMillis,
+            Uri handle,
+            GatewayInfo gatewayInfo,
+            CallServiceDescriptor descriptor,
+            CallServiceDescriptor handoffDescriptor) {
+        this(id, state, disconnectCause, capabilities, connectTimeMillis, handle, gatewayInfo,
+                descriptor, handoffDescriptor, Collections.EMPTY_LIST, null,
+                Collections.EMPTY_LIST);
+    }
 
     /** @hide */
     public InCallCall(
@@ -49,7 +69,10 @@
             Uri handle,
             GatewayInfo gatewayInfo,
             CallServiceDescriptor descriptor,
-            CallServiceDescriptor handoffDescriptor) {
+            CallServiceDescriptor handoffDescriptor,
+            List<String> conferenceCapableCallIds,
+            String parentCallId,
+            List<String> childCallIds) {
         mId = id;
         mState = state;
         mDisconnectCause = disconnectCause;
@@ -59,6 +82,9 @@
         mGatewayInfo = gatewayInfo;
         mCurrentCallServiceDescriptor = descriptor;
         mHandoffCallServiceDescriptor = handoffDescriptor;
+        mConferenceCapableCallIds = conferenceCapableCallIds;
+        mParentCallId = parentCallId;
+        mChildCallIds = childCallIds;
     }
 
     /** The unique ID of the call. */
@@ -112,6 +138,31 @@
         return mHandoffCallServiceDescriptor;
     }
 
+    /**
+     * The calls with which this call can conference.
+     * @hide
+     */
+    public List<String> getConferenceCapableCallIds() {
+        return mConferenceCapableCallIds;
+    }
+
+    /**
+     * The conference call to which this call is conferenced. Null if not conferenced.
+     * @hide
+     */
+    public String getParentCallId() {
+        return mParentCallId;
+    }
+
+    /**
+     * The child call-IDs if this call is a conference call. Returns an empty list if this is not
+     * a conference call or if the conference call contains no children.
+     * @hide
+     */
+    public List<String> getChildCallIds() {
+        return mChildCallIds;
+    }
+
     /** Responsible for creating InCallCall objects for deserialized Parcels. */
     public static final Parcelable.Creator<InCallCall> CREATOR =
             new Parcelable.Creator<InCallCall> () {
@@ -127,8 +178,14 @@
             GatewayInfo gatewayInfo = source.readParcelable(classLoader);
             CallServiceDescriptor descriptor = source.readParcelable(classLoader);
             CallServiceDescriptor handoffDescriptor = source.readParcelable(classLoader);
+            List<String> conferenceCapableCallIds = new ArrayList<>();
+            source.readList(conferenceCapableCallIds, classLoader);
+            String parentCallId = source.readString();
+            List<String> childCallIds = new ArrayList<>();
+            source.readList(childCallIds, classLoader);
             return new InCallCall(id, state, disconnectCause, capabilities, connectTimeMillis,
-                    handle, gatewayInfo, descriptor, handoffDescriptor);
+                    handle, gatewayInfo, descriptor, handoffDescriptor, conferenceCapableCallIds,
+                    parentCallId, childCallIds);
         }
 
         @Override
@@ -155,5 +212,8 @@
         destination.writeParcelable(mGatewayInfo, 0);
         destination.writeParcelable(mCurrentCallServiceDescriptor, 0);
         destination.writeParcelable(mHandoffCallServiceDescriptor, 0);
+        destination.writeList(mConferenceCapableCallIds);
+        destination.writeString(mParentCallId);
+        destination.writeList(mChildCallIds);
     }
 }
diff --git a/telecomm/java/android/telecomm/InCallService.java b/telecomm/java/android/telecomm/InCallService.java
index 63b2020..3a63077 100644
--- a/telecomm/java/android/telecomm/InCallService.java
+++ b/telecomm/java/android/telecomm/InCallService.java
@@ -42,6 +42,7 @@
     private static final int MSG_SET_POST_DIAL = 4;
     private static final int MSG_SET_POST_DIAL_WAIT = 5;
     private static final int MSG_ON_AUDIO_STATE_CHANGED = 6;
+    private static final int MSG_BRING_TO_FOREGROUND = 7;
 
     /** Default Handler used to consolidate binder method calls onto a single thread. */
     private final Handler mHandler = new Handler(Looper.getMainLooper()) {
@@ -83,6 +84,9 @@
                 case MSG_ON_AUDIO_STATE_CHANGED:
                     onAudioStateChanged((CallAudioState) msg.obj);
                     break;
+                case MSG_BRING_TO_FOREGROUND:
+                    bringToForeground(msg.arg1 == 1);
+                    break;
                 default:
                     break;
             }
@@ -130,6 +134,12 @@
         public void onAudioStateChanged(CallAudioState audioState) {
             mHandler.obtainMessage(MSG_ON_AUDIO_STATE_CHANGED, audioState).sendToTarget();
         }
+
+        /** {@inheritDoc} */
+        @Override
+        public void bringToForeground(boolean showDialpad) {
+            mHandler.obtainMessage(MSG_BRING_TO_FOREGROUND, showDialpad ? 1 : 0, 0).sendToTarget();
+        }
     }
 
     private final InCallServiceBinder mBinder;
@@ -206,4 +216,11 @@
      * @param audioState The new {@link CallAudioState}.
      */
     protected abstract void onAudioStateChanged(CallAudioState audioState);
+
+    /**
+     * Brings the in-call screen to the foreground.
+     *
+     * @param showDialpad If true, put up the dialpad when the screen is shown.
+     */
+    protected abstract void bringToForeground(boolean showDialpad);
 }
diff --git a/telecomm/java/com/android/internal/telecomm/ICallService.aidl b/telecomm/java/com/android/internal/telecomm/ICallService.aidl
index cc0641c..771a3ae 100644
--- a/telecomm/java/com/android/internal/telecomm/ICallService.aidl
+++ b/telecomm/java/com/android/internal/telecomm/ICallService.aidl
@@ -55,4 +55,8 @@
     void playDtmfTone(String callId, char digit);
 
     void stopDtmfTone(String callId);
+
+    void addToConference(String conferenceCallId, in List<String> callIds);
+
+    void splitFromConference(String conferenceCallId, String callId);
 }
diff --git a/telecomm/java/com/android/internal/telecomm/ICallServiceAdapter.aidl b/telecomm/java/com/android/internal/telecomm/ICallServiceAdapter.aidl
index dfdaa75..a92b176 100644
--- a/telecomm/java/com/android/internal/telecomm/ICallServiceAdapter.aidl
+++ b/telecomm/java/com/android/internal/telecomm/ICallServiceAdapter.aidl
@@ -43,4 +43,12 @@
     void setDisconnected(String callId, int disconnectCause, String disconnectMessage);
 
     void setOnHold(String callId);
+
+    void setRequestingRingback(String callId, boolean ringing);
+
+    void setCanConferenceWith(String callId, in List<String> conferenceCapableCallIds);
+
+    void setIsConferenced(String conferenceCallId, String callId, boolean isConferenced);
+
+    void removeCall(String callId);
 }
diff --git a/telecomm/java/com/android/internal/telecomm/IInCallAdapter.aidl b/telecomm/java/com/android/internal/telecomm/IInCallAdapter.aidl
index 512e898..6a27217 100644
--- a/telecomm/java/com/android/internal/telecomm/IInCallAdapter.aidl
+++ b/telecomm/java/com/android/internal/telecomm/IInCallAdapter.aidl
@@ -47,4 +47,8 @@
     void postDialContinue(String callId);
 
     void handoffCall(String callId);
+
+    void conferenceWith(String callId, String callIdToConference);
+
+    void splitFromConference(String callId);
 }
diff --git a/telecomm/java/com/android/internal/telecomm/IInCallService.aidl b/telecomm/java/com/android/internal/telecomm/IInCallService.aidl
index ccf7e3f..1635053 100644
--- a/telecomm/java/com/android/internal/telecomm/IInCallService.aidl
+++ b/telecomm/java/com/android/internal/telecomm/IInCallService.aidl
@@ -40,4 +40,6 @@
     void setPostDialWait(String callId, String remaining);
 
     void onAudioStateChanged(in CallAudioState audioState);
+
+    void bringToForeground(boolean showDialpad);
 }
diff --git a/telecomm/java/com/android/internal/telecomm/ITelecommService.aidl b/telecomm/java/com/android/internal/telecomm/ITelecommService.aidl
index c439211..0e94ffb 100644
--- a/telecomm/java/com/android/internal/telecomm/ITelecommService.aidl
+++ b/telecomm/java/com/android/internal/telecomm/ITelecommService.aidl
@@ -21,7 +21,7 @@
  * commands that were previously handled by ITelephony.
  * {@hide}
  */
-oneway interface ITelecommService {
+interface ITelecommService {
 
     /**
      * Silence the ringer if an incoming call is currently ringing.
@@ -31,4 +31,11 @@
      * even if there's no incoming call.  (If so, this method will do nothing.)
      */
     void silenceRinger();
+
+    /**
+     * Brings the in-call screen to the foreground if there is an active call.
+     *
+     * @param showDialpad if true, make the dialpad visible initially.
+     */
+    void showCallScreen(boolean showDialpad);
 }
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index 4aed1fe..ffa9a4e 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -1752,6 +1752,8 @@
      *
      * @param AID Application id. See ETSI 102.221 and 101.220.
      * @return The logical channel id which is negative on error.
+     *
+     * @hide
      */
     public int iccOpenLogicalChannel(String AID) {
         try {
@@ -1773,6 +1775,8 @@
      * @param channel is the channel id to be closed as retruned by a successful
      *            iccOpenLogicalChannel.
      * @return true if the channel was closed successfully.
+     *
+     * @hide
      */
     public boolean iccCloseLogicalChannel(int channel) {
         try {
@@ -1802,6 +1806,8 @@
      * @param data Data to be sent with the APDU.
      * @return The APDU response from the ICC card with the status appended at
      *            the end. If an error occurs, an empty string is returned.
+     *
+     * @hide
      */
     public String iccTransmitApduLogicalChannel(int channel, int cla,
             int instruction, int p1, int p2, int p3, String data) {
@@ -1826,6 +1832,8 @@
      * @return The APDU response from the ICC card, with the last 4 bytes
      *         being the status word. If the command fails, returns an empty
      *         string.
+     *
+     * @hide
      */
     public String sendEnvelopeWithStatus(String content) {
         try {
@@ -1980,9 +1988,10 @@
     @PrivateApi
     public boolean showCallScreen() {
         try {
-            return getITelephony().showCallScreen();
+            getTelecommService().showCallScreen(false);
+            return true;
         } catch (RemoteException e) {
-            Log.e(TAG, "Error calling ITelephony#showCallScreen", e);
+            Log.e(TAG, "Error calling ITelecommService#showCallScreen", e);
         }
         return false;
     }
@@ -1991,9 +2000,10 @@
     @PrivateApi
     public boolean showCallScreenWithDialpad(boolean showDialpad) {
         try {
-            return getITelephony().showCallScreenWithDialpad(showDialpad);
+            getTelecommService().showCallScreen(showDialpad);
+            return true;
         } catch (RemoteException e) {
-            Log.e(TAG, "Error calling ITelephony#showCallScreenWithDialpad", e);
+            Log.e(TAG, "Error calling ITelecommService#showCallScreen(" + showDialpad + ")", e);
         }
         return false;
     }
diff --git a/telephony/java/com/android/internal/telephony/DctConstants.java b/telephony/java/com/android/internal/telephony/DctConstants.java
index 59bdf64..874279b 100644
--- a/telephony/java/com/android/internal/telephony/DctConstants.java
+++ b/telephony/java/com/android/internal/telephony/DctConstants.java
@@ -98,6 +98,7 @@
     public static final int CMD_IS_PROVISIONING_APN = BASE + 38;
     public static final int EVENT_PROVISIONING_APN_ALARM = BASE + 39;
     public static final int CMD_NET_STAT_POLL = BASE + 40;
+    public static final int EVENT_DATA_RAT_CHANGED = BASE + 41;
 
     /***** Constants *****/
 
diff --git a/telephony/java/com/android/internal/telephony/ITelephony.aidl b/telephony/java/com/android/internal/telephony/ITelephony.aidl
index 6d7f158..acaa8de 100644
--- a/telephony/java/com/android/internal/telephony/ITelephony.aidl
+++ b/telephony/java/com/android/internal/telephony/ITelephony.aidl
@@ -49,28 +49,6 @@
     void call(String callingPackage, String number);
 
     /**
-     * If there is currently a call in progress, show the call screen.
-     * The DTMF dialpad may or may not be visible initially, depending on
-     * whether it was up when the user last exited the InCallScreen.
-     *
-     * @return true if the call screen was shown.
-     */
-    boolean showCallScreen();
-
-    /**
-     * Variation of showCallScreen() that also specifies whether the
-     * DTMF dialpad should be initially visible when the InCallScreen
-     * comes up.
-     *
-     * @param showDialpad if true, make the dialpad visible initially,
-     *                    otherwise hide the dialpad initially.
-     * @return true if the call screen was shown.
-     *
-     * @see showCallScreen
-     */
-    boolean showCallScreenWithDialpad(boolean showDialpad);
-
-    /**
      * End call if there is a call in progress, otherwise does nothing.
      *
      * @return whether it hung up
diff --git a/test-runner/src/android/test/MoreAsserts.java b/test-runner/src/android/test/MoreAsserts.java
index fb0faba..3364895 100644
--- a/test-runner/src/android/test/MoreAsserts.java
+++ b/test-runner/src/android/test/MoreAsserts.java
@@ -128,6 +128,33 @@
     }
 
     /**
+     * @hide Asserts that array {@code actual} is the same size and every element equals
+     * those in array {@code expected}. On failure, message indicates first
+     * specific element mismatch.
+     */
+    public static void assertEquals(
+            String message, long[] expected, long[] actual) {
+        if (expected.length != actual.length) {
+            failWrongLength(message, expected.length, actual.length);
+        }
+        for (int i = 0; i < expected.length; i++) {
+            if (expected[i] != actual[i]) {
+                failWrongElement(message, i, expected[i], actual[i]);
+            }
+        }
+    }
+
+    /**
+     * @hide Asserts that array {@code actual} is the same size and every element equals
+     * those in array {@code expected}. On failure, message indicates first
+     * specific element mismatch.
+     */
+    public static void assertEquals(long[] expected, long[] actual) {
+        assertEquals(null, expected, actual);
+    }
+
+
+    /**
      * Asserts that array {@code actual} is the same size and every element equals
      * those in array {@code expected}. On failure, message indicates first
      * specific element mismatch.
diff --git a/test-runner/src/android/test/mock/MockContext.java b/test-runner/src/android/test/mock/MockContext.java
index c162bf2..a54936b 100644
--- a/test-runner/src/android/test/mock/MockContext.java
+++ b/test-runner/src/android/test/mock/MockContext.java
@@ -608,4 +608,9 @@
     public File[] getExternalCacheDirs() {
         throw new UnsupportedOperationException();
     }
+
+    @Override
+    public File[] getExternalMediaDirs() {
+        throw new UnsupportedOperationException();
+    }
 }
diff --git a/tests/Camera2Tests/SmartCamera/SimpleCamera/src/androidx/media/filterfw/samples/simplecamera/Camera2Source.java b/tests/Camera2Tests/SmartCamera/SimpleCamera/src/androidx/media/filterfw/samples/simplecamera/Camera2Source.java
index fa0f995..6876f5a 100644
--- a/tests/Camera2Tests/SmartCamera/SimpleCamera/src/androidx/media/filterfw/samples/simplecamera/Camera2Source.java
+++ b/tests/Camera2Tests/SmartCamera/SimpleCamera/src/androidx/media/filterfw/samples/simplecamera/Camera2Source.java
@@ -26,6 +26,7 @@
 import android.hardware.camera2.CaptureFailure;
 import android.hardware.camera2.CaptureRequest;
 import android.hardware.camera2.CaptureResult;
+import android.hardware.camera2.TotalCaptureResult;
 import android.os.Handler;
 import android.renderscript.Allocation;
 import android.renderscript.Element;
@@ -86,7 +87,7 @@
 
         @Override
         public void onCaptureCompleted(CameraDevice camera, CaptureRequest request,
-                CaptureResult result) {
+                TotalCaptureResult result) {
             // TODO Auto-generated method stub
             Log.v(TAG, "in onCaptureComplete");
 
diff --git a/tests/HwAccelerationTest/AndroidManifest.xml b/tests/HwAccelerationTest/AndroidManifest.xml
index db802c5..6b70631 100644
--- a/tests/HwAccelerationTest/AndroidManifest.xml
+++ b/tests/HwAccelerationTest/AndroidManifest.xml
@@ -902,5 +902,14 @@
                 <category android:name="com.android.test.hwui.TEST" />
             </intent-filter>
         </activity>
+
+        <activity
+                android:name=".ZOrderingActivity"
+                android:label="Reordering/Z Ordering">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="com.android.test.hwui.TEST" />
+            </intent-filter>
+        </activity>
     </application>
 </manifest>
diff --git a/tests/HwAccelerationTest/res/layout/z_ordering.xml b/tests/HwAccelerationTest/res/layout/z_ordering.xml
new file mode 100644
index 0000000..970c5fd
--- /dev/null
+++ b/tests/HwAccelerationTest/res/layout/z_ordering.xml
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:orientation="horizontal"
+        android:id="@+id/parent">
+    <RelativeLayout
+            android:layout_width="400dp"
+            android:layout_height="200dp"
+            android:layout_weight="1"
+            android:orientation="vertical">
+        <TextView style="@style/TopLeftReorderTextView"/>
+        <TextView style="@style/BottomLeftReorderTextView"/>
+        <TextView style="@style/TopRightReorderTextView"/>
+        <TextView style="@style/BottomRightReorderTextView"/>
+    </RelativeLayout>
+</LinearLayout>
\ No newline at end of file
diff --git a/tests/HwAccelerationTest/res/values/styles.xml b/tests/HwAccelerationTest/res/values/styles.xml
index cde5d20..108709b 100644
--- a/tests/HwAccelerationTest/res/values/styles.xml
+++ b/tests/HwAccelerationTest/res/values/styles.xml
@@ -1,34 +1,37 @@
 <resources>
     <style name="ReorderTextView" parent="@android:style/TextAppearance.Medium">
-        <item name="android:layout_width">match_parent</item>
+        <item name="android:background">@drawable/appwidget_background</item>
+        <item name="android:layout_width">300dp</item>
         <item name="android:layout_height">100dp</item>
         <item name="android:gravity">center</item>
     </style>
     <style name="LeftReorderTextView" parent="@style/ReorderTextView">
-        <item name="android:translationX">20dp</item>
+        <item name="android:translationX">50dp</item>
+        <item name="android:layout_alignParentLeft">true</item>
     </style>
     <style name="RightReorderTextView" parent="@style/ReorderTextView">
-        <item name="android:translationX">-20dp</item>
+        <item name="android:translationX">-50dp</item>
+        <item name="android:layout_alignParentRight">true</item>
     </style>
 
     <style name="TopLeftReorderTextView" parent="@style/LeftReorderTextView">
-        <item name="android:background">#666</item>
-        <item name="android:text">100</item>
-        <item name="android:translationZ">100dp</item>
-    </style>
-    <style name="BottomLeftReorderTextView" parent="@style/LeftReorderTextView">
-        <item name="android:background">#bbb</item>
-        <item name="android:text">300</item>
-        <item name="android:translationZ">300dp</item>
-    </style>
-    <style name="TopRightReorderTextView" parent="@style/RightReorderTextView">
-        <item name="android:background">#888</item>
         <item name="android:text">200</item>
         <item name="android:translationZ">200dp</item>
+        <item name="android:layout_alignParentTop">true</item>
+    </style>
+    <style name="BottomLeftReorderTextView" parent="@style/LeftReorderTextView">
+        <item name="android:text">300</item>
+        <item name="android:translationZ">300dp</item>
+        <item name="android:layout_alignParentBottom">true</item>
+    </style>
+    <style name="TopRightReorderTextView" parent="@style/RightReorderTextView">
+        <item name="android:text">100</item>
+        <item name="android:translationZ">100dp</item>
+        <item name="android:layout_alignParentTop">true</item>
     </style>
     <style name="BottomRightReorderTextView" parent="@style/RightReorderTextView">
-        <item name="android:background">#ccc</item>
         <item name="android:text">400</item>
         <item name="android:translationZ">400dp</item>
+        <item name="android:layout_alignParentBottom">true</item>
     </style>
 </resources>
diff --git a/tests/HwAccelerationTest/src/com/android/test/hwui/CirclePropActivity.java b/tests/HwAccelerationTest/src/com/android/test/hwui/CirclePropActivity.java
index 5b0aa66..a81e063 100644
--- a/tests/HwAccelerationTest/src/com/android/test/hwui/CirclePropActivity.java
+++ b/tests/HwAccelerationTest/src/com/android/test/hwui/CirclePropActivity.java
@@ -118,19 +118,25 @@
                     mRadius, mToggle ? 250.0f : 150.0f));
 
             mRunningAnimations.add(new RenderNodeAnimator(
-                    mPaint, RenderNodeAnimator.PAINT_ALPHA,
-                    mToggle ? 64.0f : 255.0f));
-
-            mRunningAnimations.add(new RenderNodeAnimator(
                     mPaint, RenderNodeAnimator.PAINT_STROKE_WIDTH,
                     mToggle ? 5.0f : 60.0f));
 
-            TimeInterpolator interp = new OvershootInterpolator(3.0f);
+            mRunningAnimations.add(new RenderNodeAnimator(
+                    mPaint, RenderNodeAnimator.PAINT_ALPHA, 64.0f));
+
+            // Will be "chained" to run after the above
+            mRunningAnimations.add(new RenderNodeAnimator(
+                    mPaint, RenderNodeAnimator.PAINT_ALPHA, 255.0f));
+
             for (int i = 0; i < mRunningAnimations.size(); i++) {
                 RenderNodeAnimator anim = mRunningAnimations.get(i);
-                anim.setInterpolator(interp);
                 anim.setDuration(1000);
                 anim.setTarget(this);
+                if (i == (mRunningAnimations.size() - 1)) {
+                    // "chain" test
+                    anim.setStartValue(64.0f);
+                    anim.setStartDelay(anim.getDuration());
+                }
                 anim.start();
             }
 
diff --git a/tests/HwAccelerationTest/src/com/android/test/hwui/ZOrderingActivity.java b/tests/HwAccelerationTest/src/com/android/test/hwui/ZOrderingActivity.java
new file mode 100644
index 0000000..45e77ed
--- /dev/null
+++ b/tests/HwAccelerationTest/src/com/android/test/hwui/ZOrderingActivity.java
@@ -0,0 +1,28 @@
+package com.android.test.hwui;
+
+import android.app.Activity;
+import android.os.Bundle;
+import android.util.Log;
+import android.view.View;
+import android.view.ViewGroup;
+
+public class ZOrderingActivity extends Activity {
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        setContentView(R.layout.z_ordering);
+
+        ViewGroup grandParent = (ViewGroup) findViewById(R.id.parent);
+        if (grandParent == null) throw new IllegalStateException();
+        View.OnClickListener l = new View.OnClickListener() {
+            @Override
+            public void onClick(View v) {}
+        };
+        for (int i = 0; i < grandParent.getChildCount(); i++) {
+            ViewGroup parent = (ViewGroup) grandParent.getChildAt(i);
+            for (int j = 0; j < parent.getChildCount(); j++) {
+                parent.getChildAt(j).setOnClickListener(l);
+            }
+        }
+    }
+}
diff --git a/tests/OneMedia/src/com/android/onemedia/OnePlayerActivity.java b/tests/OneMedia/src/com/android/onemedia/OnePlayerActivity.java
index 158f5e4..ee407ad 100644
--- a/tests/OneMedia/src/com/android/onemedia/OnePlayerActivity.java
+++ b/tests/OneMedia/src/com/android/onemedia/OnePlayerActivity.java
@@ -99,10 +99,10 @@
             switch (v.getId()) {
                 case R.id.play_button:
                     Log.d(TAG, "Play button pressed, in state " + mPlaybackState);
-                    if (mPlaybackState == PlaybackState.PLAYSTATE_PAUSED
-                            || mPlaybackState == PlaybackState.PLAYSTATE_STOPPED) {
+                    if (mPlaybackState == PlaybackState.STATE_PAUSED
+                            || mPlaybackState == PlaybackState.STATE_STOPPED) {
                         mPlayer.play();
-                    } else if (mPlaybackState == PlaybackState.PLAYSTATE_PLAYING) {
+                    } else if (mPlaybackState == PlaybackState.STATE_PLAYING) {
                         mPlayer.pause();
                     }
                     break;
@@ -126,31 +126,31 @@
             boolean enableControls = true;
             StringBuilder statusBuilder = new StringBuilder();
             switch (mPlaybackState) {
-                case PlaybackState.PLAYSTATE_PLAYING:
+                case PlaybackState.STATE_PLAYING:
                     statusBuilder.append("playing");
                     mPlayButton.setText("Pause");
                     enablePlay = true;
                     break;
-                case PlaybackState.PLAYSTATE_PAUSED:
+                case PlaybackState.STATE_PAUSED:
                     statusBuilder.append("paused");
                     mPlayButton.setText("Play");
                     enablePlay = true;
                     break;
-                case PlaybackState.PLAYSTATE_STOPPED:
+                case PlaybackState.STATE_STOPPED:
                     statusBuilder.append("ended");
                     mPlayButton.setText("Play");
                     enablePlay = true;
                     break;
-                case PlaybackState.PLAYSTATE_ERROR:
+                case PlaybackState.STATE_ERROR:
                     statusBuilder.append("error: ").append(state.getErrorMessage());
                     break;
-                case PlaybackState.PLAYSTATE_BUFFERING:
+                case PlaybackState.STATE_BUFFERING:
                     statusBuilder.append("buffering");
                     break;
-                case PlaybackState.PLAYSTATE_NONE:
+                case PlaybackState.STATE_NONE:
                     statusBuilder.append("none");
                     break;
-                case PlaybackState.PLAYSTATE_CONNECTING:
+                case PlaybackState.STATE_CONNECTING:
                     statusBuilder.append("connecting");
                     enableControls = false;
                     break;
diff --git a/tests/OneMedia/src/com/android/onemedia/PlayerController.java b/tests/OneMedia/src/com/android/onemedia/PlayerController.java
index 9f7bb26..145b389 100644
--- a/tests/OneMedia/src/com/android/onemedia/PlayerController.java
+++ b/tests/OneMedia/src/com/android/onemedia/PlayerController.java
@@ -21,7 +21,6 @@
 import android.media.session.RouteInfo;
 import android.media.session.MediaSessionManager;
 import android.media.session.PlaybackState;
-import android.media.session.TransportController;
 import android.os.Bundle;
 import android.os.Handler;
 import android.os.IBinder;
@@ -42,12 +41,11 @@
 
     protected MediaController mController;
     protected IPlayerService mBinder;
-    protected TransportController mTransportControls;
+    protected MediaController.TransportControls mTransportControls;
 
     private final Intent mServiceIntent;
     private Context mContext;
     private Listener mListener;
-    private TransportListener mTransportListener = new TransportListener();
     private SessionCallback mControllerCb;
     private MediaSessionManager mManager;
     private Handler mHandler = new Handler();
@@ -161,16 +159,13 @@
                 return;
             }
             mController.addCallback(mControllerCb, mHandler);
-            mTransportControls = mController.getTransportController();
-            if (mTransportControls != null) {
-                mTransportControls.addStateListener(mTransportListener);
-            }
+            mTransportControls = mController.getTransportControls();
             Log.d(TAG, "Ready to use PlayerService");
 
             if (mListener != null) {
                 mListener.onConnectionStateChange(STATE_CONNECTED);
                 if (mTransportControls != null) {
-                    mListener.onPlaybackStateChange(mTransportControls.getPlaybackState());
+                    mListener.onPlaybackStateChange(mController.getPlaybackState());
                 }
             }
         }
@@ -181,9 +176,7 @@
         public void onRouteChanged(RouteInfo route) {
             // TODO
         }
-    }
 
-    private class TransportListener extends TransportController.TransportStateListener {
         @Override
         public void onPlaybackStateChanged(PlaybackState state) {
             if (state == null) {
diff --git a/tests/OneMedia/src/com/android/onemedia/PlayerService.java b/tests/OneMedia/src/com/android/onemedia/PlayerService.java
index 0ad6dd1..934f4ef 100644
--- a/tests/OneMedia/src/com/android/onemedia/PlayerService.java
+++ b/tests/OneMedia/src/com/android/onemedia/PlayerService.java
@@ -103,11 +103,11 @@
         @Override
         public void onPlayStateChanged(PlaybackState state) {
             switch (state.getState()) {
-                case PlaybackState.PLAYSTATE_PLAYING:
+                case PlaybackState.STATE_PLAYING:
                     onPlaybackStarted();
                     break;
-                case PlaybackState.PLAYSTATE_STOPPED:
-                case PlaybackState.PLAYSTATE_ERROR:
+                case PlaybackState.STATE_STOPPED:
+                case PlaybackState.STATE_ERROR:
                     onPlaybackEnded();
                     break;
             }
diff --git a/tests/OneMedia/src/com/android/onemedia/PlayerSession.java b/tests/OneMedia/src/com/android/onemedia/PlayerSession.java
index 94d0851..d6f8118 100644
--- a/tests/OneMedia/src/com/android/onemedia/PlayerSession.java
+++ b/tests/OneMedia/src/com/android/onemedia/PlayerSession.java
@@ -25,7 +25,6 @@
 import android.media.session.MediaSessionManager;
 import android.media.session.MediaSessionToken;
 import android.media.session.PlaybackState;
-import android.media.session.TransportPerformer;
 import android.os.Bundle;
 import android.util.Log;
 import android.view.KeyEvent;
@@ -45,7 +44,6 @@
     protected Renderer mRenderer;
     protected MediaSession.Callback mCallback;
     protected Renderer.Listener mRenderListener;
-    protected TransportPerformer mPerformer;
 
     protected PlaybackState mPlaybackState;
     protected Listener mListener;
@@ -84,9 +82,8 @@
         Log.d(TAG, "Creating session for package " + mContext.getBasePackageName());
         mSession = man.createSession("OneMedia");
         mSession.addCallback(mCallback);
-        mPerformer = mSession.getTransportPerformer();
-        mPerformer.addListener(new TransportListener());
-        mPerformer.setPlaybackState(mPlaybackState);
+        mSession.addTransportControlsCallback(new TransportCallback());
+        mSession.setPlaybackState(mPlaybackState);
         mSession.setFlags(MediaSession.FLAG_HANDLES_TRANSPORT_CONTROLS);
         mSession.setRouteOptions(mRouteOptions);
         mSession.setActive(true);
@@ -120,10 +117,10 @@
     }
 
     private void updateState(int newState) {
-        float rate = newState == PlaybackState.PLAYSTATE_PLAYING ? 1 : 0;
+        float rate = newState == PlaybackState.STATE_PLAYING ? 1 : 0;
         long position = mRenderer == null ? -1 : mRenderer.getSeekPosition();
         mPlaybackState.setState(newState, position, rate);
-        mPerformer.setPlaybackState(mPlaybackState);
+        mSession.setPlaybackState(mPlaybackState);
     }
 
     public interface Listener {
@@ -135,11 +132,11 @@
         @Override
         public void onError(int type, int extra, Bundle extras, Throwable error) {
             Log.d(TAG, "Sending onError with type " + type + " and extra " + extra);
-            mPlaybackState.setState(PlaybackState.PLAYSTATE_ERROR, -1, 0);
+            mPlaybackState.setState(PlaybackState.STATE_ERROR, -1, 0);
             if (error != null) {
                 mPlaybackState.setErrorMessage(error.getLocalizedMessage());
             }
-            mPerformer.setPlaybackState(mPlaybackState);
+            mSession.setPlaybackState(mPlaybackState);
             if (mListener != null) {
                 mListener.onPlayStateChanged(mPlaybackState);
             }
@@ -157,27 +154,27 @@
             switch (newState) {
                 case Renderer.STATE_ENDED:
                 case Renderer.STATE_STOPPED:
-                    mPlaybackState.setState(PlaybackState.PLAYSTATE_STOPPED, position, 0);
+                    mPlaybackState.setState(PlaybackState.STATE_STOPPED, position, 0);
                     break;
                 case Renderer.STATE_INIT:
                 case Renderer.STATE_PREPARING:
-                    mPlaybackState.setState(PlaybackState.PLAYSTATE_BUFFERING, position, 0);
+                    mPlaybackState.setState(PlaybackState.STATE_BUFFERING, position, 0);
                     break;
                 case Renderer.STATE_ERROR:
-                    mPlaybackState.setState(PlaybackState.PLAYSTATE_ERROR, position, 0);
+                    mPlaybackState.setState(PlaybackState.STATE_ERROR, position, 0);
                     break;
                 case Renderer.STATE_PAUSED:
-                    mPlaybackState.setState(PlaybackState.PLAYSTATE_PAUSED, position, 0);
+                    mPlaybackState.setState(PlaybackState.STATE_PAUSED, position, 0);
                     break;
                 case Renderer.STATE_PLAYING:
-                    mPlaybackState.setState(PlaybackState.PLAYSTATE_PLAYING, position, 1);
+                    mPlaybackState.setState(PlaybackState.STATE_PLAYING, position, 1);
                     break;
                 default:
-                    mPlaybackState.setState(PlaybackState.PLAYSTATE_ERROR, position, 0);
+                    mPlaybackState.setState(PlaybackState.STATE_ERROR, position, 0);
                     mPlaybackState.setErrorMessage("unkown state");
                     break;
             }
-            mPerformer.setPlaybackState(mPlaybackState);
+            mSession.setPlaybackState(mPlaybackState);
             if (mListener != null) {
                 mListener.onPlayStateChanged(mPlaybackState);
             }
@@ -191,8 +188,8 @@
         public void onFocusLost() {
             Log.d(TAG, "Focus lost, changing state to " + Renderer.STATE_PAUSED);
             long position = mRenderer == null ? -1 : mRenderer.getSeekPosition();
-            mPlaybackState.setState(PlaybackState.PLAYSTATE_PAUSED, position, 0);
-            mPerformer.setPlaybackState(mPlaybackState);
+            mPlaybackState.setState(PlaybackState.STATE_PAUSED, position, 0);
+            mSession.setPlaybackState(mPlaybackState);
             if (mListener != null) {
                 mListener.onPlayStateChanged(mPlaybackState);
             }
@@ -206,7 +203,7 @@
 
     private class SessionCb extends MediaSession.Callback {
         @Override
-        public void onMediaButton(Intent mediaRequestIntent) {
+        public void onMediaButtonEvent(Intent mediaRequestIntent) {
             if (Intent.ACTION_MEDIA_BUTTON.equals(mediaRequestIntent.getAction())) {
                 KeyEvent event = (KeyEvent) mediaRequestIntent
                         .getParcelableExtra(Intent.EXTRA_KEY_EVENT);
@@ -233,12 +230,12 @@
                 mRoute = null;
                 mRenderer = new LocalRenderer(mContext, null);
                 mRenderer.registerListener(mRenderListener);
-                updateState(PlaybackState.PLAYSTATE_NONE);
+                updateState(PlaybackState.STATE_NONE);
             } else {
                 // Use remote route
                 mSession.connect(route, mRouteOptions.get(0));
                 mRenderer = null;
-                updateState(PlaybackState.PLAYSTATE_CONNECTING);
+                updateState(PlaybackState.STATE_CONNECTING);
             }
         }
 
@@ -249,7 +246,7 @@
             mRouteControls.addListener(mRouteListener);
             Log.d(TAG, "Connected to route, registering listener");
             mRenderer = new OneMRPRenderer(mRouteControls);
-            updateState(PlaybackState.PLAYSTATE_NONE);
+            updateState(PlaybackState.STATE_NONE);
         }
 
         @Override
@@ -258,7 +255,7 @@
         }
     }
 
-    private class TransportListener extends TransportPerformer.Listener {
+    private class TransportCallback extends MediaSession.TransportControlsCallback {
         @Override
         public void onPlay() {
             mRenderer.onPlay();
diff --git a/tests/OneMedia/src/com/android/onemedia/provider/OneMediaRouteProvider.java b/tests/OneMedia/src/com/android/onemedia/provider/OneMediaRouteProvider.java
index 6537d49..f2d691c 100644
--- a/tests/OneMedia/src/com/android/onemedia/provider/OneMediaRouteProvider.java
+++ b/tests/OneMedia/src/com/android/onemedia/provider/OneMediaRouteProvider.java
@@ -149,7 +149,7 @@
         public void onError(int type, int extra, Bundle extras, Throwable error) {
             Log.d(TAG, "Sending onError with type " + type + " and extra " + extra);
             if (mControls != null) {
-                mControls.sendPlaybackChangeEvent(PlaybackState.PLAYSTATE_ERROR);
+                mControls.sendPlaybackChangeEvent(PlaybackState.STATE_ERROR);
             }
         }
 
@@ -165,23 +165,23 @@
             switch (newState) {
                 case Renderer.STATE_ENDED:
                 case Renderer.STATE_STOPPED:
-                    mPlaybackState.setState(PlaybackState.PLAYSTATE_STOPPED, position, 0);
+                    mPlaybackState.setState(PlaybackState.STATE_STOPPED, position, 0);
                     break;
                 case Renderer.STATE_INIT:
                 case Renderer.STATE_PREPARING:
-                    mPlaybackState.setState(PlaybackState.PLAYSTATE_BUFFERING, position, 0);
+                    mPlaybackState.setState(PlaybackState.STATE_BUFFERING, position, 0);
                     break;
                 case Renderer.STATE_ERROR:
-                    mPlaybackState.setState(PlaybackState.PLAYSTATE_ERROR, position, 0);
+                    mPlaybackState.setState(PlaybackState.STATE_ERROR, position, 0);
                     break;
                 case Renderer.STATE_PAUSED:
-                    mPlaybackState.setState(PlaybackState.PLAYSTATE_PAUSED, position, 0);
+                    mPlaybackState.setState(PlaybackState.STATE_PAUSED, position, 0);
                     break;
                 case Renderer.STATE_PLAYING:
-                    mPlaybackState.setState(PlaybackState.PLAYSTATE_PLAYING, position, 1);
+                    mPlaybackState.setState(PlaybackState.STATE_PLAYING, position, 1);
                     break;
                 default:
-                    mPlaybackState.setState(PlaybackState.PLAYSTATE_ERROR, position, 0);
+                    mPlaybackState.setState(PlaybackState.STATE_ERROR, position, 0);
                     mPlaybackState.setErrorMessage("unkown state");
                     break;
             }
@@ -196,7 +196,7 @@
         @Override
         public void onFocusLost() {
             Log.d(TAG, "Focus lost, changing state to " + Renderer.STATE_PAUSED);
-            mPlaybackState.setState(PlaybackState.PLAYSTATE_PAUSED, mRenderer.getSeekPosition(), 0);
+            mPlaybackState.setState(PlaybackState.STATE_PAUSED, mRenderer.getSeekPosition(), 0);
             mRenderer.onPause();
         }
 
diff --git a/tests/RenderThreadTest/src/com/example/renderthread/MainActivity.java b/tests/RenderThreadTest/src/com/example/renderthread/MainActivity.java
index c7715ad..fc5426c 100644
--- a/tests/RenderThreadTest/src/com/example/renderthread/MainActivity.java
+++ b/tests/RenderThreadTest/src/com/example/renderthread/MainActivity.java
@@ -43,7 +43,6 @@
     @Override
     protected void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
-        HardwareRenderer.sUseRenderThread = true;
         setContentView(R.layout.activity_main);
         ListView lv = (ListView) findViewById(android.R.id.list);
         lv.setDrawSelectorOnTop(true);
diff --git a/tests/TtsTests/src/com/android/speech/tts/AbstractTtsSemioticClassTest.java b/tests/TtsTests/src/com/android/speech/tts/AbstractTtsSemioticClassTest.java
new file mode 100644
index 0000000..31484f4
--- /dev/null
+++ b/tests/TtsTests/src/com/android/speech/tts/AbstractTtsSemioticClassTest.java
@@ -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.
+ */
+
+package com.android.speech.tts;
+
+import android.test.InstrumentationTestCase;
+
+import android.speech.tts.Markup;
+import android.speech.tts.Utterance;
+import android.speech.tts.Utterance.AbstractTtsSemioticClass;
+
+public class AbstractTtsSemioticClassTest extends InstrumentationTestCase {
+
+    public static class TtsMock extends AbstractTtsSemioticClass<TtsMock> {
+        public TtsMock() {
+            super();
+        }
+
+        public TtsMock(Markup markup) {
+            super();
+        }
+
+        public void setType(String type) {
+            mMarkup.setType(type);
+        }
+    }
+
+    public void testFluentAPI() {
+        new TtsMock()
+            .setPlainText("a plaintext") // from AbstractTts
+            .setGender(Utterance.GENDER_MALE) // from AbstractTtsSemioticClass
+            .setType("test"); // from TtsMock
+    }
+
+    public void testDefaultConstructor() {
+        new TtsMock();
+    }
+
+    public void testMarkupConstructor() {
+        Markup markup = new Markup();
+        new TtsMock(markup);
+    }
+
+    public void testGetType() {
+        TtsMock t = new TtsMock();
+        t.setType("type1");
+        assertEquals("type1", t.getType());
+        t.setType(null);
+        assertEquals(null, t.getType());
+        t.setType("type2");
+        assertEquals("type2", t.getType());
+    }
+
+
+    public void testDefaultGender() {
+        assertEquals(Utterance.GENDER_UNKNOWN, new TtsMock().getGender());
+    }
+
+    public void testSetGender() {
+        assertEquals(Utterance.GENDER_MALE,
+                     new TtsMock().setGender(Utterance.GENDER_MALE).getGender());
+    }
+
+    public void testSetGenderNegative() {
+        try {
+            new TtsMock().setGender(-1);
+            fail("Expected IllegalArgumentException");
+        } catch (IllegalArgumentException e) {}
+    }
+
+    public void testSetGenderOutOfBounds() {
+        try {
+            new TtsMock().setGender(4);
+            fail("Expected IllegalArgumentException");
+        } catch (IllegalArgumentException e) {}
+    }
+
+    public void testDefaultAnimacy() {
+        assertEquals(Utterance.ANIMACY_UNKNOWN, new TtsMock().getAnimacy());
+    }
+
+    public void testSetAnimacy() {
+        assertEquals(Utterance.ANIMACY_ANIMATE,
+                     new TtsMock().setAnimacy(Utterance.ANIMACY_ANIMATE).getAnimacy());
+    }
+
+    public void testSetAnimacyNegative() {
+        try {
+            new TtsMock().setAnimacy(-1);
+            fail("Expected IllegalArgumentException");
+        } catch (IllegalArgumentException e) {}
+    }
+
+    public void testSetAnimacyOutOfBounds() {
+        try {
+            new TtsMock().setAnimacy(4);
+            fail("Expected IllegalArgumentException");
+        } catch (IllegalArgumentException e) {}
+    }
+
+    public void testDefaultMultiplicity() {
+        assertEquals(Utterance.MULTIPLICITY_UNKNOWN, new TtsMock().getMultiplicity());
+    }
+
+    public void testSetMultiplicity() {
+        assertEquals(Utterance.MULTIPLICITY_DUAL,
+                     new TtsMock().setMultiplicity(Utterance.MULTIPLICITY_DUAL).getMultiplicity());
+    }
+
+    public void testSetMultiplicityNegative() {
+        try {
+            new TtsMock().setMultiplicity(-1);
+            fail("Expected IllegalArgumentException");
+        } catch (IllegalArgumentException e) {}
+    }
+
+    public void testSetMultiplicityOutOfBounds() {
+        try {
+            new TtsMock().setMultiplicity(4);
+            fail("Expected IllegalArgumentException");
+        } catch (IllegalArgumentException e) {}
+    }
+
+    public void testDefaultCase() {
+        assertEquals(Utterance.CASE_UNKNOWN, new TtsMock().getCase());
+    }
+
+    public void testSetCase() {
+        assertEquals(Utterance.CASE_VOCATIVE,
+                     new TtsMock().setCase(Utterance.CASE_VOCATIVE).getCase());
+    }
+
+    public void testSetCaseNegative() {
+        try {
+            new TtsMock().setCase(-1);
+            fail("Expected IllegalArgumentException");
+        } catch (IllegalArgumentException e) {}
+    }
+
+    public void testSetCaseOutOfBounds() {
+        try {
+            new TtsMock().setCase(9);
+            fail("Expected IllegalArgumentException");
+        } catch (IllegalArgumentException e) {}
+    }
+
+    public void testToString() {
+        TtsMock t = new TtsMock()
+            .setAnimacy(Utterance.ANIMACY_INANIMATE)
+            .setCase(Utterance.CASE_INSTRUMENTAL)
+            .setGender(Utterance.GENDER_FEMALE)
+            .setMultiplicity(Utterance.MULTIPLICITY_PLURAL);
+        String str =
+            "animacy: \"2\" " +
+            "case: \"8\" " +
+            "gender: \"3\" " +
+            "multiplicity: \"3\"";
+        assertEquals(str, t.toString());
+    }
+
+    public void testToStringSetToUnkown() {
+        TtsMock t = new TtsMock()
+            .setAnimacy(Utterance.ANIMACY_INANIMATE)
+            .setCase(Utterance.CASE_INSTRUMENTAL)
+            .setGender(Utterance.GENDER_FEMALE)
+            .setMultiplicity(Utterance.MULTIPLICITY_PLURAL)
+        // set back to unknown
+            .setAnimacy(Utterance.ANIMACY_UNKNOWN)
+            .setCase(Utterance.CASE_UNKNOWN)
+            .setGender(Utterance.GENDER_UNKNOWN)
+            .setMultiplicity(Utterance.MULTIPLICITY_UNKNOWN);
+        String str = "";
+        assertEquals(str, t.toString());
+    }
+
+}
diff --git a/tests/TtsTests/src/com/android/speech/tts/AbstractTtsTest.java b/tests/TtsTests/src/com/android/speech/tts/AbstractTtsTest.java
new file mode 100644
index 0000000..281c97f
--- /dev/null
+++ b/tests/TtsTests/src/com/android/speech/tts/AbstractTtsTest.java
@@ -0,0 +1,106 @@
+/*
+ * 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.speech.tts;
+
+import android.test.InstrumentationTestCase;
+
+import android.speech.tts.Markup;
+import android.speech.tts.Utterance.AbstractTts;
+
+public class AbstractTtsTest extends InstrumentationTestCase {
+
+    public static class TtsMock extends AbstractTts<TtsMock> {
+        public TtsMock() {
+            super();
+        }
+
+        public TtsMock(Markup markup) {
+            super();
+        }
+
+        public void setType(String type) {
+            mMarkup.setType(type);
+        }
+
+        @Override
+        public TtsMock setParameter(String key, String value) {
+           return super.setParameter(key, value);
+        }
+
+        @Override
+        public TtsMock removeParameter(String key) {
+           return super.removeParameter(key);
+        }
+    }
+
+    public void testDefaultConstructor() {
+        new TtsMock();
+    }
+
+    public void testMarkupConstructor() {
+        Markup markup = new Markup();
+        new TtsMock(markup);
+    }
+
+    public void testGetType() {
+        TtsMock t = new TtsMock();
+        t.setType("type1");
+        assertEquals("type1", t.getType());
+        t.setType(null);
+        assertEquals(null, t.getType());
+        t.setType("type2");
+        assertEquals("type2", t.getType());
+    }
+
+    public void testGeneratePlainText() {
+        assertNull(new TtsMock().generatePlainText());
+    }
+
+    public void testToString() {
+        TtsMock t = new TtsMock();
+        t.setType("a_type");
+        t.setPlainText("a plaintext");
+        t.setParameter("key1", "value1");
+        t.setParameter("aaa", "value2");
+        String str =
+            "type: \"a_type\" " +
+            "plain_text: \"a plaintext\" " +
+            "aaa: \"value2\" " +
+            "key1: \"value1\"";
+        assertEquals(str, t.toString());
+    }
+
+    public void testRemoveParameter() {
+        TtsMock t = new TtsMock();
+        t.setParameter("key1", "value 1");
+        t.setParameter("aaa", "value a");
+        t.removeParameter("key1");
+        String str =
+            "aaa: \"value a\"";
+        assertEquals(str, t.toString());
+    }
+
+    public void testRemoveParameterBySettingNull() {
+        TtsMock t = new TtsMock();
+        t.setParameter("key1", "value 1");
+        t.setParameter("aaa", "value a");
+        t.setParameter("aaa", null);
+        String str =
+            "key1: \"value 1\"";
+        assertEquals(str, t.toString());
+    }
+}
diff --git a/tests/TtsTests/src/com/android/speech/tts/MarkupTest.java b/tests/TtsTests/src/com/android/speech/tts/MarkupTest.java
new file mode 100644
index 0000000..7ef93ce
--- /dev/null
+++ b/tests/TtsTests/src/com/android/speech/tts/MarkupTest.java
@@ -0,0 +1,510 @@
+/*
+ * 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.speech.tts;
+
+import junit.framework.Assert;
+import android.os.Parcel;
+import android.test.InstrumentationTestCase;
+
+import android.speech.tts.Markup;
+
+public class MarkupTest extends InstrumentationTestCase {
+
+  public void testEmptyMarkup() {
+      Markup markup = new Markup();
+      assertNull(markup.getType());
+      assertNull(markup.getPlainText());
+      assertEquals(0, markup.parametersSize());
+      assertEquals(0, markup.nestedMarkupSize());
+  }
+
+  public void testGetSetType() {
+      Markup markup = new Markup();
+      markup.setType("one");
+      assertEquals("one", markup.getType());
+      markup.setType(null);
+      assertNull(markup.getType());
+      markup.setType("two");
+      assertEquals("two", markup.getType());
+  }
+
+  public void testGetSetPlainText() {
+      Markup markup = new Markup();
+      markup.setPlainText("one");
+      assertEquals("one", markup.getPlainText());
+      markup.setPlainText(null);
+      assertNull(markup.getPlainText());
+      markup.setPlainText("two");
+      assertEquals("two", markup.getPlainText());
+  }
+
+  public void testParametersSize1() {
+      Markup markup = new Markup();
+      markup.addNestedMarkup(new Markup());
+      assertEquals(1, markup.nestedMarkupSize());
+  }
+
+  public void testParametersSize2() {
+      Markup markup = new Markup();
+      markup.addNestedMarkup(new Markup());
+      markup.addNestedMarkup(new Markup());
+      assertEquals(2, markup.nestedMarkupSize());
+  }
+
+  public void testRemoveParameter() {
+      Markup m = new Markup("type");
+      m.setParameter("key1", "value1");
+      m.setParameter("key2", "value2");
+      m.setParameter("key3", "value3");
+      assertEquals(3, m.parametersSize());
+      m.removeParameter("key1");
+      assertEquals(2, m.parametersSize());
+      m.removeParameter("key3");
+      assertEquals(1, m.parametersSize());
+      assertNull(m.getParameter("key1"));
+      assertEquals("value2", m.getParameter("key2"));
+      assertNull(m.getParameter("key3"));
+  }
+
+  public void testEmptyEqual() {
+      Markup m1 = new Markup();
+      Markup m2 = new Markup();
+      assertTrue(m1.equals(m2));
+  }
+
+  public void testFilledEqual() {
+      Markup m1 = new Markup();
+      m1.setType("type");
+      m1.setPlainText("plain text");
+      m1.setParameter("key1", "value1");
+      m1.addNestedMarkup(new Markup());
+      Markup m2 = new Markup();
+      m2.setType("type");
+      m2.setPlainText("plain text");
+      m2.setParameter("key1", "value1");
+      m2.addNestedMarkup(new Markup());
+      assertTrue(m1.equals(m2));
+  }
+
+  public void testDifferentTypeEqual() {
+      Markup m1 = new Markup();
+      m1.setType("type1");
+      Markup m2 = new Markup();
+      m2.setType("type2");
+      assertFalse(m1.equals(m2));
+  }
+
+  public void testDifferentPlainTextEqual() {
+      Markup m1 = new Markup();
+      m1.setPlainText("plainText1");
+      Markup m2 = new Markup();
+      m2.setPlainText("plainText2");
+      assertFalse(m1.equals(m2));
+  }
+
+  public void testDifferentParamEqual() {
+      Markup m1 = new Markup();
+      m1.setParameter("test", "value1");
+      Markup m2 = new Markup();
+      m2.setParameter("test", "value2");
+      assertFalse(m1.equals(m2));
+  }
+
+  public void testDifferentParameterKeyEqual() {
+      Markup m1 = new Markup();
+      m1.setParameter("test1", "value");
+      Markup m2 = new Markup();
+      m2.setParameter("test2", "value");
+      assertFalse(m1.equals(m2));
+  }
+
+  public void testDifferentParameterValueEqual() {
+      Markup m1 = new Markup();
+      m1.setParameter("test", "value1");
+      Markup m2 = new Markup();
+      m2.setParameter("test", "value2");
+      assertFalse(m1.equals(m2));
+  }
+
+  public void testDifferentNestedMarkupEqual() {
+      Markup m1 = new Markup();
+      Markup nested = new Markup();
+      nested.setParameter("key", "value");
+      m1.addNestedMarkup(nested);
+      Markup m2 = new Markup();
+      m2.addNestedMarkup(new Markup());
+      assertFalse(m1.equals(m2));
+  }
+
+  public void testEmptyToFromString() {
+      Markup m1 = new Markup();
+      String str = m1.toString();
+      assertEquals("", str);
+
+      Markup m2 = Markup.markupFromString(str);
+      assertEquals(m1, m2);
+  }
+
+  public void testTypeToFromString() {
+      Markup m1 = new Markup("atype");
+      String str = m1.toString();
+      assertEquals("type: \"atype\"", str);
+      Markup m2 = Markup.markupFromString(str);
+      assertEquals(m1, m2);
+  }
+
+  public void testPlainTextToFromString() {
+      Markup m1 = new Markup();
+      m1.setPlainText("some_plainText");
+      String str = m1.toString();
+      assertEquals("plain_text: \"some_plainText\"", str);
+
+      Markup m2 = Markup.markupFromString(str);
+      assertEquals(m1, m2);
+  }
+
+  public void testParameterToFromString() {
+      Markup m1 = new Markup("cardinal");
+      m1.setParameter("integer", "-22");
+      String str = m1.toString();
+      assertEquals("type: \"cardinal\" integer: \"-22\"", str);
+      Markup m2 = Markup.markupFromString(str);
+      assertEquals(m1, m2);
+  }
+
+  // Parameters should be ordered alphabettically, so the output is stable.
+  public void testParameterOrderToFromString() {
+      Markup m1 = new Markup("cardinal");
+      m1.setParameter("ccc", "-");
+      m1.setParameter("aaa", "-");
+      m1.setParameter("aa", "-");
+      m1.setParameter("bbb", "-");
+      String str = m1.toString();
+      assertEquals(
+              "type: \"cardinal\" " +
+              "aa: \"-\" " +
+              "aaa: \"-\" " +
+              "bbb: \"-\" " +
+              "ccc: \"-\"",
+              str);
+      Markup m2 = Markup.markupFromString(str);
+      assertEquals(m1, m2);
+  }
+
+  public void testEmptyNestedToFromString() {
+      Markup m1 = new Markup("atype");
+      m1.addNestedMarkup(new Markup());
+      String str = m1.toString();
+      assertEquals("type: \"atype\" markup {}", str);
+      Markup m2 = Markup.markupFromString(str);
+      assertEquals(m1, m2);
+  }
+
+  public void testNestedWithTypeToFromString() {
+      Markup m1 = new Markup("atype");
+      m1.addNestedMarkup(new Markup("nested_type"));
+      String str = m1.toString();
+      assertEquals(
+              "type: \"atype\" " +
+              "markup { type: \"nested_type\" }",
+              str);
+      Markup m2 = Markup.markupFromString(str);
+      assertEquals(m1, m2);
+  }
+
+  public void testRemoveNestedMarkup() {
+      Markup m = new Markup("atype");
+      Markup m1 = new Markup("nested_type1");
+      Markup m2 = new Markup("nested_type2");
+      Markup m3 = new Markup("nested_type3");
+      m.addNestedMarkup(m1);
+      m.addNestedMarkup(m2);
+      m.addNestedMarkup(m3);
+      m.removeNestedMarkup(m1);
+      m.removeNestedMarkup(m3);
+      String str = m.toString();
+      assertEquals(
+              "type: \"atype\" " +
+              "markup { type: \"nested_type2\" }",
+              str);
+      Markup mFromString = Markup.markupFromString(str);
+      assertEquals(m, mFromString);
+  }
+
+  public void testLotsofNestingToFromString() {
+      Markup m1 = new Markup("top")
+          .addNestedMarkup(new Markup("top_child1")
+              .addNestedMarkup(new Markup("top_child1_child1"))
+              .addNestedMarkup(new Markup("top_child1_child2")))
+          .addNestedMarkup(new Markup("top_child2")
+              .addNestedMarkup(new Markup("top_child2_child2"))
+              .addNestedMarkup(new Markup("top_child2_child2")));
+
+      String str = m1.toString();
+      assertEquals(
+              "type: \"top\" " +
+              "markup { " +
+                  "type: \"top_child1\" " +
+                  "markup { type: \"top_child1_child1\" } " +
+                  "markup { type: \"top_child1_child2\" } " +
+              "} " +
+              "markup { " +
+                  "type: \"top_child2\" " +
+                  "markup { type: \"top_child2_child2\" } " +
+                  "markup { type: \"top_child2_child2\" } " +
+              "}",
+              str);
+      Markup m2 = Markup.markupFromString(str);
+      assertEquals(m1, m2);
+  }
+
+  public void testFilledToFromString() {
+      Markup m1 = new Markup("measure");
+      m1.setPlainText("fifty-five amps");
+      m1.setParameter("unit", "meter");
+      m1.addNestedMarkup(new Markup("cardinal").setParameter("integer", "55"));
+      String str = m1.toString();
+      assertEquals(
+              "type: \"measure\" " +
+              "plain_text: \"fifty-five amps\" " +
+              "unit: \"meter\" " +
+              "markup { type: \"cardinal\" integer: \"55\" }",
+              str);
+
+      Markup m2 = Markup.markupFromString(str);
+      assertEquals(m1, m2);
+  }
+
+  public void testErrorFromString() {
+      String str = "type: \"atype\" markup {mistake}";
+      try {
+          Markup.markupFromString(str);
+          Assert.fail("Expected IllegalArgumentException");
+      } catch (IllegalArgumentException e) {}
+  }
+
+  public void testEscapeQuotes() {
+      Markup m1 = new Markup("text")
+              .setParameter("something_unknown", "\"this\" is \"a sentence \" with quotes\"");
+      String str = m1.toString();
+      assertEquals(
+              "type: \"text\" " +
+              "something_unknown: \"\\\"this\\\" is \\\"a sentence \\\" with quotes\\\"\"",
+              str);
+
+      Markup m2 = Markup.markupFromString(str);
+      assertEquals(m1.toString(), m2.toString());
+      assertEquals(m1, m2);
+  }
+
+  public void testEscapeSlashes1() {
+      Markup m1 = new Markup("text")
+              .setParameter("something_unknown", "\\ \\\\ \t \n \"");
+      String str = m1.toString();
+      assertEquals(
+              "type: \"text\" " +
+              "something_unknown: \"\\\\ \\\\\\\\ \t \n \\\"\"",
+              str);
+
+      Markup m2 = Markup.markupFromString(str);
+      assertEquals(m1.toString(), m2.toString());
+      assertEquals(m1, m2);
+  }
+
+  public void testEscapeSlashes2() {
+      Markup m1 = new Markup("text")
+              .setParameter("something_unknown", "\\\"\\\"\\\\\"\"\\\\\\\"\"\"");
+      String str = m1.toString();
+      assertEquals(
+              "type: \"text\" " +
+              "something_unknown: \"\\\\\\\"\\\\\\\"\\\\\\\\\\\"\\\"\\\\\\\\\\\\\\\"\\\"\\\"\"",
+              str);
+
+      Markup m2 = Markup.markupFromString(str);
+      assertEquals(m1.toString(), m2.toString());
+      assertEquals(m1, m2);
+  }
+
+  public void testBadInput1() {
+      String str = "type: \"text\" text: \"\\\"";
+      try {
+          Markup.markupFromString(str);
+          fail("Expected IllegalArgumentException");
+      } catch (IllegalArgumentException e) {}
+  }
+
+  public void testBadInput2() {
+      String str = "type: \"text\" text: \"\\a\"";
+      try {
+          Markup.markupFromString(str);
+          fail("Expected IllegalArgumentException");
+      } catch (IllegalArgumentException e) {}
+  }
+
+  public void testValidParameterKey() {
+      Markup m = new Markup();
+      m.setParameter("ke9__yk_88ey_za7_", "test");
+  }
+
+  public void testInValidParameterKeyEmpty() {
+      Markup m = new Markup();
+      try {
+          m.setParameter("", "test");
+          fail("Expected IllegalArgumentException");
+      } catch (IllegalArgumentException e) {}
+  }
+
+  public void testInValidParameterKeyDollar() {
+      Markup m = new Markup();
+      try {
+          m.setParameter("ke9y$k88ey7", "test");
+          fail("Expected IllegalArgumentException");
+      } catch (IllegalArgumentException e) {}
+  }
+
+  public void testInValidParameterKeySpace() {
+      Markup m = new Markup();
+      try {
+          m.setParameter("ke9yk88ey7 ", "test");
+          fail("Expected IllegalArgumentException");
+      } catch (IllegalArgumentException e) {}
+  }
+
+  public void testValidType() {
+      new Markup("_this_is_1_valid_type_222");
+  }
+
+  public void testInValidTypeAmpersand() {
+      try {
+          new Markup("abcde1234&");
+          fail("Expected IllegalArgumentException");
+      } catch (IllegalArgumentException e) {}
+  }
+
+  public void testInValidTypeSpace() {
+      try {
+          new Markup(" ");
+          fail("Expected IllegalArgumentException");
+      } catch (IllegalArgumentException e) {}
+  }
+
+  public void testSimpleParcelable() {
+      Markup markup = new Markup();
+
+      Parcel parcel = Parcel.obtain();
+      markup.writeToParcel(parcel, 0);
+      parcel.setDataPosition(0);
+
+      Markup fromParcel = (Markup) Markup.CREATOR.createFromParcel(parcel);
+
+      assertFalse(markup == fromParcel);
+      assertEquals(markup, fromParcel);
+  }
+
+  public void testTypeParcelable() {
+      Markup markup = new Markup("text");
+
+      Parcel parcel = Parcel.obtain();
+      markup.writeToParcel(parcel, 0);
+      parcel.setDataPosition(0);
+
+      Markup fromParcel = (Markup) Markup.CREATOR.createFromParcel(parcel);
+
+      assertFalse(markup == fromParcel);
+      assertEquals(markup, fromParcel);
+  }
+
+  public void testPlainTextsParcelable() {
+      Markup markup = new Markup();
+      markup.setPlainText("plainText");
+
+      Parcel parcel = Parcel.obtain();
+      markup.writeToParcel(parcel, 0);
+      parcel.setDataPosition(0);
+
+      Markup fromParcel = (Markup) Markup.CREATOR.createFromParcel(parcel);
+
+      assertFalse(markup == fromParcel);
+      assertEquals(markup, fromParcel);
+  }
+
+  public void testParametersParcelable() {
+      Markup markup = new Markup();
+      markup.setParameter("key1", "value1");
+      markup.setParameter("key2", "value2");
+      markup.setParameter("key3", "value3");
+
+      Parcel parcel = Parcel.obtain();
+      markup.writeToParcel(parcel, 0);
+      parcel.setDataPosition(0);
+
+      Markup fromParcel = (Markup) Markup.CREATOR.createFromParcel(parcel);
+
+      assertFalse(markup == fromParcel);
+      assertEquals(markup, fromParcel);
+  }
+
+  public void testNestedParcelable() {
+      Markup markup = new Markup();
+      markup.addNestedMarkup(new Markup("first"));
+      markup.addNestedMarkup(new Markup("second"));
+      markup.addNestedMarkup(new Markup("third"));
+
+      Parcel parcel = Parcel.obtain();
+      markup.writeToParcel(parcel, 0);
+      parcel.setDataPosition(0);
+
+      Markup fromParcel = (Markup) Markup.CREATOR.createFromParcel(parcel);
+
+      assertFalse(markup == fromParcel);
+      assertEquals(markup, fromParcel);
+  }
+
+  public void testAllFieldsParcelable() {
+      Markup markup = new Markup("text");
+      markup.setPlainText("plain text");
+      markup.setParameter("key1", "value1");
+      markup.setParameter("key2", "value2");
+      markup.setParameter("key3", "value3");
+      markup.addNestedMarkup(new Markup("first"));
+      markup.addNestedMarkup(new Markup("second"));
+      markup.addNestedMarkup(new Markup("third"));
+
+      Parcel parcel = Parcel.obtain();
+      markup.writeToParcel(parcel, 0);
+      parcel.setDataPosition(0);
+
+      Markup fromParcel = (Markup) Markup.CREATOR.createFromParcel(parcel);
+
+      assertFalse(markup == fromParcel);
+      assertEquals(markup, fromParcel);
+  }
+
+  public void testKeyCannotBeType() {
+      try {
+          new Markup().setParameter("type", "vale");
+          fail("Expected IllegalArgumentException");
+      } catch (IllegalArgumentException e) {}
+  }
+
+  public void testKeyCannotBePlainText() {
+      try {
+          new Markup().setParameter("plain_text", "value");
+          fail("Expected IllegalArgumentException");
+      } catch (IllegalArgumentException e) {}
+  }
+}
diff --git a/tests/TtsTests/src/com/android/speech/tts/TtsCardinalTest.java b/tests/TtsTests/src/com/android/speech/tts/TtsCardinalTest.java
new file mode 100644
index 0000000..c34f4ac
--- /dev/null
+++ b/tests/TtsTests/src/com/android/speech/tts/TtsCardinalTest.java
@@ -0,0 +1,119 @@
+/*
+ * 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.speech.tts;
+
+import junit.framework.Assert;
+import android.test.InstrumentationTestCase;
+import android.test.MoreAsserts;
+
+import android.speech.tts.Markup;
+import android.speech.tts.Utterance;
+import android.speech.tts.Utterance.TtsCardinal;
+import android.speech.tts.Utterance.TtsText;
+
+public class TtsCardinalTest extends InstrumentationTestCase {
+
+    public void testConstruct() {
+        assertNotNull(new TtsCardinal(0));
+    }
+
+    public void testFluentAPI() {
+        new TtsCardinal()
+            .setPlainText("a plaintext") // from AbstractTts
+            .setGender(Utterance.GENDER_MALE) // from AbstractTtsSemioticClass
+            .setInteger("-10001"); // from TtsText
+    }
+
+    public void testZero() {
+        assertEquals("0", new TtsCardinal(0).getInteger());
+    }
+
+    public void testThirtyOne() {
+        assertEquals("31", new TtsCardinal(31).getInteger());
+    }
+
+    public void testMarkupZero() {
+        TtsCardinal c = new TtsCardinal(0);
+        Markup m = c.getMarkup();
+        assertEquals("0", m.getParameter("integer"));
+    }
+
+    public void testMarkupThirtyOne() {
+        TtsCardinal c = new TtsCardinal(31);
+        Markup m = c.getMarkup();
+        assertEquals("31", m.getParameter("integer"));
+    }
+
+    public void testMarkupThirtyOneString() {
+        TtsCardinal c = new TtsCardinal("31");
+        Markup m = c.getMarkup();
+        assertEquals("31", m.getParameter("integer"));
+    }
+
+    public void testMarkupNegativeThirtyOne() {
+        TtsCardinal c = new TtsCardinal(-31);
+        Markup m = c.getMarkup();
+        assertEquals("-31", m.getParameter("integer"));
+    }
+
+    public void testMarkupMinusZero() {
+        TtsCardinal c = new TtsCardinal("-0");
+        Markup m = c.getMarkup();
+        assertEquals("-0", m.getParameter("integer"));
+    }
+
+    public void testMarkupNegativeThirtyOneString() {
+        TtsCardinal c = new TtsCardinal("-31");
+        Markup m = c.getMarkup();
+        assertEquals("-31", m.getParameter("integer"));
+    }
+
+    public void testOnlyLetters() {
+        try {
+            new TtsCardinal("abc");
+            Assert.fail("Expected IllegalArgumentException");
+        } catch (IllegalArgumentException e) {}
+    }
+
+    public void testOnlyMinus() {
+        try {
+            new TtsCardinal("-");
+            Assert.fail("Expected IllegalArgumentException");
+        } catch (IllegalArgumentException e) {}
+    }
+
+    public void testNegativeLetters() {
+        try {
+            new TtsCardinal("-abc");
+            Assert.fail("Expected IllegalArgumentException");
+        } catch (IllegalArgumentException e) {}
+    }
+
+    public void testLetterNumberMix() {
+        try {
+            new TtsCardinal("-0a1b2c");
+            Assert.fail("Expected IllegalArgumentException");
+        } catch (IllegalArgumentException e) {}
+    }
+
+    public void letterNumberMix2() {
+        try {
+            new TtsCardinal("-a0b1c2");
+            Assert.fail("Expected IllegalArgumentException");
+        } catch (IllegalArgumentException e) {}
+    }
+}
diff --git a/tests/TtsTests/src/com/android/speech/tts/TtsTextTest.java b/tests/TtsTests/src/com/android/speech/tts/TtsTextTest.java
new file mode 100644
index 0000000..35fd453
--- /dev/null
+++ b/tests/TtsTests/src/com/android/speech/tts/TtsTextTest.java
@@ -0,0 +1,74 @@
+/*
+ * 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.speech.tts;
+
+import android.test.InstrumentationTestCase;
+
+import android.speech.tts.Markup;
+import android.speech.tts.Utterance;
+import android.speech.tts.Utterance.TtsText;
+
+public class TtsTextTest extends InstrumentationTestCase {
+
+    public void testConstruct() {
+        assertNotNull(new TtsText());
+    }
+
+    public void testFluentAPI() {
+        new TtsText()
+            .setPlainText("a plaintext") // from AbstractTts
+            .setGender(Utterance.GENDER_MALE) // from AbstractTtsSemioticClass
+            .setText("text"); // from TtsText
+    }
+
+    public void testConstructEmptyString() {
+        assertTrue(new TtsText("").getText().isEmpty());
+    }
+
+    public void testConstructString() {
+        assertEquals("this is a test.", new TtsText("this is a test.").getText());
+    }
+
+    public void testSetText() {
+        assertEquals("This is a test.", new TtsText().setText("This is a test.").getText());
+    }
+
+    public void testEmptyMarkup() {
+        TtsText t = new TtsText();
+        Markup m = t.getMarkup();
+        assertEquals("text", m.getType());
+        assertNull(m.getPlainText());
+        assertEquals(0, m.nestedMarkupSize());
+    }
+
+    public void testConstructStringMarkup() {
+        TtsText t = new TtsText("test");
+        Markup m = t.getMarkup();
+        assertEquals("text", m.getType());
+        assertEquals("test", m.getParameter("text"));
+        assertEquals(0, m.nestedMarkupSize());
+    }
+
+    public void testSetStringMarkup() {
+        TtsText t = new TtsText();
+        t.setText("test");
+        Markup m = t.getMarkup();
+        assertEquals("text", m.getType());
+        assertEquals("test", m.getParameter("text"));
+        assertEquals(0, m.nestedMarkupSize());
+    }
+}
diff --git a/tests/TtsTests/src/com/android/speech/tts/UtteranceTest.java b/tests/TtsTests/src/com/android/speech/tts/UtteranceTest.java
new file mode 100644
index 0000000..8014dd1
--- /dev/null
+++ b/tests/TtsTests/src/com/android/speech/tts/UtteranceTest.java
@@ -0,0 +1,248 @@
+/*
+ * 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.speech.tts;
+
+import android.speech.tts.Markup;
+import android.speech.tts.Utterance;
+import android.speech.tts.Utterance.TtsCardinal;
+import android.speech.tts.Utterance.TtsText;
+
+import android.test.InstrumentationTestCase;
+
+public class UtteranceTest extends InstrumentationTestCase {
+
+    public void testEmptyUtterance() {
+        Utterance utt = new Utterance();
+        assertEquals(0, utt.size());
+    }
+
+    public void testSizeCardinal() {
+        Utterance utt = new Utterance()
+                .append(new TtsCardinal(42));
+        assertEquals(1, utt.size());
+    }
+
+    public void testSizeCardinalString() {
+        Utterance utt = new Utterance()
+                .append(new TtsCardinal(42))
+                .append(new TtsText("is the answer"));
+        assertEquals(2, utt.size());
+    }
+
+    public void testMarkupEmpty() {
+        Markup m = new Utterance().createMarkup();
+        assertEquals("utterance", m.getType());
+        assertEquals("", m.getPlainText());
+    }
+
+    public void testMarkupCardinal() {
+        Utterance utt = new Utterance()
+                .append(new TtsCardinal(42));
+        Markup markup = utt.createMarkup();
+        assertEquals("utterance", markup.getType());
+        assertEquals("42", markup.getPlainText());
+        assertEquals("42", markup.getNestedMarkup(0).getParameter("integer"));
+        assertEquals("42", markup.getNestedMarkup(0).getPlainText());
+    }
+
+    public void testMarkupCardinalString() {
+        Utterance utt = new Utterance()
+                .append(new TtsCardinal(42))
+                .append(new TtsText("is not just a number."));
+        Markup markup = utt.createMarkup();
+        assertEquals("utterance", markup.getType());
+        assertEquals("42 is not just a number.", markup.getPlainText());
+        assertEquals("cardinal", markup.getNestedMarkup(0).getType());
+        assertEquals("42", markup.getNestedMarkup(0).getParameter("integer"));
+        assertEquals("42", markup.getNestedMarkup(0).getPlainText());
+        assertEquals("text", markup.getNestedMarkup(1).getType());
+        assertEquals("is not just a number.", markup.getNestedMarkup(1).getParameter("text"));
+        assertEquals("is not just a number.", markup.getNestedMarkup(1).getPlainText());
+    }
+
+    public void testTextCardinalToFromString() {
+        Utterance utt = new Utterance()
+                .append(new TtsCardinal(55))
+                .append(new TtsText("this is a text."));
+        String str = utt.toString();
+        assertEquals(
+            "type: \"utterance\" " +
+            "markup { " +
+                "type: \"cardinal\" " +
+                "integer: \"55\" " +
+            "} " +
+            "markup { " +
+                "type: \"text\" " +
+                "text: \"this is a text.\" " +
+            "}"
+            , str);
+
+        Utterance utt_new = Utterance.utteranceFromString(str);
+        assertEquals(str, utt_new.toString());
+    }
+
+    public void testNotUtteranceFromString() {
+        String str =
+            "type: \"this_is_not_an_utterance\" " +
+            "markup { " +
+                "type: \"cardinal\" " +
+                "plain_text: \"55\" " +
+                "integer: \"55\" " +
+            "}";
+        try {
+            Utterance.utteranceFromString(str);
+            fail("Expected IllegalArgumentException");
+        } catch (IllegalArgumentException e) {}
+    }
+
+    public void testFromMarkup() {
+        String markup_str =
+            "type: \"utterance\" " +
+            "markup { " +
+                "type: \"cardinal\" " +
+                "plain_text: \"55\" " +
+                "integer: \"55\" " +
+            "} " +
+            "markup { " +
+                "type: \"text\" " +
+                "plain_text: \"this is a text.\" " +
+                "text: \"this is a text.\" " +
+            "}";
+        Utterance utt = Utterance.utteranceFromString(markup_str);
+        assertEquals(markup_str, utt.toString());
+    }
+
+    public void testsetPlainText() {
+        Utterance utt = new Utterance()
+            .append(new TtsCardinal(-100).setPlainText("minus one hundred"));
+        assertEquals("minus one hundred", utt.get(0).getPlainText());
+    }
+
+    public void testRemoveTextThroughSet() {
+        Utterance utt = new Utterance()
+            .append(new TtsText().setText("test").setText(null));
+        assertNull(((TtsText) utt.get(0)).getText());
+    }
+
+    public void testUnknownNodeWithPlainText() {
+        String str =
+            "type: \"utterance\" " +
+            "markup { " +
+                "type: \"some_future_feature\" " +
+                "plain_text: \"biep bob bob\" " +
+                "bombom: \"lorum ipsum\" " +
+            "}";
+        Utterance utt = Utterance.utteranceFromString(str);
+        assertNotNull(utt);
+        assertEquals("text", utt.get(0).getType());
+        assertEquals("biep bob bob", ((TtsText) utt.get(0)).getText());
+    }
+
+    public void testUnknownNodeWithNoPlainTexts() {
+        String str =
+            "type: \"utterance\" " +
+            "markup { " +
+                "type: \"some_future_feature\" " +
+                "bombom: \"lorum ipsum\" " +
+                "markup { type: \"cardinal\" integer: \"10\" } " +
+                "markup { type: \"text\" text: \"pears\" } " +
+            "}";
+        Utterance utt = Utterance.utteranceFromString(str);
+        assertEquals(
+            "type: \"utterance\" " +
+            "markup { type: \"cardinal\" integer: \"10\" } " +
+            "markup { type: \"text\" text: \"pears\" }", utt.toString());
+    }
+
+    public void testCreateWarningOnFallbackTrue() {
+        Utterance utt = new Utterance()
+          .append(new TtsText("test"))
+          .setNoWarningOnFallback(true);
+        assertEquals(
+            "type: \"utterance\" " +
+            "no_warning_on_fallback: \"true\" " +
+            "markup { " +
+                "type: \"text\" " +
+                "text: \"test\" " +
+            "}", utt.toString());
+    }
+
+    public void testCreateWarningOnFallbackFalse() {
+        Utterance utt = new Utterance()
+          .append(new TtsText("test"))
+          .setNoWarningOnFallback(false);
+        assertEquals(
+            "type: \"utterance\" " +
+            "no_warning_on_fallback: \"false\" " +
+            "markup { " +
+                "type: \"text\" " +
+                "text: \"test\" " +
+            "}", utt.toString());
+    }
+
+    public void testCreatePlainTexts() {
+        Utterance utt = new Utterance()
+            .append(new TtsText("test"))
+            .append(new TtsCardinal(-55));
+        assertEquals(
+            "type: \"utterance\" " +
+            "plain_text: \"test -55\" " +
+            "markup { type: \"text\" plain_text: \"test\" text: \"test\" } " +
+            "markup { type: \"cardinal\" plain_text: \"-55\" integer: \"-55\" }",
+            utt.createMarkup().toString()
+        );
+    }
+
+    public void testDontOverwritePlainTexts() {
+        Utterance utt = new Utterance()
+            .append(new TtsText("test").setPlainText("else"))
+            .append(new TtsCardinal(-55).setPlainText("44"));
+        assertEquals(
+            "type: \"utterance\" " +
+            "plain_text: \"else 44\" " +
+            "markup { type: \"text\" plain_text: \"else\" text: \"test\" } " +
+            "markup { type: \"cardinal\" plain_text: \"44\" integer: \"-55\" }",
+            utt.createMarkup().toString()
+        );
+    }
+
+    public void test99BottlesOnWallMarkup() {
+        Utterance utt = new Utterance()
+            .append("there are")
+            .append(99)
+            .append("bottles on the wall.");
+        assertEquals(
+                "type: \"utterance\" " +
+                "plain_text: \"there are 99 bottles on the wall.\" " +
+                "markup { type: \"text\" plain_text: \"there are\" text: \"there are\" } " +
+                "markup { type: \"cardinal\" plain_text: \"99\" integer: \"99\" } " +
+                "markup { type: \"text\" plain_text: \"bottles on the wall.\" text: \"bottles on the wall.\" }",
+                utt.createMarkup().toString());
+        assertEquals("99", utt.createMarkup().getNestedMarkup(1).getPlainText());
+        Markup markup = new Markup(utt.createMarkup());
+        assertEquals("99", markup.getNestedMarkup(1).getPlainText());
+    }
+
+    public void testWhat() {
+        Utterance utt = new Utterance()
+            .append("there are")
+            .append(99)
+            .append("bottles on the wall.");
+        Markup m = utt.createMarkup();
+        m.getNestedMarkup(1).getPlainText().equals("99");
+    }
+}
diff --git a/tests/VectorDrawableTest/AndroidManifest.xml b/tests/VectorDrawableTest/AndroidManifest.xml
index 28c5f33..113dce3 100644
--- a/tests/VectorDrawableTest/AndroidManifest.xml
+++ b/tests/VectorDrawableTest/AndroidManifest.xml
@@ -22,6 +22,25 @@
     <application
         android:hardwareAccelerated="true"
         android:label="vector" >
+
+         <activity
+            android:name="VectorDrawablePerformance"
+            android:label="Vector Performance" >
+            <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>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="com.android.test.dynamic.TEST" />
+            </intent-filter>
+        </activity>
         <activity
             android:name="VectorDrawableTest"
             android:label="Vector Icon" >
@@ -41,19 +60,7 @@
                 <category android:name="com.android.test.dynamic.TEST" />
             </intent-filter>
         </activity>
-
-         <activity
-            android:name="VectorDrawablePerformance"
-            android:label="Vector Performance" >
-            <intent-filter>
-                <action android:name="android.intent.action.MAIN" />
-
-                <category android:name="com.android.test.dynamic.TEST" />
-            </intent-filter>
-
-        </activity>
-
-         <activity
+        <activity
             android:name="VectorDrawableDupPerf"
             android:label="Vector Performance of clones" >
             <intent-filter>
diff --git a/tests/VectorDrawableTest/res/drawable/animation_drawable_vector.xml b/tests/VectorDrawableTest/res/drawable/animation_drawable_vector.xml
new file mode 100644
index 0000000..a588960
--- /dev/null
+++ b/tests/VectorDrawableTest/res/drawable/animation_drawable_vector.xml
@@ -0,0 +1,36 @@
+<!--
+ 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.
+-->
+<animation-list xmlns:android="http://schemas.android.com/apk/res/android"
+     android:id="@+id/animation_drawable_vector" android:oneshot="false">
+    <item android:drawable="@drawable/vector_drawable01" android:duration="300" />
+    <item android:drawable="@drawable/vector_drawable02" android:duration="300" />
+    <item android:drawable="@drawable/vector_drawable03" android:duration="300" />
+    <item android:drawable="@drawable/vector_drawable04" android:duration="300" />
+    <item android:drawable="@drawable/vector_drawable05" android:duration="300" />
+    <item android:drawable="@drawable/vector_drawable06" android:duration="300" />
+    <item android:drawable="@drawable/vector_drawable07" android:duration="300" />
+    <item android:drawable="@drawable/vector_drawable08" android:duration="300" />
+    <item android:drawable="@drawable/vector_drawable09" android:duration="300" />
+    <item android:drawable="@drawable/vector_drawable10" android:duration="300" />
+    <item android:drawable="@drawable/vector_drawable11" android:duration="300" />
+    <item android:drawable="@drawable/vector_drawable12" android:duration="300" />
+    <item android:drawable="@drawable/vector_drawable13" android:duration="300" />
+    <item android:drawable="@drawable/vector_drawable14" android:duration="300" />
+    <item android:drawable="@drawable/vector_drawable15" android:duration="300" />
+    <item android:drawable="@drawable/vector_drawable16" android:duration="300" />
+    <item android:drawable="@drawable/vector_drawable17" android:duration="300" />
+    <item android:drawable="@drawable/vector_drawable18" android:duration="300" />
+ </animation-list>
diff --git a/tests/VectorDrawableTest/res/drawable/vector_drawable01.xml b/tests/VectorDrawableTest/res/drawable/vector_drawable01.xml
index 118f258..66a9452 100644
--- a/tests/VectorDrawableTest/res/drawable/vector_drawable01.xml
+++ b/tests/VectorDrawableTest/res/drawable/vector_drawable01.xml
@@ -13,8 +13,7 @@
      See the License for the specific language governing permissions and
      limitations under the License.
 -->
-<vector xmlns:android="http://schemas.android.com/apk/res/android"
-    android:versionCode="1" >
+<vector xmlns:android="http://schemas.android.com/apk/res/android">
 
     <size
         android:height="48dp"
@@ -24,12 +23,13 @@
         android:viewportHeight="480"
         android:viewportWidth="480" />
 
-    <path
-        android:name="box1"
-        android:fill="?android:attr/colorControlActivated"
-        android:pathData="m20,200l100,90l180,-180l-35,-35l-145,145l-60,-60l-40,40z"
-        android:stroke="?android:attr/colorControlActivated"
-        android:strokeLineCap="round"
-        android:strokeLineJoin="round" />
-
-</vector>
\ No newline at end of file
+    <group>
+        <path
+            android:name="box1"
+            android:pathData="m20,200l100,90l180,-180l-35,-35l-145,145l-60,-60l-40,40z"
+            android:fill="?android:attr/colorControlActivated"
+            android:stroke="?android:attr/colorControlActivated"
+            android:strokeLineCap="round"
+            android:strokeLineJoin="round" />
+    </group>
+</vector>
diff --git a/tests/VectorDrawableTest/res/drawable/vector_drawable02.xml b/tests/VectorDrawableTest/res/drawable/vector_drawable02.xml
index 034f7a0..40f23f0 100644
--- a/tests/VectorDrawableTest/res/drawable/vector_drawable02.xml
+++ b/tests/VectorDrawableTest/res/drawable/vector_drawable02.xml
@@ -1,5 +1,4 @@
-<!--
- Copyright (C) 2014 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.
@@ -16,23 +15,22 @@
 <vector xmlns:android="http://schemas.android.com/apk/res/android" >
 
     <size
-        android:height="64dp"
-        android:width="64dp" />
+        android:width="64dp"
+        android:height="64dp"/>
 
-    <viewport
-        android:viewportHeight="320"
-        android:viewportWidth="320" />
-
-    <path
-        android:name="house"
-        android:fill="#ff440000"
-        android:pathData="M 130,225 L 130,115 L 130,115 L 70,15 L 10,115 L 10,115 L 10,225 z"
-        android:pivotX="70"
-        android:pivotY="120"
+    <viewport android:viewportWidth="320"
+          android:viewportHeight="320"/>
+    <group
         android:rotation="180"
-        android:stroke="#FF00FF00"
-        android:strokeWidth="10"
-        android:trimPathEnd=".9"
-        android:trimPathStart=".1" />
-
-</vector>
\ No newline at end of file
+        android:pivotX="70"
+        android:pivotY="120">
+        <path
+            android:name="house"
+            android:pathData="M 130,225 L 130,115 L 130,115 L 70,15 L 10,115 L 10,115 L 10,225 z"
+            android:fill="#ff440000"
+            android:stroke="#FF00FF00"
+            android:strokeWidth="10"
+            android:trimPathStart=".1"
+            android:trimPathEnd=".9"/>
+    </group>
+</vector>
diff --git a/tests/VectorDrawableTest/res/drawable/vector_drawable03.xml b/tests/VectorDrawableTest/res/drawable/vector_drawable03.xml
index 451b28e..5b4c4ab 100644
--- a/tests/VectorDrawableTest/res/drawable/vector_drawable03.xml
+++ b/tests/VectorDrawableTest/res/drawable/vector_drawable03.xml
@@ -23,40 +23,47 @@
         android:viewportHeight="12.25"
         android:viewportWidth="7.30625" />
 
-    <path
-        android:name="clip1"
-        android:clipToPath="true"
-        android:pathData="
-                M 0, 0
-                l 7.3, 0
-                l 0, 0
-                l -7.3, 0
-                z"
+    <group
         android:pivotX="3.65"
         android:pivotY="6.125"
-        android:rotation="-30" />
-    <path
-        android:name="one"
-        android:fill="#ff88ff"
-        android:pathData="M 1.215625,9.5l 1.9375,0.0 0.0,-6.671875 -2.109375,0.421875 0.0,-1.078125
-                l 2.09375,-0.421875 1.1874998,0.0 0.0,7.75 1.9375,0.0 0.0,1.0
-                l -5.046875,0.0 0.0,-1.0Z" />
-    <path
-        android:name="clip2"
-        android:clipToPath="true"
-        android:pathData="
-                M 0, 0
+        android:rotation="-30" >
+        <path
+            android:name="clip1"
+            android:clipToPath="true"
+            android:pathData="
+                M 0, 6.125
                 l 7.3, 0
                 l 0, 12.25
                 l -7.3, 0
-                z"
+                z" />
+    </group>
+    <group>
+        <path
+            android:name="one"
+            android:fill="#ff88ff"
+            android:pathData="M 1.215625,9.5l 1.9375,0.0 0.0,-6.671875 -2.109375,0.421875 0.0,-1.078125
+                l 2.09375,-0.421875 1.1874998,0.0 0.0,7.75 1.9375,0.0 0.0,1.0
+                l -5.046875,0.0 0.0,-1.0Z" />
+    </group>
+    <group
         android:pivotX="3.65"
         android:pivotY="6.125"
-        android:rotation="-30" />
-    <path
-        android:name="two"
-        android:fill="#ff88ff"
-        android:pathData="M 2.534375,9.6875l 4.140625,0.0 0.0,1.0 -5.5625,0.0 0.0,-1.0q 0.671875,-0.6875 1.828125,-1.859375
+        android:rotation="-30" >
+        <path
+            android:name="clip2"
+            android:clipToPath="true"
+            android:pathData="
+                M 0, 0
+                l 7.3, 0
+                l 0, 6.125
+                l -7.3, 0
+                z" />
+    </group>
+    <group>
+        <path
+            android:name="two"
+            android:fill="#ff88ff"
+            android:pathData="M 2.534375,9.6875l 4.140625,0.0 0.0,1.0 -5.5625,0.0 0.0,-1.0q 0.671875,-0.6875 1.828125,-1.859375
                         q 1.1718752,-1.1875 1.4687502,-1.53125 0.578125,-0.625 0.796875,-1.0625
                         q 0.234375,-0.453125 0.234375,-0.875 0.0,-0.703125 -0.5,-1.140625
                         q -0.484375,-0.4375 -1.2656252,-0.4375 -0.5625,0.0 -1.1875,0.1875
@@ -65,5 +72,6 @@
                         q 0.8125,0.671875 0.8125,1.8125 0.0,0.53125 -0.203125,1.015625
                         q -0.203125,0.484375 -0.734375,1.140625 -0.15625,0.171875 -0.9375,0.984375
                         q -0.78125024,0.8125 -2.2187502,2.265625Z" />
+    </group>
 
 </vector>
\ No newline at end of file
diff --git a/tests/VectorDrawableTest/res/drawable/vector_drawable04.xml b/tests/VectorDrawableTest/res/drawable/vector_drawable04.xml
index 6f9caa8..90694fb 100644
--- a/tests/VectorDrawableTest/res/drawable/vector_drawable04.xml
+++ b/tests/VectorDrawableTest/res/drawable/vector_drawable04.xml
@@ -1,5 +1,4 @@
-<!--
- Copyright (C) 2014 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.
@@ -13,44 +12,48 @@
      See the License for the specific language governing permissions and
      limitations under the License.
 -->
-<vector xmlns:android="http://schemas.android.com/apk/res/android" >
+<vector xmlns:android="http://schemas.android.com/apk/res/android">
 
     <size
-        android:height="64dp"
-        android:width="64dp" />
+            android:width="64dp"
+            android:height="64dp"/>
 
     <viewport
-        android:viewportHeight="12.25"
-        android:viewportWidth="7.30625" />
+            android:viewportWidth="7.30625"
+            android:viewportHeight="12.25"/>
 
-    <path
-        android:name="clip1"
-        android:clipToPath="true"
-        android:fill="#112233"
-        android:pathData="
+    <group>
+        <path
+                android:name="clip1"
+                android:pathData="
                 M 3.65, 6.125
                 m -.001, 0
                 a .001,.001 0 1,0 .002,0
-                a .001,.001 0 1,0 -.002,0z" />
-    <path
-        android:name="one"
-        android:fill="#ff88ff"
-        android:pathData="M 1.215625,9.5l 1.9375,0.0 0.0,-6.671875 -2.109375,0.421875 0.0,-1.078125
+                a .001,.001 0 1,0 -.002,0z"
+                android:clipToPath="true"
+                android:fill="#112233"
+                />
+
+        <path
+                android:name="one"
+                android:pathData="M 1.215625,9.5l 1.9375,0.0 0.0,-6.671875 -2.109375,0.421875 0.0,-1.078125
                 l 2.09375,-0.421875 1.1874998,0.0 0.0,7.75 1.9375,0.0 0.0,1.0
-                l -5.046875,0.0 0.0,-1.0Z" />
-    <path
-        android:name="clip2"
-        android:clipToPath="true"
-        android:fill="#112233"
-        android:pathData="
+                l -5.046875,0.0 0.0,-1.0Z"
+                android:fill="#ff88ff"
+                />
+        <path
+                android:name="clip2"
+                android:pathData="
                 M 3.65, 6.125
                 m -6, 0
                 a 6,6 0 1,0 12,0
-                a 6,6 0 1,0 -12,0z" />
-    <path
-        android:name="two"
-        android:fill="#ff88ff"
-        android:pathData="M 2.534375,9.6875l 4.140625,0.0 0.0,1.0 -5.5625,0.0 0.0,-1.0q 0.671875,-0.6875 1.828125,-1.859375
+                a 6,6 0 1,0 -12,0z"
+                android:clipToPath="true"
+                android:fill="#112233"
+                />
+        <path
+                android:name="two"
+                android:pathData="M 2.534375,9.6875l 4.140625,0.0 0.0,1.0 -5.5625,0.0 0.0,-1.0q 0.671875,-0.6875 1.828125,-1.859375
                         q 1.1718752,-1.1875 1.4687502,-1.53125 0.578125,-0.625 0.796875,-1.0625
                         q 0.234375,-0.453125 0.234375,-0.875 0.0,-0.703125 -0.5,-1.140625
                         q -0.484375,-0.4375 -1.2656252,-0.4375 -0.5625,0.0 -1.1875,0.1875
@@ -58,6 +61,8 @@
                         q 0.625,-0.15625 1.140625,-0.15625 1.3593752,0.0 2.1718752,0.6875
                         q 0.8125,0.671875 0.8125,1.8125 0.0,0.53125 -0.203125,1.015625
                         q -0.203125,0.484375 -0.734375,1.140625 -0.15625,0.171875 -0.9375,0.984375
-                        q -0.78125024,0.8125 -2.2187502,2.265625Z" />
-
-</vector>
\ No newline at end of file
+                        q -0.78125024,0.8125 -2.2187502,2.265625Z"
+                android:fill="#ff88ff"
+                />
+    </group>
+</vector>
diff --git a/tests/VectorDrawableTest/res/drawable/vector_drawable05.xml b/tests/VectorDrawableTest/res/drawable/vector_drawable05.xml
index e6c2557..c6595fa 100644
--- a/tests/VectorDrawableTest/res/drawable/vector_drawable05.xml
+++ b/tests/VectorDrawableTest/res/drawable/vector_drawable05.xml
@@ -23,17 +23,18 @@
         android:viewportHeight="12.25"
         android:viewportWidth="7.30625" />
 
-    <path
-        android:name="one"
-        android:fill="#ffff00"
-        android:pathData="M 1.215625,9.5l 1.9375,0.0 0.0,-6.671875 -2.109375,0.421875 0.0,-1.078125
+    <group>
+        <path
+            android:name="one"
+            android:fill="#ffff00"
+            android:pathData="M 1.215625,9.5l 1.9375,0.0 0.0,-6.671875 -2.109375,0.421875 0.0,-1.078125
                 l 2.09375,-0.421875 1.1874998,0.0 0.0,7.75 1.9375,0.0 0.0,1.0
                 l -5.046875,0.0 0.0,-1.0Z" />
-    <path
-        android:name="two"
-        android:fill="#ffff00"
-        android:fillOpacity="0"
-        android:pathData="M 2.534375,9.6875l 4.140625,0.0 0.0,1.0 -5.5625,0.0 0.0,-1.0q 0.671875,-0.6875 1.828125,-1.859375
+        <path
+            android:name="two"
+            android:fill="#ffff00"
+            android:fillOpacity="0"
+            android:pathData="M 2.534375,9.6875l 4.140625,0.0 0.0,1.0 -5.5625,0.0 0.0,-1.0q 0.671875,-0.6875 1.828125,-1.859375
                         q 1.1718752,-1.1875 1.4687502,-1.53125 0.578125,-0.625 0.796875,-1.0625
                         q 0.234375,-0.453125 0.234375,-0.875 0.0,-0.703125 -0.5,-1.140625
                         q -0.484375,-0.4375 -1.2656252,-0.4375 -0.5625,0.0 -1.1875,0.1875
@@ -42,5 +43,5 @@
                         q 0.8125,0.671875 0.8125,1.8125 0.0,0.53125 -0.203125,1.015625
                         q -0.203125,0.484375 -0.734375,1.140625 -0.15625,0.171875 -0.9375,0.984375
                         q -0.78125024,0.8125 -2.2187502,2.265625Z" />
-
+    </group>
 </vector>
\ No newline at end of file
diff --git a/tests/VectorDrawableTest/res/drawable/vector_drawable06.xml b/tests/VectorDrawableTest/res/drawable/vector_drawable06.xml
index 3f8cc09..ab5f7f4 100644
--- a/tests/VectorDrawableTest/res/drawable/vector_drawable06.xml
+++ b/tests/VectorDrawableTest/res/drawable/vector_drawable06.xml
@@ -1,5 +1,4 @@
-<!--
- Copyright (C) 2014 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.
@@ -16,38 +15,38 @@
 <vector xmlns:android="http://schemas.android.com/apk/res/android" >
 
     <size
-        android:height="64dp"
-        android:width="64dp" />
+            android:width="64dp"
+            android:height="64dp"/>
 
     <viewport
-        android:viewportHeight="700"
-        android:viewportWidth="700" />
+            android:viewportWidth="700"
+            android:viewportHeight="700"/>
 
-    <path
-        android:name="path2451"
-        android:pathData="M 569.374 461.472L 569.374 160.658L 160.658 160.658L 160.658 461.472L 569.374 461.472z"
-        android:stroke="#FF000000"
-        android:strokeWidth="30.65500000000000" />
-    <path
-        android:name="path2453"
-        android:pathData="M 365.015 311.066"
-        android:stroke="#FF000000"
-        android:strokeWidth="30.655000000000001" />
-    <path
-        android:name="path2455"
-        android:fill="#FFFFFFFF"
-        android:pathData="M 164.46 164.49L 340.78 343.158C 353.849 356.328 377.63 356.172 390.423 343.278L 566.622 165.928"
-        android:stroke="#FF000000"
-        android:strokeWidth="30.655000000000001" />
-    <path
-        android:name="path2457"
-        android:pathData="M 170.515 451.566L 305.61 313.46"
-        android:stroke="#000000"
-        android:strokeWidth="30.655000000000001" />
-    <path
-        android:name="path2459"
-        android:pathData="M 557.968 449.974L 426.515 315.375"
-        android:stroke="#000000"
-        android:strokeWidth="30.655000000000001" />
-
-</vector>
\ No newline at end of file
+    <group>
+        <path android:pathData="M 569.374 461.472L 569.374 160.658L 160.658 160.658L 160.658 461.472L 569.374 461.472z"
+              android:name="path2451"
+              android:fill="#00000000"
+              android:stroke="#FF000000"
+              android:strokeWidth="30.65500000000000"/>
+        <path android:pathData="M 365.015 311.066"
+              android:name="path2453"
+              android:fill="#00000000"
+              android:stroke="#FF000000"
+              android:strokeWidth="30.655000000000001"/>
+        <path android:pathData="M 164.46 164.49L 340.78 343.158C 353.849 356.328 377.63 356.172 390.423 343.278L 566.622 165.928"
+              android:name="path2455"
+              android:stroke="#FF000000"
+              android:fill="#FFFFFFFF"
+              android:strokeWidth="30.655000000000001"/>
+        <path android:pathData="M 170.515 451.566L 305.61 313.46"
+              android:name="path2457"
+              android:fill="#00000000"
+              android:stroke="#000000"
+              android:strokeWidth="30.655000000000001"/>
+        <path android:pathData="M 557.968 449.974L 426.515 315.375"
+              android:name="path2459"
+              android:fill="#00000000"
+              android:stroke="#000000"
+              android:strokeWidth="30.655000000000001"/>
+    </group>
+</vector>
diff --git a/tests/VectorDrawableTest/res/drawable/vector_drawable07.xml b/tests/VectorDrawableTest/res/drawable/vector_drawable07.xml
index 4db5090..7c7e679 100644
--- a/tests/VectorDrawableTest/res/drawable/vector_drawable07.xml
+++ b/tests/VectorDrawableTest/res/drawable/vector_drawable07.xml
@@ -1,5 +1,4 @@
-<!--
- Copyright (C) 2014 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.
@@ -13,21 +12,21 @@
      See the License for the specific language governing permissions and
      limitations under the License.
 -->
-<vector xmlns:android="http://schemas.android.com/apk/res/android" >
-
+<vector xmlns:android="http://schemas.android.com/apk/res/android"   >
     <size
-        android:height="64dp"
-        android:width="64dp" />
+            android:width="64dp"
+            android:height="64dp"/>
 
-    <viewport
-        android:viewportHeight="110"
-        android:viewportWidth="140" />
+    <viewport android:viewportWidth="140"
+          android:viewportHeight="110"/>
 
-    <path
-        android:name="back"
-        android:fill="#ffffffff"
-        android:pathData="M 20,55 l 35.3,-35.3 7.07,7.07 -35.3,35.3 z
+    <group>
+        <path
+                android:name="back"
+                android:pathData="M 20,55 l 35.3,-35.3 7.07,7.07 -35.3,35.3 z
               M 27,50 l 97,0 0,10 -97,0 z
-              M 20,55 l 7.07,-7.07 35.3,35.3 -7.07,7.07 z" />
-
-</vector>
\ No newline at end of file
+              M 20,55 l 7.07,-7.07 35.3,35.3 -7.07,7.07 z"
+                android:fill="#ffffffff"
+                />
+    </group>
+</vector>
diff --git a/tests/VectorDrawableTest/res/drawable/vector_drawable08.xml b/tests/VectorDrawableTest/res/drawable/vector_drawable08.xml
index 44ef979..59f7459 100644
--- a/tests/VectorDrawableTest/res/drawable/vector_drawable08.xml
+++ b/tests/VectorDrawableTest/res/drawable/vector_drawable08.xml
@@ -1,5 +1,4 @@
-<!--
- Copyright (C) 2014 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.
@@ -16,18 +15,20 @@
 <vector xmlns:android="http://schemas.android.com/apk/res/android" >
 
     <size
-        android:height="64dp"
-        android:width="64dp" />
+            android:width="64dp"
+            android:height="64dp"/>
 
-    <viewport
-        android:viewportHeight="600"
-        android:viewportWidth="600" />
 
-    <path
-        android:name="pie1"
-        android:fill="#ffffcc00"
-        android:pathData="M535.441,412.339A280.868,280.868 0 1,1 536.186,161.733L284.493,286.29Z"
-        android:stroke="#FF00FF00"
-        android:strokeWidth="1" />
+    <viewport android:viewportWidth="600"
+          android:viewportHeight="600"/>
 
-</vector>
\ No newline at end of file
+    <group>
+        <path
+                android:name="pie1"
+                android:pathData="M535.441,412.339A280.868,280.868 0 1,1 536.186,161.733L284.493,286.29Z"
+                android:fill="#ffffcc00"
+                android:stroke="#FF00FF00"
+                android:strokeWidth="1"/>
+    </group>
+
+</vector>
diff --git a/tests/VectorDrawableTest/res/drawable/vector_drawable09.xml b/tests/VectorDrawableTest/res/drawable/vector_drawable09.xml
index 248a143..c93c85f 100644
--- a/tests/VectorDrawableTest/res/drawable/vector_drawable09.xml
+++ b/tests/VectorDrawableTest/res/drawable/vector_drawable09.xml
@@ -23,12 +23,14 @@
         android:viewportHeight="200"
         android:viewportWidth="200" />
 
-    <path
-        android:name="house"
-        android:fill="#ffffffff"
-        android:pathData="M 100,20 l 0,0 0,140 -80,0 z M 100,20 l 0,0 80,140 -80,0 z"
+    <group
         android:pivotX="100"
         android:pivotY="100"
-        android:rotation="90" />
+        android:rotation="90">
+        <path
+            android:name="house"
+            android:fill="#ffffffff"
+            android:pathData="M 100,20 l 0,0 0,140 -80,0 z M 100,20 l 0,0 80,140 -80,0 z"/>
+    </group>
 
 </vector>
\ No newline at end of file
diff --git a/tests/VectorDrawableTest/res/drawable/vector_drawable10.xml b/tests/VectorDrawableTest/res/drawable/vector_drawable10.xml
index 56c2972..8484e9e 100644
--- a/tests/VectorDrawableTest/res/drawable/vector_drawable10.xml
+++ b/tests/VectorDrawableTest/res/drawable/vector_drawable10.xml
@@ -21,24 +21,26 @@
         android:width="64dp" />
 
     <viewport
-        android:viewportHeight="200"
-        android:viewportWidth="200" />
+        android:viewportWidth="200"
+        android:viewportHeight="200"/>
 
-    <path
-        android:name="bar3"
-        android:fill="#FFFFFFFF"
-        android:pathData="M49.001,60c-5.466,0 -9.899,4.478 -9.899,10s4.434,10,9.899,10c5.468,0,9.899 -4.478,9.899 -10S54.469,60,49.001,60z" />
-    <path
-        android:name="bar2"
-        android:fill="#FFFFFFFF"
-        android:pathData="M28.001,48.787l7,7.07c7.731 -7.811,20.269 -7.81,28.001,0l6.999 -7.07C58.403,37.071,39.599,37.071,28.001,48.787z" />
-    <path
-        android:name="bar1"
-        android:fill="#FF555555"
-        android:pathData="M14.001,34.645   L21,41.716c15.464 -15.621,40.536 -15.621,56,0l7.001 -7.071C64.672,15.119,33.33,15.119,14.001,34.645z" />
-    <path
-        android:name="bar0"
-        android:fill="#FF555555"
-        android:pathData="M0,20.502l6.999,7.071   c23.196 -23.431,60.806 -23.431,84.002,0L98,20.503C70.938 -6.834,27.063 -6.834,0,20.502z" />
+    <group>
+        <path
+            android:name="bar3"
+            android:fill="#FFFFFFFF"
+            android:pathData="M49.001,60c-5.466,0 -9.899,4.478 -9.899,10s4.434,10,9.899,10c5.468,0,9.899 -4.478,9.899 -10S54.469,60,49.001,60z" />
+        <path
+            android:name="bar2"
+            android:fill="#FFFFFFFF"
+            android:pathData="M28.001,48.787l7,7.07c7.731 -7.811,20.269 -7.81,28.001,0l6.999 -7.07C58.403,37.071,39.599,37.071,28.001,48.787z" />
+        <path
+            android:name="bar1"
+            android:fill="#FF555555"
+            android:pathData="M14.001,34.645   L21,41.716c15.464 -15.621,40.536 -15.621,56,0l7.001 -7.071C64.672,15.119,33.33,15.119,14.001,34.645z" />
+        <path
+            android:name="bar0"
+            android:fill="#FF555555"
+            android:pathData="M0,20.502l6.999,7.071   c23.196 -23.431,60.806 -23.431,84.002,0L98,20.503C70.938 -6.834,27.063 -6.834,0,20.502z" />
+    </group>
 
 </vector>
\ No newline at end of file
diff --git a/tests/VectorDrawableTest/res/drawable/vector_drawable11.xml b/tests/VectorDrawableTest/res/drawable/vector_drawable11.xml
index 16d8b48..3422bbf 100644
--- a/tests/VectorDrawableTest/res/drawable/vector_drawable11.xml
+++ b/tests/VectorDrawableTest/res/drawable/vector_drawable11.xml
@@ -23,16 +23,17 @@
         android:viewportHeight="80"
         android:viewportWidth="40" />
 
-    <path
-        android:name="battery"
-        android:fill="#3388ff"
-        android:pathData="M 20.28125,2.0000002 C 17.352748,2.0000002 15,4.3527485 15,7.2812502 L 15,8.0000002 L 13.15625,8.0000002 C 9.7507553,8.0000002 7,10.750759 7,14.15625 L 7,39.84375 C 7,43.24924 9.7507558,46 13.15625,46 L 33.84375,46 C 37.249245,46 39.999999,43.24924 40,39.84375 L 40,14.15625 C 40,10.75076 37.249243,8.0000002 33.84375,8.0000002 L 32,8.0000002 L 32,7.2812502 C 32,4.3527485 29.647252,2.0000002 26.71875,2.0000002 L 20.28125,2.0000002 z"
-        android:rotation="0"
-        android:stroke="#ff8833"
-        android:strokeWidth="1" />
-    <path
-        android:name="spark"
-        android:fill="#FFFF0000"
-        android:pathData="M 30,18.031528 L 25.579581,23.421071 L 29.370621,26.765348 L 20.096792,37 L 21.156922,28.014053 L 17,24.902844 L 20.880632,18 L 30,18.031528 z" />
+    <group>
+        <path
+            android:name="battery"
+            android:fill="#3388ff"
+            android:pathData="M 20.28125,2.0000002 C 17.352748,2.0000002 15,4.3527485 15,7.2812502 L 15,8.0000002 L 13.15625,8.0000002 C 9.7507553,8.0000002 7,10.750759 7,14.15625 L 7,39.84375 C 7,43.24924 9.7507558,46 13.15625,46 L 33.84375,46 C 37.249245,46 39.999999,43.24924 40,39.84375 L 40,14.15625 C 40,10.75076 37.249243,8.0000002 33.84375,8.0000002 L 32,8.0000002 L 32,7.2812502 C 32,4.3527485 29.647252,2.0000002 26.71875,2.0000002 L 20.28125,2.0000002 z"
+            android:stroke="#ff8833"
+            android:strokeWidth="1" />
+        <path
+            android:name="spark"
+            android:fill="#FFFF0000"
+            android:pathData="M 30,18.031528 L 25.579581,23.421071 L 29.370621,26.765348 L 20.096792,37 L 21.156922,28.014053 L 17,24.902844 L 20.880632,18 L 30,18.031528 z" />
+    </group>
 
 </vector>
\ No newline at end of file
diff --git a/tests/VectorDrawableTest/res/drawable/vector_drawable12.xml b/tests/VectorDrawableTest/res/drawable/vector_drawable12.xml
index 0a0407d..3042f6a 100644
--- a/tests/VectorDrawableTest/res/drawable/vector_drawable12.xml
+++ b/tests/VectorDrawableTest/res/drawable/vector_drawable12.xml
@@ -23,20 +23,20 @@
         android:viewportHeight="600"
         android:viewportWidth="600" />
 
-    <path
-        android:name="pie1"
-        android:pathData="M300,70 a230,230 0 1,0 1,0 z"
-        android:stroke="#FF00FF00"
-        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:pivotX="300"
-        android:pivotY="300"
-        android:rotation="0" />
+    <group>
+        <path
+            android:name="pie1"
+            android:pathData="M300,70 a230,230 0 1,0 1,0 z"
+            android:fill="#00000000"
+            android:stroke="#FF00FF00"
+            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"/>
+    </group>
 
 </vector>
\ No newline at end of file
diff --git a/tests/VectorDrawableTest/res/drawable/vector_drawable13.xml b/tests/VectorDrawableTest/res/drawable/vector_drawable13.xml
index 385b1e9..8c946df 100644
--- a/tests/VectorDrawableTest/res/drawable/vector_drawable13.xml
+++ b/tests/VectorDrawableTest/res/drawable/vector_drawable13.xml
@@ -23,20 +23,19 @@
         android:viewportHeight="400"
         android:viewportWidth="600" />
 
-    <path
-        android:name="pie1"
-        android:fill="#ffffffff"
-        android:pathData="M300,200 h-150 a150,150 0 1,0 150,-150 z"
-        android:stroke="#FF00FF00"
-        android:strokeWidth="1" />
-    <path
-        android:name="half"
-        android:fill="#FFFF0000"
-        android:pathData="M275,175 v-150 a150,150 0 0,0 -150,150 z"
-        android:pivotX="300"
-        android:pivotY="200"
-        android:rotation="0"
-        android:stroke="#FF0000FF"
-        android:strokeWidth="5" />
+    <group>
+        <path
+            android:name="pie1"
+            android:fill="#ffffffff"
+            android:pathData="M300,200 h-150 a150,150 0 1,0 150,-150 z"
+            android:stroke="#FF00FF00"
+            android:strokeWidth="1" />
+        <path
+            android:name="half"
+            android:fill="#FFFF0000"
+            android:pathData="M275,175 v-150 a150,150 0 0,0 -150,150 z"
+            android:stroke="#FF0000FF"
+            android:strokeWidth="5" />
+    </group>
 
 </vector>
\ No newline at end of file
diff --git a/tests/VectorDrawableTest/res/drawable/vector_drawable14.xml b/tests/VectorDrawableTest/res/drawable/vector_drawable14.xml
index b701b35..8d4ca61 100644
--- a/tests/VectorDrawableTest/res/drawable/vector_drawable14.xml
+++ b/tests/VectorDrawableTest/res/drawable/vector_drawable14.xml
@@ -23,17 +23,20 @@
         android:viewportHeight="500"
         android:viewportWidth="800" />
 
-    <path
-        android:name="pie2"
-        android:pathData="M200,350 l 50,-25
+    <group
+        android:pivotX="90"
+        android:pivotY="100"
+        android:rotation="20">
+        <path
+            android:name="pie2"
+            android:pathData="M200,350 l 50,-25
            a25,12 -30 0,1 100,-50 l 50,-25
            a25,25 -30 0,1 100,-50 l 50,-25
            a25,37 -30 0,1 100,-50 l 50,-25
            a25,50 -30 0,1 100,-50 l 50,-25"
-        android:pivotX="90"
-        android:pivotY="100"
-        android:rotation="20"
-        android:stroke="#FF00FF00"
-        android:strokeWidth="10" />
+            android:fill="#00000000"
+            android:stroke="#FF00FF00"
+            android:strokeWidth="10" />
+    </group>
 
 </vector>
\ No newline at end of file
diff --git a/tests/VectorDrawableTest/res/drawable/vector_drawable15.xml b/tests/VectorDrawableTest/res/drawable/vector_drawable15.xml
index 8d773e1..b08e157 100644
--- a/tests/VectorDrawableTest/res/drawable/vector_drawable15.xml
+++ b/tests/VectorDrawableTest/res/drawable/vector_drawable15.xml
@@ -23,14 +23,16 @@
         android:viewportHeight="400"
         android:viewportWidth="500" />
 
-    <path
-        android:name="house"
-        android:fill="#ff440000"
-        android:pathData="M100,200 C100,100 250,100 250,200 S400,300 400,200"
+    <group
         android:pivotX="250"
         android:pivotY="200"
-        android:rotation="180"
-        android:stroke="#FFFF0000"
-        android:strokeWidth="10" />
+        android:rotation="180">
+        <path
+            android:name="house"
+            android:fill="#ff440000"
+            android:pathData="M100,200 C100,100 250,100 250,200 S400,300 400,200"
+            android:stroke="#FFFF0000"
+            android:strokeWidth="10" />
+    </group>
 
 </vector>
\ No newline at end of file
diff --git a/tests/VectorDrawableTest/res/drawable/vector_drawable16.xml b/tests/VectorDrawableTest/res/drawable/vector_drawable16.xml
index 3b7926c..ae85d9b 100644
--- a/tests/VectorDrawableTest/res/drawable/vector_drawable16.xml
+++ b/tests/VectorDrawableTest/res/drawable/vector_drawable16.xml
@@ -23,13 +23,29 @@
         android:viewportHeight="200"
         android:viewportWidth="200" />
 
-    <path
-        android:name="house"
-        android:pathData="M 100,10 v 90 M 10,100 h 90"
+    <group>
+        <path
+            android:name="background1"
+            android:pathData="M 0,0 l 100,0 l 0, 100 l -100, 0 z"
+            android:fill="#FF000000"/>
+        <path
+            android:name="background2"
+            android:pathData="M 100,100 l 100,0 l 0, 100 l -100, 0 z"
+            android:fill="#FF000000"/>
+    </group>
+    <group
         android:pivotX="100"
         android:pivotY="100"
-        android:rotation="360"
-        android:stroke="#FF00FF00"
-        android:strokeWidth="10" />
+        android:rotation="90"
+        android:scaleX="0.75"
+        android:scaleY="0.5"
+        android:translateX="0.0"
+        android:translateY="100.0">
+        <path
+            android:name="twoLines"
+            android:pathData="M 100,10 v 90 M 10,100 h 90"
+            android:stroke="#FF00FF00"
+            android:strokeWidth="10" />
+    </group>
 
 </vector>
\ No newline at end of file
diff --git a/tests/VectorDrawableTest/res/drawable/vector_drawable17.xml b/tests/VectorDrawableTest/res/drawable/vector_drawable17.xml
index 1ec72be..c28aff4 100644
--- a/tests/VectorDrawableTest/res/drawable/vector_drawable17.xml
+++ b/tests/VectorDrawableTest/res/drawable/vector_drawable17.xml
@@ -1,5 +1,4 @@
-<!--
- Copyright (C) 2014 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.
@@ -16,20 +15,19 @@
 <vector xmlns:android="http://schemas.android.com/apk/res/android" >
 
     <size
-        android:height="64dp"
-        android:width="64dp" />
+            android:width="64dp"
+            android:height="64dp"/>
 
-    <viewport
-        android:viewportHeight="600"
-        android:viewportWidth="1200" />
+    <viewport android:viewportWidth="1200"
+          android:viewportHeight="600"/>
 
-    <path
-        android:name="house"
-        android:pathData="M200,300 Q400,50 600,300 T1000,300"
-        android:pivotX="600"
-        android:pivotY="300"
-        android:rotation="360"
-        android:stroke="#FFFF0000"
-        android:strokeWidth="10" />
+    <group>
+        <path
+                android:name="house"
+                android:pathData="M200,300 Q400,50 600,300 T1000,300"
+                android:fill="#00000000"
+                android:stroke="#FFFF0000"
+                android:strokeWidth="10"/>
+    </group>
 
-</vector>
\ No newline at end of file
+</vector>
diff --git a/tests/VectorDrawableTest/res/drawable/vector_drawable18.xml b/tests/VectorDrawableTest/res/drawable/vector_drawable18.xml
index 12d0e93..d7042fd 100644
--- a/tests/VectorDrawableTest/res/drawable/vector_drawable18.xml
+++ b/tests/VectorDrawableTest/res/drawable/vector_drawable18.xml
@@ -23,13 +23,13 @@
         android:viewportHeight="400"
         android:viewportWidth="500" />
 
-    <path
-        android:name="house"
-        android:pathData="M100,200 C100,100 250,100 250,200 S400,300 400,200"
-        android:pivotX="250"
-        android:pivotY="200"
-        android:rotation="360"
-        android:stroke="#FFFFFF00"
-        android:strokeWidth="10" />
+    <group>
+        <path
+            android:name="house"
+            android:pathData="M100,200 C100,100 250,100 250,200 S400,300 400,200"
+            android:fill="#00000000"
+            android:stroke="#FFFFFF00"
+            android:strokeWidth="10" />
+    </group>
 
 </vector>
\ No newline at end of file
diff --git a/tests/VectorDrawableTest/res/drawable/vector_drawable19.xml b/tests/VectorDrawableTest/res/drawable/vector_drawable19.xml
index 017e04c..47a9574 100644
--- a/tests/VectorDrawableTest/res/drawable/vector_drawable19.xml
+++ b/tests/VectorDrawableTest/res/drawable/vector_drawable19.xml
@@ -23,12 +23,15 @@
         android:viewportHeight="800"
         android:viewportWidth="1000" />
 
-    <path
-        android:name="house"
-        android:pathData="M10,300 Q400,550 600,300 T1000,300"
-        android:pivotX="90"
-        android:pivotY="100"
-        android:stroke="#FFFF0000"
-        android:strokeWidth="60" />
+    <group>
+        <path
+            android:name="house"
+            android:pathData="M10,300 Q400,550 600,300 T1000,300"
+            android:pivotX="90"
+            android:pivotY="100"
+            android:fill="#00000000"
+            android:stroke="#FFFF0000"
+            android:strokeWidth="60" />
+    </group>
 
 </vector>
\ No newline at end of file
diff --git a/tests/VectorDrawableTest/res/drawable/vector_drawable20.xml b/tests/VectorDrawableTest/res/drawable/vector_drawable20.xml
index b7002a3..b8af7e2 100644
--- a/tests/VectorDrawableTest/res/drawable/vector_drawable20.xml
+++ b/tests/VectorDrawableTest/res/drawable/vector_drawable20.xml
@@ -23,14 +23,16 @@
         android:viewportHeight="480"
         android:viewportWidth="480" />
 
-    <path
-        android:name="edit"
-        android:fill="#FF00FFFF"
-        android:pathData="M406.667,180c0,0 -100 -100 -113.334 -113.333
+    <group>
+        <path
+            android:name="edit"
+            android:fill="#FF00FFFF"
+            android:pathData="M406.667,180c0,0 -100 -100 -113.334 -113.333
     c-13.333 -13.334 -33.333,0 -33.333,0l-160,160c0,0 -40,153.333 -40,173.333c0,13.333,13.333,13.333,13.333,13.333l173.334 -40
     c0,0,146.666 -146.666,160 -160C420,200,406.667,180,406.667,180z M226.399,356.823L131.95,378.62l-38.516 -38.522
     c7.848 -34.675,20.152 -82.52,23.538 -95.593l3.027,2.162l106.667,106.666L226.399,356.823z"
-        android:stroke="#FF000000"
-        android:strokeWidth="10" />
+            android:stroke="#FF000000"
+            android:strokeWidth="10" />
+    </group>
 
 </vector>
\ No newline at end of file
diff --git a/tests/VectorDrawableTest/res/drawable/vector_drawable21.xml b/tests/VectorDrawableTest/res/drawable/vector_drawable21.xml
new file mode 100644
index 0000000..e0013e7
--- /dev/null
+++ b/tests/VectorDrawableTest/res/drawable/vector_drawable21.xml
@@ -0,0 +1,51 @@
+<!--
+ 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:height="64dp"
+        android:width="64dp" />
+
+    <viewport
+        android:viewportHeight="200"
+        android:viewportWidth="200" />
+
+    <group>
+        <path
+            android:name="background1"
+            android:pathData="M 0,0 l 100,0 l 0, 100 l -100, 0 z"
+            android:fill="#FF000000"/>
+        <path
+            android:name="background2"
+            android:pathData="M 100,100 l 100,0 l 0, 100 l -100, 0 z"
+            android:fill="#FF000000"/>
+    </group>
+    <group
+        android:pivotX="0"
+        android:pivotY="0"
+        android:rotation="90"
+        android:scaleX="0.75"
+        android:scaleY="0.5"
+        android:translateX="100.0"
+        android:translateY="100.0">
+        <path
+            android:name="twoLines"
+            android:pathData="M 100,10 v 90 M 10,100 h 90"
+            android:stroke="#FF00FF00"
+            android:strokeWidth="10" />
+    </group>
+
+</vector>
\ No newline at end of file
diff --git a/tests/VectorDrawableTest/res/drawable/vector_drawable22.xml b/tests/VectorDrawableTest/res/drawable/vector_drawable22.xml
new file mode 100644
index 0000000..8d38cb5
--- /dev/null
+++ b/tests/VectorDrawableTest/res/drawable/vector_drawable22.xml
@@ -0,0 +1,72 @@
+<!--
+ 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:height="64dp"
+        android:width="64dp" />
+
+    <viewport
+        android:viewportHeight="400"
+        android:viewportWidth="400" />
+
+    <group android:name="backgroundGroup" >
+        <path
+            android:name="background1"
+            android:fill="#80000000"
+            android:pathData="M 0,0 l 200,0 l 0, 200 l -200, 0 z" />
+        <path
+            android:name="background2"
+            android:fill="#80000000"
+            android:pathData="M 200,200 l 200,0 l 0, 200 l -200, 0 z" />
+    </group>
+    <group
+        android:name="translateToCenterGroup"
+        android:translateX="50.0"
+        android:translateY="90.0" >
+        <path
+            android:name="twoLines"
+            android:pathData="M 0,0 v 100 M 0,0 h 100"
+            android:stroke="#FFFF0000"
+            android:strokeWidth="20" />
+
+        <group
+            android:name="rotationGroup"
+            android:pivotX="0.0"
+            android:pivotY="0.0"
+            android:rotation="-45.0" >
+            <path
+                android:name="twoLines1"
+                android:pathData="M 0,0 v 100 M 0,0 h 100"
+                android:stroke="#FF00FF00"
+                android:strokeWidth="20" />
+
+            <group
+                android:name="translateGroup"
+                android:translateX="130.0"
+                android:translateY="160.0" >
+                <group android:name="scaleGroup" >
+                    <path
+                        android:name="twoLines2"
+                        android:pathData="M 0,0 v 100 M 0,0 h 100"
+                        android:stroke="#FF0000FF"
+                        android:strokeWidth="20" />
+                </group>
+            </group>
+        </group>
+    </group>
+
+</vector>
\ No newline at end of file
diff --git a/tests/VectorDrawableTest/res/drawable/vector_drawable23.xml b/tests/VectorDrawableTest/res/drawable/vector_drawable23.xml
new file mode 100644
index 0000000..52acd7a
--- /dev/null
+++ b/tests/VectorDrawableTest/res/drawable/vector_drawable23.xml
@@ -0,0 +1,86 @@
+<!--
+ 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:height="64dp"
+        android:width="64dp" />
+
+    <viewport
+        android:viewportHeight="400"
+        android:viewportWidth="400" />
+
+    <group android:name="backgroundGroup" >
+        <path
+            android:name="background1"
+            android:fill="#80000000"
+            android:pathData="M 0,0 l 200,0 l 0, 200 l -200, 0 z" />
+        <path
+            android:name="background2"
+            android:fill="#80000000"
+            android:pathData="M 200,200 l 200,0 l 0, 200 l -200, 0 z" />
+    </group>
+    <group
+        android:name="translateToCenterGroup"
+        android:translateX="50.0"
+        android:translateY="90.0" >
+        <path
+            android:name="twoLines"
+            android:pathData="@string/twoLinePathData"
+            android:stroke="#FFFF0000"
+            android:strokeWidth="20" />
+
+        <group
+            android:name="rotationGroup"
+            android:pivotX="0.0"
+            android:pivotY="0.0"
+            android:rotation="-45.0" >
+            <path
+                android:name="twoLines1"
+                android:pathData="@string/twoLinePathData"
+                android:stroke="#FF00FF00"
+                android:strokeWidth="20" />
+
+            <group
+                android:name="translateGroup"
+                android:translateX="130.0"
+                android:translateY="160.0" >
+                <group android:name="scaleGroup" >
+                    <path
+                        android:name="twoLines3"
+                        android:pathData="@string/twoLinePathData"
+                        android:stroke="#FF0000FF"
+                        android:strokeWidth="20" />
+                </group>
+            </group>
+
+            <group
+                android:name="translateGroupHalf"
+                android:translateX="65.0"
+                android:translateY="80.0" >
+                <group android:name="scaleGroup" >
+                    <path
+                        android:name="twoLines2"
+                        android:pathData="@string/twoLinePathData"
+                        android:fill="?android:attr/colorForeground"
+                        android:stroke="?android:attr/colorForeground"
+                        android:strokeWidth="20" />
+                </group>
+            </group>
+        </group>
+    </group>
+
+</vector>
\ No newline at end of file
diff --git a/tests/VectorDrawableTest/res/drawable/vector_drawable24.xml b/tests/VectorDrawableTest/res/drawable/vector_drawable24.xml
new file mode 100644
index 0000000..c062d70
--- /dev/null
+++ b/tests/VectorDrawableTest/res/drawable/vector_drawable24.xml
@@ -0,0 +1,91 @@
+<!--
+ 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:height="64dp"
+        android:width="64dp" />
+
+    <viewport
+        android:viewportHeight="400"
+        android:viewportWidth="400" />
+
+    <group android:name="backgroundGroup"
+        android:alpha = "0.5" >
+        <path
+            android:name="background1"
+            android:fill="#FF000000"
+            android:pathData="M 0,0 l 200,0 l 0, 200 l -200, 0 z" />
+        <path
+            android:name="background2"
+            android:fill="#FF000000"
+            android:pathData="M 200,200 l 200,0 l 0, 200 l -200, 0 z" />
+    </group>
+    <group
+        android:name="translateToCenterGroup"
+        android:translateX="50.0"
+        android:translateY="90.0"
+        android:alpha = "0.5" >
+        <path
+            android:name="twoLines"
+            android:pathData="@string/twoLinePathData"
+            android:stroke="#FFFF0000"
+            android:strokeWidth="20" />
+
+        <group
+            android:name="rotationGroup"
+            android:pivotX="0.0"
+            android:pivotY="0.0"
+            android:rotation="-45.0"
+            android:alpha = "0.5" >
+            <path
+                android:name="twoLines1"
+                android:pathData="@string/twoLinePathData"
+                android:stroke="#FF00FF00"
+                android:strokeWidth="20" />
+
+            <group
+                android:name="translateGroup"
+                android:translateX="130.0"
+                android:translateY="160.0"
+                android:alpha = "0.5">
+                <group android:name="scaleGroup" >
+                    <path
+                        android:name="twoLines3"
+                        android:pathData="@string/twoLinePathData"
+                        android:stroke="#FF0000FF"
+                        android:strokeWidth="20" />
+                </group>
+            </group>
+
+            <group
+                android:name="translateGroupHalf"
+                android:translateX="65.0"
+                android:translateY="80.0"
+                android:alpha = "0.5">
+                <group android:name="scaleGroup" >
+                    <path
+                        android:name="twoLines2"
+                        android:pathData="@string/twoLinePathData"
+                        android:fill="?android:attr/colorForeground"
+                        android:stroke="?android:attr/colorForeground"
+                        android:strokeWidth="20" />
+                </group>
+            </group>
+        </group>
+    </group>
+
+</vector>
\ No newline at end of file
diff --git a/tests/VectorDrawableTest/res/drawable/vector_icon_create.xml b/tests/VectorDrawableTest/res/drawable/vector_icon_create.xml
index cda213d..22ce795 100644
--- a/tests/VectorDrawableTest/res/drawable/vector_icon_create.xml
+++ b/tests/VectorDrawableTest/res/drawable/vector_icon_create.xml
@@ -23,8 +23,10 @@
         android:viewportHeight="24"
         android:viewportWidth="24" />
 
-    <path
-        android:fill="#FF000000"
-        android:pathData="M3.0,17.25L3.0,21.0l3.75,0.0L17.813995,9.936001l-3.75,-3.75L3.0,17.25zM20.707,7.0429993c0.391,-0.391 0.391,-1.023 0.0,-1.414l-2.336,-2.336c-0.391,-0.391 -1.023,-0.391 -1.414,0.0l-1.832,1.832l3.75,3.75L20.707,7.0429993z" />
+    <group>
+        <path
+            android:fill="#FF000000"
+            android:pathData="M3.0,17.25L3.0,21.0l3.75,0.0L17.813995,9.936001l-3.75,-3.75L3.0,17.25zM20.707,7.0429993c0.391,-0.391 0.391,-1.023 0.0,-1.414l-2.336,-2.336c-0.391,-0.391 -1.023,-0.391 -1.414,0.0l-1.832,1.832l3.75,3.75L20.707,7.0429993z" />
+    </group>
 
 </vector>
\ No newline at end of file
diff --git a/tests/VectorDrawableTest/res/drawable/vector_icon_delete.xml b/tests/VectorDrawableTest/res/drawable/vector_icon_delete.xml
index 2cb6381..042173c 100644
--- a/tests/VectorDrawableTest/res/drawable/vector_icon_delete.xml
+++ b/tests/VectorDrawableTest/res/drawable/vector_icon_delete.xml
@@ -23,8 +23,10 @@
         android:viewportHeight="24"
         android:viewportWidth="24" />
 
-    <path
-        android:fill="#FF000000"
-        android:pathData="M6.0,19.0c0.0,1.104 0.896,2.0 2.0,2.0l8.0,0.0c1.104,0.0 2.0,-0.896 2.0,-2.0l0.0,-12.0L6.0,7.0L6.0,19.0zM18.0,4.0l-2.5,0.0l-1.0,-1.0l-5.0,0.0l-1.0,1.0L6.0,4.0C5.4469986,4.0 5.0,4.4469986 5.0,5.0l0.0,1.0l14.0,0.0l0.0,-1.0C19.0,4.4469986 18.552002,4.0 18.0,4.0z" />
+    <group>
+        <path
+            android:fill="#FF000000"
+            android:pathData="M6.0,19.0c0.0,1.104 0.896,2.0 2.0,2.0l8.0,0.0c1.104,0.0 2.0,-0.896 2.0,-2.0l0.0,-12.0L6.0,7.0L6.0,19.0zM18.0,4.0l-2.5,0.0l-1.0,-1.0l-5.0,0.0l-1.0,1.0L6.0,4.0C5.4469986,4.0 5.0,4.4469986 5.0,5.0l0.0,1.0l14.0,0.0l0.0,-1.0C19.0,4.4469986 18.552002,4.0 18.0,4.0z" />
+    </group>
 
 </vector>
\ No newline at end of file
diff --git a/tests/VectorDrawableTest/res/drawable/vector_icon_heart.xml b/tests/VectorDrawableTest/res/drawable/vector_icon_heart.xml
index d58942e..6b6f43d 100644
--- a/tests/VectorDrawableTest/res/drawable/vector_icon_heart.xml
+++ b/tests/VectorDrawableTest/res/drawable/vector_icon_heart.xml
@@ -23,8 +23,10 @@
         android:viewportHeight="24"
         android:viewportWidth="24" />
 
-    <path
-        android:fill="#FF000000"
-        android:pathData="M16.0,5.0c-1.955,0.0 -3.83,1.268 -4.5,3.0c-0.67,-1.732 -2.547,-3.0 -4.5,-3.0C4.4570007,5.0 2.5,6.931999 2.5,9.5c0.0,3.529 3.793,6.258 9.0,11.5c5.207,-5.242 9.0,-7.971 9.0,-11.5C20.5,6.931999 18.543,5.0 16.0,5.0z" />
+    <group>
+        <path
+            android:fill="#FF000000"
+            android:pathData="M16.0,5.0c-1.955,0.0 -3.83,1.268 -4.5,3.0c-0.67,-1.732 -2.547,-3.0 -4.5,-3.0C4.4570007,5.0 2.5,6.931999 2.5,9.5c0.0,3.529 3.793,6.258 9.0,11.5c5.207,-5.242 9.0,-7.971 9.0,-11.5C20.5,6.931999 18.543,5.0 16.0,5.0z" />
+    </group>
 
 </vector>
\ No newline at end of file
diff --git a/tests/VectorDrawableTest/res/drawable/vector_icon_schedule.xml b/tests/VectorDrawableTest/res/drawable/vector_icon_schedule.xml
index 4717be4..ba8ebca 100644
--- a/tests/VectorDrawableTest/res/drawable/vector_icon_schedule.xml
+++ b/tests/VectorDrawableTest/res/drawable/vector_icon_schedule.xml
@@ -23,11 +23,13 @@
         android:viewportHeight="24"
         android:viewportWidth="24" />
 
-    <path
-        android:fillOpacity="0.9"
-        android:pathData="M11.994999,2.0C6.4679985,2.0 2.0,6.4780006 2.0,12.0s4.468,10.0 9.995,10.0S22.0,17.522 22.0,12.0S17.521,2.0 11.994999,2.0zM12.0,20.0c-4.42,0.0 -8.0,-3.582 -8.0,-8.0s3.58,-8.0 8.0,-8.0s8.0,3.582 8.0,8.0S16.419998,20.0 12.0,20.0z" />
-    <path
-        android:fillOpacity="0.9"
-        android:pathData="M12.5,6.0l-1.5,0.0 0.0,7.0 5.3029995,3.1819992 0.75,-1.249999 -4.5529995,-2.7320004z" />
+    <group>
+        <path
+            android:fillOpacity="0.9"
+            android:pathData="M11.994999,2.0C6.4679985,2.0 2.0,6.4780006 2.0,12.0s4.468,10.0 9.995,10.0S22.0,17.522 22.0,12.0S17.521,2.0 11.994999,2.0zM12.0,20.0c-4.42,0.0 -8.0,-3.582 -8.0,-8.0s3.58,-8.0 8.0,-8.0s8.0,3.582 8.0,8.0S16.419998,20.0 12.0,20.0z" />
+        <path
+            android:fillOpacity="0.9"
+            android:pathData="M12.5,6.0l-1.5,0.0 0.0,7.0 5.3029995,3.1819992 0.75,-1.249999 -4.5529995,-2.7320004z" />
+    </group>
 
 </vector>
\ No newline at end of file
diff --git a/tests/VectorDrawableTest/res/drawable/vector_icon_settings.xml b/tests/VectorDrawableTest/res/drawable/vector_icon_settings.xml
index c626325..896a938 100644
--- a/tests/VectorDrawableTest/res/drawable/vector_icon_settings.xml
+++ b/tests/VectorDrawableTest/res/drawable/vector_icon_settings.xml
@@ -23,8 +23,10 @@
         android:viewportHeight="24"
         android:viewportWidth="24" />
 
-    <path
-        android:fill="#FF000000"
-        android:pathData="M19.429,12.975998c0.042,-0.32 0.07,-0.645 0.07,-0.976s-0.029,-0.655 -0.07,-0.976l2.113,-1.654c0.188,-0.151 0.243,-0.422 0.118,-0.639l-2.0,-3.463c-0.125,-0.217 -0.386,-0.304 -0.612,-0.218l-2.49,1.004c-0.516,-0.396 -1.081,-0.731 -1.69,-0.984l-0.375,-2.648C14.456,2.1829987 14.25,2.0 14.0,2.0l-4.0,0.0C9.75,2.0 9.544,2.1829987 9.506,2.422001L9.131,5.0699997C8.521,5.322998 7.957,5.6570015 7.44,6.054001L4.952,5.0509987C4.726,4.965 4.464,5.052002 4.34,5.269001l-2.0,3.463C2.2150002,8.947998 2.27,9.219002 2.4580002,9.369999l2.112,1.653C4.528,11.344002 4.5,11.668999 4.5,12.0s0.029,0.656 0.071,0.977L2.4580002,14.630001c-0.188,0.151 -0.243,0.422 -0.118,0.639l2.0,3.463c0.125,0.217 0.386,0.304 0.612,0.218l2.489,-1.004c0.516,0.396 1.081,0.731 1.69,0.984l0.375,2.648C9.544,21.817001 9.75,22.0 10.0,22.0l4.0,0.0c0.25,0.0 0.456,-0.183 0.494,-0.422l0.375,-2.648c0.609,-0.253 1.174,-0.588 1.689,-0.984l2.49,1.004c0.226,0.086 0.487,-0.001 0.612,-0.218l2.0,-3.463c0.125,-0.217 0.07,-0.487 -0.118,-0.639L19.429,12.975998zM12.0,16.0c-2.21,0.0 -4.0,-1.791 -4.0,-4.0c0.0,-2.21 1.79,-4.0 4.0,-4.0c2.208,0.0 4.0,1.79 4.0,4.0C16.0,14.209 14.208,16.0 12.0,16.0z" />
+    <group>
+        <path
+            android:fill="#FF000000"
+            android:pathData="M19.429,12.975998c0.042,-0.32 0.07,-0.645 0.07,-0.976s-0.029,-0.655 -0.07,-0.976l2.113,-1.654c0.188,-0.151 0.243,-0.422 0.118,-0.639l-2.0,-3.463c-0.125,-0.217 -0.386,-0.304 -0.612,-0.218l-2.49,1.004c-0.516,-0.396 -1.081,-0.731 -1.69,-0.984l-0.375,-2.648C14.456,2.1829987 14.25,2.0 14.0,2.0l-4.0,0.0C9.75,2.0 9.544,2.1829987 9.506,2.422001L9.131,5.0699997C8.521,5.322998 7.957,5.6570015 7.44,6.054001L4.952,5.0509987C4.726,4.965 4.464,5.052002 4.34,5.269001l-2.0,3.463C2.2150002,8.947998 2.27,9.219002 2.4580002,9.369999l2.112,1.653C4.528,11.344002 4.5,11.668999 4.5,12.0s0.029,0.656 0.071,0.977L2.4580002,14.630001c-0.188,0.151 -0.243,0.422 -0.118,0.639l2.0,3.463c0.125,0.217 0.386,0.304 0.612,0.218l2.489,-1.004c0.516,0.396 1.081,0.731 1.69,0.984l0.375,2.648C9.544,21.817001 9.75,22.0 10.0,22.0l4.0,0.0c0.25,0.0 0.456,-0.183 0.494,-0.422l0.375,-2.648c0.609,-0.253 1.174,-0.588 1.689,-0.984l2.49,1.004c0.226,0.086 0.487,-0.001 0.612,-0.218l2.0,-3.463c0.125,-0.217 0.07,-0.487 -0.118,-0.639L19.429,12.975998zM12.0,16.0c-2.21,0.0 -4.0,-1.791 -4.0,-4.0c0.0,-2.21 1.79,-4.0 4.0,-4.0c2.208,0.0 4.0,1.79 4.0,4.0C16.0,14.209 14.208,16.0 12.0,16.0z" />
+    </group>
 
 </vector>
\ No newline at end of file
diff --git a/tests/VectorDrawableTest/res/drawable/vector_test01.xml b/tests/VectorDrawableTest/res/drawable/vector_test01.xml
index bad5a46..fc2a15c 100644
--- a/tests/VectorDrawableTest/res/drawable/vector_test01.xml
+++ b/tests/VectorDrawableTest/res/drawable/vector_test01.xml
@@ -23,10 +23,13 @@
         android:viewportHeight="512"
         android:viewportWidth="512" />
 
-    <path
-        android:name="002b"
-        android:pathData="M100,200c0,-100 150,-100 150,0s150,100 150,0t-200,299"
-        android:stroke="#FF0000FF"
-        android:strokeWidth="4" />
+    <group>
+        <path
+            android:name="002b"
+            android:pathData="M100,200c0,-100 150,-100 150,0s150,100 150,0t-200,299"
+            android:stroke="#FF0000FF"
+            android:strokeWidth="4"
+            android:fill="#00000000" />
+    </group>
 
 </vector>
\ No newline at end of file
diff --git a/tests/VectorDrawableTest/res/drawable/vector_test02.xml b/tests/VectorDrawableTest/res/drawable/vector_test02.xml
index c92b6f4..9f4abbf 100644
--- a/tests/VectorDrawableTest/res/drawable/vector_test02.xml
+++ b/tests/VectorDrawableTest/res/drawable/vector_test02.xml
@@ -23,10 +23,13 @@
         android:viewportHeight="512"
         android:viewportWidth="512" />
 
-    <path
-        android:name="002b"
-        android:pathData="M100,200c0,-100 150,-100 150,0s150,100 150,0T-200,299"
-        android:stroke="#FF0000FF"
-        android:strokeWidth="4" />
+    <group>
+        <path
+            android:name="002b"
+            android:pathData="M100,200c0,-100 150,-100 150,0s150,100 150,0T-200,299"
+            android:stroke="#FF0000FF"
+            android:strokeWidth="4"
+            android:fill="#00000000" />
+    </group>
 
 </vector>
\ No newline at end of file
diff --git a/tests/VectorDrawableTest/res/values/strings.xml b/tests/VectorDrawableTest/res/values/strings.xml
index 64163c2..b49a1aa 100644
--- a/tests/VectorDrawableTest/res/values/strings.xml
+++ b/tests/VectorDrawableTest/res/values/strings.xml
@@ -15,4 +15,5 @@
 -->
 
 <resources>
+    <string name="twoLinePathData" >"M 0,0 v 100 M 0,0 h 100"</string>
 </resources>
diff --git a/tests/VectorDrawableTest/src/com/android/test/dynamic/VectorDrawable01.java b/tests/VectorDrawableTest/src/com/android/test/dynamic/VectorDrawable01.java
index 7ba01b1..a23d819 100644
--- a/tests/VectorDrawableTest/src/com/android/test/dynamic/VectorDrawable01.java
+++ b/tests/VectorDrawableTest/src/com/android/test/dynamic/VectorDrawable01.java
@@ -61,6 +61,8 @@
             button.setWidth(200);
             button.setBackgroundResource(icon[i]);
             container.addView(button);
+            VectorDrawable vd = (VectorDrawable) button.getBackground();
+            vd.setAlpha((i + 1) * (0xFF / (icon.length + 1)));
         }
 
         setContentView(container);
diff --git a/tests/VectorDrawableTest/src/com/android/test/dynamic/VectorDrawableAnimation.java b/tests/VectorDrawableTest/src/com/android/test/dynamic/VectorDrawableAnimation.java
new file mode 100644
index 0000000..99de037
--- /dev/null
+++ b/tests/VectorDrawableTest/src/com/android/test/dynamic/VectorDrawableAnimation.java
@@ -0,0 +1,45 @@
+/*
+ * 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.graphics.drawable.AnimationDrawable;
+import android.os.Bundle;
+import android.view.View;
+import android.widget.Button;
+
+public class VectorDrawableAnimation extends Activity {
+    private static final String LOGCAT = "VectorDrawableAnimation";
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+
+        Button button = new Button(this);
+        button.setBackgroundResource(R.drawable.animation_drawable_vector);
+
+        button.setOnClickListener(new View.OnClickListener() {
+                @Override
+            public void onClick(View v) {
+                AnimationDrawable frameAnimation = (AnimationDrawable) v.getBackground();
+                // Start the animation (looped playback by default).
+                frameAnimation.start();
+            }
+        });
+
+        setContentView(button);
+    }
+
+}
diff --git a/tests/VectorDrawableTest/src/com/android/test/dynamic/VectorDrawablePerformance.java b/tests/VectorDrawableTest/src/com/android/test/dynamic/VectorDrawablePerformance.java
index b918cdd..814deb8 100644
--- a/tests/VectorDrawableTest/src/com/android/test/dynamic/VectorDrawablePerformance.java
+++ b/tests/VectorDrawableTest/src/com/android/test/dynamic/VectorDrawablePerformance.java
@@ -17,11 +17,11 @@
 import android.content.res.Resources;
 import android.graphics.drawable.VectorDrawable;
 import android.os.Bundle;
-import android.view.View;
 import android.widget.TextView;
 import android.widget.Button;
 import android.widget.GridLayout;
 import android.widget.ScrollView;
+
 import java.text.DecimalFormat;
 
 @SuppressWarnings({"UnusedDeclaration"})
@@ -47,7 +47,11 @@
             R.drawable.vector_drawable17,
             R.drawable.vector_drawable18,
             R.drawable.vector_drawable19,
-            R.drawable.vector_drawable20
+            R.drawable.vector_drawable20,
+            R.drawable.vector_drawable21,
+            R.drawable.vector_drawable22,
+            R.drawable.vector_drawable23,
+            R.drawable.vector_drawable24,
     };
 
     @Override
@@ -68,7 +72,6 @@
         TextView t = new TextView(this);
         DecimalFormat df = new DecimalFormat("#.##");
         t.setText("avgL=" + df.format(time / (icon.length * 1000000.)) + " ms");
-        t.setBackgroundColor(0xFF000000);
         container.addView(t);
         time =  android.os.SystemClock.elapsedRealtimeNanos();
         for (int i = 0; i < icon.length; i++) {
@@ -81,7 +84,6 @@
         time =  android.os.SystemClock.elapsedRealtimeNanos()-time;
         t = new TextView(this);
         t.setText("avgS=" + df.format(time / (icon.length * 1000000.)) + " ms");
-        t.setBackgroundColor(0xFF000000);
         container.addView(t);
     }
 }
diff --git a/tests/VoiceInteraction/AndroidManifest.xml b/tests/VoiceInteraction/AndroidManifest.xml
index ac0f701..e1a5854 100644
--- a/tests/VoiceInteraction/AndroidManifest.xml
+++ b/tests/VoiceInteraction/AndroidManifest.xml
@@ -2,7 +2,8 @@
         package="com.android.test.voiceinteraction">
 
     <application>
-        <activity android:name="VoiceInteractionMain" android:label="Voice Interaction">
+        <activity android:name="VoiceInteractionMain" android:label="Voice Interaction"
+                android:theme="@android:style/Theme.Quantum">
             <intent-filter>
                 <action android:name="android.intent.action.MAIN" />
                 <category android:name="android.intent.category.DEFAULT" />
@@ -22,7 +23,8 @@
                 android:permission="android.permission.BIND_VOICE_INTERACTION"
                 android:process=":session">
         </service>
-        <activity android:name="TestInteractionActivity" android:label="Voice Interaction Target">
+        <activity android:name="TestInteractionActivity" android:label="Voice Interaction Target"
+                  android:theme="@android:style/Theme.Quantum.Light.Voice">
             <intent-filter>
                 <action android:name="android.intent.action.MAIN" />
                 <category android:name="android.intent.category.DEFAULT" />
diff --git a/tests/VoiceInteraction/res/layout/test_interaction.xml b/tests/VoiceInteraction/res/layout/test_interaction.xml
index 2abf651..4c0c67a 100644
--- a/tests/VoiceInteraction/res/layout/test_interaction.xml
+++ b/tests/VoiceInteraction/res/layout/test_interaction.xml
@@ -18,6 +18,7 @@
     android:layout_width="match_parent"
     android:layout_height="match_parent"
     android:orientation="vertical"
+    android:padding="8dp"
     >
 
     <TextView android:layout_width="wrap_content" android:layout_height="wrap_content"
@@ -29,9 +30,16 @@
         android:layout_width="match_parent"
         android:layout_height="0px"
         android:layout_weight="1"
-        android:layout_marginTop="10dp"
-        android:textSize="12sp"
+        android:layout_marginTop="16dp"
+        android:textAppearance="?android:attr/textAppearanceMedium"
         android:textColor="#ffffffff"
         />
 
+    <Button android:id="@+id/abort"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_marginTop="16dp"
+        android:text="@string/abortVoice"
+        />
+
 </LinearLayout>
diff --git a/tests/VoiceInteraction/res/layout/voice_interaction_session.xml b/tests/VoiceInteraction/res/layout/voice_interaction_session.xml
index 9fcbf3e..142d781 100644
--- a/tests/VoiceInteraction/res/layout/voice_interaction_session.xml
+++ b/tests/VoiceInteraction/res/layout/voice_interaction_session.xml
@@ -14,25 +14,49 @@
      limitations under the License.
 -->
 
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
     android:layout_width="fill_parent"
     android:layout_height="match_parent"
-    android:orientation="vertical"
-    android:background="#ffffffff"
-    >
+    android:fitsSystemWindows="true">
 
-    <TextView android:id="@+id/text"
-        android:layout_width="fill_parent"
-        android:layout_height="wrap_content"
-        android:layout_marginBottom="32dp"
-        />
+    <FrameLayout android:layout_width="fill_parent"
+        android:layout_height="match_parent"
+        android:padding="8dp">
 
-    <Button android:id="@+id/start"
-        android:layout_width="wrap_content"
-        android:layout_height="wrap_content"
-        android:text="@string/start"
-        />
+        <LinearLayout android:id="@+id/content"
+            android:layout_width="fill_parent"
+            android:layout_height="match_parent"
+            android:orientation="vertical"
+            android:background="#ffffffff"
+            android:elevation="8dp"
+            >
 
-</LinearLayout>
+            <TextView android:id="@+id/text"
+                android:layout_width="fill_parent"
+                android:layout_height="wrap_content"
+                android:layout_marginBottom="16dp"
+                android:textAppearance="?android:attr/textAppearanceMedium"
+                />
 
+            <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content"
+                    android:orientation="horizontal">
+                <Button android:id="@+id/start"
+                    android:layout_width="wrap_content"
+                    android:layout_height="wrap_content"
+                    android:text="@string/start"
+                    />
+                <Button android:id="@+id/confirm"
+                    android:layout_width="wrap_content"
+                    android:layout_height="wrap_content"
+                    android:text="@string/confirm"
+                    />
+                <Button android:id="@+id/abort"
+                    android:layout_width="wrap_content"
+                    android:layout_height="wrap_content"
+                    android:text="@string/abort"
+                    />
+            </LinearLayout>
 
+        </LinearLayout>
+    </FrameLayout>
+</FrameLayout>
diff --git a/tests/VoiceInteraction/res/values/strings.xml b/tests/VoiceInteraction/res/values/strings.xml
index 12edb31..70baa52 100644
--- a/tests/VoiceInteraction/res/values/strings.xml
+++ b/tests/VoiceInteraction/res/values/strings.xml
@@ -16,7 +16,10 @@
 
 <resources>
 
-    <string name="start">Start!</string>
+    <string name="start">Start</string>
+    <string name="confirm">Confirm</string>
+    <string name="abort">Abort</string>
+    <string name="abortVoice">Abort Voice</string>
 
 </resources>
 
diff --git a/tests/VoiceInteraction/src/com/android/test/voiceinteraction/MainInteractionSession.java b/tests/VoiceInteraction/src/com/android/test/voiceinteraction/MainInteractionSession.java
index a3af284..c24a088 100644
--- a/tests/VoiceInteraction/src/com/android/test/voiceinteraction/MainInteractionSession.java
+++ b/tests/VoiceInteraction/src/com/android/test/voiceinteraction/MainInteractionSession.java
@@ -33,9 +33,17 @@
     View mContentView;
     TextView mText;
     Button mStartButton;
+    Button mConfirmButton;
+    Button mAbortButton;
 
+    static final int STATE_IDLE = 0;
+    static final int STATE_LAUNCHING = 1;
+    static final int STATE_CONFIRM = 2;
+    static final int STATE_COMMAND = 3;
+    static final int STATE_ABORT_VOICE = 4;
+
+    int mState = STATE_IDLE;
     Request mPendingRequest;
-    boolean mPendingConfirm;
 
     MainInteractionSession(Context context) {
         super(context);
@@ -54,21 +62,39 @@
         mText = (TextView)mContentView.findViewById(R.id.text);
         mStartButton = (Button)mContentView.findViewById(R.id.start);
         mStartButton.setOnClickListener(this);
+        mConfirmButton = (Button)mContentView.findViewById(R.id.confirm);
+        mConfirmButton.setOnClickListener(this);
+        mAbortButton = (Button)mContentView.findViewById(R.id.abort);
+        mAbortButton.setOnClickListener(this);
+        updateState();
         return mContentView;
     }
 
+    void updateState() {
+        mStartButton.setEnabled(mState == STATE_IDLE);
+        mConfirmButton.setEnabled(mState == STATE_CONFIRM || mState == STATE_COMMAND);
+        mAbortButton.setEnabled(mState == STATE_ABORT_VOICE);
+    }
+
     public void onClick(View v) {
-        if (mPendingRequest == null) {
-            mStartButton.setEnabled(false);
+        if (v == mStartButton) {
+            mState = STATE_LAUNCHING;
+            updateState();
             startVoiceActivity(mStartIntent);
-        } else {
-            if (mPendingConfirm) {
+        } else if (v == mConfirmButton) {
+            if (mState == STATE_CONFIRM) {
                 mPendingRequest.sendConfirmResult(true, null);
             } else {
                 mPendingRequest.sendCommandResult(true, null);
             }
             mPendingRequest = null;
-            mStartButton.setText("Start");
+            mState = STATE_IDLE;
+            updateState();
+        } else if (v == mAbortButton) {
+            mPendingRequest.sendAbortVoiceResult(null);
+            mPendingRequest = null;
+            mState = STATE_IDLE;
+            updateState();
         }
     }
 
@@ -78,23 +104,32 @@
     }
 
     @Override
-    public void onConfirm(Caller caller, Request request, String prompt, Bundle extras) {
+    public void onConfirm(Caller caller, Request request, CharSequence prompt, Bundle extras) {
         Log.i(TAG, "onConfirm: prompt=" + prompt + " extras=" + extras);
         mText.setText(prompt);
-        mStartButton.setEnabled(true);
         mStartButton.setText("Confirm");
         mPendingRequest = request;
-        mPendingConfirm = true;
+        mState = STATE_CONFIRM;
+        updateState();
+    }
+
+    @Override
+    public void onAbortVoice(Caller caller, Request request, CharSequence message, Bundle extras) {
+        Log.i(TAG, "onAbortVoice: message=" + message + " extras=" + extras);
+        mText.setText(message);
+        mPendingRequest = request;
+        mState = STATE_ABORT_VOICE;
+        updateState();
     }
 
     @Override
     public void onCommand(Caller caller, Request request, String command, Bundle extras) {
         Log.i(TAG, "onCommand: command=" + command + " extras=" + extras);
         mText.setText("Command: " + command);
-        mStartButton.setEnabled(true);
         mStartButton.setText("Finish Command");
         mPendingRequest = request;
-        mPendingConfirm = false;
+        mState = STATE_COMMAND;
+        updateState();
     }
 
     @Override
diff --git a/tests/VoiceInteraction/src/com/android/test/voiceinteraction/TestInteractionActivity.java b/tests/VoiceInteraction/src/com/android/test/voiceinteraction/TestInteractionActivity.java
index 9c772ff..3ae6a36 100644
--- a/tests/VoiceInteraction/src/com/android/test/voiceinteraction/TestInteractionActivity.java
+++ b/tests/VoiceInteraction/src/com/android/test/voiceinteraction/TestInteractionActivity.java
@@ -20,11 +20,16 @@
 import android.app.VoiceInteractor;
 import android.os.Bundle;
 import android.util.Log;
+import android.view.Gravity;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.Button;
 
-public class TestInteractionActivity extends Activity {
+public class TestInteractionActivity extends Activity implements View.OnClickListener {
     static final String TAG = "TestInteractionActivity";
 
     VoiceInteractor mInteractor;
+    Button mAbortButton;
 
     @Override
     public void onCreate(Bundle savedInstanceState) {
@@ -37,6 +42,13 @@
         }
 
         setContentView(R.layout.test_interaction);
+        mAbortButton = (Button)findViewById(R.id.abort);
+        mAbortButton.setOnClickListener(this);
+
+        // Framework should take care of these.
+        getWindow().setGravity(Gravity.TOP);
+        getWindow().setLayout(ViewGroup.LayoutParams.MATCH_PARENT,
+                ViewGroup.LayoutParams.WRAP_CONTENT);
 
         mInteractor = getVoiceInteractor();
         VoiceInteractor.ConfirmationRequest req = new VoiceInteractor.ConfirmationRequest(
@@ -62,6 +74,26 @@
     }
 
     @Override
+    public void onClick(View v) {
+        if (v == mAbortButton) {
+            VoiceInteractor.AbortVoiceRequest req = new VoiceInteractor.AbortVoiceRequest(
+                    "Dammit, we suck :(", null) {
+                @Override
+                public void onCancel() {
+                    Log.i(TAG, "Canceled!");
+                }
+
+                @Override
+                public void onAbortResult(Bundle result) {
+                    Log.i(TAG, "Abort result: result=" + result);
+                    getActivity().finish();
+                }
+            };
+            mInteractor.submitRequest(req);
+        }
+    }
+
+    @Override
     public void onDestroy() {
         super.onDestroy();
     }
diff --git a/tests/permission/src/com/android/framework/permission/tests/WindowManagerPermissionTests.java b/tests/permission/src/com/android/framework/permission/tests/WindowManagerPermissionTests.java
index 6f5788a..a6c09f3 100644
--- a/tests/permission/src/com/android/framework/permission/tests/WindowManagerPermissionTests.java
+++ b/tests/permission/src/com/android/framework/permission/tests/WindowManagerPermissionTests.java
@@ -93,7 +93,7 @@
         }
         
         try {
-            mWm.addAppToken(0, null, 0, 0, 0, false, false, 0, 0);
+            mWm.addAppToken(0, null, 0, 0, 0, false, false, 0, 0, false);
             fail("IWindowManager.addAppToken did not throw SecurityException as"
                     + " expected");
         } catch (SecurityException e) {
diff --git a/tools/aapt/Main.cpp b/tools/aapt/Main.cpp
index 5a60014..322d86c 100644
--- a/tools/aapt/Main.cpp
+++ b/tools/aapt/Main.cpp
@@ -105,20 +105,12 @@
         "            en\n"
         "            port,en\n"
         "            port,land,en_US\n"
-        "       If you put the special locale, zz_ZZ on the list, it will perform\n"
-        "       pseudolocalization on the default locale, modifying all of the\n"
-        "       strings so you can look for strings that missed the\n"
-        "       internationalization process.  For example:\n"
-        "            port,land,zz_ZZ\n"
         "   -d  one or more device assets to include, separated by commas\n"
         "   -f  force overwrite of existing files\n"
         "   -g  specify a pixel tolerance to force images to grayscale, default 0\n"
         "   -j  specify a jar or zip file containing classes to include\n"
         "   -k  junk path of file(s) added\n"
         "   -m  make package directories under location specified by -J\n"
-#if 0
-        "   -p  pseudolocalize the default configuration\n"
-#endif
         "   -u  update existing packages (add new, replace older, remove deleted files)\n"
         "   -v  verbose output\n"
         "   -x  create extending (non-application) resource IDs\n"
@@ -141,6 +133,8 @@
         "       manifest, making the application debuggable even on production devices.\n"
         "   --include-meta-data\n"
         "       when used with \"dump badging\" also includes meta-data tags.\n"
+        "   --pseudo-localize\n"
+        "       generate resources for pseudo-locales (en-XA and ar-XB).\n"
         "   --min-sdk-version\n"
         "       inserts android:minSdkVersion in to manifest.  If the version is 7 or\n"
         "       higher, the default encoding for resources will be in UTF-8.\n"
@@ -647,6 +641,8 @@
                         goto bail;
                     }
                     gUserIgnoreAssets = argv[0];
+                } else if (strcmp(cp, "-pseudo-localize") == 0) {
+                    bundle.setPseudolocalize(PSEUDO_ACCENTED | PSEUDO_BIDI);
                 } else {
                     fprintf(stderr, "ERROR: Unknown option '-%s'\n", cp);
                     wantUsage = true;
diff --git a/tools/layoutlib/bridge/src/android/content/res/BridgeTypedArray.java b/tools/layoutlib/bridge/src/android/content/res/BridgeTypedArray.java
index cc621c4..105803e 100644
--- a/tools/layoutlib/bridge/src/android/content/res/BridgeTypedArray.java
+++ b/tools/layoutlib/bridge/src/android/content/res/BridgeTypedArray.java
@@ -826,6 +826,11 @@
         return null;
     }
 
+    @Override
+    public int[] extractThemeAttrs() {
+        return null;
+    }
+
     /**
      * Retrieve the raw TypedValue for the attribute at <var>index</var>.
      *
@@ -912,4 +917,9 @@
     public String toString() {
         return Arrays.toString(mResourceData);
     }
- }
+
+    static TypedArray obtain(Resources res, int len) {
+        return res instanceof BridgeResources ?
+                new BridgeTypedArray(((BridgeResources) res), null, len, true) : null;
+    }
+}
diff --git a/tools/layoutlib/bridge/src/android/content/res/Resources_Theme_Delegate.java b/tools/layoutlib/bridge/src/android/content/res/Resources_Theme_Delegate.java
index 31d1594..f4a9f52 100644
--- a/tools/layoutlib/bridge/src/android/content/res/Resources_Theme_Delegate.java
+++ b/tools/layoutlib/bridge/src/android/content/res/Resources_Theme_Delegate.java
@@ -97,6 +97,13 @@
         return found;
     }
 
+    @LayoutlibDelegate
+    /*package*/ static TypedArray resolveAttributes(Resources thisResources, Theme thisTheme,
+            int[] values, int[] attrs) {
+        // FIXME
+        return null;
+    }
+
     // ---- private helper methods ----
 
     private static boolean setupResources(Theme thisTheme) {
diff --git a/tools/layoutlib/bridge/src/android/content/res/TypedArray_Delegate.java b/tools/layoutlib/bridge/src/android/content/res/TypedArray_Delegate.java
index 0a7899a..faa8852 100644
--- a/tools/layoutlib/bridge/src/android/content/res/TypedArray_Delegate.java
+++ b/tools/layoutlib/bridge/src/android/content/res/TypedArray_Delegate.java
@@ -27,4 +27,9 @@
         // pass
         return false;
     }
+
+    @LayoutlibDelegate
+    /*package*/ static TypedArray obtain(Resources res, int len) {
+        return BridgeTypedArray.obtain(res, len);
+    }
 }
diff --git a/tools/layoutlib/bridge/src/android/graphics/BidiRenderer.java b/tools/layoutlib/bridge/src/android/graphics/BidiRenderer.java
index 802cf1c..33813d1 100644
--- a/tools/layoutlib/bridge/src/android/graphics/BidiRenderer.java
+++ b/tools/layoutlib/bridge/src/android/graphics/BidiRenderer.java
@@ -18,9 +18,11 @@
 
 import java.awt.Font;
 import java.awt.Graphics2D;
+import java.awt.Toolkit;
 import java.awt.font.FontRenderContext;
 import java.awt.font.GlyphVector;
 import java.awt.geom.Rectangle2D;
+import java.util.ArrayList;
 import java.util.LinkedList;
 import java.util.List;
 
@@ -36,12 +38,12 @@
 @SuppressWarnings("deprecation")
 public class BidiRenderer {
 
-    /* package */ static class ScriptRun {
+    /*package*/ static class ScriptRun {
         int start;
         int limit;
         boolean isRtl;
         int scriptCode;
-        FontInfo font;
+        Font font;
 
         public ScriptRun(int start, int limit, boolean isRtl) {
             this.start = start;
@@ -51,9 +53,10 @@
         }
     }
 
-    private Graphics2D mGraphics;
-    private Paint_Delegate mPaint;
+    private final Graphics2D mGraphics;
+    private final Paint_Delegate mPaint;
     private char[] mText;
+    private List<Font> mFonts;
     // Bounds of the text drawn so far.
     private RectF mBounds;
     private float mBaseline;
@@ -63,11 +66,15 @@
      * @param paint The Paint to use to get the fonts. Should not be null.
      * @param text Unidirectional text. Should not be null.
      */
-    /* package */ BidiRenderer(Graphics2D graphics, Paint_Delegate paint, char[] text) {
+    /*package*/ BidiRenderer(Graphics2D graphics, Paint_Delegate paint, char[] text) {
         assert (paint != null);
         mGraphics = graphics;
         mPaint = paint;
         mText = text;
+        mFonts = new ArrayList<Font>(paint.getFonts().size());
+        for (FontInfo fontInfo : paint.getFonts()) {
+            mFonts.add(fontInfo.mFont);
+        }
     }
 
     /**
@@ -94,7 +101,7 @@
         // the script runs.
         mBounds = new RectF(x, y, x, y);
         mBaseline = y;
-        for (ScriptRun run : getScriptRuns(mText, start, limit, isRtl, mPaint.getFonts())) {
+        for (ScriptRun run : getScriptRuns(mText, start, limit, isRtl, mFonts)) {
             int flag = Font.LAYOUT_NO_LIMIT_CONTEXT | Font.LAYOUT_NO_START_CONTEXT;
             flag |= isRtl ? Font.LAYOUT_RIGHT_TO_LEFT : Font.LAYOUT_LEFT_TO_RIGHT;
             renderScript(run.start, run.limit, run.font, flag, advances, advancesIndex, draw);
@@ -108,16 +115,15 @@
      * much as possible. This also implements a fallback mechanism to render characters that cannot
      * be drawn using the preferred font.
      */
-    private void renderScript(int start, int limit, FontInfo preferredFont, int flag,
+    private void renderScript(int start, int limit, Font preferredFont, int flag,
             float[] advances, int advancesIndex, boolean draw) {
-        List<FontInfo> fonts = mPaint.getFonts();
-        if (fonts == null || preferredFont == null) {
+        if (mFonts.size() == 0 || preferredFont == null) {
             return;
         }
 
         while (start < limit) {
             boolean foundFont = false;
-            int canDisplayUpTo = preferredFont.mFont.canDisplayUpTo(mText, start, limit);
+            int canDisplayUpTo = preferredFont.canDisplayUpTo(mText, start, limit);
             if (canDisplayUpTo == -1) {
                 // We can draw all characters in the text.
                 render(start, limit, preferredFont, flag, advances, advancesIndex, draw);
@@ -133,8 +139,8 @@
             // The current character cannot be drawn with the preferred font. Cycle through all the
             // fonts to check which one can draw it.
             int charCount = Character.isHighSurrogate(mText[start]) ? 2 : 1;
-            for (FontInfo font : fonts) {
-                canDisplayUpTo = font.mFont.canDisplayUpTo(mText, start, start + charCount);
+            for (Font font : mFonts) {
+                canDisplayUpTo = font.canDisplayUpTo(mText, start, start + charCount);
                 if (canDisplayUpTo == -1) {
                     render(start, start+charCount, font, flag, advances, advancesIndex, draw);
                     start += charCount;
@@ -160,15 +166,19 @@
      * Renders the text to the right of the bounds with the given font.
      * @param font The font to render the text with.
      */
-    private void render(int start, int limit, FontInfo font, int flag, float[] advances,
+    private void render(int start, int limit, Font font, int flag, float[] advances,
             int advancesIndex, boolean draw) {
 
-        // Since the metrics don't have anti-aliasing set, we create a new FontRenderContext with
-        // the anti-aliasing set.
-        FontRenderContext f = font.mMetrics.getFontRenderContext();
-        FontRenderContext frc = new FontRenderContext(f.getTransform(), mPaint.isAntiAliased(),
-                f.usesFractionalMetrics());
-        GlyphVector gv = font.mFont.layoutGlyphVector(frc, mText, start, limit, flag);
+        FontRenderContext frc;
+        if (mGraphics != null) {
+            frc = mGraphics.getFontRenderContext();
+        } else {
+            frc = Toolkit.getDefaultToolkit().getFontMetrics(font).getFontRenderContext();
+            // Metrics obtained this way don't have anti-aliasing set. So,
+            // we create a new FontRenderContext with anti-aliasing set.
+            frc = new FontRenderContext(font.getTransform(), mPaint.isAntiAliased(), frc.usesFractionalMetrics());
+        }
+        GlyphVector gv = font.layoutGlyphVector(frc, mText, start, limit, flag);
         int ng = gv.getNumGlyphs();
         int[] ci = gv.getGlyphCharIndices(0, ng, null);
         if (advances != null) {
@@ -206,7 +216,7 @@
     }
 
     /* package */  static List<ScriptRun> getScriptRuns(char[] text, int start, int limit,
-            boolean isRtl, List<FontInfo> fonts) {
+            boolean isRtl, List<Font> fonts) {
         LinkedList<ScriptRun> scriptRuns = new LinkedList<ScriptRun>();
 
         int count = limit - start;
@@ -225,10 +235,10 @@
 
     // TODO: Replace this method with one which returns the font based on the scriptCode.
     private static void setScriptFont(char[] text, ScriptRun run,
-            List<FontInfo> fonts) {
-        for (FontInfo fontInfo : fonts) {
-            if (fontInfo.mFont.canDisplayUpTo(text, run.start, run.limit) == -1) {
-                run.font = fontInfo;
+            List<Font> fonts) {
+        for (Font font : fonts) {
+            if (font.canDisplayUpTo(text, run.start, run.limit) == -1) {
+                run.font = font;
                 return;
             }
         }
diff --git a/tools/layoutlib/bridge/src/android/graphics/BitmapShader_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/BitmapShader_Delegate.java
index cdbbe46..610c867 100644
--- a/tools/layoutlib/bridge/src/android/graphics/BitmapShader_Delegate.java
+++ b/tools/layoutlib/bridge/src/android/graphics/BitmapShader_Delegate.java
@@ -79,13 +79,6 @@
         return sManager.addNewDelegate(newDelegate);
     }
 
-    @LayoutlibDelegate
-    /*package*/ static long nativePostCreate(long native_shader, long native_bitmap,
-            int shaderTileModeX, int shaderTileModeY) {
-        // pass, not needed.
-        return 0;
-    }
-
     // ---- Private delegate/helper methods ----
 
     private BitmapShader_Delegate(java.awt.image.BufferedImage image,
diff --git a/tools/layoutlib/bridge/src/android/graphics/Canvas_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/Canvas_Delegate.java
index 56c0de9..e35bc06 100644
--- a/tools/layoutlib/bridge/src/android/graphics/Canvas_Delegate.java
+++ b/tools/layoutlib/bridge/src/android/graphics/Canvas_Delegate.java
@@ -71,7 +71,7 @@
      * Returns the native delegate associated to a given {@link Canvas} object.
      */
     public static Canvas_Delegate getDelegate(Canvas canvas) {
-        return sManager.getDelegate(canvas.getNativeCanvas());
+        return sManager.getDelegate(canvas.getNativeCanvasWrapper());
     }
 
     /**
@@ -102,7 +102,7 @@
     @LayoutlibDelegate
     /*package*/ static boolean isOpaque(Canvas thisCanvas) {
         // get the delegate from the native int.
-        Canvas_Delegate canvasDelegate = sManager.getDelegate(thisCanvas.getNativeCanvas());
+        Canvas_Delegate canvasDelegate = sManager.getDelegate(thisCanvas.getNativeCanvasWrapper());
         if (canvasDelegate == null) {
             return false;
         }
@@ -113,7 +113,7 @@
     @LayoutlibDelegate
     /*package*/ static int getWidth(Canvas thisCanvas) {
         // get the delegate from the native int.
-        Canvas_Delegate canvasDelegate = sManager.getDelegate(thisCanvas.getNativeCanvas());
+        Canvas_Delegate canvasDelegate = sManager.getDelegate(thisCanvas.getNativeCanvasWrapper());
         if (canvasDelegate == null) {
             return 0;
         }
@@ -124,7 +124,7 @@
     @LayoutlibDelegate
     /*package*/ static int getHeight(Canvas thisCanvas) {
         // get the delegate from the native int.
-        Canvas_Delegate canvasDelegate = sManager.getDelegate(thisCanvas.getNativeCanvas());
+        Canvas_Delegate canvasDelegate = sManager.getDelegate(thisCanvas.getNativeCanvasWrapper());
         if (canvasDelegate == null) {
             return 0;
         }
@@ -135,7 +135,7 @@
     @LayoutlibDelegate
    /*package*/ static void translate(Canvas thisCanvas, float dx, float dy) {
         // get the delegate from the native int.
-        Canvas_Delegate canvasDelegate = sManager.getDelegate(thisCanvas.getNativeCanvas());
+        Canvas_Delegate canvasDelegate = sManager.getDelegate(thisCanvas.getNativeCanvasWrapper());
         if (canvasDelegate == null) {
             return;
         }
@@ -146,7 +146,7 @@
     @LayoutlibDelegate
     /*package*/ static void rotate(Canvas thisCanvas, float degrees) {
         // get the delegate from the native int.
-        Canvas_Delegate canvasDelegate = sManager.getDelegate(thisCanvas.getNativeCanvas());
+        Canvas_Delegate canvasDelegate = sManager.getDelegate(thisCanvas.getNativeCanvasWrapper());
         if (canvasDelegate == null) {
             return;
         }
@@ -157,7 +157,7 @@
     @LayoutlibDelegate
    /*package*/ static void scale(Canvas thisCanvas, float sx, float sy) {
         // get the delegate from the native int.
-        Canvas_Delegate canvasDelegate = sManager.getDelegate(thisCanvas.getNativeCanvas());
+        Canvas_Delegate canvasDelegate = sManager.getDelegate(thisCanvas.getNativeCanvasWrapper());
         if (canvasDelegate == null) {
             return;
         }
@@ -168,7 +168,7 @@
     @LayoutlibDelegate
    /*package*/ static void skew(Canvas thisCanvas, float kx, float ky) {
         // get the delegate from the native int.
-        Canvas_Delegate canvasDelegate = sManager.getDelegate(thisCanvas.getNativeCanvas());
+        Canvas_Delegate canvasDelegate = sManager.getDelegate(thisCanvas.getNativeCanvasWrapper());
         if (canvasDelegate == null) {
             return;
         }
@@ -204,7 +204,7 @@
     /*package*/ static boolean clipRect(Canvas thisCanvas, float left, float top, float right,
             float bottom) {
         // get the delegate from the native int.
-        Canvas_Delegate canvasDelegate = sManager.getDelegate(thisCanvas.getNativeCanvas());
+        Canvas_Delegate canvasDelegate = sManager.getDelegate(thisCanvas.getNativeCanvasWrapper());
         if (canvasDelegate == null) {
             return false;
         }
@@ -227,7 +227,7 @@
     @LayoutlibDelegate
     /*package*/ static int save(Canvas thisCanvas, int saveFlags) {
         // get the delegate from the native int.
-        Canvas_Delegate canvasDelegate = sManager.getDelegate(thisCanvas.getNativeCanvas());
+        Canvas_Delegate canvasDelegate = sManager.getDelegate(thisCanvas.getNativeCanvasWrapper());
         if (canvasDelegate == null) {
             return 0;
         }
@@ -238,7 +238,7 @@
     @LayoutlibDelegate
     /*package*/ static void restore(Canvas thisCanvas) {
         // get the delegate from the native int.
-        Canvas_Delegate canvasDelegate = sManager.getDelegate(thisCanvas.getNativeCanvas());
+        Canvas_Delegate canvasDelegate = sManager.getDelegate(thisCanvas.getNativeCanvasWrapper());
         if (canvasDelegate == null) {
             return;
         }
@@ -249,7 +249,7 @@
     @LayoutlibDelegate
     /*package*/ static int getSaveCount(Canvas thisCanvas) {
         // get the delegate from the native int.
-        Canvas_Delegate canvasDelegate = sManager.getDelegate(thisCanvas.getNativeCanvas());
+        Canvas_Delegate canvasDelegate = sManager.getDelegate(thisCanvas.getNativeCanvasWrapper());
         if (canvasDelegate == null) {
             return 0;
         }
@@ -260,7 +260,7 @@
     @LayoutlibDelegate
     /*package*/ static void restoreToCount(Canvas thisCanvas, int saveCount) {
         // get the delegate from the native int.
-        Canvas_Delegate canvasDelegate = sManager.getDelegate(thisCanvas.getNativeCanvas());
+        Canvas_Delegate canvasDelegate = sManager.getDelegate(thisCanvas.getNativeCanvasWrapper());
         if (canvasDelegate == null) {
             return;
         }
@@ -287,7 +287,7 @@
     /*package*/ static void drawLines(Canvas thisCanvas,
             final float[] pts, final int offset, final int count,
             Paint paint) {
-        draw(thisCanvas.getNativeCanvas(), paint.mNativePaint, false /*compositeOnly*/,
+        draw(thisCanvas.getNativeCanvasWrapper(), paint.mNativePaint, false /*compositeOnly*/,
                 false /*forceSrcMode*/, new GcSnapshot.Drawable() {
                     @Override
                     public void draw(Graphics2D graphics, Paint_Delegate paintDelegate) {
@@ -979,7 +979,6 @@
             final float startX, final float startY, final int flags, long paint,
             long typeface) {
 
-        // TODO: use typeface.
         draw(nativeCanvas, paint, false /*compositeOnly*/, false /*forceSrcMode*/,
                 new GcSnapshot.Drawable() {
             @Override
@@ -1097,7 +1096,7 @@
     /**
      * Executes a {@link GcSnapshot.Drawable} with a given canvas and paint.
      * <p>Note that the drawable may actually be executed several times if there are
-     * layers involved (see {@link #saveLayer(RectF, int, int)}.
+     * layers involved (see {@link #saveLayer(RectF, Paint_Delegate, int)}.
      */
     private static void draw(long nCanvas, long nPaint, boolean compositeOnly, boolean forceSrcMode,
             GcSnapshot.Drawable drawable) {
@@ -1117,7 +1116,7 @@
      * Executes a {@link GcSnapshot.Drawable} with a given canvas. No paint object will be provided
      * to {@link GcSnapshot.Drawable#draw(Graphics2D, Paint_Delegate)}.
      * <p>Note that the drawable may actually be executed several times if there are
-     * layers involved (see {@link #saveLayer(RectF, int, int)}.
+     * layers involved (see {@link #saveLayer(RectF, Paint_Delegate, int)}.
      */
     private static void draw(long nCanvas, GcSnapshot.Drawable drawable) {
         // get the delegate from the native int.
@@ -1190,12 +1189,6 @@
         return mSnapshot.clipRect(left, top, right, bottom, regionOp);
     }
 
-    private void setBitmap(Bitmap_Delegate bitmap) {
-        mBitmap = bitmap;
-        assert mSnapshot.size() == 1;
-        mSnapshot.setBitmap(mBitmap);
-    }
-
     private static void drawBitmap(
             long nativeCanvas,
             Bitmap_Delegate bitmap,
diff --git a/tools/layoutlib/bridge/src/android/graphics/ComposeShader_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/ComposeShader_Delegate.java
index fae8aef..59ddcc6 100644
--- a/tools/layoutlib/bridge/src/android/graphics/ComposeShader_Delegate.java
+++ b/tools/layoutlib/bridge/src/android/graphics/ComposeShader_Delegate.java
@@ -78,19 +78,6 @@
         return sManager.addNewDelegate(newDelegate);
     }
 
-    @LayoutlibDelegate
-    /*package*/ static long nativePostCreate1(long native_shader, long native_skiaShaderA,
-            long native_skiaShaderB, long native_mode) {
-        // pass, not needed.
-        return 0;
-    }
-
-    @LayoutlibDelegate
-    /*package*/ static long nativePostCreate2(long native_shader, long native_skiaShaderA,
-            long native_skiaShaderB, int porterDuffMode) {
-        // pass, not needed.
-        return 0;
-    }
 
     // ---- Private delegate/helper methods ----
 
diff --git a/tools/layoutlib/bridge/src/android/graphics/FontFamily_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/FontFamily_Delegate.java
new file mode 100644
index 0000000..9ea4538
--- /dev/null
+++ b/tools/layoutlib/bridge/src/android/graphics/FontFamily_Delegate.java
@@ -0,0 +1,227 @@
+/*
+ * 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.graphics;
+
+import com.android.ide.common.rendering.api.LayoutLog;
+import com.android.layoutlib.bridge.Bridge;
+import com.android.layoutlib.bridge.impl.DelegateManager;
+import com.android.tools.layoutlib.annotations.LayoutlibDelegate;
+
+import java.awt.Font;
+import java.io.File;
+import java.util.ArrayList;
+import java.util.List;
+
+import static android.graphics.Typeface_Delegate.SYSTEM_FONTS;
+
+/**
+ * Delegate implementing the native methods of android.graphics.FontFamily
+ *
+ * Through the layoutlib_create tool, the original native methods of FontFamily have been replaced
+ * by calls to methods of the same name in this delegate class.
+ *
+ * This class behaves like the original native implementation, but in Java, keeping previously
+ * native data into its own objects and mapping them to int that are sent back and forth between
+ * it and the original FontFamily class.
+ *
+ * @see DelegateManager
+ */
+public class FontFamily_Delegate {
+
+    // FONT_SUFFIX_ITALIC will always match FONT_SUFFIX_BOLDITALIC and hence it must be checked
+    // separately.
+    private static final String FONT_SUFFIX_BOLDITALIC = "BoldItalic.ttf";
+    private static final String FONT_SUFFIX_BOLD = "Bold.ttf";
+    private static final String FONT_SUFFIX_ITALIC = "Italic.ttf";
+    private static final String FONT_SUBSTRING_COMPACT = "UI";
+
+    /**
+     * A class associating {@link Font} with its metadata.
+     */
+    private static final class FontInfo {
+        Font mFont;
+        /** Regular, Bold, Italic, or BoldItalic. */
+        int mStyle;
+        /**
+         * The variant of the Font - compact or elegant.
+         * @see Paint#setElegantTextHeight(boolean)
+         */
+        boolean mIsCompact;
+    }
+
+    // ---- delegate manager ----
+    private static final DelegateManager<FontFamily_Delegate> sManager =
+            new DelegateManager<FontFamily_Delegate>(FontFamily_Delegate.class);
+
+    // ---- delegate helper data ----
+    private static String sFontLocation;
+    private static final List<FontFamily_Delegate> sPostInitDelegate = new
+            ArrayList<FontFamily_Delegate>();
+
+
+    // ---- delegate data ----
+    private List<FontInfo> mFonts = new ArrayList<FontInfo>();
+    // Path of fonts that haven't been created since sFontLoader hasn't been initialized.
+    private List<String> mPath = new ArrayList<String>();
+
+
+    // ---- Public Helper methods ----
+
+    public static FontFamily_Delegate getDelegate(long nativeFontFamily) {
+        return sManager.getDelegate(nativeFontFamily);
+    }
+
+    public static synchronized void setFontLocation(String fontLocation) {
+        sFontLocation = fontLocation;
+        for (FontFamily_Delegate fontFamily : sPostInitDelegate) {
+            fontFamily.init();
+        }
+        sPostInitDelegate.clear();
+    }
+
+    public Font getFont(int style, boolean isCompact) {
+        FontInfo plainFont = null;
+        FontInfo styledFont = null;  // Font matching the style but not isCompact
+        for (FontInfo font : mFonts) {
+            if (font.mStyle == style) {
+                if (font.mIsCompact == isCompact) {
+                    return font.mFont;
+                }
+                styledFont = font;
+            }
+            if (font.mStyle == Font.PLAIN) {
+                if (plainFont == null) {
+                    plainFont = font;
+                    continue;
+                }
+                if (font.mIsCompact == isCompact) {
+                    // Override the previous selection of plain font since we've found a better one.
+                    plainFont = font;
+                }
+            }
+        }
+        if (styledFont != null) {
+            return styledFont.mFont;
+        }
+
+        // No font with the mentioned style is found. Try to derive one.
+        if (plainFont != null && style > 0 && style < 4) {
+            styledFont = new FontInfo();
+            styledFont.mFont = plainFont.mFont.deriveFont(style);
+            styledFont.mStyle = style;
+            styledFont.mIsCompact = plainFont.mIsCompact;
+            // Add the font to the list of fonts so that we don't have to derive it the next time.
+            mFonts.add(styledFont);
+            return styledFont.mFont;
+        }
+        return null;
+    }
+
+    // ---- native methods ----
+
+    @LayoutlibDelegate
+    /*package*/ static long nCreateFamily() {
+        FontFamily_Delegate delegate = new FontFamily_Delegate();
+        if (sFontLocation != null) {
+            delegate.init();
+        } else {
+            sPostInitDelegate.add(delegate);
+        }
+        return sManager.addNewDelegate(delegate);
+    }
+
+    @LayoutlibDelegate
+    /*package*/ static void nUnrefFamily(long nativePtr) {
+        // Removing the java reference for the object doesn't mean that it's freed for garbage
+        // collection. Typeface_Delegate may still hold a reference for it.
+        sManager.removeJavaReferenceFor(nativePtr);
+    }
+
+    @LayoutlibDelegate
+    /*package*/ static boolean nAddFont(long nativeFamily, String path) {
+        FontFamily_Delegate delegate = getDelegate(nativeFamily);
+        if (delegate != null) {
+            if (sFontLocation == null) {
+                delegate.mPath.add(path);
+                return true;
+            }
+            return delegate.addFont(path);
+        }
+        return false;
+    }
+
+    private void init() {
+        for (String path : mPath) {
+            addFont(path);
+        }
+        mPath = null;
+    }
+
+    private boolean addFont(String path) {
+        Font font = loadFont(path);
+        if (font == null) {
+            return false;
+        }
+        FontInfo fontInfo = new FontInfo();
+        fontInfo.mFont = font;
+        addFontMetadata(fontInfo, path);
+        // TODO ensure that mFonts doesn't have the font with this style already.
+        mFonts.add(fontInfo);
+        return true;
+    }
+
+    private static void addFontMetadata(FontInfo fontInfo, String path) {
+        int style = Font.PLAIN;
+        String fontName = path.substring(path.lastIndexOf('/'), path.length());
+        if (fontName.endsWith(FONT_SUFFIX_BOLDITALIC)) {
+            style = Font.BOLD | Font.ITALIC;
+        } else if (fontName.endsWith(FONT_SUFFIX_BOLD)) {
+            style = Font.BOLD;
+        } else if (fontName.endsWith(FONT_SUFFIX_ITALIC)) {
+            style = Font.ITALIC;
+        }
+        fontInfo.mStyle = style;
+
+        // Names of compact fonts end with UI-<style>.ttf. For example, NotoNakshUI-Regular.ttf.
+        // This should go away when this info is passed on by nAddFont().
+        int hyphenIndex = fontName.lastIndexOf('-');
+        fontInfo.mIsCompact = hyphenIndex > 0 &&
+                fontName.substring(0, hyphenIndex).endsWith(FONT_SUBSTRING_COMPACT);
+
+    }
+
+    private static Font loadFont(String path) {
+        if (path.startsWith(SYSTEM_FONTS) ) {
+            String relativePath = path.substring(SYSTEM_FONTS.length());
+            File f = new File(sFontLocation, relativePath);
+
+            try {
+                return Font.createFont(Font.TRUETYPE_FONT, f);
+            } catch (Exception e) {
+                Bridge.getLog().fidelityWarning(LayoutLog.TAG_BROKEN,
+                        String.format("Unable to load font %1$s", relativePath),
+                        null /*throwable*/, null /*data*/);
+            }
+        } else {
+            Bridge.getLog().fidelityWarning(LayoutLog.TAG_UNSUPPORTED,
+                    "Only platform fonts located in " + SYSTEM_FONTS + "can be loaded.",
+                    null /*throwable*/, null /*data*/);
+        }
+
+        return null;
+    }
+}
diff --git a/tools/layoutlib/bridge/src/android/graphics/LinearGradient_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/LinearGradient_Delegate.java
index ac77377..55c4b98 100644
--- a/tools/layoutlib/bridge/src/android/graphics/LinearGradient_Delegate.java
+++ b/tools/layoutlib/bridge/src/android/graphics/LinearGradient_Delegate.java
@@ -71,22 +71,6 @@
                 tileMode);
     }
 
-    @LayoutlibDelegate
-    /*package*/ static long nativePostCreate1(LinearGradient thisGradient,
-            long native_shader, float x0, float y0, float x1, float y1,
-            int colors[], float positions[], int tileMode) {
-        // nothing to be done here.
-        return 0;
-    }
-
-    @LayoutlibDelegate
-    /*package*/ static long nativePostCreate2(LinearGradient thisGradient,
-            long native_shader, float x0, float y0, float x1, float y1,
-            int color0, int color1, int tileMode) {
-        // nothing to be done here.
-        return 0;
-    }
-
     // ---- Private delegate/helper methods ----
 
     /**
diff --git a/tools/layoutlib/bridge/src/android/graphics/Matrix_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/Matrix_Delegate.java
index 8862f5b..f42f48f 100644
--- a/tools/layoutlib/bridge/src/android/graphics/Matrix_Delegate.java
+++ b/tools/layoutlib/bridge/src/android/graphics/Matrix_Delegate.java
@@ -203,6 +203,16 @@
     }
 
     @LayoutlibDelegate
+    /*package*/ static boolean native_isAffine(long native_object) {
+        Matrix_Delegate d = sManager.getDelegate(native_object);
+        if (d == null) {
+            return true;
+        }
+
+        return (d.computeTypeMask() & kPerspective_Mask) == 0;
+    }
+
+    @LayoutlibDelegate
     /*package*/ static boolean native_rectStaysRect(long native_object) {
         Matrix_Delegate d = sManager.getDelegate(native_object);
         if (d == null) {
diff --git a/tools/layoutlib/bridge/src/android/graphics/Paint_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/Paint_Delegate.java
index 83df745..911f4e7 100644
--- a/tools/layoutlib/bridge/src/android/graphics/Paint_Delegate.java
+++ b/tools/layoutlib/bridge/src/android/graphics/Paint_Delegate.java
@@ -53,7 +53,7 @@
 public class Paint_Delegate {
 
     /**
-     * Class associating a {@link Font} and it's {@link java.awt.FontMetrics}.
+     * Class associating a {@link Font} and its {@link java.awt.FontMetrics}.
      */
     /*package*/ static final class FontInfo {
         Font mFont;
@@ -66,8 +66,6 @@
 
     // ---- delegate helper data ----
     private List<FontInfo> mFonts;
-    private final FontRenderContext mFontContext = new FontRenderContext(
-            new AffineTransform(), true, true);
 
     // ---- delegate data ----
     private int mFlags;
@@ -83,6 +81,7 @@
     private float mTextScaleX;
     private float mTextSkewX;
     private int mHintingMode = Paint.HINTING_ON;
+    private boolean mIsCompact = true;
 
     private Xfermode_Delegate mXfermode;
     private ColorFilter_Delegate mColorFilter;
@@ -101,8 +100,7 @@
     }
 
     /**
-     * Returns the list of {@link Font} objects. The first item is the main font, the rest
-     * are fall backs for characters not present in the main font.
+     * Returns the list of {@link Font} objects.
      */
     public List<FontInfo> getFonts() {
         return mFonts;
@@ -437,12 +435,20 @@
 
     @LayoutlibDelegate
     /*package*/ static boolean isElegantTextHeight(Paint thisPaint) {
-        return false;
+        // get the delegate from the native int.
+        Paint_Delegate delegate = sManager.getDelegate(thisPaint.mNativePaint);
+        return delegate != null && !delegate.mIsCompact;
     }
 
     @LayoutlibDelegate
     /*package*/ static void setElegantTextHeight(Paint thisPaint, boolean elegant) {
-        // TODO
+        // get the delegate from the native int.
+        Paint_Delegate delegate = sManager.getDelegate(thisPaint.mNativePaint);
+        if (delegate == null) {
+            return;
+        }
+
+        delegate.mIsCompact = !elegant;
     }
 
     @LayoutlibDelegate
@@ -621,7 +627,6 @@
         int inc = count > 0 ? 1 : -1;
 
         int measureIndex = 0;
-        float measureAcc = 0;
         for (int i = index; i != index + count; i += inc, measureIndex++) {
             int start, end;
             if (i < index) {
@@ -640,7 +645,6 @@
                 measuredWidth[measureIndex] = res;
             }
 
-            measureAcc += res;
             if (res > maxWidth) {
                 // we should not return this char index, but since it's 0-based
                 // and we need to return a count, we simply return measureIndex;
@@ -818,7 +822,7 @@
             return filter;
         }
 
-        delegate.mColorFilter = ColorFilter_Delegate.getDelegate(filter);;
+        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) {
@@ -940,52 +944,17 @@
     }
 
     @LayoutlibDelegate
-    /*package*/ static int native_getTextWidths(long native_object, char[] text, int index,
-            int count, int bidiFlags, float[] widths) {
-        // get the delegate from the native int.
-        Paint_Delegate delegate = sManager.getDelegate(native_object);
-        if (delegate == null) {
-            return 0;
-        }
-
-        if (delegate.mFonts.size() > 0) {
-            // FIXME: handle multi-char characters (see measureText)
-            float totalAdvance = 0;
-            for (int i = 0; i < count; i++) {
-                char c = text[i + index];
-                boolean found = false;
-                for (FontInfo info : delegate.mFonts) {
-                    if (info.mFont.canDisplay(c)) {
-                        float adv = info.mMetrics.charWidth(c);
-                        totalAdvance += adv;
-                        if (widths != null) {
-                            widths[i] = adv;
-                        }
-
-                        found = true;
-                        break;
-                    }
-                }
-
-                if (found == false) {
-                    // no advance for this char.
-                    if (widths != null) {
-                        widths[i] = 0.f;
-                    }
-                }
-            }
-
-            return (int) totalAdvance;
-        }
-
-        return 0;
+    /*package*/ static int native_getTextWidths(long native_object, long native_typeface,
+            char[] text, int index, int count, int bidiFlags, float[] widths) {
+        return (int) native_getTextRunAdvances(native_object, native_typeface, text, index, count,
+                index, count, bidiFlags, widths, 0);
     }
 
     @LayoutlibDelegate
-    /*package*/ static int native_getTextWidths(long native_object, String text, int start,
-            int end, int bidiFlags, float[] widths) {
-        return native_getTextWidths(native_object, text.toCharArray(), start, end - start,
-                bidiFlags, widths);
+    /*package*/ static int native_getTextWidths(long native_object, long native_typeface,
+            String text, int start, int end, int bidiFlags, float[] widths) {
+        return native_getTextWidths(native_object, native_typeface, text.toCharArray(), start,
+                end - start, bidiFlags, widths);
     }
 
     @LayoutlibDelegate
@@ -997,15 +966,20 @@
 
     @LayoutlibDelegate
     /*package*/ static float native_getTextRunAdvances(long native_object,
+            long native_typeface /*ignored*/,
             char[] text, int index, int count, int contextIndex, int contextCount,
             int flags, float[] advances, int advancesIndex) {
 
+        // native_typeface is passed here since Framework's old implementation did not have the
+        // typeface object associated with the Paint. Since, we follow the new framework way,
+        // we store the typeface with the paint and use it directly.
+
         if (advances != null)
             for (int i = advancesIndex; i< advancesIndex+count; i++)
                 advances[i]=0;
         // get the delegate from the native int.
         Paint_Delegate delegate = sManager.getDelegate(native_object);
-        if (delegate == null || delegate.mFonts == null || delegate.mFonts.size() == 0) {
+        if (delegate == null) {
             return 0.f;
         }
         boolean isRtl = isRtl(flags);
@@ -1017,7 +991,7 @@
     }
 
     @LayoutlibDelegate
-    /*package*/ static float native_getTextRunAdvances(long native_object,
+    /*package*/ static float native_getTextRunAdvances(long native_object, long native_typeface,
             String text, int start, int end, int contextStart, int contextEnd,
             int flags, float[] advances, int advancesIndex) {
         // FIXME: support contextStart and contextEnd
@@ -1025,8 +999,8 @@
         char[] buffer = TemporaryBuffer.obtain(count);
         TextUtils.getChars(text, start, end, buffer, 0);
 
-        return native_getTextRunAdvances(native_object, buffer, 0, count, contextStart,
-                contextEnd - contextStart, flags, advances, advancesIndex);
+        return native_getTextRunAdvances(native_object, native_typeface, buffer, 0, count,
+                contextStart, contextEnd - contextStart, flags, advances, advancesIndex);
     }
 
     @LayoutlibDelegate
@@ -1076,7 +1050,7 @@
 
         // get the delegate from the native int.
         Paint_Delegate delegate = sManager.getDelegate(nativePaint);
-        if (delegate == null || delegate.mFonts == null || delegate.mFonts.size() == 0) {
+        if (delegate == null) {
             return;
         }
         delegate.measureText(text, index, count, isRtl(bidiFlags)).roundOut(bounds);
@@ -1150,7 +1124,7 @@
     private void updateFontObject() {
         if (mTypeface != null) {
             // Get the fonts from the TypeFace object.
-            List<Font> fonts = mTypeface.getFonts();
+            List<Font> fonts = mTypeface.getFonts(mIsCompact);
 
             // create new font objects as well as FontMetrics, based on the current text size
             // and skew info.
diff --git a/tools/layoutlib/bridge/src/android/graphics/RadialGradient_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/RadialGradient_Delegate.java
index 4f16dcf..80179ee 100644
--- a/tools/layoutlib/bridge/src/android/graphics/RadialGradient_Delegate.java
+++ b/tools/layoutlib/bridge/src/android/graphics/RadialGradient_Delegate.java
@@ -68,20 +68,6 @@
                 tileMode);
     }
 
-    @LayoutlibDelegate
-    /*package*/ static long nativePostCreate1(long native_shader, float x, float y, float radius,
-            int colors[], float positions[], int tileMode) {
-        // nothing to be done here.
-        return 0;
-    }
-
-    @LayoutlibDelegate
-    /*package*/ static long nativePostCreate2(long native_shader, float x, float y, float radius,
-            int color0, int color1, int tileMode) {
-        // nothing to be done here.
-        return 0;
-    }
-
     // ---- Private delegate/helper methods ----
 
     /**
diff --git a/tools/layoutlib/bridge/src/android/graphics/Shader_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/Shader_Delegate.java
index 70a0a43..14e9960 100644
--- a/tools/layoutlib/bridge/src/android/graphics/Shader_Delegate.java
+++ b/tools/layoutlib/bridge/src/android/graphics/Shader_Delegate.java
@@ -76,13 +76,12 @@
     // ---- native methods ----
 
     @LayoutlibDelegate
-    /*package*/ static void nativeDestructor(long native_shader, long native_skiaShader) {
+    /*package*/ static void nativeDestructor(long native_shader) {
         sManager.removeJavaReferenceFor(native_shader);
     }
 
     @LayoutlibDelegate
-    /*package*/ static void nativeSetLocalMatrix(long native_shader, long native_skiaShader,
-            long matrix_instance) {
+    /*package*/ static void nativeSetLocalMatrix(long native_shader, long matrix_instance) {
         // get the delegate from the native int.
         Shader_Delegate shaderDelegate = sManager.getDelegate(native_shader);
         if (shaderDelegate == null) {
diff --git a/tools/layoutlib/bridge/src/android/graphics/SweepGradient_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/SweepGradient_Delegate.java
index f2b3e8d..95a57a9 100644
--- a/tools/layoutlib/bridge/src/android/graphics/SweepGradient_Delegate.java
+++ b/tools/layoutlib/bridge/src/android/graphics/SweepGradient_Delegate.java
@@ -62,20 +62,6 @@
         return nativeCreate1(x, y, new int[] { color0, color1 }, null /*positions*/);
     }
 
-    @LayoutlibDelegate
-    /*package*/ static long nativePostCreate1(long native_shader, float cx, float cy,
-            int[] colors, float[] positions) {
-        // nothing to be done here.
-        return 0;
-    }
-
-    @LayoutlibDelegate
-    /*package*/ static long nativePostCreate2(long native_shader, float cx, float cy,
-            int color0, int color1) {
-        // nothing to be done here.
-        return 0;
-    }
-
     // ---- Private delegate/helper methods ----
 
     /**
diff --git a/tools/layoutlib/bridge/src/android/graphics/Typeface_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/Typeface_Delegate.java
index 60cd157..9746b48 100644
--- a/tools/layoutlib/bridge/src/android/graphics/Typeface_Delegate.java
+++ b/tools/layoutlib/bridge/src/android/graphics/Typeface_Delegate.java
@@ -19,7 +19,6 @@
 import com.android.ide.common.rendering.api.LayoutLog;
 import com.android.layoutlib.bridge.Bridge;
 import com.android.layoutlib.bridge.impl.DelegateManager;
-import com.android.layoutlib.bridge.impl.FontLoader;
 import com.android.tools.layoutlib.annotations.LayoutlibDelegate;
 
 import android.content.res.AssetManager;
@@ -44,100 +43,66 @@
  */
 public final class Typeface_Delegate {
 
-    private static final String SYSTEM_FONTS = "/system/fonts/";
+    public static final String SYSTEM_FONTS = "/system/fonts/";
 
     // ---- delegate manager ----
     private static final DelegateManager<Typeface_Delegate> sManager =
             new DelegateManager<Typeface_Delegate>(Typeface_Delegate.class);
 
     // ---- delegate helper data ----
-    private static final String DEFAULT_FAMILY = "sans-serif";
-
-    private static FontLoader sFontLoader;
-    private static final List<Typeface_Delegate> sPostInitDelegate =
-            new ArrayList<Typeface_Delegate>();
+    private static String sFontLocation;
 
     // ---- delegate data ----
 
-    private final String mFamily;
+    private final FontFamily_Delegate[] mFontFamilies;  // the reference to FontFamily_Delegate.
     private int mStyle;
-    private List<Font> mFonts;
 
+    private static long sDefaultTypeface;
 
     // ---- Public Helper methods ----
-
-    public static synchronized void init(FontLoader fontLoader) {
-        sFontLoader = fontLoader;
-
-        for (Typeface_Delegate delegate : sPostInitDelegate) {
-            delegate.init();
-        }
-        sPostInitDelegate.clear();
+    public static synchronized void setFontLocation(String fontLocation) {
+        sFontLocation = fontLocation;
+        FontFamily_Delegate.setFontLocation(fontLocation);
     }
 
     public static Typeface_Delegate getDelegate(long nativeTypeface) {
         return sManager.getDelegate(nativeTypeface);
     }
 
-    public static List<Font> getFonts(Typeface typeface) {
-        return getFonts(typeface.native_instance);
-    }
-
-    public static List<Font> getFonts(long native_int) {
-        Typeface_Delegate delegate = sManager.getDelegate(native_int);
-        if (delegate == null) {
-            return null;
+    public List<Font> getFonts(boolean compact) {
+        List<Font> fonts = new ArrayList<Font>(mFontFamilies.length);
+        for (FontFamily_Delegate ffd : mFontFamilies) {
+            if (ffd != null) {
+                Font font = ffd.getFont(mStyle, compact);
+                if (font != null) {
+                    fonts.add(font);
+                }
+            }
         }
-
-        return delegate.getFonts();
-    }
-
-    public List<Font> getFonts() {
-        return mFonts;
+        return fonts;
     }
 
     // ---- native methods ----
 
     @LayoutlibDelegate
     /*package*/ static synchronized long nativeCreate(String familyName, int style) {
-        if (familyName == null) {
-            familyName = DEFAULT_FAMILY;
-        }
-        if (style < 0) {
-            style = Typeface.NORMAL;
-        }
-
-        Typeface_Delegate newDelegate = new Typeface_Delegate(familyName, style);
-        if (sFontLoader != null) {
-            newDelegate.init();
-        } else {
-            // font loader has not been initialized yet, add the delegate to a list of delegates
-            // to init when the font loader is initialized.
-            // There won't be any rendering before this happens anyway.
-            sPostInitDelegate.add(newDelegate);
-        }
-
-        return sManager.addNewDelegate(newDelegate);
+        Bridge.getLog().fidelityWarning(LayoutLog.TAG_UNSUPPORTED,
+                "Could not find font with family \"" + familyName + "\".",
+                null /*throwable*/, null /*data*/);
+        return 0;
     }
 
     @LayoutlibDelegate
     /*package*/ static synchronized long nativeCreateFromTypeface(long native_instance, int style) {
         Typeface_Delegate delegate = sManager.getDelegate(native_instance);
         if (delegate == null) {
+            delegate = sManager.getDelegate(sDefaultTypeface);
+        }
+        if (delegate == null) {
             return 0;
         }
 
-        Typeface_Delegate newDelegate = new Typeface_Delegate(delegate.mFamily, style);
-        if (sFontLoader != null) {
-            newDelegate.init();
-        } else {
-            // font loader has not been initialized yet, add the delegate to a list of delegates
-            // to init when the font loader is initialized.
-            // There won't be any rendering before this happens anyway.
-            sPostInitDelegate.add(newDelegate);
-        }
-
-        return sManager.addNewDelegate(newDelegate);
+        return sManager.addNewDelegate(new Typeface_Delegate(delegate.mFontFamilies, style));
     }
 
     @LayoutlibDelegate
@@ -149,31 +114,19 @@
 
     @LayoutlibDelegate
     /*package*/ static synchronized long nativeCreateFromFile(String path) {
-        if (path.startsWith(SYSTEM_FONTS) ) {
-            String relativePath = path.substring(SYSTEM_FONTS.length());
-            File f = new File(sFontLoader.getOsFontsLocation(), relativePath);
+        Bridge.getLog().fidelityWarning(LayoutLog.TAG_UNSUPPORTED,
+                "Typeface.createFromFile() is not supported.,", null, null);
+        return 0;
+    }
 
-            try {
-                Font font = Font.createFont(Font.TRUETYPE_FONT, f);
-                if (font != null) {
-                    Typeface_Delegate newDelegate = new Typeface_Delegate(font);
-                    return sManager.addNewDelegate(newDelegate);
-                }
-            } catch (Exception e) {
-                Bridge.getLog().fidelityWarning(LayoutLog.TAG_BROKEN,
-                        String.format("Unable to load font %1$s", relativePath),
-                            null /*throwable*/, null /*data*/);
-            }
-        } else {
-            Bridge.getLog().fidelityWarning(LayoutLog.TAG_UNSUPPORTED,
-                    "Typeface.createFromFile() can only work with platform fonts located in " +
-                        SYSTEM_FONTS,
-                    null /*throwable*/, null /*data*/);
+    @LayoutlibDelegate
+    /*package*/ static synchronized long nativeCreateFromArray(long[] familyArray) {
+        FontFamily_Delegate[] fontFamilies = new FontFamily_Delegate[familyArray.length];
+        for (int i = 0; i < familyArray.length; i++) {
+            fontFamilies[i] = FontFamily_Delegate.getDelegate(familyArray[i]);
         }
-
-
-        // return a copy of the base font
-        return nativeCreate(null, 0);
+        Typeface_Delegate delegate = new Typeface_Delegate(fontFamilies, Typeface.NORMAL);
+        return sManager.addNewDelegate(delegate);
     }
 
     @LayoutlibDelegate
@@ -191,24 +144,20 @@
         return delegate.mStyle;
     }
 
+    @LayoutlibDelegate
+    /*package*/ static void nativeSetDefault(long native_instance) {
+        sDefaultTypeface = native_instance;
+    }
+
+    @LayoutlibDelegate
+    /*package*/ static File getSystemFontConfigLocation() {
+        return new File(sFontLocation);
+    }
+
     // ---- Private delegate/helper methods ----
 
-    private Typeface_Delegate(String family, int style) {
-        mFamily = family;
+    private Typeface_Delegate(FontFamily_Delegate[] fontFamilies, int style) {
+        mFontFamilies = fontFamilies;
         mStyle = style;
     }
-
-    private Typeface_Delegate(Font font) {
-        mFamily = font.getFamily();
-        mStyle = Typeface.NORMAL;
-
-        mFonts = sFontLoader.getFallbackFonts(mStyle);
-
-        // insert the font glyph first.
-        mFonts.add(0, font);
-    }
-
-    private void init() {
-        mFonts = sFontLoader.getFont(mFamily, mStyle);
-    }
 }
diff --git a/tools/layoutlib/bridge/src/android/util/Xml_Delegate.java b/tools/layoutlib/bridge/src/android/util/Xml_Delegate.java
new file mode 100644
index 0000000..a193330
--- /dev/null
+++ b/tools/layoutlib/bridge/src/android/util/Xml_Delegate.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.util;
+
+import com.android.layoutlib.bridge.impl.DelegateManager;
+import com.android.tools.layoutlib.annotations.LayoutlibDelegate;
+
+import org.kxml2.io.KXmlParser;
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+
+/**
+ * Delegate overriding some methods of android.util.Xml
+ *
+ * Through the layoutlib_create tool, the original methods of Xml have been replaced
+ * by calls to methods of the same name in this delegate class.
+ *
+ * Because it's a stateless class to start with, there's no need to keep a {@link DelegateManager}
+ * around to map int to instance of the delegate.
+ */
+public class Xml_Delegate {
+
+    @LayoutlibDelegate
+    /*package*/ static XmlPullParser newPullParser() {
+        try {
+            KXmlParser parser = new KXmlParser();
+            // The prebuilt kxml2 library with the IDE doesn't support DOCECL.
+//            parser.setFeature(XmlPullParser.FEATURE_PROCESS_DOCDECL, true);
+            parser.setFeature(XmlPullParser.FEATURE_PROCESS_NAMESPACES, true);
+            return parser;
+        } catch (XmlPullParserException e) {
+            throw new AssertionError();
+        }
+    }
+}
diff --git a/tools/layoutlib/bridge/src/android/view/IWindowManagerImpl.java b/tools/layoutlib/bridge/src/android/view/IWindowManagerImpl.java
index 757cdd2..3bf2b20 100644
--- a/tools/layoutlib/bridge/src/android/view/IWindowManagerImpl.java
+++ b/tools/layoutlib/bridge/src/android/view/IWindowManagerImpl.java
@@ -72,7 +72,7 @@
 
     @Override
     public void addAppToken(int arg0, IApplicationToken arg1, int arg2, int arg3, int arg4,
-            boolean arg5, boolean arg6, int arg7, int arg8)
+            boolean arg5, boolean arg6, int arg7, int arg8, boolean arg9)
             throws RemoteException {
         // TODO Auto-generated method stub
 
@@ -439,6 +439,10 @@
     }
 
     @Override
+    public void keyguardGoingAway() throws RemoteException {
+    }
+
+    @Override
     public void lockNow(Bundle options) {
         // TODO Auto-generated method stub
     }
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/Bridge.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/Bridge.java
index fa8050f..ffab4de 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/Bridge.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/Bridge.java
@@ -26,7 +26,6 @@
 import com.android.ide.common.rendering.api.Result;
 import com.android.ide.common.rendering.api.Result.Status;
 import com.android.ide.common.rendering.api.SessionParams;
-import com.android.layoutlib.bridge.impl.FontLoader;
 import com.android.layoutlib.bridge.impl.RenderDrawable;
 import com.android.layoutlib.bridge.impl.RenderSessionImpl;
 import com.android.layoutlib.bridge.util.DynamicIdMap;
@@ -61,7 +60,7 @@
 /**
  * Main entry point of the LayoutLib Bridge.
  * <p/>To use this bridge, simply instantiate an object of type {@link Bridge} and call
- * {@link #createScene(SceneParams)}
+ * {@link #createSession(SessionParams)}
  */
 public final class Bridge extends com.android.ide.common.rendering.api.Bridge {
 
@@ -147,8 +146,7 @@
             if (getClass() != obj.getClass()) return false;
 
             IntArray other = (IntArray) obj;
-            if (!Arrays.equals(mArray, other.mArray)) return false;
-            return true;
+            return Arrays.equals(mArray, other.mArray);
         }
     }
 
@@ -251,14 +249,7 @@
         }
 
         // load the fonts.
-        FontLoader fontLoader = FontLoader.create(fontLocation.getAbsolutePath());
-        if (fontLoader != null) {
-            Typeface_Delegate.init(fontLoader);
-        } else {
-            log.error(LayoutLog.TAG_BROKEN,
-                    "Failed create FontLoader in layout lib.", null);
-            return false;
-        }
+        Typeface_Delegate.setFontLocation(fontLocation.getAbsolutePath());
 
         // now parse com.android.internal.R (and only this one as android.R is a subset of
         // the internal version), and put the content in the maps.
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 d31239b..5c51c63 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
@@ -1466,4 +1466,10 @@
         // pass
         return new File[0];
     }
+
+    @Override
+    public File[] getExternalMediaDirs() {
+        // pass
+        return new File[0];
+    }
 }
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/ActionBarLayout.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/ActionBarLayout.java
index 936ab4f..e59ccd7 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/ActionBarLayout.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/ActionBarLayout.java
@@ -171,7 +171,7 @@
             // Set action bar to be split, if needed.
             ActionBarContainer splitView = (ActionBarContainer) findViewById(R.id.split_action_bar);
             mActionBarView.setSplitView(splitView);
-            mActionBarView.setSplitActionBar(mSplit);
+            mActionBarView.setSplitToolbar(mSplit);
 
             inflateMenus();
 
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/FontLoader.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/FontLoader.java
deleted file mode 100644
index cc7338a..0000000
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/FontLoader.java
+++ /dev/null
@@ -1,398 +0,0 @@
-/*
- * 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 com.android.layoutlib.bridge.impl;
-
-import org.xml.sax.Attributes;
-import org.xml.sax.SAXException;
-import org.xml.sax.helpers.DefaultHandler;
-
-import android.graphics.Typeface;
-
-import java.awt.Font;
-import java.io.File;
-import java.io.FileInputStream;
-import java.io.FileNotFoundException;
-import java.io.IOException;
-import java.util.ArrayList;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Set;
-
-import javax.xml.parsers.ParserConfigurationException;
-import javax.xml.parsers.SAXParser;
-import javax.xml.parsers.SAXParserFactory;
-
-/**
- * Provides {@link Font} object to the layout lib.
- * <p/>
- * The fonts are loaded from the SDK directory. Family/style mapping is done by parsing the
- * fonts.xml file located alongside the ttf files.
- */
-public final class FontLoader {
-    private static final String FONTS_SYSTEM = "system_fonts.xml";
-    private static final String FONTS_VENDOR = "vendor_fonts.xml";
-    private static final String FONTS_FALLBACK = "fallback_fonts.xml";
-
-    private static final String NODE_FAMILYSET = "familyset";
-    private static final String NODE_FAMILY = "family";
-    private static final String NODE_NAME = "name";
-    private static final String NODE_FILE = "file";
-
-    private static final String ATTRIBUTE_VARIANT = "variant";
-    private static final String ATTRIBUTE_VALUE_ELEGANT = "elegant";
-    private static final String FONT_SUFFIX_NONE = ".ttf";
-    private static final String FONT_SUFFIX_REGULAR = "-Regular.ttf";
-    private static final String FONT_SUFFIX_BOLD = "-Bold.ttf";
-    // FONT_SUFFIX_ITALIC will always match FONT_SUFFIX_BOLDITALIC and hence it must be checked
-    // separately.
-    private static final String FONT_SUFFIX_ITALIC = "Italic.ttf";
-    private static final String FONT_SUFFIX_BOLDITALIC = "-BoldItalic.ttf";
-
-    // This must match the values of Typeface styles so that we can use them for indices in this
-    // array.
-    private static final int[] AWT_STYLES = new int[] {
-        Font.PLAIN,
-        Font.BOLD,
-        Font.ITALIC,
-        Font.BOLD | Font.ITALIC
-    };
-    private static int[] DERIVE_BOLD_ITALIC = new int[] {
-        Typeface.ITALIC, Typeface.BOLD, Typeface.NORMAL
-    };
-    private static int[] DERIVE_ITALIC = new int[] { Typeface.NORMAL };
-    private static int[] DERIVE_BOLD = new int[] { Typeface.NORMAL };
-
-    private static final List<FontInfo> mMainFonts = new ArrayList<FontInfo>();
-    private static final List<FontInfo> mFallbackFonts = new ArrayList<FontInfo>();
-
-    private final String mOsFontsLocation;
-
-    public static FontLoader create(String fontOsLocation) {
-        try {
-            SAXParserFactory parserFactory = SAXParserFactory.newInstance();
-                parserFactory.setNamespaceAware(true);
-
-            // parse the system fonts
-            FontHandler handler = parseFontFile(parserFactory, fontOsLocation, FONTS_SYSTEM);
-            List<FontInfo> systemFonts = handler.getFontList();
-
-
-            // parse the fallback fonts
-            handler = parseFontFile(parserFactory, fontOsLocation, FONTS_FALLBACK);
-            List<FontInfo> fallbackFonts = handler.getFontList();
-
-            return new FontLoader(fontOsLocation, systemFonts, fallbackFonts);
-        } catch (ParserConfigurationException e) {
-            // return null below
-        } catch (SAXException e) {
-            // return null below
-        } catch (FileNotFoundException e) {
-            // return null below
-        } catch (IOException e) {
-            // return null below
-        }
-
-        return null;
-    }
-
-    private static FontHandler parseFontFile(SAXParserFactory parserFactory,
-            String fontOsLocation, String fontFileName)
-            throws ParserConfigurationException, SAXException, IOException, FileNotFoundException {
-
-        SAXParser parser = parserFactory.newSAXParser();
-        File f = new File(fontOsLocation, fontFileName);
-
-        FontHandler definitionParser = new FontHandler(
-                fontOsLocation + File.separator);
-        parser.parse(new FileInputStream(f), definitionParser);
-        return definitionParser;
-    }
-
-    private FontLoader(String fontOsLocation,
-            List<FontInfo> fontList, List<FontInfo> fallBackList) {
-        mOsFontsLocation = fontOsLocation;
-        mMainFonts.addAll(fontList);
-        mFallbackFonts.addAll(fallBackList);
-    }
-
-
-    public String getOsFontsLocation() {
-        return mOsFontsLocation;
-    }
-
-    /**
-     * Returns a {@link Font} object given a family name and a style value (constant in
-     * {@link Typeface}).
-     * @param family the family name
-     * @param style a 1-item array containing the requested style. Based on the font being read
-     *              the actual style may be different. The array contains the actual style after
-     *              the method returns.
-     * @return the font object or null if no match could be found.
-     */
-    public synchronized List<Font> getFont(String family, int style) {
-        List<Font> result = new ArrayList<Font>();
-
-        if (family == null) {
-            return result;
-        }
-
-
-        // get the font objects from the main list based on family.
-        for (FontInfo info : mMainFonts) {
-            if (info.families.contains(family)) {
-                result.add(info.font[style]);
-                break;
-            }
-        }
-
-        // add all the fallback fonts for the given style
-        for (FontInfo info : mFallbackFonts) {
-            result.add(info.font[style]);
-        }
-
-        return result;
-    }
-
-
-    public synchronized List<Font> getFallbackFonts(int style) {
-        List<Font> result = new ArrayList<Font>();
-        // add all the fallback fonts
-        for (FontInfo info : mFallbackFonts) {
-            result.add(info.font[style]);
-        }
-        return result;
-    }
-
-
-    private final static class FontInfo {
-        final Font[] font = new Font[4]; // Matches the 4 type-face styles.
-        final Set<String> families;
-
-        FontInfo() {
-            families = new HashSet<String>();
-        }
-    }
-
-    private final static class FontHandler extends DefaultHandler {
-        private final String mOsFontsLocation;
-
-        private FontInfo mFontInfo = null;
-        private final StringBuilder mBuilder = new StringBuilder();
-        private List<FontInfo> mFontList = new ArrayList<FontInfo>();
-        private boolean isCompactFont = true;
-
-        private FontHandler(String osFontsLocation) {
-            super();
-            mOsFontsLocation = osFontsLocation;
-        }
-
-        public List<FontInfo> getFontList() {
-            return mFontList;
-        }
-
-        /* (non-Javadoc)
-         * @see org.xml.sax.helpers.DefaultHandler#startElement(java.lang.String, java.lang.String, java.lang.String, org.xml.sax.Attributes)
-         */
-        @Override
-        public void startElement(String uri, String localName, String name, Attributes attributes)
-                throws SAXException {
-            if (NODE_FAMILYSET.equals(localName)) {
-                mFontList = new ArrayList<FontInfo>();
-            } else if (NODE_FAMILY.equals(localName)) {
-                if (mFontList != null) {
-                    mFontInfo = null;
-                }
-            } else if (NODE_NAME.equals(localName)) {
-                if (mFontList != null && mFontInfo == null) {
-                    mFontInfo = new FontInfo();
-                }
-            } else if (NODE_FILE.equals(localName)) {
-                if (mFontList != null && mFontInfo == null) {
-                    mFontInfo = new FontInfo();
-                }
-                if (ATTRIBUTE_VALUE_ELEGANT.equals(attributes.getValue(ATTRIBUTE_VARIANT))) {
-                    isCompactFont = false;
-                } else {
-                    isCompactFont = true;
-                }
-            }
-
-            mBuilder.setLength(0);
-
-            super.startElement(uri, localName, name, attributes);
-        }
-
-        /* (non-Javadoc)
-         * @see org.xml.sax.helpers.DefaultHandler#characters(char[], int, int)
-         */
-        @Override
-        public void characters(char[] ch, int start, int length) throws SAXException {
-            if (isCompactFont) {
-              mBuilder.append(ch, start, length);
-            }
-        }
-
-        /* (non-Javadoc)
-         * @see org.xml.sax.helpers.DefaultHandler#endElement(java.lang.String, java.lang.String, java.lang.String)
-         */
-        @Override
-        public void endElement(String uri, String localName, String name) throws SAXException {
-            if (NODE_FAMILY.equals(localName)) {
-                if (mFontInfo != null) {
-                    // if has a normal font file, add to the list
-                    if (mFontInfo.font[Typeface.NORMAL] != null) {
-                        mFontList.add(mFontInfo);
-
-                        // create missing font styles, order is important.
-                        if (mFontInfo.font[Typeface.BOLD_ITALIC] == null) {
-                            computeDerivedFont(Typeface.BOLD_ITALIC, DERIVE_BOLD_ITALIC);
-                        }
-                        if (mFontInfo.font[Typeface.ITALIC] == null) {
-                            computeDerivedFont(Typeface.ITALIC, DERIVE_ITALIC);
-                        }
-                        if (mFontInfo.font[Typeface.BOLD] == null) {
-                            computeDerivedFont(Typeface.BOLD, DERIVE_BOLD);
-                        }
-                    }
-
-                    mFontInfo = null;
-                }
-            } else if (NODE_NAME.equals(localName)) {
-                // handle a new name for an existing Font Info
-                if (mFontInfo != null) {
-                    String family = trimXmlWhitespaces(mBuilder.toString());
-                    mFontInfo.families.add(family);
-                }
-            } else if (NODE_FILE.equals(localName)) {
-                // handle a new file for an existing Font Info
-                if (isCompactFont && mFontInfo != null) {
-                    String fileName = trimXmlWhitespaces(mBuilder.toString());
-                    Font font = getFont(fileName);
-                    if (font != null) {
-                        if (fileName.endsWith(FONT_SUFFIX_REGULAR)) {
-                            mFontInfo.font[Typeface.NORMAL] = font;
-                        } else if (fileName.endsWith(FONT_SUFFIX_BOLD)) {
-                            mFontInfo.font[Typeface.BOLD] = font;
-                        } else if (fileName.endsWith(FONT_SUFFIX_BOLDITALIC)) {
-                            mFontInfo.font[Typeface.BOLD_ITALIC] = font;
-                        } else if (fileName.endsWith(FONT_SUFFIX_ITALIC)) {
-                            mFontInfo.font[Typeface.ITALIC] = font;
-                        } else if (fileName.endsWith(FONT_SUFFIX_NONE)) {
-                            mFontInfo.font[Typeface.NORMAL] = font;
-                        }
-                    }
-                }
-            }
-        }
-
-        private Font getFont(String fileName) {
-            try {
-                File file = new File(mOsFontsLocation, fileName);
-                if (file.exists()) {
-                    return Font.createFont(Font.TRUETYPE_FONT, file);
-                }
-            } catch (Exception e) {
-
-            }
-
-            return null;
-        }
-
-        private void computeDerivedFont( int toCompute, int[] basedOnList) {
-            for (int basedOn : basedOnList) {
-                if (mFontInfo.font[basedOn] != null) {
-                    mFontInfo.font[toCompute] =
-                        mFontInfo.font[basedOn].deriveFont(AWT_STYLES[toCompute]);
-                    return;
-                }
-            }
-
-            // we really shouldn't stop there. This means we don't have a NORMAL font...
-            assert false;
-        }
-
-        private String trimXmlWhitespaces(String value) {
-            if (value == null) {
-                return null;
-            }
-
-            // look for carriage return and replace all whitespace around it by just 1 space.
-            int index;
-
-            while ((index = value.indexOf('\n')) != -1) {
-                // look for whitespace on each side
-                int left = index - 1;
-                while (left >= 0) {
-                    if (Character.isWhitespace(value.charAt(left))) {
-                        left--;
-                    } else {
-                        break;
-                    }
-                }
-
-                int right = index + 1;
-                int count = value.length();
-                while (right < count) {
-                    if (Character.isWhitespace(value.charAt(right))) {
-                        right++;
-                    } else {
-                        break;
-                    }
-                }
-
-                // remove all between left and right (non inclusive) and replace by a single space.
-                String leftString = null;
-                if (left >= 0) {
-                    leftString = value.substring(0, left + 1);
-                }
-                String rightString = null;
-                if (right < count) {
-                    rightString = value.substring(right);
-                }
-
-                if (leftString != null) {
-                    value = leftString;
-                    if (rightString != null) {
-                        value += " " + rightString;
-                    }
-                } else {
-                    value = rightString != null ? rightString : "";
-                }
-            }
-
-            // now we un-escape the string
-            int length = value.length();
-            char[] buffer = value.toCharArray();
-
-            for (int i = 0 ; i < length ; i++) {
-                if (buffer[i] == '\\') {
-                    if (buffer[i+1] == 'n') {
-                        // replace the char with \n
-                        buffer[i+1] = '\n';
-                    }
-
-                    // offset the rest of the buffer since we go from 2 to 1 char
-                    System.arraycopy(buffer, i+1, buffer, i, length - i - 1);
-                    length--;
-                }
-            }
-
-            return new String(buffer, 0, length);
-        }
-
-    }
-}
diff --git a/tools/layoutlib/create/src/com/android/tools/layoutlib/create/CreateInfo.java b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/CreateInfo.java
index bb72a1e..1f7a28e 100644
--- a/tools/layoutlib/create/src/com/android/tools/layoutlib/create/CreateInfo.java
+++ b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/CreateInfo.java
@@ -125,15 +125,19 @@
         "android.app.Fragment#instantiate", //(Landroid/content/Context;Ljava/lang/String;Landroid/os/Bundle;)Landroid/app/Fragment;",
         "android.content.res.Resources$Theme#obtainStyledAttributes",
         "android.content.res.Resources$Theme#resolveAttribute",
+        "android.content.res.Resources$Theme#resolveAttributes",
         "android.content.res.Resources#localeToLanguageTag",
         "android.content.res.AssetManager#newTheme",
         "android.content.res.AssetManager#deleteTheme",
         "android.content.res.AssetManager#applyThemeStyle",
         "android.content.res.TypedArray#getValueAt",
+        "android.content.res.TypedArray#obtain",
         "android.graphics.BitmapFactory#finishDecode",
+        "android.graphics.Typeface#getSystemFontConfigLocation",
         "android.os.Handler#sendMessageAtTime",
         "android.os.HandlerThread#run",
         "android.text.format.DateFormat#is24HourFormat",
+        "android.util.Xml#newPullParser",
         "android.view.Choreographer#getRefreshRate",
         "android.view.Display#updateDisplayInfoLocked",
         "android.view.Display#getWindowManager",
@@ -170,6 +174,7 @@
         "android.graphics.DiscretePathEffect",
         "android.graphics.DrawFilter",
         "android.graphics.EmbossMaskFilter",
+        "android.graphics.FontFamily",
         "android.graphics.LayerRasterizer",
         "android.graphics.LightingColorFilter",
         "android.graphics.LinearGradient",
diff --git a/tools/layoutlib/rename_font/build_font.py b/tools/layoutlib/rename_font/build_font.py
index ea3dccc..aea3241 100755
--- a/tools/layoutlib/rename_font/build_font.py
+++ b/tools/layoutlib/rename_font/build_font.py
@@ -15,10 +15,10 @@
 # limitations under the License.
 
 """
-Rename the PS name of all fonts in the input directory and copy them to the
+Rename the PS name of all fonts in the input directories and copy them to the
 output directory.
 
-Usage: build_font.py /path/to/input_fonts/ /path/to/output_fonts/
+Usage: build_font.py /path/to/input_fonts1/ /path/to/input_fonts2/ /path/to/output_fonts/
 
 """
 
@@ -30,50 +30,86 @@
 from lxml import etree
 import shutil
 import glob
+from multiprocessing import Pool
+
+# global variable
+dest_dir = '/tmp'
 
 def main(argv):
-  if len(argv) != 2:
-    print "Usage: build_font.py /path/to/input_fonts/ /path/to/out/dir/"
-    sys.exit(1)
-  if not os.path.isdir(argv[0]):
-    print argv[0] + "is not a valid directory"
-    sys.exit(1)
-  if not os.path.isdir(argv[1]):
-    print argv[1] + "is not a valid directory"
-    sys.exit(1)
+  if len(argv) < 2:
+    sys.exit('Usage: build_font.py /path/to/input_fonts/ /path/to/out/dir/')
+  for directory in argv:
+    if not os.path.isdir(directory):
+      sys.exit(directory + ' is not a valid directory')
+  global dest_dir
+  dest_dir = argv[-1]
+  src_dirs = argv[:-1]
   cwd = os.getcwd()
-  os.chdir(argv[1])
+  os.chdir(dest_dir)
   files = glob.glob('*')
   for filename in files:
     os.remove(filename)
   os.chdir(cwd)
-  for filename in os.listdir(argv[0]):
-    if not os.path.splitext(filename)[1].lower() == ".ttf":
-      shutil.copy(os.path.join(argv[0], filename), argv[1])
-      continue
-    print os.path.join(argv[0], filename)
-    old_ttf_path = os.path.join(argv[0], filename)
+  input_fonts = list()
+  for src_dir in src_dirs:
+    for dirname, dirnames, filenames in os.walk(src_dir):
+      for filename in filenames:
+          input_path = os.path.join(dirname, filename)
+          extension = os.path.splitext(filename)[1].lower()
+          if (extension == '.ttf'):
+            input_fonts.append(input_path)
+          elif (extension == '.xml'):
+            shutil.copy(input_path, dest_dir)
+      if '.git' in dirnames:
+          # don't go into any .git directories.
+          dirnames.remove('.git')
+  # Create as many threads as the number of CPUs
+  pool = Pool(processes=None)
+  pool.map(convert_font, input_fonts)
+
+
+class InvalidFontException(Exception):
+  pass
+
+def convert_font(input_path):
+  filename = os.path.basename(input_path)
+  print 'Converting font: ' + filename
+  # the path to the output file. The file name is the fontfilename.ttx
+  ttx_path = os.path.join(dest_dir, filename)
+  ttx_path = ttx_path[:-1] + 'x'
+  try:
     # run ttx to generate an xml file in the output folder which represents all
     # its info
-    ttx_args = ["-d", argv[1], old_ttf_path]
+    ttx_args = ['-q', '-d', dest_dir, input_path]
     ttx.main(ttx_args)
-    # the path to the output file. The file name is the fontfilename.ttx
-    ttx_path = os.path.join(argv[1], filename)
-    ttx_path = ttx_path[:-1] + "x"
     # now parse the xml file to change its PS name.
     tree = etree.parse(ttx_path)
     encoding = tree.docinfo.encoding
     root = tree.getroot()
     for name in root.iter('name'):
       [old_ps_name, version] = get_font_info(name)
-      new_ps_name = old_ps_name + version
-      update_name(name, new_ps_name)
+      if old_ps_name is not None and version is not None:
+        new_ps_name = old_ps_name + version
+        update_name(name, new_ps_name)
     tree.write(ttx_path, xml_declaration=True, encoding=encoding )
     # generate the udpated font now.
-    ttx_args = ["-d", argv[1], ttx_path]
+    ttx_args = ['-q', '-d', dest_dir, ttx_path]
     ttx.main(ttx_args)
-    # delete the temp ttx file.
+  except InvalidFontException:
+    # In case of invalid fonts, we exit.
+    print filename + ' is not a valid font'
+    raise
+  except Exception as e:
+    print 'Error converting font: ' + filename
+    print e
+    # Some fonts are too big to be handled by the ttx library.
+    # Just copy paste them.
+    shutil.copy(input_path, dest_dir)
+  try:
+    # delete the temp ttx file is it exists.
     os.remove(ttx_path)
+  except OSError:
+    pass
 
 def get_font_info(tag):
   ps_name = None
@@ -85,19 +121,17 @@
       if namerecord.attrib['nameID'] == '6':
         if ps_name is not None:
           if not sanitize(namerecord.text) == ps_name:
-            sys.exit('found multiple possibilities of the font name')
+            raise InvalidFontException('found multiple possibilities of the font name')
         else:
           ps_name = sanitize(namerecord.text)
       # nameID=5 means the font version
       if namerecord.attrib['nameID'] == '5':
         if ps_version is not None:
           if not ps_version == get_version(namerecord.text):
-            sys.exit('found multiple possibilities of the font version')
+            raise InvalidFontException('found multiple possibilities of the font version')
         else:
           ps_version = get_version(namerecord.text)
-  if ps_name is not None and ps_version is not None:
-    return [ps_name, ps_version]
-  sys.exit('didn\'t find the font name or version')
+  return [ps_name, ps_version]
 
 
 def update_name(tag, name):
@@ -110,11 +144,11 @@
   return re.sub(r'[^\w-]+', '', string)
 
 def get_version(string):
-  # The string must begin with "Version n.nn "
+  # The string must begin with 'Version n.nn '
   # to extract n.nn, we return the second entry in the split strings.
   string = string.strip()
-  if not string.startswith("Version "):
-    sys.exit('mal-formed font version')
+  if not string.startswith('Version '):
+    raise InvalidFontException('mal-formed font version')
   return sanitize(string.split()[1])
 
 if __name__ == '__main__':
diff --git a/wifi/java/android/net/wifi/ScanResult.java b/wifi/java/android/net/wifi/ScanResult.java
index 58b0d61..99151c3 100644
--- a/wifi/java/android/net/wifi/ScanResult.java
+++ b/wifi/java/android/net/wifi/ScanResult.java
@@ -90,6 +90,19 @@
      */
     public final static int UNSPECIFIED = -1;
 
+    /** information element from beacon
+     * @hide
+     */
+    public static class InformationElement {
+        public int id;
+        public byte[] bytes;
+    }
+
+    /** information elements found in the beacon
+     * @hide
+     */
+    public InformationElement informationElements[];
+
     /** {@hide} */
     public ScanResult(WifiSsid wifiSsid, String BSSID, String caps, int level, int frequency,
             long tsf) {
@@ -131,8 +144,7 @@
             distanceCm = source.distanceCm;
             distanceSdCm = source.distanceSdCm;
             seen = source.seen;
-            if (source.passpoint != null)
-                passpoint = new WifiPasspointInfo(source.passpoint);
+            passpoint = source.passpoint;
         }
     }
 
@@ -166,8 +178,7 @@
         sb.append(", distanceSd: ").append((distanceSdCm != UNSPECIFIED ? distanceSdCm : "?")).
                 append("(cm)");
 
-        if (passpoint != null)
-            sb.append(", passpoint: [").append(passpoint.toString()).append("]");
+        sb.append(", passpoint: ").append(passpoint != null ? "yes" : "no");
 
         return sb.toString();
     }
@@ -199,6 +210,16 @@
         } else {
             dest.writeInt(0);
         }
+        if (informationElements != null) {
+            dest.writeInt(informationElements.length);
+            for (int i = 0; i < informationElements.length; i++) {
+                dest.writeInt(informationElements[i].id);
+                dest.writeInt(informationElements[i].bytes.length);
+                dest.writeByteArray(informationElements[i].bytes);
+            }
+        } else {
+            dest.writeInt(0);
+        }
     }
 
     /** Implement the Parcelable interface {@hide} */
@@ -223,6 +244,17 @@
                 if (in.readInt() == 1) {
                     sr.passpoint = WifiPasspointInfo.CREATOR.createFromParcel(in);
                 }
+                int n = in.readInt();
+                if (n != 0) {
+                    sr.informationElements = new InformationElement[n];
+                    for (int i = 0; i < n; i++) {
+                        sr.informationElements[i] = new InformationElement();
+                        sr.informationElements[i].id = in.readInt();
+                        int len = in.readInt();
+                        sr.informationElements[i].bytes = new byte[len];
+                        in.readByteArray(sr.informationElements[i].bytes);
+                    }
+                }
                 return sr;
             }
 
@@ -230,5 +262,4 @@
                 return new ScanResult[size];
             }
         };
-
 }
diff --git a/wifi/java/android/net/wifi/WifiConfiguration.java b/wifi/java/android/net/wifi/WifiConfiguration.java
index 1157de7..5dfc318 100644
--- a/wifi/java/android/net/wifi/WifiConfiguration.java
+++ b/wifi/java/android/net/wifi/WifiConfiguration.java
@@ -216,6 +216,18 @@
      * <code>XX:XX:XX:XX:XX:XX</code> where each <code>X</code> is a hex digit.
      */
     public String BSSID;
+    /**
+     * Fully qualified domain name (FQDN), for Passpoint credential.
+     * e.g. {@code "mail.example.com"}.
+     * @hide
+     */
+    public String FQDN;
+    /**
+     * Network access identifier (NAI) realm, for Passpoint credential.
+     * e.g. {@code "myhost.example.com"}.
+     * @hide
+     */
+    public String naiRealm;
 
     /**
      * Pre-shared key for use with WPA-PSK.
@@ -312,6 +324,24 @@
 
     /**
      * @hide
+     * Uid of app creating the configuration
+     */
+    public int creatorUid;
+
+    /**
+     * @hide
+     * Uid of last app issuing a connection related command
+     */
+    public int lastConnectUid;
+
+    /**
+     * @hide
+     * Uid of last app modifying the configuration
+     */
+    public int lastUpdateUid;
+
+    /**
+     * @hide
      * BSSID list on which this configuration was seen.
      * TODO: prevent this list to grow infinitely, age-out the results
      */
@@ -429,10 +459,17 @@
     /** @hide
      * if this is set, the WifiConfiguration cannot use linkages so as to bump
      * it's relative priority.
+     * - status between and 128 indicate various level of blacklisting depending
+     * on the severity or frequency of the connection error
+     * - deleted status indicates that the user is deleting the configuration, and so
+     * although it may have been self added we will not re-self-add it, ignore it,
+     * not return it to applications, and not connect to it
      * */
     public static final int AUTO_JOIN_TEMPORARY_DISABLED  = 1;
     /** @hide */
-    public static final int AUTO_JOIN_DISABLED_ON_AUTH_FAILURE  = 2;
+    public static final int AUTO_JOIN_DISABLED_ON_AUTH_FAILURE  = 128;
+    /** @hide */
+    public static final int AUTO_JOIN_DELETED  = 200;
 
     /**
      * @hide
@@ -441,11 +478,29 @@
 
     /**
      * Set if the configuration was self added by the framework
+     * This boolean is cleared if we get a connect/save/ update or
+     * any wifiManager command that indicate the user interacted with the configuration
+     * since we will now consider that the configuration belong to him.
      * @hide
      */
     public boolean selfAdded;
 
     /**
+     * Set if the configuration was self added by the framework
+     * This boolean is set once and never cleared. It is used
+     * so as we never loose track of who created the
+     * configuration in the first place.
+     * @hide
+     */
+    public boolean didSelfAdd;
+
+    /**
+     * peer WifiConfiguration this WifiConfiguration was added for
+     * @hide
+     */
+    public String peerWifiConfiguration;
+
+    /**
      * @hide
      * Indicate that a WifiConfiguration is temporary and should not be saved
      * nor considered by AutoJoin.
@@ -484,6 +539,8 @@
         networkId = INVALID_NETWORK_ID;
         SSID = null;
         BSSID = null;
+        FQDN = null;
+        naiRealm = null;
         priority = 0;
         hiddenSSID = false;
         disableReason = DISABLED_UNKNOWN_REASON;
@@ -499,6 +556,7 @@
         enterpriseConfig = new WifiEnterpriseConfig();
         autoJoinStatus = AUTO_JOIN_ENABLED;
         selfAdded = false;
+        didSelfAdd = false;
         ephemeral = false;
         mIpConfiguration = new IpConfiguration();
     }
@@ -565,7 +623,8 @@
             sbuf.append("- DSBLE: ").append(this.disableReason).append(" ");
         }
         sbuf.append("ID: ").append(this.networkId).append(" SSID: ").append(this.SSID).
-                append(" BSSID: ").append(this.BSSID).append(" PRIO: ").append(this.priority).
+                append(" BSSID: ").append(this.BSSID).append(" FQDN: ").append(this.FQDN).
+                append(" REALM: ").append(this.naiRealm).append(" PRIO: ").append(this.priority).
                 append('\n');
         sbuf.append(" KeyMgmt:");
         for (int k = 0; k < this.allowedKeyManagement.size(); k++) {
@@ -635,6 +694,10 @@
 
         sbuf.append(mIpConfiguration.toString());
 
+        if (selfAdded)  sbuf.append("selfAdded");
+        if (creatorUid != 0)  sbuf.append("uid=" + Integer.toString(creatorUid));
+
+
         return sbuf.toString();
     }
 
@@ -868,8 +931,11 @@
             networkId = source.networkId;
             status = source.status;
             disableReason = source.disableReason;
+            disableReason = source.disableReason;
             SSID = source.SSID;
             BSSID = source.BSSID;
+            FQDN = source.FQDN;
+            naiRealm = source.naiRealm;
             preSharedKey = source.preSharedKey;
 
             wepKeys = new String[4];
@@ -916,6 +982,11 @@
             }
 
             lastFailure = source.lastFailure;
+            didSelfAdd = source.didSelfAdd;
+            lastConnectUid = source.lastConnectUid;
+            lastUpdateUid = source.lastUpdateUid;
+            creatorUid = source.creatorUid;
+            peerWifiConfiguration = source.peerWifiConfiguration;
         }
     }
 
@@ -932,6 +1003,8 @@
         dest.writeInt(disableReason);
         dest.writeString(SSID);
         dest.writeString(BSSID);
+        dest.writeString(FQDN);
+        dest.writeString(naiRealm);
         dest.writeString(preSharedKey);
         for (String wepKey : wepKeys) {
             dest.writeString(wepKey);
@@ -953,6 +1026,10 @@
         dest.writeString(defaultGwMacAddress);
         dest.writeInt(autoJoinStatus);
         dest.writeInt(selfAdded ? 1 : 0);
+        dest.writeInt(didSelfAdd ? 1 : 0);
+        dest.writeInt(creatorUid);
+        dest.writeInt(lastConnectUid);
+        dest.writeInt(lastUpdateUid);
         /*
         TODO: should we write the cache results to the parcel?
         if (scanResultCache != null) {
@@ -976,6 +1053,8 @@
                 config.disableReason = in.readInt();
                 config.SSID = in.readString();
                 config.BSSID = in.readString();
+                config.FQDN = in.readString();
+                config.naiRealm = in.readString();
                 config.preSharedKey = in.readString();
                 for (int i = 0; i < config.wepKeys.length; i++) {
                     config.wepKeys[i] = in.readString();
@@ -996,6 +1075,10 @@
                 config.defaultGwMacAddress = in.readString();
                 config.autoJoinStatus = in.readInt();
                 config.selfAdded = in.readInt() != 0;
+                config.didSelfAdd = in.readInt() != 0;
+                config.creatorUid = in.readInt();
+                config.lastConnectUid = in.readInt();
+                config.lastUpdateUid = in.readInt();
                 /*
                 TODO: should we write the cache results to the parcel?
                 boolean done = false;
diff --git a/wifi/java/android/net/wifi/WifiScanner.java b/wifi/java/android/net/wifi/WifiScanner.java
index 9ea7027..3b65ca8 100644
--- a/wifi/java/android/net/wifi/WifiScanner.java
+++ b/wifi/java/android/net/wifi/WifiScanner.java
@@ -40,6 +40,7 @@
  * Get an instance of this class by calling
  * {@link android.content.Context#getSystemService(String) Context.getSystemService(Context
  * .WIFI_SCANNING_SERVICE)}.
+ * @hide
  */
 public class WifiScanner {
 
@@ -72,16 +73,14 @@
     public static final int REASON_INVALID_LISTENER = -2;
     /** Invalid request */
     public static final int REASON_INVALID_REQUEST = -3;
-    /** Request conflicts with other scans that may be going on */
-    public static final int REASON_CONFLICTING_REQUEST = -4;
 
     /**
      * Generic action callback invocation interface
      *  @hide
      */
     public static interface ActionListener {
-        public void onSuccess(Object result);
-        public void onFailure(int reason, Object exception);
+        public void onSuccess();
+        public void onFailure(int reason, String description);
     }
 
     /**
@@ -193,58 +192,6 @@
 
     }
 
-    /** information element from beacon */
-    public static class InformationElement {
-        public int id;
-        public byte[] bytes;
-    }
-
-    /** scan result with information elements from beacons */
-    public static class FullScanResult implements Parcelable {
-        public ScanResult result;
-        public InformationElement informationElements[];
-
-        /** Implement the Parcelable interface {@hide} */
-        public int describeContents() {
-            return 0;
-        }
-
-        /** Implement the Parcelable interface {@hide} */
-        public void writeToParcel(Parcel dest, int flags) {
-            result.writeToParcel(dest, flags);
-            dest.writeInt(informationElements.length);
-            for (int i = 0; i < informationElements.length; i++) {
-                dest.writeInt(informationElements[i].id);
-                dest.writeInt(informationElements[i].bytes.length);
-                dest.writeByteArray(informationElements[i].bytes);
-            }
-        }
-
-        /** Implement the Parcelable interface {@hide} */
-        public static final Creator<FullScanResult> CREATOR =
-                new Creator<FullScanResult>() {
-                    public FullScanResult createFromParcel(Parcel in) {
-                        FullScanResult result = new FullScanResult();
-                        result.result = ScanResult.CREATOR.createFromParcel(in);
-                        int n = in.readInt();
-                        result.informationElements = new InformationElement[n];
-                        for (int i = 0; i < n; i++) {
-                            result.informationElements[i] = new InformationElement();
-                            result.informationElements[i].id = in.readInt();
-                            int len = in.readInt();
-                            result.informationElements[i].bytes = new byte[len];
-                            in.readByteArray(result.informationElements[i].bytes);
-                        }
-
-                        return result;
-                    }
-
-                    public FullScanResult[] newArray(int size) {
-                        return new FullScanResult[size];
-                    }
-                };
-    }
-
     /** @hide */
     public static class ParcelableScanResults implements Parcelable {
         public ScanResult mResults[];
@@ -305,7 +252,7 @@
         /**
          * reports full scan result for each access point found in scan
          */
-        public void onFullResult(FullScanResult fullScanResult);
+        public void onFullResult(ScanResult fullScanResult);
     }
 
     /** @hide */
@@ -336,13 +283,12 @@
     }
     /**
      * retrieves currently available scan results
-     * @param flush {@code true} means flush all results
-     * @param listener specifies which scan to cancel; must be same object as passed in {@link
-     *                 #startBackgroundScan}
      */
-    public void retrieveScanResults(boolean flush, ScanListener listener) {
+    public ScanResult[] getScanResults() {
         validateChannel();
-        sAsyncChannel.sendMessage(CMD_GET_SCAN_RESULTS, 0, getListenerKey(listener));
+        Message reply = sAsyncChannel.sendMessageSynchronously(CMD_GET_SCAN_RESULTS, 0);
+        ScanResult[] results = (ScanResult[]) reply.obj;
+        return results;
     }
 
     /** specifies information about an access point of interest */
@@ -490,7 +436,7 @@
     }
 
     /** interface to receive hotlist events on; use this on {@link #setHotlist} */
-    public static interface HotlistListener extends ActionListener {
+    public static interface HotspotListener extends ActionListener {
         /** indicates that access points were found by on going scans
          * @param results list of scan results, one for each access point visible currently
          */
@@ -550,10 +496,10 @@
      * @param hotspots access points of interest
      * @param apLostThreshold number of scans needed to indicate that AP is lost
      * @param listener object provided to report events on; this object must be unique and must
-     *                 also be provided on {@link #resetHotlist}
+     *                 also be provided on {@link #stopTrackingHotspots}
      */
-    public void setHotlist(HotspotInfo[] hotspots,
-            int apLostThreshold, HotlistListener listener) {
+    public void startTrackingHotspots(HotspotInfo[] hotspots,
+            int apLostThreshold, HotspotListener listener) {
         validateChannel();
         HotlistSettings settings = new HotlistSettings();
         settings.hotspotInfos = hotspots;
@@ -562,9 +508,9 @@
 
     /**
      * remove tracking of interesting access points
-     * @param listener same object provided in {@link #setHotlist}
+     * @param listener same object provided in {@link #startTrackingHotspots}
      */
-    public void resetHotlist(HotlistListener listener) {
+    public void stopTrackingHotspots(HotspotListener listener) {
         validateChannel();
         sAsyncChannel.sendMessage(CMD_RESET_HOTLIST, 0, removeListener(listener));
     }
@@ -769,10 +715,10 @@
             switch (msg.what) {
                     /* ActionListeners grouped together */
                 case CMD_OP_SUCCEEDED :
-                    ((ActionListener) listener).onSuccess(msg.obj);
+                    ((ActionListener) listener).onSuccess();
                     break;
                 case CMD_OP_FAILED :
-                    ((ActionListener) listener).onFailure(msg.arg1, msg.obj);
+                    ((ActionListener) listener).onFailure(msg.arg1, (String)msg.obj);
                     removeListener(msg.arg2);
                     break;
                 case CMD_SCAN_RESULT :
@@ -780,14 +726,14 @@
                             ((ParcelableScanResults) msg.obj).getResults());
                     return;
                 case CMD_FULL_SCAN_RESULT :
-                    FullScanResult result = (FullScanResult) msg.obj;
+                    ScanResult result = (ScanResult) msg.obj;
                     ((ScanListener) listener).onFullResult(result);
                     return;
                 case CMD_PERIOD_CHANGED:
                     ((ScanListener) listener).onPeriodChanged(msg.arg1);
                     return;
                 case CMD_AP_FOUND:
-                    ((HotlistListener) listener).onFound(
+                    ((HotspotListener) listener).onFound(
                             ((ParcelableScanResults) msg.obj).getResults());
                     return;
                 case CMD_WIFI_CHANGE_DETECTED:
diff --git a/wifi/java/android/net/wifi/passpoint/WifiPasspointCredential.java b/wifi/java/android/net/wifi/passpoint/WifiPasspointCredential.java
index 090ac56..54ac71e 100644
--- a/wifi/java/android/net/wifi/passpoint/WifiPasspointCredential.java
+++ b/wifi/java/android/net/wifi/passpoint/WifiPasspointCredential.java
@@ -20,75 +20,91 @@
 import android.os.Parcelable;
 import android.os.Parcel;
 
+import java.util.ArrayList;
 import java.util.Collection;
-import java.util.Set;
+import java.util.List;
 import java.util.Iterator;
 import java.util.Map;
+import java.util.Set;
+
 
 /**
  * A class representing a Wi-Fi Passpoint credential.
+ * @hide
  */
 public class WifiPasspointCredential implements Parcelable {
 
     private final static String TAG = "PasspointCredential";
-    private String mWifiTreePath;
-    private String mWifiSPFQDN;
+    private final static boolean DBG = true;
+
+    /** Wi-Fi nodes**/
+    private String mWifiSpFqdn;
+
+    /** PerProviderSubscription nodes **/
     private String mCredentialName;
-    private String mUpdateIdentifier;
+
+    /** SubscriptionUpdate nodes **/
+    private String mSubscriptionUpdateInterval;
     private String mSubscriptionUpdateMethod;
-    private WifiEnterpriseConfig mEnterpriseConfig;
+    private String mSubscriptionUpdateRestriction;
+    private String mSubscriptionUpdateURI;
+    private String mSubscriptionUpdateUsername;
+    private String mSubscriptionUpdatePassword;
+
+    /** HomeSP nodes **/
+    private String mHomeSpFqdn;
+    private String mFriendlyName;
+    private Collection<WifiPasspointDmTree.HomeOIList> mHomeOIList;
+    private Collection<WifiPasspointDmTree.OtherHomePartners> mOtherHomePartnerList;
+
+    /** SubscriptionParameters nodes**/
+    private String mCreationDate;
+    private String mExpirationDate;
+
+    /** Credential nodes **/
     private String mType;
     private String mInnerMethod;
     private String mCertType;
     private String mCertSha256Fingerprint;
+    private String mUpdateIdentifier;
     private String mUsername;
     private String mPasswd;
+    private String mRealm;
     private String mImsi;
     private String mMcc;
     private String mMnc;
     private String mCaRootCert;
-    private String mRealm;
-    private int mPriority; //User preferred priority; The smaller, the higher
-    private boolean mUserPreferred = false;
-    private String mHomeSpFqdn;
-    private String mFriendlyName;
-    private String mOtherhomepartnerFqdn;
     private String mClientCert;
-    private String mCreationDate;
-    private String mExpirationDate;
-
-    private String mSubscriptionDMAccUsername;
-    private String mSubscriptionDMAccPassword;
-    private String mSubscriptionUpdateInterval;
-
-    private String mPolicyUpdateURI;
-    private String mPolicyUpdateInterval;
-    private String mPolicyDMAccUsername;
-    private String mPolicyDMAccPassword;
-    private String mPolicyUpdateRestriction;
-    private String mPolicyUpdateMethod;
-
-    private Collection<WifiPasspointDmTree.PreferredRoamingPartnerList> mPreferredRoamingPartnerList;
-    private Collection<WifiPasspointDmTree.HomeOIList> mHomeOIList;
-    private Collection<WifiPasspointDmTree.MinBackhaulThresholdNetwork> mMinBackhaulThresholdNetwork;
-    private Collection<WifiPasspointDmTree.RequiredProtoPortTuple> mRequiredProtoPortTuple;
-    private Collection<WifiPasspointDmTree.SPExclusionList> mSpExclusionList;
-    private String mMaxBssLoad;
-
-    private boolean mIsMachineRemediation;
-
-    private String mAAACertURL;
-    private String mAAASha256Fingerprint;
-
-    private String mSubscriptionUpdateRestriction;
-    private String mSubscriptionUpdateURI;
-
     private boolean mCheckAaaServerCertStatus;
 
-    /** @hide */
-    public WifiPasspointCredential() {
+    /** Policy nodes **/
+    private String mPolicyUpdateUri;
+    private String mPolicyUpdateInterval;
+    private String mPolicyUpdateUsername;
+    private String mPolicyUpdatePassword;
+    private String mPolicyUpdateRestriction;
+    private String mPolicyUpdateMethod;
+    private Collection<WifiPasspointDmTree.PreferredRoamingPartnerList> mPreferredRoamingPartnerList;
+    private Collection<WifiPasspointDmTree.MinBackhaulThresholdNetwork> mMinBackhaulThresholdNetwork;
+    private Collection<WifiPasspointDmTree.SPExclusionList> mSpExclusionList;
+    private Collection<WifiPasspointDmTree.RequiredProtoPortTuple> mRequiredProtoPortTuple;
+    private String mMaxBssLoad;
 
-    }
+    /** CrednetialPriority node **/
+    private int mCrednetialPriority;
+
+    /** AAAServerTrustRoot nodes **/
+    private String mAaaCertUrl;
+    private String mAaaSha256Fingerprint;
+
+    /** Others **/
+    private boolean mIsMachineRemediation;
+    private boolean mUserPreferred = false;
+    private String mWifiTreePath;
+    private WifiEnterpriseConfig mEnterpriseConfig;
+
+    /** @hide */
+    public WifiPasspointCredential() {}
 
     /**
      * Constructor
@@ -113,84 +129,6 @@
     public WifiPasspointCredential(String type,
             String caroot,
             String clientcert,
-            WifiPasspointDmTree.SpFqdn sp,
-            WifiPasspointDmTree.CredentialInfo credinfo) {
-
-        if (credinfo == null) {
-            return;
-        }
-
-        mType = type;
-        mCaRootCert = caroot;
-        mClientCert = clientcert;
-
-        mWifiSPFQDN = sp.nodeName;
-        mUpdateIdentifier = sp.perProviderSubscription.UpdateIdentifier;
-
-        mCredentialName = credinfo.nodeName;
-        Set set = credinfo.homeSP.otherHomePartners.entrySet();
-        Iterator i = set.iterator();
-        if (i.hasNext()) {
-            Map.Entry entry3 = (Map.Entry) i.next();
-            WifiPasspointDmTree.OtherHomePartners ohp = (WifiPasspointDmTree.OtherHomePartners) entry3.getValue();
-            mOtherhomepartnerFqdn = ohp.FQDN;
-        }
-
-        set = credinfo.aAAServerTrustRoot.entrySet();
-        i = set.iterator();
-        if (i.hasNext()) {
-            Map.Entry entry3 = (Map.Entry) i.next();
-            WifiPasspointDmTree.AAAServerTrustRoot aaa = (WifiPasspointDmTree.AAAServerTrustRoot) entry3.getValue();
-            mAAACertURL = aaa.CertURL;
-            mAAASha256Fingerprint = aaa.CertSHA256Fingerprint;
-        }
-
-        mCertType = credinfo.credential.digitalCertificate.CertificateType;
-        mCertSha256Fingerprint = credinfo.credential.digitalCertificate.CertSHA256Fingerprint;
-        mUsername = credinfo.credential.usernamePassword.Username;
-        mPasswd = credinfo.credential.usernamePassword.Password;
-        mIsMachineRemediation = credinfo.credential.usernamePassword.MachineManaged;
-        mInnerMethod = credinfo.credential.usernamePassword.eAPMethod.InnerMethod;
-        mImsi = credinfo.credential.sim.IMSI;
-        mCreationDate = credinfo.credential.CreationDate;
-        mExpirationDate = credinfo.credential.ExpirationDate;
-        mRealm = credinfo.credential.Realm;
-
-        if (credinfo.credentialPriority == null) {
-            credinfo.credentialPriority = "128";
-        }
-        mPriority = Integer.parseInt(credinfo.credentialPriority);
-
-        mHomeSpFqdn = credinfo.homeSP.FQDN;
-
-        mSubscriptionUpdateInterval = credinfo.subscriptionUpdate.UpdateInterval;
-        mSubscriptionUpdateMethod = credinfo.subscriptionUpdate.UpdateMethod;
-        mSubscriptionUpdateRestriction = credinfo.subscriptionUpdate.Restriction;
-        mSubscriptionUpdateURI = credinfo.subscriptionUpdate.URI;
-        mSubscriptionDMAccUsername = credinfo.subscriptionUpdate.usernamePassword.Username;
-        mSubscriptionDMAccPassword = credinfo.subscriptionUpdate.usernamePassword.Password;
-
-        mPolicyUpdateURI = credinfo.policy.policyUpdate.URI;
-        mPolicyUpdateInterval = credinfo.policy.policyUpdate.UpdateInterval;
-        mPolicyDMAccUsername = credinfo.policy.policyUpdate.usernamePassword.Username;
-        mPolicyDMAccPassword = credinfo.policy.policyUpdate.usernamePassword.Password;
-        mPolicyUpdateRestriction = credinfo.policy.policyUpdate.Restriction;
-        mPolicyUpdateMethod = credinfo.policy.policyUpdate.UpdateMethod;
-        mPreferredRoamingPartnerList = credinfo.policy.preferredRoamingPartnerList.values();
-        mMinBackhaulThresholdNetwork = credinfo.policy.minBackhaulThreshold.values();
-        mRequiredProtoPortTuple = credinfo.policy.requiredProtoPortTuple.values();
-        mMaxBssLoad = credinfo.policy.maximumBSSLoadValue;
-        mSpExclusionList = credinfo.policy.sPExclusionList.values();
-
-        mHomeOIList = credinfo.homeSP.homeOIList.values();
-        mFriendlyName = credinfo.homeSP.FriendlyName;
-        mCheckAaaServerCertStatus = credinfo.credential.CheckAAAServerCertStatus;
-    }
-
-    /** @hide */
-    public WifiPasspointCredential(String type,
-            String caroot,
-            String clientcert,
             String mcc,
             String mnc,
             WifiPasspointDmTree.SpFqdn sp,
@@ -204,25 +142,19 @@
         mCaRootCert = caroot;
         mClientCert = clientcert;
 
-        mWifiSPFQDN = sp.nodeName;
+        mWifiSpFqdn = sp.nodeName;
         mUpdateIdentifier = sp.perProviderSubscription.UpdateIdentifier;
 
         mCredentialName = credinfo.nodeName;
-        Set set = credinfo.homeSP.otherHomePartners.entrySet();
+        mOtherHomePartnerList = credinfo.homeSP.otherHomePartners.values();
+
+        Set set = credinfo.aAAServerTrustRoot.entrySet();
         Iterator i = set.iterator();
         if (i.hasNext()) {
             Map.Entry entry3 = (Map.Entry) i.next();
-            WifiPasspointDmTree.OtherHomePartners ohp = (WifiPasspointDmTree.OtherHomePartners) entry3.getValue();
-            mOtherhomepartnerFqdn = ohp.FQDN;
-        }
-
-        set = credinfo.aAAServerTrustRoot.entrySet();
-        i = set.iterator();
-        if (i.hasNext()) {
-            Map.Entry entry3 = (Map.Entry) i.next();
             WifiPasspointDmTree.AAAServerTrustRoot aaa = (WifiPasspointDmTree.AAAServerTrustRoot) entry3.getValue();
-            mAAACertURL = aaa.CertURL;
-            mAAASha256Fingerprint = aaa.CertSHA256Fingerprint;
+            mAaaCertUrl = aaa.CertURL;
+            mAaaSha256Fingerprint = aaa.CertSHA256Fingerprint;
         }
 
         mCertType = credinfo.credential.digitalCertificate.CertificateType;
@@ -239,22 +171,24 @@
         mRealm = credinfo.credential.Realm;
 
         if (credinfo.credentialPriority == null) {
-            credinfo.credentialPriority = "128";
+            mCrednetialPriority = 128;
+        } else {
+            mCrednetialPriority = Integer.parseInt(credinfo.credentialPriority);
         }
-        mPriority = Integer.parseInt(credinfo.credentialPriority);
 
         mHomeSpFqdn = credinfo.homeSP.FQDN;
 
+        mSubscriptionUpdateInterval = credinfo.subscriptionUpdate.UpdateInterval;
         mSubscriptionUpdateMethod = credinfo.subscriptionUpdate.UpdateMethod;
         mSubscriptionUpdateRestriction = credinfo.subscriptionUpdate.Restriction;
         mSubscriptionUpdateURI = credinfo.subscriptionUpdate.URI;
-        mSubscriptionDMAccUsername = credinfo.subscriptionUpdate.usernamePassword.Username;
-        mSubscriptionDMAccPassword = credinfo.subscriptionUpdate.usernamePassword.Password;
+        mSubscriptionUpdateUsername = credinfo.subscriptionUpdate.usernamePassword.Username;
+        mSubscriptionUpdatePassword = credinfo.subscriptionUpdate.usernamePassword.Password;
 
-        mPolicyUpdateURI = credinfo.policy.policyUpdate.URI;
+        mPolicyUpdateUri = credinfo.policy.policyUpdate.URI;
         mPolicyUpdateInterval = credinfo.policy.policyUpdate.UpdateInterval;
-        mPolicyDMAccUsername = credinfo.policy.policyUpdate.usernamePassword.Username;
-        mPolicyDMAccPassword = credinfo.policy.policyUpdate.usernamePassword.Password;
+        mPolicyUpdateUsername = credinfo.policy.policyUpdate.usernamePassword.Username;
+        mPolicyUpdatePassword = credinfo.policy.policyUpdate.usernamePassword.Password;
         mPolicyUpdateRestriction = credinfo.policy.policyUpdate.Restriction;
         mPolicyUpdateMethod = credinfo.policy.policyUpdate.UpdateMethod;
         mPreferredRoamingPartnerList = credinfo.policy.preferredRoamingPartnerList.values();
@@ -265,6 +199,7 @@
 
         mHomeOIList = credinfo.homeSP.homeOIList.values();
         mFriendlyName = credinfo.homeSP.FriendlyName;
+        mCheckAaaServerCertStatus = credinfo.credential.CheckAAAServerCertStatus;
     }
 
     /** @hide */
@@ -283,8 +218,8 @@
     }
 
     /** @hide */
-    public String getWifiSPFQDN() {
-        return mWifiSPFQDN;
+    public String getWifiSpFqdn() {
+        return mWifiSpFqdn;
     }
 
     /** @hide */
@@ -293,7 +228,7 @@
     }
 
     /** @hide */
-    public String getEapMethodStr() {
+    public String getType() {
         return mType;
     }
 
@@ -383,14 +318,14 @@
             return 0;
         }
 
-        return mPriority;
+        return mCrednetialPriority;
     }
 
     /**
      * Get the fully qualified domain name (FQDN) of this Passpoint credential.
      * @return FQDN
      */
-    public String getFqdn() {
+    public String getHomeSpFqdn() {
         return mHomeSpFqdn;
     }
 
@@ -404,23 +339,23 @@
 
 
     /** @hide */
-    public String getOtherhomepartners() {
-        return mOtherhomepartnerFqdn;
+    public Collection<WifiPasspointDmTree.OtherHomePartners> getOtherHomePartnerList() {
+        return mOtherHomePartnerList;
     }
 
     /** @hide */
-    public String getSubscriptionDMAccUsername() {
-        return mSubscriptionDMAccUsername;
+    public String getSubscriptionUpdateUsername() {
+        return mSubscriptionUpdateUsername;
     }
 
     /** @hide */
-    public String getSubscriptionDMAccPassword() {
-        return mSubscriptionDMAccPassword;
+    public String getSubscriptionUpdatePassword() {
+        return mSubscriptionUpdatePassword;
     }
 
     /** @hide */
-    public String getPolicyUpdateURI() {
-        return mPolicyUpdateURI;
+    public String getPolicyUpdateUri() {
+        return mPolicyUpdateUri;
     }
 
     /** @hide */
@@ -429,13 +364,13 @@
     }
 
     /** @hide */
-    public String getPolicyDMAccUsername() {
-        return mPolicyDMAccUsername;
+    public String getPolicyUpdateUsername() {
+        return mPolicyUpdateUsername;
     }
 
     /** @hide */
-    public String getPolicyDMAccPassword() {
-        return mPolicyDMAccPassword;
+    public String getPolicyUpdatePassword() {
+        return mPolicyUpdatePassword;
     }
 
     /** @hide */
@@ -464,12 +399,12 @@
     }
 
     /** @hide */
-    public Collection<WifiPasspointDmTree.PreferredRoamingPartnerList> getPrpList() {
+    public Collection<WifiPasspointDmTree.PreferredRoamingPartnerList> getPreferredRoamingPartnerList() {
         return mPreferredRoamingPartnerList;
     }
 
     /** @hide */
-    public Collection<WifiPasspointDmTree.HomeOIList> getHomeOIList() {
+    public Collection<WifiPasspointDmTree.HomeOIList> getHomeOiList() {
         return mHomeOIList;
     }
 
@@ -494,13 +429,13 @@
     }
 
     /** @hide */
-    public String getAAACertURL() {
-        return mAAACertURL;
+    public String getAaaCertUrl() {
+        return mAaaCertUrl;
     }
 
     /** @hide */
-    public String getAAASha256Fingerprint() {
-        return mAAASha256Fingerprint;
+    public String getAaaSha256Fingerprint() {
+        return mAaaSha256Fingerprint;
     }
 
     /** @hide */
@@ -577,72 +512,75 @@
         StringBuffer sb = new StringBuffer();
         String none = "<none>";
 
-        sb.append(", UpdateIdentifier: ")
-                .append(mUpdateIdentifier == null ? none : mUpdateIdentifier).
-                append(", SubscriptionUpdateMethod: ")
-                .append(mSubscriptionUpdateMethod == null ? none : mSubscriptionUpdateMethod).
-                append(", Type: ").append(mType == null ? none : mType).
-                append(", Username: ").append(mUsername == null ? none : mUsername).
-                append(", Passwd: ").append(mPasswd == null ? none : mPasswd).
-                append(", SubDMAccUsername: ")
-                .append(mSubscriptionDMAccUsername == null ? none : mSubscriptionDMAccUsername).
-                append(", SubDMAccPassword: ")
-                .append(mSubscriptionDMAccPassword == null ? none : mSubscriptionDMAccPassword).
-                append(", PolDMAccUsername: ")
-                .append(mPolicyDMAccUsername == null ? none : mPolicyDMAccUsername).
-                append(", PolDMAccPassword: ")
-                .append(mPolicyDMAccPassword == null ? none : mPolicyDMAccPassword).
-                append(", Imsi: ").append(mImsi == null ? none : mImsi).
-                append(", Mcc: ").append(mMcc == null ? none : mMcc).
-                append(", Mnc: ").append(mMnc == null ? none : mMnc).
-                append(", CaRootCert: ").append(mCaRootCert == null ? none : mCaRootCert).
-                append(", Realm: ").append(mRealm == null ? none : mRealm).
-                append(", Priority: ").append(mPriority).
-                append(", Fqdn: ").append(mHomeSpFqdn == null ? none : mHomeSpFqdn).
-                append(", Otherhomepartners: ")
-                .append(mOtherhomepartnerFqdn == null ? none : mOtherhomepartnerFqdn).
-                append(", ExpirationDate: ")
-                .append(mExpirationDate == null ? none : mExpirationDate).
-                append(", MaxBssLoad: ").append(mMaxBssLoad == null ? none : mMaxBssLoad).
-                append(", SPExclusionList: ").append(mSpExclusionList);
+        if (!DBG) {
+            sb.append(none);
+        } else {
+            sb.append(", UpdateIdentifier: ")
+            .append(mUpdateIdentifier == null ? none : mUpdateIdentifier)
+            .append(", SubscriptionUpdateMethod: ")
+            .append(mSubscriptionUpdateMethod == null ? none : mSubscriptionUpdateMethod)
+            .append(", Type: ").append(mType == null ? none : mType)
+            .append(", Username: ").append(mUsername == null ? none : mUsername)
+            .append(", Passwd: ").append(mPasswd == null ? none : mPasswd)
+            .append(", SubDMAccUsername: ")
+            .append(mSubscriptionUpdateUsername == null ? none : mSubscriptionUpdateUsername)
+            .append(", SubDMAccPassword: ")
+            .append(mSubscriptionUpdatePassword == null ? none : mSubscriptionUpdatePassword)
+            .append(", PolDMAccUsername: ")
+            .append(mPolicyUpdateUsername == null ? none : mPolicyUpdateUsername)
+            .append(", PolDMAccPassword: ")
+            .append(mPolicyUpdatePassword == null ? none : mPolicyUpdatePassword)
+            .append(", Imsi: ").append(mImsi == null ? none : mImsi)
+            .append(", Mcc: ").append(mMcc == null ? none : mMcc)
+            .append(", Mnc: ").append(mMnc == null ? none : mMnc)
+            .append(", CaRootCert: ").append(mCaRootCert == null ? none : mCaRootCert)
+            .append(", Realm: ").append(mRealm == null ? none : mRealm)
+            .append(", Priority: ").append(mCrednetialPriority)
+            .append(", Fqdn: ").append(mHomeSpFqdn == null ? none : mHomeSpFqdn)
+            .append(", Otherhomepartners: ")
+            .append(mOtherHomePartnerList == null ? none : mOtherHomePartnerList)
+            .append(", ExpirationDate: ")
+            .append(mExpirationDate == null ? none : mExpirationDate)
+            .append(", MaxBssLoad: ").append(mMaxBssLoad == null ? none : mMaxBssLoad)
+            .append(", SPExclusionList: ").append(mSpExclusionList);
 
-        if (mPreferredRoamingPartnerList != null) {
-            sb.append("PreferredRoamingPartnerList:");
-            for (WifiPasspointDmTree.PreferredRoamingPartnerList prpListItem : mPreferredRoamingPartnerList) {
-                sb.append("[fqdnmatch:").append(prpListItem.FQDN_Match).
-                        append(", priority:").append(prpListItem.Priority).
-                        append(", country:").append(prpListItem.Country).append("]");
+            if (mPreferredRoamingPartnerList != null) {
+                sb.append("PreferredRoamingPartnerList:");
+                for (WifiPasspointDmTree.PreferredRoamingPartnerList prpListItem : mPreferredRoamingPartnerList) {
+                    sb.append("[fqdnmatch:").append(prpListItem.FQDN_Match).
+                            append(", priority:").append(prpListItem.Priority).
+                            append(", country:").append(prpListItem.Country).append("]");
+                }
+            }
+
+            if (mHomeOIList != null) {
+                sb.append("HomeOIList:");
+                for (WifiPasspointDmTree.HomeOIList HomeOIListItem : mHomeOIList) {
+                    sb.append("[HomeOI:").append(HomeOIListItem.HomeOI).
+                            append(", HomeOIRequired:").append(HomeOIListItem.HomeOIRequired).
+                            append("]");
+                }
+            }
+
+            if (mMinBackhaulThresholdNetwork != null) {
+                sb.append("BackHaulThreshold:");
+                for (WifiPasspointDmTree.MinBackhaulThresholdNetwork BhtListItem : mMinBackhaulThresholdNetwork) {
+                    sb.append("[networkType:").append(BhtListItem.NetworkType).
+                            append(", dlBandwidth:").append(BhtListItem.DLBandwidth).
+                            append(", ulBandwidth:").append(BhtListItem.ULBandwidth).
+                            append("]");
+                }
+            }
+
+            if (mRequiredProtoPortTuple != null) {
+                sb.append("WifiMORequiredProtoPortTupleList:");
+                for (WifiPasspointDmTree.RequiredProtoPortTuple RpptListItem : mRequiredProtoPortTuple) {
+                    sb.append("[IPProtocol:").append(RpptListItem.IPProtocol).
+                            append(", PortNumber:").append(RpptListItem.PortNumber).
+                            append("]");
+                }
             }
         }
-
-        if (mHomeOIList != null) {
-            sb.append("HomeOIList:");
-            for (WifiPasspointDmTree.HomeOIList HomeOIListItem : mHomeOIList) {
-                sb.append("[HomeOI:").append(HomeOIListItem.HomeOI).
-                        append(", HomeOIRequired:").append(HomeOIListItem.HomeOIRequired).
-                        append("]");
-            }
-        }
-
-        if (mMinBackhaulThresholdNetwork != null) {
-            sb.append("BackHaulThreshold:");
-            for (WifiPasspointDmTree.MinBackhaulThresholdNetwork BhtListItem : mMinBackhaulThresholdNetwork) {
-                sb.append("[networkType:").append(BhtListItem.NetworkType).
-                        append(", dlBandwidth:").append(BhtListItem.DLBandwidth).
-                        append(", ulBandwidth:").append(BhtListItem.ULBandwidth).
-                        append("]");
-            }
-        }
-
-        if (mRequiredProtoPortTuple != null) {
-            sb.append("WifiMORequiredProtoPortTupleList:");
-            for (WifiPasspointDmTree.RequiredProtoPortTuple RpptListItem : mRequiredProtoPortTuple) {
-                sb.append("[IPProtocol:").append(RpptListItem.IPProtocol).
-                        append(", PortNumber:").append(RpptListItem.PortNumber).
-                        append("]");
-            }
-        }
-
         return sb.toString();
     }
 
@@ -653,19 +591,22 @@
 
     /** Implement the Parcelable interface {@hide} */
     public void writeToParcel(Parcel dest, int flags) {
+        dest.writeString(mWifiSpFqdn);
+        dest.writeString(mCredentialName);
         dest.writeString(mType);
-        dest.writeString(mUsername);
-        dest.writeString(mPasswd);
-        dest.writeString(mImsi);
-        dest.writeString(mMcc);
-        dest.writeString(mMnc);
-        dest.writeString(mCaRootCert);
-        dest.writeString(mRealm);
-        dest.writeInt(mPriority);
+        dest.writeInt(mCrednetialPriority);
         dest.writeString(mHomeSpFqdn);
-        dest.writeString(mOtherhomepartnerFqdn);
-        dest.writeString(mClientCert);
-        dest.writeString(mExpirationDate);
+        dest.writeString(mRealm);
+    }
+
+    /** Implement the Parcelable interface {@hide} */
+    public void readFromParcel(Parcel in) {
+        mWifiSpFqdn = in.readString();
+        mCredentialName = in.readString();
+        mType = in.readString();
+        mCrednetialPriority = in.readInt();
+        mHomeSpFqdn = in.readString();
+        mRealm = in.readString();
     }
 
     /** Implement the Parcelable interface {@hide} */
@@ -673,19 +614,12 @@
             new Creator<WifiPasspointCredential>() {
                 public WifiPasspointCredential createFromParcel(Parcel in) {
                     WifiPasspointCredential pc = new WifiPasspointCredential();
+                    pc.mWifiSpFqdn = in.readString();
+                    pc.mCredentialName = in.readString();
                     pc.mType = in.readString();
-                    pc.mUsername = in.readString();
-                    pc.mPasswd = in.readString();
-                    pc.mImsi = in.readString();
-                    pc.mMcc = in.readString();
-                    pc.mMnc = in.readString();
-                    pc.mCaRootCert = in.readString();
-                    pc.mRealm = in.readString();
-                    pc.mPriority = in.readInt();
+                    pc.mCrednetialPriority = in.readInt();
                     pc.mHomeSpFqdn = in.readString();
-                    pc.mOtherhomepartnerFqdn = in.readString();
-                    pc.mClientCert = in.readString();
-                    pc.mExpirationDate = in.readString();
+                    pc.mRealm = in.readString();
                     return pc;
                 }
 
@@ -698,9 +632,9 @@
     public int compareTo(WifiPasspointCredential another) {
 
         //The smaller the higher
-        if (mPriority < another.mPriority) {
+        if (mCrednetialPriority < another.mCrednetialPriority) {
             return -1;
-        } else if (mPriority == another.mPriority) {
+        } else if (mCrednetialPriority == another.mCrednetialPriority) {
             return this.mType.compareTo(another.mType);
         } else {
             return 1;
diff --git a/wifi/java/android/net/wifi/passpoint/WifiPasspointDmTree.java b/wifi/java/android/net/wifi/passpoint/WifiPasspointDmTree.java
index 9ff1973..bbf5fc6 100644
--- a/wifi/java/android/net/wifi/passpoint/WifiPasspointDmTree.java
+++ b/wifi/java/android/net/wifi/passpoint/WifiPasspointDmTree.java
@@ -25,19 +25,17 @@
 /**
  * Required Mobile Device Management Tree Structure
  *
- *                           +----------+
- *                           | ./(Root) |
- *                           +----+-----+
- *                                |
- *          +---------+           |         +---------+   +---------+
- *          | DevInfo |-----------+---------| Wi-Fi   |---|SP FQDN* |
- *          +---------+           |         +---------+   +---------+
- *          +---------+           |
- *          |DevDetail|-----------+
- *          +---------+
- *
- * For example,
- * ./Wi-Fi/wi-fi.org/PerproviderSubscription/Cred01/Policy/PreferredRoamingPartnerList/Roa01/FQDN_Math
+ *                   +----------+
+ *                   | ./(Root) |
+ *                   +----+-----+
+ *                        |
+ *  +---------+           |         +---------+  +---------+
+ *  | DevInfo |-----------+---------|  Wi-Fi  |--|SP FQDN* |
+ *  +---------+           |         +---------+  +---------+
+ *  +---------+           |                           |
+ *  |DevDetail|-----------+                      +-----------------------+
+ *  +---------+                                  |PerproviderSubscription|--<X>+
+ *                                               +-----------------------+
  *
  * This class contains all nodes start from Wi-Fi
  * @hide
diff --git a/wifi/java/android/net/wifi/passpoint/WifiPasspointInfo.java b/wifi/java/android/net/wifi/passpoint/WifiPasspointInfo.java
index 99bea2f..8ab5c1e 100644
--- a/wifi/java/android/net/wifi/passpoint/WifiPasspointInfo.java
+++ b/wifi/java/android/net/wifi/passpoint/WifiPasspointInfo.java
@@ -88,68 +88,160 @@
                     CONNECTION_CAPABILITY |
                     OSU_PROVIDER;
 
-    /** TODO doc */
-    public String bssid;
 
-    /** TODO doc */
-    public String venueName;
+    public static class WanMetrics {
+        public static final int STATUS_RESERVED = 0;
+        public static final int STATUS_UP = 1;
+        public static final int STATUS_DOWN = 2;
+        public static final int STATUS_TEST = 3;
 
-    /** TODO doc */
-    public String networkAuthType;
+        public int wanInfo;
+        public long downlinkSpeed;
+        public long uplinkSpeed;
+        public int downlinkLoad;
+        public int uplinkLoad;
+        public int lmd;
 
-    /** TODO doc */
-    public String roamingConsortium;
+        public int getLinkStatus() {
+            return wanInfo & 0x3;
+        }
 
-    /** TODO doc */
-    public String ipAddrTypeAvaibility;
+        public boolean getSymmetricLink() {
+            return (wanInfo & (1 << 2)) != 0;
+        }
 
-    /** TODO doc */
-    public String naiRealm;
+        public boolean getAtCapacity() {
+            return (wanInfo & (1 << 3)) != 0;
+        }
 
-    /** TODO doc */
-    public String cellularNetwork;
-
-    /** TODO doc */
-    public String domainName;
-
-    /** TODO doc */
-    public String operatorFriendlyName;
-
-    /** TODO doc */
-    public String wanMetrics;
-
-    /** TODO doc */
-    public String connectionCapability;
-
-    /** TODO doc */
-    public List<WifiPasspointOsuProvider> osuProviderList;
-
-    /** default constructor @hide */
-    public WifiPasspointInfo() {
-        //        osuProviderList = new ArrayList<OsuProvider>();
-    }
-
-    /** copy constructor @hide */
-    public WifiPasspointInfo(WifiPasspointInfo source) {
-        // TODO
-        bssid = source.bssid;
-        venueName = source.venueName;
-        networkAuthType = source.networkAuthType;
-        roamingConsortium = source.roamingConsortium;
-        ipAddrTypeAvaibility = source.ipAddrTypeAvaibility;
-        naiRealm = source.naiRealm;
-        cellularNetwork = source.cellularNetwork;
-        domainName = source.domainName;
-        operatorFriendlyName = source.operatorFriendlyName;
-        wanMetrics = source.wanMetrics;
-        connectionCapability = source.connectionCapability;
-        if (source.osuProviderList != null) {
-            osuProviderList = new ArrayList<WifiPasspointOsuProvider>();
-            for (WifiPasspointOsuProvider osu : source.osuProviderList)
-                osuProviderList.add(new WifiPasspointOsuProvider(osu));
+        @Override
+        public String toString() {
+            return wanInfo + "," + downlinkSpeed + "," + uplinkSpeed + "," +
+                    downlinkLoad + "," + uplinkLoad + "," + lmd;
         }
     }
 
+    public static class IpProtoPort {
+        public static final int STATUS_CLOSED = 0;
+        public static final int STATUS_OPEN = 1;
+        public static final int STATUS_UNKNOWN = 2;
+
+        public int proto;
+        public int port;
+        public int status;
+
+        @Override
+        public String toString() {
+            return proto + "," + port + "," + status;
+        }
+    }
+
+    public static class NetworkAuthType {
+        public static final int TYPE_TERMS_AND_CONDITION = 0;
+        public static final int TYPE_ONLINE_ENROLLMENT = 1;
+        public static final int TYPE_HTTP_REDIRECTION = 2;
+        public static final int TYPE_DNS_REDIRECTION = 3;
+
+        public int type;
+        public String redirectUrl;
+
+        @Override
+        public String toString() {
+            return type + "," + redirectUrl;
+        }
+    }
+
+    public static class IpAddressType {
+        public static final int IPV6_NOT_AVAILABLE = 0;
+        public static final int IPV6_AVAILABLE = 1;
+        public static final int IPV6_UNKNOWN = 2;
+
+        public static final int IPV4_NOT_AVAILABLE = 0;
+        public static final int IPV4_PUBLIC = 1;
+        public static final int IPV4_PORT_RESTRICTED = 2;
+        public static final int IPV4_SINGLE_NAT = 3;
+        public static final int IPV4_DOUBLE_NAT = 4;
+        public static final int IPV4_PORT_RESTRICTED_SINGLE_NAT = 5;
+        public static final int IPV4_PORT_RESTRICTED_DOUBLE_NAT = 6;
+        public static final int IPV4_PORT_UNKNOWN = 7;
+
+        private static final int NULL_VALUE = -1;
+
+        public int availability;
+
+        public int getIpv6Availability() {
+            return availability & 0x3;
+        }
+
+        public int getIpv4Availability() {
+            return (availability & 0xFF) >> 2;
+        }
+
+        @Override
+        public String toString() {
+            return getIpv6Availability() + "," + getIpv4Availability();
+        }
+    }
+
+    public static class NaiRealm {
+        public static final int ENCODING_RFC4282 = 0;
+        public static final int ENCODING_UTF8 = 1;
+
+        public int encoding;
+        public String realm;
+
+        @Override
+        public String toString() {
+            return encoding + "," + realm;
+        }
+    }
+
+    public static class CellularNetwork {
+        public String mcc;
+        public String mnc;
+
+        @Override
+        public String toString() {
+            return mcc + "," + mnc;
+        }
+    }
+
+    /** BSSID */
+    public String bssid;
+
+    /** venue name */
+    public String venueName;
+
+    /** list of network authentication types */
+    public List<NetworkAuthType> networkAuthType;
+
+    /** list of roaming consortium OIs */
+    public List<String> roamingConsortium;
+
+    /** IP address availability */
+    public IpAddressType ipAddrTypeAvailability;
+
+    /** NAI realm */
+    public List<NaiRealm> naiRealm;
+
+    /** 3GPP cellular network */
+    public List<CellularNetwork> cellularNetwork;
+
+    /** fully qualified domain name (FQDN) */
+    public List<String> domainName;
+
+    /** HS 2.0 operator friendly name */
+    public String operatorFriendlyName;
+
+    /** HS 2.0 wan metrics */
+    public WanMetrics wanMetrics;
+
+    /** HS 2.0 list of IP proto port */
+    public List<IpProtoPort> connectionCapability;
+
+    /** HS 2.0 list of OSU providers */
+    public List<WifiPasspointOsuProvider> osuProviderList;
+
     /**
      * Convert mask to ANQP subtypes, for supplicant command use.
      *
@@ -193,46 +285,154 @@
     @Override
     public String toString() {
         StringBuffer sb = new StringBuffer();
+
         sb.append("BSSID: ").append(bssid);
+
         if (venueName != null)
-            sb.append(" venueName: ").append(venueName);
-        if (networkAuthType != null)
-            sb.append(" networkAuthType: ").append(networkAuthType);
-        if (roamingConsortium != null)
-            sb.append(" roamingConsortium: ").append(roamingConsortium);
-        if (ipAddrTypeAvaibility != null)
-            sb.append(" ipAddrTypeAvaibility: ").append(ipAddrTypeAvaibility);
-        if (naiRealm != null)
-            sb.append(" naiRealm: ").append(naiRealm);
-        if (cellularNetwork != null)
-            sb.append(" cellularNetwork: ").append(cellularNetwork);
-        if (domainName != null)
-            sb.append(" domainName: ").append(domainName);
+            sb.append(" venueName: ").append(venueName.replace("\n", "\\n"));
+
+        if (networkAuthType != null) {
+            sb.append(" networkAuthType: ");
+            for (NetworkAuthType auth : networkAuthType)
+                sb.append("(").append(auth.toString()).append(")");
+        }
+
+        if (roamingConsortium != null) {
+            sb.append(" roamingConsortium: ");
+            for (String oi : roamingConsortium)
+                sb.append("(").append(oi).append(")");
+        }
+
+        if (ipAddrTypeAvailability != null) {
+            sb.append(" ipAddrTypeAvaibility: ").append("(")
+              .append(ipAddrTypeAvailability.toString()).append(")");
+        }
+
+        if (naiRealm != null) {
+            sb.append(" naiRealm: ");
+            for (NaiRealm realm : naiRealm)
+                sb.append("(").append(realm.toString()).append(")");
+        }
+
+        if (cellularNetwork != null) {
+            sb.append(" cellularNetwork: ");
+            for (CellularNetwork plmn : cellularNetwork)
+                sb.append("(").append(plmn.toString()).append(")");
+        }
+
+        if (domainName != null) {
+            sb.append(" domainName: ");
+            for (String fqdn : domainName)
+                sb.append("(").append(fqdn).append(")");
+        }
+
         if (operatorFriendlyName != null)
-            sb.append(" operatorFriendlyName: ").append(operatorFriendlyName);
+            sb.append(" operatorFriendlyName: ").append("(")
+              .append(operatorFriendlyName).append(")");
+
         if (wanMetrics != null)
-            sb.append(" wanMetrics: ").append(wanMetrics);
-        if (connectionCapability != null)
-            sb.append(" connectionCapability: ").append(connectionCapability);
-        if (osuProviderList != null)
-            sb.append(" osuProviderList: (size=" + osuProviderList.size() + ")");
+            sb.append(" wanMetrics: ").append("(")
+              .append(wanMetrics.toString()).append(")");
+
+        if (connectionCapability != null) {
+            sb.append(" connectionCapability: ");
+            for (IpProtoPort ip : connectionCapability)
+                sb.append("(").append(ip.toString()).append(")");
+        }
+
+        if (osuProviderList != null) {
+            sb.append(" osuProviderList: ");
+            for (WifiPasspointOsuProvider osu : osuProviderList)
+                sb.append("(").append(osu.toString()).append(")");
+        }
+
         return sb.toString();
     }
 
     /** Implement the Parcelable interface {@hide} */
     @Override
     public void writeToParcel(Parcel out, int flags) {
-        out.writeValue(bssid);
-        out.writeValue(venueName);
-        out.writeValue(networkAuthType);
-        out.writeValue(roamingConsortium);
-        out.writeValue(ipAddrTypeAvaibility);
-        out.writeValue(naiRealm);
-        out.writeValue(cellularNetwork);
-        out.writeValue(domainName);
-        out.writeValue(operatorFriendlyName);
-        out.writeValue(wanMetrics);
-        out.writeValue(connectionCapability);
+        out.writeString(bssid);
+        out.writeString(venueName);
+
+        if (networkAuthType == null) {
+            out.writeInt(0);
+        } else {
+            out.writeInt(networkAuthType.size());
+            for (NetworkAuthType auth : networkAuthType) {
+                out.writeInt(auth.type);
+                out.writeString(auth.redirectUrl);
+            }
+        }
+
+        if (roamingConsortium == null) {
+            out.writeInt(0);
+        } else {
+            out.writeInt(roamingConsortium.size());
+            for (String oi : roamingConsortium)
+                out.writeString(oi);
+        }
+
+        if (ipAddrTypeAvailability == null) {
+            out.writeInt(IpAddressType.NULL_VALUE);
+        } else {
+            out.writeInt(ipAddrTypeAvailability.availability);
+        }
+
+        if (naiRealm == null) {
+            out.writeInt(0);
+        } else {
+            out.writeInt(naiRealm.size());
+            for (NaiRealm realm : naiRealm) {
+                out.writeInt(realm.encoding);
+                out.writeString(realm.realm);
+            }
+        }
+
+        if (cellularNetwork == null) {
+            out.writeInt(0);
+        } else {
+            out.writeInt(cellularNetwork.size());
+            for (CellularNetwork plmn : cellularNetwork) {
+                out.writeString(plmn.mcc);
+                out.writeString(plmn.mnc);
+            }
+        }
+
+
+        if (domainName == null) {
+            out.writeInt(0);
+        } else {
+            out.writeInt(domainName.size());
+            for (String fqdn : domainName)
+                out.writeString(fqdn);
+        }
+
+        out.writeString(operatorFriendlyName);
+
+        if (wanMetrics == null) {
+            out.writeInt(0);
+        } else {
+            out.writeInt(1);
+            out.writeInt(wanMetrics.wanInfo);
+            out.writeLong(wanMetrics.downlinkSpeed);
+            out.writeLong(wanMetrics.uplinkSpeed);
+            out.writeInt(wanMetrics.downlinkLoad);
+            out.writeInt(wanMetrics.uplinkLoad);
+            out.writeInt(wanMetrics.lmd);
+        }
+
+        if (connectionCapability == null) {
+            out.writeInt(0);
+        } else {
+            out.writeInt(connectionCapability.size());
+            for (IpProtoPort ip : connectionCapability) {
+                out.writeInt(ip.proto);
+                out.writeInt(ip.port);
+                out.writeInt(ip.status);
+            }
+        }
+
         if (osuProviderList == null) {
             out.writeInt(0);
         } else {
@@ -254,18 +454,90 @@
                 @Override
                 public WifiPasspointInfo createFromParcel(Parcel in) {
                     WifiPasspointInfo p = new WifiPasspointInfo();
-                    p.bssid = (String) in.readValue(String.class.getClassLoader());
-                    p.venueName = (String) in.readValue(String.class.getClassLoader());
-                    p.networkAuthType = (String) in.readValue(String.class.getClassLoader());
-                    p.roamingConsortium = (String) in.readValue(String.class.getClassLoader());
-                    p.ipAddrTypeAvaibility = (String) in.readValue(String.class.getClassLoader());
-                    p.naiRealm = (String) in.readValue(String.class.getClassLoader());
-                    p.cellularNetwork = (String) in.readValue(String.class.getClassLoader());
-                    p.domainName = (String) in.readValue(String.class.getClassLoader());
-                    p.operatorFriendlyName = (String) in.readValue(String.class.getClassLoader());
-                    p.wanMetrics = (String) in.readValue(String.class.getClassLoader());
-                    p.connectionCapability = (String) in.readValue(String.class.getClassLoader());
-                    int n = in.readInt();
+                    int n;
+
+                    p.bssid = in.readString();
+                    p.venueName = in.readString();
+
+                    n = in.readInt();
+                    if (n > 0) {
+                        p.networkAuthType = new ArrayList<NetworkAuthType>();
+                        for (int i = 0; i < n; i++) {
+                            NetworkAuthType auth = new NetworkAuthType();
+                            auth.type = in.readInt();
+                            auth.redirectUrl = in.readString();
+                            p.networkAuthType.add(auth);
+                        }
+                    }
+
+                    n = in.readInt();
+                    if (n > 0) {
+                        p.roamingConsortium = new ArrayList<String>();
+                        for (int i = 0; i < n; i++)
+                            p.roamingConsortium.add(in.readString());
+                    }
+
+                    n = in.readInt();
+                    if (n != IpAddressType.NULL_VALUE) {
+                        p.ipAddrTypeAvailability = new IpAddressType();
+                        p.ipAddrTypeAvailability.availability = n;
+                    }
+
+                    n = in.readInt();
+                    if (n > 0) {
+                        p.naiRealm = new ArrayList<NaiRealm>();
+                        for (int i = 0; i < n; i++) {
+                            NaiRealm realm = new NaiRealm();
+                            realm.encoding = in.readInt();
+                            realm.realm = in.readString();
+                            p.naiRealm.add(realm);
+                        }
+                    }
+
+                    n = in.readInt();
+                    if (n > 0) {
+                        p.cellularNetwork = new ArrayList<CellularNetwork>();
+                        for (int i = 0; i < n; i++) {
+                            CellularNetwork plmn = new CellularNetwork();
+                            plmn.mcc = in.readString();
+                            plmn.mnc = in.readString();
+                            p.cellularNetwork.add(plmn);
+                        }
+                    }
+
+                    n = in.readInt();
+                    if (n > 0) {
+                        p.domainName = new ArrayList<String>();
+                        for (int i = 0; i < n; i++)
+                            p.domainName.add(in.readString());
+                    }
+
+                    p.operatorFriendlyName = in.readString();
+
+                    n = in.readInt();
+                    if (n > 0) {
+                        p.wanMetrics = new WanMetrics();
+                        p.wanMetrics.wanInfo = in.readInt();
+                        p.wanMetrics.downlinkSpeed = in.readLong();
+                        p.wanMetrics.uplinkSpeed = in.readLong();
+                        p.wanMetrics.downlinkLoad = in.readInt();
+                        p.wanMetrics.uplinkLoad = in.readInt();
+                        p.wanMetrics.lmd = in.readInt();
+                    }
+
+                    n = in.readInt();
+                    if (n > 0) {
+                        p.connectionCapability = new ArrayList<IpProtoPort>();
+                        for (int i = 0; i < n; i++) {
+                            IpProtoPort ip = new IpProtoPort();
+                            ip.proto = in.readInt();
+                            ip.port = in.readInt();
+                            ip.status = in.readInt();
+                            p.connectionCapability.add(ip);
+                        }
+                    }
+
+                    n = in.readInt();
                     if (n > 0) {
                         p.osuProviderList = new ArrayList<WifiPasspointOsuProvider>();
                         for (int i = 0; i < n; i++) {
@@ -274,6 +546,7 @@
                             p.osuProviderList.add(osu);
                         }
                     }
+
                     return p;
                 }
 
diff --git a/wifi/java/android/net/wifi/passpoint/WifiPasspointManager.java b/wifi/java/android/net/wifi/passpoint/WifiPasspointManager.java
index ebaa8c9..55acbad 100644
--- a/wifi/java/android/net/wifi/passpoint/WifiPasspointManager.java
+++ b/wifi/java/android/net/wifi/passpoint/WifiPasspointManager.java
@@ -35,6 +35,7 @@
 
 /**
  * Provides APIs for managing Wifi Passpoint credentials.
+ * @hide
  */
 public class WifiPasspointManager {
 
diff --git a/wifi/java/android/net/wifi/passpoint/WifiPasspointOsuProvider.java b/wifi/java/android/net/wifi/passpoint/WifiPasspointOsuProvider.java
index 18a8f1e..f40dc4f 100644
--- a/wifi/java/android/net/wifi/passpoint/WifiPasspointOsuProvider.java
+++ b/wifi/java/android/net/wifi/passpoint/WifiPasspointOsuProvider.java
@@ -94,10 +94,10 @@
             sb.append(" serverUri: ").append(serverUri);
         sb.append(" osuMethod: ").append(osuMethod);
         if (iconFileName != null) {
-            sb.append(" icon: [").append(iconWidth).append("x")
+            sb.append(" icon: <").append(iconWidth).append("x")
                     .append(iconHeight).append(" ")
                     .append(iconType).append(" ")
-                    .append(iconFileName);
+                    .append(iconFileName).append(">");
         }
         if (osuNai != null)
             sb.append(" osuNai: ").append(osuNai);
@@ -113,16 +113,16 @@
 
     @Override
     public void writeToParcel(Parcel out, int flags) {
-        out.writeValue(ssid);
-        out.writeValue(friendlyName);
-        out.writeValue(serverUri);
+        out.writeString(ssid);
+        out.writeString(friendlyName);
+        out.writeString(serverUri);
         out.writeInt(osuMethod);
         out.writeInt(iconWidth);
         out.writeInt(iconHeight);
-        out.writeValue(iconType);
-        out.writeValue(iconFileName);
-        out.writeValue(osuNai);
-        out.writeValue(osuService);
+        out.writeString(iconType);
+        out.writeString(iconFileName);
+        out.writeString(osuNai);
+        out.writeString(osuService);
         // TODO: icon image?
     }
 
@@ -131,16 +131,16 @@
                 @Override
                 public WifiPasspointOsuProvider createFromParcel(Parcel in) {
                     WifiPasspointOsuProvider osu = new WifiPasspointOsuProvider();
-                    osu.ssid = (String) in.readValue(String.class.getClassLoader());
-                    osu.friendlyName = (String) in.readValue(String.class.getClassLoader());
-                    osu.serverUri = (String) in.readValue(String.class.getClassLoader());
+                    osu.ssid = in.readString();
+                    osu.friendlyName = in.readString();
+                    osu.serverUri = in.readString();
                     osu.osuMethod = in.readInt();
                     osu.iconWidth = in.readInt();
                     osu.iconHeight = in.readInt();
-                    osu.iconType = (String) in.readValue(String.class.getClassLoader());
-                    osu.iconFileName = (String) in.readValue(String.class.getClassLoader());
-                    osu.osuNai = (String) in.readValue(String.class.getClassLoader());
-                    osu.osuService = (String) in.readValue(String.class.getClassLoader());
+                    osu.iconType = in.readString();
+                    osu.iconFileName = in.readString();
+                    osu.osuNai = in.readString();
+                    osu.osuService = in.readString();
                     return osu;
                 }
 
diff --git a/wifi/java/android/net/wifi/passpoint/WifiPasspointPolicy.java b/wifi/java/android/net/wifi/passpoint/WifiPasspointPolicy.java
index 5f76562..9fccf0a 100644
--- a/wifi/java/android/net/wifi/passpoint/WifiPasspointPolicy.java
+++ b/wifi/java/android/net/wifi/passpoint/WifiPasspointPolicy.java
@@ -35,7 +35,7 @@
     public static final int UNRESTRICTED = 2;
 
     private String mName;
-    private int mSubscriptionPriority;
+    private int mCredentialPriority;
     private int mRoamingPriority;
     private String mBssid;
     private String mSsid;
@@ -44,11 +44,13 @@
     private boolean mIsHomeSp;
 
     /** @hide */
-    public WifiPasspointPolicy(String name, int priority, String ssid,
+    public WifiPasspointPolicy(String name, String ssid,
             String bssid, WifiPasspointCredential pc,
             int restriction, boolean ishomesp) {
         mName = name;
-        mSubscriptionPriority = priority;
+        if (pc != null) {
+            mCredentialPriority = pc.getPriority();
+        }
         //PerProviderSubscription/<X+>/Policy/PreferredRoamingPartnerList/<X+>/Priority
         mRoamingPriority = 128; //default priority value of 128
         mSsid = ssid;
@@ -102,8 +104,8 @@
     }
 
     /** @hide */
-    public void setSubscriptionPriority(int priority) {
-        mSubscriptionPriority = priority;
+    public void setCredentialPriority(int priority) {
+        mCredentialPriority = priority;
     }
 
     /** @hide */
@@ -111,8 +113,8 @@
         mRoamingPriority = priority;
     }
 
-    public int getSubscriptionPriority() {
-        return mSubscriptionPriority;
+    public int getCredentialPriority() {
+        return mCredentialPriority;
     }
 
     public int getRoamingPriority() {
@@ -132,11 +134,11 @@
             return -1;
         } else if ((this.mIsHomeSp == true && another.getHomeSp() == true)) {
             Log.d(TAG, "both HomeSP");
-            //if both home sp, compare subscription priority
-            if (this.mSubscriptionPriority < another.getSubscriptionPriority()) {
+            //if both home sp, compare credential priority
+            if (this.mCredentialPriority < another.getCredentialPriority()) {
                 Log.d(TAG, "this priority is higher");
                 return -1;
-            } else if (this.mSubscriptionPriority == another.getSubscriptionPriority()) {
+            } else if (this.mCredentialPriority == another.getCredentialPriority()) {
                 Log.d(TAG, "both priorities equal");
                 //if priority still the same, compare name(ssid)
                 if (this.mName.compareTo(another.mName) != 0) {
@@ -192,7 +194,7 @@
     @Override
     /** @hide */
     public String toString() {
-        return "PasspointPolicy: name=" + mName + " SubscriptionPriority=" + mSubscriptionPriority +
+        return "PasspointPolicy: name=" + mName + " CredentialPriority=" + mCredentialPriority +
                 " mRoamingPriority" + mRoamingPriority +
                 " ssid=" + mSsid + " restriction=" + mRestriction +
                 " ishomesp=" + mIsHomeSp + " Credential=" + mCredential;