Merge "Add the tree hierarchy for the group"
diff --git a/Android.mk b/Android.mk
index c58ef53..2f3a990 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 \
diff --git a/api/current.txt b/api/current.txt
index 47b71b6..b059841 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -1017,6 +1017,7 @@
     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
@@ -1867,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
@@ -1935,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
@@ -2019,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
@@ -2327,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
@@ -3427,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();
@@ -4693,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
@@ -5030,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);
@@ -5045,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 {
@@ -5219,6 +5230,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);
@@ -5358,13 +5370,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();
@@ -5377,7 +5389,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);
@@ -5391,8 +5403,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 {
@@ -5401,8 +5414,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 {
@@ -5564,8 +5577,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();
@@ -6208,180 +6221,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);
@@ -6429,6 +6268,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 {
@@ -6913,6 +6913,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();
@@ -7025,6 +7026,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";
@@ -7038,7 +7040,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";
   }
@@ -7082,6 +7083,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();
@@ -7781,12 +7783,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();
@@ -7798,6 +7802,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);
@@ -7806,10 +7811,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[]);
@@ -8198,7 +8229,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();
@@ -8209,21 +8240,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 {
@@ -10174,8 +10205,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);
@@ -11475,6 +11506,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 {
@@ -12177,9 +12209,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);
   }
 
@@ -12206,7 +12240,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;
@@ -12220,11 +12256,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;
@@ -12286,9 +12323,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);
   }
 
@@ -12442,6 +12481,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
@@ -12535,9 +12575,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;
@@ -12558,9 +12596,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;
   }
 
@@ -12579,7 +12615,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();
@@ -12613,9 +12649,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;
@@ -12644,12 +12678,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;
   }
 
@@ -12672,6 +12704,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 {
@@ -12719,6 +12755,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 {
@@ -14290,7 +14329,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();
@@ -14340,10 +14378,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();
@@ -14672,6 +14706,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;
@@ -15809,56 +15844,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;
@@ -15870,95 +15930,50 @@
     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
-  }
-
-  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();
-  }
-
-  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 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 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();
+    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
   }
 
 }
@@ -17284,86 +17299,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);
@@ -17577,29 +17512,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 {
@@ -17734,25 +17646,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";
@@ -20593,6 +20495,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;
@@ -20721,17 +20654,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);
@@ -20748,37 +20678,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[]);
@@ -20790,30 +20704,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;
@@ -20831,9 +20734,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);
@@ -21413,46 +21313,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;
@@ -21757,10 +21625,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
   }
 
 }
@@ -23507,6 +23371,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";
@@ -26406,16 +26277,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);
@@ -26436,12 +26308,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);
@@ -26596,6 +26469,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();
@@ -26658,9 +26553,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();
@@ -26763,7 +26659,9 @@
     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();
   }
 
@@ -26837,6 +26735,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);
@@ -27598,6 +27575,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);
   }
 
@@ -27663,6 +27641,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();
@@ -27671,6 +27650,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();
@@ -27679,6 +27659,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);
   }
@@ -27688,6 +27669,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);
   }
@@ -27698,6 +27680,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);
   }
@@ -27778,6 +27761,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);
@@ -28777,6 +28761,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();
@@ -35934,10 +35919,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 {
@@ -38119,6 +38108,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();
@@ -38129,7 +38120,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);
@@ -38137,6 +38131,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);
@@ -38145,19 +38141,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 d08978bd..5e4ddd0 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;
 
@@ -181,6 +190,11 @@
      */
     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;
+
     final private Window mWindow;
     final protected ArrayList<String> mAllSharedElementNames;
     final protected ArrayList<View> mSharedElements = new ArrayList<View>();
@@ -334,6 +348,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..4f335bb 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;
@@ -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..a8617b8 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;
@@ -38,7 +34,6 @@
 import android.widget.ImageView;
 
 import java.util.ArrayList;
-import java.util.Collection;
 
 /**
  * This ActivityTransitionCoordinator is created by the Activity to manage
@@ -56,6 +51,7 @@
     private Handler mHandler;
     private boolean mIsCanceled;
     private ObjectAnimator mBackgroundAnimator;
+    private boolean mIsExitTransitionComplete;
 
     public EnterTransitionCoordinator(Activity activity, ResultReceiver resultReceiver,
             ArrayList<String> sharedElementNames,
@@ -76,6 +72,8 @@
                 }
             };
             mHandler.sendEmptyMessageDelayed(MSG_CANCEL, MAX_WAIT_MS);
+            Bundle state = captureSharedElementState();
+            mResultReceiver.send(MSG_SHARED_ELEMENT_DESTINATION, state);
         }
     }
 
@@ -98,9 +96,8 @@
                 break;
             case MSG_EXIT_TRANSITION_COMPLETE:
                 if (!mIsCanceled) {
-                    if (!mSharedElementTransitionStarted) {
-                        send(resultCode, resultData);
-                    } else {
+                    mIsExitTransitionComplete = true;
+                    if (mSharedElementTransitionStarted) {
                         onRemoteExitTransitionComplete();
                     }
                 }
@@ -183,6 +180,7 @@
         setViewVisibility(mSharedElements, View.VISIBLE);
         ArrayMap<ImageView, Pair<ImageView.ScaleType, Matrix>> originalImageViewState =
                 setSharedElementState(sharedElementState, sharedElementSnapshots);
+        requestLayoutForSharedElements();
 
         boolean startEnterTransition = allowOverlappingTransitions();
         boolean startSharedElementTransition = true;
@@ -200,6 +198,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 +218,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 +242,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 +335,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..a71d649 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 && (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 70f270f..8dba1dc 100644
--- a/core/java/android/app/Notification.java
+++ b/core/java/android/app/Notification.java
@@ -1671,7 +1671,6 @@
         private Notification mPublicVersion = null;
         private final NotificationColorUtil mColorUtil;
         private ArrayList<String> mPeople;
-        private boolean mPreQuantum;
         private int mColor = COLOR_DEFAULT;
 
         /**
@@ -1694,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
@@ -1702,7 +1710,6 @@
             mPriority = PRIORITY_DEFAULT;
             mPeople = new ArrayList<String>();
 
-            mPreQuantum = context.getApplicationInfo().targetSdkVersion < Build.VERSION_CODES.L;
             mColorUtil = NotificationColorUtil.getInstance();
         }
 
@@ -3355,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";
 
@@ -3708,7 +3723,7 @@
          * 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 action's
+         * <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.
          *
@@ -3723,16 +3738,18 @@
         }
 
         /**
-         * Get the action index from this notification's actions to be clickable with the
-         * content of this notification. This action will no longer display separately
+         * 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>For notifications with multiple pages, child pages can also have content action's
+         * <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 6b486c4..b3b1d47 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;
@@ -2320,4 +2321,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 cc6d715..7f754dc 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%
copy from core/java/android/bluetooth/BluetoothLeScanFilter.aidl
copy 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/bluetooth/BluetoothLeScanFilter.aidl b/core/java/android/bluetooth/le/ScanFilter.aidl
similarity index 90%
copy from core/java/android/bluetooth/BluetoothLeScanFilter.aidl
copy to core/java/android/bluetooth/le/ScanFilter.aidl
index 86ee06d..4cecfe6 100644
--- a/core/java/android/bluetooth/BluetoothLeScanFilter.aidl
+++ b/core/java/android/bluetooth/le/ScanFilter.aidl
@@ -14,6 +14,6 @@
  * limitations under the License.
  */
 
-package android.bluetooth;
+package android.bluetooth.le;
 
-parcelable BluetoothLeScanFilter;
+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/bluetooth/BluetoothLeScanFilter.aidl b/core/java/android/bluetooth/le/ScanResult.aidl
similarity index 90%
copy from core/java/android/bluetooth/BluetoothLeScanFilter.aidl
copy to core/java/android/bluetooth/le/ScanResult.aidl
index 86ee06d..3943035 100644
--- a/core/java/android/bluetooth/BluetoothLeScanFilter.aidl
+++ b/core/java/android/bluetooth/le/ScanResult.aidl
@@ -14,6 +14,6 @@
  * limitations under the License.
  */
 
-package android.bluetooth;
+package android.bluetooth.le;
 
-parcelable BluetoothLeScanFilter;
+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/bluetooth/BluetoothLeScanFilter.aidl b/core/java/android/bluetooth/le/ScanSettings.aidl
similarity index 90%
rename from core/java/android/bluetooth/BluetoothLeScanFilter.aidl
rename to core/java/android/bluetooth/le/ScanSettings.aidl
index 86ee06d..eb169c1 100644
--- a/core/java/android/bluetooth/BluetoothLeScanFilter.aidl
+++ b/core/java/android/bluetooth/le/ScanSettings.aidl
@@ -14,6 +14,6 @@
  * limitations under the License.
  */
 
-package android.bluetooth;
+package android.bluetooth.le;
 
-parcelable BluetoothLeScanFilter;
+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..c69e669 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.
      *
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/content/IRestrictionsManager.aidl b/core/java/android/content/IRestrictionsManager.aidl
new file mode 100644
index 0000000..b1c0a3a
--- /dev/null
+++ b/core/java/android/content/IRestrictionsManager.aidl
@@ -0,0 +1,30 @@
+/*
+ * 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.os.Bundle;
+
+/**
+ * Interface used by the RestrictionsManager
+ * @hide
+ */
+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/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..b4289db2 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 b328f578..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 480cb057..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/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..474192fd 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 5e005d0..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;
@@ -45,11 +48,14 @@
 
     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 int mOriginalStreamVolume;
     private Ringtone mRingtone;
@@ -63,17 +69,6 @@
     private static final int MSG_INIT_SAMPLE = 3;
     private static final int CHECK_RINGTONE_PLAYBACK_DELAY_MS = 1000;
 
-    private ContentObserver mVolumeObserver = new ContentObserver(new Handler()) {
-        @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, int streamType, Uri defaultUri,
             Callback callback) {
         mContext = context;
@@ -85,10 +80,11 @@
         mHandler = new Handler(thread.getLooper(), this);
         mCallback = callback;
         mOriginalStreamVolume = mAudioManager.getStreamVolume(mStreamType);
+        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;
@@ -103,6 +99,9 @@
     }
 
     public void setSeekBar(SeekBar seekBar) {
+        if (mSeekBar != null) {
+            mSeekBar.setOnSeekBarChangeListener(null);
+        }
         mSeekBar = seekBar;
         mSeekBar.setOnSeekBarChangeListener(null);
         mSeekBar.setMax(mMaxStreamVolume);
@@ -150,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);
+                }
             }
         }
     }
@@ -172,6 +175,8 @@
         postStopSample();
         mContext.getContentResolver().unregisterContentObserver(mVolumeObserver);
         mSeekBar.setOnSeekBarChangeListener(null);
+        mReceiver.setListening(false);
+        mHandler.getLooper().quitSafely();
     }
 
     public void revertVolume() {
@@ -252,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/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/provider/TvContract.java b/core/java/android/provider/TvContract.java
index e4f93a8..0d90a16 100644
--- a/core/java/android/provider/TvContract.java
+++ b/core/java/android/provider/TvContract.java
@@ -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.
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..e17b498 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.
@@ -876,7 +875,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,12 +886,35 @@
      *            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) {
+        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(ACTION_QUEUE_SPEAK_NAME) {
             @Override
             public void run(ITextToSpeechService service) throws RemoteException {
@@ -908,7 +930,7 @@
 
                 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);
                 }
@@ -931,12 +953,37 @@
      * @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) {
+        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(ACTION_QUEUE_SYNTHESIZE_TO_FILE) {
             @Override
             public void run(ITextToSpeechService service) throws RemoteException {
@@ -964,8 +1011,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);
@@ -981,6 +1027,13 @@
         });
     }
 
+    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";
 
     /**
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/ITvInputClient.aidl b/core/java/android/tv/ITvInputClient.aidl
index ac83356..ef89c68 100644
--- a/core/java/android/tv/ITvInputClient.aidl
+++ b/core/java/android/tv/ITvInputClient.aidl
@@ -17,6 +17,7 @@
 package android.tv;
 
 import android.content.ComponentName;
+import android.os.Bundle;
 import android.tv.ITvInputSession;
 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/ITvInputSessionCallback.aidl b/core/java/android/tv/ITvInputSessionCallback.aidl
index a2bd0d7..e27b8bf 100644
--- a/core/java/android/tv/ITvInputSessionCallback.aidl
+++ b/core/java/android/tv/ITvInputSessionCallback.aidl
@@ -16,6 +16,7 @@
 
 package android.tv;
 
+import android.os.Bundle;
 import android.tv.ITvInputSession;
 
 /**
@@ -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/TvInputInfo.java b/core/java/android/tv/TvInputInfo.java
index 50462cc..217e4b7 100644
--- a/core/java/android/tv/TvInputInfo.java
+++ b/core/java/android/tv/TvInputInfo.java
@@ -39,7 +39,7 @@
     public TvInputInfo(ResolveInfo service) {
         mService = service;
         ServiceInfo si = service.serviceInfo;
-        mId = generateInputIdForComponenetName(new ComponentName(si.packageName, si.name));
+        mId = generateInputIdForComponentName(new ComponentName(si.packageName, si.name));
     }
 
     /**
@@ -134,7 +134,7 @@
      * @return the generated input id for the given {@code name}.
      * @hide
      */
