Merge "add uid and debug fields to wifiConfiguration"
diff --git a/api/current.txt b/api/current.txt
index a2a8661..fb1259f 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -1867,28 +1867,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 +1935,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 +2019,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 +2329,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 +3429,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();
@@ -5031,6 +5033,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);
@@ -5046,7 +5053,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 {
@@ -5566,8 +5575,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();
@@ -6210,180 +6219,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);
@@ -6431,6 +6266,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 {
@@ -6915,6 +6911,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();
@@ -7040,7 +7037,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";
   }
@@ -7084,6 +7080,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();
@@ -8200,7 +8197,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();
@@ -8211,21 +8208,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 {
@@ -12725,6 +12722,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 {
@@ -14296,7 +14296,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();
@@ -14346,10 +14345,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();
@@ -14678,6 +14673,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;
@@ -15815,56 +15811,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;
@@ -15876,95 +15897,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
   }
 
 }
@@ -17290,86 +17266,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);
@@ -17583,29 +17479,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 {
@@ -17740,25 +17613,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";
@@ -20599,6 +20462,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;
@@ -20727,17 +20621,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);
@@ -20754,37 +20645,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[]);
@@ -20796,30 +20671,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;
@@ -20837,9 +20701,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);
@@ -21419,46 +21280,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;
@@ -26415,16 +26244,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);
@@ -26451,6 +26281,7 @@
   }
 
   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);
@@ -26605,6 +26436,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();
@@ -26667,9 +26520,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();
@@ -26772,7 +26626,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();
   }
 
@@ -26846,6 +26702,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);
@@ -27787,6 +27722,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);
@@ -28786,6 +28722,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();
@@ -38165,8 +38102,10 @@
     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();
   }
 
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/core/java/android/app/Activity.java b/core/java/android/app/Activity.java
index b5281ff..5257430 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;
     }
 
@@ -5632,8 +5638,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),
@@ -5842,6 +5854,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/ContextImpl.java b/core/java/android/app/ContextImpl.java
index ff8688d..460732f 100644
--- a/core/java/android/app/ContextImpl.java
+++ b/core/java/android/app/ContextImpl.java
@@ -58,7 +58,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 +251,8 @@
     private File[] mExternalFilesDirs;
     @GuardedBy("mSync")
     private File[] mExternalCacheDirs;
+    @GuardedBy("mSync")
+    private File[] mExternalMediaDirs;
 
     private static final String[] EMPTY_FILE_LIST = {};
 
@@ -386,6 +390,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) {
@@ -1032,6 +1041,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 +1755,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/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 90aeaae..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();
         }
 
