Merge changes I1fd178b6,I5093672e into lmp-preview-dev
* changes:
DO NOT MERGE: media.MediaCodec: hide NotificationCallback listener
media.MediaCodec: document and fix-up setNotificationCallback
diff --git a/api/current.txt b/api/current.txt
index 02d8450..ab287fb 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -1863,28 +1863,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
@@ -1931,11 +1931,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
@@ -2015,26 +2015,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
@@ -2323,126 +2325,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
@@ -3423,11 +3425,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();
@@ -5042,7 +5044,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 {
@@ -5355,13 +5359,13 @@
method public int describeContents();
method public int getBackoffPolicy();
method public android.os.Bundle getExtras();
+ method public int getId();
method public long getInitialBackoffMillis();
method public long getIntervalMillis();
method public long getMaxExecutionDelayMillis();
method public long getMinLatencyMillis();
method public int getNetworkCapabilities();
method public android.content.ComponentName getService();
- method public int getTaskId();
method public boolean isPeriodic();
method public boolean isRequireCharging();
method public boolean isRequireDeviceIdle();
@@ -5374,7 +5378,7 @@
field public static final int LINEAR = 0; // 0x0
}
- public final class Task.Builder {
+ public static final class Task.Builder {
ctor public Task.Builder(int, android.content.ComponentName);
method public android.app.task.Task build();
method public android.app.task.Task.Builder setBackoffCriteria(long, int);
@@ -5388,8 +5392,9 @@
}
public static abstract interface Task.NetworkType {
- field public static final int ANY = 0; // 0x0
- field public static final int UNMETERED = 1; // 0x1
+ field public static final int ANY = 1; // 0x1
+ field public static final int NONE = 0; // 0x0
+ field public static final int UNMETERED = 2; // 0x2
}
public abstract class TaskManager {
@@ -5398,8 +5403,8 @@
method public abstract void cancelAll();
method public abstract java.util.List<android.app.task.Task> getAllPendingTasks();
method public abstract int schedule(android.app.task.Task);
- field public static final int RESULT_INVALID_PARAMETERS = -1; // 0xffffffff
- field public static final int RESULT_OVER_QUOTA = -2; // 0xfffffffe
+ field public static final int RESULT_FAILURE = 0; // 0x0
+ field public static final int RESULT_SUCCESS = 1; // 0x1
}
public class TaskParams implements android.os.Parcelable {
@@ -5561,8 +5566,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();
@@ -6205,180 +6210,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);
@@ -6426,6 +6257,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 {
@@ -7035,7 +7027,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";
}
@@ -8195,7 +8186,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();
@@ -8206,21 +8197,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 {
@@ -12668,6 +12659,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 {
@@ -14578,6 +14572,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;
@@ -15715,56 +15710,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;
@@ -15776,95 +15796,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
}
}
@@ -17102,7 +17077,6 @@
public class WifiManager {
method public int addNetwork(android.net.wifi.WifiConfiguration);
method public static int calculateSignalLevel(int, int);
- method public void cancelWps(android.net.wifi.WifiManager.ActionListener);
method public static int compareSignalLevel(int, int);
method public android.net.wifi.WifiManager.MulticastLock createMulticastLock(java.lang.String);
method public android.net.wifi.WifiManager.WifiLock createWifiLock(int, java.lang.String);
@@ -17126,12 +17100,9 @@
method public void setTdlsEnabledWithMacAddress(java.lang.String, boolean);
method public boolean setWifiEnabled(boolean);
method public boolean startScan();
- method public void startWps(android.net.wifi.WpsInfo, android.net.wifi.WifiManager.WpsListener);
method public int updateNetwork(android.net.wifi.WifiConfiguration);
field public static final java.lang.String ACTION_PICK_WIFI_NETWORK = "android.net.wifi.PICK_WIFI_NETWORK";
field public static final java.lang.String ACTION_REQUEST_SCAN_ALWAYS_AVAILABLE = "android.net.wifi.action.REQUEST_SCAN_ALWAYS_AVAILABLE";
- field public static final int BUSY = 2; // 0x2
- field public static final int ERROR = 0; // 0x0
field public static final int ERROR_AUTHENTICATING = 1; // 0x1
field public static final java.lang.String EXTRA_BSSID = "bssid";
field public static final java.lang.String EXTRA_NETWORK_INFO = "networkInfo";
@@ -17142,8 +17113,6 @@
field public static final java.lang.String EXTRA_SUPPLICANT_ERROR = "supplicantError";
field public static final java.lang.String EXTRA_WIFI_INFO = "wifiInfo";
field public static final java.lang.String EXTRA_WIFI_STATE = "wifi_state";
- field public static final int INVALID_ARGS = 8; // 0x8
- field public static final int IN_PROGRESS = 1; // 0x1
field public static final java.lang.String NETWORK_IDS_CHANGED_ACTION = "android.net.wifi.NETWORK_IDS_CHANGED";
field public static final java.lang.String NETWORK_STATE_CHANGED_ACTION = "android.net.wifi.STATE_CHANGE";
field public static final java.lang.String RSSI_CHANGED_ACTION = "android.net.wifi.RSSI_CHANGED";
@@ -17159,16 +17128,6 @@
field public static final int WIFI_STATE_ENABLED = 3; // 0x3
field public static final int WIFI_STATE_ENABLING = 2; // 0x2
field public static final int WIFI_STATE_UNKNOWN = 4; // 0x4
- field public static final int WPS_AUTH_FAILURE = 6; // 0x6
- field public static final int WPS_OVERLAP_ERROR = 3; // 0x3
- field public static final int WPS_TIMED_OUT = 7; // 0x7
- field public static final int WPS_TKIP_ONLY_PROHIBITED = 5; // 0x5
- field public static final int WPS_WEP_PROHIBITED = 4; // 0x4
- }
-
- public static abstract interface WifiManager.ActionListener {
- method public abstract void onFailure(int);
- method public abstract void onSuccess();
}
public class WifiManager.MulticastLock {
@@ -17186,12 +17145,6 @@
method public void setWorkSource(android.os.WorkSource);
}
- public static abstract interface WifiManager.WpsListener {
- method public abstract void onCompletion();
- method public abstract void onFailure(int);
- 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);
@@ -17277,7 +17230,6 @@
ctor public WpsInfo(android.net.wifi.WpsInfo);
method public int describeContents();
method public void writeToParcel(android.os.Parcel, int);
- field public java.lang.String BSSID;
field public static final android.os.Parcelable.Creator CREATOR;
field public static final int DISPLAY = 1; // 0x1
field public static final int INVALID = 4; // 0x4
@@ -17485,29 +17437,6 @@
}
-package android.net.wifi.passpoint {
-
- public class WifiPasspointCredential implements android.os.Parcelable {
- ctor public WifiPasspointCredential(java.lang.String, android.net.wifi.WifiEnterpriseConfig);
- method public int describeContents();
- method public java.lang.String getClientCertPath();
- method public int getEapMethod();
- method public java.lang.String getFqdn();
- method public java.lang.String getImsi();
- method public java.lang.String getRealm();
- method public java.lang.String getUserName();
- 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 {
@@ -17642,25 +17571,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";
@@ -20501,6 +20420,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;
@@ -20629,17 +20579,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);
@@ -20656,37 +20603,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[]);
@@ -20698,30 +20629,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;
@@ -20739,9 +20659,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);
@@ -21321,46 +21238,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;
@@ -21663,10 +21548,6 @@
method public boolean isObbMounted(java.lang.String);
method public boolean mountObb(java.lang.String, java.lang.String, android.os.storage.OnObbStateChangeListener);
method public boolean unmountObb(java.lang.String, boolean, android.os.storage.OnObbStateChangeListener);
- field public static final int CRYPT_TYPE_DEFAULT = 1; // 0x1
- field public static final int CRYPT_TYPE_PASSWORD = 0; // 0x0
- field public static final int CRYPT_TYPE_PATTERN = 2; // 0x2
- field public static final int CRYPT_TYPE_PIN = 3; // 0x3
}
}
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/ActivityTransitionCoordinator.java b/core/java/android/app/ActivityTransitionCoordinator.java
index d08978bd..5e4ddd0 100644
--- a/core/java/android/app/ActivityTransitionCoordinator.java
+++ b/core/java/android/app/ActivityTransitionCoordinator.java
@@ -15,14 +15,23 @@
*/
package android.app;
+import android.content.Context;
+import android.content.res.Resources;
+import android.graphics.Bitmap;
+import android.graphics.Canvas;
+import android.graphics.Matrix;
import android.graphics.Rect;
+import android.graphics.drawable.BitmapDrawable;
+import android.os.Bundle;
import android.os.Handler;
import android.os.ResultReceiver;
import android.transition.Transition;
import android.transition.TransitionSet;
import android.util.ArrayMap;
+import android.util.Pair;
import android.view.View;
import android.view.ViewGroup;
+import android.view.ViewTreeObserver;
import android.view.Window;
import android.widget.ImageView;
@@ -181,6 +190,11 @@
*/
public static final int MSG_CANCEL = 106;
+ /**
+ * When returning, this is the destination location for the shared element.
+ */
+ public static final int MSG_SHARED_ELEMENT_DESTINATION = 107;
+
final private Window mWindow;
final protected ArrayList<String> mAllSharedElementNames;
final protected ArrayList<View> mSharedElements = new ArrayList<View>();
@@ -334,6 +348,210 @@
protected abstract Transition getViewsTransition();
+ private static void setSharedElementState(View view, String name, Bundle transitionArgs,
+ int[] parentLoc) {
+ Bundle sharedElementBundle = transitionArgs.getBundle(name);
+ if (sharedElementBundle == null) {
+ return;
+ }
+
+ if (view instanceof ImageView) {
+ int scaleTypeInt = sharedElementBundle.getInt(KEY_SCALE_TYPE, -1);
+ if (scaleTypeInt >= 0) {
+ ImageView imageView = (ImageView) view;
+ ImageView.ScaleType scaleType = SCALE_TYPE_VALUES[scaleTypeInt];
+ imageView.setScaleType(scaleType);
+ if (scaleType == ImageView.ScaleType.MATRIX) {
+ float[] matrixValues = sharedElementBundle.getFloatArray(KEY_IMAGE_MATRIX);
+ Matrix matrix = new Matrix();
+ matrix.setValues(matrixValues);
+ imageView.setImageMatrix(matrix);
+ }
+ }
+ }
+
+ float z = sharedElementBundle.getFloat(KEY_TRANSLATION_Z);
+ view.setTranslationZ(z);
+
+ int x = sharedElementBundle.getInt(KEY_SCREEN_X);
+ int y = sharedElementBundle.getInt(KEY_SCREEN_Y);
+ int width = sharedElementBundle.getInt(KEY_WIDTH);
+ int height = sharedElementBundle.getInt(KEY_HEIGHT);
+
+ int widthSpec = View.MeasureSpec.makeMeasureSpec(width, View.MeasureSpec.EXACTLY);
+ int heightSpec = View.MeasureSpec.makeMeasureSpec(height, View.MeasureSpec.EXACTLY);
+ view.measure(widthSpec, heightSpec);
+
+ int left = x - parentLoc[0];
+ int top = y - parentLoc[1];
+ int right = left + width;
+ int bottom = top + height;
+ view.layout(left, top, right, bottom);
+ }
+
+ protected ArrayMap<ImageView, Pair<ImageView.ScaleType, Matrix>> setSharedElementState(
+ Bundle sharedElementState, final ArrayList<View> snapshots) {
+ ArrayMap<ImageView, Pair<ImageView.ScaleType, Matrix>> originalImageState =
+ new ArrayMap<ImageView, Pair<ImageView.ScaleType, Matrix>>();
+ if (sharedElementState != null) {
+ int[] tempLoc = new int[2];
+ for (int i = 0; i < mSharedElementNames.size(); i++) {
+ View sharedElement = mSharedElements.get(i);
+ String name = mSharedElementNames.get(i);
+ Pair<ImageView.ScaleType, Matrix> originalState = getOldImageState(sharedElement,
+ name, sharedElementState);
+ if (originalState != null) {
+ originalImageState.put((ImageView) sharedElement, originalState);
+ }
+ View parent = (View) sharedElement.getParent();
+ parent.getLocationOnScreen(tempLoc);
+ setSharedElementState(sharedElement, name, sharedElementState, tempLoc);
+ }
+ }
+ mListener.setSharedElementStart(mSharedElementNames, mSharedElements, snapshots);
+
+ getDecor().getViewTreeObserver().addOnPreDrawListener(
+ new ViewTreeObserver.OnPreDrawListener() {
+ @Override
+ public boolean onPreDraw() {
+ getDecor().getViewTreeObserver().removeOnPreDrawListener(this);
+ mListener.setSharedElementEnd(mSharedElementNames, mSharedElements,
+ snapshots);
+ return true;
+ }
+ }
+ );
+ return originalImageState;
+ }
+
+ private static Pair<ImageView.ScaleType, Matrix> getOldImageState(View view, String name,
+ Bundle transitionArgs) {
+ if (!(view instanceof ImageView)) {
+ return null;
+ }
+ Bundle bundle = transitionArgs.getBundle(name);
+ if (bundle == null) {
+ return null;
+ }
+ int scaleTypeInt = bundle.getInt(KEY_SCALE_TYPE, -1);
+ if (scaleTypeInt < 0) {
+ return null;
+ }
+
+ ImageView imageView = (ImageView) view;
+ ImageView.ScaleType originalScaleType = imageView.getScaleType();
+
+ Matrix originalMatrix = null;
+ if (originalScaleType == ImageView.ScaleType.MATRIX) {
+ originalMatrix = new Matrix(imageView.getImageMatrix());
+ }
+
+ return Pair.create(originalScaleType, originalMatrix);
+ }
+
+ protected ArrayList<View> createSnapshots(Bundle state, Collection<String> names) {
+ int numSharedElements = names.size();
+ if (numSharedElements == 0) {
+ return null;
+ }
+ ArrayList<View> snapshots = new ArrayList<View>(numSharedElements);
+ Context context = getWindow().getContext();
+ int[] parentLoc = new int[2];
+ getDecor().getLocationOnScreen(parentLoc);
+ for (String name: names) {
+ Bundle sharedElementBundle = state.getBundle(name);
+ if (sharedElementBundle != null) {
+ Bitmap bitmap = sharedElementBundle.getParcelable(KEY_BITMAP);
+ View snapshot = new View(context);
+ Resources resources = getWindow().getContext().getResources();
+ if (bitmap != null) {
+ snapshot.setBackground(new BitmapDrawable(resources, bitmap));
+ }
+ snapshot.setViewName(name);
+ setSharedElementState(snapshot, name, state, parentLoc);
+ snapshots.add(snapshot);
+ }
+ }
+ return snapshots;
+ }
+
+ protected static void setOriginalImageViewState(
+ ArrayMap<ImageView, Pair<ImageView.ScaleType, Matrix>> originalState) {
+ for (int i = 0; i < originalState.size(); i++) {
+ ImageView imageView = originalState.keyAt(i);
+ Pair<ImageView.ScaleType, Matrix> state = originalState.valueAt(i);
+ imageView.setScaleType(state.first);
+ imageView.setImageMatrix(state.second);
+ }
+ }
+
+ protected Bundle captureSharedElementState() {
+ Bundle bundle = new Bundle();
+ int[] tempLoc = new int[2];
+ for (int i = 0; i < mSharedElementNames.size(); i++) {
+ View sharedElement = mSharedElements.get(i);
+ String name = mSharedElementNames.get(i);
+ captureSharedElementState(sharedElement, name, bundle, tempLoc);
+ }
+ return bundle;
+ }
+
+ /**
+ * Captures placement information for Views with a shared element name for
+ * Activity Transitions.
+ *
+ * @param view The View to capture the placement information for.
+ * @param name The shared element name in the target Activity to apply the placement
+ * information for.
+ * @param transitionArgs Bundle to store shared element placement information.
+ * @param tempLoc A temporary int[2] for capturing the current location of views.
+ */
+ private static void captureSharedElementState(View view, String name, Bundle transitionArgs,
+ int[] tempLoc) {
+ Bundle sharedElementBundle = new Bundle();
+ view.getLocationOnScreen(tempLoc);
+ float scaleX = view.getScaleX();
+ sharedElementBundle.putInt(KEY_SCREEN_X, tempLoc[0]);
+ int width = Math.round(view.getWidth() * scaleX);
+ sharedElementBundle.putInt(KEY_WIDTH, width);
+
+ float scaleY = view.getScaleY();
+ sharedElementBundle.putInt(KEY_SCREEN_Y, tempLoc[1]);
+ int height = Math.round(view.getHeight() * scaleY);
+ sharedElementBundle.putInt(KEY_HEIGHT, height);
+
+ sharedElementBundle.putFloat(KEY_TRANSLATION_Z, view.getTranslationZ());
+
+ if (width > 0 && height > 0) {
+ Bitmap bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
+ Canvas canvas = new Canvas(bitmap);
+ view.draw(canvas);
+ sharedElementBundle.putParcelable(KEY_BITMAP, bitmap);
+ }
+
+ if (view instanceof ImageView) {
+ ImageView imageView = (ImageView) view;
+ int scaleTypeInt = scaleTypeToInt(imageView.getScaleType());
+ sharedElementBundle.putInt(KEY_SCALE_TYPE, scaleTypeInt);
+ if (imageView.getScaleType() == ImageView.ScaleType.MATRIX) {
+ float[] matrix = new float[9];
+ imageView.getImageMatrix().getValues(matrix);
+ sharedElementBundle.putFloatArray(KEY_IMAGE_MATRIX, matrix);
+ }
+ }
+
+ transitionArgs.putBundle(name, sharedElementBundle);
+ }
+
+ private static int scaleTypeToInt(ImageView.ScaleType scaleType) {
+ for (int i = 0; i < SCALE_TYPE_VALUES.length; i++) {
+ if (scaleType == SCALE_TYPE_VALUES[i]) {
+ return i;
+ }
+ }
+ return -1;
+ }
+
private static class FixedEpicenterCallback extends Transition.EpicenterCallback {
private Rect mEpicenter;
diff --git a/core/java/android/app/EnterTransitionCoordinator.java b/core/java/android/app/EnterTransitionCoordinator.java
index bc97852..a8617b8 100644
--- a/core/java/android/app/EnterTransitionCoordinator.java
+++ b/core/java/android/app/EnterTransitionCoordinator.java
@@ -18,11 +18,7 @@
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.ObjectAnimator;
-import android.content.Context;
-import android.content.res.Resources;
-import android.graphics.Bitmap;
import android.graphics.Matrix;
-import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.os.Bundle;
import android.os.Handler;
@@ -38,7 +34,6 @@
import android.widget.ImageView;
import java.util.ArrayList;
-import java.util.Collection;
/**
* This ActivityTransitionCoordinator is created by the Activity to manage
@@ -56,6 +51,7 @@
private Handler mHandler;
private boolean mIsCanceled;
private ObjectAnimator mBackgroundAnimator;
+ private boolean mIsExitTransitionComplete;
public EnterTransitionCoordinator(Activity activity, ResultReceiver resultReceiver,
ArrayList<String> sharedElementNames,
@@ -76,6 +72,8 @@
}
};
mHandler.sendEmptyMessageDelayed(MSG_CANCEL, MAX_WAIT_MS);
+ Bundle state = captureSharedElementState();
+ mResultReceiver.send(MSG_SHARED_ELEMENT_DESTINATION, state);
}
}
@@ -98,9 +96,8 @@
break;
case MSG_EXIT_TRANSITION_COMPLETE:
if (!mIsCanceled) {
- if (!mSharedElementTransitionStarted) {
- send(resultCode, resultData);
- } else {
+ mIsExitTransitionComplete = true;
+ if (mSharedElementTransitionStarted) {
onRemoteExitTransitionComplete();
}
}
@@ -183,6 +180,7 @@
setViewVisibility(mSharedElements, View.VISIBLE);
ArrayMap<ImageView, Pair<ImageView.ScaleType, Matrix>> originalImageViewState =
setSharedElementState(sharedElementState, sharedElementSnapshots);
+ requestLayoutForSharedElements();
boolean startEnterTransition = allowOverlappingTransitions();
boolean startSharedElementTransition = true;
@@ -200,6 +198,13 @@
mResultReceiver = null; // all done sending messages.
}
+ private void requestLayoutForSharedElements() {
+ int numSharedElements = mSharedElements.size();
+ for (int i = 0; i < numSharedElements; i++) {
+ mSharedElements.get(i).requestLayout();
+ }
+ }
+
private Transition beginTransition(boolean startEnterTransition,
boolean startSharedElementTransition) {
Transition sharedElementTransition = null;
@@ -213,6 +218,19 @@
}
Transition transition = mergeTransitions(sharedElementTransition, viewsTransition);
+ if (startSharedElementTransition) {
+ if (transition == null) {
+ sharedElementTransitionStarted();
+ } else {
+ transition.addListener(new Transition.TransitionListenerAdapter() {
+ @Override
+ public void onTransitionStart(Transition transition) {
+ transition.removeListener(this);
+ sharedElementTransitionStarted();
+ }
+ });
+ }
+ }
if (transition != null) {
TransitionManager.beginDelayedTransition(getDecor(), transition);
if (startSharedElementTransition && !mSharedElementNames.isEmpty()) {
@@ -224,6 +242,13 @@
return transition;
}
+ private void sharedElementTransitionStarted() {
+ mSharedElementTransitionStarted = true;
+ if (mIsExitTransitionComplete) {
+ send(MSG_EXIT_TRANSITION_COMPLETE, null);
+ }
+ }
+
private void startEnterTransition(Transition transition) {
setViewVisibility(mTransitioningViews, View.VISIBLE);
if (!mIsReturning) {
@@ -310,142 +335,4 @@
startEnterTransition(transition);
}
}
-
- private ArrayList<View> createSnapshots(Bundle state, Collection<String> names) {
- int numSharedElements = names.size();
- if (numSharedElements == 0) {
- return null;
- }
- ArrayList<View> snapshots = new ArrayList<View>(numSharedElements);
- Context context = getWindow().getContext();
- int[] parentLoc = new int[2];
- getDecor().getLocationOnScreen(parentLoc);
- for (String name: names) {
- Bundle sharedElementBundle = state.getBundle(name);
- if (sharedElementBundle != null) {
- Bitmap bitmap = sharedElementBundle.getParcelable(KEY_BITMAP);
- View snapshot = new View(context);
- Resources resources = getWindow().getContext().getResources();
- snapshot.setBackground(new BitmapDrawable(resources, bitmap));
- snapshot.setViewName(name);
- setSharedElementState(snapshot, name, state, parentLoc);
- snapshots.add(snapshot);
- }
- }
- return snapshots;
- }
-
- private static void setSharedElementState(View view, String name, Bundle transitionArgs,
- int[] parentLoc) {
- Bundle sharedElementBundle = transitionArgs.getBundle(name);
- if (sharedElementBundle == null) {
- return;
- }
-
- if (view instanceof ImageView) {
- int scaleTypeInt = sharedElementBundle.getInt(KEY_SCALE_TYPE, -1);
- if (scaleTypeInt >= 0) {
- ImageView imageView = (ImageView) view;
- ImageView.ScaleType scaleType = SCALE_TYPE_VALUES[scaleTypeInt];
- imageView.setScaleType(scaleType);
- if (scaleType == ImageView.ScaleType.MATRIX) {
- float[] matrixValues = sharedElementBundle.getFloatArray(KEY_IMAGE_MATRIX);
- Matrix matrix = new Matrix();
- matrix.setValues(matrixValues);
- imageView.setImageMatrix(matrix);
- }
- }
- }
-
- float z = sharedElementBundle.getFloat(KEY_TRANSLATION_Z);
- view.setTranslationZ(z);
-
- int x = sharedElementBundle.getInt(KEY_SCREEN_X);
- int y = sharedElementBundle.getInt(KEY_SCREEN_Y);
- int width = sharedElementBundle.getInt(KEY_WIDTH);
- int height = sharedElementBundle.getInt(KEY_HEIGHT);
-
- int widthSpec = View.MeasureSpec.makeMeasureSpec(width, View.MeasureSpec.EXACTLY);
- int heightSpec = View.MeasureSpec.makeMeasureSpec(height, View.MeasureSpec.EXACTLY);
- view.measure(widthSpec, heightSpec);
-
- int left = x - parentLoc[0];
- int top = y - parentLoc[1];
- int right = left + width;
- int bottom = top + height;
- view.layout(left, top, right, bottom);
- }
-
- private ArrayMap<ImageView, Pair<ImageView.ScaleType, Matrix>> setSharedElementState(
- Bundle sharedElementState, final ArrayList<View> snapshots) {
- ArrayMap<ImageView, Pair<ImageView.ScaleType, Matrix>> originalImageState =
- new ArrayMap<ImageView, Pair<ImageView.ScaleType, Matrix>>();
- if (sharedElementState != null) {
- int[] tempLoc = new int[2];
- for (int i = 0; i < mSharedElementNames.size(); i++) {
- View sharedElement = mSharedElements.get(i);
- String name = mSharedElementNames.get(i);
- Pair<ImageView.ScaleType, Matrix> originalState = getOldImageState(sharedElement,
- name, sharedElementState);
- if (originalState != null) {
- originalImageState.put((ImageView) sharedElement, originalState);
- }
- View parent = (View) sharedElement.getParent();
- parent.getLocationOnScreen(tempLoc);
- setSharedElementState(sharedElement, name, sharedElementState, tempLoc);
- sharedElement.requestLayout();
- }
- }
- mListener.setSharedElementStart(mSharedElementNames, mSharedElements, snapshots);
-
- getDecor().getViewTreeObserver().addOnPreDrawListener(
- new ViewTreeObserver.OnPreDrawListener() {
- @Override
- public boolean onPreDraw() {
- getDecor().getViewTreeObserver().removeOnPreDrawListener(this);
- mListener.setSharedElementEnd(mSharedElementNames, mSharedElements,
- snapshots);
- mSharedElementTransitionStarted = true;
- return true;
- }
- }
- );
- return originalImageState;
- }
-
- private static Pair<ImageView.ScaleType, Matrix> getOldImageState(View view, String name,
- Bundle transitionArgs) {
- if (!(view instanceof ImageView)) {
- return null;
- }
- Bundle bundle = transitionArgs.getBundle(name);
- if (bundle == null) {
- return null;
- }
- int scaleTypeInt = bundle.getInt(KEY_SCALE_TYPE, -1);
- if (scaleTypeInt < 0) {
- return null;
- }
-
- ImageView imageView = (ImageView) view;
- ImageView.ScaleType originalScaleType = imageView.getScaleType();
-
- Matrix originalMatrix = null;
- if (originalScaleType == ImageView.ScaleType.MATRIX) {
- originalMatrix = new Matrix(imageView.getImageMatrix());
- }
-
- return Pair.create(originalScaleType, originalMatrix);
- }
-
- private static void setOriginalImageViewState(
- ArrayMap<ImageView, Pair<ImageView.ScaleType, Matrix>> originalState) {
- for (int i = 0; i < originalState.size(); i++) {
- ImageView imageView = originalState.keyAt(i);
- Pair<ImageView.ScaleType, Matrix> state = originalState.valueAt(i);
- imageView.setScaleType(state.first);
- imageView.setImageMatrix(state.second);
- }
- }
-
}
diff --git a/core/java/android/app/ExitTransitionCoordinator.java b/core/java/android/app/ExitTransitionCoordinator.java
index 93eb53e..a71d649 100644
--- a/core/java/android/app/ExitTransitionCoordinator.java
+++ b/core/java/android/app/ExitTransitionCoordinator.java
@@ -19,8 +19,6 @@
import android.animation.AnimatorListenerAdapter;
import android.animation.ObjectAnimator;
import android.content.Intent;
-import android.graphics.Bitmap;
-import android.graphics.Canvas;
import android.graphics.drawable.ColorDrawable;
import android.graphics.drawable.Drawable;
import android.os.Bundle;
@@ -29,7 +27,7 @@
import android.transition.Transition;
import android.transition.TransitionManager;
import android.view.View;
-import android.widget.ImageView;
+import android.view.ViewTreeObserver;
import java.util.ArrayList;
@@ -62,6 +60,10 @@
private boolean mIsHidden;
+ private boolean mExitTransitionStarted;
+
+ private Bundle mExitSharedElementBundle;
+
public ExitTransitionCoordinator(Activity activity, ArrayList<String> names,
ArrayList<String> accepted, ArrayList<String> mapped, boolean isReturning) {
super(activity.getWindow(), names, accepted, mapped, getListener(activity, isReturning),
@@ -102,15 +104,32 @@
setViewVisibility(mSharedElements, View.VISIBLE);
mIsHidden = true;
break;
+ case MSG_SHARED_ELEMENT_DESTINATION:
+ mExitSharedElementBundle = resultData;
+ if (mExitTransitionStarted) {
+ startSharedElementExit();
+ }
+ break;
+ }
+ }
+
+ private void startSharedElementExit() {
+ if (!mSharedElements.isEmpty() && getSharedElementTransition() != null) {
+ Transition transition = getSharedElementExitTransition();
+ TransitionManager.beginDelayedTransition(getDecor(), transition);
+ ArrayList<View> sharedElementSnapshots = createSnapshots(mExitSharedElementBundle,
+ mSharedElementNames);
+ setSharedElementState(mExitSharedElementBundle, sharedElementSnapshots);
}
}
private void hideSharedElements() {
setViewVisibility(mSharedElements, View.INVISIBLE);
+ finishIfNecessary();
}
public void startExit() {
- beginTransition();
+ beginTransitions();
setViewVisibility(mTransitioningViews, View.INVISIBLE);
}
@@ -140,7 +159,30 @@
}
}
}, options);
- startExit();
+ Transition sharedElementTransition = mSharedElements.isEmpty()
+ ? null : getSharedElementTransition();
+ if (sharedElementTransition == null) {
+ sharedElementTransitionComplete();
+ }
+ Transition transition = mergeTransitions(sharedElementTransition, getExitTransition());
+ if (transition == null) {
+ mExitTransitionStarted = true;
+ } else {
+ TransitionManager.beginDelayedTransition(getDecor(), transition);
+ setViewVisibility(mTransitioningViews, View.INVISIBLE);
+ getDecor().getViewTreeObserver().addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() {
+ @Override
+ public boolean onPreDraw() {
+ getDecor().getViewTreeObserver().removeOnPreDrawListener(this);
+ mExitTransitionStarted = true;
+ if (mExitSharedElementBundle != null) {
+ startSharedElementExit();
+ }
+ notifyComplete();
+ return true;
+ }
+ });
+ }
}
private void fadeOutBackground() {
@@ -162,24 +204,13 @@
}
}
- private void beginTransition() {
- Transition sharedElementTransition = configureTransition(getSharedElementTransition());
- Transition viewsTransition = configureTransition(getViewsTransition());
- viewsTransition = addTargets(viewsTransition, mTransitioningViews);
- if (sharedElementTransition == null || mSharedElements.isEmpty()) {
- sharedElementTransitionComplete();
- sharedElementTransition = null;
- } else {
- sharedElementTransition.addListener(new Transition.TransitionListenerAdapter() {
- @Override
- public void onTransitionEnd(Transition transition) {
- sharedElementTransitionComplete();
- }
- });
+ private Transition getExitTransition() {
+ Transition viewsTransition = null;
+ if (!mTransitioningViews.isEmpty()) {
+ viewsTransition = configureTransition(getViewsTransition());
}
- if (viewsTransition == null || mTransitioningViews.isEmpty()) {
+ if (viewsTransition == null) {
exitTransitionComplete();
- viewsTransition = null;
} else {
viewsTransition.addListener(new Transition.TransitionListenerAdapter() {
@Override
@@ -189,13 +220,46 @@
setViewVisibility(mTransitioningViews, View.VISIBLE);
}
}
+
+ @Override
+ public void onTransitionCancel(Transition transition) {
+ super.onTransitionCancel(transition);
+ }
});
}
+ return viewsTransition;
+ }
+
+ private Transition getSharedElementExitTransition() {
+ Transition sharedElementTransition = null;
+ if (!mSharedElements.isEmpty()) {
+ sharedElementTransition = configureTransition(getSharedElementTransition());
+ }
+ if (sharedElementTransition == null) {
+ sharedElementTransitionComplete();
+ } else {
+ sharedElementTransition.addListener(new Transition.TransitionListenerAdapter() {
+ @Override
+ public void onTransitionEnd(Transition transition) {
+ sharedElementTransitionComplete();
+ if (mIsHidden) {
+ setViewVisibility(mSharedElements, View.VISIBLE);
+ }
+ }
+ });
+ mSharedElements.get(0).invalidate();
+ }
+ return sharedElementTransition;
+ }
+
+ private void beginTransitions() {
+ Transition sharedElementTransition = getSharedElementExitTransition();
+ Transition viewsTransition = getExitTransition();
Transition transition = mergeTransitions(sharedElementTransition, viewsTransition);
- TransitionManager.beginDelayedTransition(getDecor(), transition);
- if (viewsTransition == null && sharedElementTransition != null) {
- mSharedElements.get(0).requestLayout();
+ mExitTransitionStarted = true;
+ if (transition != null) {
+ TransitionManager.beginDelayedTransition(getDecor(), transition);
}
}
@@ -205,18 +269,12 @@
}
protected boolean isReadyToNotify() {
- return mSharedElementBundle != null && mResultReceiver != null && mIsBackgroundReady;
+ return mSharedElementBundle != null && mResultReceiver != null && mIsBackgroundReady
+ && mExitTransitionStarted;
}
private void sharedElementTransitionComplete() {
- Bundle bundle = new Bundle();
- int[] tempLoc = new int[2];
- for (int i = 0; i < mSharedElementNames.size(); i++) {
- View sharedElement = mSharedElements.get(i);
- String name = mSharedElementNames.get(i);
- captureSharedElementState(sharedElement, name, bundle, tempLoc);
- }
- mSharedElementBundle = bundle;
+ mSharedElementBundle = captureSharedElementState();
notifyComplete();
}
@@ -230,15 +288,23 @@
mExitNotified = true;
mResultReceiver.send(MSG_EXIT_TRANSITION_COMPLETE, null);
mResultReceiver = null; // done talking
- if (mIsReturning) {
- mActivity.finish();
- mActivity.overridePendingTransition(0, 0);
- }
- mActivity = null;
+ finishIfNecessary();
}
}
}
+ private void finishIfNecessary() {
+ if (mIsReturning && mExitNotified && (mSharedElements.isEmpty()
+ || mSharedElements.get(0).getVisibility() == View.INVISIBLE)) {
+ mActivity.finish();
+ mActivity.overridePendingTransition(0, 0);
+ mActivity = null;
+ }
+ if (!mIsReturning && mExitNotified) {
+ mActivity = null; // don't need it anymore
+ }
+ }
+
@Override
protected Transition getViewsTransition() {
if (mIsReturning) {
@@ -255,58 +321,4 @@
return getWindow().getSharedElementExitTransition();
}
}
-
- /**
- * Captures placement information for Views with a shared element name for
- * Activity Transitions.
- *
- * @param view The View to capture the placement information for.
- * @param name The shared element name in the target Activity to apply the placement
- * information for.
- * @param transitionArgs Bundle to store shared element placement information.
- * @param tempLoc A temporary int[2] for capturing the current location of views.
- */
- private static void captureSharedElementState(View view, String name, Bundle transitionArgs,
- int[] tempLoc) {
- Bundle sharedElementBundle = new Bundle();
- view.getLocationOnScreen(tempLoc);
- float scaleX = view.getScaleX();
- sharedElementBundle.putInt(KEY_SCREEN_X, tempLoc[0]);
- int width = Math.round(view.getWidth() * scaleX);
- sharedElementBundle.putInt(KEY_WIDTH, width);
-
- float scaleY = view.getScaleY();
- sharedElementBundle.putInt(KEY_SCREEN_Y, tempLoc[1]);
- int height = Math.round(view.getHeight() * scaleY);
- sharedElementBundle.putInt(KEY_HEIGHT, height);
-
- sharedElementBundle.putFloat(KEY_TRANSLATION_Z, view.getTranslationZ());
-
- Bitmap bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
- Canvas canvas = new Canvas(bitmap);
- view.draw(canvas);
- sharedElementBundle.putParcelable(KEY_BITMAP, bitmap);
-
- if (view instanceof ImageView) {
- ImageView imageView = (ImageView) view;
- int scaleTypeInt = scaleTypeToInt(imageView.getScaleType());
- sharedElementBundle.putInt(KEY_SCALE_TYPE, scaleTypeInt);
- if (imageView.getScaleType() == ImageView.ScaleType.MATRIX) {
- float[] matrix = new float[9];
- imageView.getImageMatrix().getValues(matrix);
- sharedElementBundle.putFloatArray(KEY_IMAGE_MATRIX, matrix);
- }
- }
-
- transitionArgs.putBundle(name, sharedElementBundle);
- }
-
- private static int scaleTypeToInt(ImageView.ScaleType scaleType) {
- for (int i = 0; i < SCALE_TYPE_VALUES.length; i++) {
- if (scaleType == SCALE_TYPE_VALUES[i]) {
- return i;
- }
- }
- return -1;
- }
}
diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java
index 25f24b1..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();
}
@@ -3716,7 +3723,7 @@
* this notification. This action will no longer display separately from the
* notification's content.
*
- * <p>For notifications with multiple pages, child pages can also have content action's
+ * <p>For notifications with multiple pages, child pages can also have content actions
* set, although the list of available actions comes from the main notification and not
* from the child page's notification.
*
@@ -3731,16 +3738,18 @@
}
/**
- * Get the action index from this notification's actions to be clickable with the
- * content of this notification. This action will no longer display separately
+ * Get the index of the notification action, if any, that was specified as being clickable
+ * with the content of this notification. This action will no longer display separately
* from the notification's content.
*
- * <p>For notifications with multiple pages, child pages can also have content action's
+ * <p>For notifications with multiple pages, child pages can also have content actions
* set, although the list of available actions comes from the main notification and not
* from the child page's notification.
*
* <p>If wearable specific actions were added to the main notification, this index will
* apply to that list, otherwise it will apply to the regular actions list.
+ *
+ * @return the action index or {@link #UNSET_ACTION_INDEX} if no action was selected.
*/
public int getContentAction() {
return mContentActionIndex;
diff --git a/core/java/android/app/VoiceInteractor.java b/core/java/android/app/VoiceInteractor.java
index 6dc48b0..fe85ef4 100644
--- a/core/java/android/app/VoiceInteractor.java
+++ b/core/java/android/app/VoiceInteractor.java
@@ -30,7 +30,7 @@
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.
@@ -39,9 +39,11 @@
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
@@ -140,6 +142,12 @@
public void onCancel() {
}
+ public void onAttached(Activity activity) {
+ }
+
+ public void onDetached() {
+ }
+
void clear() {
mRequestInterface = null;
mContext = null;
@@ -220,11 +228,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 +246,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/task/Task.java b/core/java/android/app/task/Task.java
index dd184a5..ca4aeb2 100644
--- a/core/java/android/app/task/Task.java
+++ b/core/java/android/app/task/Task.java
@@ -27,10 +27,13 @@
* using the {@link Task.Builder}.
*/
public class Task implements Parcelable {
-
public interface NetworkType {
- public final int ANY = 0;
- public final int UNMETERED = 1;
+ /** Default. */
+ public final int NONE = 0;
+ /** This task requires network connectivity. */
+ public final int ANY = 1;
+ /** This task requires network connectivity that is unmetered. */
+ public final int UNMETERED = 2;
}
/**
@@ -48,6 +51,8 @@
private final ComponentName service;
private final boolean requireCharging;
private final boolean requireDeviceIdle;
+ private final boolean hasEarlyConstraint;
+ private final boolean hasLateConstraint;
private final int networkCapabilities;
private final long minLatencyMillis;
private final long maxExecutionDelayMillis;
@@ -59,7 +64,7 @@
/**
* Unique task id associated with this class. This is assigned to your task by the scheduler.
*/
- public int getTaskId() {
+ public int getId() {
return taskId;
}
@@ -146,6 +151,24 @@
return backoffPolicy;
}
+ /**
+ * User can specify an early constraint of 0L, which is valid, so we keep track of whether the
+ * function was called at all.
+ * @hide
+ */
+ public boolean hasEarlyConstraint() {
+ return hasEarlyConstraint;
+ }
+
+ /**
+ * User can specify a late constraint of 0L, which is valid, so we keep track of whether the
+ * function was called at all.
+ * @hide
+ */
+ public boolean hasLateConstraint() {
+ return hasLateConstraint;
+ }
+
private Task(Parcel in) {
taskId = in.readInt();
extras = in.readBundle();
@@ -159,6 +182,8 @@
intervalMillis = in.readLong();
initialBackoffMillis = in.readLong();
backoffPolicy = in.readInt();
+ hasEarlyConstraint = in.readInt() == 1;
+ hasLateConstraint = in.readInt() == 1;
}
private Task(Task.Builder b) {
@@ -174,6 +199,8 @@
intervalMillis = b.mIntervalMillis;
initialBackoffMillis = b.mInitialBackoffMillis;
backoffPolicy = b.mBackoffPolicy;
+ hasEarlyConstraint = b.mHasEarlyConstraint;
+ hasLateConstraint = b.mHasLateConstraint;
}
@Override
@@ -195,6 +222,8 @@
out.writeLong(intervalMillis);
out.writeLong(initialBackoffMillis);
out.writeInt(backoffPolicy);
+ out.writeInt(hasEarlyConstraint ? 1 : 0);
+ out.writeInt(hasLateConstraint ? 1 : 0);
}
public static final Creator<Task> CREATOR = new Creator<Task>() {
@@ -212,7 +241,7 @@
/**
* Builder class for constructing {@link Task} objects.
*/
- public final class Builder {
+ public static final class Builder {
private int mTaskId;
private Bundle mExtras;
private ComponentName mTaskService;
@@ -225,6 +254,8 @@
private long mMaxExecutionDelayMillis;
// Periodic parameters.
private boolean mIsPeriodic;
+ private boolean mHasEarlyConstraint;
+ private boolean mHasLateConstraint;
private long mIntervalMillis;
// Back-off parameters.
private long mInitialBackoffMillis = 5000L;
@@ -307,6 +338,7 @@
public Builder setPeriodic(long intervalMillis) {
mIsPeriodic = true;
mIntervalMillis = intervalMillis;
+ mHasEarlyConstraint = mHasLateConstraint = true;
return this;
}
@@ -320,6 +352,7 @@
*/
public Builder setMinimumLatency(long minLatencyMillis) {
mMinLatencyMillis = minLatencyMillis;
+ mHasEarlyConstraint = true;
return this;
}
@@ -332,6 +365,7 @@
*/
public Builder setOverrideDeadline(long maxExecutionDelayMillis) {
mMaxExecutionDelayMillis = maxExecutionDelayMillis;
+ mHasLateConstraint = true;
return this;
}
@@ -360,31 +394,18 @@
* @return The task object to hand to the TaskManager. This object is immutable.
*/
public Task build() {
- // Check that extras bundle only contains primitive types.
- try {
- for (String key : extras.keySet()) {
- Object value = extras.get(key);
- if (value == null) continue;
- if (value instanceof Long) continue;
- if (value instanceof Integer) continue;
- if (value instanceof Boolean) continue;
- if (value instanceof Float) continue;
- if (value instanceof Double) continue;
- if (value instanceof String) continue;
- throw new IllegalArgumentException("Unexpected value type: "
- + value.getClass().getName());
- }
- } catch (IllegalArgumentException e) {
- throw e;
- } catch (RuntimeException exc) {
- throw new IllegalArgumentException("error unparcelling Bundle", exc);
+ if (mExtras == null) {
+ mExtras = Bundle.EMPTY;
+ }
+ if (mTaskId < 0) {
+ throw new IllegalArgumentException("Task id must be greater than 0.");
}
// Check that a deadline was not set on a periodic task.
- if (mIsPeriodic && (mMaxExecutionDelayMillis != 0L)) {
+ if (mIsPeriodic && mHasLateConstraint) {
throw new IllegalArgumentException("Can't call setOverrideDeadline() on a " +
"periodic task.");
}
- if (mIsPeriodic && (mMinLatencyMillis != 0L)) {
+ if (mIsPeriodic && mHasEarlyConstraint) {
throw new IllegalArgumentException("Can't call setMinimumLatency() on a " +
"periodic task");
}
diff --git a/core/java/android/app/task/TaskManager.java b/core/java/android/app/task/TaskManager.java
index 0fbe37d..00f57da 100644
--- a/core/java/android/app/task/TaskManager.java
+++ b/core/java/android/app/task/TaskManager.java
@@ -34,14 +34,13 @@
* if the run-time for your task is too short, or perhaps the system can't resolve the
* requisite {@link TaskService} in your package.
*/
- public static final int RESULT_INVALID_PARAMETERS = -1;
-
+ public static final int RESULT_FAILURE = 0;
/**
* Returned from {@link #schedule(Task)} if this application has made too many requests for
* work over too short a time.
*/
// TODO: Determine if this is necessary.
- public static final int RESULT_OVER_QUOTA = -2;
+ public static final int RESULT_SUCCESS = 1;
/**
* @param task The task you wish scheduled. See
diff --git a/core/java/android/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..2ff85c6 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -2363,6 +2363,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/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/ddm/DdmHandleHello.java b/core/java/android/ddm/DdmHandleHello.java
index 220b40d..2dce425 100644
--- a/core/java/android/ddm/DdmHandleHello.java
+++ b/core/java/android/ddm/DdmHandleHello.java
@@ -22,6 +22,7 @@
import android.util.Log;
import android.os.Debug;
import android.os.UserHandle;
+import dalvik.system.VMRuntime;
import java.nio.ByteBuffer;
@@ -126,8 +127,21 @@
// appName = "unknown";
String appName = DdmHandleAppName.getAppName();
- ByteBuffer out = ByteBuffer.allocate(20
- + vmIdent.length()*2 + appName.length()*2);
+ VMRuntime vmRuntime = VMRuntime.getRuntime();
+ String instructionSetDescription =
+ vmRuntime.is64Bit() ? "64-bit" : "32-bit";
+ String vmInstructionSet = vmRuntime.vmInstructionSet();
+ if (vmInstructionSet != null && vmInstructionSet.length() > 0) {
+ instructionSetDescription += " (" + vmInstructionSet + ")";
+ }
+ String vmFlags = "CheckJNI="
+ + (vmRuntime.isCheckJniEnabled() ? "true" : "false");
+
+ ByteBuffer out = ByteBuffer.allocate(28
+ + vmIdent.length() * 2
+ + appName.length() * 2
+ + instructionSetDescription.length() * 2
+ + vmFlags.length() * 2);
out.order(ChunkHandler.CHUNK_ORDER);
out.putInt(DdmServer.CLIENT_PROTOCOL_VERSION);
out.putInt(android.os.Process.myPid());
@@ -136,6 +150,10 @@
putString(out, vmIdent);
putString(out, appName);
out.putInt(UserHandle.myUserId());
+ out.putInt(instructionSetDescription.length());
+ putString(out, instructionSetDescription);
+ out.putInt(vmFlags.length());
+ putString(out, vmFlags);
Chunk reply = new Chunk(CHUNK_HELO, out);
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/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/net/Network.java b/core/java/android/net/Network.java
index e0d69e3..64516e6 100644
--- a/core/java/android/net/Network.java
+++ b/core/java/android/net/Network.java
@@ -16,10 +16,13 @@
package android.net;
+import android.net.NetworkUtils;
import android.os.Parcelable;
import android.os.Parcel;
+import java.io.IOException;
import java.net.InetAddress;
+import java.net.InetSocketAddress;
import java.net.Socket;
import java.net.UnknownHostException;
import javax.net.SocketFactory;
@@ -38,6 +41,8 @@
*/
public final int netId;
+ private NetworkBoundSocketFactory mNetworkBoundSocketFactory = null;
+
/**
* @hide
*/
@@ -79,6 +84,74 @@
}
/**
+ * A {@code SocketFactory} that produces {@code Socket}'s bound to this network.
+ */
+ private class NetworkBoundSocketFactory extends SocketFactory {
+ private final int mNetId;
+
+ public NetworkBoundSocketFactory(int netId) {
+ super();
+ mNetId = netId;
+ }
+
+ private void connectToHost(Socket socket, String host, int port) throws IOException {
+ // Lookup addresses only on this Network.
+ InetAddress[] hostAddresses = getAllByName(host);
+ // Try all but last address ignoring exceptions.
+ for (int i = 0; i < hostAddresses.length - 1; i++) {
+ try {
+ socket.connect(new InetSocketAddress(hostAddresses[i], port));
+ return;
+ } catch (IOException e) {
+ }
+ }
+ // Try last address. Do throw exceptions.
+ socket.connect(new InetSocketAddress(hostAddresses[hostAddresses.length - 1], port));
+ }
+
+ @Override
+ public Socket createSocket(String host, int port, InetAddress localHost, int localPort) throws IOException {
+ Socket socket = createSocket();
+ socket.bind(new InetSocketAddress(localHost, localPort));
+ connectToHost(socket, host, port);
+ return socket;
+ }
+
+ @Override
+ public Socket createSocket(InetAddress address, int port, InetAddress localAddress,
+ int localPort) throws IOException {
+ Socket socket = createSocket();
+ socket.bind(new InetSocketAddress(localAddress, localPort));
+ socket.connect(new InetSocketAddress(address, port));
+ return socket;
+ }
+
+ @Override
+ public Socket createSocket(InetAddress host, int port) throws IOException {
+ Socket socket = createSocket();
+ socket.connect(new InetSocketAddress(host, port));
+ return socket;
+ }
+
+ @Override
+ public Socket createSocket(String host, int port) throws IOException {
+ Socket socket = createSocket();
+ connectToHost(socket, host, port);
+ return socket;
+ }
+
+ @Override
+ public Socket createSocket() throws IOException {
+ Socket socket = new Socket();
+ // Query a property of the underlying socket to ensure the underlying
+ // socket exists so a file descriptor is available to bind to a network.
+ socket.getReuseAddress();
+ NetworkUtils.bindSocketToNetwork(socket.getFileDescriptor$().getInt$(), mNetId);
+ return socket;
+ }
+ }
+
+ /**
* Returns a {@link SocketFactory} bound to this network. Any {@link Socket} created by
* this factory will have its traffic sent over this {@code Network}. Note that if this
* {@code Network} ever disconnects, this factory and any {@link Socket} it produced in the
@@ -88,7 +161,10 @@
* {@code Network}.
*/
public SocketFactory socketFactory() {
- return null;
+ if (mNetworkBoundSocketFactory == null) {
+ mNetworkBoundSocketFactory = new NetworkBoundSocketFactory(netId);
+ }
+ return mNetworkBoundSocketFactory;
}
/**
@@ -99,6 +175,29 @@
* doesn't accidentally use sockets it thinks are still bound to a particular {@code Network}.
*/
public void bindProcess() {
+ NetworkUtils.bindProcessToNetwork(netId);
+ }
+
+ /**
+ * Binds host resolutions performed by this process to this network. {@link #bindProcess}
+ * takes precedence over this setting.
+ *
+ * @hide
+ * @deprecated This is strictly for legacy usage to support startUsingNetworkFeature().
+ */
+ public void bindProcessForHostResolution() {
+ NetworkUtils.bindProcessToNetworkForHostResolution(netId);
+ }
+
+ /**
+ * Clears any process specific {@link Network} binding for host resolution. This does
+ * not clear bindings enacted via {@link #bindProcess}.
+ *
+ * @hide
+ * @deprecated This is strictly for legacy usage to support startUsingNetworkFeature().
+ */
+ public void unbindProcessForHostResolution() {
+ NetworkUtils.unbindProcessToNetworkForHostResolution();
}
/**
@@ -107,7 +206,7 @@
* @return {@code Network} to which this process is bound.
*/
public static Network getProcessBoundNetwork() {
- return null;
+ return new Network(NetworkUtils.getNetworkBoundToProcess());
}
/**
@@ -115,6 +214,7 @@
* {@link Network#bindProcess}.
*/
public static void unbindProcess() {
+ NetworkUtils.unbindProcessToNetwork();
}
// implement the Parcelable interface
diff --git a/core/java/android/net/NetworkUtils.java b/core/java/android/net/NetworkUtils.java
index b24d396..edb3286 100644
--- a/core/java/android/net/NetworkUtils.java
+++ b/core/java/android/net/NetworkUtils.java
@@ -109,6 +109,50 @@
public native static void markSocket(int socketfd, int mark);
/**
+ * Binds the current process to the network designated by {@code netId}. All sockets created
+ * in the future (and not explicitly bound via a bound {@link SocketFactory} (see
+ * {@link Network#socketFactory}) will be bound to this network. Note that if this
+ * {@code Network} ever disconnects all sockets created in this way will cease to work. This
+ * is by design so an application doesn't accidentally use sockets it thinks are still bound to
+ * a particular {@code Network}.
+ */
+ public native static void bindProcessToNetwork(int netId);
+
+ /**
+ * Clear any process specific {@code Network} binding. This reverts a call to
+ * {@link #bindProcessToNetwork}.
+ */
+ public native static void unbindProcessToNetwork();
+
+ /**
+ * Return the netId last passed to {@link #bindProcessToNetwork}, or NETID_UNSET if
+ * {@link #unbindProcessToNetwork} has been called since {@link #bindProcessToNetwork}.
+ */
+ public native static int getNetworkBoundToProcess();
+
+ /**
+ * Binds host resolutions performed by this process to the network designated by {@code netId}.
+ * {@link #bindProcessToNetwork} takes precedence over this setting.
+ *
+ * @deprecated This is strictly for legacy usage to support startUsingNetworkFeature().
+ */
+ public native static void bindProcessToNetworkForHostResolution(int netId);
+
+ /**
+ * Clears any process specific {@link Network} binding for host resolution. This does
+ * not clear bindings enacted via {@link #bindProcessToNetwork}.
+ *
+ * @deprecated This is strictly for legacy usage to support startUsingNetworkFeature().
+ */
+ public native static void unbindProcessToNetworkForHostResolution();
+
+ /**
+ * Explicitly binds {@code socketfd} to the network designated by {@code netId}. This
+ * overrides any binding via {@link #bindProcessToNetwork}.
+ */
+ public native static void bindSocketToNetwork(int socketfd, int netId);
+
+ /**
* Convert a IPv4 address from an integer to an InetAddress.
* @param hostAddress an int corresponding to the IPv4 address in network byte order
*/
diff --git a/core/java/android/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/PersistableBundle.java b/core/java/android/os/PersistableBundle.java
index cd8d515..c01f688 100644
--- a/core/java/android/os/PersistableBundle.java
+++ b/core/java/android/os/PersistableBundle.java
@@ -32,7 +32,8 @@
* restored.
*
*/
-public final class PersistableBundle extends CommonBundle implements XmlUtils.WriteMapCallback {
+public final class PersistableBundle extends BaseBundle implements Cloneable, Parcelable,
+ XmlUtils.WriteMapCallback {
private static final String TAG_PERSISTABLEMAP = "pbundle_as_map";
public static final PersistableBundle EMPTY;
static final Parcel EMPTY_PARCEL;
@@ -40,7 +41,7 @@
static {
EMPTY = new PersistableBundle();
EMPTY.mMap = ArrayMap.EMPTY;
- EMPTY_PARCEL = CommonBundle.EMPTY_PARCEL;
+ EMPTY_PARCEL = BaseBundle.EMPTY_PARCEL;
}
/**
@@ -51,31 +52,6 @@
}
/**
- * Constructs a PersistableBundle whose data is stored as a Parcel. The data
- * will be unparcelled on first contact, using the assigned ClassLoader.
- *
- * @param parcelledData a Parcel containing a PersistableBundle
- */
- PersistableBundle(Parcel parcelledData) {
- super(parcelledData);
- }
-
- /* package */ PersistableBundle(Parcel parcelledData, int length) {
- super(parcelledData, length);
- }
-
- /**
- * Constructs a new, empty PersistableBundle that uses a specific ClassLoader for
- * instantiating Parcelable and Serializable objects.
- *
- * @param loader An explicit ClassLoader to use when instantiating objects
- * inside of the PersistableBundle.
- */
- public PersistableBundle(ClassLoader loader) {
- super(loader);
- }
-
- /**
* Constructs a new, empty PersistableBundle sized to hold the given number of
* elements. The PersistableBundle will grow as needed.
*
@@ -127,6 +103,10 @@
}
}
+ /* package */ PersistableBundle(Parcel parcelledData, int length) {
+ super(parcelledData, length);
+ }
+
/**
* Make a PersistableBundle for a single key/value pair.
*
@@ -139,33 +119,6 @@
}
/**
- * @hide
- */
- @Override
- public String getPairValue() {
- return super.getPairValue();
- }
-
- /**
- * Changes the ClassLoader this PersistableBundle uses when instantiating objects.
- *
- * @param loader An explicit ClassLoader to use when instantiating objects
- * inside of the PersistableBundle.
- */
- @Override
- public void setClassLoader(ClassLoader loader) {
- super.setClassLoader(loader);
- }
-
- /**
- * Return the ClassLoader currently associated with this PersistableBundle.
- */
- @Override
- public ClassLoader getClassLoader() {
- return super.getClassLoader();
- }
-
- /**
* Clones the current PersistableBundle. The internal map is cloned, but the keys and
* values to which it refers are copied by reference.
*/
@@ -175,300 +128,15 @@
}
/**
- * @hide
- */
- @Override
- public boolean isParcelled() {
- return super.isParcelled();
- }
-
- /**
- * Returns the number of mappings contained in this PersistableBundle.
- *
- * @return the number of mappings as an int.
- */
- @Override
- public int size() {
- return super.size();
- }
-
- /**
- * Returns true if the mapping of this PersistableBundle is empty, false otherwise.
- */
- @Override
- public boolean isEmpty() {
- return super.isEmpty();
- }
-
- /**
- * Removes all elements from the mapping of this PersistableBundle.
- */
- @Override
- public void clear() {
- super.clear();
- }
-
- /**
- * Returns true if the given key is contained in the mapping
- * of this PersistableBundle.
- *
- * @param key a String key
- * @return true if the key is part of the mapping, false otherwise
- */
- @Override
- public boolean containsKey(String key) {
- return super.containsKey(key);
- }
-
- /**
- * Returns the entry with the given key as an object.
- *
- * @param key a String key
- * @return an Object, or null
- */
- @Override
- public Object get(String key) {
- return super.get(key);
- }
-
- /**
- * Removes any entry with the given key from the mapping of this PersistableBundle.
- *
- * @param key a String key
- */
- @Override
- public void remove(String key) {
- super.remove(key);
- }
-
- /**
- * Inserts all mappings from the given PersistableBundle into this Bundle.
- *
- * @param bundle a PersistableBundle
- */
- @Override
- public void putAll(PersistableBundle bundle) {
- super.putAll(bundle);
- }
-
- /**
- * Returns a Set containing the Strings used as keys in this PersistableBundle.
- *
- * @return a Set of String keys
- */
- @Override
- public Set<String> keySet() {
- return super.keySet();
- }
-
- /**
- * Inserts an int value into the mapping of this PersistableBundle, replacing
- * any existing value for the given key.
- *
- * @param key a String, or null
- * @param value an int, or null
- */
- @Override
- public void putInt(String key, int value) {
- super.putInt(key, value);
- }
-
- /**
- * Inserts a long value into the mapping of this PersistableBundle, replacing
- * any existing value for the given key.
- *
- * @param key a String, or null
- * @param value a long
- */
- @Override
- public void putLong(String key, long value) {
- super.putLong(key, value);
- }
-
- /**
- * Inserts a double value into the mapping of this PersistableBundle, replacing
- * any existing value for the given key.
- *
- * @param key a String, or null
- * @param value a double
- */
- @Override
- public void putDouble(String key, double value) {
- super.putDouble(key, value);
- }
-
- /**
- * Inserts a String value into the mapping of this PersistableBundle, replacing
- * any existing value for the given key. Either key or value may be null.
- *
- * @param key a String, or null
- * @param value a String, or null
- */
- @Override
- public void putString(String key, String value) {
- super.putString(key, value);
- }
-
- /**
- * Inserts an int array value into the mapping of this PersistableBundle, replacing
- * any existing value for the given key. Either key or value may be null.
- *
- * @param key a String, or null
- * @param value an int array object, or null
- */
- @Override
- public void putIntArray(String key, int[] value) {
- super.putIntArray(key, value);
- }
-
- /**
- * Inserts a long array value into the mapping of this PersistableBundle, replacing
- * any existing value for the given key. Either key or value may be null.
- *
- * @param key a String, or null
- * @param value a long array object, or null
- */
- @Override
- public void putLongArray(String key, long[] value) {
- super.putLongArray(key, value);
- }
-
- /**
- * Inserts a double array value into the mapping of this PersistableBundle, replacing
- * any existing value for the given key. Either key or value may be null.
- *
- * @param key a String, or null
- * @param value a double array object, or null
- */
- @Override
- public void putDoubleArray(String key, double[] value) {
- super.putDoubleArray(key, value);
- }
-
- /**
- * Inserts a String array value into the mapping of this PersistableBundle, replacing
- * any existing value for the given key. Either key or value may be null.
- *
- * @param key a String, or null
- * @param value a String array object, or null
- */
- @Override
- public void putStringArray(String key, String[] value) {
- super.putStringArray(key, value);
- }
-
- /**
* Inserts a PersistableBundle value into the mapping of this Bundle, replacing
* any existing value for the given key. Either key or value may be null.
*
* @param key a String, or null
* @param value a Bundle object, or null
*/
- @Override
public void putPersistableBundle(String key, PersistableBundle value) {
- super.putPersistableBundle(key, value);
- }
-
- /**
- * Returns the value associated with the given key, or 0 if
- * no mapping of the desired type exists for the given key.
- *
- * @param key a String
- * @return an int value
- */
- @Override
- public int getInt(String key) {
- return super.getInt(key);
- }
-
- /**
- * Returns the value associated with the given key, or defaultValue if
- * no mapping of the desired type exists for the given key.
- *
- * @param key a String
- * @param defaultValue Value to return if key does not exist
- * @return an int value
- */
- @Override
- public int getInt(String key, int defaultValue) {
- return super.getInt(key, defaultValue);
- }
-
- /**
- * Returns the value associated with the given key, or 0L if
- * no mapping of the desired type exists for the given key.
- *
- * @param key a String
- * @return a long value
- */
- @Override
- public long getLong(String key) {
- return super.getLong(key);
- }
-
- /**
- * Returns the value associated with the given key, or defaultValue if
- * no mapping of the desired type exists for the given key.
- *
- * @param key a String
- * @param defaultValue Value to return if key does not exist
- * @return a long value
- */
- @Override
- public long getLong(String key, long defaultValue) {
- return super.getLong(key, defaultValue);
- }
-
- /**
- * Returns the value associated with the given key, or 0.0 if
- * no mapping of the desired type exists for the given key.
- *
- * @param key a String
- * @return a double value
- */
- @Override
- public double getDouble(String key) {
- return super.getDouble(key);
- }
-
- /**
- * Returns the value associated with the given key, or defaultValue if
- * no mapping of the desired type exists for the given key.
- *
- * @param key a String
- * @param defaultValue Value to return if key does not exist
- * @return a double value
- */
- @Override
- public double getDouble(String key, double defaultValue) {
- return super.getDouble(key, defaultValue);
- }
-
- /**
- * Returns the value associated with the given key, or null if
- * no mapping of the desired type exists for the given key or a null
- * value is explicitly associated with the key.
- *
- * @param key a String, or null
- * @return a String value, or null
- */
- @Override
- public String getString(String key) {
- return super.getString(key);
- }
-
- /**
- * Returns the value associated with the given key, or defaultValue if
- * no mapping of the desired type exists for the given key.
- *
- * @param key a String, or null
- * @param defaultValue Value to return if key does not exist
- * @return the String value associated with the given key, or defaultValue
- * if no valid String object is currently mapped to that key.
- */
- @Override
- public String getString(String key, String defaultValue) {
- return super.getString(key, defaultValue);
+ unparcel();
+ mMap.put(key, value);
}
/**
@@ -479,61 +147,18 @@
* @param key a String, or null
* @return a Bundle value, or null
*/
- @Override
public PersistableBundle getPersistableBundle(String key) {
- return super.getPersistableBundle(key);
- }
-
- /**
- * Returns the value associated with the given key, or null if
- * no mapping of the desired type exists for the given key or a null
- * value is explicitly associated with the key.
- *
- * @param key a String, or null
- * @return an int[] value, or null
- */
- @Override
- public int[] getIntArray(String key) {
- return super.getIntArray(key);
- }
-
- /**
- * Returns the value associated with the given key, or null if
- * no mapping of the desired type exists for the given key or a null
- * value is explicitly associated with the key.
- *
- * @param key a String, or null
- * @return a long[] value, or null
- */
- @Override
- public long[] getLongArray(String key) {
- return super.getLongArray(key);
- }
-
- /**
- * Returns the value associated with the given key, or null if
- * no mapping of the desired type exists for the given key or a null
- * value is explicitly associated with the key.
- *
- * @param key a String, or null
- * @return a double[] value, or null
- */
- @Override
- public double[] getDoubleArray(String key) {
- return super.getDoubleArray(key);
- }
-
- /**
- * Returns the value associated with the given key, or null if
- * no mapping of the desired type exists for the given key or a null
- * value is explicitly associated with the key.
- *
- * @param key a String, or null
- * @return a String[] value, or null
- */
- @Override
- public String[] getStringArray(String key) {
- return super.getStringArray(key);
+ unparcel();
+ Object o = mMap.get(key);
+ if (o == null) {
+ return null;
+ }
+ try {
+ return (PersistableBundle) o;
+ } catch (ClassCastException e) {
+ typeWarning(key, o, "Bundle", e);
+ return null;
+ }
}
public static final Parcelable.Creator<PersistableBundle> CREATOR =
@@ -549,38 +174,6 @@
}
};
- /**
- * Report the nature of this Parcelable's contents
- */
- @Override
- public int describeContents() {
- return 0;
- }
-
- /**
- * Writes the PersistableBundle contents to a Parcel, typically in order for
- * it to be passed through an IBinder connection.
- * @param parcel The parcel to copy this bundle to.
- */
- @Override
- public void writeToParcel(Parcel parcel, int flags) {
- final boolean oldAllowFds = parcel.pushAllowFds(false);
- try {
- super.writeToParcelInner(parcel, flags);
- } finally {
- parcel.restoreAllowFds(oldAllowFds);
- }
- }
-
- /**
- * Reads the Parcel contents into this PersistableBundle, typically in order for
- * it to be passed through an IBinder connection.
- * @param parcel The parcel to overwrite this bundle from.
- */
- public void readFromParcel(Parcel parcel) {
- super.readFromParcelInner(parcel);
- }
-
/** @hide */
@Override
public void writeUnknownObject(Object v, String name, XmlSerializer out)
@@ -614,8 +207,29 @@
}
/**
- * @hide
+ * Report the nature of this Parcelable's contents
*/
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ /**
+ * Writes the PersistableBundle contents to a Parcel, typically in order for
+ * it to be passed through an IBinder connection.
+ * @param parcel The parcel to copy this bundle to.
+ */
+ @Override
+ public void writeToParcel(Parcel parcel, int flags) {
+ final boolean oldAllowFds = parcel.pushAllowFds(false);
+ try {
+ writeToParcelInner(parcel, flags);
+ } finally {
+ parcel.restoreAllowFds(oldAllowFds);
+ }
+ }
+
+ /** @hide */
public static PersistableBundle restoreFromXml(XmlPullParser in) throws IOException,
XmlPullParserException {
final int outerDepth = in.getDepth();
diff --git a/core/java/android/os/storage/StorageManager.java b/core/java/android/os/storage/StorageManager.java
index 4963991..68b91cb 100644
--- a/core/java/android/os/storage/StorageManager.java
+++ b/core/java/android/os/storage/StorageManager.java
@@ -58,24 +58,6 @@
* argument of {@link android.content.Context#STORAGE_SERVICE}.
*/
public class StorageManager {
-
- /// Consts to match the password types in cryptfs.h
- /** Master key is encrypted with a password.
- */
- public static final int CRYPT_TYPE_PASSWORD = 0;
-
- /** Master key is encrypted with the default password.
- */
- public static final int CRYPT_TYPE_DEFAULT = 1;
-
- /** Master key is encrypted with a pattern.
- */
- public static final int CRYPT_TYPE_PATTERN = 2;
-
- /** Master key is encrypted with a pin.
- */
- public static final int CRYPT_TYPE_PIN = 3;
-
private static final String TAG = "StorageManager";
private final ContentResolver mResolver;
@@ -663,4 +645,14 @@
return Settings.Global.getLong(mResolver, Settings.Global.SYS_STORAGE_FULL_THRESHOLD_BYTES,
DEFAULT_FULL_THRESHOLD_BYTES);
}
+
+ /// Consts to match the password types in cryptfs.h
+ /** @hide */
+ public static final int CRYPT_TYPE_PASSWORD = 0;
+ /** @hide */
+ public static final int CRYPT_TYPE_DEFAULT = 1;
+ /** @hide */
+ public static final int CRYPT_TYPE_PATTERN = 2;
+ /** @hide */
+ public static final int CRYPT_TYPE_PIN = 3;
}
diff --git a/core/java/android/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..1e29f8e 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;
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/IWindowManager.aidl b/core/java/android/view/IWindowManager.aidl
index 34d1f0e..af16185 100644
--- a/core/java/android/view/IWindowManager.aidl
+++ b/core/java/android/view/IWindowManager.aidl
@@ -120,6 +120,7 @@
boolean isKeyguardSecure();
boolean inKeyguardRestrictedInputMode();
void dismissKeyguard();
+ void keyguardGoingAway();
void closeSystemDialogs(String reason);
diff --git a/core/java/android/view/RenderNode.java b/core/java/android/view/RenderNode.java
index cf125bc..e63829e 100644
--- a/core/java/android/view/RenderNode.java
+++ b/core/java/android/view/RenderNode.java
@@ -850,6 +850,13 @@
nOutput(mNativeRenderNode);
}
+ /**
+ * Gets the size of the DisplayList for debug purposes.
+ */
+ public int getDebugSize() {
+ return nGetDebugSize(mNativeRenderNode);
+ }
+
///////////////////////////////////////////////////////////////////////////
// Animations
///////////////////////////////////////////////////////////////////////////
@@ -941,6 +948,7 @@
private static native float nGetPivotX(long renderNode);
private static native float nGetPivotY(long renderNode);
private static native void nOutput(long renderNode);
+ private static native int nGetDebugSize(long renderNode);
///////////////////////////////////////////////////////////////////////////
// Animations
diff --git a/core/java/android/view/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 fc7bf0e..aa06d15 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -5329,7 +5329,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/WindowManagerPolicy.java b/core/java/android/view/WindowManagerPolicy.java
index 20194eb..d45d686 100644
--- a/core/java/android/view/WindowManagerPolicy.java
+++ b/core/java/android/view/WindowManagerPolicy.java
@@ -608,8 +608,15 @@
* Return whether the given window should forcibly hide everything
* behind it. Typically returns true for the keyguard.
*/
- public boolean doesForceHide(WindowState win, WindowManager.LayoutParams attrs);
-
+ public boolean doesForceHide(WindowManager.LayoutParams attrs);
+
+
+ /**
+ * Return whether the given window can become one that passes doesForceHide() test.
+ * Typically returns true for the StatusBar.
+ */
+ public boolean isKeyguardHostWindow(WindowManager.LayoutParams attrs);
+
/**
* Determine if a window that is behind one that is force hiding
* (as determined by {@link #doesForceHide}) should actually be hidden.
@@ -618,7 +625,7 @@
* will conflict with what you set.
*/
public boolean canBeForceHidden(WindowState win, WindowManager.LayoutParams attrs);
-
+
/**
* Called when the system would like to show a UI to indicate that an
* application is starting. You can use this to add a
@@ -1149,12 +1156,6 @@
public void setLastInputMethodWindowLw(WindowState ime, WindowState target);
/**
- * Show the recents task list app.
- * @hide
- */
- public void showRecentApps();
-
- /**
* @return The current height of the input method window.
*/
public int getInputMethodWindowVisibleHeightLw();
@@ -1195,4 +1196,9 @@
* @return True if the window is a top level one.
*/
public boolean isTopLevelWindow(int windowType);
+
+ /**
+ * Notifies the keyguard to start fading out.
+ */
+ public void startKeyguardExitAnimation(long fadeoutDuration);
}
diff --git a/core/java/android/widget/Toolbar.java b/core/java/android/widget/Toolbar.java
index 8c67bb7..5033bee 100644
--- a/core/java/android/widget/Toolbar.java
+++ b/core/java/android/widget/Toolbar.java
@@ -592,7 +592,7 @@
*/
public void setNavigationContentDescription(int resId) {
ensureNavButtonView();
- mNavButtonView.setContentDescription(getContext().getText(resId));
+ mNavButtonView.setContentDescription(resId != 0 ? getContext().getText(resId) : null);
}
/**
diff --git a/core/java/com/android/internal/app/WindowDecorActionBar.java b/core/java/com/android/internal/app/WindowDecorActionBar.java
index a0c75a6..c0b5b97 100644
--- a/core/java/com/android/internal/app/WindowDecorActionBar.java
+++ b/core/java/com/android/internal/app/WindowDecorActionBar.java
@@ -342,7 +342,7 @@
@Override
public void setCustomView(int resId) {
setCustomView(LayoutInflater.from(getThemedContext()).inflate(resId,
- (ViewGroup) mDecorToolbar, false));
+ mDecorToolbar.getViewGroup(), false));
}
@Override
@@ -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/os/HandlerCaller.java b/core/java/com/android/internal/os/HandlerCaller.java
index 40834ba..17685fd 100644
--- a/core/java/com/android/internal/os/HandlerCaller.java
+++ b/core/java/com/android/internal/os/HandlerCaller.java
@@ -22,9 +22,6 @@
import android.os.Message;
public class HandlerCaller {
-
- public final Context mContext;
-
final Looper mMainLooper;
final Handler mH;
@@ -47,7 +44,6 @@
public HandlerCaller(Context context, Looper looper, Callback callback,
boolean asyncHandler) {
- mContext = context;
mMainLooper = looper != null ? looper : context.getMainLooper();
mH = new MyHandler(mMainLooper, asyncHandler);
mCallback = callback;
diff --git a/core/java/com/android/internal/policy/IKeyguardService.aidl b/core/java/com/android/internal/policy/IKeyguardService.aidl
index b78c70f..f22800c 100644
--- a/core/java/com/android/internal/policy/IKeyguardService.aidl
+++ b/core/java/com/android/internal/policy/IKeyguardService.aidl
@@ -56,4 +56,10 @@
oneway void dispatch(in MotionEvent event);
oneway void launchCamera();
oneway void onBootCompleted();
+
+ /**
+ * Notifies that the activity behind has now been drawn and it's safe to remove the wallpaper
+ * and keyguard flag.
+ */
+ oneway void startKeyguardExitAnimation(long fadeoutDuration);
}
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/jni/Android.mk b/core/jni/Android.mk
index a159715..f446c3a 100644
--- a/core/jni/Android.mk
+++ b/core/jni/Android.mk
@@ -173,8 +173,10 @@
$(call include-path-for, bluedroid) \
$(call include-path-for, libhardware)/hardware \
$(call include-path-for, libhardware_legacy)/hardware_legacy \
+ $(TOP)/bionic/libc/dns/include \
$(TOP)/frameworks/av/include \
$(TOP)/system/media/camera/include \
+ $(TOP)/system/netd/include \
external/pdfium/core/include/fpdfapi \
external/pdfium/core/include/fpdfdoc \
external/pdfium/fpdfsdk/include \
@@ -233,6 +235,7 @@
libaudioutils \
libpdfium \
libimg_utils \
+ libnetd_client \
ifeq ($(USE_OPENGL_RENDERER),true)
LOCAL_SHARED_LIBRARIES += libhwui
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/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_net_NetUtils.cpp b/core/jni/android_net_NetUtils.cpp
index 6d23c32..bc5e1b3 100644
--- a/core/jni/android_net_NetUtils.cpp
+++ b/core/jni/android_net_NetUtils.cpp
@@ -18,6 +18,8 @@
#include "jni.h"
#include "JNIHelp.h"
+#include "NetdClient.h"
+#include "resolv_netid.h"
#include <utils/misc.h>
#include <android_runtime/AndroidRuntime.h>
#include <utils/Log.h>
@@ -250,6 +252,36 @@
}
}
+static void android_net_utils_bindProcessToNetwork(JNIEnv *env, jobject thiz, jint netId)
+{
+ setNetworkForProcess(netId);
+}
+
+static void android_net_utils_unbindProcessToNetwork(JNIEnv *env, jobject thiz)
+{
+ setNetworkForProcess(NETID_UNSET);
+}
+
+static jint android_net_utils_getNetworkBoundToProcess(JNIEnv *env, jobject thiz)
+{
+ return getNetworkForProcess();
+}
+
+static void android_net_utils_bindProcessToNetworkForHostResolution(JNIEnv *env, jobject thiz, jint netId)
+{
+ setNetworkForResolv(netId);
+}
+
+static void android_net_utils_unbindProcessToNetworkForHostResolution(JNIEnv *env, jobject thiz)
+{
+ setNetworkForResolv(NETID_UNSET);
+}
+
+static void android_net_utils_bindSocketToNetwork(JNIEnv *env, jobject thiz, jint socket, jint netId)
+{
+ setNetworkForSocket(netId, socket);
+}
+
// ----------------------------------------------------------------------------
/*
@@ -267,6 +299,12 @@
{ "releaseDhcpLease", "(Ljava/lang/String;)Z", (void *)android_net_utils_releaseDhcpLease },
{ "getDhcpError", "()Ljava/lang/String;", (void*) android_net_utils_getDhcpError },
{ "markSocket", "(II)V", (void*) android_net_utils_markSocket },
+ { "bindProcessToNetwork", "(I)V", (void*) android_net_utils_bindProcessToNetwork },
+ { "getNetworkBoundToProcess", "()I", (void*) android_net_utils_getNetworkBoundToProcess },
+ { "unbindProcessToNetwork", "()V", (void*) android_net_utils_unbindProcessToNetwork },
+ { "bindProcessToNetworkForHostResolution", "(I)V", (void*) android_net_utils_bindProcessToNetworkForHostResolution },
+ { "unbindProcessToNetworkForHostResolution", "()V", (void*) android_net_utils_unbindProcessToNetworkForHostResolution },
+ { "bindSocketToNetwork", "(II)V", (void*) android_net_utils_bindSocketToNetwork },
};
int register_android_net_NetworkUtils(JNIEnv* env)
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/anim/lock_screen_behind_enter.xml b/core/res/res/anim/lock_screen_behind_enter.xml
index cb47b3c..4a956d7 100644
--- a/core/res/res/anim/lock_screen_behind_enter.xml
+++ b/core/res/res/anim/lock_screen_behind_enter.xml
@@ -20,9 +20,8 @@
<set xmlns:android="http://schemas.android.com/apk/res/android"
android:background="#ff000000" android:shareInterpolator="false">
<alpha
- android:fromAlpha="0.0" android:toAlpha="1.0"
+ android:fromAlpha="1.0" android:toAlpha="1.0"
android:fillEnabled="true" android:fillBefore="true"
android:interpolator="@interpolator/decelerate_quint"
- android:startOffset="@android:integer/config_shortAnimTime"
- android:duration="@android:integer/config_shortAnimTime"/>
+ android:duration="0"/>
</set>
\ No newline at end of file
diff --git a/core/res/res/anim/lock_screen_wallpaper_behind_enter.xml b/core/res/res/anim/lock_screen_wallpaper_behind_enter.xml
index c29fd1a..f7a6a65 100644
--- a/core/res/res/anim/lock_screen_wallpaper_behind_enter.xml
+++ b/core/res/res/anim/lock_screen_wallpaper_behind_enter.xml
@@ -23,6 +23,6 @@
android:fromAlpha="0.0" android:toAlpha="1.0"
android:fillEnabled="true" android:fillBefore="true"
android:interpolator="@interpolator/decelerate_quad"
- android:startOffset="@android:integer/config_shortAnimTime"
+ android:startOffset="@android:integer/config_mediumAnimTime"
android:duration="@android:integer/config_shortAnimTime"/>
</set>
diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml
index 7dc967c..2d5477c 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -2252,6 +2252,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" />
@@ -2268,6 +2269,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 933063f..a0b3b63 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/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/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/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..d964efc 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();
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/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 da85d448..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/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/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/CameraMetadataTest.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/CameraMetadataTest.java
index fe51215..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",
@@ -1032,11 +1049,14 @@
float[] redOut = mMetadata.get(CaptureResult.TONEMAP_CURVE_RED);
float[] greenOut = mMetadata.get(CaptureResult.TONEMAP_CURVE_GREEN);
float[] blueOut = mMetadata.get(CaptureResult.TONEMAP_CURVE_BLUE);
- assertTrue("Input and output tonemap curve should match", Arrays.equals(red, redOut));
- assertTrue("Input and output tonemap curve should match", Arrays.equals(green, greenOut));
- assertTrue("Input and output tonemap curve should match", Arrays.equals(blue, blueOut));
+ assertArrayEquals(red, redOut);
+ assertArrayEquals(green, greenOut);
+ assertArrayEquals(blue, blueOut);
TonemapCurve tcOut = mMetadata.get(CaptureResult.TONEMAP_CURVE);
- assertTrue("Input and output tonemap curve should match", tcIn.equals(tcOut));
+ assertEquals(tcIn, tcOut);
+ mMetadata.set(CaptureResult.TONEMAP_CURVE_GREEN, null);
+ // If any of channel has null curve, return a null TonemapCurve
+ assertNull(mMetadata.get(CaptureResult.TONEMAP_CURVE));
}
/**
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/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/volume_panel.xml b/packages/SystemUI/res/layout/volume_panel.xml
index bc7288d..046862f 100644
--- a/packages/SystemUI/res/layout/volume_panel.xml
+++ b/packages/SystemUI/res/layout/volume_panel.xml
@@ -24,6 +24,7 @@
android:id="@+id/slider_panel"
android:layout_width="match_parent"
android:layout_height="wrap_content"
+ android:minHeight="64dip"
android:layout_toLeftOf="@+id/expand_button_divider" />
<ImageView
diff --git a/packages/SystemUI/res/layout/zen_mode_condition.xml b/packages/SystemUI/res/layout/zen_mode_condition.xml
index 8b344000..6d63bb0 100644
--- a/packages/SystemUI/res/layout/zen_mode_condition.xml
+++ b/packages/SystemUI/res/layout/zen_mode_condition.xml
@@ -16,11 +16,14 @@
-->
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
- android:layout_height="wrap_content" >
+ android:layout_height="wrap_content"
+ android:layout_marginLeft="@dimen/zen_mode_condition_detail_button_padding"
+ android:layout_marginRight="@dimen/zen_mode_condition_detail_button_padding" >
<RadioButton
android:id="@android:id/checkbox"
- android:layout_width="32dp"
+ android:layout_width="40dp"
+ android:layout_marginStart="2dp"
android:layout_height="@dimen/zen_mode_condition_height"
android:layout_alignParentStart="true"
android:gravity="center" />
diff --git a/packages/SystemUI/res/layout/zen_mode_panel.xml b/packages/SystemUI/res/layout/zen_mode_panel.xml
index ae04bf5..0a8f852 100644
--- a/packages/SystemUI/res/layout/zen_mode_panel.xml
+++ b/packages/SystemUI/res/layout/zen_mode_panel.xml
@@ -21,7 +21,7 @@
android:layout_height="wrap_content"
android:background="@color/system_primary_color"
android:orientation="vertical"
- android:padding="@dimen/qs_panel_padding" >
+ android:paddingTop="@dimen/qs_panel_padding" >
<TextView
android:id="@android:id/title"
@@ -29,8 +29,10 @@
android:layout_height="wrap_content"
android:layout_alignParentStart="true"
android:layout_marginBottom="8dp"
+ android:layout_marginStart="@dimen/qs_panel_padding"
+ android:layout_marginEnd="@dimen/qs_panel_padding"
android:text="@string/zen_mode_title"
- android:textAppearance="@style/TextAppearance.QS.DetailHeader" />
+ android:textAppearance="@style/TextAppearance.QS.DetailItemPrimary" />
<LinearLayout
android:id="@android:id/content"
@@ -44,6 +46,7 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentEnd="true"
+ android:layout_marginEnd="4dp"
android:layout_gravity="end"
android:text="@string/quick_settings_more_settings"
android:textAppearance="@style/TextAppearance.QS.DetailButton" />
diff --git a/packages/SystemUI/res/values-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/keyguard/KeyguardService.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java
index 41c0e78..4c7f3df 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java
@@ -202,6 +202,12 @@
checkPermission();
mKeyguardViewMediator.onBootCompleted();
}
+
+ @Override
+ public void startKeyguardExitAnimation(long fadeoutDuration) {
+ checkPermission();
+ mKeyguardViewMediator.startKeyguardExitAnimation(fadeoutDuration);
+ }
};
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
index b2872fa..7110d8d 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
@@ -46,7 +46,8 @@
import android.util.Log;
import android.util.Slog;
import android.view.ViewGroup;
-import android.view.WindowManager;
+import android.view.IWindowManager;
+import android.view.WindowManagerGlobal;
import android.view.WindowManagerPolicy;
import com.android.internal.policy.IKeyguardExitCallback;
@@ -137,6 +138,7 @@
private static final int SET_OCCLUDED = 12;
private static final int KEYGUARD_TIMEOUT = 13;
private static final int DISMISS = 17;
+ private static final int START_KEYGUARD_EXIT_ANIM = 18;
/**
* The default amount of time we stay awake (used for all key input)
@@ -180,6 +182,9 @@
/** High level access to the power manager for WakeLocks */
private PowerManager mPM;
+ /** High level access to the window manager for dismissing keyguard animation */
+ private IWindowManager mWM;
+
/** UserManager for querying number of users */
private UserManager mUserManager;
@@ -440,6 +445,7 @@
private void setup() {
mPM = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
+ mWM = WindowManagerGlobal.getWindowManagerService();
mUserManager = (UserManager) mContext.getSystemService(Context.USER_SERVICE);
mShowKeyguardWakeLock = mPM.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "show keyguard");
mShowKeyguardWakeLock.setReferenceCounted(false);
@@ -1076,6 +1082,9 @@
case DISMISS:
handleDismiss();
break;
+ case START_KEYGUARD_EXIT_ANIM:
+ handleStartKeyguardExitAnimation((Long) msg.obj);
+ break;
}
}
};
@@ -1207,6 +1216,19 @@
private void handleHide() {
synchronized (KeyguardViewMediator.this) {
if (DEBUG) Log.d(TAG, "handleHide");
+ try {
+
+ // Don't actually hide the Keyguard at the moment, wait for window manager until
+ // it tells us it's safe to do so with startKeyguardExitAnimation.
+ mWM.keyguardGoingAway();
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error while calling WindowManager", e);
+ }
+ }
+ }
+
+ private void handleStartKeyguardExitAnimation(long fadeoutDuration) {
+ synchronized (KeyguardViewMediator.this) {
// only play "unlock" noises if not on a call (since the incall UI
// disables the keyguard)
@@ -1324,6 +1346,11 @@
return mStatusBarKeyguardViewManager;
}
+ public void startKeyguardExitAnimation(long fadeoutDuration) {
+ Message msg = mHandler.obtainMessage(START_KEYGUARD_EXIT_ANIM, fadeoutDuration);
+ mHandler.sendMessage(msg);
+ }
+
public ViewMediatorCallback getViewMediatorCallback() {
return mViewMediatorCallback;
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
index bdac7a0..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);
@@ -191,15 +201,23 @@
final int ch = record.row == 0 ? mLargeCellHeight : mCellHeight;
record.tileView.measure(exactly(cw), exactly(ch));
}
- final int actualHeight = rows == 0 ? 0 : getRowTop(rows);
- mDetail.measure(exactly(width), exactly(actualHeight));
- setMeasuredDimension(width, actualHeight);
+ int h = rows == 0 ? 0 : getRowTop(rows);
+ mDetail.measure(exactly(width), unspecified());
+ if (mDetail.getVisibility() == VISIBLE && mDetail.getChildCount() > 0) {
+ final int dmh = mDetail.getMeasuredHeight();
+ if (dmh > 0) h = Math.max(h, dmh);
+ }
+ setMeasuredDimension(width, h);
}
private static int exactly(int size) {
return MeasureSpec.makeMeasureSpec(size, MeasureSpec.EXACTLY);
}
+ private static int unspecified() {
+ return MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED);
+ }
+
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
final int w = getWidth();
@@ -214,7 +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) {
@@ -231,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;
@@ -257,4 +282,8 @@
mDetailRecord = null;
};
};
+
+ public interface Callback {
+ void onShowingDetail(boolean showingDetail);
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/NotificationsTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/NotificationsTile.java
index 267786b..20bbf8b 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/NotificationsTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/NotificationsTile.java
@@ -108,11 +108,7 @@
@Override
protected void handleClick() {
- if (mState.zen) {
- mZenController.setZen(false);
- } else {
- showDetail(true);
- }
+ showDetail(true);
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/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 fbe76f9..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();
@@ -182,6 +186,7 @@
*/
protected int mState;
protected boolean mBouncerShowing;
+ protected boolean mShowLockscreenNotifications;
protected NotificationOverflowContainer mKeyguardIconOverflowContainer;
@@ -201,6 +206,9 @@
final int mode = Settings.Global.getInt(mContext.getContentResolver(),
Settings.Global.ZEN_MODE, Settings.Global.ZEN_MODE_OFF);
setZenMode(mode);
+ final boolean show = Settings.Global.getInt(mContext.getContentResolver(),
+ Settings.Global.LOCK_SCREEN_SHOW_NOTIFICATIONS, 1) != 0;
+ setShowLockscreenNotifications(show);
}
};
@@ -224,7 +232,7 @@
}
final boolean isActivity = pendingIntent.isActivity();
if (isActivity) {
- startNotificationActivity(new OnDismissAction() {
+ dismissKeyguardThenExecute(new OnDismissAction() {
@Override
public boolean onDismiss() {
try {
@@ -246,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;
@@ -328,8 +337,7 @@
mHandler.post(new Runnable() {
@Override
public void run() {
- mNotificationData.updateRanking(currentRanking);
- updateNotifications();
+ updateRankingInternal(currentRanking);
}
});
}
@@ -362,6 +370,9 @@
mContext.getContentResolver().registerContentObserver(
Settings.Global.getUriFor(Settings.Global.ZEN_MODE), false,
mSettingsObserver);
+ mContext.getContentResolver().registerContentObserver(
+ Settings.Global.getUriFor(Settings.Global.LOCK_SCREEN_SHOW_NOTIFICATIONS), false,
+ mSettingsObserver);
mContext.getContentResolver().registerContentObserver(
Settings.Secure.getUriFor(Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS),
@@ -472,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();
}
@@ -1042,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
@@ -1062,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;
@@ -1087,7 +1098,7 @@
visibilityChanged(false);
boolean waitForActivityLaunch = sent && mIntent.isActivity();
- return waitForActivityLaunch;
+ return waitForActivityLaunch && DELAY_DISMISS_TO_ACTIVITY_LAUNCH;
}
});
}
@@ -1247,6 +1258,10 @@
updateNotifications();
}
+ protected void setShowLockscreenNotifications(boolean show) {
+ mShowLockscreenNotifications = show;
+ }
+
protected abstract void haltTicker();
protected abstract void setAreThereNotifications();
protected abstract void updateNotifications();
@@ -1264,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) {
@@ -1271,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 0c27ab0..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;
@@ -164,7 +167,11 @@
// Calculate quick setting heights.
mQsMinExpansionHeight = mHeader.getCollapsedHeight() + mQsPeekHeight;
mQsMaxExpansionHeight = mHeader.getExpandedHeight() + mQsContainer.getHeight();
- if (!mQsExpanded) {
+ if (mQsExpanded) {
+ if (mQsFullyExpanded) {
+ setQsStackScrollerPadding(mQsMaxExpansionHeight);
+ }
+ } else {
setQsExpansion(mQsMinExpansionHeight);
positionClockAndNotifications();
mNotificationStackScroller.setStackHeight(getExpandedHeight());
@@ -520,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) {
@@ -699,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 5dcd61c..f9afcf3 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
@@ -505,6 +505,12 @@
}
@Override
+ protected void setShowLockscreenNotifications(boolean show) {
+ super.setShowLockscreenNotifications(show);
+ updateStackScrollerState();
+ }
+
+ @Override
public void start() {
mDisplay = ((WindowManager)mContext.getSystemService(Context.WINDOW_SERVICE))
.getDefaultDisplay();
@@ -1044,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;
@@ -1108,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);
@@ -2342,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() {
@@ -2412,7 +2433,7 @@
};
@Override
- protected void startNotificationActivity(OnDismissAction action) {
+ protected void dismissKeyguardThenExecute(OnDismissAction action) {
if (mStatusBarKeyguardViewManager.isShowing()) {
mStatusBarKeyguardViewManager.dismissWithAction(action);
} else {
@@ -2851,7 +2872,10 @@
}
public void updateStackScrollerState() {
+ if (mStackScroller == null) return;
mStackScroller.setDimmed(mState == StatusBarState.KEYGUARD, false /* animate */);
+ mStackScroller.setVisibility(!mShowLockscreenNotifications && mState == StatusBarState.KEYGUARD
+ ? View.INVISIBLE : View.VISIBLE);
}
public void userActivity() {
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 06f4c2e..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() {
@@ -1035,8 +1056,8 @@
if (isShowing()) {
if (mDialog != null) {
mDialog.dismiss();
+ mActiveStreamType = -1;
}
- mActiveStreamType = -1;
}
synchronized (sConfirmSafeVolumeLock) {
if (sConfirmSafeVolumeDialog != null) {
@@ -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/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 0754b12..c483836 100644
--- a/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java
+++ b/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java
@@ -483,7 +483,6 @@
private static final int MSG_DISABLE_POINTER_LOCATION = 2;
private static final int MSG_DISPATCH_MEDIA_KEY_WITH_WAKE_LOCK = 3;
private static final int MSG_DISPATCH_MEDIA_KEY_REPEAT_WITH_WAKE_LOCK = 4;
- private static final int MSG_DISPATCH_SHOW_RECENTS = 5;
private class PolicyHandler extends Handler {
@Override
@@ -501,9 +500,6 @@
case MSG_DISPATCH_MEDIA_KEY_REPEAT_WITH_WAKE_LOCK:
dispatchMediaKeyRepeatWithWakeLock((KeyEvent)msg.obj);
break;
- case MSG_DISPATCH_SHOW_RECENTS:
- showRecentApps(false);
- break;
}
}
}
@@ -1576,11 +1572,16 @@
}
@Override
- public boolean doesForceHide(WindowState win, WindowManager.LayoutParams attrs) {
+ public boolean doesForceHide(WindowManager.LayoutParams attrs) {
return (attrs.privateFlags & PRIVATE_FLAG_KEYGUARD) != 0;
}
@Override
+ public boolean isKeyguardHostWindow(WindowManager.LayoutParams attrs) {
+ return attrs.type == TYPE_STATUS_BAR;
+ }
+
+ @Override
public boolean canBeForceHidden(WindowState win, WindowManager.LayoutParams attrs) {
switch (attrs.type) {
case TYPE_STATUS_BAR:
@@ -2467,12 +2468,6 @@
}
}
- @Override
- public void showRecentApps() {
- mHandler.removeMessages(MSG_DISPATCH_SHOW_RECENTS);
- mHandler.sendEmptyMessage(MSG_DISPATCH_SHOW_RECENTS);
- }
-
private void showRecentApps(boolean triggeredFromAltTab) {
mPreloadedRecentApps = false; // preloading no longer needs to be canceled
try {
@@ -3353,7 +3348,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;
@@ -4570,6 +4570,18 @@
}
}
+ @Override
+ public void startKeyguardExitAnimation(final long fadeoutDuration) {
+ if (mKeyguardDelegate != null) {
+ mHandler.post(new Runnable() {
+ @Override
+ public void run() {
+ mKeyguardDelegate.startKeyguardExitAnimation(fadeoutDuration);
+ }
+ });
+ }
+ }
+
void sendCloseSystemWindows() {
sendCloseSystemWindows(mContext, null);
}
@@ -5616,6 +5628,7 @@
pw.print(prefix); pw.print("mDemoHdmiRotation="); pw.print(mDemoHdmiRotation);
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);
diff --git a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardServiceDelegate.java b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardServiceDelegate.java
index 966924b..faf7020 100644
--- a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardServiceDelegate.java
+++ b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardServiceDelegate.java
@@ -274,6 +274,12 @@
mKeyguardState.currentUser = newUserId;
}
+ public void startKeyguardExitAnimation(long fadeoutDuration) {
+ if (mKeyguardService != null) {
+ mKeyguardService.startKeyguardExitAnimation(fadeoutDuration);
+ }
+ }
+
private static final View createScrim(Context context) {
View view = new View(context);
diff --git a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardServiceWrapper.java b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardServiceWrapper.java
index 7cb48fa..f236ce7 100644
--- a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardServiceWrapper.java
+++ b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardServiceWrapper.java
@@ -190,6 +190,14 @@
}
}
+ public void startKeyguardExitAnimation(long fadeoutDuration) {
+ try {
+ mService.startKeyguardExitAnimation(fadeoutDuration);
+ } catch (RemoteException e) {
+ Slog.w(TAG , "Remote Exception", e);
+ }
+ }
+
public void showAssistant() {
// Not used by PhoneWindowManager
}
diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java
index 1e21e1c..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);
@@ -5606,16 +5625,23 @@
boolean isNewDefault = false;
if (DBG) log("handleConnectionValidated for "+newNetwork.name());
// check if any NetworkRequest wants this NetworkAgent
- // first check if it satisfies the NetworkCapabilities
ArrayList<NetworkAgentInfo> affectedNetworks = new ArrayList<NetworkAgentInfo>();
if (VDBG) log(" new Network has: " + newNetwork.networkCapabilities);
for (NetworkRequestInfo nri : mNetworkRequests.values()) {
+ NetworkAgentInfo currentNetwork = mNetworkForRequestId.get(nri.request.requestId);
+ if (newNetwork == currentNetwork) {
+ if (VDBG) log("Network " + newNetwork.name() + " was already satisfying" +
+ " request " + nri.request.requestId + ". No change.");
+ keep = true;
+ continue;
+ }
+
+ // check if it satisfies the NetworkCapabilities
if (VDBG) log(" checking if request is satisfied: " + nri.request);
if (nri.request.networkCapabilities.satisfiedByNetworkCapabilities(
newNetwork.networkCapabilities)) {
// next check if it's better than any current network we're using for
// this request
- NetworkAgentInfo currentNetwork = mNetworkForRequestId.get(nri.request.requestId);
if (VDBG) {
log("currentScore = " +
(currentNetwork != null ? currentNetwork.currentScore : 0) +
@@ -5744,12 +5770,19 @@
}
if (state == NetworkInfo.State.CONNECTED) {
- // TODO - check if we want it (optimization)
try {
+ // This is likely caused by the fact that this network already
+ // exists. An example is when a network goes from CONNECTED to
+ // CONNECTING and back (like wifi on DHCP renew).
+ // TODO: keep track of which networks we've created, or ask netd
+ // to tell us whether we've already created this network or not.
mNetd.createNetwork(networkAgent.network.netId);
} catch (Exception e) {
- loge("Error creating Network " + networkAgent.network.netId);
+ loge("Error creating network " + networkAgent.network.netId + ": "
+ + e.getMessage());
+ return;
}
+
updateLinkProperties(networkAgent, null);
notifyNetworkCallbacks(networkAgent, ConnectivityManager.CALLBACK_PRECHECK);
networkAgent.networkMonitor.sendMessage(NetworkMonitor.CMD_NETWORK_CONNECTED);
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/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java
index 033b967..108a079 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/ActivityStack.java b/services/core/java/com/android/server/am/ActivityStack.java
index 8f60b03..1804d03 100755
--- a/services/core/java/com/android/server/am/ActivityStack.java
+++ b/services/core/java/com/android/server/am/ActivityStack.java
@@ -30,10 +30,6 @@
import static com.android.server.am.ActivityManagerService.DEBUG_VISBILITY;
import static com.android.server.am.ActivityManagerService.VALIDATE_TOKENS;
-import static com.android.server.am.ActivityRecord.HOME_ACTIVITY_TYPE;
-import static com.android.server.am.ActivityRecord.APPLICATION_ACTIVITY_TYPE;
-import static com.android.server.am.ActivityRecord.RECENTS_ACTIVITY_TYPE;
-
import static com.android.server.am.ActivityStackSupervisor.DEBUG_ADD_REMOVE;
import static com.android.server.am.ActivityStackSupervisor.DEBUG_APP;
import static com.android.server.am.ActivityStackSupervisor.DEBUG_SAVED_STATE;
@@ -1067,6 +1063,40 @@
}
}
+ /**
+ * Determine if home should be visible below the passed record.
+ * @param record activity we are querying for.
+ * @return true if home is visible below the passed activity, false otherwise.
+ */
+ boolean isActivityOverHome(ActivityRecord record) {
+ // Start at record and go down, look for either home or a visible fullscreen activity.
+ final TaskRecord recordTask = record.task;
+ for (int taskNdx = mTaskHistory.indexOf(recordTask); taskNdx >= 0; --taskNdx) {
+ TaskRecord task = mTaskHistory.get(taskNdx);
+ final ArrayList<ActivityRecord> activities = task.mActivities;
+ final int startNdx =
+ task == recordTask ? activities.indexOf(record) : activities.size() - 1;
+ for (int activityNdx = startNdx; activityNdx >= 0; --activityNdx) {
+ final ActivityRecord r = activities.get(activityNdx);
+ if (r.isHomeActivity()) {
+ return true;
+ }
+ if (!r.finishing && r.fullscreen) {
+ // Passed activity is over a fullscreen activity.
+ return false;
+ }
+ }
+ if (task.mOnTopOfHome) {
+ // Got to the bottom of a task on top of home without finding a visible fullscreen
+ // activity. Home is visible.
+ return true;
+ }
+ }
+ // Got to the bottom of this stack and still don't know. If this is over the home stack
+ // then record is over home. May not work if we ever get more than two layers.
+ return mStackSupervisor.isFrontStack(this);
+ }
+
private void setVisibile(ActivityRecord r, boolean visible) {
r.visible = visible;
mWindowManager.setAppVisibility(r.appToken, visible);
@@ -1096,8 +1126,7 @@
for (int i = mStacks.indexOf(this) + 1; i < mStacks.size(); i++) {
final ArrayList<TaskRecord> tasks = mStacks.get(i).getAllTasks();
for (int taskNdx = 0; taskNdx < tasks.size(); taskNdx++) {
- final TaskRecord task = tasks.get(taskNdx);
- final ArrayList<ActivityRecord> activities = task.mActivities;
+ final ArrayList<ActivityRecord> activities = tasks.get(taskNdx).mActivities;
for (int activityNdx = 0; activityNdx < activities.size(); activityNdx++) {
final ActivityRecord r = activities.get(activityNdx);
@@ -1108,7 +1137,7 @@
// - Full Screen Activity OR
// - On top of Home and our stack is NOT home
if (!r.finishing && r.visible && (r.fullscreen ||
- (!isHomeStack() && r.frontOfTask && task.isOverHomeStack()))) {
+ (!isHomeStack() && r.frontOfTask && tasks.get(taskNdx).mOnTopOfHome))) {
return false;
}
}
@@ -1236,7 +1265,7 @@
// At this point, nothing else needs to be shown
if (DEBUG_VISBILITY) Slog.v(TAG, "Fullscreen: at " + r);
behindFullscreen = true;
- } else if (!isHomeStack() && r.frontOfTask && task.isOverHomeStack()) {
+ } else if (!isHomeStack() && r.frontOfTask && task.mOnTopOfHome) {
if (DEBUG_VISBILITY) Slog.v(TAG, "Showing home: at " + r);
behindFullscreen = true;
}
@@ -1390,7 +1419,6 @@
final boolean userLeaving = mStackSupervisor.mUserLeaving;
mStackSupervisor.mUserLeaving = false;
- final TaskRecord prevTask = prev != null ? prev.task : null;
if (next == null) {
// There are no more activities! Let's just start up the
// Launcher...
@@ -1398,10 +1426,7 @@
if (DEBUG_STATES) Slog.d(TAG, "resumeTopActivityLocked: No more activities go home");
if (DEBUG_STACK) mStackSupervisor.validateTopActivitiesLocked();
// Only resume home if on home display
- final int returnTaskType = prevTask == null || !prevTask.isOverHomeStack() ?
- HOME_ACTIVITY_TYPE : prevTask.getTaskToReturnTo();
- return isOnHomeDisplay() &&
- mStackSupervisor.resumeHomeStackTask(returnTaskType, prev);
+ return isOnHomeDisplay() && mStackSupervisor.resumeHomeActivity(prev);
}
next.delayedResume = false;
@@ -1420,24 +1445,22 @@
}
final TaskRecord nextTask = next.task;
+ final TaskRecord prevTask = prev != null ? prev.task : null;
if (prevTask != null && prevTask.stack == this &&
- prevTask.isOverHomeStack() && prev.finishing && prev.frontOfTask) {
+ prevTask.mOnTopOfHome && prev.finishing && prev.frontOfTask) {
if (DEBUG_STACK) mStackSupervisor.validateTopActivitiesLocked();
if (prevTask == nextTask) {
prevTask.setFrontOfTask();
} else if (prevTask != topTask()) {
- // This task is going away but it was supposed to return to the home stack.
+ // This task is going away but it was supposed to return to the home task.
// Now the task above it has to return to the home task instead.
final int taskNdx = mTaskHistory.indexOf(prevTask) + 1;
- mTaskHistory.get(taskNdx).setTaskToReturnTo(HOME_ACTIVITY_TYPE);
+ mTaskHistory.get(taskNdx).mOnTopOfHome = true;
} else {
if (DEBUG_STATES && isOnHomeDisplay()) Slog.d(TAG,
"resumeTopActivityLocked: Launching home next");
// Only resume home if on home display
- final int returnTaskType = prevTask == null || !prevTask.isOverHomeStack() ?
- HOME_ACTIVITY_TYPE : prevTask.getTaskToReturnTo();
- return isOnHomeDisplay() &&
- mStackSupervisor.resumeHomeStackTask(returnTaskType, prev);
+ return isOnHomeDisplay() && mStackSupervisor.resumeHomeActivity(prev);
}
}
@@ -1808,11 +1831,10 @@
ActivityStack lastStack = mStackSupervisor.getLastStack();
final boolean fromHome = lastStack.isHomeStack();
if (!isHomeStack() && (fromHome || topTask() != task)) {
- task.setTaskToReturnTo(fromHome ?
- lastStack.topTask().taskType : APPLICATION_ACTIVITY_TYPE);
+ task.mOnTopOfHome = fromHome;
}
} else {
- task.setTaskToReturnTo(APPLICATION_ACTIVITY_TYPE);
+ task.mOnTopOfHome = false;
}
mTaskHistory.remove(task);
@@ -2357,8 +2379,8 @@
ActivityRecord next = topRunningActivityLocked(null);
if (next != r) {
final TaskRecord task = r.task;
- if (r.frontOfTask && task == topTask() && task.isOverHomeStack()) {
- mStackSupervisor.moveHomeStackTaskToTop(task.getTaskToReturnTo());
+ if (r.frontOfTask && task == topTask() && task.mOnTopOfHome) {
+ mStackSupervisor.moveHomeToTop();
}
}
ActivityRecord top = mStackSupervisor.topRunningActivityLocked();
@@ -2842,9 +2864,8 @@
if (task != null && task.removeActivity(r)) {
if (DEBUG_STACK) Slog.i(TAG,
"removeActivityFromHistoryLocked: last activity removed from " + this);
- if (mStackSupervisor.isFrontStack(this) && task == topTask() &&
- task.isOverHomeStack()) {
- mStackSupervisor.moveHomeStackTaskToTop(task.getTaskToReturnTo());
+ if (mStackSupervisor.isFrontStack(this) && task == topTask() && task.mOnTopOfHome) {
+ mStackSupervisor.moveHomeToTop();
}
removeTask(task);
}
@@ -3159,13 +3180,12 @@
}
}
- void moveHomeStackTaskToTop(int homeStackTaskType) {
+ void moveHomeTaskToTop() {
final int top = mTaskHistory.size() - 1;
for (int taskNdx = top; taskNdx >= 0; --taskNdx) {
final TaskRecord task = mTaskHistory.get(taskNdx);
- if (task.taskType == homeStackTaskType) {
- if (DEBUG_TASKS || DEBUG_STACK)
- Slog.d(TAG, "moveHomeStackTaskToTop: moving " + task);
+ if (task.isHomeTask()) {
+ if (DEBUG_TASKS || DEBUG_STACK) Slog.d(TAG, "moveHomeTaskToTop: moving " + task);
mTaskHistory.remove(taskNdx);
mTaskHistory.add(top, task);
updateTaskMovement(task, true);
@@ -3277,12 +3297,12 @@
int numTasks = mTaskHistory.size();
for (int taskNdx = numTasks - 1; taskNdx >= 1; --taskNdx) {
final TaskRecord task = mTaskHistory.get(taskNdx);
- if (task.isOverHomeStack()) {
+ if (task.mOnTopOfHome) {
break;
}
if (taskNdx == 1) {
// Set the last task before tr to go to home.
- task.setTaskToReturnTo(HOME_ACTIVITY_TYPE);
+ task.mOnTopOfHome = true;
}
}
@@ -3303,10 +3323,9 @@
}
final TaskRecord task = mResumedActivity != null ? mResumedActivity.task : null;
- if (task == tr && tr.isOverHomeStack() || numTasks <= 1 && isOnHomeDisplay()) {
- final int taskToReturnTo = tr.getTaskToReturnTo();
- tr.setTaskToReturnTo(APPLICATION_ACTIVITY_TYPE);
- return mStackSupervisor.resumeHomeStackTask(taskToReturnTo, null);
+ if (task == tr && tr.mOnTopOfHome || numTasks <= 1 && isOnHomeDisplay()) {
+ tr.mOnTopOfHome = false;
+ return mStackSupervisor.resumeHomeActivity(null);
}
mStackSupervisor.resumeTopActivitiesLocked();
@@ -3747,11 +3766,8 @@
final int taskNdx = mTaskHistory.indexOf(task);
final int topTaskNdx = mTaskHistory.size() - 1;
- if (task.isOverHomeStack() && taskNdx < topTaskNdx) {
- final TaskRecord nextTask = mTaskHistory.get(taskNdx + 1);
- if (!nextTask.isOverHomeStack()) {
- nextTask.setTaskToReturnTo(HOME_ACTIVITY_TYPE);
- }
+ if (task.mOnTopOfHome && taskNdx < topTaskNdx) {
+ mTaskHistory.get(taskNdx + 1).mOnTopOfHome = true;
}
mTaskHistory.remove(task);
updateTaskMovement(task, true);
diff --git a/services/core/java/com/android/server/am/ActivityStackSupervisor.java b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
index 6d20a32..0cc53d1 100644
--- a/services/core/java/com/android/server/am/ActivityStackSupervisor.java
+++ b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
@@ -31,9 +31,6 @@
import static com.android.server.am.ActivityManagerService.DEBUG_USER_LEAVING;
import static com.android.server.am.ActivityManagerService.FIRST_SUPERVISOR_STACK_MSG;
import static com.android.server.am.ActivityManagerService.TAG;
-import static com.android.server.am.ActivityRecord.HOME_ACTIVITY_TYPE;
-import static com.android.server.am.ActivityRecord.RECENTS_ACTIVITY_TYPE;
-import static com.android.server.am.ActivityRecord.APPLICATION_ACTIVITY_TYPE;
import android.app.Activity;
import android.app.ActivityManager;
@@ -207,6 +204,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
@@ -271,6 +272,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();
}
}
@@ -322,27 +326,18 @@
}
}
- void moveHomeStackTaskToTop(int homeStackTaskType) {
- if (homeStackTaskType == RECENTS_ACTIVITY_TYPE) {
- mWindowManager.showRecentApps();
- return;
- }
+ void moveHomeToTop() {
moveHomeStack(true);
- mHomeStack.moveHomeStackTaskToTop(homeStackTaskType);
+ mHomeStack.moveHomeTaskToTop();
}
- boolean resumeHomeStackTask(int homeStackTaskType, ActivityRecord prev) {
- if (homeStackTaskType == RECENTS_ACTIVITY_TYPE) {
- mWindowManager.showRecentApps();
- return false;
- }
- moveHomeStackTaskToTop(homeStackTaskType);
+ boolean resumeHomeActivity(ActivityRecord prev) {
+ moveHomeToTop();
if (prev != null) {
- prev.task.setTaskToReturnTo(APPLICATION_ACTIVITY_TYPE);
+ prev.task.mOnTopOfHome = false;
}
-
ActivityRecord r = mHomeStack.topRunningActivityLocked(null);
- if (r != null && (r.isHomeActivity() || r.isRecentsActivity())) {
+ if (r != null && r.isHomeActivity()) {
mService.setFocusedActivityLocked(r);
return resumeTopActivitiesLocked(mHomeStack, prev, null);
}
@@ -696,7 +691,7 @@
}
void startHomeActivity(Intent intent, ActivityInfo aInfo) {
- moveHomeStackTaskToTop(HOME_ACTIVITY_TYPE);
+ moveHomeToTop();
startActivityLocked(null, intent, null, aInfo, null, null, null, null, 0, 0, 0, null, 0,
null, false, null, null);
}
@@ -1387,7 +1382,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()) {
@@ -1631,7 +1629,7 @@
(FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_TASK_ON_HOME))
== (FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_TASK_ON_HOME)) {
// Caller wants to appear on home activity.
- intentActivity.task.setTaskToReturnTo(HOME_ACTIVITY_TYPE);
+ intentActivity.task.mOnTopOfHome = true;
}
options = null;
}
@@ -1816,11 +1814,6 @@
newTaskInfo != null ? newTaskInfo : r.info,
newTaskIntent != null ? newTaskIntent : intent,
voiceSession, voiceInteractor, true), null, true);
- if (sourceRecord == null) {
- // Launched from a service or notification or task that is finishing.
- r.task.setTaskToReturnTo(isFrontStack(mHomeStack) ?
- mHomeStack.topTask().taskType : RECENTS_ACTIVITY_TYPE);
- }
if (DEBUG_TASKS) Slog.v(TAG, "Starting new activity " + r + " in new task " +
r.task);
} else {
@@ -1832,7 +1825,7 @@
== (Intent.FLAG_ACTIVITY_NEW_TASK|Intent.FLAG_ACTIVITY_TASK_ON_HOME)) {
// Caller wants to appear on home activity, so before starting
// their own activity we will bring home to the front.
- r.task.setTaskToReturnTo(HOME_ACTIVITY_TYPE);
+ r.task.mOnTopOfHome = r.task.stack.isOnHomeDisplay();
}
}
} else if (sourceRecord != null) {
@@ -2183,7 +2176,7 @@
if ((flags & ActivityManager.MOVE_TASK_WITH_HOME) != 0) {
// Caller wants the home activity moved with it. To accomplish this,
// we'll just indicate that this task returns to the home task.
- task.setTaskToReturnTo(HOME_ACTIVITY_TYPE);
+ task.mOnTopOfHome = true;
}
task.stack.moveTaskToFrontLocked(task, null, options);
if (DEBUG_STACK) Slog.d(TAG, "findTaskToMoveToFront: moved to front of stack="
@@ -2294,7 +2287,7 @@
}
mWindowManager.addTask(taskId, stackId, false);
}
- resumeHomeStackTask(HOME_ACTIVITY_TYPE, null);
+ resumeHomeActivity(null);
}
void moveTaskToStack(int taskId, int stackId, boolean toTop) {
@@ -2556,7 +2549,7 @@
}
} else {
// Stack was moved to another display while user was swapped out.
- resumeHomeStackTask(HOME_ACTIVITY_TYPE, null);
+ resumeHomeActivity(null);
}
return homeInFront;
}
@@ -3440,4 +3433,16 @@
return "VirtualActivityDisplay={" + mDisplayId + "}";
}
}
+
+ private boolean isLeanbackOnlyDevice() {
+ boolean onLeanbackOnly = false;
+ try {
+ onLeanbackOnly = AppGlobals.getPackageManager().hasSystemFeature(
+ PackageManager.FEATURE_LEANBACK_ONLY);
+ } catch (RemoteException e) {
+ // noop
+ }
+
+ return onLeanbackOnly;
+ }
}
diff --git a/services/core/java/com/android/server/am/TaskRecord.java b/services/core/java/com/android/server/am/TaskRecord.java
index c07bc1e..ce83ae6 100644
--- a/services/core/java/com/android/server/am/TaskRecord.java
+++ b/services/core/java/com/android/server/am/TaskRecord.java
@@ -17,9 +17,6 @@
package com.android.server.am;
import static com.android.server.am.ActivityManagerService.TAG;
-import static com.android.server.am.ActivityRecord.HOME_ACTIVITY_TYPE;
-import static com.android.server.am.ActivityRecord.APPLICATION_ACTIVITY_TYPE;
-import static com.android.server.am.ActivityRecord.RECENTS_ACTIVITY_TYPE;
import static com.android.server.am.ActivityStackSupervisor.DEBUG_ADD_REMOVE;
import android.app.Activity;
@@ -57,6 +54,7 @@
private static final String ATTR_ASKEDCOMPATMODE = "asked_compat_mode";
private static final String ATTR_USERID = "user_id";
private static final String ATTR_TASKTYPE = "task_type";
+ private static final String ATTR_ONTOPOFHOME = "on_top_of_home";
private static final String ATTR_LASTDESCRIPTION = "last_description";
private static final String ATTR_LASTTIMEMOVED = "last_time_moved";
@@ -106,11 +104,9 @@
/** True if persistable, has changed, and has not yet been persisted */
boolean needsPersisting = false;
-
- /** Indication of what to run next when task exits. Use ActivityRecord types.
- * ActivityRecord.APPLICATION_ACTIVITY_TYPE indicates to resume the task below this one in the
- * task stack. */
- private int mTaskToReturnTo = APPLICATION_ACTIVITY_TYPE;
+ /** Launch the home activity when leaving this task. Will be false for tasks that are not on
+ * Display.DEFAULT_DISPLAY. */
+ boolean mOnTopOfHome = false;
final ActivityManagerService mService;
@@ -127,8 +123,9 @@
TaskRecord(ActivityManagerService service, int _taskId, Intent _intent, Intent _affinityIntent,
String _affinity, ComponentName _realActivity, ComponentName _origActivity,
- boolean _rootWasReset, boolean _askedCompatMode, int _taskType, int _userId,
- String _lastDescription, ArrayList<ActivityRecord> activities, long lastTimeMoved) {
+ boolean _rootWasReset, boolean _askedCompatMode, int _taskType, boolean _onTopOfHome,
+ int _userId, String _lastDescription, ArrayList<ActivityRecord> activities,
+ long lastTimeMoved) {
mService = service;
taskId = _taskId;
intent = _intent;
@@ -141,7 +138,7 @@
rootWasReset = _rootWasReset;
askedCompatMode = _askedCompatMode;
taskType = _taskType;
- mTaskToReturnTo = HOME_ACTIVITY_TYPE;
+ mOnTopOfHome = _onTopOfHome;
userId = _userId;
lastDescription = _lastDescription;
mActivities = activities;
@@ -209,14 +206,6 @@
}
}
- void setTaskToReturnTo(int taskToReturnTo) {
- mTaskToReturnTo = taskToReturnTo;
- }
-
- int getTaskToReturnTo() {
- return mTaskToReturnTo;
- }
-
void disposeThumbnail() {
super.disposeThumbnail();
for (int i=mActivities.size()-1; i>=0; i--) {
@@ -488,15 +477,11 @@
}
boolean isHomeTask() {
- return taskType == HOME_ACTIVITY_TYPE;
+ return taskType == ActivityRecord.HOME_ACTIVITY_TYPE;
}
boolean isApplicationTask() {
- return taskType == APPLICATION_ACTIVITY_TYPE;
- }
-
- boolean isOverHomeStack() {
- return mTaskToReturnTo == HOME_ACTIVITY_TYPE || mTaskToReturnTo == RECENTS_ACTIVITY_TYPE;
+ return taskType == ActivityRecord.APPLICATION_ACTIVITY_TYPE;
}
public TaskAccessInfo getTaskAccessInfoLocked() {
@@ -638,6 +623,7 @@
out.attribute(null, ATTR_ASKEDCOMPATMODE, String.valueOf(askedCompatMode));
out.attribute(null, ATTR_USERID, String.valueOf(userId));
out.attribute(null, ATTR_TASKTYPE, String.valueOf(taskType));
+ out.attribute(null, ATTR_ONTOPOFHOME, String.valueOf(mOnTopOfHome));
out.attribute(null, ATTR_LASTTIMEMOVED, String.valueOf(mLastTimeMoved));
if (lastDescription != null) {
out.attribute(null, ATTR_LASTDESCRIPTION, lastDescription.toString());
@@ -683,6 +669,7 @@
boolean rootHasReset = false;
boolean askedCompatMode = false;
int taskType = ActivityRecord.APPLICATION_ACTIVITY_TYPE;
+ boolean onTopOfHome = true;
int userId = 0;
String lastDescription = null;
long lastTimeOnTop = 0;
@@ -710,6 +697,8 @@
userId = Integer.valueOf(attrValue);
} else if (ATTR_TASKTYPE.equals(attrName)) {
taskType = Integer.valueOf(attrValue);
+ } else if (ATTR_ONTOPOFHOME.equals(attrName)) {
+ onTopOfHome = Boolean.valueOf(attrValue);
} else if (ATTR_LASTDESCRIPTION.equals(attrName)) {
lastDescription = attrValue;
} else if (ATTR_LASTTIMEMOVED.equals(attrName)) {
@@ -747,7 +736,8 @@
final TaskRecord task = new TaskRecord(stackSupervisor.mService, taskId, intent,
affinityIntent, affinity, realActivity, origActivity, rootHasReset,
- askedCompatMode, taskType, userId, lastDescription, activities, lastTimeOnTop);
+ askedCompatMode, taskType, onTopOfHome, userId, lastDescription, activities,
+ lastTimeOnTop);
for (int activityNdx = activities.size() - 1; activityNdx >=0; --activityNdx) {
final ActivityRecord r = activities.get(activityNdx);
@@ -766,7 +756,7 @@
pw.print(" userId="); pw.print(userId);
pw.print(" taskType="); pw.print(taskType);
pw.print(" numFullscreen="); pw.print(numFullscreen);
- pw.print(" mTaskToReturnTo="); pw.println(mTaskToReturnTo);
+ pw.print(" mOnTopOfHome="); pw.println(mOnTopOfHome);
}
if (affinity != null) {
pw.print(prefix); pw.print("affinity="); pw.println(affinity);
diff --git a/services/core/java/com/android/server/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 66cc532..bab4895 100644
--- a/services/core/java/com/android/server/notification/NotificationUsageStats.java
+++ b/services/core/java/com/android/server/notification/NotificationUsageStats.java
@@ -16,8 +16,6 @@
package com.android.server.notification;
-import com.android.server.notification.NotificationManagerService.NotificationRecord;
-
import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
diff --git a/services/core/java/com/android/server/notification/RankingReconsideration.java b/services/core/java/com/android/server/notification/RankingReconsideration.java
index cf5e210..057f0f1 100644
--- a/services/core/java/com/android/server/notification/RankingReconsideration.java
+++ b/services/core/java/com/android/server/notification/RankingReconsideration.java
@@ -15,8 +15,6 @@
*/
package com.android.server.notification;
-import com.android.server.notification.NotificationManagerService.NotificationRecord;
-
import java.util.concurrent.TimeUnit;
/**
diff --git a/services/core/java/com/android/server/notification/ValidateNotificationPeople.java b/services/core/java/com/android/server/notification/ValidateNotificationPeople.java
index a629a5f..02f95e9 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;
@@ -49,9 +47,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/task/StateChangedListener.java b/services/core/java/com/android/server/task/StateChangedListener.java
index db2d4ee..b1a4636 100644
--- a/services/core/java/com/android/server/task/StateChangedListener.java
+++ b/services/core/java/com/android/server/task/StateChangedListener.java
@@ -27,9 +27,8 @@
/**
* Called by the controller to notify the TaskManager that it should check on the state of a
* task.
- * @param taskStatus The state of the task which has changed.
*/
- public void onTaskStateChanged(TaskStatus taskStatus);
+ public void onControllerStateChanged();
/**
* Called by the controller to notify the TaskManager that regardless of the state of the task,
diff --git a/services/core/java/com/android/server/task/TaskCompletedListener.java b/services/core/java/com/android/server/task/TaskCompletedListener.java
index 0210442..c53f5ca 100644
--- a/services/core/java/com/android/server/task/TaskCompletedListener.java
+++ b/services/core/java/com/android/server/task/TaskCompletedListener.java
@@ -16,6 +16,8 @@
package com.android.server.task;
+import com.android.server.task.controllers.TaskStatus;
+
/**
* Used for communication between {@link com.android.server.task.TaskServiceContext} and the
* {@link com.android.server.task.TaskManagerService}.
@@ -26,13 +28,5 @@
* Callback for when a task is completed.
* @param needsReschedule Whether the implementing class should reschedule this task.
*/
- public void onTaskCompleted(int serviceToken, int taskId, boolean needsReschedule);
-
- /**
- * Callback for when the implementing class needs to clean up the
- * {@link com.android.server.task.TaskServiceContext}. The scheduler can get this callback
- * several times if the TaskServiceContext got into a bad state (for e.g. the client crashed
- * and it needs to clean up).
- */
- public void onAllTasksCompleted(int serviceToken);
+ public void onTaskCompleted(TaskStatus taskStatus, boolean needsReschedule);
}
diff --git a/services/core/java/com/android/server/task/TaskManagerService.java b/services/core/java/com/android/server/task/TaskManagerService.java
index 80030b4..d5b70e6 100644
--- a/services/core/java/com/android/server/task/TaskManagerService.java
+++ b/services/core/java/com/android/server/task/TaskManagerService.java
@@ -19,10 +19,12 @@
import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.util.ArrayList;
+import java.util.Iterator;
import java.util.List;
import android.app.task.ITaskManager;
import android.app.task.Task;
+import android.app.task.TaskManager;
import android.content.Context;
import android.content.pm.PackageManager;
import android.os.Binder;
@@ -30,11 +32,17 @@
import android.os.Looper;
import android.os.Message;
import android.os.RemoteException;
-import android.os.UserHandle;
+import android.os.SystemClock;
import android.util.Slog;
import android.util.SparseArray;
+import com.android.server.task.controllers.ConnectivityController;
+import com.android.server.task.controllers.IdleController;
+import com.android.server.task.controllers.StateController;
import com.android.server.task.controllers.TaskStatus;
+import com.android.server.task.controllers.TimeController;
+
+import java.util.LinkedList;
/**
* Responsible for taking tasks representing work to be performed by a client app, and determining
@@ -44,61 +52,148 @@
*/
public class TaskManagerService extends com.android.server.SystemService
implements StateChangedListener, TaskCompletedListener {
+ // TODO: Switch this off for final version.
+ private static final boolean DEBUG = true;
+ /** The number of concurrent tasks we run at one time. */
+ private static final int MAX_TASK_CONTEXTS_COUNT = 3;
static final String TAG = "TaskManager";
+ /**
+ * When a task fails, it gets rescheduled according to its backoff policy. To be nice, we allow
+ * this amount of time from the rescheduled time by which the retry must occur.
+ */
+ private static final long RESCHEDULE_WINDOW_SLOP_MILLIS = 5000L;
/** Master list of tasks. */
private final TaskStore mTasks;
+ static final int MSG_TASK_EXPIRED = 0;
+ static final int MSG_CHECK_TASKS = 1;
+
+ // Policy constants
+ /**
+ * Minimum # of idle tasks that must be ready in order to force the TM to schedule things
+ * early.
+ */
+ private static final int MIN_IDLE_COUNT = 1;
+ /**
+ * Minimum # of connectivity tasks that must be ready in order to force the TM to schedule
+ * things early.
+ */
+ private static final int MIN_CONNECTIVITY_COUNT = 2;
+ /**
+ * Minimum # of tasks (with no particular constraints) for which the TM will be happy running
+ * some work early.
+ */
+ private static final int MIN_READY_TASKS_COUNT = 4;
+
/**
* Track Services that have currently active or pending tasks. The index is provided by
* {@link TaskStatus#getServiceToken()}
*/
- private final SparseArray<TaskServiceContext> mActiveServices =
- new SparseArray<TaskServiceContext>();
+ private final List<TaskServiceContext> mActiveServices = new LinkedList<TaskServiceContext>();
+ /** List of controllers that will notify this service of updates to tasks. */
+ private List<StateController> mControllers;
+ /**
+ * Queue of pending tasks. The TaskServiceContext class will receive tasks from this list
+ * when ready to execute them.
+ */
+ private final LinkedList<TaskStatus> mPendingTasks = new LinkedList<TaskStatus>();
private final TaskHandler mHandler;
private final TaskManagerStub mTaskManagerStub;
- /** Check the pending queue and start any tasks. */
- static final int MSG_RUN_PENDING = 0;
- /** Initiate the stop task flow. */
- static final int MSG_STOP_TASK = 1;
- /** */
- static final int MSG_CHECK_TASKS = 2;
+ /**
+ * Entry point from client to schedule the provided task.
+ * This will add the task to the
+ * @param task Task object containing execution parameters
+ * @param uId The package identifier of the application this task is for.
+ * @param canPersistTask Whether or not the client has the appropriate permissions for persisting
+ * of this task.
+ * @return Result of this operation. See <code>TaskManager#RESULT_*</code> return codes.
+ */
+ public int schedule(Task task, int uId, boolean canPersistTask) {
+ TaskStatus taskStatus = new TaskStatus(task, uId, canPersistTask);
+ return startTrackingTask(taskStatus) ?
+ TaskManager.RESULT_SUCCESS : TaskManager.RESULT_FAILURE;
+ }
- private class TaskHandler extends Handler {
-
- public TaskHandler(Looper looper) {
- super(looper);
- }
-
- @Override
- public void handleMessage(Message message) {
- switch (message.what) {
- case MSG_RUN_PENDING:
-
- break;
- case MSG_STOP_TASK:
-
- break;
- case MSG_CHECK_TASKS:
- checkTasks();
- break;
+ public List<Task> getPendingTasks(int uid) {
+ ArrayList<Task> outList = new ArrayList<Task>();
+ synchronized (mTasks) {
+ for (TaskStatus ts : mTasks.getTasks()) {
+ if (ts.getUid() == uid) {
+ outList.add(ts.getTask());
+ }
}
}
+ return outList;
+ }
- /**
- * Called when we need to run through the list of all tasks and start/stop executing one or
- * more of them.
- */
- private void checkTasks() {
- synchronized (mTasks) {
- final SparseArray<TaskStatus> tasks = mTasks.getTasks();
- for (int i = 0; i < tasks.size(); i++) {
- TaskStatus ts = tasks.valueAt(i);
- if (ts.isReady() && ! isCurrentlyActive(ts)) {
- assignTaskToServiceContext(ts);
- }
+ /**
+ * Entry point from client to cancel all tasks originating from their uid.
+ * This will remove the task from the master list, and cancel the task if it was staged for
+ * execution or being executed.
+ * @param uid To check against for removal of a task.
+ */
+ public void cancelTaskForUid(int uid) {
+ // Remove from master list.
+ synchronized (mTasks) {
+ if (!mTasks.removeAllByUid(uid)) {
+ // If it's not in the master list, it's nowhere.
+ return;
+ }
+ }
+ // Remove from pending queue.
+ synchronized (mPendingTasks) {
+ Iterator<TaskStatus> it = mPendingTasks.iterator();
+ while (it.hasNext()) {
+ TaskStatus ts = it.next();
+ if (ts.getUid() == uid) {
+ it.remove();
+ }
+ }
+ }
+ // Cancel if running.
+ synchronized (mActiveServices) {
+ for (TaskServiceContext tsc : mActiveServices) {
+ if (tsc.getRunningTask().getUid() == uid) {
+ tsc.cancelExecutingTask();
+ }
+ }
+ }
+ }
+
+ /**
+ * Entry point from client to cancel the task corresponding to the taskId provided.
+ * This will remove the task from the master list, and cancel the task if it was staged for
+ * execution or being executed.
+ * @param uid Uid of the calling client.
+ * @param taskId Id of the task, provided at schedule-time.
+ */
+ public void cancelTask(int uid, int taskId) {
+ synchronized (mTasks) {
+ if (!mTasks.remove(uid, taskId)) {
+ // If it's not in the master list, it's nowhere.
+ return;
+ }
+ }
+ synchronized (mPendingTasks) {
+ Iterator<TaskStatus> it = mPendingTasks.iterator();
+ while (it.hasNext()) {
+ TaskStatus ts = it.next();
+ if (ts.getUid() == uid && ts.getTaskId() == taskId) {
+ it.remove();
+ // If we got it from pending, it didn't make it to active so return.
+ return;
+ }
+ }
+ }
+ synchronized (mActiveServices) {
+ for (TaskServiceContext tsc : mActiveServices) {
+ if (tsc.getRunningTask().getUid() == uid &&
+ tsc.getRunningTask().getTaskId() == taskId) {
+ tsc.cancelExecutingTask();
+ return;
}
}
}
@@ -118,6 +213,17 @@
mTasks = new TaskStore(context);
mHandler = new TaskHandler(context.getMainLooper());
mTaskManagerStub = new TaskManagerStub();
+ // Create the "runners".
+ for (int i = 0; i < MAX_TASK_CONTEXTS_COUNT; i++) {
+ mActiveServices.add(
+ new TaskServiceContext(this, context.getMainLooper()));
+ }
+
+ mControllers = new LinkedList<StateController>();
+ mControllers.add(ConnectivityController.get(this));
+ mControllers.add(TimeController.get(this));
+ mControllers.add(IdleController.get(this));
+ // TODO: Add BatteryStateController when implemented.
}
@Override
@@ -126,33 +232,156 @@
}
/**
- * Entry point from client to schedule the provided task.
- * This will add the task to the
- * @param task Task object containing execution parameters
- * @param userId The id of the user this task is for.
- * @param uId The package identifier of the application this task is for.
- * @param canPersistTask Whether or not the client has the appropriate permissions for
- * persisting of this task.
- * @return Result of this operation. See <code>TaskManager#RESULT_*</code> return codes.
+ * Called when we have a task status object that we need to insert in our
+ * {@link com.android.server.task.TaskStore}, and make sure all the relevant controllers know
+ * about.
*/
- public int schedule(Task task, int userId, int uId, boolean canPersistTask) {
- TaskStatus taskStatus = mTasks.addNewTaskForUser(task, userId, uId, canPersistTask);
- return 0;
- }
-
- public List<Task> getPendingTasks(int uid) {
- ArrayList<Task> outList = new ArrayList<Task>(3);
+ private boolean startTrackingTask(TaskStatus taskStatus) {
+ boolean added = false;
synchronized (mTasks) {
- final SparseArray<TaskStatus> tasks = mTasks.getTasks();
- final int N = tasks.size();
- for (int i = 0; i < N; i++) {
- TaskStatus ts = tasks.get(i);
- if (ts.getUid() == uid) {
- outList.add(ts.getTask());
- }
+ added = mTasks.add(taskStatus);
+ }
+ if (added) {
+ for (StateController controller : mControllers) {
+ controller.maybeStartTrackingTask(taskStatus);
}
}
- return outList;
+ return added;
+ }
+
+ /**
+ * Called when we want to remove a TaskStatus object that we've finished executing. Returns the
+ * object removed.
+ */
+ private boolean stopTrackingTask(TaskStatus taskStatus) {
+ boolean removed;
+ synchronized (mTasks) {
+ // Remove from store as well as controllers.
+ removed = mTasks.remove(taskStatus);
+ }
+ if (removed) {
+ for (StateController controller : mControllers) {
+ controller.maybeStopTrackingTask(taskStatus);
+ }
+ }
+ return removed;
+ }
+
+ private boolean cancelTaskOnServiceContext(TaskStatus ts) {
+ synchronized (mActiveServices) {
+ for (TaskServiceContext tsc : mActiveServices) {
+ if (tsc.getRunningTask() == ts) {
+ tsc.cancelExecutingTask();
+ return true;
+ }
+ }
+ return false;
+ }
+ }
+
+ /**
+ * @param ts TaskStatus we are querying against.
+ * @return Whether or not the task represented by the status object is currently being run or
+ * is pending.
+ */
+ private boolean isCurrentlyActive(TaskStatus ts) {
+ synchronized (mActiveServices) {
+ for (TaskServiceContext serviceContext : mActiveServices) {
+ if (serviceContext.getRunningTask() == ts) {
+ return true;
+ }
+ }
+ return false;
+ }
+ }
+
+ /**
+ * A task is rescheduled with exponential back-off if the client requests this from their
+ * execution logic.
+ * A caveat is for idle-mode tasks, for which the idle-mode constraint will usurp the
+ * timeliness of the reschedule. For an idle-mode task, no deadline is given.
+ * @param failureToReschedule Provided task status that we will reschedule.
+ * @return A newly instantiated TaskStatus with the same constraints as the last task except
+ * with adjusted timing constraints.
+ */
+ private TaskStatus getRescheduleTaskForFailure(TaskStatus failureToReschedule) {
+ final long elapsedNowMillis = SystemClock.elapsedRealtime();
+ final Task task = failureToReschedule.getTask();
+
+ final long initialBackoffMillis = task.getInitialBackoffMillis();
+ final int backoffAttempt = failureToReschedule.getNumFailures() + 1;
+ long newEarliestRuntimeElapsed = elapsedNowMillis;
+
+ switch (task.getBackoffPolicy()) {
+ case Task.BackoffPolicy.LINEAR:
+ newEarliestRuntimeElapsed += initialBackoffMillis * backoffAttempt;
+ break;
+ default:
+ if (DEBUG) {
+ Slog.v(TAG, "Unrecognised back-off policy, defaulting to exponential.");
+ }
+ case Task.BackoffPolicy.EXPONENTIAL:
+ newEarliestRuntimeElapsed += Math.pow(initialBackoffMillis, backoffAttempt);
+ break;
+ }
+ long newLatestRuntimeElapsed = failureToReschedule.hasIdleConstraint() ? Long.MAX_VALUE
+ : newEarliestRuntimeElapsed + RESCHEDULE_WINDOW_SLOP_MILLIS;
+ return new TaskStatus(failureToReschedule, newEarliestRuntimeElapsed,
+ newLatestRuntimeElapsed, backoffAttempt);
+ }
+
+ /**
+ * Called after a periodic has executed so we can to re-add it. We take the last execution time
+ * of the task to be the time of completion (i.e. the time at which this function is called).
+ * This could be inaccurate b/c the task can run for as long as
+ * {@link com.android.server.task.TaskServiceContext#EXECUTING_TIMESLICE_MILLIS}, but will lead
+ * to underscheduling at least, rather than if we had taken the last execution time to be the
+ * start of the execution.
+ * @return A new task representing the execution criteria for this instantiation of the
+ * recurring task.
+ */
+ private TaskStatus getRescheduleTaskForPeriodic(TaskStatus periodicToReschedule) {
+ final long elapsedNow = SystemClock.elapsedRealtime();
+ // Compute how much of the period is remaining.
+ long runEarly = Math.max(periodicToReschedule.getLatestRunTimeElapsed() - elapsedNow, 0);
+ long newEarliestRunTimeElapsed = elapsedNow + runEarly;
+ long period = periodicToReschedule.getTask().getIntervalMillis();
+ long newLatestRuntimeElapsed = newEarliestRunTimeElapsed + period;
+
+ if (DEBUG) {
+ Slog.v(TAG, "Rescheduling executed periodic. New execution window [" +
+ newEarliestRunTimeElapsed/1000 + ", " + newLatestRuntimeElapsed/1000 + "]s");
+ }
+ return new TaskStatus(periodicToReschedule, newEarliestRunTimeElapsed,
+ newLatestRuntimeElapsed, 0 /* backoffAttempt */);
+ }
+
+ // TaskCompletedListener implementations.
+
+ /**
+ * A task just finished executing. We fetch the
+ * {@link com.android.server.task.controllers.TaskStatus} from the store and depending on
+ * whether we want to reschedule we readd it to the controllers.
+ * @param taskStatus Completed task.
+ * @param needsReschedule Whether the implementing class should reschedule this task.
+ */
+ @Override
+ public void onTaskCompleted(TaskStatus taskStatus, boolean needsReschedule) {
+ if (!stopTrackingTask(taskStatus)) {
+ if (DEBUG) {
+ Slog.e(TAG, "Error removing task: could not find task to remove. Was task" +
+ "removed while executing?");
+ }
+ return;
+ }
+ if (needsReschedule) {
+ TaskStatus rescheduled = getRescheduleTaskForFailure(taskStatus);
+ startTrackingTask(rescheduled);
+ } else if (taskStatus.getTask().isPeriodic()) {
+ TaskStatus rescheduledPeriodic = getRescheduleTaskForPeriodic(taskStatus);
+ startTrackingTask(rescheduledPeriodic);
+ }
+ mHandler.obtainMessage(MSG_CHECK_TASKS).sendToTarget();
}
// StateChangedListener implementations.
@@ -165,70 +394,123 @@
* see which are ready. This will further decouple the controllers from the execution logic.
*/
@Override
- public void onTaskStateChanged(TaskStatus taskStatus) {
- postCheckTasksMessage();
-
+ public void onControllerStateChanged() {
+ // Post a message to to run through the list of tasks and start/stop any that are eligible.
+ mHandler.obtainMessage(MSG_CHECK_TASKS).sendToTarget();
}
@Override
public void onTaskDeadlineExpired(TaskStatus taskStatus) {
-
+ mHandler.obtainMessage(MSG_TASK_EXPIRED, taskStatus);
}
- // TaskCompletedListener implementations.
+ private class TaskHandler extends Handler {
- /**
- * A task just finished executing. We fetch the
- * {@link com.android.server.task.controllers.TaskStatus} from the store and depending on
- * whether we want to reschedule we readd it to the controllers.
- * @param serviceToken key for the service context in {@link #mActiveServices}.
- * @param taskId Id of the task that is complete.
- * @param needsReschedule Whether the implementing class should reschedule this task.
- */
- @Override
- public void onTaskCompleted(int serviceToken, int taskId, boolean needsReschedule) {
- final TaskServiceContext serviceContext = mActiveServices.get(serviceToken);
- if (serviceContext == null) {
- Slog.e(TAG, "Task completed for invalid service context; " + serviceToken);
- return;
+ public TaskHandler(Looper looper) {
+ super(looper);
}
- }
-
- @Override
- public void onAllTasksCompleted(int serviceToken) {
-
- }
-
- private void assignTaskToServiceContext(TaskStatus ts) {
- TaskServiceContext serviceContext =
- mActiveServices.get(ts.getServiceToken());
- if (serviceContext == null) {
- serviceContext = new TaskServiceContext(this, mHandler.getLooper(), ts);
- mActiveServices.put(ts.getServiceToken(), serviceContext);
+ @Override
+ public void handleMessage(Message message) {
+ switch (message.what) {
+ case MSG_TASK_EXPIRED:
+ final TaskStatus expired = (TaskStatus) message.obj; // Unused for now.
+ queueReadyTasksForExecutionH();
+ break;
+ case MSG_CHECK_TASKS:
+ // Check the list of tasks and run some of them if we feel inclined.
+ maybeQueueReadyTasksForExecutionH();
+ break;
+ }
+ maybeRunNextPendingTaskH();
+ // Don't remove TASK_EXPIRED in case one came along while processing the queue.
+ removeMessages(MSG_CHECK_TASKS);
}
- serviceContext.addPendingTask(ts);
- }
- /**
- * @param ts TaskStatus we are querying against.
- * @return Whether or not the task represented by the status object is currently being run or
- * is pending.
- */
- private boolean isCurrentlyActive(TaskStatus ts) {
- TaskServiceContext serviceContext = mActiveServices.get(ts.getServiceToken());
- if (serviceContext == null) {
- return false;
+ /**
+ * Run through list of tasks and execute all possible - at least one is expired so we do
+ * as many as we can.
+ */
+ private void queueReadyTasksForExecutionH() {
+ synchronized (mTasks) {
+ for (TaskStatus ts : mTasks.getTasks()) {
+ final boolean criteriaSatisfied = ts.isReady();
+ final boolean isRunning = isCurrentlyActive(ts);
+ if (criteriaSatisfied && !isRunning) {
+ synchronized (mPendingTasks) {
+ mPendingTasks.add(ts);
+ }
+ } else if (!criteriaSatisfied && isRunning) {
+ cancelTaskOnServiceContext(ts);
+ }
+ }
+ }
}
- return serviceContext.hasTaskPending(ts);
- }
- /**
- * Post a message to {@link #mHandler} to run through the list of tasks and start/stop any that
- * are eligible.
- */
- private void postCheckTasksMessage() {
- mHandler.obtainMessage(MSG_CHECK_TASKS).sendToTarget();
+ /**
+ * The state of at least one task has changed. Here is where we could enforce various
+ * policies on when we want to execute tasks.
+ * Right now the policy is such:
+ * If >1 of the ready tasks is idle mode we send all of them off
+ * if more than 2 network connectivity tasks are ready we send them all off.
+ * If more than 4 tasks total are ready we send them all off.
+ * TODO: It would be nice to consolidate these sort of high-level policies somewhere.
+ */
+ private void maybeQueueReadyTasksForExecutionH() {
+ synchronized (mTasks) {
+ int idleCount = 0;
+ int connectivityCount = 0;
+ List<TaskStatus> runnableTasks = new ArrayList<TaskStatus>();
+ for (TaskStatus ts : mTasks.getTasks()) {
+ final boolean criteriaSatisfied = ts.isReady();
+ final boolean isRunning = isCurrentlyActive(ts);
+ if (criteriaSatisfied && !isRunning) {
+ if (ts.hasIdleConstraint()) {
+ idleCount++;
+ }
+ if (ts.hasConnectivityConstraint() || ts.hasMeteredConstraint()) {
+ connectivityCount++;
+ }
+ runnableTasks.add(ts);
+ } else if (!criteriaSatisfied && isRunning) {
+ cancelTaskOnServiceContext(ts);
+ }
+ }
+ if (idleCount >= MIN_IDLE_COUNT || connectivityCount >= MIN_CONNECTIVITY_COUNT ||
+ runnableTasks.size() >= MIN_READY_TASKS_COUNT) {
+ for (TaskStatus ts : runnableTasks) {
+ synchronized (mPendingTasks) {
+ mPendingTasks.add(ts);
+ }
+ }
+ }
+ }
+ }
+
+ /**
+ * Checks the state of the pending queue against any available
+ * {@link com.android.server.task.TaskServiceContext} that can run a new task.
+ * {@link com.android.server.task.TaskServiceContext}.
+ */
+ private void maybeRunNextPendingTaskH() {
+ TaskStatus nextPending;
+ synchronized (mPendingTasks) {
+ nextPending = mPendingTasks.poll();
+ }
+ if (nextPending == null) {
+ return;
+ }
+
+ synchronized (mActiveServices) {
+ for (TaskServiceContext tsc : mActiveServices) {
+ if (tsc.isAvailable()) {
+ if (tsc.executeRunnableTask(nextPending)) {
+ return;
+ }
+ }
+ }
+ }
+ }
}
/**
@@ -268,11 +550,10 @@
public int schedule(Task task) throws RemoteException {
final boolean canPersist = canCallerPersistTasks();
final int uid = Binder.getCallingUid();
- final int userId = UserHandle.getCallingUserId();
long ident = Binder.clearCallingIdentity();
try {
- return TaskManagerService.this.schedule(task, userId, uid, canPersist);
+ return TaskManagerService.this.schedule(task, uid, canPersist);
} finally {
Binder.restoreCallingIdentity(ident);
}
@@ -280,15 +561,38 @@
@Override
public List<Task> getAllPendingTasks() throws RemoteException {
- return null;
+ final int uid = Binder.getCallingUid();
+
+ long ident = Binder.clearCallingIdentity();
+ try {
+ return TaskManagerService.this.getPendingTasks(uid);
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
}
@Override
public void cancelAll() throws RemoteException {
+ final int uid = Binder.getCallingUid();
+
+ long ident = Binder.clearCallingIdentity();
+ try {
+ TaskManagerService.this.cancelTaskForUid(uid);
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
}
@Override
public void cancel(int taskId) throws RemoteException {
+ final int uid = Binder.getCallingUid();
+
+ long ident = Binder.clearCallingIdentity();
+ try {
+ TaskManagerService.this.cancelTask(uid, taskId);
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
}
/**
@@ -311,9 +615,7 @@
synchronized (mTasks) {
pw.print("Registered tasks:");
if (mTasks.size() > 0) {
- SparseArray<TaskStatus> tasks = mTasks.getTasks();
- for (int i = 0; i < tasks.size(); i++) {
- TaskStatus ts = tasks.get(i);
+ for (TaskStatus ts : mTasks.getTasks()) {
pw.println();
ts.dump(pw, " ");
}
diff --git a/services/core/java/com/android/server/task/TaskServiceContext.java b/services/core/java/com/android/server/task/TaskServiceContext.java
index 165445a..75e9212 100644
--- a/services/core/java/com/android/server/task/TaskServiceContext.java
+++ b/services/core/java/com/android/server/task/TaskServiceContext.java
@@ -24,6 +24,7 @@
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
+import android.os.Binder;
import android.os.Handler;
import android.os.IBinder;
import android.os.Looper;
@@ -36,20 +37,18 @@
import android.util.Slog;
import android.util.SparseArray;
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.annotations.VisibleForTesting;
import com.android.server.task.controllers.TaskStatus;
import java.util.concurrent.atomic.AtomicBoolean;
/**
- * Maintains information required to bind to a {@link android.app.task.TaskService}. This binding
- * is reused to start concurrent tasks on the TaskService. Information here is unique
- * to the service.
- * Functionality provided by this class:
- * - Manages wakelock for the service.
- * - Sends onStartTask() and onStopTask() messages to client app, and handles callbacks.
- * -
+ * Handles client binding and lifecycle of a task. A task will only execute one at a time on an
+ * instance of this class.
*/
public class TaskServiceContext extends ITaskCallback.Stub implements ServiceConnection {
+ private static final boolean DEBUG = true;
private static final String TAG = "TaskServiceContext";
/** Define the maximum # of tasks allowed to run on a service at once. */
private static final int defaultMaxActiveTasksPerService =
@@ -66,10 +65,10 @@
};
// States that a task occupies while interacting with the client.
- private static final int VERB_STARTING = 0;
- private static final int VERB_EXECUTING = 1;
- private static final int VERB_STOPPING = 2;
- private static final int VERB_PENDING = 3;
+ static final int VERB_BINDING = 0;
+ static final int VERB_STARTING = 1;
+ static final int VERB_EXECUTING = 2;
+ static final int VERB_STOPPING = 3;
// Messages that result from interactions with the client service.
/** System timed out waiting for a response. */
@@ -77,178 +76,173 @@
/** Received a callback from client. */
private static final int MSG_CALLBACK = 1;
/** Run through list and start any ready tasks.*/
- private static final int MSG_CHECK_PENDING = 2;
- /** Cancel an active task. */
+ private static final int MSG_SERVICE_BOUND = 2;
+ /** Cancel a task. */
private static final int MSG_CANCEL = 3;
- /** Add a pending task. */
- private static final int MSG_ADD_PENDING = 4;
- /** Client crashed, so we need to wind things down. */
- private static final int MSG_SHUTDOWN = 5;
+ /** Shutdown the Task. Used when the client crashes and we can't die gracefully.*/
+ private static final int MSG_SHUTDOWN_EXECUTION = 4;
- /** Used to identify this task service context when communicating with the TaskManager. */
- final int token;
- final ComponentName component;
- final int userId;
- ITaskService service;
private final Handler mCallbackHandler;
- /** Tasks that haven't been sent to the client for execution yet. */
- private final SparseArray<ActiveTask> mPending;
+ /** Make callbacks to {@link TaskManagerService} to inform on task completion status. */
+ private final TaskCompletedListener mCompletedListener;
/** Used for service binding, etc. */
private final Context mContext;
- /** Make callbacks to {@link TaskManagerService} to inform on task completion status. */
- final private TaskCompletedListener mCompletedListener;
- private final PowerManager.WakeLock mWakeLock;
+ private PowerManager.WakeLock mWakeLock;
- /** Whether this service is actively bound. */
- boolean mBound;
+ // Execution state.
+ private TaskParams mParams;
+ @VisibleForTesting
+ int mVerb;
+ private AtomicBoolean mCancelled = new AtomicBoolean();
- TaskServiceContext(TaskManagerService taskManager, Looper looper, TaskStatus taskStatus) {
- mContext = taskManager.getContext();
- this.component = taskStatus.getServiceComponent();
- this.token = taskStatus.getServiceToken();
- this.userId = taskStatus.getUserId();
+ /** All the information maintained about the task currently being executed. */
+ private TaskStatus mRunningTask;
+ /** Binder to the client service. */
+ ITaskService service;
+
+ private final Object mAvailableLock = new Object();
+ /** Whether this context is free. */
+ @GuardedBy("mAvailableLock")
+ private boolean mAvailable;
+
+ TaskServiceContext(TaskManagerService service, Looper looper) {
+ this(service.getContext(), service, looper);
+ }
+
+ @VisibleForTesting
+ TaskServiceContext(Context context, TaskCompletedListener completedListener, Looper looper) {
+ mContext = context;
mCallbackHandler = new TaskServiceHandler(looper);
- mPending = new SparseArray<ActiveTask>();
- mCompletedListener = taskManager;
- final PowerManager pm = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
+ mCompletedListener = completedListener;
+ }
+
+ /**
+ * Give a task to this context for execution. Callers must first check {@link #isAvailable()}
+ * to make sure this is a valid context.
+ * @param ts The status of the task that we are going to run.
+ * @return True if the task was accepted and is going to run.
+ */
+ boolean executeRunnableTask(TaskStatus ts) {
+ synchronized (mAvailableLock) {
+ if (!mAvailable) {
+ Slog.e(TAG, "Starting new runnable but context is unavailable > Error.");
+ return false;
+ }
+ mAvailable = false;
+ }
+
+ final PowerManager pm =
+ (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
mWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK,
- TM_WAKELOCK_PREFIX + component.getPackageName());
- mWakeLock.setWorkSource(new WorkSource(taskStatus.getUid()));
+ TM_WAKELOCK_PREFIX + ts.getServiceComponent().getPackageName());
+ mWakeLock.setWorkSource(new WorkSource(ts.getUid()));
mWakeLock.setReferenceCounted(false);
+
+ mRunningTask = ts;
+ mParams = new TaskParams(ts.getTaskId(), ts.getExtras(), this);
+
+ mVerb = VERB_BINDING;
+ final Intent intent = new Intent().setComponent(ts.getServiceComponent());
+ boolean binding = mContext.bindServiceAsUser(intent, this,
+ Context.BIND_AUTO_CREATE | Context.BIND_NOT_FOREGROUND,
+ new UserHandle(ts.getUserId()));
+ if (!binding) {
+ if (DEBUG) {
+ Slog.d(TAG, ts.getServiceComponent().getShortClassName() + " unavailable.");
+ }
+ return false;
+ }
+
+ return true;
+ }
+
+ /** Used externally to query the running task. Will return null if there is no task running. */
+ TaskStatus getRunningTask() {
+ return mRunningTask;
+ }
+
+ /** Called externally when a task that was scheduled for execution should be cancelled. */
+ void cancelExecutingTask() {
+ mCallbackHandler.obtainMessage(MSG_CANCEL).sendToTarget();
+ }
+
+ /**
+ * @return Whether this context is available to handle incoming work.
+ */
+ boolean isAvailable() {
+ synchronized (mAvailableLock) {
+ return mAvailable;
+ }
}
@Override
public void taskFinished(int taskId, boolean reschedule) {
+ if (!verifyCallingUid()) {
+ return;
+ }
mCallbackHandler.obtainMessage(MSG_CALLBACK, taskId, reschedule ? 1 : 0)
.sendToTarget();
}
@Override
public void acknowledgeStopMessage(int taskId, boolean reschedule) {
+ if (!verifyCallingUid()) {
+ return;
+ }
mCallbackHandler.obtainMessage(MSG_CALLBACK, taskId, reschedule ? 1 : 0)
.sendToTarget();
}
@Override
public void acknowledgeStartMessage(int taskId, boolean ongoing) {
+ if (!verifyCallingUid()) {
+ return;
+ }
mCallbackHandler.obtainMessage(MSG_CALLBACK, taskId, ongoing ? 1 : 0).sendToTarget();
}
/**
- * Queue up this task to run on the client. This will execute the task as quickly as possible.
- * @param ts Status of the task to run.
- */
- public void addPendingTask(TaskStatus ts) {
- final TaskParams params = new TaskParams(ts.getTaskId(), ts.getExtras(), this);
- final ActiveTask newTask = new ActiveTask(params, VERB_PENDING);
- mCallbackHandler.obtainMessage(MSG_ADD_PENDING, newTask).sendToTarget();
- if (!mBound) {
- Intent intent = new Intent().setComponent(component);
- boolean binding = mContext.bindServiceAsUser(intent, this,
- Context.BIND_AUTO_CREATE | Context.BIND_NOT_FOREGROUND,
- new UserHandle(userId));
- if (!binding) {
- Log.e(TAG, component.getShortClassName() + " unavailable.");
- cancelPendingTask(ts);
- }
- }
- }
-
- /**
- * Called externally when a task that was scheduled for execution should be cancelled.
- * @param ts The status of the task to cancel.
- */
- public void cancelPendingTask(TaskStatus ts) {
- mCallbackHandler.obtainMessage(MSG_CANCEL, ts.getTaskId(), -1 /* arg2 */)
- .sendToTarget();
- }
-
- /**
- * MSG_TIMEOUT is sent with the {@link com.android.server.task.TaskServiceContext.ActiveTask}
- * set in the {@link Message#obj} field. This makes it easier to remove timeouts for a given
- * ActiveTask.
- * @param op Operation that is taking place.
- */
- private void scheduleOpTimeOut(ActiveTask op) {
- mCallbackHandler.removeMessages(MSG_TIMEOUT, op);
-
- final long timeoutMillis = (op.verb == VERB_EXECUTING) ?
- EXECUTING_TIMESLICE_MILLIS : OP_TIMEOUT_MILLIS;
- if (Log.isLoggable(TaskManagerService.TAG, Log.DEBUG)) {
- Slog.d(TAG, "Scheduling time out for '" + component.getShortClassName() + "' tId: " +
- op.params.getTaskId() + ", in " + (timeoutMillis / 1000) + " s");
- }
- Message m = mCallbackHandler.obtainMessage(MSG_TIMEOUT, op);
- mCallbackHandler.sendMessageDelayed(m, timeoutMillis);
- }
-
- /**
- * @return true if this task is pending or active within this context.
- */
- public boolean hasTaskPending(TaskStatus taskStatus) {
- synchronized (mPending) {
- return mPending.get(taskStatus.getTaskId()) != null;
- }
- }
-
- public boolean isBound() {
- return mBound;
- }
-
- /**
- * We acquire/release the wakelock on onServiceConnected/unbindService. This mirrors the work
+ * We acquire/release a wakelock on onServiceConnected/unbindService. This mirrors the work
* we intend to send to the client - we stop sending work when the service is unbound so until
* then we keep the wakelock.
- * @param name The concrete component name of the service that has
- * been connected.
+ * @param name The concrete component name of the service that has been connected.
* @param service The IBinder of the Service's communication channel,
*/
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
- mBound = true;
+ if (!name.equals(mRunningTask.getServiceComponent())) {
+ mCallbackHandler.obtainMessage(MSG_SHUTDOWN_EXECUTION).sendToTarget();
+ return;
+ }
this.service = ITaskService.Stub.asInterface(service);
- // Remove all timeouts. We've just connected to the client so there are no other
- // MSG_TIMEOUTs at this point.
+ // Remove all timeouts.
mCallbackHandler.removeMessages(MSG_TIMEOUT);
mWakeLock.acquire();
- mCallbackHandler.obtainMessage(MSG_CHECK_PENDING).sendToTarget();
+ mCallbackHandler.obtainMessage(MSG_SERVICE_BOUND).sendToTarget();
}
/**
- * When the client service crashes we can have a couple tasks executing, in various stages of
- * undress. We'll cancel all of them and request that they be rescheduled.
+ * If the client service crashes we reschedule this task and clean up.
* @param name The concrete component name of the service whose
*/
@Override
public void onServiceDisconnected(ComponentName name) {
- // Service disconnected... probably client crashed.
- startShutdown();
+ mCallbackHandler.obtainMessage(MSG_SHUTDOWN_EXECUTION).sendToTarget();
}
/**
- * We don't just shutdown outright - we make sure the scheduler isn't going to send us any more
- * tasks, then we do the shutdown.
+ * This class is reused across different clients, and passes itself in as a callback. Check
+ * whether the client exercising the callback is the client we expect.
+ * @return True if the binder calling is coming from the client we expect.
*/
- private void startShutdown() {
- mCompletedListener.onAllTasksCompleted(token);
- mCallbackHandler.obtainMessage(MSG_SHUTDOWN).sendToTarget();
- }
-
- /** Tracks a task across its various state changes. */
- private static class ActiveTask {
- final TaskParams params;
- int verb;
- AtomicBoolean cancelled = new AtomicBoolean();
-
- ActiveTask(TaskParams params, int verb) {
- this.params = params;
- this.verb = verb;
+ private boolean verifyCallingUid() {
+ if (mRunningTask == null || Binder.getCallingUid() != mRunningTask.getUid()) {
+ if (DEBUG) {
+ Slog.d(TAG, "Stale callback received, ignoring.");
+ }
+ return false;
}
-
- @Override
- public String toString() {
- return params.getTaskId() + " " + VERB_STRINGS[verb];
- }
+ return true;
}
/**
@@ -264,52 +258,67 @@
@Override
public void handleMessage(Message message) {
switch (message.what) {
- case MSG_ADD_PENDING:
- if (message.obj != null) {
- ActiveTask pendingTask = (ActiveTask) message.obj;
- mPending.put(pendingTask.params.getTaskId(), pendingTask);
- }
- // fall through.
- case MSG_CHECK_PENDING:
- checkPendingTasksH();
+ case MSG_SERVICE_BOUND:
+ handleServiceBoundH();
break;
case MSG_CALLBACK:
- ActiveTask receivedCallback = mPending.get(message.arg1);
- removeMessages(MSG_TIMEOUT, receivedCallback);
-
- if (Log.isLoggable(TaskManagerService.TAG, Log.DEBUG)) {
- Log.d(TAG, "MSG_CALLBACK of : " + receivedCallback);
+ if (DEBUG) {
+ Slog.d(TAG, "MSG_CALLBACK of : " + mRunningTask);
}
+ removeMessages(MSG_TIMEOUT);
- if (receivedCallback.verb == VERB_STARTING) {
+ if (mVerb == VERB_STARTING) {
final boolean workOngoing = message.arg2 == 1;
- handleStartedH(receivedCallback, workOngoing);
- } else if (receivedCallback.verb == VERB_EXECUTING ||
- receivedCallback.verb == VERB_STOPPING) {
+ handleStartedH(workOngoing);
+ } else if (mVerb == VERB_EXECUTING ||
+ mVerb == VERB_STOPPING) {
final boolean reschedule = message.arg2 == 1;
- handleFinishedH(receivedCallback, reschedule);
+ handleFinishedH(reschedule);
} else {
- if (Log.isLoggable(TaskManagerService.TAG, Log.DEBUG)) {
- Log.d(TAG, "Unrecognised callback: " + receivedCallback);
+ if (DEBUG) {
+ Slog.d(TAG, "Unrecognised callback: " + mRunningTask);
}
}
break;
case MSG_CANCEL:
- ActiveTask cancelled = mPending.get(message.arg1);
- handleCancelH(cancelled);
+ handleCancelH();
break;
case MSG_TIMEOUT:
- // Timeout msgs have the ActiveTask ref so we can remove them easily.
- handleOpTimeoutH((ActiveTask) message.obj);
+ handleOpTimeoutH();
break;
- case MSG_SHUTDOWN:
- handleShutdownH();
- break;
+ case MSG_SHUTDOWN_EXECUTION:
+ closeAndCleanupTaskH(true /* needsReschedule */);
default:
Log.e(TAG, "Unrecognised message: " + message);
}
}
+ /** Start the task on the service. */
+ private void handleServiceBoundH() {
+ if (mVerb != VERB_BINDING) {
+ Slog.e(TAG, "Sending onStartTask for a task that isn't pending. "
+ + VERB_STRINGS[mVerb]);
+ closeAndCleanupTaskH(false /* reschedule */);
+ return;
+ }
+ if (mCancelled.get()) {
+ if (DEBUG) {
+ Slog.d(TAG, "Task cancelled while waiting for bind to complete. "
+ + mRunningTask);
+ }
+ closeAndCleanupTaskH(true /* reschedule */);
+ return;
+ }
+ try {
+ mVerb = VERB_STARTING;
+ scheduleOpTimeOut();
+ service.startTask(mParams);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error sending onStart message to '" +
+ mRunningTask.getServiceComponent().getShortClassName() + "' ", e);
+ }
+ }
+
/**
* State behaviours.
* VERB_STARTING -> Successful start, change task to VERB_EXECUTING and post timeout.
@@ -317,24 +326,25 @@
* _EXECUTING -> Error
* _STOPPING -> Error
*/
- private void handleStartedH(ActiveTask started, boolean workOngoing) {
- switch (started.verb) {
+ private void handleStartedH(boolean workOngoing) {
+ switch (mVerb) {
case VERB_STARTING:
- started.verb = VERB_EXECUTING;
+ mVerb = VERB_EXECUTING;
if (!workOngoing) {
// Task is finished already so fast-forward to handleFinished.
- handleFinishedH(started, false);
+ handleFinishedH(false);
return;
- } else if (started.cancelled.get()) {
- // Cancelled *while* waiting for acknowledgeStartMessage from client.
- handleCancelH(started);
- return;
- } else {
- scheduleOpTimeOut(started);
}
+ if (mCancelled.get()) {
+ // Cancelled *while* waiting for acknowledgeStartMessage from client.
+ handleCancelH();
+ return;
+ }
+ scheduleOpTimeOut();
break;
default:
- Log.e(TAG, "Handling started task but task wasn't starting! " + started);
+ Log.e(TAG, "Handling started task but task wasn't starting! Was "
+ + VERB_STRINGS[mVerb] + ".");
return;
}
}
@@ -345,155 +355,104 @@
* _STARTING -> Error
* _PENDING -> Error
*/
- private void handleFinishedH(ActiveTask executedTask, boolean reschedule) {
- switch (executedTask.verb) {
+ private void handleFinishedH(boolean reschedule) {
+ switch (mVerb) {
case VERB_EXECUTING:
case VERB_STOPPING:
- closeAndCleanupTaskH(executedTask, reschedule);
+ closeAndCleanupTaskH(reschedule);
break;
default:
- Log.e(TAG, "Got an execution complete message for a task that wasn't being" +
- "executed. " + executedTask);
+ Slog.e(TAG, "Got an execution complete message for a task that wasn't being" +
+ "executed. Was " + VERB_STRINGS[mVerb] + ".");
}
}
/**
* A task can be in various states when a cancel request comes in:
- * VERB_PENDING -> Remove from queue.
- * _STARTING -> Mark as cancelled and wait for {@link #acknowledgeStartMessage(int)}.
+ * VERB_BINDING -> Cancelled before bind completed. Mark as cancelled and wait for
+ * {@link #onServiceConnected(android.content.ComponentName, android.os.IBinder)}
+ * _STARTING -> Mark as cancelled and wait for
+ * {@link TaskServiceContext#acknowledgeStartMessage(int, boolean)}
* _EXECUTING -> call {@link #sendStopMessageH}}.
* _ENDING -> No point in doing anything here, so we ignore.
*/
- private void handleCancelH(ActiveTask cancelledTask) {
- switch (cancelledTask.verb) {
- case VERB_PENDING:
- mPending.remove(cancelledTask.params.getTaskId());
- break;
+ private void handleCancelH() {
+ switch (mVerb) {
+ case VERB_BINDING:
case VERB_STARTING:
- cancelledTask.cancelled.set(true);
+ mCancelled.set(true);
break;
case VERB_EXECUTING:
- cancelledTask.verb = VERB_STOPPING;
- sendStopMessageH(cancelledTask);
+ sendStopMessageH();
break;
case VERB_STOPPING:
// Nada.
break;
default:
- Log.e(TAG, "Cancelling a task without a valid verb: " + cancelledTask);
+ Slog.e(TAG, "Cancelling a task without a valid verb: " + mVerb);
break;
}
}
- /**
- * This TaskServiceContext is shutting down. Remove all the tasks from the pending queue
- * and reschedule them as if they had failed.
- * Before posting this message, caller must invoke
- * {@link com.android.server.task.TaskCompletedListener#onAllTasksCompleted(int)}.
- */
- private void handleShutdownH() {
- for (int i = 0; i < mPending.size(); i++) {
- ActiveTask at = mPending.valueAt(i);
- closeAndCleanupTaskH(at, true /* needsReschedule */);
- }
- mWakeLock.release();
- mContext.unbindService(TaskServiceContext.this);
- service = null;
- mBound = false;
- }
-
- /**
- * MSG_TIMEOUT gets processed here.
- * @param timedOutTask The task that timed out.
- */
- private void handleOpTimeoutH(ActiveTask timedOutTask) {
+ /** Process MSG_TIMEOUT here. */
+ private void handleOpTimeoutH() {
if (Log.isLoggable(TaskManagerService.TAG, Log.DEBUG)) {
- Log.d(TAG, "MSG_TIMEOUT of " + component.getShortClassName() + " : "
- + timedOutTask.params.getTaskId());
+ Log.d(TAG, "MSG_TIMEOUT of " +
+ mRunningTask.getServiceComponent().getShortClassName() + " : "
+ + mParams.getTaskId());
}
- final int taskId = timedOutTask.params.getTaskId();
- switch (timedOutTask.verb) {
+ final int taskId = mParams.getTaskId();
+ switch (mVerb) {
case VERB_STARTING:
// Client unresponsive - wedged or failed to respond in time. We don't really
// know what happened so let's log it and notify the TaskManager
// FINISHED/NO-RETRY.
Log.e(TAG, "No response from client for onStartTask '" +
- component.getShortClassName() + "' tId: " + taskId);
- closeAndCleanupTaskH(timedOutTask, false /* needsReschedule */);
+ mRunningTask.getServiceComponent().getShortClassName() + "' tId: "
+ + taskId);
+ closeAndCleanupTaskH(false /* needsReschedule */);
break;
case VERB_STOPPING:
// At least we got somewhere, so fail but ask the TaskManager to reschedule.
Log.e(TAG, "No response from client for onStopTask, '" +
- component.getShortClassName() + "' tId: " + taskId);
- closeAndCleanupTaskH(timedOutTask, true /* needsReschedule */);
+ mRunningTask.getServiceComponent().getShortClassName() + "' tId: "
+ + taskId);
+ closeAndCleanupTaskH(true /* needsReschedule */);
break;
case VERB_EXECUTING:
// Not an error - client ran out of time.
Log.i(TAG, "Client timed out while executing (no taskFinished received)." +
" Reporting failure and asking for reschedule. " +
- component.getShortClassName() + "' tId: " + taskId);
- sendStopMessageH(timedOutTask);
+ mRunningTask.getServiceComponent().getShortClassName() + "' tId: "
+ + taskId);
+ sendStopMessageH();
break;
default:
Log.e(TAG, "Handling timeout for an unknown active task state: "
- + timedOutTask);
+ + mRunningTask);
return;
}
}
/**
- * Called on the handler thread. Checks the state of the pending queue and starts the task
- * if it can. The task only starts if there is capacity on the service.
+ * Already running, need to stop. Will switch {@link #mVerb} from VERB_EXECUTING ->
+ * VERB_STOPPING.
*/
- private void checkPendingTasksH() {
- if (!mBound) {
- return;
- }
- for (int i = 0; i < mPending.size() && i < defaultMaxActiveTasksPerService; i++) {
- ActiveTask at = mPending.valueAt(i);
- if (at.verb != VERB_PENDING) {
- continue;
- }
- sendStartMessageH(at);
- }
- }
-
- /**
- * Already running, need to stop. Rund on handler.
- * @param stoppingTask Task we are sending onStopMessage for. This task will be moved from
- * VERB_EXECUTING -> VERB_STOPPING.
- */
- private void sendStopMessageH(ActiveTask stoppingTask) {
- mCallbackHandler.removeMessages(MSG_TIMEOUT, stoppingTask);
- if (stoppingTask.verb != VERB_EXECUTING) {
- Log.e(TAG, "Sending onStopTask for a task that isn't started. " + stoppingTask);
- // TODO: Handle error?
+ private void sendStopMessageH() {
+ mCallbackHandler.removeMessages(MSG_TIMEOUT);
+ if (mVerb != VERB_EXECUTING) {
+ Log.e(TAG, "Sending onStopTask for a task that isn't started. " + mRunningTask);
+ closeAndCleanupTaskH(false /* reschedule */);
return;
}
try {
- service.stopTask(stoppingTask.params);
- stoppingTask.verb = VERB_STOPPING;
- scheduleOpTimeOut(stoppingTask);
+ mVerb = VERB_STOPPING;
+ scheduleOpTimeOut();
+ service.stopTask(mParams);
} catch (RemoteException e) {
Log.e(TAG, "Error sending onStopTask to client.", e);
- closeAndCleanupTaskH(stoppingTask, false);
- }
- }
-
- /** Start the task on the service. */
- private void sendStartMessageH(ActiveTask pendingTask) {
- if (pendingTask.verb != VERB_PENDING) {
- Log.e(TAG, "Sending onStartTask for a task that isn't pending. " + pendingTask);
- // TODO: Handle error?
- }
- try {
- service.startTask(pendingTask.params);
- pendingTask.verb = VERB_STARTING;
- scheduleOpTimeOut(pendingTask);
- } catch (RemoteException e) {
- Log.e(TAG, "Error sending onStart message to '" + component.getShortClassName()
- + "' ", e);
+ closeAndCleanupTaskH(false);
}
}
@@ -503,13 +462,42 @@
* or from acknowledging the stop message we sent. Either way, we're done tracking it and
* we want to clean up internally.
*/
- private void closeAndCleanupTaskH(ActiveTask completedTask, boolean reschedule) {
- removeMessages(MSG_TIMEOUT, completedTask);
- mPending.remove(completedTask.params.getTaskId());
- if (mPending.size() == 0) {
- startShutdown();
+ private void closeAndCleanupTaskH(boolean reschedule) {
+ removeMessages(MSG_TIMEOUT);
+ mWakeLock.release();
+ mContext.unbindService(TaskServiceContext.this);
+ mWakeLock = null;
+
+ mRunningTask = null;
+ mParams = null;
+ mVerb = -1;
+ mCancelled.set(false);
+
+ service = null;
+
+ mCompletedListener.onTaskCompleted(mRunningTask, reschedule);
+ synchronized (mAvailableLock) {
+ mAvailable = true;
}
- mCompletedListener.onTaskCompleted(token, completedTask.params.getTaskId(), reschedule);
+ }
+
+ /**
+ * Called when sending a message to the client, over whose execution we have no control. If we
+ * haven't received a response in a certain amount of time, we want to give up and carry on
+ * with life.
+ */
+ private void scheduleOpTimeOut() {
+ mCallbackHandler.removeMessages(MSG_TIMEOUT);
+
+ final long timeoutMillis = (mVerb == VERB_EXECUTING) ?
+ EXECUTING_TIMESLICE_MILLIS : OP_TIMEOUT_MILLIS;
+ if (DEBUG) {
+ Slog.d(TAG, "Scheduling time out for '" +
+ mRunningTask.getServiceComponent().getShortClassName() + "' tId: " +
+ mParams.getTaskId() + ", in " + (timeoutMillis / 1000) + " s");
+ }
+ Message m = mCallbackHandler.obtainMessage(MSG_TIMEOUT);
+ mCallbackHandler.sendMessageDelayed(m, timeoutMillis);
}
}
}
diff --git a/services/core/java/com/android/server/task/TaskStore.java b/services/core/java/com/android/server/task/TaskStore.java
index 81187c8..f72ab22 100644
--- a/services/core/java/com/android/server/task/TaskStore.java
+++ b/services/core/java/com/android/server/task/TaskStore.java
@@ -18,10 +18,16 @@
import android.app.task.Task;
import android.content.Context;
+import android.util.ArraySet;
+import android.util.Slog;
import android.util.SparseArray;
import com.android.server.task.controllers.TaskStatus;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.Set;
+
/**
* Maintain a list of classes, and accessor methods/logic for these tasks.
* This class offers the following functionality:
@@ -35,53 +41,122 @@
* - This class is <strong>not</strong> thread-safe.
*/
public class TaskStore {
-
- /**
- * Master list, indexed by {@link com.android.server.task.controllers.TaskStatus#hashCode()}.
- */
- final SparseArray<TaskStatus> mTasks;
+ private static final String TAG = "TaskManagerStore";
+ /** Threshold to adjust how often we want to write to the db. */
+ private static final int MAX_OPS_BEFORE_WRITE = 1;
+ final ArraySet<TaskStatus> mTasks;
final Context mContext;
+ private int mDirtyOperations;
+
TaskStore(Context context) {
- mTasks = intialiseTaskMapFromDisk();
+ mTasks = intialiseTasksFromDisk();
mContext = context;
+ mDirtyOperations = 0;
}
/**
- * Add a task to the master list, persisting it if necessary.
- * Will first check to see if the task already exists. If so, it will replace it.
- * {@link android.content.pm.PackageManager} is queried to see if the calling package has
- * permission to
- * @param task Task to add.
- * @return The initialised TaskStatus object if this operation was successful, null if it
- * failed.
+ * Add a task to the master list, persisting it if necessary. If the TaskStatus already exists,
+ * it will be replaced.
+ * @param taskStatus Task to add.
+ * @return true if the operation succeeded.
*/
- public TaskStatus addNewTaskForUser(Task task, int userId, int uId,
- boolean canPersistTask) {
- TaskStatus taskStatus = TaskStatus.getForTaskAndUser(task, userId, uId);
- if (canPersistTask && task.isPeriodic()) {
- if (writeStatusToDisk()) {
- mTasks.put(taskStatus.hashCode(), taskStatus);
+ public boolean add(TaskStatus taskStatus) {
+ if (taskStatus.isPersisted()) {
+ if (!maybeWriteStatusToDisk()) {
+ return false;
}
}
- return taskStatus;
+ mTasks.remove(taskStatus);
+ mTasks.add(taskStatus);
+ return true;
+ }
+
+ public int size() {
+ return mTasks.size();
}
/**
- * Remove the provided task. Will also delete the task if it was persisted. Note that this
- * function does not return the validity of the operation, as we assume a delete will always
- * succeed.
- * @param task Task to remove.
+ * Remove the provided task. Will also delete the task if it was persisted.
+ * @return The TaskStatus that was removed, or null if an invalid token was provided.
*/
- public void remove(Task task) {
+ public boolean remove(TaskStatus taskStatus) {
+ boolean removed = mTasks.remove(taskStatus);
+ if (!removed) {
+ Slog.e(TAG, "Error removing task: " + taskStatus);
+ return false;
+ } else {
+ maybeWriteStatusToDisk();
+ }
+ return true;
+ }
+ /**
+ * Removes all TaskStatus objects for a given uid from the master list. Note that it is
+ * possible to remove a task that is pending/active. This operation will succeed, and the
+ * removal will take effect when the task has completed executing.
+ * @param uid Uid of the requesting app.
+ * @return True if at least one task was removed, false if nothing matching the provided uId
+ * was found.
+ */
+ public boolean removeAllByUid(int uid) {
+ Iterator<TaskStatus> it = mTasks.iterator();
+ boolean removed = false;
+ while (it.hasNext()) {
+ TaskStatus ts = it.next();
+ if (ts.getUid() == uid) {
+ it.remove();
+ removed = true;
+ }
+ }
+ if (removed) {
+ maybeWriteStatusToDisk();
+ }
+ return removed;
+ }
+
+ /**
+ * Remove the TaskStatus that matches the provided uId and taskId. Note that it is possible
+ * to remove a task that is pending/active. This operation will succeed, and the removal will
+ * take effect when the task has completed executing.
+ * @param uid Uid of the requesting app.
+ * @param taskId Task id, specified at schedule-time.
+ * @return true if a removal occurred, false if the provided parameters didn't match anything.
+ */
+ public boolean remove(int uid, int taskId) {
+ Iterator<TaskStatus> it = mTasks.iterator();
+ while (it.hasNext()) {
+ TaskStatus ts = it.next();
+ if (ts.getUid() == uid && ts.getTaskId() == taskId) {
+ it.remove();
+ maybeWriteStatusToDisk();
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * @return The live array of TaskStatus objects.
+ */
+ public Set<TaskStatus> getTasks() {
+ return mTasks;
}
/**
* Every time the state changes we write all the tasks in one swathe, instead of trying to
* track incremental changes.
+ * @return Whether the operation was successful. This will only fail for e.g. if the system is
+ * low on storage. If this happens, we continue as normal
*/
- private boolean writeStatusToDisk() {
+ private boolean maybeWriteStatusToDisk() {
+ mDirtyOperations++;
+ if (mDirtyOperations > MAX_OPS_BEFORE_WRITE) {
+ for (TaskStatus ts : mTasks) {
+ //
+ }
+ mDirtyOperations = 0;
+ }
return true;
}
@@ -90,21 +165,7 @@
* @return
*/
// TODO: Implement this.
- private SparseArray<TaskStatus> intialiseTaskMapFromDisk() {
- return new SparseArray<TaskStatus>();
- }
-
- /**
- * @return the number of tasks in the store
- */
- public int size() {
- return mTasks.size();
- }
-
- /**
- * @return The live array of TaskStatus objects.
- */
- public SparseArray<TaskStatus> getTasks() {
- return mTasks;
+ private ArraySet<TaskStatus> intialiseTasksFromDisk() {
+ return new ArraySet<TaskStatus>();
}
}
diff --git a/services/core/java/com/android/server/task/controllers/ConnectivityController.java b/services/core/java/com/android/server/task/controllers/ConnectivityController.java
index a0038c5d..474af8f 100644
--- a/services/core/java/com/android/server/task/controllers/ConnectivityController.java
+++ b/services/core/java/com/android/server/task/controllers/ConnectivityController.java
@@ -25,6 +25,7 @@
import android.net.NetworkInfo;
import android.os.UserHandle;
import android.util.Log;
+import android.util.Slog;
import com.android.server.task.TaskManagerService;
@@ -32,21 +33,33 @@
import java.util.List;
/**
- *
+ * Handles changes in connectivity.
+ * We are only interested in metered vs. unmetered networks, and we're interested in them on a
+ * per-user basis.
*/
public class ConnectivityController extends StateController {
private static final String TAG = "TaskManager.Connectivity";
+ private static final boolean DEBUG = true;
private final List<TaskStatus> mTrackedTasks = new LinkedList<TaskStatus>();
private final BroadcastReceiver mConnectivityChangedReceiver =
new ConnectivityChangedReceiver();
+ /** Singleton. */
+ private static ConnectivityController mSingleton;
/** Track whether the latest active network is metered. */
private boolean mMetered;
/** Track whether the latest active network is connected. */
private boolean mConnectivity;
- public ConnectivityController(TaskManagerService service) {
+ public static synchronized ConnectivityController get(TaskManagerService taskManager) {
+ if (mSingleton == null) {
+ mSingleton = new ConnectivityController(taskManager);
+ }
+ return mSingleton;
+ }
+
+ private ConnectivityController(TaskManagerService service) {
super(service);
// Register connectivity changed BR.
IntentFilter intentFilter = new IntentFilter();
@@ -56,7 +69,7 @@
}
@Override
- public void maybeTrackTaskState(TaskStatus taskStatus) {
+ public synchronized void maybeStartTrackingTask(TaskStatus taskStatus) {
if (taskStatus.hasConnectivityConstraint() || taskStatus.hasMeteredConstraint()) {
taskStatus.connectivityConstraintSatisfied.set(mConnectivity);
taskStatus.meteredConstraintSatisfied.set(mMetered);
@@ -65,21 +78,28 @@
}
@Override
- public void removeTaskStateIfTracked(TaskStatus taskStatus) {
+ public synchronized void maybeStopTrackingTask(TaskStatus taskStatus) {
mTrackedTasks.remove(taskStatus);
}
/**
- *
+ * @param userId Id of the user for whom we are updating the connectivity state.
*/
- private void updateTrackedTasks() {
+ private void updateTrackedTasks(int userId) {
+ boolean changed = false;
for (TaskStatus ts : mTrackedTasks) {
+ if (ts.getUserId() != userId) {
+ continue;
+ }
boolean prevIsConnected = ts.connectivityConstraintSatisfied.getAndSet(mConnectivity);
boolean prevIsMetered = ts.meteredConstraintSatisfied.getAndSet(mMetered);
if (prevIsConnected != mConnectivity || prevIsMetered != mMetered) {
- mStateChangedListener.onTaskStateChanged(ts);
+ changed = true;
}
}
+ if (changed) {
+ mStateChangedListener.onControllerStateChanged();
+ }
}
class ConnectivityChangedReceiver extends BroadcastReceiver {
@@ -103,17 +123,20 @@
context.getSystemService(Context.CONNECTIVITY_SERVICE);
final NetworkInfo activeNetwork = connManager.getActiveNetworkInfo();
// This broadcast gets sent a lot, only update if the active network has changed.
- if (activeNetwork.getType() == networkType) {
+ if (activeNetwork != null && activeNetwork.getType() == networkType) {
+ final int userid = context.getUserId();
mMetered = false;
mConnectivity =
!intent.getBooleanExtra(ConnectivityManager.EXTRA_NO_CONNECTIVITY, false);
if (mConnectivity) { // No point making the call if we know there's no conn.
mMetered = connManager.isActiveNetworkMetered();
}
- updateTrackedTasks();
+ updateTrackedTasks(userid);
}
} else {
- Log.w(TAG, "Unrecognised action in intent: " + action);
+ if (DEBUG) {
+ Slog.d(TAG, "Unrecognised action in intent: " + action);
+ }
}
}
};
diff --git a/services/core/java/com/android/server/task/controllers/IdleController.java b/services/core/java/com/android/server/task/controllers/IdleController.java
index a319a31..9489644 100644
--- a/services/core/java/com/android/server/task/controllers/IdleController.java
+++ b/services/core/java/com/android/server/task/controllers/IdleController.java
@@ -49,7 +49,7 @@
private static Object sCreationLock = new Object();
private static volatile IdleController sController;
- public IdleController getController(TaskManagerService service) {
+ public static IdleController get(TaskManagerService service) {
synchronized (sCreationLock) {
if (sController == null) {
sController = new IdleController(service);
@@ -67,7 +67,7 @@
* StateController interface
*/
@Override
- public void maybeTrackTaskState(TaskStatus taskStatus) {
+ public void maybeStartTrackingTask(TaskStatus taskStatus) {
if (taskStatus.hasIdleConstraint()) {
synchronized (mTrackedTasks) {
mTrackedTasks.add(taskStatus);
@@ -77,7 +77,7 @@
}
@Override
- public void removeTaskStateIfTracked(TaskStatus taskStatus) {
+ public void maybeStopTrackingTask(TaskStatus taskStatus) {
synchronized (mTrackedTasks) {
mTrackedTasks.remove(taskStatus);
}
@@ -90,9 +90,9 @@
synchronized (mTrackedTasks) {
for (TaskStatus task : mTrackedTasks) {
task.idleConstraintSatisfied.set(isIdle);
- mStateChangedListener.onTaskStateChanged(task);
}
}
+ mStateChangedListener.onControllerStateChanged();
}
/**
diff --git a/services/core/java/com/android/server/task/controllers/StateController.java b/services/core/java/com/android/server/task/controllers/StateController.java
index e1cd662..ed31eac 100644
--- a/services/core/java/com/android/server/task/controllers/StateController.java
+++ b/services/core/java/com/android/server/task/controllers/StateController.java
@@ -42,10 +42,10 @@
* Also called when updating a task, so implementing controllers have to be aware of
* preexisting tasks.
*/
- public abstract void maybeTrackTaskState(TaskStatus taskStatus);
+ public abstract void maybeStartTrackingTask(TaskStatus taskStatus);
/**
* Remove task - this will happen if the task is cancelled, completed, etc.
*/
- public abstract void removeTaskStateIfTracked(TaskStatus taskStatus);
+ public abstract void maybeStopTrackingTask(TaskStatus taskStatus);
}
diff --git a/services/core/java/com/android/server/task/controllers/TaskStatus.java b/services/core/java/com/android/server/task/controllers/TaskStatus.java
index d270016..b7f84ec 100644
--- a/services/core/java/com/android/server/task/controllers/TaskStatus.java
+++ b/services/core/java/com/android/server/task/controllers/TaskStatus.java
@@ -18,7 +18,6 @@
import android.app.task.Task;
import android.content.ComponentName;
-import android.content.pm.PackageParser;
import android.os.Bundle;
import android.os.SystemClock;
import android.os.UserHandle;
@@ -39,65 +38,67 @@
*/
public class TaskStatus {
final Task task;
- final int taskId;
final int uId;
- final Bundle extras;
+ /** At reschedule time we need to know whether to update task on disk. */
+ final boolean persisted;
+
+ // Constraints.
final AtomicBoolean chargingConstraintSatisfied = new AtomicBoolean();
- final AtomicBoolean timeConstraintSatisfied = new AtomicBoolean();
+ final AtomicBoolean timeDelayConstraintSatisfied = new AtomicBoolean();
+ final AtomicBoolean deadlineConstraintSatisfied = new AtomicBoolean();
final AtomicBoolean idleConstraintSatisfied = new AtomicBoolean();
final AtomicBoolean meteredConstraintSatisfied = new AtomicBoolean();
final AtomicBoolean connectivityConstraintSatisfied = new AtomicBoolean();
- private final boolean hasChargingConstraint;
- private final boolean hasTimingConstraint;
- private final boolean hasIdleConstraint;
- private final boolean hasMeteredConstraint;
- private final boolean hasConnectivityConstraint;
-
+ /**
+ * Earliest point in the future at which this task will be eligible to run. A value of 0
+ * indicates there is no delay constraint. See {@link #hasTimingDelayConstraint()}.
+ */
private long earliestRunTimeElapsedMillis;
+ /**
+ * Latest point in the future at which this task must be run. A value of {@link Long#MAX_VALUE}
+ * indicates there is no deadline constraint. See {@link #hasDeadlineConstraint()}.
+ */
private long latestRunTimeElapsedMillis;
+ private final int numFailures;
+
/** Provide a handle to the service that this task will be run on. */
public int getServiceToken() {
return uId;
}
- /** Generate a TaskStatus object for a given task and uid. */
- // TODO: reimplement this to reuse these objects instead of creating a new one each time?
- public static TaskStatus getForTaskAndUser(Task task, int userId, int uId) {
- return new TaskStatus(task, userId, uId);
- }
-
- /** Set up the state of a newly scheduled task. */
- TaskStatus(Task task, int userId, int uId) {
+ /** Create a newly scheduled task. */
+ public TaskStatus(Task task, int uId, boolean persisted) {
this.task = task;
- this.taskId = task.getTaskId();
- this.extras = task.getExtras();
this.uId = uId;
+ this.numFailures = 0;
+ this.persisted = persisted;
- hasChargingConstraint = task.isRequireCharging();
- hasIdleConstraint = task.isRequireDeviceIdle();
-
+ final long elapsedNow = SystemClock.elapsedRealtime();
// Timing constraints
if (task.isPeriodic()) {
- long elapsedNow = SystemClock.elapsedRealtime();
earliestRunTimeElapsedMillis = elapsedNow;
latestRunTimeElapsedMillis = elapsedNow + task.getIntervalMillis();
- hasTimingConstraint = true;
- } else if (task.getMinLatencyMillis() != 0L || task.getMaxExecutionDelayMillis() != 0L) {
- earliestRunTimeElapsedMillis = task.getMinLatencyMillis() > 0L ?
- task.getMinLatencyMillis() : Long.MAX_VALUE;
- latestRunTimeElapsedMillis = task.getMaxExecutionDelayMillis() > 0L ?
- task.getMaxExecutionDelayMillis() : Long.MAX_VALUE;
- hasTimingConstraint = true;
} else {
- hasTimingConstraint = false;
+ earliestRunTimeElapsedMillis = task.hasEarlyConstraint() ?
+ elapsedNow + task.getMinLatencyMillis() : 0L;
+ latestRunTimeElapsedMillis = task.hasLateConstraint() ?
+ elapsedNow + task.getMaxExecutionDelayMillis() : Long.MAX_VALUE;
}
+ }
- // Networking constraints
- hasMeteredConstraint = task.getNetworkCapabilities() == Task.NetworkType.UNMETERED;
- hasConnectivityConstraint = task.getNetworkCapabilities() == Task.NetworkType.ANY;
+ public TaskStatus(TaskStatus rescheduling, long newEarliestRuntimeElapsed,
+ long newLatestRuntimeElapsed, int backoffAttempt) {
+ this.task = rescheduling.task;
+
+ this.uId = rescheduling.getUid();
+ this.persisted = rescheduling.isPersisted();
+ this.numFailures = backoffAttempt;
+
+ earliestRunTimeElapsedMillis = newEarliestRuntimeElapsed;
+ latestRunTimeElapsedMillis = newLatestRuntimeElapsed;
}
public Task getTask() {
@@ -105,7 +106,11 @@
}
public int getTaskId() {
- return taskId;
+ return task.getId();
+ }
+
+ public int getNumFailures() {
+ return numFailures;
}
public ComponentName getServiceComponent() {
@@ -121,52 +126,60 @@
}
public Bundle getExtras() {
- return extras;
+ return task.getExtras();
}
- boolean hasConnectivityConstraint() {
- return hasConnectivityConstraint;
+ public boolean hasConnectivityConstraint() {
+ return task.getNetworkCapabilities() == Task.NetworkType.ANY;
}
- boolean hasMeteredConstraint() {
- return hasMeteredConstraint;
+ public boolean hasMeteredConstraint() {
+ return task.getNetworkCapabilities() == Task.NetworkType.UNMETERED;
}
- boolean hasChargingConstraint() {
- return hasChargingConstraint;
+ public boolean hasChargingConstraint() {
+ return task.isRequireCharging();
}
- boolean hasTimingConstraint() {
- return hasTimingConstraint;
+ public boolean hasTimingDelayConstraint() {
+ return earliestRunTimeElapsedMillis != 0L;
}
- boolean hasIdleConstraint() {
- return hasIdleConstraint;
+ public boolean hasDeadlineConstraint() {
+ return latestRunTimeElapsedMillis != Long.MAX_VALUE;
}
- long getEarliestRunTime() {
+ public boolean hasIdleConstraint() {
+ return task.isRequireDeviceIdle();
+ }
+
+ public long getEarliestRunTime() {
return earliestRunTimeElapsedMillis;
}
- long getLatestRunTime() {
+ public long getLatestRunTimeElapsed() {
return latestRunTimeElapsedMillis;
}
+ public boolean isPersisted() {
+ return persisted;
+ }
/**
- * @return whether this task is ready to run, based on its requirements.
+ * @return Whether or not this task is ready to run, based on its requirements.
*/
public synchronized boolean isReady() {
- return (!hasChargingConstraint || chargingConstraintSatisfied.get())
- && (!hasTimingConstraint || timeConstraintSatisfied.get())
- && (!hasConnectivityConstraint || connectivityConstraintSatisfied.get())
- && (!hasMeteredConstraint || meteredConstraintSatisfied.get())
- && (!hasIdleConstraint || idleConstraintSatisfied.get());
+ return (!hasChargingConstraint() || chargingConstraintSatisfied.get())
+ && (!hasTimingDelayConstraint() || timeDelayConstraintSatisfied.get())
+ && (!hasConnectivityConstraint() || connectivityConstraintSatisfied.get())
+ && (!hasMeteredConstraint() || meteredConstraintSatisfied.get())
+ && (!hasIdleConstraint() || idleConstraintSatisfied.get())
+ && (!hasDeadlineConstraint() || deadlineConstraintSatisfied.get());
}
@Override
public int hashCode() {
int result = getServiceComponent().hashCode();
- result = 31 * result + taskId;
+ result = 31 * result + task.getId();
result = 31 * result + uId;
return result;
}
@@ -177,14 +190,14 @@
if (!(o instanceof TaskStatus)) return false;
TaskStatus that = (TaskStatus) o;
- return ((taskId == that.taskId)
+ return ((task.getId() == that.task.getId())
&& (uId == that.uId)
&& (getServiceComponent().equals(that.getServiceComponent())));
}
// Dumpsys infrastructure
public void dump(PrintWriter pw, String prefix) {
- pw.print(prefix); pw.print("Task "); pw.println(taskId);
+ pw.print(prefix); pw.print("Task "); pw.println(task.getId());
pw.print(prefix); pw.print("uid="); pw.println(uId);
pw.print(prefix); pw.print("component="); pw.println(task.getService());
}
diff --git a/services/core/java/com/android/server/task/controllers/TimeController.java b/services/core/java/com/android/server/task/controllers/TimeController.java
index 6d97a53..72f312c 100644
--- a/services/core/java/com/android/server/task/controllers/TimeController.java
+++ b/services/core/java/com/android/server/task/controllers/TimeController.java
@@ -23,7 +23,6 @@
import android.content.Intent;
import android.content.IntentFilter;
import android.os.SystemClock;
-import android.util.Log;
import com.android.server.task.TaskManagerService;
@@ -54,8 +53,17 @@
private AlarmManager mAlarmService = null;
/** List of tracked tasks, sorted asc. by deadline */
private final List<TaskStatus> mTrackedTasks = new LinkedList<TaskStatus>();
+ /** Singleton. */
+ private static TimeController mSingleton;
- public TimeController(TaskManagerService service) {
+ public static synchronized TimeController get(TaskManagerService taskManager) {
+ if (mSingleton == null) {
+ mSingleton = new TimeController(taskManager);
+ }
+ return mSingleton;
+ }
+
+ private TimeController(TaskManagerService service) {
super(service);
mTaskExpiredAlarmIntent =
PendingIntent.getBroadcast(mContext, 0 /* ignored */,
@@ -75,8 +83,8 @@
* list.
*/
@Override
- public synchronized void maybeTrackTaskState(TaskStatus task) {
- if (task.hasTimingConstraint()) {
+ public synchronized void maybeStartTrackingTask(TaskStatus task) {
+ if (task.hasTimingDelayConstraint()) {
ListIterator<TaskStatus> it = mTrackedTasks.listIterator(mTrackedTasks.size());
while (it.hasPrevious()) {
TaskStatus ts = it.previous();
@@ -85,13 +93,13 @@
it.remove();
it.add(task);
break;
- } else if (ts.getLatestRunTime() < task.getLatestRunTime()) {
+ } else if (ts.getLatestRunTimeElapsed() < task.getLatestRunTimeElapsed()) {
// Insert
it.add(task);
break;
}
}
- maybeUpdateAlarms(task.getEarliestRunTime(), task.getLatestRunTime());
+ maybeUpdateAlarms(task.getEarliestRunTime(), task.getLatestRunTimeElapsed());
}
}
@@ -100,12 +108,12 @@
* so, update them.
*/
@Override
- public synchronized void removeTaskStateIfTracked(TaskStatus taskStatus) {
+ public synchronized void maybeStopTrackingTask(TaskStatus taskStatus) {
if (mTrackedTasks.remove(taskStatus)) {
if (mNextDelayExpiredElapsedMillis <= taskStatus.getEarliestRunTime()) {
handleTaskDelayExpired();
}
- if (mNextTaskExpiredElapsedMillis <= taskStatus.getLatestRunTime()) {
+ if (mNextTaskExpiredElapsedMillis <= taskStatus.getLatestRunTimeElapsed()) {
handleTaskDeadlineExpired();
}
}
@@ -140,10 +148,10 @@
* back and forth.
*/
private boolean canStopTrackingTask(TaskStatus taskStatus) {
- final long elapsedNowMillis = SystemClock.elapsedRealtime();
- return taskStatus.timeConstraintSatisfied.get() &&
- (taskStatus.getLatestRunTime() == Long.MAX_VALUE ||
- taskStatus.getLatestRunTime() < elapsedNowMillis);
+ return (!taskStatus.hasTimingDelayConstraint() ||
+ taskStatus.timeDelayConstraintSatisfied.get()) &&
+ (!taskStatus.hasDeadlineConstraint() ||
+ taskStatus.deadlineConstraintSatisfied.get());
}
private void maybeUpdateAlarms(long delayExpiredElapsed, long deadlineExpiredElapsed) {
@@ -174,10 +182,10 @@
Iterator<TaskStatus> it = mTrackedTasks.iterator();
while (it.hasNext()) {
TaskStatus ts = it.next();
- final long taskDeadline = ts.getLatestRunTime();
+ final long taskDeadline = ts.getLatestRunTimeElapsed();
if (taskDeadline <= nowElapsedMillis) {
- ts.timeConstraintSatisfied.set(true);
+ ts.deadlineConstraintSatisfied.set(true);
mStateChangedListener.onTaskDeadlineExpired(ts);
it.remove();
} else { // Sorted by expiry time, so take the next one and stop.
@@ -199,10 +207,12 @@
Iterator<TaskStatus> it = mTrackedTasks.iterator();
while (it.hasNext()) {
final TaskStatus ts = it.next();
+ if (!ts.hasTimingDelayConstraint()) {
+ continue;
+ }
final long taskDelayTime = ts.getEarliestRunTime();
if (taskDelayTime < nowElapsedMillis) {
- ts.timeConstraintSatisfied.set(true);
- mStateChangedListener.onTaskStateChanged(ts);
+ ts.timeDelayConstraintSatisfied.set(true);
if (canStopTrackingTask(ts)) {
it.remove();
}
@@ -212,6 +222,7 @@
}
}
}
+ mStateChangedListener.onControllerStateChanged();
maybeUpdateAlarms(nextDelayTime, Long.MAX_VALUE);
}
diff --git a/services/core/java/com/android/server/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/java/com/android/server/wm/WindowAnimator.java b/services/core/java/com/android/server/wm/WindowAnimator.java
index 266527d..6fdd535 100644
--- a/services/core/java/com/android/server/wm/WindowAnimator.java
+++ b/services/core/java/com/android/server/wm/WindowAnimator.java
@@ -36,6 +36,7 @@
import android.view.Display;
import android.view.SurfaceControl;
import android.view.WindowManagerPolicy;
+import android.view.animation.AlphaAnimation;
import android.view.animation.Animation;
import com.android.server.wm.WindowManagerService.LayoutFields;
@@ -50,6 +51,9 @@
public class WindowAnimator {
private static final String TAG = "WindowAnimator";
+ /** How long to give statusbar to clear the private keyguard flag when animating out */
+ private static final long KEYGUARD_ANIM_TIMEOUT_MS = 1000;
+
final WindowManagerService mService;
final Context mContext;
final WindowManagerPolicy mPolicy;
@@ -82,6 +86,8 @@
boolean mInitialized = false;
+ boolean mKeyguardGoingAway;
+
// forceHiding states.
static final int KEYGUARD_NOT_SHOWN = 0;
static final int KEYGUARD_ANIMATING_IN = 1;
@@ -213,6 +219,29 @@
final WindowList windows = mService.getWindowListLocked(displayId);
ArrayList<WindowStateAnimator> unForceHiding = null;
boolean wallpaperInUnForceHiding = false;
+
+ if (mKeyguardGoingAway) {
+ for (int i = windows.size() - 1; i >= 0; i--) {
+ WindowState win = windows.get(i);
+ if (!mPolicy.isKeyguardHostWindow(win.mAttrs)) {
+ continue;
+ }
+ final WindowStateAnimator winAnimator = win.mWinAnimator;
+ if (mPolicy.doesForceHide(win.mAttrs)) {
+ if (!winAnimator.mAnimating) {
+ // Create a new animation to delay until keyguard is gone on its own.
+ winAnimator.mAnimation = new AlphaAnimation(1.0f, 1.0f);
+ winAnimator.mAnimation.setDuration(KEYGUARD_ANIM_TIMEOUT_MS);
+ winAnimator.mAnimationIsEntrance = false;
+ }
+ } else {
+ mKeyguardGoingAway = false;
+ winAnimator.clearAnimation();
+ }
+ break;
+ }
+ }
+
mForceHiding = KEYGUARD_NOT_SHOWN;
for (int i = windows.size() - 1; i >= 0; i--) {
@@ -239,7 +268,7 @@
}
}
- if (mPolicy.doesForceHide(win, win.mAttrs)) {
+ if (mPolicy.doesForceHide(win.mAttrs)) {
if (!wasAnimating && nowAnimating) {
if (WindowManagerService.DEBUG_ANIM ||
WindowManagerService.DEBUG_VISIBILITY) Slog.v(TAG,
@@ -252,6 +281,11 @@
getPendingLayoutChanges(displayId));
}
mService.mFocusMayChange = true;
+ } else if (mKeyguardGoingAway && !nowAnimating) {
+ // Timeout!!
+ Slog.e(TAG, "Timeout waiting for animation to startup");
+ mPolicy.startKeyguardExitAnimation(0);
+ mKeyguardGoingAway = false;
}
if (win.isReadyForDisplay()) {
if (nowAnimating) {
@@ -265,7 +299,7 @@
}
}
if (WindowManagerService.DEBUG_VISIBILITY) Slog.v(TAG,
- "Force hide " + mForceHiding
+ "Force hide " + forceHidingToString()
+ " hasSurface=" + win.mHasSurface
+ " policyVis=" + win.mPolicyVisibility
+ " destroying=" + win.mDestroying
@@ -349,12 +383,18 @@
// If we have windows that are being show due to them no longer
// being force-hidden, apply the appropriate animation to them.
if (unForceHiding != null) {
+ boolean startKeyguardExit = true;
for (int i=unForceHiding.size()-1; i>=0; i--) {
Animation a = mPolicy.createForceHideEnterAnimation(wallpaperInUnForceHiding);
if (a != null) {
final WindowStateAnimator winAnimator = unForceHiding.get(i);
winAnimator.setAnimation(a);
winAnimator.mAnimationIsEntrance = true;
+ if (startKeyguardExit) {
+ // Do one time only.
+ mPolicy.startKeyguardExitAnimation(a.getStartOffset());
+ startKeyguardExit = false;
+ }
}
}
}
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 68fface..7382f4ca 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -5139,6 +5139,18 @@
}
@Override
+ public void keyguardGoingAway() {
+ if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DISABLE_KEYGUARD)
+ != PackageManager.PERMISSION_GRANTED) {
+ throw new SecurityException("Requires DISABLE_KEYGUARD permission");
+ }
+ synchronized (mWindowMap) {
+ mAnimator.mKeyguardGoingAway = true;
+ requestTraversalLocked();
+ }
+ }
+
+ @Override
public void closeSystemDialogs(String reason) {
synchronized(mWindowMap) {
final int numDisplays = mDisplayContents.size();
@@ -7161,9 +7173,7 @@
public static final int TAP_OUTSIDE_STACK = 31;
public static final int NOTIFY_ACTIVITY_DRAWN = 32;
- public static final int REMOVE_STARTING_TIMEOUT = 33;
-
- public static final int SHOW_DISPLAY_MASK = 34;
+ public static final int SHOW_DISPLAY_MASK = 33;
@Override
public void handleMessage(Message msg) {
@@ -10281,10 +10291,6 @@
mPolicy.lockNow(options);
}
- public void showRecentApps() {
- mPolicy.showRecentApps();
- }
-
@Override
public boolean isSafeModeEnabled() {
return mSafeMode;
diff --git a/telecomm/java/android/telecomm/CallServiceAdapter.java b/telecomm/java/android/telecomm/CallServiceAdapter.java
index d5bb989..dafc310 100644
--- a/telecomm/java/android/telecomm/CallServiceAdapter.java
+++ b/telecomm/java/android/telecomm/CallServiceAdapter.java
@@ -156,5 +156,17 @@
}
}
+ /**
+ * Asks Telecomm to start or stop a ringback tone for a call.
+ *
+ * @param callId The unique ID of the call whose ringback is being changed.
+ * @param ringback Whether Telecomm should start playing a ringback tone.
+ */
+ public void setRequestingRingback(String callId, boolean ringback) {
+ try {
+ mAdapter.setRequestingRingback(callId, ringback);
+ } catch (RemoteException e) {
+ }
+ }
}
diff --git a/telecomm/java/android/telecomm/Connection.java b/telecomm/java/android/telecomm/Connection.java
index 88de17a..8cce8e6 100644
--- a/telecomm/java/android/telecomm/Connection.java
+++ b/telecomm/java/android/telecomm/Connection.java
@@ -33,6 +33,7 @@
void onHandleChanged(Connection c, Uri newHandle);
void onSignalChanged(Connection c, Bundle details);
void onDisconnected(Connection c, int cause, String message);
+ void onRequestingRingback(Connection c, boolean ringback);
void onDestroyed(Connection c);
}
@@ -60,6 +61,10 @@
/** {@inheritDoc} */
@Override
public void onDestroyed(Connection c) {}
+
+ /** {@inheritDoc} */
+ @Override
+ public void onRequestingRingback(Connection c, boolean ringback) {}
}
public final class State {
@@ -77,6 +82,7 @@
private int mState = State.NEW;
private CallAudioState mCallAudioState;
private Uri mHandle;
+ private boolean mRequestingRingback = false;
/**
* Create a new Connection.
@@ -268,6 +274,14 @@
}
/**
+ * @return Whether this connection is requesting that the system play a ringback tone
+ * on its behalf.
+ */
+ public boolean isRequestingRingback() {
+ return mRequestingRingback;
+ }
+
+ /**
* Sets the value of the {@link #getHandle()} property and notifies listeners.
*
* @param handle The new handle.
@@ -286,6 +300,7 @@
* communicate).
*/
protected void setActive() {
+ setRequestingRingback(false);
setState(State.ACTIVE);
}
@@ -329,6 +344,21 @@
}
/**
+ * Requests that the framework play a ringback tone. This is to be invoked by implementations
+ * that do not play a ringback tone themselves in the call's audio stream.
+ *
+ * @param ringback Whether the ringback tone is to be played.
+ */
+ protected void setRequestingRingback(boolean ringback) {
+ if (mRequestingRingback != ringback) {
+ mRequestingRingback = ringback;
+ for (Listener l : mListeners) {
+ l.onRequestingRingback(this, ringback);
+ }
+ }
+ }
+
+ /**
* Notifies this Connection and listeners that the {@link #getCallAudioState()} property
* has a new value.
*
@@ -336,7 +366,7 @@
*/
protected void onSetAudioState(CallAudioState state) {
// TODO: Enforce super called
- this.mCallAudioState = state;
+ mCallAudioState = state;
for (Listener l : mListeners) {
l.onAudioStateChanged(this, state);
}
@@ -356,6 +386,21 @@
}
/**
+ * Notifies this Connection of an internal state change. This method is called before the
+ * state is actually changed. Overriding implementations must call
+ * {@code super.onSetState(state)}.
+ *
+ * @param state The new state, a {@link Connection.State} member.
+ */
+ protected void onSetState(int state) {
+ // TODO: Enforce super called
+ this.mState = state;
+ for (Listener l : mListeners) {
+ l.onStateChanged(this, state);
+ }
+ }
+
+ /**
* Notifies this Connection of a request to play a DTMF tone.
*
* @param c A DTMF character.
@@ -401,9 +446,6 @@
private void setState(int state) {
Log.d(this, "setState: %s", stateToString(state));
- this.mState = state;
- for (Listener l : mListeners) {
- l.onStateChanged(this, state);
- }
+ onSetState(state);
}
}
diff --git a/telecomm/java/android/telecomm/ConnectionService.java b/telecomm/java/android/telecomm/ConnectionService.java
index 492b08e..8d02842 100644
--- a/telecomm/java/android/telecomm/ConnectionService.java
+++ b/telecomm/java/android/telecomm/ConnectionService.java
@@ -89,6 +89,13 @@
public void onDestroyed(Connection c) {
removeConnection(c);
}
+
+ @Override
+ public void onRequestingRingback(Connection c, boolean ringback) {
+ String id = mIdByConnection.get(c);
+ Log.d(this, "Adapter onRingback %b", ringback);
+ getAdapter().setRequestingRingback(id, ringback);
+ }
};
@Override
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/ICallServiceAdapter.aidl b/telecomm/java/com/android/internal/telecomm/ICallServiceAdapter.aidl
index dfdaa75..6d36494 100644
--- a/telecomm/java/com/android/internal/telecomm/ICallServiceAdapter.aidl
+++ b/telecomm/java/com/android/internal/telecomm/ICallServiceAdapter.aidl
@@ -43,4 +43,6 @@
void setDisconnected(String callId, int disconnectCause, String disconnectMessage);
void setOnHold(String callId);
+
+ void setRequestingRingback(String callId, boolean ringing);
}
diff --git a/telecomm/java/com/android/internal/telecomm/IInCallService.aidl b/telecomm/java/com/android/internal/telecomm/IInCallService.aidl
index ccf7e3f..1635053 100644
--- a/telecomm/java/com/android/internal/telecomm/IInCallService.aidl
+++ b/telecomm/java/com/android/internal/telecomm/IInCallService.aidl
@@ -40,4 +40,6 @@
void setPostDialWait(String callId, String remaining);
void onAudioStateChanged(in CallAudioState audioState);
+
+ void bringToForeground(boolean showDialpad);
}
diff --git a/telecomm/java/com/android/internal/telecomm/ITelecommService.aidl b/telecomm/java/com/android/internal/telecomm/ITelecommService.aidl
index c439211..0e94ffb 100644
--- a/telecomm/java/com/android/internal/telecomm/ITelecommService.aidl
+++ b/telecomm/java/com/android/internal/telecomm/ITelecommService.aidl
@@ -21,7 +21,7 @@
* commands that were previously handled by ITelephony.
* {@hide}
*/
-oneway interface ITelecommService {
+interface ITelecommService {
/**
* Silence the ringer if an incoming call is currently ringing.
@@ -31,4 +31,11 @@
* even if there's no incoming call. (If so, this method will do nothing.)
*/
void silenceRinger();
+
+ /**
+ * Brings the in-call screen to the foreground if there is an active call.
+ *
+ * @param showDialpad if true, make the dialpad visible initially.
+ */
+ void showCallScreen(boolean showDialpad);
}
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index 4aed1fe..525441d72 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -1980,9 +1980,10 @@
@PrivateApi
public boolean showCallScreen() {
try {
- return getITelephony().showCallScreen();
+ getTelecommService().showCallScreen(false);
+ return true;
} catch (RemoteException e) {
- Log.e(TAG, "Error calling ITelephony#showCallScreen", e);
+ Log.e(TAG, "Error calling ITelecommService#showCallScreen", e);
}
return false;
}
@@ -1991,9 +1992,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/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/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/src/com/android/test/voiceinteraction/TestInteractionActivity.java b/tests/VoiceInteraction/src/com/android/test/voiceinteraction/TestInteractionActivity.java
index 9c772ff..a61e0da 100644
--- a/tests/VoiceInteraction/src/com/android/test/voiceinteraction/TestInteractionActivity.java
+++ b/tests/VoiceInteraction/src/com/android/test/voiceinteraction/TestInteractionActivity.java
@@ -20,6 +20,8 @@
import android.app.VoiceInteractor;
import android.os.Bundle;
import android.util.Log;
+import android.view.Gravity;
+import android.view.ViewGroup;
public class TestInteractionActivity extends Activity {
static final String TAG = "TestInteractionActivity";
@@ -38,6 +40,11 @@
setContentView(R.layout.test_interaction);
+ // 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(
"This is a confirmation", null) {
diff --git a/tools/layoutlib/bridge/src/android/content/res/BridgeTypedArray.java b/tools/layoutlib/bridge/src/android/content/res/BridgeTypedArray.java
index cc621c4..105803e 100644
--- a/tools/layoutlib/bridge/src/android/content/res/BridgeTypedArray.java
+++ b/tools/layoutlib/bridge/src/android/content/res/BridgeTypedArray.java
@@ -826,6 +826,11 @@
return null;
}
+ @Override
+ public int[] extractThemeAttrs() {
+ return null;
+ }
+
/**
* Retrieve the raw TypedValue for the attribute at <var>index</var>.
*
@@ -912,4 +917,9 @@
public String toString() {
return Arrays.toString(mResourceData);
}
- }
+
+ static TypedArray obtain(Resources res, int len) {
+ return res instanceof BridgeResources ?
+ new BridgeTypedArray(((BridgeResources) res), null, len, true) : null;
+ }
+}
diff --git a/tools/layoutlib/bridge/src/android/content/res/TypedArray_Delegate.java b/tools/layoutlib/bridge/src/android/content/res/TypedArray_Delegate.java
index 5d89f83..faa8852 100644
--- a/tools/layoutlib/bridge/src/android/content/res/TypedArray_Delegate.java
+++ b/tools/layoutlib/bridge/src/android/content/res/TypedArray_Delegate.java
@@ -30,7 +30,6 @@
@LayoutlibDelegate
/*package*/ static TypedArray obtain(Resources res, int len) {
- // FIXME
- return null;
+ return BridgeTypedArray.obtain(res, len);
}
}
diff --git a/tools/layoutlib/bridge/src/android/graphics/BitmapShader_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/BitmapShader_Delegate.java
index cdbbe46..610c867 100644
--- a/tools/layoutlib/bridge/src/android/graphics/BitmapShader_Delegate.java
+++ b/tools/layoutlib/bridge/src/android/graphics/BitmapShader_Delegate.java
@@ -79,13 +79,6 @@
return sManager.addNewDelegate(newDelegate);
}
- @LayoutlibDelegate
- /*package*/ static long nativePostCreate(long native_shader, long native_bitmap,
- int shaderTileModeX, int shaderTileModeY) {
- // pass, not needed.
- return 0;
- }
-
// ---- Private delegate/helper methods ----
private BitmapShader_Delegate(java.awt.image.BufferedImage image,
diff --git a/tools/layoutlib/bridge/src/android/graphics/ComposeShader_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/ComposeShader_Delegate.java
index fae8aef..59ddcc6 100644
--- a/tools/layoutlib/bridge/src/android/graphics/ComposeShader_Delegate.java
+++ b/tools/layoutlib/bridge/src/android/graphics/ComposeShader_Delegate.java
@@ -78,19 +78,6 @@
return sManager.addNewDelegate(newDelegate);
}
- @LayoutlibDelegate
- /*package*/ static long nativePostCreate1(long native_shader, long native_skiaShaderA,
- long native_skiaShaderB, long native_mode) {
- // pass, not needed.
- return 0;
- }
-
- @LayoutlibDelegate
- /*package*/ static long nativePostCreate2(long native_shader, long native_skiaShaderA,
- long native_skiaShaderB, int porterDuffMode) {
- // pass, not needed.
- return 0;
- }
// ---- Private delegate/helper methods ----
diff --git a/tools/layoutlib/bridge/src/android/graphics/FontFamily_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/FontFamily_Delegate.java
index 5e7543a0..9ea4538 100644
--- a/tools/layoutlib/bridge/src/android/graphics/FontFamily_Delegate.java
+++ b/tools/layoutlib/bridge/src/android/graphics/FontFamily_Delegate.java
@@ -146,6 +146,8 @@
@LayoutlibDelegate
/*package*/ static void nUnrefFamily(long nativePtr) {
+ // Removing the java reference for the object doesn't mean that it's freed for garbage
+ // collection. Typeface_Delegate may still hold a reference for it.
sManager.removeJavaReferenceFor(nativePtr);
}
diff --git a/tools/layoutlib/bridge/src/android/graphics/LinearGradient_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/LinearGradient_Delegate.java
index ac77377..55c4b98 100644
--- a/tools/layoutlib/bridge/src/android/graphics/LinearGradient_Delegate.java
+++ b/tools/layoutlib/bridge/src/android/graphics/LinearGradient_Delegate.java
@@ -71,22 +71,6 @@
tileMode);
}
- @LayoutlibDelegate
- /*package*/ static long nativePostCreate1(LinearGradient thisGradient,
- long native_shader, float x0, float y0, float x1, float y1,
- int colors[], float positions[], int tileMode) {
- // nothing to be done here.
- return 0;
- }
-
- @LayoutlibDelegate
- /*package*/ static long nativePostCreate2(LinearGradient thisGradient,
- long native_shader, float x0, float y0, float x1, float y1,
- int color0, int color1, int tileMode) {
- // nothing to be done here.
- return 0;
- }
-
// ---- Private delegate/helper methods ----
/**
diff --git a/tools/layoutlib/bridge/src/android/graphics/RadialGradient_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/RadialGradient_Delegate.java
index 4f16dcf..80179ee 100644
--- a/tools/layoutlib/bridge/src/android/graphics/RadialGradient_Delegate.java
+++ b/tools/layoutlib/bridge/src/android/graphics/RadialGradient_Delegate.java
@@ -68,20 +68,6 @@
tileMode);
}
- @LayoutlibDelegate
- /*package*/ static long nativePostCreate1(long native_shader, float x, float y, float radius,
- int colors[], float positions[], int tileMode) {
- // nothing to be done here.
- return 0;
- }
-
- @LayoutlibDelegate
- /*package*/ static long nativePostCreate2(long native_shader, float x, float y, float radius,
- int color0, int color1, int tileMode) {
- // nothing to be done here.
- return 0;
- }
-
// ---- Private delegate/helper methods ----
/**
diff --git a/tools/layoutlib/bridge/src/android/graphics/Shader_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/Shader_Delegate.java
index 70a0a43..14e9960 100644
--- a/tools/layoutlib/bridge/src/android/graphics/Shader_Delegate.java
+++ b/tools/layoutlib/bridge/src/android/graphics/Shader_Delegate.java
@@ -76,13 +76,12 @@
// ---- native methods ----
@LayoutlibDelegate
- /*package*/ static void nativeDestructor(long native_shader, long native_skiaShader) {
+ /*package*/ static void nativeDestructor(long native_shader) {
sManager.removeJavaReferenceFor(native_shader);
}
@LayoutlibDelegate
- /*package*/ static void nativeSetLocalMatrix(long native_shader, long native_skiaShader,
- long matrix_instance) {
+ /*package*/ static void nativeSetLocalMatrix(long native_shader, long matrix_instance) {
// get the delegate from the native int.
Shader_Delegate shaderDelegate = sManager.getDelegate(native_shader);
if (shaderDelegate == null) {
diff --git a/tools/layoutlib/bridge/src/android/graphics/SweepGradient_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/SweepGradient_Delegate.java
index f2b3e8d..95a57a9 100644
--- a/tools/layoutlib/bridge/src/android/graphics/SweepGradient_Delegate.java
+++ b/tools/layoutlib/bridge/src/android/graphics/SweepGradient_Delegate.java
@@ -62,20 +62,6 @@
return nativeCreate1(x, y, new int[] { color0, color1 }, null /*positions*/);
}
- @LayoutlibDelegate
- /*package*/ static long nativePostCreate1(long native_shader, float cx, float cy,
- int[] colors, float[] positions) {
- // nothing to be done here.
- return 0;
- }
-
- @LayoutlibDelegate
- /*package*/ static long nativePostCreate2(long native_shader, float cx, float cy,
- int color0, int color1) {
- // nothing to be done here.
- return 0;
- }
-
// ---- Private delegate/helper methods ----
/**
diff --git a/tools/layoutlib/bridge/src/android/graphics/Typeface_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/Typeface_Delegate.java
index ed8f3b4..9746b48 100644
--- a/tools/layoutlib/bridge/src/android/graphics/Typeface_Delegate.java
+++ b/tools/layoutlib/bridge/src/android/graphics/Typeface_Delegate.java
@@ -54,7 +54,7 @@
// ---- delegate data ----
- private final long[] mFontFamilies; // the reference to FontFamily_Delegate.
+ private final FontFamily_Delegate[] mFontFamilies; // the reference to FontFamily_Delegate.
private int mStyle;
private static long sDefaultTypeface;
@@ -71,8 +71,7 @@
public List<Font> getFonts(boolean compact) {
List<Font> fonts = new ArrayList<Font>(mFontFamilies.length);
- for (long fontFamily : mFontFamilies) {
- FontFamily_Delegate ffd = FontFamily_Delegate.getDelegate(fontFamily);
+ for (FontFamily_Delegate ffd : mFontFamilies) {
if (ffd != null) {
Font font = ffd.getFont(mStyle, compact);
if (font != null) {
@@ -122,7 +121,11 @@
@LayoutlibDelegate
/*package*/ static synchronized long nativeCreateFromArray(long[] familyArray) {
- Typeface_Delegate delegate = new Typeface_Delegate(familyArray, Typeface.NORMAL);
+ FontFamily_Delegate[] fontFamilies = new FontFamily_Delegate[familyArray.length];
+ for (int i = 0; i < familyArray.length; i++) {
+ fontFamilies[i] = FontFamily_Delegate.getDelegate(familyArray[i]);
+ }
+ Typeface_Delegate delegate = new Typeface_Delegate(fontFamilies, Typeface.NORMAL);
return sManager.addNewDelegate(delegate);
}
@@ -153,9 +156,8 @@
// ---- Private delegate/helper methods ----
- private Typeface_Delegate(long[] fontFamilies, int style) {
+ private Typeface_Delegate(FontFamily_Delegate[] fontFamilies, int style) {
mFontFamilies = fontFamilies;
mStyle = style;
}
-
}
diff --git a/tools/layoutlib/bridge/src/android/view/IWindowManagerImpl.java b/tools/layoutlib/bridge/src/android/view/IWindowManagerImpl.java
index 757cdd2..3bf2b20 100644
--- a/tools/layoutlib/bridge/src/android/view/IWindowManagerImpl.java
+++ b/tools/layoutlib/bridge/src/android/view/IWindowManagerImpl.java
@@ -72,7 +72,7 @@
@Override
public void addAppToken(int arg0, IApplicationToken arg1, int arg2, int arg3, int arg4,
- boolean arg5, boolean arg6, int arg7, int arg8)
+ boolean arg5, boolean arg6, int arg7, int arg8, boolean arg9)
throws RemoteException {
// TODO Auto-generated method stub
@@ -439,6 +439,10 @@
}
@Override
+ public void keyguardGoingAway() throws RemoteException {
+ }
+
+ @Override
public void lockNow(Bundle options) {
// TODO Auto-generated method stub
}
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/ActionBarLayout.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/ActionBarLayout.java
index 936ab4f..e59ccd7 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/ActionBarLayout.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/ActionBarLayout.java
@@ -171,7 +171,7 @@
// Set action bar to be split, if needed.
ActionBarContainer splitView = (ActionBarContainer) findViewById(R.id.split_action_bar);
mActionBarView.setSplitView(splitView);
- mActionBarView.setSplitActionBar(mSplit);
+ mActionBarView.setSplitToolbar(mSplit);
inflateMenus();
diff --git a/tools/layoutlib/rename_font/build_font.py b/tools/layoutlib/rename_font/build_font.py
index ea3dccc..aea3241 100755
--- a/tools/layoutlib/rename_font/build_font.py
+++ b/tools/layoutlib/rename_font/build_font.py
@@ -15,10 +15,10 @@
# limitations under the License.
"""
-Rename the PS name of all fonts in the input directory and copy them to the
+Rename the PS name of all fonts in the input directories and copy them to the
output directory.
-Usage: build_font.py /path/to/input_fonts/ /path/to/output_fonts/
+Usage: build_font.py /path/to/input_fonts1/ /path/to/input_fonts2/ /path/to/output_fonts/
"""
@@ -30,50 +30,86 @@
from lxml import etree
import shutil
import glob
+from multiprocessing import Pool
+
+# global variable
+dest_dir = '/tmp'
def main(argv):
- if len(argv) != 2:
- print "Usage: build_font.py /path/to/input_fonts/ /path/to/out/dir/"
- sys.exit(1)
- if not os.path.isdir(argv[0]):
- print argv[0] + "is not a valid directory"
- sys.exit(1)
- if not os.path.isdir(argv[1]):
- print argv[1] + "is not a valid directory"
- sys.exit(1)
+ if len(argv) < 2:
+ sys.exit('Usage: build_font.py /path/to/input_fonts/ /path/to/out/dir/')
+ for directory in argv:
+ if not os.path.isdir(directory):
+ sys.exit(directory + ' is not a valid directory')
+ global dest_dir
+ dest_dir = argv[-1]
+ src_dirs = argv[:-1]
cwd = os.getcwd()
- os.chdir(argv[1])
+ os.chdir(dest_dir)
files = glob.glob('*')
for filename in files:
os.remove(filename)
os.chdir(cwd)
- for filename in os.listdir(argv[0]):
- if not os.path.splitext(filename)[1].lower() == ".ttf":
- shutil.copy(os.path.join(argv[0], filename), argv[1])
- continue
- print os.path.join(argv[0], filename)
- old_ttf_path = os.path.join(argv[0], filename)
+ input_fonts = list()
+ for src_dir in src_dirs:
+ for dirname, dirnames, filenames in os.walk(src_dir):
+ for filename in filenames:
+ input_path = os.path.join(dirname, filename)
+ extension = os.path.splitext(filename)[1].lower()
+ if (extension == '.ttf'):
+ input_fonts.append(input_path)
+ elif (extension == '.xml'):
+ shutil.copy(input_path, dest_dir)
+ if '.git' in dirnames:
+ # don't go into any .git directories.
+ dirnames.remove('.git')
+ # Create as many threads as the number of CPUs
+ pool = Pool(processes=None)
+ pool.map(convert_font, input_fonts)
+
+
+class InvalidFontException(Exception):
+ pass
+
+def convert_font(input_path):
+ filename = os.path.basename(input_path)
+ print 'Converting font: ' + filename
+ # the path to the output file. The file name is the fontfilename.ttx
+ ttx_path = os.path.join(dest_dir, filename)
+ ttx_path = ttx_path[:-1] + 'x'
+ try:
# run ttx to generate an xml file in the output folder which represents all
# its info
- ttx_args = ["-d", argv[1], old_ttf_path]
+ ttx_args = ['-q', '-d', dest_dir, input_path]
ttx.main(ttx_args)
- # the path to the output file. The file name is the fontfilename.ttx
- ttx_path = os.path.join(argv[1], filename)
- ttx_path = ttx_path[:-1] + "x"
# now parse the xml file to change its PS name.
tree = etree.parse(ttx_path)
encoding = tree.docinfo.encoding
root = tree.getroot()
for name in root.iter('name'):
[old_ps_name, version] = get_font_info(name)
- new_ps_name = old_ps_name + version
- update_name(name, new_ps_name)
+ if old_ps_name is not None and version is not None:
+ new_ps_name = old_ps_name + version
+ update_name(name, new_ps_name)
tree.write(ttx_path, xml_declaration=True, encoding=encoding )
# generate the udpated font now.
- ttx_args = ["-d", argv[1], ttx_path]
+ ttx_args = ['-q', '-d', dest_dir, ttx_path]
ttx.main(ttx_args)
- # delete the temp ttx file.
+ except InvalidFontException:
+ # In case of invalid fonts, we exit.
+ print filename + ' is not a valid font'
+ raise
+ except Exception as e:
+ print 'Error converting font: ' + filename
+ print e
+ # Some fonts are too big to be handled by the ttx library.
+ # Just copy paste them.
+ shutil.copy(input_path, dest_dir)
+ try:
+ # delete the temp ttx file is it exists.
os.remove(ttx_path)
+ except OSError:
+ pass
def get_font_info(tag):
ps_name = None
@@ -85,19 +121,17 @@
if namerecord.attrib['nameID'] == '6':
if ps_name is not None:
if not sanitize(namerecord.text) == ps_name:
- sys.exit('found multiple possibilities of the font name')
+ raise InvalidFontException('found multiple possibilities of the font name')
else:
ps_name = sanitize(namerecord.text)
# nameID=5 means the font version
if namerecord.attrib['nameID'] == '5':
if ps_version is not None:
if not ps_version == get_version(namerecord.text):
- sys.exit('found multiple possibilities of the font version')
+ raise InvalidFontException('found multiple possibilities of the font version')
else:
ps_version = get_version(namerecord.text)
- if ps_name is not None and ps_version is not None:
- return [ps_name, ps_version]
- sys.exit('didn\'t find the font name or version')
+ return [ps_name, ps_version]
def update_name(tag, name):
@@ -110,11 +144,11 @@
return re.sub(r'[^\w-]+', '', string)
def get_version(string):
- # The string must begin with "Version n.nn "
+ # The string must begin with 'Version n.nn '
# to extract n.nn, we return the second entry in the split strings.
string = string.strip()
- if not string.startswith("Version "):
- sys.exit('mal-formed font version')
+ if not string.startswith('Version '):
+ raise InvalidFontException('mal-formed font version')
return sanitize(string.split()[1])
if __name__ == '__main__':
diff --git a/wifi/java/android/net/wifi/WifiConfiguration.java b/wifi/java/android/net/wifi/WifiConfiguration.java
index 1157de7d..963117c 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.
@@ -484,6 +496,8 @@
networkId = INVALID_NETWORK_ID;
SSID = null;
BSSID = null;
+ FQDN = null;
+ naiRealm = null;
priority = 0;
hiddenSSID = false;
disableReason = DISABLED_UNKNOWN_REASON;
@@ -565,7 +579,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++) {
@@ -870,6 +885,8 @@
disableReason = source.disableReason;
SSID = source.SSID;
BSSID = source.BSSID;
+ FQDN = source.FQDN;
+ naiRealm = source.naiRealm;
preSharedKey = source.preSharedKey;
wepKeys = new String[4];
@@ -932,6 +949,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);
@@ -976,6 +995,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/WifiManager.java b/wifi/java/android/net/wifi/WifiManager.java
index 141a69e..bf46745 100644
--- a/wifi/java/android/net/wifi/WifiManager.java
+++ b/wifi/java/android/net/wifi/WifiManager.java
@@ -1413,12 +1413,14 @@
/**
* Passed with {@link ActionListener#onFailure}.
* Indicates that the operation failed due to an internal error.
+ * @hide
*/
public static final int ERROR = 0;
/**
* Passed with {@link ActionListener#onFailure}.
* Indicates that the operation is already in progress
+ * @hide
*/
public static final int IN_PROGRESS = 1;
@@ -1426,28 +1428,30 @@
* Passed with {@link ActionListener#onFailure}.
* Indicates that the operation failed because the framework is busy and
* unable to service the request
+ * @hide
*/
public static final int BUSY = 2;
/* WPS specific errors */
- /** WPS overlap detected */
+ /** WPS overlap detected {@hide} */
public static final int WPS_OVERLAP_ERROR = 3;
- /** WEP on WPS is prohibited */
+ /** WEP on WPS is prohibited {@hide} */
public static final int WPS_WEP_PROHIBITED = 4;
- /** TKIP only prohibited */
+ /** TKIP only prohibited {@hide} */
public static final int WPS_TKIP_ONLY_PROHIBITED = 5;
- /** Authentication failure on WPS */
+ /** Authentication failure on WPS {@hide} */
public static final int WPS_AUTH_FAILURE = 6;
- /** WPS timed out */
+ /** WPS timed out {@hide} */
public static final int WPS_TIMED_OUT = 7;
/**
* Passed with {@link ActionListener#onFailure}.
* Indicates that the operation failed due to invalid inputs
+ * @hide
*/
public static final int INVALID_ARGS = 8;
- /** Interface for callback invocation on an application action */
+ /** Interface for callback invocation on an application action {@hide} */
public interface ActionListener {
/** The operation succeeded */
public void onSuccess();
@@ -1459,7 +1463,7 @@
public void onFailure(int reason);
}
- /** Interface for callback invocation on a start WPS action */
+ /** Interface for callback invocation on a start WPS action {@hide} */
public interface WpsListener {
/** WPS start succeeded */
public void onStartSuccess(String pin);
@@ -1741,6 +1745,7 @@
* @param listener for callbacks on success or failure. Can be null.
* @throws IllegalStateException if the WifiManager instance needs to be
* initialized again
+ * @hide
*/
public void startWps(WpsInfo config, WpsListener listener) {
if (config == null) throw new IllegalArgumentException("config cannot be null");
@@ -1754,6 +1759,7 @@
* @param listener for callbacks on success or failure. Can be null.
* @throws IllegalStateException if the WifiManager instance needs to be
* initialized again
+ * @hide
*/
public void cancelWps(ActionListener listener) {
validateChannel();
diff --git a/wifi/java/android/net/wifi/WpsInfo.java b/wifi/java/android/net/wifi/WpsInfo.java
index ae2e771..2ad4ad0 100644
--- a/wifi/java/android/net/wifi/WpsInfo.java
+++ b/wifi/java/android/net/wifi/WpsInfo.java
@@ -40,7 +40,7 @@
/** Wi-Fi Protected Setup. www.wi-fi.org/wifi-protected-setup has details */
public int setup;
- /** Passed with pin method KEYPAD */
+ /** @hide */
public String BSSID;
/** Passed with pin method configuration */
diff --git a/wifi/java/android/net/wifi/passpoint/WifiPasspointCredential.java b/wifi/java/android/net/wifi/passpoint/WifiPasspointCredential.java
index 08b430f..0769a64 100644
--- a/wifi/java/android/net/wifi/passpoint/WifiPasspointCredential.java
+++ b/wifi/java/android/net/wifi/passpoint/WifiPasspointCredential.java
@@ -25,6 +25,10 @@
import java.util.Iterator;
import java.util.Map;
+/**
+ * A class representing a Wi-Fi Passpoint credential.
+ * @hide
+ */
public class WifiPasspointCredential implements Parcelable {
private final static String TAG = "PasspointCredential";
diff --git a/wifi/java/android/net/wifi/passpoint/WifiPasspointManager.java b/wifi/java/android/net/wifi/passpoint/WifiPasspointManager.java
index ee4dc5a..e140c135 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 {