-    public static final String generateInputIdForComponenetName(ComponentName name) {
+    public static final String generateInputIdForComponentName(ComponentName name) {
         return name.flattenToShortString();
     }
 
diff --git a/core/java/android/tv/TvInputManager.java b/core/java/android/tv/TvInputManager.java
index dfa84f8..d0c2ca6 100644
--- a/core/java/android/tv/TvInputManager.java
+++ b/core/java/android/tv/TvInputManager.java
@@ -18,6 +18,7 @@
 
 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/core/java/android/tv/TvInputService.java
index eeb738d..03d24db 100644
--- a/core/java/android/tv/TvInputService.java
+++ b/core/java/android/tv/TvInputService.java
@@ -23,6 +23,7 @@
 import android.graphics.PixelFormat;
 import android.graphics.Rect;
 import android.net.Uri;
+import android.os.Bundle;
 import android.os.Handler;
 import android.os.IBinder;
 import android.os.Message;
@@ -69,7 +70,7 @@
     @Override
     public void onCreate() {
         super.onCreate();
-        mId = TvInputInfo.generateInputIdForComponenetName(
+        mId = TvInputInfo.generateInputIdForComponentName(
                 new ComponentName(getPackageName(), getClass().getName()));
     }
 
@@ -156,6 +157,7 @@
         private boolean mOverlayViewEnabled;
         private IBinder mWindowToken;
         private Rect mOverlayFrame;
+        private ITvInputSessionCallback mSessionCallback;
 
         public TvInputSessionImpl() {
             mWindowManager = (WindowManager) getSystemService(Context.WINDOW_SERVICE);
@@ -188,6 +190,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 +442,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 +477,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 +493,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 +540,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 +563,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/TvView.java b/core/java/android/tv/TvView.java
index 59b6386..2d31701 100644
--- a/core/java/android/tv/TvView.java
+++ b/core/java/android/tv/TvView.java
@@ -18,6 +18,7 @@
 
 import android.content.Context;
 import android.graphics.Rect;
+import android.os.Bundle;
 import android.os.Handler;
 import android.text.TextUtils;
 import android.tv.TvInputManager.Session;
@@ -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/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..b500e46 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);
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/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/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 df96488..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,15 +366,53 @@
 
     @VisibleForTesting
     public static class ControllerImpl {
-        // TODO: Switch to DynamicRotationList for smarter rotation.
-        private final StaticRotationList mSwitchingAwareSubtypeList;
-        private final StaticRotationList mSwitchingUnawareSubtypeList;
+        private final DynamicRotationList mSwitchingAwareRotationList;
+        private final StaticRotationList mSwitchingUnawareRotationList;
 
-        public ControllerImpl(final List<ImeSubtypeListItem> sortedItems) {
-            mSwitchingAwareSubtypeList = new StaticRotationList(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,
@@ -364,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,10 +433,9 @@
             if (imi == null) {
                 return;
             }
-            // TODO: Enable the following code when DynamicRotationList is enabled.
-            // if (imi.supportsSwitchingToNextInputMethod()) {
-            //     mSwitchingAwareSubtypeList.onUserAction(imi, subtype);
-            // }
+            if (imi.supportsSwitchingToNextInputMethod()) {
+                mSwitchingAwareRotationList.onUserAction(imi, subtype);
+            }
         }
 
         private static List<ImeSubtypeListItem> filterImeSubtypeList(
@@ -424,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/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/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/FontFamily.cpp b/core/jni/android/graphics/FontFamily.cpp
index 041790f..3bab8a2 100644
--- a/core/jni/android/graphics/FontFamily.cpp
+++ b/core/jni/android/graphics/FontFamily.cpp
@@ -31,9 +31,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
@@ -67,7 +72,7 @@
 ///////////////////////////////////////////////////////////////////////////////
 
 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 },
 };
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/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_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..bb6a1cb 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 -->
     <!-- ====================================== -->
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_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 e109425f4..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/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/values-fr/strings.xml b/core/res/res/values-fr/strings.xml
index a7cd9a7..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>
diff --git a/core/res/res/values-km-rKH/strings.xml b/core/res/res/values-km-rKH/strings.xml
index dbe2d7f..f3b8495 100644
--- a/core/res/res/values-km-rKH/strings.xml
+++ b/core/res/res/values-km-rKH/strings.xml
@@ -68,7 +68,7 @@
   </plurals>
     <string name="imei" msgid="2625429890869005782">"IMEI"</string>
     <string name="meid" msgid="4841221237681254195">"MEID"</string>
-    <string name="ClipMmi" msgid="6952821216480289285">"លេខ​សម្គាល់​អ្នក​ហៅ​​ចូល​"</string>
+    <string name="ClipMmi" msgid="6952821216480289285">"លេខ​សម្គាល់​អ្នក​ហៅ​​ចូល"</string>
     <string name="ClirMmi" msgid="7784673673446833091">"លេខ​សម្គាល់​អ្នក​ហៅ​ចេញ"</string>
     <string name="CfMmi" msgid="5123218989141573515">"បញ្ជូន​ការ​ហៅ​បន្ត"</string>
     <string name="CwMmi" msgid="9129678056795016867">"រង់ចាំ​ការ​ហៅ"</string>
@@ -125,7 +125,7 @@
     <string name="cfTemplateRegisteredTime" msgid="6781621964320635172">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g> ៖ មិន​បាន​បញ្ជូន​បន្ត"</string>
     <string name="fcComplete" msgid="3118848230966886575">"កូដ​លក្ខណៈ​ពេញលេញ។"</string>
     <string name="fcError" msgid="3327560126588500777">"បញ្ហា​ការ​តភ្ជាប់​ ឬ​កូដ​លក្ខណៈ​​​មិន​ត្រឹមត្រូវ​។"</string>
-    <string name="httpErrorOk" msgid="1191919378083472204">"យល់​ព្រម​"</string>
+    <string name="httpErrorOk" msgid="1191919378083472204">"យល់​ព្រម"</string>
     <string name="httpError" msgid="7956392511146698522">"មាន​កំហុស​បណ្ដាញ។"</string>
     <string name="httpErrorLookup" msgid="4711687456111963163">"រក​មិន​ឃើញ URL ។"</string>
     <string name="httpErrorUnsupportedAuthScheme" msgid="6299980280442076799">"គ្រោងការណ៍​ផ្ទៀងផ្ទាត់​តំបន់បណ្ដាញ​មិន​ត្រូវ​បាន​គាំទ្រ។"</string>
@@ -183,7 +183,7 @@
     <string name="global_action_silent_mode_off_status" msgid="1506046579177066419">"បើក​សំឡេង"</string>
     <string name="global_actions_toggle_airplane_mode" msgid="5884330306926307456">"ពេល​ជិះ​យន្តហោះ"</string>
     <string name="global_actions_airplane_mode_on_status" msgid="2719557982608919750">"បាន​បើក​របៀប​ពេល​ជិះ​យន្ត​ហោះ"</string>
-    <string name="global_actions_airplane_mode_off_status" msgid="5075070442854490296">"បាន​បិទ​របៀបពេលជិះ​យន្តហោះ​"</string>
+    <string name="global_actions_airplane_mode_off_status" msgid="5075070442854490296">"បាន​បិទ​របៀបពេលជិះ​យន្តហោះ"</string>
     <string name="global_action_settings" msgid="1756531602592545966">"ការ​កំណត់"</string>
     <string name="status_bar_notification_info_overflow" msgid="5301981741705354993">"999+"</string>
     <string name="safeMode" msgid="2788228061547930246">"របៀប​​​សុវត្ថិភាព"</string>
@@ -195,7 +195,7 @@
     <string name="permgrouplab_messages" msgid="7521249148445456662">"សារ​របស់​អ្នក"</string>
     <string name="permgroupdesc_messages" msgid="7821999071003699236">"អាន និង​សរសេរ​សារ SMS, អ៊ីមែល និង​សារ​ផ្សេងៗ​ទៀត​របស់​អ្នក។"</string>
     <string name="permgrouplab_personalInfo" msgid="3519163141070533474">"ព័ត៌មាន​ផ្ទាល់ខ្លួន​របស់​អ្នក"</string>
-    <string name="permgroupdesc_personalInfo" msgid="8426453129788861338">"ចូល​ដំណើរការ​ព័ត៌មាន​ដោយ​ផ្ទាល់​អំពី​អ្នក​ ដែល​បា​ន​រក្សាទុក​ក្នុង​កាត​ទំនាក់ទំនង​របស់​អ្នក។​"</string>
+    <string name="permgroupdesc_personalInfo" msgid="8426453129788861338">"ចូល​ដំណើរការ​ព័ត៌មាន​ដោយ​ផ្ទាល់​អំពី​អ្នក​ ដែល​បា​ន​រក្សាទុក​ក្នុង​កាត​ទំនាក់ទំនង​របស់​អ្នក។"</string>
     <string name="permgrouplab_socialInfo" msgid="5799096623412043791">"ព័ត៌មាន​សង្គម​របស់​អ្នក"</string>
     <string name="permgroupdesc_socialInfo" msgid="7129842457611643493">"ចូល​ដំណើរការ​ព័ត៌មាន​ដោយ​ផ្ទាល់​អំពី​ទំនាក់ទំនង និង​ការ​ភ្ជាប់​សង្គម​របស់​អ្នក។"</string>
     <string name="permgrouplab_location" msgid="635149742436692049">"ទីតាំង​របស់​អ្នក"</string>
@@ -384,7 +384,7 @@
     <string name="permdesc_readInputState" msgid="8387754901688728043">"ឲ្យ​កម្មវិធី​មើល​គ្រាប់​ចុច​ដែល​អ្នក​ចុច​ពេល​មាន​អន្តរកម្ម​ជា​មួយ​កម្មវិធី​ផ្សេង (ដូចជា បញ្ចូល​ពាក្យ​សម្ងាត់)។ មិន​គួរ​ចាំបាច់​សម្រាប់​កម្មវិធី​ធម្មតា​ទេ។"</string>
     <string name="permlab_bindInputMethod" msgid="3360064620230515776">"ចង​ទៅ​វិធីសាស្ត្រ​បញ្ចូល"</string>
     <string name="permdesc_bindInputMethod" msgid="3250440322807286331">"ឲ្យ​ម្ចាស់​ចង​ចំណុច​ប្រទាក់​កម្រិត​កំពូល​នៃ​វិធី​សាស្ត្រ​បញ្ចូល។ មិន​គួរ​​ចាំបាច់​សម្រាប់​កម្មវិធី​ធម្មតា​ទេ។"</string>
-    <string name="permlab_bindAccessibilityService" msgid="5357733942556031593">"ចង​សេវា​កម្ម​ភាព​មធ្យោបាយ​ងាយស្រួល​"</string>
+    <string name="permlab_bindAccessibilityService" msgid="5357733942556031593">"ចង​សេវា​កម្ម​ភាព​មធ្យោបាយ​ងាយស្រួល"</string>
     <string name="permdesc_bindAccessibilityService" msgid="7034615928609331368">"ឲ្យ​​ម្ចាស់​ចង​ចំណុច​ប្រទាក់​កម្រិត​កំពូល​នៃ​សេវាកម្ម​ភាព​ងាយស្រួល។ មិន​គួរ​ចាំបាច់​សម្រាប់​កម្មវិធី​ធម្មតា​ទេ។"</string>
     <string name="permlab_bindPrintService" msgid="8462815179572748761">"ចង​សេវាកម្ម​​បោះពុម្ព"</string>
     <string name="permdesc_bindPrintService" msgid="7960067623209111135">"ឲ្យ​ម្ចាស់​ចង​ចំណុច​ប្រទាក់​កម្រិត​កំពូល​នៃ​សេវាកម្ម​ធាតុ​ក្រាហ្វិក។ មិន​គួរ​​ចាំបាច់​សម្រាប់​កម្មវិធី​ធម្មតា​ទេ។"</string>
@@ -402,7 +402,7 @@
     <string name="permdesc_bindVoiceInteraction" msgid="2345721766501778101">"អនុញ្ញាត​ឲ្យ​ម្ចាស់​ភ្ជាប់​ទៅ​ចំណុច​ប្រទាក់​កម្រិត​កំពូល​​របស់​សេវាកម្ម​អន្តរកម្ម​សំឡេង។ មិន​គួរ​ចាំបាច់​សម្រាប់​កម្មវិធី​ធម្មតា​ទេ។"</string>
     <string name="permlab_bindRemoteDisplay" msgid="1782923938029941960">"ភ្ជាប់​ទៅ​ការ​បង្ហាញ​ពី​ចម្ងាយ"</string>
     <string name="permdesc_bindRemoteDisplay" msgid="1261242718727295981">"អនុញ្ញាត​ឲ្យ​ម្ចាស់​ភ្ជាប់​​ទៅ​ចំណុច​ប្រទាក់​កម្រិត​កំពូល​នៃ​ការ​បង្ហាញ​ពី​ចម្ងាយ។ មិន​គួរ​ចាំបាច់​សម្រាប់​កម្មវិធី​ធម្មតា​ទេ។"</string>
-    <string name="permlab_bindRemoteViews" msgid="5697987759897367099">"ចង​សេវា​កម្ម​ធាតុ​ក្រាហ្វិក​"</string>
+    <string name="permlab_bindRemoteViews" msgid="5697987759897367099">"ចង​សេវា​កម្ម​ធាតុ​ក្រាហ្វិក"</string>
     <string name="permdesc_bindRemoteViews" msgid="4717987810137692572">"ឲ្យ​ម្ចាស់​ចង​ចំណុច​ប្រទាក់​កម្រិត​កំពូល​នៃ​សេវាកម្ម​ធាតុ​ក្រាហ្វិក។ មិន​គួរ​​ចាំបាច់​សម្រាប់​កម្មវិធី​ធម្មតា​ទេ។"</string>
     <string name="permlab_bindRouteProvider" msgid="4869394607915096847">"ភ្ជាប់​ទៅ​សេវាកម្ម​ក្រុមហ៊ុន​ផ្ដល់​ច្រក"</string>
     <string name="permdesc_bindRouteProvider" msgid="4703804520859960329">"អនុញ្ញាត​ឲ្យ​ម្ចាស់​ភ្ជាប់​ទៅ​ក្រុមហ៊ុន​ផ្ដល់​​ច្រក​ដែល​បាន​ចុះ​ឈ្មោះ។ មិន​គួរ​ចាំបាច់​សម្រាប់​កម្មវិធី​ធម្មតា​ទេ។"</string>
@@ -410,7 +410,7 @@
     <string name="permdesc_bindDeviceAdmin" msgid="569715419543907930">"ឲ្យ​ម្ចាស់​ផ្ញើ​គោលបំណង​​ទៅ​អ្នក​គ្រប់គ្រង​ឧបករណ៍។ មិន​គួរ​ចាំបាច់​សម្រាប់​កម្មវិធី​ធម្មតា​ទេ។"</string>
     <string name="permlab_bindTvInput" msgid="5601264742478168987">"ភ្ជាប់​ទៅ​ការ​បញ្ចូល​ទូរទស្សន៍"</string>
     <string name="permdesc_bindTvInput" msgid="2371008331852001924">"អនុញ្ញាត​ឲ្យ​ម្ចាស់​ភ្ជាប់​ទៅ​ចំណុចប្រទាក់​កម្រិត​ខ្ពស់​នៃ​ការ​បញ្ចូល​ទូរទស្សន៍។ មិន​គួរ​ចាំបាច់​សម្រាប់​កម្មវិធី​ធម្មតា​ទេ។"</string>
-    <string name="permlab_manageDeviceAdmins" msgid="4248828900045808722">"បន្ថែម​ ឬ​លុប​កម្មវិធី​គ្រប់គ្រង​​​ឧបករណ៍​​"</string>
+    <string name="permlab_manageDeviceAdmins" msgid="4248828900045808722">"បន្ថែម​ ឬ​លុប​កម្មវិធី​គ្រប់គ្រង​​​ឧបករណ៍"</string>
     <string name="permdesc_manageDeviceAdmins" msgid="5025608167709942485">"អនុញ្ញាត​​​ឲ្យ​ម្ចាស់​​​បន្ថែម​ ឬ​លុប​កម្មវិធី​គ្រប់គ្រង​ឧបករណ៍​សកម្ម​ចេញ​។ មិន​គួរ​ប្រើ​សម្រាប់​កម្មវិធី​​ធម្មតា​ទេ​។"</string>
     <string name="permlab_setOrientation" msgid="3365947717163866844">"ប្ដូរ​ទិស​អេក្រង់"</string>
     <string name="permdesc_setOrientation" msgid="3046126619316671476">"ឲ្យ​កម្មវិធី​ប្ដូរ​ការ​បង្វិល​អេក្រង់​នៅ​ពេល​ណា​មួយ។ មិន​ចាំបាច់​សម្រាប់​កម្មវិធី​ធម្មតា​ទេ។"</string>
@@ -422,9 +422,9 @@
     <string name="permdesc_signalPersistentProcesses" msgid="4896992079182649141">"ឲ្យ​កម្មវិធី​ស្នើ​​សញ្ញា​ដែល​បាន​ផ្ដល់​ត្រូវ​ផ្ញើ​ទៅ​ដំណើរការ​ស្ថិតស្ថេរ​​ទាំង​អស់។"</string>
     <string name="permlab_persistentActivity" msgid="8841113627955563938">"ធ្វើ​ឲ្យ​កម្មវិធី​ដំណើរការ​ជា​និច្ច"</string>
     <string name="permdesc_persistentActivity" product="tablet" msgid="8525189272329086137">"ឲ្យ​កម្មវិធី​ធ្វើជា​ផ្នែក​​ស្ថិតស្ថេរ​ដោយ​ខ្លួន​ឯង​ក្នុង​អង្គ​ចងចាំ។ វា​អាច​កំណត់​អង្គ​ចងចាំ​ដែល​អាច​ប្រើ​បាន​ចំពោះ​កម្មវិធី​ផ្សេងៗ​ ដោយ​ធ្វើឲ្យ​កុំព្យូទ័រ​បន្ទះ​យឺត។"</string>
-    <string name="permdesc_persistentActivity" product="default" msgid="4384760047508278272">"ឲ្យ​កម្មវិធី ធ្វើជា​ផ្នែក​អចិន្ត្រៃយ៍​នៃ​ខ្លួន​ក្នុង​អង្គ​ចងចាំ។ វា​អាច​កម្រិត​អង្គ​ចងចាំ​អាច​ប្រើ​បាន​ ដើម្បី​ធ្វើ​ឲ្យ​កម្មវិធី​ផ្សេង​ធ្វើ​ឲ្យ​ទូរស័ព្ទ​របស់​អ្នក​យឺត។​"</string>
+    <string name="permdesc_persistentActivity" product="default" msgid="4384760047508278272">"ឲ្យ​កម្មវិធី ធ្វើជា​ផ្នែក​អចិន្ត្រៃយ៍​នៃ​ខ្លួន​ក្នុង​អង្គ​ចងចាំ។ វា​អាច​កម្រិត​អង្គ​ចងចាំ​អាច​ប្រើ​បាន​ ដើម្បី​ធ្វើ​ឲ្យ​កម្មវិធី​ផ្សេង​ធ្វើ​ឲ្យ​ទូរស័ព្ទ​របស់​អ្នក​យឺត។"</string>
     <string name="permlab_deletePackages" msgid="184385129537705938">"លុប​កម្មវិធី"</string>
-    <string name="permdesc_deletePackages" msgid="7411480275167205081">"ឲ្យ​កម្មវិធី​លុប​កញ្ចប់ Android ។ កម្មវិធី​ព្យាបាទ​អាច​ប្រើ​វា ដើម្បី​លុប​កម្មវិធី​សំខាន់​ៗ។ ​"</string>
+    <string name="permdesc_deletePackages" msgid="7411480275167205081">"ឲ្យ​កម្មវិធី​លុប​កញ្ចប់ Android ។ កម្មវិធី​ព្យាបាទ​អាច​ប្រើ​វា ដើម្បី​លុប​កម្មវិធី​សំខាន់​ៗ។"</string>
     <string name="permlab_clearAppUserData" msgid="274109191845842756">"លុប​ទិន្នន័យ​របស់​​កម្មវិធី​ផ្សេង"</string>
     <string name="permdesc_clearAppUserData" msgid="4625323684125459488">"ឲ្យ​កម្មវិធី​សម្អាត​ទិន្នន័យ​អ្នក​ប្រើ។"</string>
     <string name="permlab_deleteCacheFiles" msgid="3128665571837408675">"លុប​ឃ្លាំង​សម្ងាត់​កម្មវិធី​ផ្សេងៗ"</string>
@@ -475,7 +475,7 @@
     <string name="permdesc_writeContacts" product="tablet" msgid="897243932521953602">"ឲ្យ​កម្មវិធី​កែ​ទិន្នន័យ​អំពី​ទំនាក់ទំនង​របស់​អ្នក​ដែល​បាន​រក្សាទុក​ក្នុង​កុំព្យូទ័រ​បន្ទះ រួមមាន​ប្រេកង់​​ដែល​អ្នក​បាន​ហៅ អ៊ីមែល ឬ​ទាក់ទង​តាម​វិធី​ផ្សេងៗ​ជា​មួយ​ទំនាក់ទំនង​ជាក់លាក់។ សិទ្ធិ​​នេះ​អនុញ្ញាត​ឲ្យ​​​កម្មវិធី​លុប​ទិន្នន័យ​ទំនាក់ទំនង​របស់​អ្នក។"</string>
     <string name="permdesc_writeContacts" product="default" msgid="589869224625163558">"ឲ្យ​កម្មវិធី​កែ​ទិន្នន័យ​អំពី​ទំនាក់ទំនង​របស់​អ្នក​ដែល​បាន​រក្សាទុក​ក្នុង​ទូរស័ព្ទ​របស់​អ្នក រួមមាន​ប្រេកង់​ដែល​អ្នក​បាន​ហៅ អ៊ីមែល ឬ​បាន​ទាក់ទង​​តាម​វិធី​ផ្សេងៗ​ជា​មួយ​ទំនាក់​ទំនាក់​ជាក់លាក់។ សិទ្ធិ​នេះ​ឲ្យ​កម្មវិធី​លុប​ទិន្នន័យ​ទំនាក់ទំនង។"</string>
     <string name="permlab_readCallLog" msgid="3478133184624102739">"អាន​​កំណត់​ហេតុ​​​ហៅ"</string>
-    <string name="permdesc_readCallLog" product="tablet" msgid="3700645184870760285">"ឲ្យ​កម្មវិធី​អាន​បញ្ជី​ហៅ​កុំព្យូទ័រ​បន្ទះ​របស់​អ្នក រួមមាន​ទិន្នន័យ​អំពី​ការ​ហៅ​ចូល និង​ចេញ។ សិទ្ធិ​នេះ​អនុញ្ញាត​ឲ្យ​កម្មវិធី​រក្សាទុក​ទិន្នន័យ​បញ្ជី​ហៅ​របស់​អ្នក ហើយ​កម្មវិធី​ព្យាបាទ​អាច​ចែករំលែក​ទិន្នន័យ​បញ្ជី​ហៅ​ដោយ​មិន​ឲ្យ​អ្នក​ដឹង។​"</string>
+    <string name="permdesc_readCallLog" product="tablet" msgid="3700645184870760285">"ឲ្យ​កម្មវិធី​អាន​បញ្ជី​ហៅ​កុំព្យូទ័រ​បន្ទះ​របស់​អ្នក រួមមាន​ទិន្នន័យ​អំពី​ការ​ហៅ​ចូល និង​ចេញ។ សិទ្ធិ​នេះ​អនុញ្ញាត​ឲ្យ​កម្មវិធី​រក្សាទុក​ទិន្នន័យ​បញ្ជី​ហៅ​របស់​អ្នក ហើយ​កម្មវិធី​ព្យាបាទ​អាច​ចែករំលែក​ទិន្នន័យ​បញ្ជី​ហៅ​ដោយ​មិន​ឲ្យ​អ្នក​ដឹង។"</string>
     <string name="permdesc_readCallLog" product="default" msgid="5777725796813217244">"ឲ្យ​កម្មវិធី​អាន​​​បញ្ជី​ហៅ​ទូរស័ព្ទ​របស់​អ្នក រួមមាន​ទិន្នន័យ​អំពី​ការ​ហៅ​ចូល និង​ចេញ។ សិទ្ធិ​នេះ​អនុញ្ញាត​ឲ្យ​កម្មវិធី​រក្សាទុក​ទិន្នន័យ​បញ្ជី​ហៅ​របស់​អ្នក ហើយ​កម្មវិធី​ព្យាបាទ​អាច​ចែករំលែក​ទិន្នន័យ​បញ្ជី​ហៅ​ដោយ​មិន​ឲ្យ​អ្នកដឹង។"</string>
     <string name="permlab_writeCallLog" msgid="8552045664743499354">"សរសេរ​បញ្ជី​ហៅ"</string>
     <string name="permdesc_writeCallLog" product="tablet" msgid="6661806062274119245">"ឲ្យ​កម្មវិធី​កែ​បញ្ជី​ហៅ​កុំព្យូទ័រ​បន្ទះ​របស់​អ្នក​រួមមាន​ទិន្នន័យ​អំពី​ការ​ហៅ​ចូល និង​ចេញ។​កម្មវិធី​ព្យាបាទ​អាច​ប្រើ​វា ដើម្បី​លុប ឬ​កែ​បញ្ជី​ហៅ​របស់​អ្នក។"</string>
@@ -609,7 +609,7 @@
     <string name="permdesc_setWallpaperHints" msgid="8235784384223730091">"ឲ្យ​កម្មវិធី​កំណត់​ជំនួយ​ទំហំ​ផ្ទាំង​រូបភាព​ប្រព័ន្ធ។"</string>
     <string name="permlab_masterClear" msgid="2315750423139697397">"កំណត់​ប្រព័ន្ធ​ទៅ​លំនាំដើម​រោងចក្រ​ឡើងវិញ"</string>
     <string name="permdesc_masterClear" msgid="3665380492633910226">"ឲ្យ​កម្មវិធី​កំណត់​ប្រព័ន្ធ​​ដូច​ការ​កំណត់​ចេញ​ពី​រោងចក្រ​ឡើងវិញ​ពេញលេញ ដោយ​លុប​ទិន្នន័យ ការ​កំណត់​រចនាសម្ព័ន្ធ និង​កម្មវិធី​បាន​ដំឡើង។"</string>
-    <string name="permlab_setTime" msgid="2021614829591775646">"កំណត់​​ម៉ោង​"</string>
+    <string name="permlab_setTime" msgid="2021614829591775646">"កំណត់​​ម៉ោង"</string>
     <string name="permdesc_setTime" product="tablet" msgid="1896341438151152881">"ឲ្យ​កម្មវិធី​ប្ដូរ​ម៉ោង​កុំព្យូទ័រ​បន្ទះ។"</string>
     <string name="permdesc_setTime" product="default" msgid="1855702730738020">"ឲ្យ​កម្មវិធី​ប្ដូរ​ម៉ោង​ទូរស័ព្ទ។"</string>
     <string name="permlab_setTimeZone" msgid="2945079801013077340">"កំណត់​តំបន់​ពេលវេលា"</string>
@@ -775,7 +775,7 @@
   <string-array name="organizationTypes">
     <item msgid="7546335612189115615">"កន្លែង​ធ្វើការ"</item>
     <item msgid="4378074129049520373">"ផ្សេងៗ"</item>
-    <item msgid="3455047468583965104">"តាម​តម្រូវ​ការ​"</item>
+    <item msgid="3455047468583965104">"តាម​តម្រូវ​ការ"</item>
   </string-array>
   <string-array name="imProtocols">
     <item msgid="8595261363518459565">"AIM"</item>
@@ -791,7 +791,7 @@
     <string name="phoneTypeHome" msgid="2570923463033985887">"ផ្ទះ"</string>
     <string name="phoneTypeMobile" msgid="6501463557754751037">"​ចល័ត"</string>
     <string name="phoneTypeWork" msgid="8863939667059911633">"កន្លែង​ធ្វើការ"</string>
-    <string name="phoneTypeFaxWork" msgid="3517792160008890912">"ទូរសារ​កន្លែង​ធ្វើការ​"</string>
+    <string name="phoneTypeFaxWork" msgid="3517792160008890912">"ទូរសារ​កន្លែង​ធ្វើការ"</string>
     <string name="phoneTypeFaxHome" msgid="2067265972322971467">"ទូរសារ​ផ្ទះ"</string>
     <string name="phoneTypePager" msgid="7582359955394921732">"ភេយ័រ"</string>
     <string name="phoneTypeOther" msgid="1544425847868765990">"ផ្សេងៗ"</string>
@@ -916,7 +916,7 @@
     <string name="lockscreen_glogin_too_many_attempts" msgid="2751368605287288808">"ព្យាយាម​លំនាំ​ច្រើន​ពេក"</string>
     <string name="lockscreen_glogin_instructions" msgid="3931816256100707784">"ដើម្បី​ដោះ​សោ ចូល​គណនី Google របស់​អ្នក។"</string>
     <string name="lockscreen_glogin_username_hint" msgid="8846881424106484447">"ឈ្មោះ​អ្នក​ប្រើ (អ៊ីមែល​)"</string>
-    <string name="lockscreen_glogin_password_hint" msgid="5958028383954738528">"ពាក្យសម្ងាត់​"</string>
+    <string name="lockscreen_glogin_password_hint" msgid="5958028383954738528">"ពាក្យសម្ងាត់"</string>
     <string name="lockscreen_glogin_submit_button" msgid="7130893694795786300">"ចូល"</string>
     <string name="lockscreen_glogin_invalid_input" msgid="1364051473347485908">"ឈ្មោះ​អ្នកប្រើ ឬ​ពាក្យ​សម្ងាត់​មិន​ត្រឹមត្រូវ។"</string>
     <string name="lockscreen_glogin_account_recovery_hint" msgid="1696924763690379073">"ភ្លេច​ឈ្មោះ​អ្នក​ប្រើ ឬ​ពាក្យ​សម្ងាត់​របស់​អ្នក?\nមើល "<b>"google.com/accounts/recovery"</b>" ។"</string>
@@ -961,7 +961,7 @@
     <string name="factorytest_failed" msgid="5410270329114212041">"បាន​បរាជ័យ​ក្នុង​ការ​សាកល្បង​រោងចក្រ"</string>
     <string name="factorytest_not_system" msgid="4435201656767276723">"សកម្មភាព FACTORY_TEST ត្រូវ​បាន​គាំទ្រ​សម្រាប់​តែ​កញ្ចប់​បាន​ដំឡើង​ក្នុង /system/app."</string>
     <string name="factorytest_no_action" msgid="872991874799998561">"រក​មិន​ឃើញ​កញ្ចប់​ដែល​ផ្ដល់​សកម្មភាព FACTORY_TEST ។"</string>
-    <string name="factorytest_reboot" msgid="6320168203050791643">"ចាប់​ផ្ដើម​ឡើង​វិញ​"</string>
+    <string name="factorytest_reboot" msgid="6320168203050791643">"ចាប់​ផ្ដើម​ឡើង​វិញ"</string>
     <string name="js_dialog_title" msgid="1987483977834603872">"ទំព័រ​មាន​ចំណងជើង \"<xliff:g id="TITLE">%s</xliff:g>\" សរសេរ៖"</string>
     <string name="js_dialog_title_default" msgid="6961903213729667573">"JavaScript"</string>
     <string name="js_dialog_before_unload_title" msgid="2619376555525116593">"បញ្ជាក់​ការ​រុករក"</string>
@@ -1019,7 +1019,7 @@
     <string name="prepend_shortcut_label" msgid="2572214461676015642">"ម៉ឺនុយ +"</string>
     <string name="menu_space_shortcut_label" msgid="2410328639272162537">"ដកឃ្លា"</string>
     <string name="menu_enter_shortcut_label" msgid="2743362785111309668">"enter"</string>
-    <string name="menu_delete_shortcut_label" msgid="3658178007202748164">"លុប​"</string>
+    <string name="menu_delete_shortcut_label" msgid="3658178007202748164">"លុប"</string>
     <string name="search_go" msgid="8298016669822141719">"ស្វែងរក"</string>
     <string name="searchview_description_search" msgid="6749826639098512120">"ស្វែងរក"</string>
     <string name="searchview_description_query" msgid="5911778593125355124">"ស្វែងរក​សំណួរ"</string>
@@ -1103,18 +1103,18 @@
     <string name="preposition_for_date" msgid="9093949757757445117">"នៅ <xliff:g id="DATE">%s</xliff:g>"</string>
     <string name="preposition_for_time" msgid="5506831244263083793">"នៅ​ម៉ោង <xliff:g id="TIME">%s</xliff:g>"</string>
     <string name="preposition_for_year" msgid="5040395640711867177">"ក្នុង​ឆ្នាំ <xliff:g id="YEAR">%s</xliff:g>"</string>
-    <string name="day" msgid="8144195776058119424">"ថ្ងៃ​"</string>
+    <string name="day" msgid="8144195776058119424">"ថ្ងៃ"</string>
     <string name="days" msgid="4774547661021344602">"​ថ្ងៃ"</string>
     <string name="hour" msgid="2126771916426189481">"ម៉ោង"</string>
     <string name="hours" msgid="894424005266852993">"ម៉ោង"</string>
-    <string name="minute" msgid="9148878657703769868">"នាទី​"</string>
+    <string name="minute" msgid="9148878657703769868">"នាទី"</string>
     <string name="minutes" msgid="5646001005827034509">"នាទី"</string>
-    <string name="second" msgid="3184235808021478">"វិនាទី​"</string>
+    <string name="second" msgid="3184235808021478">"វិនាទី"</string>
     <string name="seconds" msgid="3161515347216589235">"វិនាទី"</string>
-    <string name="week" msgid="5617961537173061583">"សប្ដាហ៍​"</string>
-    <string name="weeks" msgid="6509623834583944518">"សប្ដាហ៍​"</string>
-    <string name="year" msgid="4001118221013892076">"ឆ្នាំ​"</string>
-    <string name="years" msgid="6881577717993213522">"ឆ្នាំ​"</string>
+    <string name="week" msgid="5617961537173061583">"សប្ដាហ៍"</string>
+    <string name="weeks" msgid="6509623834583944518">"សប្ដាហ៍"</string>
+    <string name="year" msgid="4001118221013892076">"ឆ្នាំ"</string>
+    <string name="years" msgid="6881577717993213522">"ឆ្នាំ"</string>
   <plurals name="duration_seconds">
     <item quantity="one" msgid="6962015528372969481">"1 វិនាទី"</item>
     <item quantity="other" msgid="1886107766577166786">"<xliff:g id="COUNT">%d</xliff:g> វិនាទី"</item>
@@ -1130,12 +1130,12 @@
     <string name="VideoView_error_title" msgid="3534509135438353077">"បញ្ហា​វីដេអូ"</string>
     <string name="VideoView_error_text_invalid_progressive_playback" msgid="3186670335938670444">"វីដេអូ​នេះ​មិន​ត្រឹមត្រូវ​សម្រាប់​​ចរន្ត​ចូល​ឧបករណ៍​នេះ។"</string>
     <string name="VideoView_error_text_unknown" msgid="3450439155187810085">"មិន​អាច​ចាក់​វីដេអូ​នេះ។"</string>
-    <string name="VideoView_error_button" msgid="2822238215100679592">"យល់​ព្រម​"</string>
+    <string name="VideoView_error_button" msgid="2822238215100679592">"យល់​ព្រម"</string>
     <string name="relative_time" msgid="1818557177829411417">"<xliff:g id="DATE">%1$s</xliff:g>, <xliff:g id="TIME">%2$s</xliff:g>"</string>
     <string name="noon" msgid="7245353528818587908">"រសៀល"</string>
     <string name="Noon" msgid="3342127745230013127">"រសៀល"</string>
     <string name="midnight" msgid="7166259508850457595">"កណ្ដាលអធ្រាត្រ"</string>
-    <string name="Midnight" msgid="5630806906897892201">"កណ្ដាល​អធ្រាត្រ​"</string>
+    <string name="Midnight" msgid="5630806906897892201">"កណ្ដាល​អធ្រាត្រ"</string>
     <string name="elapsed_time_short_format_mm_ss" msgid="4431555943828711473">"<xliff:g id="MINUTES">%1$02d</xliff:g>:<xliff:g id="SECONDS">%2$02d</xliff:g>"</string>
     <string name="elapsed_time_short_format_h_mm_ss" msgid="1846071997616654124">"<xliff:g id="HOURS">%1$d</xliff:g>:<xliff:g id="MINUTES">%2$02d</xliff:g>:<xliff:g id="SECONDS">%3$02d</xliff:g>"</string>
     <string name="selectAll" msgid="6876518925844129331">"ជ្រើស​ទាំងអស់"</string>
@@ -1152,13 +1152,13 @@
     <string name="inputMethod" msgid="1653630062304567879">"វិធីសាស្ត្រ​បញ្ចូល"</string>
     <string name="editTextMenuTitle" msgid="4909135564941815494">"សកម្មភាព​អត្ថបទ"</string>
     <string name="low_internal_storage_view_title" msgid="5576272496365684834">"អស់​ទំហំ​ផ្ទុក"</string>
-    <string name="low_internal_storage_view_text" msgid="6640505817617414371">"មុខងារ​ប្រព័ន្ធ​មួយ​ចំនួន​អាច​មិន​ដំណើរការ​"</string>
+    <string name="low_internal_storage_view_text" msgid="6640505817617414371">"មុខងារ​ប្រព័ន្ធ​មួយ​ចំនួន​អាច​មិន​ដំណើរការ"</string>
     <string name="app_running_notification_title" msgid="8718335121060787914">"<xliff:g id="APP_NAME">%1$s</xliff:g> កំពុង​ដំណើរការ"</string>
     <string name="app_running_notification_text" msgid="4653586947747330058">"ប៉ះ​ ដើម្បី​មើល​ព័ត៌មាន​បន្ថែម ឬ​បញ្ឈប់​កម្មវិធី។"</string>
-    <string name="ok" msgid="5970060430562524910">"យល់​ព្រម​"</string>
-    <string name="cancel" msgid="6442560571259935130">"បោះ​បង់​"</string>
-    <string name="yes" msgid="5362982303337969312">"យល់​ព្រម​"</string>
-    <string name="no" msgid="5141531044935541497">"បោះ​បង់​"</string>
+    <string name="ok" msgid="5970060430562524910">"យល់​ព្រម"</string>
+    <string name="cancel" msgid="6442560571259935130">"បោះ​បង់"</string>
+    <string name="yes" msgid="5362982303337969312">"យល់​ព្រម"</string>
+    <string name="no" msgid="5141531044935541497">"បោះ​បង់"</string>
     <string name="dialog_alert_title" msgid="2049658708609043103">"ប្រយ័ត្ន"</string>
     <string name="loading" msgid="7933681260296021180">"កំពុង​ផ្ទុក..."</string>
     <string name="capital_on" msgid="1544682755514494298">"បើក"</string>
@@ -1167,7 +1167,7 @@
     <string name="whichHomeApplication" msgid="4616420172727326782">"ជ្រើស​កម្មវិធី​ដើម"</string>
     <string name="alwaysUse" msgid="4583018368000610438">"ប្រើ​តាម​លំនាំដើម​សម្រាប់​សកម្មភាព​នេះ។"</string>
     <string name="clearDefaultHintMsg" msgid="3252584689512077257">"សម្អាត​លំនាំដើម​ក្នុង​ការកំណត់​ប្រព័ន្ធ &gt; កម្មវិធី &gt; ទាញ​យក។"</string>
-    <string name="chooseActivity" msgid="7486876147751803333">"ជ្រើស​សកម្មភាព​​"</string>
+    <string name="chooseActivity" msgid="7486876147751803333">"ជ្រើស​សកម្មភាព"</string>
     <string name="chooseUsbActivity" msgid="6894748416073583509">"ជ្រើស​កម្មវិធី​សម្រាប់​ឧបករណ៍​យូអេសប៊ី"</string>
     <string name="noApplications" msgid="2991814273936504689">"គ្មាន​កម្មវិធី​អាច​អនុវត្ត​សកម្មភាព​នេះ។"</string>
     <string name="aerr_title" msgid="1905800560317137752"></string>
@@ -1178,7 +1178,7 @@
     <string name="anr_activity_process" msgid="5776209883299089767">"សកម្មភាព <xliff:g id="ACTIVITY">%1$s</xliff:g> មិន​ឆ្លើយតប។\n\nតើ​អ្នក​ចង់​បិទ​វា?"</string>
     <string name="anr_application_process" msgid="8941757607340481057">"<xliff:g id="APPLICATION">%1$s</xliff:g> មិន​ឆ្លើយតប។ តើ​អ្នក​ចង់​បិទ​វា?"</string>
     <string name="anr_process" msgid="6513209874880517125">"ដំណើរការ <xliff:g id="PROCESS">%1$s</xliff:g> មិន​ឆ្លើយតប។ \n\nតើ​អ្នក​ចង់​បិទ​វា​ឬ?"</string>
-    <string name="force_close" msgid="8346072094521265605">"យល់​ព្រម​"</string>
+    <string name="force_close" msgid="8346072094521265605">"យល់​ព្រម"</string>
     <string name="report" msgid="4060218260984795706">"រាយការណ៍"</string>
     <string name="wait" msgid="7147118217226317732">"រង់ចាំ"</string>
     <string name="webpage_unresponsive" msgid="3272758351138122503">"ទំព័រ​ក្លាយ​ជា​មិន​ឆ្លើយតប។\n\nតើ​អ្នក​​ចង់​បិទ​វា?"</string>
@@ -1260,19 +1260,19 @@
     <string name="sms_short_code_details" msgid="3492025719868078457"><font fgcolor="#ffffb060">"នេះ​អាច​កាត់​លុយ"</font>" លើ​គណនី​ចល័ត​របស់​អ្នក។"</string>
     <string name="sms_premium_short_code_details" msgid="5523826349105123687"><font fgcolor="#ffffb060">"វា​នឹង​គិត​ថ្លៃ​សេវាកម្ម​លើ​គណនី​ចល័ត​របស់​អ្នក។"</font></string>
     <string name="sms_short_code_confirm_allow" msgid="4458878637111023413">"ផ្ញើ"</string>
-    <string name="sms_short_code_confirm_deny" msgid="2927389840209170706">"បោះ​បង់​"</string>
+    <string name="sms_short_code_confirm_deny" msgid="2927389840209170706">"បោះ​បង់"</string>
     <string name="sms_short_code_remember_choice" msgid="5289538592272218136">"ចងចាំ​ជម្រើស​របស់​ខ្ញុំ"</string>
     <string name="sms_short_code_remember_undo_instruction" msgid="4960944133052287484">"អ្នក​អាច​ប្ដូរ​វា​ពេល​ក្រោយ​ក្នុង​ការ​កំណត់ &gt; កម្មវិធី"</string>
     <string name="sms_short_code_confirm_always_allow" msgid="3241181154869493368">"អនុញ្ញាត​ជា​និច្ច"</string>
     <string name="sms_short_code_confirm_never_allow" msgid="446992765774269673">"កុំ​អនុញ្ញាត"</string>
     <string name="sim_removed_title" msgid="6227712319223226185">"បាន​ដក​ស៊ីម​កាត​ចេញ"</string>
-    <string name="sim_removed_message" msgid="2333164559970958645">"បណ្ដាញ​ចល័ត​នឹង​ប្រើ​លែង​បាន​រហូត​ដល់​អ្នក​ចាប់ផ្ដើម​ជា​មួយ​ស៊ីម​កាត​ដែល​បា​បញ្ចូល​ត្រឹមត្រូវ។​"</string>
+    <string name="sim_removed_message" msgid="2333164559970958645">"បណ្ដាញ​ចល័ត​នឹង​ប្រើ​លែង​បាន​រហូត​ដល់​អ្នក​ចាប់ផ្ដើម​ជា​មួយ​ស៊ីម​កាត​ដែល​បា​បញ្ចូល​ត្រឹមត្រូវ។"</string>
     <string name="sim_done_button" msgid="827949989369963775">"រួចរាល់"</string>
     <string name="sim_added_title" msgid="3719670512889674693">"បាន​បន្ថែម​ស៊ីម​កាត"</string>
     <string name="sim_added_message" msgid="6599945301141050216">"ចាប់ផ្ដើម​ឧបករណ៍​របស់​អ្នក​ឡើង​វិញ ដើម្បី​ចូល​ដំណើរការ​បណ្ដាញ​ចល័ត។"</string>
     <string name="sim_restart_button" msgid="4722407842815232347">"ចាប់ផ្ដើម​ឡើងវិញ"</string>
-    <string name="time_picker_dialog_title" msgid="8349362623068819295">"កំណត់​ម៉ោង​"</string>
-    <string name="date_picker_dialog_title" msgid="5879450659453782278">"កំណត់​កាល​បរិច្ឆេទ​"</string>
+    <string name="time_picker_dialog_title" msgid="8349362623068819295">"កំណត់​ម៉ោង"</string>
+    <string name="date_picker_dialog_title" msgid="5879450659453782278">"កំណត់​កាល​បរិច្ឆេទ"</string>
     <string name="date_time_set" msgid="5777075614321087758">"កំណត់"</string>
     <string name="date_time_done" msgid="2507683751759308828">"រួចរាល់"</string>
     <string name="perms_new_perm_prefix" msgid="8257740710754301407"><font size="12" fgcolor="#ff33b5e5">"ថ្មី៖ "</font></string>
@@ -1350,7 +1350,7 @@
     <string name="permdesc_copyProtectedData" msgid="4390697124288317831">"ឲ្យ​កម្មវិធី​ដក​សេវាកម្ម​នៃ​កម្មវិធី​ផ្ទុក​​លំនាំដើម ដើម្បី​ចម្លង​មាតិកា។​ មិន​សម្រាប់​ប្រើ​ដោយ​កម្មវិធី​លំនាំដើម។"</string>
     <string name="permlab_route_media_output" msgid="1642024455750414694">"នាំ​ផ្លូវ​លទ្ធផល​មេឌៀ"</string>
     <string name="permdesc_route_media_output" msgid="4932818749547244346">"ឲ្យ​កម្មវិធី​នាំ​ផ្លូវ​លទ្ធផល​មេឌៀ​ទៅ​ឧបករណ៍​​ខាង​ក្រៅ​ផ្សេង។"</string>
-    <string name="permlab_access_keyguard_secure_storage" msgid="7565552237977815047">"ចូល​ដំណើរការ​ឧបករណ៍​ផ្ទុក​សុវត្ថិភាព​"</string>
+    <string name="permlab_access_keyguard_secure_storage" msgid="7565552237977815047">"ចូល​ដំណើរការ​ឧបករណ៍​ផ្ទុក​សុវត្ថិភាព"</string>
     <string name="permdesc_access_keyguard_secure_storage" msgid="5866245484303285762">"ឲ្យ​កម្មវិធី​ចូល​​ការ​ផ្ទុក​មាន​សុវត្ថិភាព keguard ។"</string>
     <string name="permlab_control_keyguard" msgid="172195184207828387">"ពិនិត្យ​ការ​បង្ហាញ និង​លាក់​ការ​ការពារ"</string>
     <string name="permdesc_control_keyguard" msgid="3043732290518629061">"ឲ្យ​កម្មវិធី​គ្រប់គ្រង keguard ។"</string>
@@ -1365,7 +1365,7 @@
     <string name="ime_action_go" msgid="8320845651737369027">"ទៅ"</string>
     <string name="ime_action_search" msgid="658110271822807811">"ស្វែងរក"</string>
     <string name="ime_action_send" msgid="2316166556349314424">"ផ្ញើ"</string>
-    <string name="ime_action_next" msgid="3138843904009813834">"បន្ទាប់​"</string>
+    <string name="ime_action_next" msgid="3138843904009813834">"បន្ទាប់"</string>
     <string name="ime_action_done" msgid="8971516117910934605">"រួចរាល់"</string>
     <string name="ime_action_previous" msgid="1443550039250105948">"មុន"</string>
     <string name="ime_action_default" msgid="2840921885558045721">"អនុវត្ត"</string>
@@ -1374,7 +1374,7 @@
     <string name="grant_credentials_permission_message_header" msgid="2106103817937859662">"កម្មវិធី​មួយ ឬ​ច្រើន​ដូច​ខាង​ក្រោម​ស្នើ​សិទ្ធិ ដើម្បី​ចូល​គណនី​របស់​អ្នក​ឥឡូវ និង​ពេល​អនាគត។"</string>
     <string name="grant_credentials_permission_message_footer" msgid="3125211343379376561">"តើ​អ្នក​ចង់​អនុញ្ញាត​សំណើ​នេះ?"</string>
     <string name="grant_permissions_header_text" msgid="6874497408201826708">"ស្នើ​ចូល"</string>
-    <string name="allow" msgid="7225948811296386551">"អនុញ្ញាត​"</string>
+    <string name="allow" msgid="7225948811296386551">"អនុញ្ញាត"</string>
     <string name="deny" msgid="2081879885755434506">"បដិសេធ"</string>
     <string name="permission_request_notification_title" msgid="6486759795926237907">"បាន​ស្នើ​សិទ្ធិ"</string>
     <string name="permission_request_notification_with_subtitle" msgid="8530393139639560189">"បាន​ស្នើ​សិទ្ធិ\nសម្រាប់​គណនី <xliff:g id="ACCOUNT">%s</xliff:g> ។"</string>
@@ -1397,12 +1397,12 @@
     <string name="no_file_chosen" msgid="6363648562170759465">"គ្មាន​ឯកសារ​បាន​ជ្រើស"</string>
     <string name="reset" msgid="2448168080964209908">"កំណត់​ឡើងវិញ"</string>
     <string name="submit" msgid="1602335572089911941">"ដាក់​ស្នើ"</string>
-    <string name="car_mode_disable_notification_title" msgid="3164768212003864316">"បាន​បើក​របៀប​រថយន្ត​"</string>
+    <string name="car_mode_disable_notification_title" msgid="3164768212003864316">"បាន​បើក​របៀប​រថយន្ត"</string>
     <string name="car_mode_disable_notification_message" msgid="8035230537563503262">"ប៉ះ​ ដើម្បី​ចេញ​ពី​របៀប​រថយន្ត​។"</string>
     <string name="tethered_notification_title" msgid="3146694234398202601">"ភ្ជាប់ ឬ​ហតស្ពត​សកម្ម"</string>
     <string name="tethered_notification_message" msgid="6857031760103062982">"ប៉ះ​ ដើម្បី​រៀបចំ។"</string>
     <string name="back_button_label" msgid="2300470004503343439">"ថយក្រោយ"</string>
-    <string name="next_button_label" msgid="1080555104677992408">"បន្ទាប់​"</string>
+    <string name="next_button_label" msgid="1080555104677992408">"បន្ទាប់"</string>
     <string name="skip_button_label" msgid="1275362299471631819">"រំលង"</string>
     <string name="throttle_warning_notification_title" msgid="4890894267454867276">"ការ​ប្រើ​ទិន្នន័យ​ចល័ត​ខ្ពស់"</string>
     <string name="throttle_warning_notification_message" msgid="3340822228599337743">"ប៉ះ​ ដើម្បី​​ស្វែងយល់​បន្ថែម​អំពី​ការ​ប្រើ​​​ទិន្នន័យ​ចល័ត​។"</string>
@@ -1428,7 +1428,7 @@
     <string name="media_shared" product="nosdcard" msgid="5830814349250834225">"ឧបករណ៍​ផ្ទុក​យូអេសប៊ី​បច្ចុប្បន្ន​កំពុង​ប្រើ​ដោយ​កុំព្យូទ័រ។"</string>
     <string name="media_shared" product="default" msgid="5706130568133540435">"បច្ចុប្បន្ន​កាត​អេសឌី​កំពុង​ប្រើ​ដោយ​កុំព្យូទ័រ"</string>
     <string name="media_unknown_state" msgid="729192782197290385">"មិន​ស្គាល់​ស្ថានភាព​មេឌៀ​ខាង​ក្រៅ។"</string>
-    <string name="share" msgid="1778686618230011964">"ចែក​រំលែក​"</string>
+    <string name="share" msgid="1778686618230011964">"ចែក​រំលែក"</string>
     <string name="find" msgid="4808270900322985960">"រក"</string>
     <string name="websearch" msgid="4337157977400211589">"ស្វែងរក​តាម​បណ្ដាញ"</string>
     <string name="find_next" msgid="5742124618942193978">"រក​បន្ទាប់"</string>
@@ -1444,7 +1444,7 @@
     <string name="sync_undo_deletes" msgid="2941317360600338602">"មិន​ធ្វើ​ការ​លុប​វិញ"</string>
     <string name="sync_do_nothing" msgid="3743764740430821845">"មិន​ធ្វើអ្វី​ទេ​ឥឡូវ"</string>
     <string name="choose_account_label" msgid="5655203089746423927">"ជ្រើស​គណនី"</string>
-    <string name="add_account_label" msgid="2935267344849993553">"បន្ថែម​គណនី​ថ្មី​​"</string>
+    <string name="add_account_label" msgid="2935267344849993553">"បន្ថែម​គណនី​ថ្មី"</string>
     <string name="add_account_button_label" msgid="3611982894853435874">"បន្ថែម​គណនី"</string>
     <string name="number_picker_increment_button" msgid="2412072272832284313">"បង្កើន"</string>
     <string name="number_picker_decrement_button" msgid="476050778386779067">"បន្ថយ"</string>
@@ -1463,15 +1463,15 @@
     <string name="date_picker_increment_year_button" msgid="6318697384310808899">"បង្កើន​​ឆ្នាំ"</string>
     <string name="date_picker_decrement_year_button" msgid="4482021813491121717">"បន្ថយ​ឆ្នាំ"</string>
     <string name="keyboardview_keycode_alt" msgid="4856868820040051939">"Alt"</string>
-    <string name="keyboardview_keycode_cancel" msgid="1203984017245783244">"បោះ​បង់​"</string>
+    <string name="keyboardview_keycode_cancel" msgid="1203984017245783244">"បោះ​បង់"</string>
     <string name="keyboardview_keycode_delete" msgid="3337914833206635744">"លុប"</string>
     <string name="keyboardview_keycode_done" msgid="1992571118466679775">"រួចរាល់"</string>
     <string name="keyboardview_keycode_mode_change" msgid="4547387741906537519">"ប្ដូរ​របៀប"</string>
     <string name="keyboardview_keycode_shift" msgid="2270748814315147690">"Shift"</string>
     <string name="keyboardview_keycode_enter" msgid="2985864015076059467">"Enter"</string>
-    <string name="activitychooserview_choose_application" msgid="2125168057199941199">"ជ្រើស​កម្មវិធី​​"</string>
+    <string name="activitychooserview_choose_application" msgid="2125168057199941199">"ជ្រើស​កម្មវិធី"</string>
     <string name="activitychooserview_choose_application_error" msgid="8624618365481126668">"មិន​អាច​ចាប់ផ្ដើម <xliff:g id="APPLICATION_NAME">%s</xliff:g>"</string>
-    <string name="shareactionprovider_share_with" msgid="806688056141131819">"ចែករំលែក​ជា​មួយ​"</string>
+    <string name="shareactionprovider_share_with" msgid="806688056141131819">"ចែករំលែក​ជា​មួយ"</string>
     <string name="shareactionprovider_share_with_application" msgid="5627411384638389738">"ចែក​រំលែក​ជា​មួយ <xliff:g id="APPLICATION_NAME">%s</xliff:g>"</string>
     <string name="content_description_sliding_handle" msgid="415975056159262248">"គ្រប់គ្រង​ការ​រុញ។ ប៉ះ &amp; សង្កត់។"</string>
     <string name="description_target_unlock_tablet" msgid="3833195335629795055">"អូស​ ដើម្បី​ដោះ​សោ។"</string>
@@ -1485,7 +1485,7 @@
     <string name="storage_internal" msgid="4891916833657929263">"ឧបករណ៍​ផ្ទុក​ខាង​ក្នុង"</string>
     <string name="storage_sd_card" msgid="3282948861378286745">"កាត​អេសឌី"</string>
     <string name="storage_usb" msgid="3017954059538517278">"ឧបករណ៍​ផ្ទុក​យូអេសប៊ី"</string>
-    <string name="extract_edit_menu_button" msgid="8940478730496610137">"កែសម្រួល​"</string>
+    <string name="extract_edit_menu_button" msgid="8940478730496610137">"កែសម្រួល"</string>
     <string name="data_usage_warning_title" msgid="1955638862122232342">"ការព្រមាន​ប្រើ​ទិន្នន័យ"</string>
     <string name="data_usage_warning_body" msgid="2814673551471969954">"ប៉ះ ដើម្បី​មើល​ការ​ប្រើ និង​ការ​កំណត់។"</string>
     <string name="data_usage_3g_limit_title" msgid="7093334419518706686">"បាន​បិទ​ទិន្នន័យ 2G​-3G"</string>
@@ -1542,7 +1542,7 @@
     <string name="media_route_status_available" msgid="6983258067194649391">"ទំនេរ"</string>
     <string name="media_route_status_not_available" msgid="6739899962681886401">"មិន​ទំនេរ"</string>
     <string name="media_route_status_in_use" msgid="4533786031090198063">"កំពុង​ប្រើ"</string>
-    <string name="display_manager_built_in_display_name" msgid="2583134294292563941">"អេក្រង់​ជាប់​"</string>
+    <string name="display_manager_built_in_display_name" msgid="2583134294292563941">"អេក្រង់​ជាប់"</string>
     <string name="display_manager_hdmi_display_name" msgid="1555264559227470109">"អេក្រង់ HDMI"</string>
     <string name="display_manager_overlay_display_name" msgid="5142365982271620716">"#<xliff:g id="ID">%1$d</xliff:g> ត្រួត​គ្នា"</string>
     <string name="display_manager_overlay_display_title" msgid="652124517672257172">"<xliff:g id="NAME">%1$s</xliff:g>: <xliff:g id="WIDTH">%2$d</xliff:g>x<xliff:g id="HEIGHT">%3$d</xliff:g>, <xliff:g id="DPI">%4$d</xliff:g> dpi"</string>
@@ -1574,7 +1574,7 @@
     <string name="kg_login_too_many_attempts" msgid="6486842094005698475">"ព្យាយាម​លំនាំ​ច្រើន​ពេក"</string>
     <string name="kg_login_instructions" msgid="1100551261265506448">"ដើម្បី​ដោះ​សោ ចូល​ក្នុង​គណនី Google ។"</string>
     <string name="kg_login_username_hint" msgid="5718534272070920364">"ឈ្មោះ​អ្នក​ប្រើ (អ៊ី​ម៉ែ​ល​)"</string>
-    <string name="kg_login_password_hint" msgid="9057289103827298549">"ពាក្យសម្ងាត់​"</string>
+    <string name="kg_login_password_hint" msgid="9057289103827298549">"ពាក្យសម្ងាត់"</string>
     <string name="kg_login_submit_button" msgid="5355904582674054702">"ចូល"</string>
     <string name="kg_login_invalid_input" msgid="5754664119319872197">"ឈ្មោះ​អ្នកប្រើ ឬ​ពាក្យ​សម្ងាត់​មិន​ត្រឹមត្រូវ។"</string>
     <string name="kg_login_account_recovery_hint" msgid="5690709132841752974">"ភ្លេច​ឈ្មោះ​អ្នកប្រើ ឬ​ពាក្យ​សម្ងាត់​របស់​អ្នក?\nមើល "<b>"google.com/accounts/recovery"</b>" ។"</string>
@@ -1683,7 +1683,7 @@
     <string name="mediasize_japanese_you4" msgid="2091777168747058008">"You4"</string>
     <string name="mediasize_unknown_portrait" msgid="3088043641616409762">"​មិន​ស្គាល់​បញ្ឈរ"</string>
     <string name="mediasize_unknown_landscape" msgid="4876995327029361552">"មិន​ស្គាល់​ទេសភាព"</string>
-    <string name="write_fail_reason_cancelled" msgid="7091258378121627624">"បាន​បោះ​បង់​"</string>
+    <string name="write_fail_reason_cancelled" msgid="7091258378121627624">"បាន​បោះ​បង់"</string>
     <string name="write_fail_reason_cannot_write" msgid="8132505417935337724">"កំហុស​ក្នុង​ការ​សរសេរ​មាតិកា"</string>
     <string name="reason_unknown" msgid="6048913880184628119">"មិន​ស្គាល់"</string>
     <string name="reason_service_unavailable" msgid="7824008732243903268">"មិន​បា​ន​បើក​សេវាកម្ម​បោះពុម្ព"</string>
diff --git a/core/res/res/values-lo-rLA/strings.xml b/core/res/res/values-lo-rLA/strings.xml
index 3b275a5..71f55fb 100644
--- a/core/res/res/values-lo-rLA/strings.xml
+++ b/core/res/res/values-lo-rLA/strings.xml
@@ -1695,7 +1695,7 @@
     <string name="restr_pin_enter_old_pin" msgid="1462206225512910757">"PIN ປະ​ຈຸ​ບັນ"</string>
     <string name="restr_pin_enter_new_pin" msgid="5959606691619959184">"ລະຫັດ PIN ໃໝ່"</string>
     <string name="restr_pin_confirm_pin" msgid="8501523829633146239">"ຢືນຢັນລະຫັດ PIN ໃໝ່"</string>
-    <string name="restr_pin_create_pin" msgid="8017600000263450337">"ສ້າງ PIN ສໍາ​ລັບ​ການ​ປັບ​ປຸງ​ຂໍ້ຈໍາ​ກັດ​"</string>
+    <string name="restr_pin_create_pin" msgid="8017600000263450337">"ສ້າງ PIN ສໍາ​ລັບ​ການ​ປັບ​ປຸງ​ຂໍ້ຈໍາ​ກັດ"</string>
     <string name="restr_pin_error_doesnt_match" msgid="2224214190906994548">"PIN ບໍ່​ກົງກັນ. ລອງໃໝ່ອີກຄັ້ງ​."</string>
     <string name="restr_pin_error_too_short" msgid="8173982756265777792">"PIN ​ສັ້ນ​ເກີນ​ໄປ​. ຕ້ອງມີຢ່າງໜ້ອຍ 4 ຫຼັກ​."</string>
   <plurals name="restr_pin_countdown">
diff --git a/core/res/res/values-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 d28f9de..f1f1802 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>
 
@@ -6141,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>
 
     <!-- =============================== -->
@@ -6420,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">
@@ -6675,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">
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 e9d8ccc..f6732d3 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -1538,6 +1538,7 @@
          -->
     <string-array translatable="false" name="config_globalActionsList">
         <item>power</item>
+        <item>bugreport</item>
         <item>users</item>
     </string-array>
 
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 9c33d80..7fc2f49 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -2182,6 +2182,7 @@
   <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" />
 
@@ -2256,6 +2257,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" />
 
@@ -2272,6 +2274,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..108334fa 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 69f73e5..8b1ca31 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -1286,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" />
@@ -1384,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" />
@@ -1675,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" />
@@ -1862,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 c0c20e2..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 */,
@@ -214,9 +215,8 @@
                 disabledSubtypeUnawareIme, null);
     }
 
-    // This test is disabled until DynamicRotationList is enabled.
     @SmallTest
-    public void DISABLED_testControllerImplWithUserAction() throws Exception {
+    public void testControllerImplWithUserAction() throws Exception {
         final List<ImeSubtypeListItem> enabledItems = createEnabledImeSubtypes();
         final ImeSubtypeListItem latinIme_en_US = enabledItems.get(0);
         final ImeSubtypeListItem latinIme_fr = enabledItems.get(1);
@@ -226,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 */,
@@ -272,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 7fa04481..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..50728c5 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,12 @@
   <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>
+  </h2>
 
-  <hr>
+
 </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..c301f8c 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>
@@ -244,12 +244,12 @@
 data-maxresults="6">
 </div>
 
-<div class="headerLine clearfloat">
-  <h1 id="fn">
+<div class="headerLine">
+  <h2 id="fn">
   Functionality
-  </h1>
+  </h2>
 
-  <hr>
+
 </div>
 
 <p>
@@ -518,12 +518,12 @@
 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>
@@ -679,12 +679,12 @@
 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>
@@ -828,12 +828,12 @@
 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 +863,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..966c462 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
@@ -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"
@@ -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>
 
@@ -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
@@ -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>
@@ -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
@@ -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
@@ -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>
@@ -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
@@ -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>
@@ -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..1c003cf 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>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..3f6b1a6 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>
@@ -95,12 +95,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>
@@ -119,12 +119,12 @@
 "-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>
@@ -163,12 +163,12 @@
 "-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>
@@ -221,12 +221,12 @@
 "-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>
@@ -289,12 +289,12 @@
 "-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>
@@ -344,12 +344,12 @@
 "-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>
@@ -396,12 +396,12 @@
 "-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">
@@ -459,12 +459,12 @@
 "-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>
@@ -495,12 +495,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>
@@ -526,12 +526,12 @@
 "-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>
@@ -609,12 +609,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>
@@ -668,12 +668,12 @@
 "-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>
@@ -709,12 +709,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 +765,11 @@
 </table> -->
 
 <div class="headerLine">
-  <h1 id="complete-details">
+  <h2 id="complete-details">
     15. Complete the Apps’ Store Listing
-  </h1>
+  </h2>
 
-  <hr>
+
 </div>
 
 <p>
@@ -825,12 +825,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>
@@ -862,12 +862,12 @@
 "-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>
@@ -969,12 +969,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>
diff --git a/docs/html/distribute/tools/localization-checklist.jd b/docs/html/distribute/tools/localization-checklist.jd
index 7a638ed..569ed02 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>
@@ -93,12 +93,12 @@
 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>
@@ -233,12 +233,12 @@
 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>
@@ -408,12 +408,12 @@
 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>
@@ -579,12 +579,12 @@
 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>
@@ -699,12 +699,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>
@@ -897,12 +897,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>
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..b481802 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>
@@ -137,7 +137,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>
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/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 210ea86b..6802b9a 100644
--- a/graphics/java/android/graphics/FontFamily.java
+++ b/graphics/java/android/graphics/FontFamily.java
@@ -30,9 +30,22 @@
     public long mNativePtr;
 
     public FontFamily() {
-        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");
         }
     }
 
@@ -49,7 +62,7 @@
         return nAddFont(mNativePtr, path.getAbsolutePath());
     }
 
-    static native long nCreateFamily();
+    static native long nCreateFamily(String lang, int variant);
     static native void nUnrefFamily(long nativePtr);
     static native boolean nAddFont(long nativeFamily, 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 b7613fb..2b07c3f 100644
--- a/graphics/java/android/graphics/Typeface.java
+++ b/graphics/java/android/graphics/Typeface.java
@@ -245,7 +245,7 @@
 
     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));
         }
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/LayerDrawable.java b/graphics/java/android/graphics/drawable/LayerDrawable.java
index 2e47d3a..75cb0a00 100644
--- a/graphics/java/android/graphics/drawable/LayerDrawable.java
+++ b/graphics/java/android/graphics/drawable/LayerDrawable.java
@@ -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/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/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..9212b9de 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..0e47c6e2 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/AudioManager.java b/media/java/android/media/AudioManager.java
index f4affa0..84d4ab6 100644
--- a/media/java/android/media/AudioManager.java
+++ b/media/java/android/media/AudioManager.java
@@ -41,6 +41,7 @@
 import java.util.HashMap;
 import java.util.ArrayList;
 