diff --git a/core/java/android/app/VoiceInteractor.java b/core/java/android/app/VoiceInteractor.java
index 6dc48b0..f332c9d 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 evetually 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 actvitity 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 resonsponse 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/backup/BackupTransport.java b/core/java/android/app/backup/BackupTransport.java
new file mode 100644
index 0000000..da5cb10
--- /dev/null
+++ b/core/java/android/app/backup/BackupTransport.java
@@ -0,0 +1,334 @@
+/*
+ * 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.BackupConstants;
+import com.android.internal.backup.IBackupTransport;
+
+/**
+ * Concrete class that provides a stable-API bridge between IBackupTransport
+ * and its implementations.
+ *
+ * @hide
+ */
+public class BackupTransport {
+    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");
+    }
+
+    // ------------------------------------------------------------------------------------
+    // Key/value incremental backup support interfaces
+
+    /**
+     * Verify that this is a suitable time for a 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;
+    }
+
+    /**
+     * 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 BackupConstants#TRANSPORT_OK} (OK so far) or
+     *   {@link BackupConstants#TRANSPORT_ERROR} (on network error or other failure).
+     */
+    public int initializeDevice() {
+        return BackupConstants.TRANSPORT_ERROR;
+    }
+
+    /**
+     * 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 BackupConstants#TRANSPORT_OK} (OK so far),
+     *  {@link BackupConstants#TRANSPORT_ERROR} (on network error or other failure), or
+     *  {@link BackupConstants#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 BackupConstants.TRANSPORT_ERROR;
+    }
+
+    /**
+     * Erase the give 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 BackupConstants.TRANSPORT_ERROR;
+    }
+
+    /**
+     * Finish sending application data to the backup destination.  This must be
+     * called after {@link #performBackup} or {@link clearBackupData} to ensure that
+     * all data is sent.  Only when this method returns true can a backup be assumed
+     * to have succeeded.
+     *
+     * @return the same error codes as {@link #performBackup}.
+     */
+    public int finishBackup() {
+        return BackupConstants.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 BackupConstants#TRANSPORT_OK} (OK so far, call
+     *   {@link #nextRestorePackage}) or {@link BackupConstants#TRANSPORT_ERROR}
+     *   (an error occurred, the restore should be aborted and rescheduled).
+     */
+    public int startRestore(long token, PackageInfo[] packages) {
+        return BackupConstants.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 BackupConstants.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");
+    }
+
+    /**
+     * 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/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/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..d3a979c 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";
 
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/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/hardware/camera2/CameraCharacteristics.java b/core/java/android/hardware/camera2/CameraCharacteristics.java
index cea68d2..2f5b4fe 100644
--- a/core/java/android/hardware/camera2/CameraCharacteristics.java
+++ b/core/java/android/hardware/camera2/CameraCharacteristics.java
@@ -822,8 +822,8 @@
      * @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
diff --git a/core/java/android/hardware/camera2/CaptureRequest.java b/core/java/android/hardware/camera2/CaptureRequest.java
index 6aa24e6..d4dfdd5 100644
--- a/core/java/android/hardware/camera2/CaptureRequest.java
+++ b/core/java/android/hardware/camera2/CaptureRequest.java
@@ -691,8 +691,12 @@
      * 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,
@@ -763,8 +767,12 @@
      * 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,
@@ -846,8 +854,12 @@
      * 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,
diff --git a/core/java/android/hardware/camera2/CaptureResult.java b/core/java/android/hardware/camera2/CaptureResult.java
index 42020eb..7d07c92 100644
--- a/core/java/android/hardware/camera2/CaptureResult.java
+++ b/core/java/android/hardware/camera2/CaptureResult.java
@@ -537,8 +537,12 @@
      * 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,
@@ -807,8 +811,12 @@
      * 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,
@@ -1287,8 +1295,12 @@
      * 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,
@@ -1792,8 +1804,8 @@
      * <p>If variable focus not supported, can still report
      * fixed depth of field range</p>
      */
-    public static final Key<android.util.Range<Float>> LENS_FOCUS_RANGE =
-            new Key<android.util.Range<Float>>("android.lens.focusRange", new TypeReference<android.util.Range<Float>>() {{ }});
+    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)
diff --git a/core/java/android/hardware/camera2/impl/CameraMetadataNative.java b/core/java/android/hardware/camera2/impl/CameraMetadataNative.java
index dc0c652..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;
@@ -1006,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/MeteringRectangle.java b/core/java/android/hardware/camera2/params/MeteringRectangle.java
index a7a3b59..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,8 +84,8 @@
      * @param y coordinate >= 0
      * @param width width >= 0
      * @param height height >= 0
-     * @param meteringWeight weight >= 0
-     *
+     * @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) {
@@ -64,7 +93,8 @@
         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");
     }
 
     /**
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/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/SoftInputWindow.java b/core/java/android/inputmethodservice/SoftInputWindow.java
index 38a65c5..795117e 100644
--- a/core/java/android/inputmethodservice/SoftInputWindow.java
+++ b/core/java/android/inputmethodservice/SoftInputWindow.java
@@ -115,6 +115,10 @@
         getWindow().setAttributes(lp);
     }
 
+    public int getGravity() {
+        return getWindow().getAttributes().gravity;
+    }
+
     private void updateWidthHeight(WindowManager.LayoutParams lp) {
         if (lp.gravity == Gravity.TOP || lp.gravity == Gravity.BOTTOM) {
             lp.width = WindowManager.LayoutParams.MATCH_PARENT;
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/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 cd357b7..2e9077a 100644
--- a/core/java/android/service/voice/VoiceInteractionSession.java
+++ b/core/java/android/service/voice/VoiceInteractionSession.java
@@ -20,7 +20,6 @@
 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;
@@ -48,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;
@@ -81,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));
@@ -93,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));
@@ -144,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) {
             }
@@ -175,6 +231,7 @@
         public void sendCancelResult() {
             try {
                 if (DEBUG) Log.d(TAG, "sendCancelResult: req=" + mInterface);
+                finishRequest();
                 mCallback.deliverCancel(mInterface);
             } catch (RemoteException e) {
             }
@@ -192,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;
@@ -210,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
@@ -330,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;
         }
     }
@@ -426,6 +493,27 @@
         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()");
@@ -440,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()");
@@ -459,6 +556,12 @@
         }
     }
 
+    /**
+     * 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;
@@ -473,9 +576,15 @@
         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;
     }
@@ -508,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();
     }
@@ -531,15 +645,98 @@
         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/view/GLRenderer.java b/core/java/android/view/GLRenderer.java
index 6dd7c00..f1163e2 100644
--- a/core/java/android/view/GLRenderer.java
+++ b/core/java/android/view/GLRenderer.java
@@ -61,6 +61,7 @@
 
 import com.google.android.gles_jni.EGLImpl;
 
+import java.io.FileDescriptor;
 import java.io.PrintWriter;
 import java.util.ArrayList;
 import java.util.List;
@@ -106,7 +107,6 @@
 
     private static final String[] VISUALIZERS = {
             PROFILE_PROPERTY_VISUALIZE_BARS,
-            PROFILE_PROPERTY_VISUALIZE_LINES
     };
 
     private static final String[] OVERDRAW = {
@@ -178,7 +178,7 @@
     private static EGLSurface sPbuffer;
     private static final Object[] sPbufferLock = new Object[0];
 
-    private List<HardwareLayer> mAttachedLayers = new ArrayList<HardwareLayer>();
+    private List<HardwareLayer> mLayerUpdates = new ArrayList<HardwareLayer>();
 
     private static class GLRendererEglContext extends ManagedEGLContext {
         final Handler mHandler = new Handler();
@@ -471,7 +471,7 @@
 
     @Override
     void pushLayerUpdate(HardwareLayer layer) {
-        mGlCanvas.pushLayerUpdate(layer);
+        mLayerUpdates.add(layer);
     }
 
     @Override
@@ -494,11 +494,6 @@
         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());
@@ -509,11 +504,7 @@
         if (mGlCanvas != null) {
             mGlCanvas.cancelLayerUpdate(layer);
         }
-        if (hasContext()) {
-            long backingLayer = layer.detachBackingLayer();
-            nDestroyLayer(backingLayer);
-        }
-        mAttachedLayers.remove(layer);
+        mLayerUpdates.remove(layer);
     }
 
     @Override
@@ -674,7 +665,7 @@
             mProfilePaint = null;
 
             if (value) {
-                mDebugDataProvider = new DrawPerformanceDataProvider(graphType);
+                mDebugDataProvider = new GraphDataProvider(graphType);
             } else {
                 mDebugDataProvider = null;
             }
@@ -742,7 +733,7 @@
     }
 
     @Override
-    void dumpGfxInfo(PrintWriter pw) {
+    void dumpGfxInfo(PrintWriter pw, FileDescriptor fd) {
         if (mProfileEnabled) {
             pw.printf("\n\tDraw\tProcess\tExecute\n");
 
@@ -763,11 +754,6 @@
         }
     }
 
-    @Override
-    long getFrameCount() {
-        return mFrameCount;
-    }
-
     /**
      * Indicates whether this renderer instance can track and update dirty regions.
      */
@@ -1203,16 +1189,19 @@
 
     private void flushLayerChanges() {
         // Loop through and apply any pending layer changes
-        for (int i = 0; i < mAttachedLayers.size(); i++) {
-            HardwareLayer layer = mAttachedLayers.get(i);
+        for (int i = 0; i < mLayerUpdates.size(); i++) {
+            HardwareLayer layer = mLayerUpdates.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--;
+            } else if (layer.hasDisplayList()) {
+                mCanvas.pushLayerUpdate(layer);
             }
         }
+        mLayerUpdates.clear();
     }
 
     @Override
@@ -1446,7 +1435,18 @@
 
     private static native void nPrepareTree(long displayListPtr);
 
-    class DrawPerformanceDataProvider extends GraphDataProvider {
+    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;
+
         private final int mGraphType;
 
         private int mVerticalUnit;
@@ -1454,11 +1454,10 @@
         private int mHorizontalMargin;
         private int mThresholdStroke;
 
-        DrawPerformanceDataProvider(int graphType) {
+        public GraphDataProvider(int graphType) {
             mGraphType = graphType;
         }
 
-        @Override
         void prepare(DisplayMetrics metrics) {
             final float density = metrics.density;
 
@@ -1468,64 +1467,52 @@
             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/HardwareLayer.java b/core/java/android/view/HardwareLayer.java
index 4d78733..652bcd2 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,7 +167,7 @@
                 surface.detachFromGLContext();
                 // SurfaceTexture owns the texture name and detachFromGLContext
                 // should have deleted it
-                nOnTextureDestroyed(mFinalizer.mDeferredUpdater);
+                nOnTextureDestroyed(mFinalizer.get());
             }
         });
     }
@@ -200,24 +184,26 @@
             return;
         }
 
-        boolean success = nFlushChanges(mFinalizer.mDeferredUpdater);
+        boolean success = nFlushChanges(mFinalizer.get());
         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,8 +211,8 @@
      */
     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;
     }
 
@@ -258,15 +244,6 @@
     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 +258,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..d71de9f 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.
@@ -298,16 +288,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
@@ -341,12 +323,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.
      */
@@ -583,98 +559,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/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/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 9c9a939..11db996 100644
--- a/core/java/android/view/ThreadedRenderer.java
+++ b/core/java/android/view/ThreadedRenderer.java
@@ -22,12 +22,14 @@
 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;
 
 /**
@@ -60,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();
@@ -77,6 +84,8 @@
         // Setup timing
         mChoreographer = Choreographer.getInstance();
         nSetFrameInterval(mNativeProxy, mChoreographer.getFrameIntervalNanos());
+
+        loadSystemProperties();
     }
 
     @Override
@@ -166,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) {
@@ -210,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();
@@ -261,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
@@ -276,7 +304,7 @@
 
     @Override
     void onLayerDestroyed(HardwareLayer layer) {
-        nDestroyLayer(mNativeProxy, layer.getDeferredLayerUpdater());
+        nCancelLayerUpdate(mNativeProxy, layer.getDeferredLayerUpdater());
     }
 
     @Override
@@ -354,7 +382,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);
@@ -364,10 +393,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/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index c3bf295..ac25b57 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -5351,7 +5351,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/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/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/Toolbar.java b/core/java/android/widget/Toolbar.java
index 5033bee..f35ca27 100644
--- a/core/java/android/widget/Toolbar.java
+++ b/core/java/android/widget/Toolbar.java
@@ -557,6 +557,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
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/WindowDecorActionBar.java b/core/java/com/android/internal/app/WindowDecorActionBar.java
index 5c7a4e6..c0b5b97 100644
--- a/core/java/com/android/internal/app/WindowDecorActionBar.java
+++ b/core/java/com/android/internal/app/WindowDecorActionBar.java
@@ -588,7 +588,7 @@
             return;
         }
 
-        final FragmentTransaction trans = ((View) mDecorToolbar).isInEditMode() ? null :
+        final FragmentTransaction trans = mDecorToolbar.getViewGroup().isInEditMode() ? null :
                 mActivity.getFragmentManager().beginTransaction().disallowAddToBackStack();
 
         if (mSelectedTab == tab) {
@@ -847,7 +847,7 @@
         mDecorToolbar.animateToVisibility(toActionMode ? View.GONE : View.VISIBLE);
         mContextView.animateToVisibility(toActionMode ? View.VISIBLE : View.GONE);
         if (mTabScrollView != null && !mDecorToolbar.hasEmbeddedTabs() &&
-                isCollapsed((View) mDecorToolbar)) {
+                isCollapsed(mDecorToolbar.getViewGroup())) {
             mTabScrollView.animateToVisibility(toActionMode ? View.GONE : View.VISIBLE);
         }
     }
@@ -959,7 +959,7 @@
 
             // Clear out the context mode views after the animation finishes
             mContextView.closeMode();
-            ((View) mDecorToolbar).sendAccessibilityEvent(
+            mDecorToolbar.getViewGroup().sendAccessibilityEvent(
                     AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED);
             mOverlayLayout.setHideOnContentScrollEnabled(mHideOnContentScroll);
 
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..f2b29ef6 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;
 
@@ -217,7 +218,7 @@
 
     // 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;
 
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/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/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/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/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
index f90aaea..b8dc307 100644
--- a/core/java/com/android/internal/widget/ToolbarWidgetWrapper.java
+++ b/core/java/com/android/internal/widget/ToolbarWidgetWrapper.java
@@ -132,6 +132,16 @@
             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() {
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/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_view_GLES20Canvas.cpp b/core/jni/android_view_GLES20Canvas.cpp
index c5dd06f..9141d44 100644
--- a/core/jni/android_view_GLES20Canvas.cpp
+++ b/core/jni/android_view_GLES20Canvas.cpp
@@ -373,7 +373,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 +387,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 +430,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,
diff --git a/core/jni/android_view_HardwareLayer.cpp b/core/jni/android_view_HardwareLayer.cpp
index b2f17de..33a2705 100644
--- a/core/jni/android_view_HardwareLayer.cpp
+++ b/core/jni/android_view_HardwareLayer.cpp
@@ -55,9 +55,7 @@
     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) );
+    return reinterpret_cast<jlong>( new DeferredLayerUpdater(layer) );
 }
 
 static void android_view_HardwareLayer_onTextureDestroyed(JNIEnv* env, jobject clazz,
@@ -66,18 +64,6 @@
     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);
@@ -157,8 +143,6 @@
     { "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_ThreadedRenderer.cpp b/core/jni/android_view_ThreadedRenderer.cpp
index 6f256f0..1397131 100644
--- a/core/jni/android_view_ThreadedRenderer.cpp
+++ b/core/jni/android_view_ThreadedRenderer.cpp
@@ -238,10 +238,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 +287,18 @@
     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,
@@ -311,6 +319,13 @@
     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
 
 // ----------------------------------------------------------------------------
@@ -332,17 +347,19 @@
     { "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
 };
 
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/public.xml b/core/res/res/values/public.xml
index 9c33d80..5cdbcfa 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -2256,6 +2256,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 +2273,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/styles.xml b/core/res/res/values/styles.xml
index 63f2674..59bd667 100644
--- a/core/res/res/values/styles.xml
+++ b/core/res/res/values/styles.xml
@@ -169,6 +169,12 @@
         <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/voice_layer_enter</item>
diff --git a/core/res/res/values/styles_quantum.xml b/core/res/res/values/styles_quantum.xml
index ea32681..6943533 100644
--- a/core/res/res/values/styles_quantum.xml
+++ b/core/res/res/values/styles_quantum.xml
@@ -783,7 +783,6 @@
         <item name="itemPadding">8dip</item>
         <item name="homeLayout">@layout/action_bar_home_quantum</item>
         <item name="gravity">center_vertical</item>
-        <item name="contentInsetStart">56dp</item>
     </style>
 
     <style name="Widget.Quantum.ActionBar.Solid">
diff --git a/core/res/res/values/themes_quantum.xml b/core/res/res/values/themes_quantum.xml
index 484c694..47ba764 100644
--- a/core/res/res/values/themes_quantum.xml
+++ b/core/res/res/values/themes_quantum.xml
@@ -913,6 +913,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
@@ -929,7 +945,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. -->
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/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/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/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/libs/hwui/Android.mk b/libs/hwui/Android.mk
index 2cadf09..442f327 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 \
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..02f686f 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 {
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 160fbea..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;
diff --git a/libs/hwui/renderthread/CanvasContext.h b/libs/hwui/renderthread/CanvasContext.h
index da85d44..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();
 
@@ -77,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();
@@ -100,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 8e772f2..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,11 +284,12 @@
     return (bool) postAndWait(task);
 }
 
-void RenderProxy::destroyLayer(DeferredLayerUpdater* layer) {
-    mDrawFrameTask.removeLayer(layer);
-    SETUP_TASK(destroyLayer);
-    args->layer = layer->detachBackingLayer();
-    post(task);
+void RenderProxy::pushLayerUpdate(DeferredLayerUpdater* layer) {
+    mDrawFrameTask.pushLayerUpdate(layer);
+}
+
+void RenderProxy::cancelLayerUpdate(DeferredLayerUpdater* layer) {
+    mDrawFrameTask.removeLayerUpdate(layer);
 }
 
 CREATE_BRIDGE2(flushCaches, CanvasContext* context, Caches::FlushMode flushMode) {
@@ -315,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 22d4e22..944ff9c 100644
--- a/libs/hwui/renderthread/RenderProxy.h
+++ b/libs/hwui/renderthread/RenderProxy.h
@@ -69,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);
@@ -80,13 +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/AudioService.java b/media/java/android/media/AudioService.java
index 98a7c65..abba06fd 100644
--- a/media/java/android/media/AudioService.java
+++ b/media/java/android/media/AudioService.java
@@ -4141,8 +4141,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) ||
@@ -4159,7 +4160,7 @@
                             MUSIC_ACTIVE_POLL_PERIOD_MS);
                 }
             }
-            if (!isUsb) {
+            if (!isUsb && (device != AudioSystem.DEVICE_IN_WIRED_HEADSET)) {
                 sendDeviceConnectionIntent(device, state, name);
             }
         }
@@ -4175,7 +4176,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)) {
@@ -4210,7 +4212,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);
@@ -4224,10 +4227,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;
                     }
                 }
@@ -4237,7 +4240,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;
@@ -4257,8 +4262,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);
 
@@ -4273,14 +4278,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/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/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/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..90ccf68 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,12 +124,6 @@
      */
     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;