+
 /**
  * AudioManager provides access to volume and ringer mode control.
  * <p>
@@ -61,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
@@ -337,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.)
      *
@@ -438,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()
@@ -635,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);
@@ -665,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);
@@ -2818,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 {
@@ -3003,7 +3026,7 @@
      * @hide
      */
     public int listAudioPorts(ArrayList<AudioPort> ports) {
-        return ERROR_INVALID_OPERATION;
+        return updateAudioPortCache(ports, null);
     }
 
     /**
@@ -3012,7 +3035,17 @@
      * @hide
      */
     public int listAudioDevicePorts(ArrayList<AudioPort> devices) {
-        return ERROR_INVALID_OPERATION;
+        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;
     }
 
     /**
@@ -3041,7 +3074,7 @@
     public int createAudioPatch(AudioPatch[] patch,
                                  AudioPortConfig[] sources,
                                  AudioPortConfig[] sinks) {
-        return ERROR_INVALID_OPERATION;
+        return AudioSystem.createAudioPatch(patch, sources, sinks);
     }
 
     /**
@@ -3056,7 +3089,7 @@
      * @hide
      */
     public int releaseAudioPatch(AudioPatch patch) {
-        return  ERROR_INVALID_OPERATION;
+        return AudioSystem.releaseAudioPatch(patch);
     }
 
     /**
@@ -3065,7 +3098,7 @@
      * @hide
      */
     public int listAudioPatches(ArrayList<AudioPatch> patches) {
-        return ERROR_INVALID_OPERATION;
+        return updateAudioPortCache(null, patches);
     }
 
     /**
@@ -3074,7 +3107,14 @@
      * @hide
      */
     public int setAudioPortGain(AudioPort port, AudioGainConfig gain) {
-        return ERROR_INVALID_OPERATION;
+        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);
     }
 
     /**
@@ -3102,16 +3142,128 @@
     }
 
     /**
-     * Register an audio port update listener.
+     * Register an audio port list update listener.
      * @hide
      */
     public void registerAudioPortUpdateListener(OnAudioPortUpdateListener l) {
+        mAudioPortEventHandler.registerListener(l);
     }
 
     /**
-     * Unregister an audio port update listener.
+     * 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/AudioPort.java b/media/java/android/media/AudioPort.java
index 9aeddef..fbd5022 100644
--- a/media/java/android/media/AudioPort.java
+++ b/media/java/android/media/AudioPort.java
@@ -133,6 +133,9 @@
      * Get the gain descriptor at a given index
      */
     AudioGain gain(int index) {
+        if (index < mGains.length) {
+            return null;
+        }
         return mGains[index];
     }
 
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 cf18902..74f39b7 100644
--- a/media/java/android/media/AudioService.java
+++ b/media/java/android/media/AudioService.java
@@ -115,6 +115,9 @@
     /** 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;
 
@@ -126,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;
@@ -169,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;
@@ -941,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);
@@ -2371,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,
@@ -2384,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
@@ -2452,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;
@@ -2538,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) {
@@ -2578,17 +2617,21 @@
             } 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);
@@ -2599,7 +2642,7 @@
 
         mPrevVolDirection = direction;
 
-        return adjustVolumeIndex;
+        return result;
     }
 
     @Override
@@ -2863,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,
@@ -3726,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;
 
@@ -3854,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);
     }
@@ -3863,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;
         }
@@ -3924,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) {
@@ -3989,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;
         }
@@ -4056,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) ||
@@ -4074,7 +4177,7 @@
                             MUSIC_ACTIVE_POLL_PERIOD_MS);
                 }
             }
-            if (!isUsb) {
+            if (!isUsb && (device != AudioSystem.DEVICE_IN_WIRED_HEADSET)) {
                 sendDeviceConnectionIntent(device, state, name);
             }
         }
@@ -4090,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)) {
@@ -4125,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);
@@ -4139,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;
                     }
                 }
@@ -4152,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;
@@ -4172,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);
 
@@ -4188,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/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..0316d1fa 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..47f672f3 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/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-km-rKH/strings.xml b/packages/DocumentsUI/res/values-km-rKH/strings.xml
index 8c9030d..e8944ec 100644
--- a/packages/DocumentsUI/res/values-km-rKH/strings.xml
+++ b/packages/DocumentsUI/res/values-km-rKH/strings.xml
@@ -20,14 +20,14 @@
     <string name="title_open" msgid="4353228937663917801">"បើក​ពី"</string>
     <string name="title_save" msgid="2433679664882857999">"រក្សា​ទុក​ទៅ"</string>
     <string name="menu_create_dir" msgid="5947289605844398389">"បង្កើត​ថត"</string>
-    <string name="menu_grid" msgid="6878021334497835259">"ទិដ្ឋភាព​ក្រឡា​"</string>
+    <string name="menu_grid" msgid="6878021334497835259">"ទិដ្ឋភាព​ក្រឡា"</string>
     <string name="menu_list" msgid="7279285939892417279">"ទិដ្ឋភាព​បញ្ជី"</string>
     <string name="menu_sort" msgid="7677740407158414452">"តម្រៀប​តាម"</string>
     <string name="menu_search" msgid="3816712084502856974">"ស្វែងរក"</string>
     <string name="menu_settings" msgid="6008033148948428823">"ការ​កំណត់"</string>
     <string name="menu_open" msgid="432922957274920903">"បើក"</string>
     <string name="menu_save" msgid="2394743337684426338">"រក្សាទុក"</string>
-    <string name="menu_share" msgid="3075149983979628146">"ចែករំលែក​"</string>
+    <string name="menu_share" msgid="3075149983979628146">"ចែករំលែក"</string>
     <string name="menu_delete" msgid="8138799623850614177">"លុប"</string>
     <string name="menu_select" msgid="8711270657353563424">"ជ្រើស \"<xliff:g id="DIRECTORY">^1</xliff:g>\""</string>
     <string name="mode_selected_count" msgid="459111894725594625">"បាន​ជ្រើស <xliff:g id="COUNT">%1$d</xliff:g>"</string>
@@ -48,7 +48,7 @@
     <string name="pref_advanced_devices" msgid="903257239609301276">"បង្ហាញ​ឧបករណ៍​កម្រិត​ខ្ពស់"</string>
     <string name="pref_file_size" msgid="2826879315743961459">"បង្ហាញ​ទំហំ​ឯកសារ"</string>
     <string name="pref_device_size" msgid="3542106883278997222">"បង្ហាញ​ទំហំ​ឧបករណ៍"</string>
-    <string name="empty" msgid="7858882803708117596">"គ្មានធាតុ​"</string>
+    <string name="empty" msgid="7858882803708117596">"គ្មានធាតុ"</string>
     <string name="toast_no_application" msgid="1339885974067891667">"មិន​អាច​បើក​ឯកសារ"</string>
     <string name="toast_failed_delete" msgid="2180678019407244069">"មិន​អាច​លុប​ឯកសារ​មួយ​ចំនួន"</string>
     <string name="share_via" msgid="8966594246261344259">"ចែករំលែក​តាម"</string>
diff --git a/packages/Keyguard/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_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-km-rKH/strings.xml b/packages/Keyguard/res/values-km-rKH/strings.xml
index 18b59f1..a2e54a7 100644
--- a/packages/Keyguard/res/values-km-rKH/strings.xml
+++ b/packages/Keyguard/res/values-km-rKH/strings.xml
@@ -83,7 +83,7 @@
     <string name="password_keyboard_label_alpha_key" msgid="8001096175167485649">"ABC"</string>
     <string name="password_keyboard_label_alt_key" msgid="1284820942620288678">"ALT"</string>
     <string name="keyboardview_keycode_alt" msgid="4856868820040051939">"Alt"</string>
-    <string name="keyboardview_keycode_cancel" msgid="1203984017245783244">"បោះ​បង់​"</string>
+    <string name="keyboardview_keycode_cancel" msgid="1203984017245783244">"បោះ​បង់"</string>
     <string name="keyboardview_keycode_delete" msgid="3337914833206635744">"លុប"</string>
     <string name="keyboardview_keycode_done" msgid="1992571118466679775">"រួចរាល់"</string>
     <string name="keyboardview_keycode_mode_change" msgid="4547387741906537519">"ប្ដូរ​របៀប"</string>
@@ -120,7 +120,7 @@
     <string name="kg_login_too_many_attempts" msgid="6486842094005698475">"ព្យាយាម​លំនាំ​ច្រើន​ពេក"</string>
     <string name="kg_login_instructions" msgid="1100551261265506448">"ដើម្បី​ដោះ​សោ ចូល​ក្នុង​គណនី Google ។"</string>
     <string name="kg_login_username_hint" msgid="5718534272070920364">"ឈ្មោះ​អ្នក​ប្រើ (អ៊ី​ម៉ែ​ល​)"</string>
-    <string name="kg_login_password_hint" msgid="9057289103827298549">"ពាក្យសម្ងាត់​"</string>
+    <string name="kg_login_password_hint" msgid="9057289103827298549">"ពាក្យសម្ងាត់"</string>
     <string name="kg_login_submit_button" msgid="5355904582674054702">"ចូល"</string>
     <string name="kg_login_invalid_input" msgid="5754664119319872197">"ឈ្មោះ​អ្នកប្រើ ឬ​ពាក្យ​សម្ងាត់​មិន​ត្រឹមត្រូវ។"</string>
     <string name="kg_login_account_recovery_hint" msgid="5690709132841752974">"ភ្លេច​ឈ្មោះ​អ្នកប្រើ ឬ​ពាក្យ​សម្ងាត់​របស់​អ្នក?\nមើល "<b>"google.com/accounts/recovery"</b>" ។"</string>
diff --git a/packages/Keyguard/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/KeyguardPINView.java b/packages/Keyguard/src/com/android/keyguard/KeyguardPINView.java
index 4dfda91..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() {
@@ -117,8 +133,45 @@
 
     @Override
     public void startAppearAnimation() {
-        // TODO: Fancy animation.
-        setAlpha(0);
-        animate().alpha(1).withLayer().setDuration(200);
+        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/KeyguardPatternView.java b/packages/Keyguard/src/com/android/keyguard/KeyguardPatternView.java
index 5853ff9..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) {
@@ -403,8 +418,69 @@
 
     @Override
     public void startAppearAnimation() {
-        // TODO: Fancy animation.
-        setAlpha(0);
-        animate().alpha(1).withLayer().setDuration(200);
+        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/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/packages/PrintSpooler/res/values-km-rKH/strings.xml b/packages/PrintSpooler/res/values-km-rKH/strings.xml
index ba3c042..c89f9bf 100644
--- a/packages/PrintSpooler/res/values-km-rKH/strings.xml
+++ b/packages/PrintSpooler/res/values-km-rKH/strings.xml
@@ -60,7 +60,7 @@
   </plurals>
     <string name="cancel" msgid="4373674107267141885">"បោះបង់"</string>
     <string name="restart" msgid="2472034227037808749">"ចាប់ផ្ដើម​ឡើងវិញ"</string>
-    <string name="no_connection_to_printer" msgid="2159246915977282728">"គ្មាន​​​ការ​ភ្ជាប់​ទៅ​ម៉ាស៊ីន​បោះពុម្ព​"</string>
+    <string name="no_connection_to_printer" msgid="2159246915977282728">"គ្មាន​​​ការ​ភ្ជាប់​ទៅ​ម៉ាស៊ីន​បោះពុម្ព"</string>
     <string name="reason_unknown" msgid="5507940196503246139">"មិន​ស្គាល់"</string>
     <string name="printer_unavailable" msgid="2434170617003315690">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> – មិន​អាច​ប្រើ​បាន"</string>
     <string name="print_error_default_message" msgid="8568506918983980567">"មិន​អាច​បង្កើត​ការ​ងារ​បោះពុម្ព"</string>
diff --git a/packages/SettingsProvider/res/values-fr/defaults.xml b/packages/SettingsProvider/res/values-fr/defaults.xml
new file mode 100644
index 0000000..295b4f5
--- /dev/null
+++ b/packages/SettingsProvider/res/values-fr/defaults.xml
@@ -0,0 +1,23 @@
+<?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.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+ -->
+
+<resources 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-zh-rCN/defaults.xml b/packages/SettingsProvider/res/values-zh-rCN/defaults.xml
new file mode 100644
index 0000000..295b4f5
--- /dev/null
+++ b/packages/SettingsProvider/res/values-zh-rCN/defaults.xml
@@ -0,0 +1,23 @@
+<?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.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+ -->
+
+<resources 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/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_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_vol_zen_off.xml b/packages/SystemUI/res/drawable/ic_vol_zen_off.xml
index ea5ab70..477c36b 100644
--- a/packages/SystemUI/res/drawable/ic_vol_zen_off.xml
+++ b/packages/SystemUI/res/drawable/ic_vol_zen_off.xml
@@ -19,12 +19,10 @@
         android:height="32dp"/>
 
     <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="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: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_vol_zen_on.xml b/packages/SystemUI/res/drawable/ic_vol_zen_on.xml
index 44024f3..0a43a7b 100644
--- a/packages/SystemUI/res/drawable/ic_vol_zen_on.xml
+++ b/packages/SystemUI/res/drawable/ic_vol_zen_on.xml
@@ -19,10 +19,10 @@
         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="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/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/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/qs_detail.xml b/packages/SystemUI/res/layout/qs_detail.xml
index e73b431..e1c460c 100644
--- a/packages/SystemUI/res/layout/qs_detail.xml
+++ b/packages/SystemUI/res/layout/qs_detail.xml
@@ -26,7 +26,7 @@
         android:layout_alignParentStart="true"
         android:contentDescription="@string/accessibility_quick_settings_close"
         android:padding="@dimen/qs_panel_padding"
-        android:src="@drawable/ic_qs_close" />
+        android:src="@drawable/ic_qs_back" />
 
     <TextView
         android:id="@android:id/title"
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/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
index bc7288d..046862f 100644
--- a/packages/SystemUI/res/layout/volume_panel.xml
+++ b/packages/SystemUI/res/layout/volume_panel.xml
@@ -24,6 +24,7 @@
         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
diff --git a/packages/SystemUI/res/layout/volume_panel_item.xml b/packages/SystemUI/res/layout/volume_panel_item.xml
index 98cb8f4..4a2a0c0 100644
--- a/packages/SystemUI/res/layout/volume_panel_item.xml
+++ b/packages/SystemUI/res/layout/volume_panel_item.xml
@@ -15,28 +15,34 @@
 -->
 
 <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">
+        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="?android: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" />
-
+            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
index 8b344000..6d63bb0 100644
--- a/packages/SystemUI/res/layout/zen_mode_condition.xml
+++ b/packages/SystemUI/res/layout/zen_mode_condition.xml
@@ -16,11 +16,14 @@
 -->
 <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
     android:layout_width="match_parent"
-    android:layout_height="wrap_content" >
+    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="32dp"
+        android:layout_width="40dp"
+        android:layout_marginStart="2dp"
         android:layout_height="@dimen/zen_mode_condition_height"
         android:layout_alignParentStart="true"
         android:gravity="center" />
diff --git a/packages/SystemUI/res/layout/zen_mode_panel.xml b/packages/SystemUI/res/layout/zen_mode_panel.xml
index ae04bf5..0a8f852 100644
--- a/packages/SystemUI/res/layout/zen_mode_panel.xml
+++ b/packages/SystemUI/res/layout/zen_mode_panel.xml
@@ -21,7 +21,7 @@
     android:layout_height="wrap_content"
     android:background="@color/system_primary_color"
     android:orientation="vertical"
-    android:padding="@dimen/qs_panel_padding" >
+    android:paddingTop="@dimen/qs_panel_padding" >
 
     <TextView
         android:id="@android:id/title"
@@ -29,8 +29,10 @@
         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.DetailHeader" />
+        android:textAppearance="@style/TextAppearance.QS.DetailItemPrimary" />
 
     <LinearLayout
         android:id="@android:id/content"
@@ -44,6 +46,7 @@
         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" />
diff --git a/packages/SystemUI/res/values-af/strings.xml b/packages/SystemUI/res/values-af/strings.xml
index e43464c..c45254e 100644
--- a/packages/SystemUI/res/values-af/strings.xml
+++ b/packages/SystemUI/res/values-af/strings.xml
@@ -156,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">"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,6 +210,7 @@
     <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="quick_settings_notifications_label" msgid="4818156442169154523">"Kennisgewings"</string>
     <string name="recents_empty_message" msgid="2269156590813544104">"ONLANGS"</string>
     <string name="recents_app_info_button_label" msgid="2890317189376000030">"Programinligting"</string>
     <string name="recents_search_bar_label" msgid="8074997400187836677">"soek"</string>
@@ -226,4 +230,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..5c4d98b 100644
--- a/packages/SystemUI/res/values-am/strings.xml
+++ b/packages/SystemUI/res/values-am/strings.xml
@@ -156,6 +156,12 @@
     <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>
+    <!-- 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 />
     <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,6 +213,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>
+    <!-- no translation found for quick_settings_notifications_label (4818156442169154523) -->
+    <skip />
     <string name="recents_empty_message" msgid="2269156590813544104">"የቅርብ ጊዜዎች"</string>
     <string name="recents_app_info_button_label" msgid="2890317189376000030">"የመተግበሪያ መረጃ"</string>
     <string name="recents_search_bar_label" msgid="8074997400187836677">"ፈልግ"</string>
@@ -226,4 +234,10 @@
     <string name="speed_bump_explanation" msgid="1288875699658819755">"በጣም አስቸካይ ያልሆኑ ማሳወቂያዎች ከታች"</string>
     <string name="notification_tap_again" msgid="7590196980943943842">"ለመክፈት ዳግም መታ ያድርጉ"</string>
     <string name="keyguard_unlock" msgid="8043466894212841998">"ለማስከፈት ወደ ላይ ያንሸራትቱ"</string>
+    <!-- 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-ar/strings.xml b/packages/SystemUI/res/values-ar/strings.xml
index c9ddc23..d7c5651 100644
--- a/packages/SystemUI/res/values-ar/strings.xml
+++ b/packages/SystemUI/res/values-ar/strings.xml
@@ -156,6 +156,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,6 +210,7 @@
     <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="quick_settings_notifications_label" msgid="4818156442169154523">"الإشعارات"</string>
     <string name="recents_empty_message" msgid="2269156590813544104">"الأخيرة"</string>
     <string name="recents_app_info_button_label" msgid="2890317189376000030">"معلومات التطبيق"</string>
     <string name="recents_search_bar_label" msgid="8074997400187836677">"بحث"</string>
@@ -226,4 +230,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..2405804 100644
--- a/packages/SystemUI/res/values-bg/strings.xml
+++ b/packages/SystemUI/res/values-bg/strings.xml
@@ -156,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">"Местоположението е <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,6 +210,7 @@
     <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="quick_settings_notifications_label" msgid="4818156442169154523">"Известия"</string>
     <string name="recents_empty_message" msgid="2269156590813544104">"СКОРОШНИ"</string>
     <string name="recents_app_info_button_label" msgid="2890317189376000030">"Информация за приложението"</string>
     <string name="recents_search_bar_label" msgid="8074997400187836677">"търсене"</string>
@@ -226,4 +230,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..982e0c6 100644
--- a/packages/SystemUI/res/values-ca/strings.xml
+++ b/packages/SystemUI/res/values-ca/strings.xml
@@ -158,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ó: <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,6 +212,7 @@
     <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="quick_settings_notifications_label" msgid="4818156442169154523">"Notificacions"</string>
     <string name="recents_empty_message" msgid="2269156590813544104">"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>
@@ -228,4 +232,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..570e1bb 100644
--- a/packages/SystemUI/res/values-cs/strings.xml
+++ b/packages/SystemUI/res/values-cs/strings.xml
@@ -158,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">"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,6 +212,7 @@
     <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="quick_settings_notifications_label" msgid="4818156442169154523">"Oznámení"</string>
     <string name="recents_empty_message" msgid="2269156590813544104">"POSLEDNÍ"</string>
     <string name="recents_app_info_button_label" msgid="2890317189376000030">"Informace o aplikaci"</string>
     <string name="recents_search_bar_label" msgid="8074997400187836677">"vyhledat"</string>
@@ -228,4 +232,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..127192a 100644
--- a/packages/SystemUI/res/values-da/strings.xml
+++ b/packages/SystemUI/res/values-da/strings.xml
@@ -156,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">"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,6 +210,7 @@
     <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="quick_settings_notifications_label" msgid="4818156442169154523">"Underretninger"</string>
     <string name="recents_empty_message" msgid="2269156590813544104">"SENESTE"</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>
@@ -226,4 +230,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..c9c7b8e 100644
--- a/packages/SystemUI/res/values-de/strings.xml
+++ b/packages/SystemUI/res/values-de/strings.xml
@@ -158,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">"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,6 +212,7 @@
     <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="quick_settings_notifications_label" msgid="4818156442169154523">"Benachrichtigungen"</string>
     <string name="recents_empty_message" msgid="2269156590813544104">"Letzte"</string>
     <string name="recents_app_info_button_label" msgid="2890317189376000030">"App-Info"</string>
     <string name="recents_search_bar_label" msgid="8074997400187836677">"Suche"</string>
@@ -228,4 +232,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..1d90dae 100644
--- a/packages/SystemUI/res/values-el/strings.xml
+++ b/packages/SystemUI/res/values-el/strings.xml
@@ -158,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>
@@ -209,6 +212,7 @@
     <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="quick_settings_notifications_label" msgid="4818156442169154523">"Ειδοποιήσεις"</string>
     <string name="recents_empty_message" msgid="2269156590813544104">"ΠΡΟΣΦΑΤΑ"</string>
     <string name="recents_app_info_button_label" msgid="2890317189376000030">"Πληροφορίες εφαρμογής"</string>
     <string name="recents_search_bar_label" msgid="8074997400187836677">"αναζήτηση"</string>
@@ -228,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-en-rGB/strings.xml b/packages/SystemUI/res/values-en-rGB/strings.xml
index d26cfae..a9c6678 100644
--- a/packages/SystemUI/res/values-en-rGB/strings.xml
+++ b/packages/SystemUI/res/values-en-rGB/strings.xml
@@ -156,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">"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,6 +210,7 @@
     <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="quick_settings_notifications_label" msgid="4818156442169154523">"Notifications"</string>
     <string name="recents_empty_message" msgid="2269156590813544104">"RECENTS"</string>
     <string name="recents_app_info_button_label" msgid="2890317189376000030">"Application Info"</string>
     <string name="recents_search_bar_label" msgid="8074997400187836677">"search"</string>
@@ -226,4 +230,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 d26cfae..a9c6678 100644
--- a/packages/SystemUI/res/values-en-rIN/strings.xml
+++ b/packages/SystemUI/res/values-en-rIN/strings.xml
@@ -156,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">"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,6 +210,7 @@
     <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="quick_settings_notifications_label" msgid="4818156442169154523">"Notifications"</string>
     <string name="recents_empty_message" msgid="2269156590813544104">"RECENTS"</string>
     <string name="recents_app_info_button_label" msgid="2890317189376000030">"Application Info"</string>
     <string name="recents_search_bar_label" msgid="8074997400187836677">"search"</string>
@@ -226,4 +230,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-es-rUS/strings.xml b/packages/SystemUI/res/values-es-rUS/strings.xml
index c93fbf6..6ca4ebc 100644
--- a/packages/SystemUI/res/values-es-rUS/strings.xml
+++ b/packages/SystemUI/res/values-es-rUS/strings.xml
@@ -158,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">"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,6 +212,7 @@
     <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="quick_settings_notifications_label" msgid="4818156442169154523">"Notificaciones"</string>
     <string name="recents_empty_message" msgid="2269156590813544104">"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>
@@ -228,4 +232,13 @@
     <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..d0e9c0a 100644
--- a/packages/SystemUI/res/values-es/strings.xml
+++ b/packages/SystemUI/res/values-es/strings.xml
@@ -156,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">"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,6 +210,7 @@
     <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="quick_settings_notifications_label" msgid="4818156442169154523">"Notificaciones"</string>
     <string name="recents_empty_message" msgid="2269156590813544104">"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>
@@ -226,4 +230,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..c692b28 100644
--- a/packages/SystemUI/res/values-et-rEE/strings.xml
+++ b/packages/SystemUI/res/values-et-rEE/strings.xml
@@ -156,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">"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,6 +210,7 @@
     <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="quick_settings_notifications_label" msgid="4818156442169154523">"Märguanded"</string>
     <string name="recents_empty_message" msgid="2269156590813544104">"HILJUTISED"</string>
     <string name="recents_app_info_button_label" msgid="2890317189376000030">"Rakenduste teave"</string>
     <string name="recents_search_bar_label" msgid="8074997400187836677">"otsing"</string>
@@ -224,4 +228,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..ee2c48d 100644
--- a/packages/SystemUI/res/values-fa/strings.xml
+++ b/packages/SystemUI/res/values-fa/strings.xml
@@ -156,6 +156,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,6 +210,7 @@
     <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="quick_settings_notifications_label" msgid="4818156442169154523">"اعلان‌ها"</string>
     <string name="recents_empty_message" msgid="2269156590813544104">"موارد اخیر"</string>
     <string name="recents_app_info_button_label" msgid="2890317189376000030">"اطلاعات برنامه"</string>
     <string name="recents_search_bar_label" msgid="8074997400187836677">"جستجو"</string>
@@ -226,4 +230,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..44d35b3 100644
--- a/packages/SystemUI/res/values-fi/strings.xml
+++ b/packages/SystemUI/res/values-fi/strings.xml
@@ -156,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">"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,6 +210,7 @@
     <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="quick_settings_notifications_label" msgid="4818156442169154523">"Ilmoitukset"</string>
     <string name="recents_empty_message" msgid="2269156590813544104">"VIIMEISIMMÄT"</string>
     <string name="recents_app_info_button_label" msgid="2890317189376000030">"Sovellustiedot"</string>
     <string name="recents_search_bar_label" msgid="8074997400187836677">"haku"</string>
@@ -226,4 +230,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..d9790f6 100644
--- a/packages/SystemUI/res/values-fr-rCA/strings.xml
+++ b/packages/SystemUI/res/values-fr-rCA/strings.xml
@@ -158,6 +158,12 @@
     <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>
+    <!-- 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 />
     <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,6 +215,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 sans fil"</string>
+    <!-- no translation found for quick_settings_notifications_label (4818156442169154523) -->
+    <skip />
     <string name="recents_empty_message" msgid="2269156590813544104">"RÉCENTS"</string>
     <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>
@@ -228,4 +236,10 @@
     <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>
+    <!-- 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-fr/strings.xml b/packages/SystemUI/res/values-fr/strings.xml
index a0c5cfe..d95fe06 100644
--- a/packages/SystemUI/res/values-fr/strings.xml
+++ b/packages/SystemUI/res/values-fr/strings.xml
@@ -158,6 +158,12 @@
     <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>
+    <!-- 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 />
     <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,6 +215,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>
+    <!-- no translation found for quick_settings_notifications_label (4818156442169154523) -->
+    <skip />
     <string name="recents_empty_message" msgid="2269156590813544104">"RÉCENTS"</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>
@@ -228,4 +236,10 @@
     <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>
+    <!-- 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-hi/strings.xml b/packages/SystemUI/res/values-hi/strings.xml
index 6850b3d..60d7de1 100644
--- a/packages/SystemUI/res/values-hi/strings.xml
+++ b/packages/SystemUI/res/values-hi/strings.xml
@@ -156,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">"स्थान <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,6 +210,7 @@
     <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="quick_settings_notifications_label" msgid="4818156442169154523">"सूचनाएं"</string>
     <string name="recents_empty_message" msgid="2269156590813544104">"हाल ही का"</string>
     <string name="recents_app_info_button_label" msgid="2890317189376000030">"एप्‍लिकेशन जानकारी"</string>
     <string name="recents_search_bar_label" msgid="8074997400187836677">"खोज"</string>
@@ -226,4 +230,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..09dde42 100644
--- a/packages/SystemUI/res/values-hr/strings.xml
+++ b/packages/SystemUI/res/values-hr/strings.xml
@@ -156,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">"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,6 +210,7 @@
     <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="quick_settings_notifications_label" msgid="4818156442169154523">"Obavijesti"</string>
     <string name="recents_empty_message" msgid="2269156590813544104">"NEDAVNO"</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>
@@ -226,4 +230,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..dc85416 100644
--- a/packages/SystemUI/res/values-hu/strings.xml
+++ b/packages/SystemUI/res/values-hu/strings.xml
@@ -156,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">"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,6 +210,7 @@
     <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="quick_settings_notifications_label" msgid="4818156442169154523">"Értesítések"</string>
     <string name="recents_empty_message" msgid="2269156590813544104">"LEGUTÓBBIAK"</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>
@@ -226,4 +230,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..b11a004 100644
--- a/packages/SystemUI/res/values-hy-rAM/strings.xml
+++ b/packages/SystemUI/res/values-hy-rAM/strings.xml
@@ -156,6 +156,12 @@
     <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>
+    <!-- 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 />
     <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,6 +213,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>
+    <!-- no translation found for quick_settings_notifications_label (4818156442169154523) -->
+    <skip />
     <string name="recents_empty_message" msgid="2269156590813544104">"ՎԵՐՋԻՆՆԵՐԸ"</string>
     <string name="recents_app_info_button_label" msgid="2890317189376000030">"Հավելվածի մասին"</string>
     <string name="recents_search_bar_label" msgid="8074997400187836677">"որոնել"</string>
@@ -226,4 +234,10 @@
     <string name="speed_bump_explanation" msgid="1288875699658819755">"Պակաս հրատապ ծանուցումները ստորև"</string>
     <string name="notification_tap_again" msgid="7590196980943943842">"Կրկին հպեք՝ բացելու համար"</string>
     <string name="keyguard_unlock" msgid="8043466894212841998">"Սահեցրեք վերև` ապակողպելու համար"</string>
+    <!-- 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-in/strings.xml b/packages/SystemUI/res/values-in/strings.xml
index 98373c9..f62209f 100644
--- a/packages/SystemUI/res/values-in/strings.xml
+++ b/packages/SystemUI/res/values-in/strings.xml
@@ -156,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">"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>
@@ -207,6 +210,7 @@
     <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="2269156590813544104">"TERBARU"</string>
     <string name="recents_app_info_button_label" msgid="2890317189376000030">"Info Aplikasi"</string>
     <string name="recents_search_bar_label" msgid="8074997400187836677">"telusuri"</string>
@@ -226,4 +230,13 @@
     <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..6cf13be 100644
--- a/packages/SystemUI/res/values-it/strings.xml
+++ b/packages/SystemUI/res/values-it/strings.xml
@@ -158,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">"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,6 +212,7 @@
     <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="quick_settings_notifications_label" msgid="4818156442169154523">"Notifiche"</string>
     <string name="recents_empty_message" msgid="2269156590813544104">"MESSAGGI RECENTI"</string>
     <string name="recents_app_info_button_label" msgid="2890317189376000030">"Informazioni sull\'applicazione"</string>
     <string name="recents_search_bar_label" msgid="8074997400187836677">"cerca"</string>
@@ -228,4 +232,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..4f07006 100644
--- a/packages/SystemUI/res/values-iw/strings.xml
+++ b/packages/SystemUI/res/values-iw/strings.xml
@@ -156,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">"המיקום <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,6 +210,7 @@
     <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="quick_settings_notifications_label" msgid="4818156442169154523">"הודעות"</string>
     <string name="recents_empty_message" msgid="2269156590813544104">"אחרונים"</string>
     <string name="recents_app_info_button_label" msgid="2890317189376000030">"מידע על האפליקציה"</string>
     <string name="recents_search_bar_label" msgid="8074997400187836677">"חפש"</string>
@@ -226,4 +230,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..84bbf8b 100644
--- a/packages/SystemUI/res/values-ja/strings.xml
+++ b/packages/SystemUI/res/values-ja/strings.xml
@@ -158,6 +158,12 @@
     <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>
+    <!-- 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 />
     <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,6 +215,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>
+    <!-- no translation found for quick_settings_notifications_label (4818156442169154523) -->
+    <skip />
     <string name="recents_empty_message" msgid="2269156590813544104">"最近"</string>
     <string name="recents_app_info_button_label" msgid="2890317189376000030">"アプリ情報"</string>
     <string name="recents_search_bar_label" msgid="8074997400187836677">"検索"</string>
@@ -228,4 +236,10 @@
     <string name="speed_bump_explanation" msgid="1288875699658819755">"緊急度の低い通知を下に表示"</string>
     <string name="notification_tap_again" msgid="7590196980943943842">"開くにはもう一度タップしてください"</string>
     <string name="keyguard_unlock" msgid="8043466894212841998">"ロック解除するには上にスワイプしてください"</string>
+    <!-- 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-ka-rGE/strings.xml b/packages/SystemUI/res/values-ka-rGE/strings.xml
index 409b9ba..c31ea24 100644
--- a/packages/SystemUI/res/values-ka-rGE/strings.xml
+++ b/packages/SystemUI/res/values-ka-rGE/strings.xml
@@ -156,6 +156,12 @@
     <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>
+    <!-- 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 />
     <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,6 +213,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>
+    <!-- no translation found for quick_settings_notifications_label (4818156442169154523) -->
+    <skip />
     <string name="recents_empty_message" msgid="2269156590813544104">"ბოლო დროის"</string>
     <string name="recents_app_info_button_label" msgid="2890317189376000030">"აპლიკაციის შესახებ"</string>
     <string name="recents_search_bar_label" msgid="8074997400187836677">"ძიება"</string>
@@ -226,4 +234,10 @@
     <string name="speed_bump_explanation" msgid="1288875699658819755">"ქვემოთ მითითებულია ნაკლებად სასწრაფო შეტყობინებები"</string>
     <string name="notification_tap_again" msgid="7590196980943943842">"შეეხეთ ისევ გასახსნელად"</string>
     <string name="keyguard_unlock" msgid="8043466894212841998">"გაასრიალეთ ზევით განსაბლოკად"</string>
+    <!-- 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-km-rKH/strings.xml b/packages/SystemUI/res/values-km-rKH/strings.xml
index f3b4e9e..a9cf7d6 100644
--- a/packages/SystemUI/res/values-km-rKH/strings.xml
+++ b/packages/SystemUI/res/values-km-rKH/strings.xml
@@ -64,7 +64,7 @@
     <string name="screenshot_saving_ticker" msgid="7403652894056693515">"កំពុង​រក្សាទុក​រូបថត​អេក្រង់…"</string>
     <string name="screenshot_saving_title" msgid="8242282144535555697">"កំពុង​រក្សាទុក​រូបថត​អេក្រង់..."</string>
     <string name="screenshot_saving_text" msgid="2419718443411738818">"រូបថត​អេក្រង់​កំពុង​ត្រូវ​បាន​រក្សាទុក។"</string>
-    <string name="screenshot_saved_title" msgid="6461865960961414961">"បាន​ចាប់​យក​រូបថត​អេក្រង់។​"</string>
+    <string name="screenshot_saved_title" msgid="6461865960961414961">"បាន​ចាប់​យក​រូបថត​អេក្រង់។"</string>
     <string name="screenshot_saved_text" msgid="1152839647677558815">"ប៉ះ ​ដើម្បី​មើល​រូបថត​អេក្រង់​របស់​អ្នក​។"</string>
     <string name="screenshot_failed_title" msgid="705781116746922771">"មិន​អាច​ចាប់​យក​រូប​ថត​អេក្រង់​។"</string>
     <string name="screenshot_failed_text" msgid="8134011269572415402">"មិន​អាច​រក្សាទុក​រូបថត​អេក្រង់​។ ឧបករណ៍​ផ្ទុក​អាច​កំពុង​ប្រើ​​។"</string>
@@ -140,7 +140,7 @@
     <string name="accessibility_remove_notification" msgid="3603099514902182350">"សម្អាត​ការ​ជូន​ដំណឹង។"</string>
     <string name="accessibility_gps_enabled" msgid="3511469499240123019">"បាន​បើក GPS ។"</string>
     <string name="accessibility_gps_acquiring" msgid="8959333351058967158">"ទទួល​​ GPS ។"</string>
-    <string name="accessibility_tty_enabled" msgid="4613200365379426561">"បាន​បើក​ម៉ាស៊ីន​អង្គុលីលេខ​"</string>
+    <string name="accessibility_tty_enabled" msgid="4613200365379426561">"បាន​បើក​ម៉ាស៊ីន​អង្គុលីលេខ"</string>
     <string name="accessibility_ringer_vibrate" msgid="666585363364155055">"កម្មវិធី​រោទ៍​ញ័រ។"</string>
     <string name="accessibility_ringer_silent" msgid="9061243307939135383">"កម្មវិធី​រោទ៍​ស្ងាត់។"</string>
     <string name="accessibility_recents_item_dismissed" msgid="6803574935084867070">"<xliff:g id="APP">%s</xliff:g> បដិសេធ។"</string>
@@ -156,6 +156,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>
@@ -187,7 +190,7 @@
     <string name="quick_settings_rotation_locked_portrait_label" msgid="1553131290066230775">"ចាក់​សោ​​បញ្ឈរ"</string>
     <string name="quick_settings_rotation_locked_landscape_label" msgid="7216265671276086593">"ចាក់​សោ​​​ផ្ដេក"</string>
     <string name="quick_settings_ime_label" msgid="7073463064369468429">"វិធីសាស្ត្រ​បញ្ចូល"</string>
-    <string name="quick_settings_location_label" msgid="5011327048748762257">"ទី​តាំង​"</string>
+    <string name="quick_settings_location_label" msgid="5011327048748762257">"ទី​តាំង"</string>
     <string name="quick_settings_location_off_label" msgid="7464544086507331459">"ទីតាំង​បាន​បិទ"</string>
     <string name="quick_settings_media_device_label" msgid="1302906836372603762">"ឧបករណ៍​មេឌៀ"</string>
     <string name="quick_settings_rssi_label" msgid="7725671335550695589">"RSSI"</string>
@@ -207,10 +210,11 @@
     <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="quick_settings_notifications_label" msgid="4818156442169154523">"ការ​ជូនដំណឹង"</string>
     <string name="recents_empty_message" msgid="2269156590813544104">"ថ្មីៗ"</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>
+    <string name="ssl_ca_cert_warning" msgid="9005954106902053641">"បណ្ដាញ​អាច​\nត្រូវ​បាន​ត្រួតពិនិត្យ"</string>
     <string name="description_target_search" msgid="3091587249776033139">"ស្វែងរក"</string>
     <string name="description_direction_up" msgid="7169032478259485180">"រុញ​ឡើង​លើ​ដើម្បី <xliff:g id="TARGET_DESCRIPTION">%s</xliff:g> ។"</string>
     <string name="description_direction_left" msgid="7207478719805562165">"រុញ​ទៅ​ឆ្វេង​ដើម្បី <xliff:g id="TARGET_DESCRIPTION">%s</xliff:g> ។"</string>
@@ -226,4 +230,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..bb2e5c1 100644
--- a/packages/SystemUI/res/values-ko/strings.xml
+++ b/packages/SystemUI/res/values-ko/strings.xml
@@ -156,6 +156,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,6 +210,7 @@
     <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="quick_settings_notifications_label" msgid="4818156442169154523">"알림"</string>
     <string name="recents_empty_message" msgid="2269156590813544104">"최근"</string>
     <string name="recents_app_info_button_label" msgid="2890317189376000030">"애플리케이션 정보"</string>
     <string name="recents_search_bar_label" msgid="8074997400187836677">"검색"</string>
@@ -226,4 +230,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..7220777 100644
--- a/packages/SystemUI/res/values-lo-rLA/strings.xml
+++ b/packages/SystemUI/res/values-lo-rLA/strings.xml
@@ -156,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">"ສະ​ຖານ​ທີ່ <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,6 +210,7 @@
     <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="quick_settings_notifications_label" msgid="4818156442169154523">"ການແຈ້ງເຕືອນ"</string>
     <string name="recents_empty_message" msgid="2269156590813544104">"ບໍ່​ດົນ​ມາ​ນີ້"</string>
     <string name="recents_app_info_button_label" msgid="2890317189376000030">"​ຂໍ້​ມູນ​ແອັບ​ພ​ລິ​ເຄ​ຊັນ"</string>
     <string name="recents_search_bar_label" msgid="8074997400187836677">"ຊອກຫາ"</string>
@@ -226,4 +230,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..d0ebd54 100644
--- a/packages/SystemUI/res/values-lt/strings.xml
+++ b/packages/SystemUI/res/values-lt/strings.xml
@@ -156,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">"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,6 +210,7 @@
     <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="quick_settings_notifications_label" msgid="4818156442169154523">"Pranešimai"</string>
     <string name="recents_empty_message" msgid="2269156590813544104">"PASTARIEJI"</string>
     <string name="recents_app_info_button_label" msgid="2890317189376000030">"Programos informacija"</string>
     <string name="recents_search_bar_label" msgid="8074997400187836677">"paieška"</string>
@@ -226,4 +230,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..23ce4a9 100644
--- a/packages/SystemUI/res/values-lv/strings.xml
+++ b/packages/SystemUI/res/values-lv/strings.xml
@@ -156,6 +156,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,6 +210,7 @@
     <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="quick_settings_notifications_label" msgid="4818156442169154523">"Paziņojumi"</string>
     <string name="recents_empty_message" msgid="2269156590813544104">"JAUNĀKIE"</string>
     <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>
@@ -226,4 +230,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..c00b7ed 100644
--- a/packages/SystemUI/res/values-mn-rMN/strings.xml
+++ b/packages/SystemUI/res/values-mn-rMN/strings.xml
@@ -156,6 +156,12 @@
     <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>
+    <!-- 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 />
     <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,6 +213,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>
+    <!-- no translation found for quick_settings_notifications_label (4818156442169154523) -->
+    <skip />
     <string name="recents_empty_message" msgid="2269156590813544104">"СҮҮЛИЙН"</string>
     <string name="recents_app_info_button_label" msgid="2890317189376000030">"Аппликешны мэдээлэл"</string>
     <string name="recents_search_bar_label" msgid="8074997400187836677">"хайх"</string>
@@ -226,4 +234,10 @@
     <string name="speed_bump_explanation" msgid="1288875699658819755">"Яаралтай биш мэдэгдлүүдийг доор"</string>
     <string name="notification_tap_again" msgid="7590196980943943842">"Нээхийн тулд дахин товшино уу"</string>
     <string name="keyguard_unlock" msgid="8043466894212841998">"Түгжээг тайлах бол шудрана уу"</string>
+    <!-- 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-ms-rMY/strings.xml b/packages/SystemUI/res/values-ms-rMY/strings.xml
index 82216de1..263fc46 100644
--- a/packages/SystemUI/res/values-ms-rMY/strings.xml
+++ b/packages/SystemUI/res/values-ms-rMY/strings.xml
@@ -156,6 +156,12 @@
     <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>
+    <!-- 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 />
     <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,6 +213,8 @@
     <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>
+    <!-- no translation found for quick_settings_notifications_label (4818156442169154523) -->
+    <skip />
     <string name="recents_empty_message" msgid="2269156590813544104">"TERBAHARU"</string>
     <string name="recents_app_info_button_label" msgid="2890317189376000030">"Maklumat Aplikasi"</string>
     <string name="recents_search_bar_label" msgid="8074997400187836677">"cari"</string>
@@ -226,4 +234,10 @@
     <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>
+    <!-- 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-nb/strings.xml b/packages/SystemUI/res/values-nb/strings.xml
index 7ffd381..75706a2 100644
--- a/packages/SystemUI/res/values-nb/strings.xml
+++ b/packages/SystemUI/res/values-nb/strings.xml
@@ -156,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">"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,6 +210,7 @@
     <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="quick_settings_notifications_label" msgid="4818156442169154523">"Varsler"</string>
     <string name="recents_empty_message" msgid="2269156590813544104">"NYLIGE"</string>
     <string name="recents_app_info_button_label" msgid="2890317189376000030">"Appinformasjon"</string>
     <string name="recents_search_bar_label" msgid="8074997400187836677">"Søk"</string>
@@ -226,4 +230,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..c4bef68 100644
--- a/packages/SystemUI/res/values-nl/strings.xml
+++ b/packages/SystemUI/res/values-nl/strings.xml
@@ -156,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">"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,6 +210,7 @@
     <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="quick_settings_notifications_label" msgid="4818156442169154523">"Meldingen"</string>
     <string name="recents_empty_message" msgid="2269156590813544104">"RECENTE"</string>
     <string name="recents_app_info_button_label" msgid="2890317189376000030">"App-informatie"</string>
     <string name="recents_search_bar_label" msgid="8074997400187836677">"zoeken"</string>
@@ -226,4 +230,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..825dfec 100644
--- a/packages/SystemUI/res/values-pl/strings.xml
+++ b/packages/SystemUI/res/values-pl/strings.xml
@@ -156,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">"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,6 +210,7 @@
     <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="quick_settings_notifications_label" msgid="4818156442169154523">"Powiadomienia"</string>
     <string name="recents_empty_message" msgid="2269156590813544104">"OSTATNIE"</string>
     <string name="recents_app_info_button_label" msgid="2890317189376000030">"Informacje o aplikacji"</string>
     <string name="recents_search_bar_label" msgid="8074997400187836677">"szukaj"</string>
@@ -226,4 +230,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..f9ac828 100644
--- a/packages/SystemUI/res/values-pt-rPT/strings.xml
+++ b/packages/SystemUI/res/values-pt-rPT/strings.xml
@@ -156,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">"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,6 +210,7 @@
     <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="quick_settings_notifications_label" msgid="4818156442169154523">"Notificações"</string>
     <string name="recents_empty_message" msgid="2269156590813544104">"RECENTES"</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>
@@ -226,4 +230,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 b0e58e4..1c13e62 100644
--- a/packages/SystemUI/res/values-pt/strings.xml
+++ b/packages/SystemUI/res/values-pt/strings.xml
@@ -158,6 +158,12 @@
     <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>
+    <!-- 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 />
     <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>
@@ -209,6 +215,8 @@
     <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>
+    <!-- no translation found for quick_settings_notifications_label (4818156442169154523) -->
+    <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>
@@ -228,4 +236,10 @@
     <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>
+    <!-- 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-rm/strings.xml b/packages/SystemUI/res/values-rm/strings.xml
index b9d121a..00bf8c0 100644
--- a/packages/SystemUI/res/values-rm/strings.xml
+++ b/packages/SystemUI/res/values-rm/strings.xml
@@ -282,6 +282,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,6 +390,8 @@
     <skip />
     <!-- no translation found for quick_settings_hotspot_label (6046917934974004879) -->
     <skip />
+    <!-- no translation found for quick_settings_notifications_label (4818156442169154523) -->
+    <skip />
     <!-- no translation found for recents_empty_message (2269156590813544104) -->
     <skip />
     <!-- no translation found for recents_app_info_button_label (2890317189376000030) -->
@@ -411,4 +419,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 c48733f..7ffa77c 100644
--- a/packages/SystemUI/res/values-ro/strings.xml
+++ b/packages/SystemUI/res/values-ro/strings.xml
@@ -156,6 +156,12 @@
     <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>
+    <!-- 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 />
     <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,6 +213,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>
+    <!-- no translation found for quick_settings_notifications_label (4818156442169154523) -->
+    <skip />
     <string name="recents_empty_message" msgid="2269156590813544104">"RECENTE"</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>
@@ -226,4 +234,10 @@
     <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>
+    <!-- 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-ru/strings.xml b/packages/SystemUI/res/values-ru/strings.xml
index c726014..d726428 100644
--- a/packages/SystemUI/res/values-ru/strings.xml
+++ b/packages/SystemUI/res/values-ru/strings.xml
@@ -158,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>
@@ -209,6 +212,7 @@
     <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="quick_settings_notifications_label" msgid="4818156442169154523">"Уведомления"</string>
     <string name="recents_empty_message" msgid="2269156590813544104">"НЕДАВНИЕ СООБЩЕНИЯ"</string>
     <string name="recents_app_info_button_label" msgid="2890317189376000030">"Сведения о приложении"</string>
     <string name="recents_search_bar_label" msgid="8074997400187836677">"поиск"</string>
@@ -228,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-sk/strings.xml b/packages/SystemUI/res/values-sk/strings.xml
index 45bb2ad..8debc9a 100644
--- a/packages/SystemUI/res/values-sk/strings.xml
+++ b/packages/SystemUI/res/values-sk/strings.xml
@@ -158,6 +158,12 @@
     <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>
+    <!-- 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 />
     <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,6 +215,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>
+    <!-- no translation found for quick_settings_notifications_label (4818156442169154523) -->
+    <skip />
     <string name="recents_empty_message" msgid="2269156590813544104">"NEDÁVNE"</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>
@@ -228,4 +236,10 @@
     <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>
+    <!-- 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-sl/strings.xml b/packages/SystemUI/res/values-sl/strings.xml
index 99470d6..6171c16 100644
--- a/packages/SystemUI/res/values-sl/strings.xml
+++ b/packages/SystemUI/res/values-sl/strings.xml
@@ -156,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">"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,6 +210,7 @@
     <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="quick_settings_notifications_label" msgid="4818156442169154523">"Obvestila"</string>
     <string name="recents_empty_message" msgid="2269156590813544104">"NEDAVNI"</string>
     <string name="recents_app_info_button_label" msgid="2890317189376000030">"Podatki o aplikaciji"</string>
     <string name="recents_search_bar_label" msgid="8074997400187836677">"iskanje"</string>
@@ -226,4 +230,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..14782e6 100644
--- a/packages/SystemUI/res/values-sr/strings.xml
+++ b/packages/SystemUI/res/values-sr/strings.xml
@@ -156,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">"Локација је <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,6 +210,7 @@
     <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="quick_settings_notifications_label" msgid="4818156442169154523">"Обавештења"</string>
     <string name="recents_empty_message" msgid="2269156590813544104">"НАЈНОВИЈЕ"</string>
     <string name="recents_app_info_button_label" msgid="2890317189376000030">"Информације о апликацији"</string>
     <string name="recents_search_bar_label" msgid="8074997400187836677">"претражи"</string>
@@ -226,4 +230,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..a7fb15d 100644
--- a/packages/SystemUI/res/values-sv/strings.xml
+++ b/packages/SystemUI/res/values-sv/strings.xml
@@ -156,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">"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,6 +210,7 @@
     <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="quick_settings_notifications_label" msgid="4818156442169154523">"Aviseringar"</string>
     <string name="recents_empty_message" msgid="2269156590813544104">"NYA"</string>
     <string name="recents_app_info_button_label" msgid="2890317189376000030">"Appinformation"</string>
     <string name="recents_search_bar_label" msgid="8074997400187836677">"sök"</string>
@@ -226,4 +230,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 3525b5e..b8d8c58 100644
--- a/packages/SystemUI/res/values-sw/strings.xml
+++ b/packages/SystemUI/res/values-sw/strings.xml
@@ -154,6 +154,12 @@
     <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>
+    <!-- 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 />
     <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,6 +211,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>
+    <!-- no translation found for quick_settings_notifications_label (4818156442169154523) -->
+    <skip />
     <string name="recents_empty_message" msgid="2269156590813544104">"YA HIVI 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>
@@ -224,4 +232,10 @@
     <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>
+    <!-- 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-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 3217daa..dc75e78 100644
--- a/packages/SystemUI/res/values-th/strings.xml
+++ b/packages/SystemUI/res/values-th/strings.xml
@@ -156,6 +156,12 @@
     <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>
+    <!-- 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 />
     <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,6 +213,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>
+    <!-- no translation found for quick_settings_notifications_label (4818156442169154523) -->
+    <skip />
     <string name="recents_empty_message" msgid="2269156590813544104">"ล่าสุด"</string>
     <string name="recents_app_info_button_label" msgid="2890317189376000030">"ข้อมูลแอปพลิเคชัน"</string>
     <string name="recents_search_bar_label" msgid="8074997400187836677">"ค้นหา"</string>
@@ -226,4 +234,10 @@
     <string name="speed_bump_explanation" msgid="1288875699658819755">"การแจ้งเตือนที่เร่งด่วนน้อยด้านล่าง"</string>
     <string name="notification_tap_again" msgid="7590196980943943842">"แตะอีกครั้งเพื่อเปิด"</string>
     <string name="keyguard_unlock" msgid="8043466894212841998">"กวาดขึ้นเพื่อปลดล็อก"</string>
+    <!-- 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-tl/strings.xml b/packages/SystemUI/res/values-tl/strings.xml
index 2f8ae2f..0713ddd 100644
--- a/packages/SystemUI/res/values-tl/strings.xml
+++ b/packages/SystemUI/res/values-tl/strings.xml
@@ -156,6 +156,12 @@
     <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>
+    <!-- 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 />
     <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>
@@ -207,6 +213,8 @@
     <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>
+    <!-- no translation found for quick_settings_notifications_label (4818156442169154523) -->
+    <skip />
     <string name="recents_empty_message" msgid="2269156590813544104">"MGA KAMAKAILAN"</string>
     <string name="recents_app_info_button_label" msgid="2890317189376000030">"Impormasyon ng Application"</string>
     <string name="recents_search_bar_label" msgid="8074997400187836677">"maghanap"</string>
@@ -226,4 +234,10 @@
     <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>
+    <!-- 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-tr/strings.xml b/packages/SystemUI/res/values-tr/strings.xml
index 506d27e..c0ba250 100644
--- a/packages/SystemUI/res/values-tr/strings.xml
+++ b/packages/SystemUI/res/values-tr/strings.xml
@@ -156,6 +156,12 @@
     <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>
+    <!-- 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 />
     <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,6 +213,8 @@
     <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>
+    <!-- no translation found for quick_settings_notifications_label (4818156442169154523) -->
+    <skip />
     <string name="recents_empty_message" msgid="2269156590813544104">"SON İLETİLER"</string>
     <string name="recents_app_info_button_label" msgid="2890317189376000030">"Uygulama Bilgileri"</string>
     <string name="recents_search_bar_label" msgid="8074997400187836677">"ara"</string>
@@ -226,4 +234,10 @@
     <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>
+    <!-- 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-uk/strings.xml b/packages/SystemUI/res/values-uk/strings.xml
index 5b3c5b2..e149d7b 100644
--- a/packages/SystemUI/res/values-uk/strings.xml
+++ b/packages/SystemUI/res/values-uk/strings.xml
@@ -156,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">"Місцезнаходження <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,6 +210,7 @@
     <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="quick_settings_notifications_label" msgid="4818156442169154523">"Сповіщення"</string>
     <string name="recents_empty_message" msgid="2269156590813544104">"ОСТАННІ"</string>
     <string name="recents_app_info_button_label" msgid="2890317189376000030">"Інформація про додаток"</string>
     <string name="recents_search_bar_label" msgid="8074997400187836677">"пошук"</string>
@@ -226,4 +230,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..f99c341 100644
--- a/packages/SystemUI/res/values-vi/strings.xml
+++ b/packages/SystemUI/res/values-vi/strings.xml
@@ -156,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">"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,6 +210,7 @@
     <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="quick_settings_notifications_label" msgid="4818156442169154523">"Thông báo"</string>
     <string name="recents_empty_message" msgid="2269156590813544104">"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>
@@ -226,4 +230,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 d8e1c35..6940b2c 100644
--- a/packages/SystemUI/res/values-zh-rCN/strings.xml
+++ b/packages/SystemUI/res/values-zh-rCN/strings.xml
@@ -158,6 +158,12 @@
     <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>
+    <!-- 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 />
     <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,6 +215,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>
+    <!-- no translation found for quick_settings_notifications_label (4818156442169154523) -->
+    <skip />
     <string name="recents_empty_message" msgid="2269156590813544104">"最近"</string>
     <string name="recents_app_info_button_label" msgid="2890317189376000030">"应用信息"</string>
     <string name="recents_search_bar_label" msgid="8074997400187836677">"搜索"</string>
@@ -228,4 +236,10 @@
     <string name="speed_bump_explanation" msgid="1288875699658819755">"不太紧急的通知会显示在下方"</string>
     <string name="notification_tap_again" msgid="7590196980943943842">"再次点按即可打开"</string>
     <string name="keyguard_unlock" msgid="8043466894212841998">"向上滑动即可解锁"</string>
+    <!-- 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-zh-rHK/strings.xml b/packages/SystemUI/res/values-zh-rHK/strings.xml
index ce9e260..04c00f5 100644
--- a/packages/SystemUI/res/values-zh-rHK/strings.xml
+++ b/packages/SystemUI/res/values-zh-rHK/strings.xml
@@ -158,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>
@@ -209,6 +212,7 @@
     <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="quick_settings_notifications_label" msgid="4818156442169154523">"通知"</string>
     <string name="recents_empty_message" msgid="2269156590813544104">"近期"</string>
     <string name="recents_app_info_button_label" msgid="2890317189376000030">"應用程式資料"</string>
     <string name="recents_search_bar_label" msgid="8074997400187836677">"搜尋"</string>
@@ -228,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-zh-rTW/strings.xml b/packages/SystemUI/res/values-zh-rTW/strings.xml
index cafb688..4ffb01b 100644
--- a/packages/SystemUI/res/values-zh-rTW/strings.xml
+++ b/packages/SystemUI/res/values-zh-rTW/strings.xml
@@ -158,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>
@@ -209,6 +212,7 @@
     <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="quick_settings_notifications_label" msgid="4818156442169154523">"通知"</string>
     <string name="recents_empty_message" msgid="2269156590813544104">"近期"</string>
     <string name="recents_app_info_button_label" msgid="2890317189376000030">"應用程式資訊"</string>
     <string name="recents_search_bar_label" msgid="8074997400187836677">"搜尋"</string>
@@ -228,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-zu/strings.xml b/packages/SystemUI/res/values-zu/strings.xml
index 4865f7b..4c44972 100644
--- a/packages/SystemUI/res/values-zu/strings.xml
+++ b/packages/SystemUI/res/values-zu/strings.xml
@@ -156,6 +156,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,6 +210,7 @@
     <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="quick_settings_notifications_label" msgid="4818156442169154523">"Izaziso"</string>
     <string name="recents_empty_message" msgid="2269156590813544104">"OKWAKAMUVA"</string>
     <string name="recents_app_info_button_label" msgid="2890317189376000030">"Ulwazi lohlelo lokusebenza"</string>
     <string name="recents_search_bar_label" msgid="8074997400187836677">"sesha"</string>
@@ -226,4 +230,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 6405ae6..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. -->
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index 6e35230..bfbdcf3 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -212,7 +212,7 @@
     <dimen name="glowpadview_inner_radius">15dip</dimen>
 
     <!-- The size of the application icon in the recents task view. -->
-    <dimen name="recents_task_view_application_icon_size">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>
@@ -221,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>
@@ -293,10 +305,20 @@
          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>
+
+    <!-- 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 ef3956e..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>
@@ -522,7 +524,7 @@
     <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] -->
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/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/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/NotificationsTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/NotificationsTile.java
index 130f9ce..20bbf8b 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/NotificationsTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/NotificationsTile.java
@@ -85,7 +85,7 @@
                 // noop
             }
         });
-        vp.postVolumeChanged(AudioManager.STREAM_NOTIFICATION, AudioManager.FLAG_SHOW_UI);
+        vp.postVolumeChanged(AudioManager.STREAM_RING, AudioManager.FLAG_SHOW_UI);
         return v;
     }
 
@@ -108,11 +108,7 @@
 
     @Override
     protected void handleClick() {
-        if (mState.zen) {
-            mZenController.setZen(false);
-        } else {
-            showDetail(true);
-        }
+        showDetail(true);
     }
 
     @Override
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/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..5f1325b5 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..de27119f 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 b7a7b0a..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,
@@ -86,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.
      */