-
     private static final String KEY_COMMAND = "command";
     private static final String KEY_EXTRAS = "extras";
     private static final String KEY_CALLBACK = "callback";
@@ -139,12 +134,14 @@
     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 boolean mActive = false;;
@@ -162,11 +159,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 +191,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 +209,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 +233,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,21 +241,21 @@
      *
      * @param stream The {@link AudioManager} stream this session is playing on.
      */
-    public void useLocalPlayback(int stream) {
+    public void setPlaybackToLocal(int stream) {
         // TODO
     }
 
     /**
      * 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!");
         }
@@ -312,7 +299,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 +419,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 +585,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 +598,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 +606,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 +614,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 +623,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 +632,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 +660,7 @@
          * @param mediaButtonIntent an intent containing the KeyEvent as an
          *            extra
          */
-        public void onMediaButton(Intent mediaButtonIntent) {
+        public void onMediaButtonEvent(Intent mediaButtonIntent) {
         }
 
         /**
@@ -536,7 +671,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 +717,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 +854,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 +862,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 +870,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 +878,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 +886,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 +894,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 +902,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 +910,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 +918,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);
             }
         }
 
@@ -760,10 +944,16 @@
 
     }
 
-    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 +966,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 +1005,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..c303e77 100644
--- a/media/java/android/media/session/MediaSessionLegacyHelper.java
+++ b/media/java/android/media/session/MediaSessionLegacyHelper.java
@@ -76,13 +76,13 @@
         }
     }
 
-    public void addRccListener(PendingIntent pi, TransportPerformer.Listener listener) {
+    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 +92,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 +110,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 +141,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 +156,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 +201,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 +226,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 +272,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..8d5e338 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());
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/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 afc2931..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;
@@ -790,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",
diff --git a/packages/Keyguard/res/values/dimens.xml b/packages/Keyguard/res/values/dimens.xml
index 01d9ab3..e9cdfcd 100644
--- a/packages/Keyguard/res/values/dimens.xml
+++ b/packages/Keyguard/res/values/dimens.xml
@@ -162,5 +162,5 @@
     <dimen name="big_font_size">120dp</dimen>
 
     <!-- The y translation to apply at the start in appear animations. -->
-    <dimen name="appear_y_translation_start">24dp</dimen>
+    <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
index ea896d5..6bb1f2c 100644
--- a/packages/Keyguard/src/com/android/keyguard/AppearAnimationUtils.java
+++ b/packages/Keyguard/src/com/android/keyguard/AppearAnimationUtils.java
@@ -16,84 +16,124 @@
 
 package com.android.keyguard;
 
-import android.animation.Animator;
-import android.animation.AnimatorListenerAdapter;
-import android.animation.TimeInterpolator;
 import android.content.Context;
 import android.view.View;
-import android.view.ViewPropertyAnimator;
 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 {
+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) {
-        mLinearOutSlowIn = AnimationUtils.loadInterpolator(
-                ctx, android.R.interpolator.linear_out_slow_in);
-        mStartTranslation =
-                ctx.getResources().getDimensionPixelOffset(R.dimen.appear_y_translation_start);
+        this(ctx, 1.0f, 1.0f);
     }
 
-    public void startAppearAnimation(View[][] views, final Runnable finishListener) {
+    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;
-        ViewPropertyAnimator maxDelayAnimator = null;
-        for (int row = 0; row < views.length; row++) {
-            View[] columns = views[row];
+        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);
-                ViewPropertyAnimator animator = startAppearAnimation(columns[col], delay);
-                if (animator != null && delay > maxDelay) {
+                mProperties.delays[row][col] = delay;
+                if (items[row][col] != null && delay > maxDelay) {
                     maxDelay = delay;
-                    maxDelayAnimator = animator;
+                    mProperties.maxDelayColIndex = col;
+                    mProperties.maxDelayRowIndex = row;
                 }
             }
         }
-        if (maxDelayAnimator != null) {
-            maxDelayAnimator.setListener(new AnimatorListenerAdapter() {
-                @Override
-                public void onAnimationEnd(Animator animation) {
-                    finishListener.run();
-                }
-            });
-        } else {
-            finishListener.run();
-        }
-    }
-
-    private ViewPropertyAnimator startAppearAnimation(View view, long delay) {
-        if (view == null) return null;
-        view.setAlpha(0f);
-        view.setTranslationY(mStartTranslation);
-        view.animate()
-                .alpha(1f)
-                .translationY(0)
-                .setInterpolator(mLinearOutSlowIn)
-                .setDuration(APPEAR_DURATION)
-                .setStartDelay(delay)
-                .setListener(null);
-        if (view.hasOverlappingRendering()) {
-            view.animate().withLayer();
-        }
-        return view.animate();
+        return mProperties;
     }
 
     private long calculateDelay(int row, int col) {
-        return (long) (row * 40 + col * (Math.pow(row, 0.4) + 0.4) * 20);
+        return (long) ((row * 40 + col * (Math.pow(row, 0.4) + 0.4) * 20) * mDelayScale);
     }
 
-    public TimeInterpolator getInterpolator() {
+    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/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/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/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_zen_off.xml b/packages/SystemUI/res/drawable/ic_qs_zen_off.xml
index 73886ec..88a2c3a 100644
--- a/packages/SystemUI/res/drawable/ic_qs_zen_off.xml
+++ b/packages/SystemUI/res/drawable/ic_qs_zen_off.xml
@@ -23,8 +23,6 @@
         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" />
+        android:fill="#4DFFFFFF"
+        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..0e933cf 100644
--- a/packages/SystemUI/res/drawable/ic_qs_zen_on.xml
+++ b/packages/SystemUI/res/drawable/ic_qs_zen_on.xml
@@ -24,5 +24,5 @@
 
     <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="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_vol_zen_off.xml b/packages/SystemUI/res/drawable/ic_vol_zen_off.xml
index ea5ab70..51be1a8 100644
--- a/packages/SystemUI/res/drawable/ic_vol_zen_off.xml
+++ b/packages/SystemUI/res/drawable/ic_vol_zen_off.xml
@@ -23,8 +23,6 @@
         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" />
+        android:fill="#4DFFFFFF"
+        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_vol_zen_on.xml b/packages/SystemUI/res/drawable/ic_vol_zen_on.xml
index 44024f3..c8c217d 100644
--- a/packages/SystemUI/res/drawable/ic_vol_zen_on.xml
+++ b/packages/SystemUI/res/drawable/ic_vol_zen_on.xml
@@ -24,5 +24,5 @@
 
     <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="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/stat_sys_ringer_zen.xml b/packages/SystemUI/res/drawable/stat_sys_ringer_zen.xml
index afab88f..8b104d17 100644
--- a/packages/SystemUI/res/drawable/stat_sys_ringer_zen.xml
+++ b/packages/SystemUI/res/drawable/stat_sys_ringer_zen.xml
@@ -24,5 +24,5 @@
 
     <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="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/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/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 0936cc2..0a8f852 100644
--- a/packages/SystemUI/res/layout/zen_mode_panel.xml
+++ b/packages/SystemUI/res/layout/zen_mode_panel.xml
@@ -21,9 +21,7 @@
     android:layout_height="wrap_content"
     android:background="@color/system_primary_color"
     android:orientation="vertical"
-    android:paddingTop="@dimen/qs_panel_padding"
-    android:paddingLeft="@dimen/qs_panel_padding"
-    android:paddingRight="@dimen/qs_panel_padding" >
+    android:paddingTop="@dimen/qs_panel_padding" >
 
     <TextView
         android:id="@android:id/title"
@@ -31,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"
@@ -46,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-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-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/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 610b376..3646ade 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>
@@ -302,4 +314,7 @@
 
     <!-- Volume panel dialog width -->
     <dimen name="volume_panel_width">300dp</dimen>
+
+    <!-- Volume panel z depth -->
+    <dimen name="volume_panel_z">3dp</dimen>
 </resources>
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index ef3956e..59f1efd 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -522,7 +522,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/qs/QSPanel.java b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
index 626fc0d..c8cf05d 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();
             }
         }
@@ -156,6 +165,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);
@@ -195,7 +205,7 @@
         mDetail.measure(exactly(width), unspecified());
         if (mDetail.getVisibility() == VISIBLE && mDetail.getChildCount() > 0) {
             final int dmh = mDetail.getMeasuredHeight();
-            if (dmh > 0) h = dmh;
+            if (dmh > 0) h = Math.max(h, dmh);
         }
         setMeasuredDimension(width, h);
     }
@@ -222,7 +232,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) {
@@ -239,6 +250,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;
@@ -265,4 +282,8 @@
             mDetailRecord = null;
         };
     };
+
+    public interface Callback {
+        void onShowingDetail(boolean showingDetail);
+    }
 }
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/BaseStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
index 21b41c7..06cc476 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
@@ -83,6 +83,7 @@
 import com.android.systemui.statusbar.stack.NotificationStackScrollLayout;
 
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.Collections;
 import java.util.Locale;
 
@@ -117,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();
@@ -228,7 +232,7 @@
             }
             final boolean isActivity = pendingIntent.isActivity();
             if (isActivity) {
-                startNotificationActivity(new OnDismissAction() {
+                dismissKeyguardThenExecute(new OnDismissAction() {
                     @Override
                     public boolean onDismiss() {
                         try {
@@ -250,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;
@@ -332,8 +337,7 @@
             mHandler.post(new Runnable() {
                 @Override
                 public void run() {
-                    mNotificationData.updateRanking(currentRanking);
-                    updateNotifications();
+                    updateRankingInternal(currentRanking);
                 }
             });
         }
@@ -479,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();
     }
 
@@ -1049,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
@@ -1069,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;
@@ -1094,7 +1098,7 @@
                     visibilityChanged(false);
 
                     boolean waitForActivityLaunch = sent && mIntent.isActivity();
-                    return waitForActivityLaunch;
+                    return waitForActivityLaunch && DELAY_DISMISS_TO_ACTIVITY_LAUNCH;
                 }
             });
         }
@@ -1275,6 +1279,8 @@
     public abstract void addNotificationInternal(StatusBarNotification notification,
             Ranking ranking);
 
+    protected abstract void updateRankingInternal(Ranking ranking);
+
     @Override
     public void removeNotification(String key) {
         if (!USE_NOTIFICATION_LISTENER) {
@@ -1282,7 +1288,7 @@
         }
     }
 
-    protected abstract void removeNotificationInternal(String key, Ranking ranking);
+    public abstract void removeNotificationInternal(String key, Ranking ranking);
 
     public void updateNotification(StatusBarNotification notification) {
         if (!USE_NOTIFICATION_LISTENER) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/InterceptedNotifications.java b/packages/SystemUI/src/com/android/systemui/statusbar/InterceptedNotifications.java
index 24da5c2..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,12 +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;
 
@@ -48,25 +51,45 @@
         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);
+            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) {
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 802e5e5..c8f185c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
@@ -47,6 +47,8 @@
         ExpandableView.OnHeightChangedListener, ObservableScrollView.Listener,
         View.OnClickListener, KeyguardPageSwipeHelper.Callback {
 
+    private static float EXPANSION_RUBBER_BAND_EXTRA_FACTOR = 0.4f;
+
     private KeyguardPageSwipeHelper mPageSwiper;
     PhoneStatusBar mStatusBar;
     private StatusBarHeaderView mHeader;
@@ -70,6 +72,7 @@
      */
     private boolean mIntercepting;
     private boolean mQsExpanded;
+    private boolean mQsFullyExpanded;
     private boolean mKeyguardShowing;
     private float mInitialHeightOnTouch;
     private float mInitialTouchX;
@@ -165,7 +168,9 @@
         mQsMinExpansionHeight = mHeader.getCollapsedHeight() + mQsPeekHeight;
         mQsMaxExpansionHeight = mHeader.getExpandedHeight() + mQsContainer.getHeight();
         if (mQsExpanded) {
-            setQsStackScrollerPadding(mQsMaxExpansionHeight);
+            if (mQsFullyExpanded) {
+                setQsStackScrollerPadding(mQsMaxExpansionHeight);
+            }
         } else {
             setQsExpansion(mQsMinExpansionHeight);
             positionClockAndNotifications();
@@ -522,6 +527,7 @@
 
     private void setQsExpansion(float height) {
         height = Math.min(Math.max(height, mQsMinExpansionHeight), mQsMaxExpansionHeight);
+        mQsFullyExpanded = height == mQsMaxExpansionHeight;
         if (height > mQsMinExpansionHeight && !mQsExpanded) {
             setQsExpanded(true);
         } else if (height <= mQsMinExpansionHeight && mQsExpanded) {
@@ -701,8 +707,11 @@
     @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);
     }
 
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 0e5b7e1..f9afcf3 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
@@ -1050,7 +1050,7 @@
         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;
@@ -1114,6 +1114,13 @@
     }
 
     @Override
+    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);
@@ -2348,16 +2355,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() {
@@ -2418,7 +2433,7 @@
     };
 
     @Override
-    protected void startNotificationActivity(OnDismissAction action) {
+    protected void dismissKeyguardThenExecute(OnDismissAction action) {
         if (mStatusBarKeyguardViewManager.isShowing()) {
             mStatusBarKeyguardViewManager.dismissWithAction(action);
         } else {
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..c097e2d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarHeaderView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarHeaderView.java
@@ -293,5 +293,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/tv/TvStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/tv/TvStatusBar.java
index c2bd1cb..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,6 +17,7 @@
 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;
@@ -54,11 +55,15 @@
     }
 
     @Override
+    protected void updateRankingInternal(Ranking ranking) {
+    }
+
+    @Override
     public void updateNotification(StatusBarNotification notification) {
     }
 
     @Override
-    protected void removeNotificationInternal(String key, Ranking ranking) {
+    public void removeNotificationInternal(String key, Ranking ranking) {
     }
 
     @Override
diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumePanel.java b/packages/SystemUI/src/com/android/systemui/volume/VolumePanel.java
index 67f3a3d..cbfc641 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/VolumePanel.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/VolumePanel.java
@@ -25,12 +25,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;
@@ -44,6 +47,7 @@
 import android.view.Window;
 import android.view.WindowManager;
 import android.view.WindowManager.LayoutParams;
+import android.widget.FrameLayout;
 import android.widget.ImageView;
 import android.widget.SeekBar;
 import android.widget.SeekBar.OnSeekBarChangeListener;
@@ -76,6 +80,7 @@
     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 int MSG_VOLUME_CHANGED = 0;
     private static final int MSG_FREE_RESOURCES = 1;
@@ -103,6 +108,7 @@
     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;
@@ -301,17 +307,26 @@
             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);
+            // temporary workaround for no window shadows
+            final FrameLayout layout = new FrameLayout(mContext);
+            final int z = res.getDimensionPixelSize(com.android.systemui.R.dimen.volume_panel_z);
+            layout.setPadding(z, z, z, z);
+            final View v = LayoutInflater.from(mContext).inflate(layoutId, layout, false);
+            v.setBackgroundResource(com.android.systemui.R.drawable.qs_panel_background);
+            v.setElevation(z);
+            layout.addView(v);
+            mDialog.setContentView(layout);
             mDialog.setOnDismissListener(new OnDismissListener() {
                 @Override
                 public void onDismiss(DialogInterface dialog) {
@@ -321,6 +336,7 @@
             });
 
             mDialog.create();
+            mDialog.getWindow().setBackgroundDrawable(new ColorDrawable(0x00000000));
 
             mView = window.findViewById(R.id.content);
             mView.setOnTouchListener(new View.OnTouchListener() {
@@ -513,6 +529,7 @@
 
                 @Override
                 public void onInteraction() {
+                    resetTimeout();
                     if (mZenPanelCallback != null) {
                         mZenPanelCallback.onInteraction();
                     }
@@ -521,6 +538,8 @@
         }
         mZenPanel.setVisibility(View.VISIBLE);
         mZenPanelDivider.setVisibility(View.VISIBLE);
+        mTimeoutDelay = TIMEOUT_DELAY_EXPANDED;
+        resetTimeout();
     }
 
     private void collapse() {
@@ -529,6 +548,8 @@
             mZenPanel.setVisibility(View.GONE);
         }
         mZenPanelDivider.setVisibility(View.GONE);
+        mTimeoutDelay = TIMEOUT_DELAY;
+        resetTimeout();
     }
 
     public void updateStates() {
@@ -1082,7 +1103,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 +1155,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 {
diff --git a/policy/src/com/android/internal/policy/impl/GlobalActions.java b/policy/src/com/android/internal/policy/impl/GlobalActions.java
index 673ce0b..762d3df 100644
--- a/policy/src/com/android/internal/policy/impl/GlobalActions.java
+++ b/policy/src/com/android/internal/policy/impl/GlobalActions.java
@@ -185,7 +185,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 +263,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 +301,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,29 +315,33 @@
         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() {
@@ -367,10 +376,6 @@
                 dialog.show();
             }
 
-            public boolean onLongPress() {
-                return false;
-            }
-
             public boolean showDuringKeyguard() {
                 return true;
             }
@@ -393,11 +398,6 @@
             }
 
             @Override
-            public boolean onLongPress() {
-                return false;
-            }
-
-            @Override
             public boolean showDuringKeyguard() {
                 return true;
             }
@@ -583,8 +583,6 @@
 
         void onPress();
 
-        public boolean onLongPress();
-
         /**
          * @return whether this action should appear in the dialog when the keygaurd
          *    is showing.
@@ -601,6 +599,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.
      */
@@ -637,10 +642,6 @@
 
         abstract public void onPress();
 
-        public boolean onLongPress() {
-            return false;
-        }
-
         public View create(
                 Context context, View convertView, ViewGroup parent, LayoutInflater inflater) {
             View v = inflater.inflate(R.layout.global_actions_item, parent, false);
@@ -769,10 +770,6 @@
             changeStateFromPress(nowOn);
         }
 
-        public boolean onLongPress() {
-            return false;
-        }
-
         public boolean isEnabled() {
             return !mState.inTransition();
         }
@@ -862,10 +859,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 6b0095a..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);
diff --git a/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java b/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java
index 1a7c0a8..6bd854a 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;
@@ -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);
@@ -3348,7 +3393,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;
@@ -4406,6 +4456,7 @@
             mKeyguardDelegate.onScreenTurnedOff(why);
         }
         synchronized (mLock) {
+            updateWakeGestureListenerLp();
             updateOrientationListenerLp();
             updateLockScreenTimeout();
         }
@@ -4422,6 +4473,7 @@
 
         synchronized (mLock) {
             mScreenOnEarly = true;
+            updateWakeGestureListenerLp();
             updateOrientationListenerLp();
             updateLockScreenTimeout();
         }
@@ -5022,6 +5074,10 @@
                     PowerManager.GO_TO_SLEEP_REASON_USER,
                     PowerManager.GO_TO_SLEEP_FLAG_NO_DOZE);
         }
+
+        synchronized (mLock) {
+            updateWakeGestureListenerLp();
+        }
     }
 
     void updateRotation(boolean alwaysSendConfiguration) {
@@ -5482,6 +5538,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);
@@ -5624,10 +5683,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/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java
index 5527528..d7a19ad 100644
--- a/services/core/java/com/android/server/ConnectivityService.java
+++ b/services/core/java/com/android/server/ConnectivityService.java
@@ -514,6 +514,8 @@
     // sequence number of NetworkRequests
     private int mNextNetworkRequestId = 1;
 
+    private static final int UID_UNUSED = -1;
+
     public ConnectivityService(Context context, INetworkManagementService netd,
             INetworkStatsService statsService, INetworkPolicyManager policyManager) {
         // Currently, omitting a NetworkFactory will create one internally
@@ -1673,10 +1675,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 +1690,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 +1713,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 +1753,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 +1764,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 +1778,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 +1798,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 +1820,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);
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 eebb503..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) {
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index fd31b41..61ba6e0 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -2834,7 +2834,7 @@
             return app;
         }
 
-        startProcessLocked(app, hostingType, hostingNameStr);
+        startProcessLocked(app, hostingType, hostingNameStr, null /* ABI override */);
         return (app.pid != 0) ? app : null;
     }
 