@@ -110,7 +139,7 @@
     }
 
     public boolean isShowing() {
-        return mRoot != null && mRoot.getVisibility() == View.VISIBLE;
+        return mRoot != null && mRoot.getVisibility() == View.VISIBLE && !mFadingOut;
     }
 
     public void prepare() {
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/NotificationPanelView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
index 52688df..dfd5a88 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
@@ -21,6 +21,7 @@
 import android.animation.ObjectAnimator;
 import android.animation.ValueAnimator;
 import android.content.Context;
+import android.content.res.Configuration;
 import android.util.AttributeSet;
 import android.view.MotionEvent;
 import android.view.VelocityTracker;
@@ -40,11 +41,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 +64,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 +72,7 @@
      */
     private boolean mIntercepting;
     private boolean mQsExpanded;
+    private boolean mQsFullyExpanded;
     private boolean mKeyguardShowing;
     private float mInitialHeightOnTouch;
     private float mInitialTouchX;
@@ -79,6 +86,7 @@
     private int mQsPeekHeight;
     private float mNotificationTranslation;
     private int mStackScrollerIntrinsicPadding;
+    private boolean mStackScrollerOverscrolling;
     private boolean mQsExpansionEnabled = true;
     private ValueAnimator mQsExpansionAnimator;
     private FlingAnimationUtils mFlingAnimationUtils;
@@ -92,6 +100,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 +140,13 @@
         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);