@@ -2843,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);
@@ -2928,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];
             }
@@ -5020,7 +5020,7 @@
 
             if (app.persistent && !app.isolated) {
                 if (!callerWillRestart) {
-                    addAppLocked(app.info, false);
+                    addAppLocked(app.info, false, null /* ABI override */);
                 } else {
                     needRestart = true;
                 }
@@ -5133,7 +5133,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;
         }
 
@@ -5226,7 +5226,7 @@
 
             app.resetPackageList(mProcessStats);
             app.unlinkDeathRecipient();
-            startProcessLocked(app, "bind fail", processName);
+            startProcessLocked(app, "bind fail", processName, null /* ABI override */);
             return false;
         }
 
@@ -5377,7 +5377,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 */);
                 }
             }
             
@@ -8566,7 +8566,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);
@@ -8601,7 +8602,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;
@@ -9818,7 +9820,7 @@
                                 = (ApplicationInfo)apps.get(i);
                             if (info != null &&
                                     !info.packageName.equals("android")) {
-                                addAppLocked(info, false);
+                                addAppLocked(info, false, null /* ABI override */);
                             }
                         }
                     }
@@ -12998,7 +13000,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;
@@ -14312,7 +14314,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);
@@ -14361,7 +14363,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;
@@ -16325,7 +16327,7 @@
                     
                     if (app.persistent) {
                         if (app.persistent) {
-                            addAppLocked(app.info, false);
+                            addAppLocked(app.info, false, null /* ABI override */);
                         }
                     }
                 }