+        mKeyguardBottomArea = (KeyguardBottomAreaView) findViewById(R.id.keyguard_bottom_area);
+        mSwipeTranslationViews.add(mNotificationStackScroller);
+        mSwipeTranslationViews.add(mKeyguardStatusView);
+        mPageSwiper = new KeyguardPageSwipeHelper(this, getContext());
     }
 
     @Override
@@ -151,8 +169,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 +189,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 +274,13 @@
         mQsExpansionEnabled = qsExpansionEnabled;
     }
 
+    @Override
+    public void resetViews() {
+        mBlockTouches = false;
+        mPageSwiper.reset();
+        closeQs();
+    }
+
     public void closeQs() {
         cancelAnimation();
         setQsExpansion(mQsMinExpansionHeight);
@@ -263,9 +297,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 +315,9 @@
 
     @Override
     public boolean onInterceptTouchEvent(MotionEvent event) {
+        if (mBlockTouches) {
+            return false;
+        }
         int pointerIndex = event.findPointerIndex(mTrackingPointer);
         if (pointerIndex < 0) {
             pointerIndex = 0;
@@ -298,7 +333,7 @@
                 mInitialTouchX = x;
                 initVelocityTracker();
                 trackMovement(event);
-                if (shouldIntercept(mInitialTouchX, mInitialTouchY, 0)) {
+                if (shouldQuickSettingsIntercept(mInitialTouchX, mInitialTouchY, 0)) {
                     getParent().requestDisallowInterceptTouchEvent(true);
                 }
                 break;
@@ -316,7 +351,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 +362,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 +376,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 +397,7 @@
         super.requestDisallowInterceptTouchEvent(disallowIntercept);
     }
 
-    private void flingWithCurrentVelocity() {
+    private void flingQsWithCurrentVelocity() {
         float vel = getCurrentVelocity();
 
         // TODO: Better logic whether we should expand or not.
@@ -371,65 +406,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 +492,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 +533,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 +545,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 +554,9 @@
         mQsExpansionHeight = height;
         mHeader.setExpansion(height - mQsPeekHeight);
         setQsTranslation(height);
-        setQsStackScrollerPadding(height);
+        if (!mStackScrollerOverscrolling) {
+            setQsStackScrollerPadding(height);
+        }
         mStatusBar.userActivity();
     }
 
@@ -569,7 +636,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;
         }
@@ -647,19 +714,24 @@
     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);
     }
 
@@ -686,6 +758,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 +774,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 1015d5b..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);
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 7500c10..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,6 +72,9 @@
     private float mInitialTouchY;
     private float mInitialTouchX;
 
+    private Interpolator mLinearOutSlowInInterpolator;
+    private Interpolator mBounceInterpolator;
+
     protected void onExpandingFinished() {
         mBar.onExpandingFinished();
     }
@@ -89,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() {
@@ -98,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) {
@@ -130,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();
@@ -166,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);
                 }
 
@@ -183,25 +214,35 @@
 
             case MotionEvent.ACTION_UP:
             case MotionEvent.ACTION_CANCEL:
-                mTracking = false;
                 mTrackingPointer = -1;
                 trackMovement(event);
-                boolean expand = flingWithCurrentVelocity();
-                onTrackingStopped(expand);
+                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 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();
     }
@@ -244,6 +285,7 @@
                 }
                 mInitialTouchY = y;
                 mInitialTouchX = x;
+                mTouchSlopExceeded = false;
                 initVelocityTracker();
                 trackMovement(event);
                 break;
@@ -267,6 +309,7 @@
                         mInitialTouchY = y;
                         mInitialTouchX = x;
                         mTracking = true;
+                        mTouchSlopExceeded = true;
                         onTrackingStarted();
                         return true;
                     }
@@ -305,18 +348,15 @@
     }
 
     /**
-     * @return whether the panel will be expanded after the animation
+     * @param vel the current velocity of the motion
+     * @return whether a fling should expands the panel; contracts otherwise
      */