diff --git a/services/core/java/com/android/server/am/ActivityStackSupervisor.java b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
index 598e524..dc4ad48 100644
--- a/services/core/java/com/android/server/am/ActivityStackSupervisor.java
+++ b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
@@ -214,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
@@ -293,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();
         }
     }
 
@@ -1400,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()) {
@@ -3479,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/hdmi/DeviceDiscoveryAction.java b/services/core/java/com/android/server/hdmi/DeviceDiscoveryAction.java
index d46cc7b..579f89f 100644
--- a/services/core/java/com/android/server/hdmi/DeviceDiscoveryAction.java
+++ b/services/core/java/com/android/server/hdmi/DeviceDiscoveryAction.java
@@ -115,12 +115,12 @@
             @Override
             public void onPollingFinished(List<Integer> ackedAddress) {
                 if (ackedAddress.isEmpty()) {
-                    Slog.i(TAG, "No device is detected.");
+                    Slog.v(TAG, "No device is detected.");
                     finish();
                     return;
                 }
 
-                Slog.i(TAG, "Device detected: " + ackedAddress);
+                Slog.v(TAG, "Device detected: " + ackedAddress);
                 allocateDevices(ackedAddress);
                 startPhysicalAddressStage();
             }
@@ -136,6 +136,7 @@
     }
 
     private void startPhysicalAddressStage() {
+        Slog.v(TAG, "Start [Physical Address Stage]:" + mDevices.size());
         mProcessedDeviceCount = 0;
         mState = STATE_WAITING_FOR_PHYSICAL_ADDRESS;
 
@@ -158,6 +159,7 @@
     }
 
     private void startOsdNameStage() {
+        Slog.v(TAG, "Start [Osd Name Stage]:" + mDevices.size());
         mProcessedDeviceCount = 0;
         mState = STATE_WAITING_FOR_OSD_NAME;
 
@@ -176,6 +178,8 @@
     }
 
     private void startVendorIdStage() {
+        Slog.v(TAG, "Start [Vendor Id Stage]:" + mDevices.size());
+
         mProcessedDeviceCount = 0;
         mState = STATE_WAITING_FOR_VENDOR_ID;
 
@@ -301,11 +305,14 @@
     }
 
     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();
     }
@@ -355,6 +362,7 @@
             return;
         }
 
+        Slog.v(TAG, "Timeout[State=" + mState + ", Processed=" + mProcessedDeviceCount);
         removeDevice(mProcessedDeviceCount);
         checkAndProceedStage();
     }
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecController.java b/services/core/java/com/android/server/hdmi/HdmiCecController.java
index e0a01f0..d0b716d 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecController.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecController.java
@@ -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;
             }
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/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/HdmiControlService.java b/services/core/java/com/android/server/hdmi/HdmiControlService.java
index 072b97f..d775733 100644
--- a/services/core/java/com/android/server/hdmi/HdmiControlService.java
+++ b/services/core/java/com/android/server/hdmi/HdmiControlService.java
@@ -30,10 +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;
@@ -130,7 +132,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.");
         }