-    private boolean flingWithCurrentVelocity() {
-        float vel = getCurrentVelocity();
-        boolean expand;
+    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);
-        return expand;
     }
 
     protected void fling(float vel, boolean expand) {
@@ -324,9 +364,10 @@
         float target = expand ? getMaxPanelHeight() : 0.0f;
         if (target == mExpandedHeight) {
             onExpandingFinished();
+            mBar.panelExpansionChanged(this, mExpandedFraction);
             return;
         }
-        ValueAnimator animator = ValueAnimator.ofFloat(mExpandedHeight, target);
+        ValueAnimator animator = createHeightAnimator(target);
         if (expand) {
             mFlingAnimationUtils.apply(animator, mExpandedHeight, target, vel, getHeight());
         } else {
@@ -338,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) {
@@ -412,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) {
@@ -424,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
@@ -508,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"
@@ -522,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 ba998c5..b1216e69 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;
@@ -979,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
@@ -1043,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)) {
@@ -1089,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);
@@ -1105,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) {
@@ -1149,7 +1161,7 @@
             R.integer.config_show_search_delay);
     }
 
-    private void loadNotificationShade() {
+    private void updateNotificationShade() {
         if (mStackScroller == null) return;
 
         int N = mNotificationData.size();
@@ -1159,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.
@@ -1190,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>();
@@ -1217,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;
@@ -2296,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() {
@@ -2366,7 +2435,7 @@
     };
 
     @Override
-    protected void startNotificationActivity(OnDismissAction action) {
+    protected void dismissKeyguardThenExecute(OnDismissAction action) {
         if (mStatusBarKeyguardViewManager.isShowing()) {
             mStatusBarKeyguardViewManager.dismissWithAction(action);
         } else {
@@ -2394,7 +2463,7 @@
     public void userSwitched(int newUserId) {
         if (MULTIUSER_DEBUG) mNotificationPanelDebugText.setText("USER " + newUserId);
         animateCollapsePanels();
-        updateNotificationIcons();
+        updateNotifications();
         resetUserSetupObserver();
     }
 
@@ -2780,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);
@@ -2799,10 +2868,8 @@
 
         updateStackScrollerState();
         updatePublicMode();
-        updateRowStates();
-        updateSpeedBump();
+        updateNotifications();
         checkBarModes();
-        updateNotificationIcons();
         updateCarrierLabelVisibility(false);
     }
 
@@ -2890,15 +2957,17 @@
     }
 
     public void onTrackingStarted() {
-        if (mState == StatusBarState.KEYGUARD) {
-            mKeyguardIndicationTextView.switchIndication(R.string.keyguard_unlock);
-        }
+    }
+
+    public void onUnlockHintStarted() {
+        mKeyguardIndicationTextView.switchIndication(R.string.keyguard_unlock);
+    }
+
+    public void onUnlockHintFinished() {
+        mKeyguardIndicationTextView.switchIndication(mKeyguardHotwordPhrase);
     }
 
     public void onTrackingStopped(boolean expand) {
-        if (mState == StatusBarState.KEYGUARD) {
-            mKeyguardIndicationTextView.switchIndication(mKeyguardHotwordPhrase);
-        }
         if (mState == StatusBarState.KEYGUARD || mState == StatusBarState.SHADE_LOCKED) {
             if (!expand && !mUnlockMethodCache.isMethodInsecure()) {
                 showBouncer();
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 1344703..7c87580 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/QSTileHost.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/QSTileHost.java
@@ -95,7 +95,6 @@
         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
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 6156fc3..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
@@ -53,6 +49,11 @@
     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();
 
@@ -87,14 +88,26 @@
         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 {
@@ -170,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) {
@@ -188,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 d5551b8..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;
@@ -183,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..6892b85 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);
@@ -1591,6 +1615,13 @@
         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 +1630,7 @@
                 new AnimationFilter()
                         .animateAlpha()
                         .animateHeight()
+                        .animateTopInset()
                         .animateY()
                         .animateZ()
                         .hasDelays(),
@@ -1607,6 +1639,7 @@
                 new AnimationFilter()
                         .animateAlpha()
                         .animateHeight()
+                        .animateTopInset()
                         .animateY()
                         .animateZ()
                         .hasDelays(),
@@ -1615,6 +1648,7 @@
                 new AnimationFilter()
                         .animateAlpha()
                         .animateHeight()
+                        .animateTopInset()
                         .animateY()
                         .animateZ()
                         .hasDelays(),
@@ -1623,6 +1657,7 @@
                 new AnimationFilter()
                         .animateAlpha()
                         .animateHeight()
+                        .animateTopInset()
                         .animateY()
                         .animateDimmed()
                         .animateScale()
@@ -1651,6 +1686,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/packages/SystemUI/src/com/android/systemui/volume/VolumePanel.java b/packages/SystemUI/src/com/android/systemui/volume/VolumePanel.java
index 06f4c2e..898b46e 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/VolumePanel.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/VolumePanel.java
@@ -16,6 +16,8 @@
 
 package com.android.systemui.volume;
 
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
 import android.app.AlertDialog;
 import android.app.Dialog;
 import android.content.BroadcastReceiver;
@@ -25,12 +27,15 @@
 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;
@@ -39,11 +44,15 @@
 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;
@@ -76,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;
@@ -100,9 +111,11 @@
     protected final Context mContext;
     private final AudioManager mAudioManager;
     private final ZenModeController mZenController;
+    private final Interpolator mFastOutSlowInInterpolator;
     private boolean mRingIsSilent;
     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;
@@ -214,6 +227,7 @@
         ViewGroup group;
         ImageView icon;
         SeekBar seekbarView;
+        View seekbarContainer;
         int iconRes;
         int iconMuteRes;
     }
@@ -267,6 +281,8 @@
         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();
@@ -278,7 +294,6 @@
             }
         }
         if (LOGD) Log.d(mTag, String.format("new VolumePanel hasParent=%s", parent != null));
-        final int layoutId = com.android.systemui.R.layout.volume_panel;
         if (parent == null) {
             // dialog mode
             mDialog = new Dialog(context) {
@@ -301,17 +316,18 @@
             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.setBackgroundDrawableResource(com.android.systemui.R.drawable.qs_panel_background);
             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_WATCH_OUTSIDE_TOUCH
+                    | LayoutParams.FLAG_HARDWARE_ACCELERATED);
             mDialog.setCanceledOnTouchOutside(true);
-            mDialog.setContentView(layoutId);
+            mDialog.setContentView(com.android.systemui.R.layout.volume_dialog);
             mDialog.setOnDismissListener(new OnDismissListener() {
                 @Override
                 public void onDismiss(DialogInterface dialog) {
@@ -321,6 +337,8 @@
             });
 
             mDialog.create();
+            // temporary workaround, until we support window-level shadows
+            mDialog.getWindow().setBackgroundDrawable(new ColorDrawable(0x00000000));
 
             mView = window.findViewById(R.id.content);
             mView.setOnTouchListener(new View.OnTouchListener() {
@@ -334,7 +352,8 @@
         } else {
             // embedded mode
             mDialog = null;
-            mView = LayoutInflater.from(mContext).inflate(layoutId, parent, true);
+            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);
@@ -444,6 +463,26 @@
             sc.iconRes = streamRes.iconRes;
             sc.iconMuteRes = streamRes.iconMuteRes;
             sc.icon.setImageResource(sc.iconRes);
+            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;
@@ -454,6 +493,26 @@
         }
     }
 
+    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) {
         mSliderPanel.removeAllViews();
 
@@ -477,21 +536,62 @@
         // 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(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) {
@@ -513,6 +613,7 @@
 
                 @Override
                 public void onInteraction() {
+                    resetTimeout();
                     if (mZenPanelCallback != null) {
                         mZenPanelCallback.onInteraction();
                     }
@@ -521,6 +622,8 @@
         }
         mZenPanel.setVisibility(View.VISIBLE);
         mZenPanelDivider.setVisibility(View.VISIBLE);
+        mTimeoutDelay = TIMEOUT_DELAY_EXPANDED;
+        resetTimeout();
     }
 
     private void collapse() {
@@ -529,6 +632,8 @@
             mZenPanel.setVisibility(View.GONE);
         }
         mZenPanelDivider.setVisibility(View.GONE);
+        mTimeoutDelay = TIMEOUT_DELAY;
+        resetTimeout();
     }
 
     public void updateStates() {
@@ -541,8 +646,7 @@
 
     private void updateZenMode(boolean zen) {
         if (mZenModeCapable) {
-            final boolean show = mActiveStreamType == AudioManager.STREAM_NOTIFICATION
-                    || mActiveStreamType == AudioManager.STREAM_RING;
+            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
@@ -555,7 +659,7 @@
 
     public void postZenModeChanged(boolean zen) {
         removeMessages(MSG_ZEN_MODE_CHANGED);
-        obtainMessage(MSG_ZEN_MODE_CHANGED, zen ? 1 : 0).sendToTarget();
+        obtainMessage(MSG_ZEN_MODE_CHANGED, zen ? 1 : 0, 0).sendToTarget();
     }
 
     public void postVolumeChanged(int streamType, int flags) {
@@ -633,7 +737,7 @@
 
     public void postLayoutDirection(int layoutDirection) {
         removeMessages(MSG_LAYOUT_DIRECTION);
-        obtainMessage(MSG_LAYOUT_DIRECTION, layoutDirection).sendToTarget();
+        obtainMessage(MSG_LAYOUT_DIRECTION, layoutDirection, 0).sendToTarget();
     }
 
     /**
@@ -769,15 +873,8 @@
             }
 
             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 (!isShowing()) {
@@ -801,6 +898,11 @@
                 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() {
@@ -1035,8 +1137,8 @@
                 if (isShowing()) {
                     if (mDialog != null) {
                         mDialog.dismiss();
+                        mActiveStreamType = -1;
                     }
-                    mActiveStreamType = -1;
                 }
                 synchronized (sConfirmSafeVolumeLock) {
                     if (sConfirmSafeVolumeDialog != null) {
@@ -1082,7 +1184,7 @@
     public void resetTimeout() {
         if (LOGD) Log.d(mTag, "resetTimeout at " + System.currentTimeMillis());
         removeMessages(MSG_TIMEOUT);
-        sendEmptyMessageDelayed(MSG_TIMEOUT, TIMEOUT_DELAY);
+        sendEmptyMessageDelayed(MSG_TIMEOUT, mTimeoutDelay);
     }
 
     private void forceTimeout() {
@@ -1134,7 +1236,12 @@
         public void onClick(View v) {
             if (v == mExpandButton && mZenController != null) {
                 final boolean newZen = !mZenController.isZen();
-                mZenController.setZen(newZen);
+                AsyncTask.execute(new Runnable() {
+                    @Override
+                    public void run() {
+                        mZenController.setZen(newZen);
+                    }
+                });
                 if (newZen) {
                     expand();
                 } else {
@@ -1147,7 +1254,7 @@
 
     private final ZenModeController.Callback mZenCallback = new ZenModeController.Callback() {
         public void onZenChanged(boolean zen) {
-            updateZenMode(zen);
+            postZenModeChanged(zen);
         }
     };
 }
diff --git a/packages/SystemUI/src/com/android/systemui/volume/ZenModePanel.java b/packages/SystemUI/src/com/android/systemui/volume/ZenModePanel.java
index 77d267e..c338563 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/ZenModePanel.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/ZenModePanel.java
@@ -45,6 +45,7 @@
     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();
@@ -56,6 +57,7 @@
 
     public ZenModePanel(Context context, AttributeSet attrs) {
         super(context, attrs);
+        mContext = context;
         mInflater = LayoutInflater.from(new ContextThemeWrapper(context, R.style.QSWhiteTheme));
     }
 
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..5dc9e58 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;
             }
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/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..3a8f767 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) {
@@ -2650,7 +2655,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/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 cd8c13fb..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 ed83b4b..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;
         }
@@ -7737,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);
@@ -7754,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) {
@@ -7890,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);
                 }
 
@@ -7976,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);
                 }
 
@@ -8520,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);
@@ -8555,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;
@@ -9772,7 +9824,7 @@
                                 = (ApplicationInfo)apps.get(i);
                             if (info != null &&
                                     !info.packageName.equals("android")) {
-                                addAppLocked(info, false);
+                                addAppLocked(info, false, null /* ABI override */);
                             }
                         }
                     }
@@ -10563,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());
 
@@ -10653,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) {
@@ -12954,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;
@@ -13094,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) {
@@ -13115,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);
@@ -13145,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) {
@@ -13157,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
@@ -13171,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) {
@@ -13717,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 {
@@ -14251,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);
@@ -14300,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;
@@ -16264,7 +16331,7 @@
                     
                     if (app.persistent) {
                         if (app.persistent) {
-                            addAppLocked(app.info, false);
+                            addAppLocked(app.info, false, null /* ABI override */);
                         }
                     }
                 }
@@ -16528,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);
         }
@@ -16899,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);
         }
@@ -17042,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()
@@ -17128,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 b429b93..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
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 92f41f7..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);
     }
@@ -1412,7 +1407,10 @@
 
     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()) {
@@ -1656,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;
                         }
@@ -1841,11 +1839,6 @@
                         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 {
@@ -1857,7 +1850,7 @@
                         == (Intent.FLAG_ACTIVITY_NEW_TASK|Intent.FLAG_ACTIVITY_TASK_ON_HOME)) {
                     // Caller wants to appear on home activity, so before starting
                     // their own activity we will bring home to the front.
-                    r.task.setTaskToReturnTo(HOME_ACTIVITY_TYPE);
+                    r.task.mOnTopOfHome = r.task.stack.isOnHomeDisplay();
                 }
             }
         } else if (sourceRecord != null) {
@@ -1930,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);
@@ -2208,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="
@@ -2315,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) {
@@ -2581,7 +2574,7 @@
             }
         } else {
             // Stack was moved to another display while user was swapped out.
-            resumeHomeStackTask(HOME_ACTIVITY_TYPE, null);
+            resumeHomeActivity(null);
         }
         return homeInFront;
     }
@@ -3496,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/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/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/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 009943f..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;
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 a629a5f..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,7 +149,8 @@
         };
     }
 
-    private String[] getExtraPeople(Bundle extras) {
+    // VisibleForTesting
+    public static String[] getExtraPeople(Bundle extras) {
         Object people = extras.get(Notification.EXTRA_PEOPLE);
         if (people instanceof String[]) {
             return (String[]) people;
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 a0038c5d..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/TvInputManagerService.java b/services/core/java/com/android/server/tv/TvInputManagerService.java
index 6c38a4c..e52f218 100644
--- a/services/core/java/com/android/server/tv/TvInputManagerService.java
+++ b/services/core/java/com/android/server/tv/TvInputManagerService.java
@@ -33,6 +33,7 @@
 import android.graphics.Rect;
 import android.net.Uri;
 import android.os.Binder;
+import android.os.Bundle;
 import android.os.Handler;
 import android.os.IBinder;
 import android.os.Looper;
@@ -338,6 +339,41 @@
                     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.
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/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/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index d78fb13..a52396e 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..164fe05 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -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 2483419..ffa9a4e 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -1988,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;
     }
@@ -1999,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 c162bf28..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/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 2abf65194..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/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/TypedArray_Delegate.java b/tools/layoutlib/bridge/src/android/content/res/TypedArray_Delegate.java
index 5d89f83..faa8852 100644
--- a/tools/layoutlib/bridge/src/android/content/res/TypedArray_Delegate.java
+++ b/tools/layoutlib/bridge/src/android/content/res/TypedArray_Delegate.java
@@ -30,7 +30,6 @@
 
     @LayoutlibDelegate
     /*package*/ static TypedArray obtain(Resources res, int len) {
-        // FIXME
-        return null;
+        return BridgeTypedArray.obtain(res, len);
     }
 }
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 e9daffd..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) {
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
index 5e7543a..9ea4538 100644
--- a/tools/layoutlib/bridge/src/android/graphics/FontFamily_Delegate.java
+++ b/tools/layoutlib/bridge/src/android/graphics/FontFamily_Delegate.java
@@ -146,6 +146,8 @@
 
     @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);
     }
 
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/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 ed8f3b4..9746b48 100644
--- a/tools/layoutlib/bridge/src/android/graphics/Typeface_Delegate.java
+++ b/tools/layoutlib/bridge/src/android/graphics/Typeface_Delegate.java
@@ -54,7 +54,7 @@
 
     // ---- delegate data ----
 
-    private final long[] mFontFamilies;  // the reference to FontFamily_Delegate.
+    private final FontFamily_Delegate[] mFontFamilies;  // the reference to FontFamily_Delegate.
     private int mStyle;
 
     private static long sDefaultTypeface;
@@ -71,8 +71,7 @@
 
     public List<Font> getFonts(boolean compact) {
         List<Font> fonts = new ArrayList<Font>(mFontFamilies.length);
-        for (long fontFamily : mFontFamilies) {
-            FontFamily_Delegate ffd = FontFamily_Delegate.getDelegate(fontFamily);
+        for (FontFamily_Delegate ffd : mFontFamilies) {
             if (ffd != null) {
                 Font font = ffd.getFont(mStyle, compact);
                 if (font != null) {
@@ -122,7 +121,11 @@
 
     @LayoutlibDelegate
     /*package*/ static synchronized long nativeCreateFromArray(long[] familyArray) {
-        Typeface_Delegate delegate = new Typeface_Delegate(familyArray, Typeface.NORMAL);
+        FontFamily_Delegate[] fontFamilies = new FontFamily_Delegate[familyArray.length];
+        for (int i = 0; i < familyArray.length; i++) {
+            fontFamilies[i] = FontFamily_Delegate.getDelegate(familyArray[i]);
+        }
+        Typeface_Delegate delegate = new Typeface_Delegate(fontFamilies, Typeface.NORMAL);
         return sManager.addNewDelegate(delegate);
     }
 
@@ -153,9 +156,8 @@
 
     // ---- Private delegate/helper methods ----
 
-    private Typeface_Delegate(long[] fontFamilies, int style) {
+    private Typeface_Delegate(FontFamily_Delegate[] fontFamilies, int style) {
         mFontFamilies = fontFamilies;
         mStyle = style;
     }
-
 }
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/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/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..5ef1bf9 100644
--- a/wifi/java/android/net/wifi/passpoint/WifiPasspointInfo.java
+++ b/wifi/java/android/net/wifi/passpoint/WifiPasspointInfo.java
@@ -88,68 +88,174 @@
                     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 byte[] rawData;
+
+        public int getMnc() {
+            // TODO
+            return 0;
+        }
+
+        public int getMcc() {
+            // TODO
+            return 0;
+        }
+
+        @Override
+        public String toString() {
+            if (rawData == null) return null;
+            StringBuilder sb = new StringBuilder();
+            for (int i = 0; i < rawData.length; i++)
+                sb.append(String.format("%02X",  rawData[i]));
+            return sb.toString();
+        }
+
+    }
+
+    /** 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 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 +299,149 @@
     @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);
+            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: ").append(cellularNetwork);
-        if (domainName != null)
-            sb.append(" domainName: ").append(domainName);
+            sb.append(" cellularNetwork: ").append("(")
+              .append(cellularNetwork.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.rawData.length);
+            out.writeByteArray(cellularNetwork.rawData);
+        }
+
+
+        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 +463,86 @@
                 @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 CellularNetwork();
+                        p.cellularNetwork.rawData = new byte[n];
+                        in.readByteArray(p.cellularNetwork.rawData);
+                    }
+
+                    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 +551,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;