@@ -140,8 +157,7 @@
             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());
     }
 
     /**
@@ -287,6 +303,9 @@
             case HdmiCec.MESSAGE_TERMINATE_ARC:
                 handleTerminateArc(message);
                 return true;
+            case HdmiCec.MESSAGE_REPORT_PHYSICAL_ADDRESS:
+                handleReportPhysicalAddress(message);
+                return true;
             // TODO: Add remaining system information query such as
             // <Give Device Power Status> and <Request Active Source> handler.
             default:
@@ -297,7 +316,7 @@
     /**
      * 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 +334,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.
@@ -428,12 +463,12 @@
     // 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.
-    void launchDeviceDiscovery() {
+    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, HdmiCec.ADDR_TV,
+        DeviceDiscoveryAction action = new DeviceDiscoveryAction(this, sourceAddress,
                 new DeviceDiscoveryCallback() {
                     @Override
                     public void onDeviceDiscoveryDone(List<HdmiCecDeviceInfo> deviceInfos) {
diff --git a/services/core/java/com/android/server/media/MediaSessionRecord.java b/services/core/java/com/android/server/media/MediaSessionRecord.java
index 030e3ed..c909a54 100644
--- a/services/core/java/com/android/server/media/MediaSessionRecord.java
+++ b/services/core/java/com/android/server/media/MediaSessionRecord.java
@@ -66,13 +66,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
@@ -301,7 +301,7 @@
         if (isActiveState(state)) {
             return true;
         }
-        if (state == mPlaybackState.PLAYSTATE_PAUSED) {
+        if (state == mPlaybackState.STATE_PAUSED) {
             long inactiveTime = SystemClock.uptimeMillis() - mLastActiveTime;
             if (inactiveTime < ACTIVE_BUFFER) {
                 return true;
@@ -509,12 +509,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 +522,7 @@
                         position = 0;
                     }
                     result = new PlaybackState(state);
-                    result.setState(state.getState(), position, state.getRate());
+                    result.setState(state.getState(), position, state.getPlaybackRate());
                 }
             }
         }
@@ -588,7 +588,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;
@@ -649,14 +649,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) {
@@ -788,8 +790,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/MediaSessionStack.java b/services/core/java/com/android/server/media/MediaSessionStack.java
index 7ba9212..56236f8 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>();
 
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 a3858d0..4ac2dcc 100644
--- a/services/core/java/com/android/server/notification/ValidateNotificationPeople.java
+++ b/services/core/java/com/android/server/notification/ValidateNotificationPeople.java
@@ -28,8 +28,6 @@
 import android.util.LruCache;
 import android.util.Slog;
 
-import com.android.server.notification.NotificationManagerService.NotificationRecord;
-
 import java.util.ArrayList;
 import java.util.LinkedList;
 
@@ -51,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;
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/Settings.java b/services/core/java/com/android/server/pm/Settings.java
index 69aeae6..3483fae 100644
--- a/services/core/java/com/android/server/pm/Settings.java
+++ b/services/core/java/com/android/server/pm/Settings.java
@@ -2940,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/wm/AppTransition.java b/services/core/java/com/android/server/wm/AppTransition.java
index 4f8b9d7..e007600 100644
--- a/services/core/java/com/android/server/wm/AppTransition.java
+++ b/services/core/java/com/android/server/wm/AppTransition.java
@@ -115,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;
diff --git a/services/core/jni/com_android_server_hdmi_HdmiCecController.cpp b/services/core/jni/com_android_server_hdmi_HdmiCecController.cpp
index c6de676..a734026 100644
--- a/services/core/jni/com_android_server_hdmi_HdmiCecController.cpp
+++ b/services/core/jni/com_android_server_hdmi_HdmiCecController.cpp
@@ -240,13 +240,6 @@
         var = env->GetMethodID(clazz, methodName, methodDescriptor); \
         LOG_FATAL_IF(! var, "Unable to find method " methodName);
 
-// TODO: replace above code with following once
-// replace old HdmiCecService with HdmiControlService
-#undef HDMI_CEC_HARDWARE_MODULE_ID
-#define HDMI_CEC_HARDWARE_MODULE_ID "hdmi_cec_module"
-#undef HDMI_CEC_HARDWARE_INTERFACE
-#define HDMI_CEC_HARDWARE_INTERFACE "hdmi_cec_module_hw_if"
-
 static jlong nativeInit(JNIEnv* env, jclass clazz, jobject callbacksObj,
         jobject messageQueueObj) {
     int err;
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index 0f24ff6..c0f60c7 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -330,7 +330,6 @@
         IPackageManager pm = null;
         WindowManagerService wm = null;
         BluetoothManagerService bluetooth = null;
-        DockObserver dock = null;
         UsbService usb = null;
         SerialService serial = null;
         RecognitionManagerService recognition = null;
@@ -789,13 +788,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) {
@@ -1085,7 +1078,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 +1151,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/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/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/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/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/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..c1fa74f 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 TransportListener());
+        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 TransportListener 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/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 2d08163..e1a5854 100644
--- a/tests/VoiceInteraction/AndroidManifest.xml
+++ b/tests/VoiceInteraction/AndroidManifest.xml
@@ -23,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 563fa44..142d781 100644
--- a/tests/VoiceInteraction/res/layout/voice_interaction_session.xml
+++ b/tests/VoiceInteraction/res/layout/voice_interaction_session.xml
@@ -14,26 +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"
-    >
+    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/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/wifi/java/android/net/wifi/ScanResult.java b/wifi/java/android/net/wifi/ScanResult.java
index 58b0d61..f6d7f55 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) {
@@ -199,6 +212,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 +246,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 +264,4 @@
                 return new ScanResult[size];
             }
         };
-
 }
diff --git a/wifi/java/android/net/wifi/WifiConfiguration.java b/wifi/java/android/net/wifi/WifiConfiguration.java
index 4f223b0..0faaeba 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.
@@ -521,6 +533,8 @@
         networkId = INVALID_NETWORK_ID;
         SSID = null;
         BSSID = null;
+        FQDN = null;
+        naiRealm = null;
         priority = 0;
         hiddenSSID = false;
         disableReason = DISABLED_UNKNOWN_REASON;
@@ -603,7 +617,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++) {
@@ -913,6 +928,8 @@
             disableReason = source.disableReason;
             SSID = source.SSID;
             BSSID = source.BSSID;
+            FQDN = source.FQDN;
+            naiRealm = source.naiRealm;
             preSharedKey = source.preSharedKey;
 
             wepKeys = new String[4];
@@ -979,6 +996,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);
@@ -1027,6 +1046,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();
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/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/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;