summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--cmds/statsd/Android.bp2
-rw-r--r--cmds/statsd/src/atoms.proto59
-rw-r--r--cmds/statsd/src/external/CarStatsPuller.cpp96
-rw-r--r--cmds/statsd/src/external/CarStatsPuller.h36
-rw-r--r--cmds/statsd/src/external/StatsPullerManager.cpp5
-rw-r--r--core/java/Android.bp5
-rw-r--r--core/java/android/accounts/AccountManager.java31
-rw-r--r--core/java/android/animation/FloatEvaluator.java2
-rw-r--r--core/java/android/app/DisabledWallpaperManager.java346
-rw-r--r--core/java/android/app/IUiModeManager.aidl5
-rw-r--r--core/java/android/app/PendingIntent.java7
-rw-r--r--core/java/android/app/SystemServiceRegistry.java21
-rw-r--r--core/java/android/app/UiModeManager.java14
-rw-r--r--core/java/android/app/WallpaperManager.java4
-rw-r--r--core/java/android/app/admin/DevicePolicyManager.java18
-rw-r--r--core/java/android/app/backup/WallpaperBackupHelper.java4
-rw-r--r--core/java/android/app/contentsuggestions/ContentSuggestionsManager.java13
-rw-r--r--core/java/android/app/slice/SliceManager.java2
-rw-r--r--core/java/android/bluetooth/BluetoothDevice.java18
-rw-r--r--core/java/android/bluetooth/le/ScanFilter.java2
-rw-r--r--core/java/android/content/Intent.java3
-rw-r--r--core/java/android/content/SyncStats.java2
-rw-r--r--core/java/android/content/pm/PackageInstaller.java13
-rw-r--r--core/java/android/content/pm/UserInfo.java77
-rw-r--r--core/java/android/content/res/TypedArray.java5
-rw-r--r--core/java/android/net/nsd/NsdManager.java4
-rwxr-xr-xcore/java/android/os/Build.java3
-rw-r--r--core/java/android/os/FileObserver.java2
-rw-r--r--core/java/android/os/IUserManager.aidl4
-rw-r--r--core/java/android/os/UserManager.java91
-rw-r--r--core/java/android/provider/Settings.java44
-rw-r--r--core/java/android/service/contentsuggestions/ContentSuggestionsService.java19
-rw-r--r--core/java/android/service/notification/NotificationListenerService.java15
-rwxr-xr-xcore/java/android/util/DisplayMetrics.java7
-rw-r--r--core/java/android/view/View.java16
-rw-r--r--core/java/android/view/ViewDebug.java101
-rw-r--r--core/java/android/view/animation/AnimationUtils.java8
-rw-r--r--core/java/android/view/inputmethod/InputMethodManager.java97
-rw-r--r--core/java/android/webkit/CookieManager.java5
-rw-r--r--core/java/android/webkit/WebChromeClient.java34
-rw-r--r--core/java/android/webkit/WebSettings.java7
-rw-r--r--core/java/android/widget/ArrayAdapter.java3
-rw-r--r--core/java/android/widget/ListView.java4
-rw-r--r--core/java/android/widget/NumberPicker.java4
-rw-r--r--core/java/android/widget/RelativeLayout.java2
-rw-r--r--core/java/com/android/internal/app/AbstractResolverComparator.java43
-rw-r--r--core/java/com/android/internal/app/ChooserActivity.java148
-rw-r--r--core/java/com/android/internal/app/ResolverActivity.java75
-rw-r--r--core/java/com/android/internal/app/ResolverListController.java11
-rw-r--r--core/java/com/android/internal/app/ResolverTargetActionsDialogFragment.java27
-rw-r--r--core/java/com/android/internal/car/ICarStatsService.aidl31
-rw-r--r--core/java/com/android/internal/colorextraction/ColorExtractor.java6
-rw-r--r--core/java/com/android/internal/config/sysui/SystemUiDeviceConfigFlags.java14
-rw-r--r--core/java/com/android/internal/util/ScreenshotHelper.java16
-rw-r--r--core/jni/android/graphics/ByteBufferStreamAdaptor.cpp29
-rw-r--r--core/jni/android_view_Surface.cpp2
-rw-r--r--core/proto/android/app/settings_enums.proto7
-rw-r--r--core/proto/android/providers/settings/secure.proto5
-rw-r--r--core/res/res/drawable/chooser_action_button_bg.xml33
-rw-r--r--core/res/res/drawable/ic_content_copy_gm2.xml25
-rw-r--r--core/res/res/drawable/ic_menu_copy_material.xml39
-rw-r--r--core/res/res/layout/chooser_action_button.xml28
-rw-r--r--core/res/res/layout/chooser_action_row.xml26
-rw-r--r--core/res/res/layout/chooser_grid_preview_file.xml15
-rw-r--r--core/res/res/layout/chooser_grid_preview_image.xml9
-rw-r--r--core/res/res/layout/chooser_grid_preview_text.xml43
-rw-r--r--core/res/res/layout/resolve_list_item.xml20
-rw-r--r--core/res/res/layout/resolver_different_item_header.xml16
-rw-r--r--core/res/res/layout/resolver_list.xml63
-rw-r--r--core/res/res/layout/resolver_list_with_default.xml76
-rw-r--r--core/res/res/values-mcc310-mnc170/config.xml4
-rw-r--r--core/res/res/values-mcc310-mnc380/config.xml4
-rw-r--r--core/res/res/values-mcc310-mnc410/config.xml3
-rw-r--r--core/res/res/values-mcc310-mnc560/config.xml4
-rw-r--r--core/res/res/values-mcc311-mnc180/config.xml4
-rw-r--r--core/res/res/values-mcc313-mnc100/config.xml4
-rw-r--r--core/res/res/values/arrays.xml10
-rw-r--r--core/res/res/values/attrs_manifest.xml8
-rw-r--r--core/res/res/values/config.xml8
-rw-r--r--core/res/res/values/dimens.xml13
-rw-r--r--core/res/res/values/ids.xml3
-rw-r--r--core/res/res/values/strings.xml8
-rw-r--r--core/res/res/values/symbols.xml16
-rw-r--r--core/res/res/values/themes_device_defaults.xml1
-rw-r--r--core/tests/coretests/src/android/provider/SettingsBackupTest.java9
-rw-r--r--core/tests/coretests/src/com/android/internal/app/AbstractResolverComparatorTest.java105
-rw-r--r--core/tests/coretests/src/com/android/internal/app/ChooserActivityTest.java4
-rw-r--r--core/tests/screenshothelpertests/src/com/android/internal/util/ScreenshotHelperTest.java6
-rw-r--r--graphics/java/android/graphics/Canvas.java6
-rw-r--r--graphics/java/android/graphics/Paint.java12
-rw-r--r--libs/hwui/renderthread/CanvasContext.cpp8
-rw-r--r--libs/hwui/renderthread/CanvasContext.h2
-rw-r--r--libs/hwui/renderthread/RenderProxy.cpp7
-rw-r--r--libs/hwui/renderthread/RenderProxy.h2
-rw-r--r--location/java/android/location/Location.java5
-rw-r--r--location/java/android/location/OnNmeaMessageListener.java4
-rw-r--r--media/Android.bp2
-rw-r--r--media/java/android/media/ExifInterface.java81
-rw-r--r--media/java/android/media/Image.java2
-rw-r--r--media/java/android/media/ImageWriter.java8
-rw-r--r--packages/CarSystemUI/res/layout/car_top_navigation_bar_unprovisioned.xml147
-rw-r--r--packages/CarSystemUI/res/layout/super_status_bar.xml14
-rw-r--r--packages/CarSystemUI/res/values/config.xml15
-rw-r--r--packages/CarSystemUI/res/values/integers_car.xml5
-rw-r--r--packages/CarSystemUI/src/com/android/systemui/CarSystemUIFactory.java10
-rw-r--r--packages/CarSystemUI/src/com/android/systemui/car/CarNotificationInterruptionStateProvider.java6
-rw-r--r--packages/CarSystemUI/src/com/android/systemui/car/CarServiceProvider.java59
-rw-r--r--packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarBatteryController.java5
-rw-r--r--packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java414
-rw-r--r--packages/CarSystemUI/src/com/android/systemui/statusbar/car/DrivingStateHelper.java82
-rw-r--r--packages/CarSystemUI/src/com/android/systemui/statusbar/car/FullscreenUserSwitcher.java43
-rw-r--r--packages/CarSystemUI/src/com/android/systemui/statusbar/car/PowerManagerHelper.java64
-rw-r--r--packages/CarSystemUI/src/com/android/systemui/statusbar/car/hvac/HvacController.java70
-rw-r--r--packages/CarSystemUI/src/com/android/systemui/volume/CarVolumeDialogComponent.java6
-rw-r--r--packages/CarSystemUI/src/com/android/systemui/volume/CarVolumeDialogImpl.java248
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/utils/ThreadUtils.java24
-rw-r--r--packages/SettingsLib/tests/robotests/src/com/android/settingslib/utils/ThreadUtilsTest.java2
-rw-r--r--packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java6
-rw-r--r--packages/SystemUI/AndroidManifest.xml7
-rw-r--r--packages/SystemUI/res/values/strings.xml10
-rw-r--r--packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java10
-rw-r--r--packages/SystemUI/src/com/android/systemui/ForegroundServiceLifetimeExtender.java90
-rw-r--r--packages/SystemUI/src/com/android/systemui/ForegroundServiceNotificationListener.java3
-rw-r--r--packages/SystemUI/src/com/android/systemui/ImageWallpaper.java59
-rw-r--r--packages/SystemUI/src/com/android/systemui/SystemUIApplication.java12
-rw-r--r--packages/SystemUI/src/com/android/systemui/SystemUIFactory.java13
-rw-r--r--packages/SystemUI/src/com/android/systemui/SystemUIService.java31
-rw-r--r--packages/SystemUI/src/com/android/systemui/assist/AssistHandleBehaviorController.java14
-rw-r--r--packages/SystemUI/src/com/android/systemui/assist/AssistHandleReminderExpBehavior.java24
-rw-r--r--packages/SystemUI/src/com/android/systemui/assist/DeviceConfigHelper.java (renamed from packages/SystemUI/src/com/android/systemui/assist/PhenotypeHelper.java)6
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/BiometricDialogView.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/bubbles/BubbleData.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java155
-rw-r--r--packages/SystemUI/src/com/android/systemui/bubbles/BubbleView.java54
-rw-r--r--packages/SystemUI/src/com/android/systemui/colorextraction/SysuiColorExtractor.java8
-rw-r--r--packages/SystemUI/src/com/android/systemui/dock/DockManager.java61
-rw-r--r--packages/SystemUI/src/com/android/systemui/dock/DockManagerImpl.java8
-rw-r--r--packages/SystemUI/src/com/android/systemui/doze/DozeFactory.java3
-rw-r--r--packages/SystemUI/src/com/android/systemui/doze/DozeMachine.java9
-rw-r--r--packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java5
-rw-r--r--packages/SystemUI/src/com/android/systemui/doze/DozeUi.java3
-rw-r--r--packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java12
-rw-r--r--packages/SystemUI/src/com/android/systemui/glwallpaper/ImageRevealHelper.java29
-rw-r--r--packages/SystemUI/src/com/android/systemui/glwallpaper/ImageWallpaperRenderer.java24
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/KeyguardSliceProvider.java24
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/UiModeNightTile.java21
-rw-r--r--packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java424
-rw-r--r--packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotNotificationSmartActionsProvider.java101
-rw-r--r--packages/SystemUI/src/com/android/systemui/screenshot/TakeScreenshotService.java11
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java71
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/NotificationLifetimeExtender.java12
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java18
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationInterruptionStateProvider.java14
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationData.java9
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java7
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGuts.java8
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGutsManager.java7
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithm.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardLiftController.kt3
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/LockscreenWallpaper.java16
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java272
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java17
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryController.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/policy/SmartReplyView.java5
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/ForegroundServiceLifetimeExtenderTest.java82
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/assist/AssistHandleBehaviorControllerTest.java8
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleControllerTest.java9
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/dock/DockManagerFake.java11
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/doze/DozeMachineTest.java5
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/screenshot/ScreenshotNotificationSmartActionsTest.java211
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyguardIndicationControllerTest.java134
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationInterruptionStateProviderTest.java14
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationEntryManagerTest.java4
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotificationDataTest.java5
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java25
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java9
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeBatteryController.java5
-rw-r--r--services/contentsuggestions/java/com/android/server/contentsuggestions/ContentSuggestionsManagerService.java3
-rw-r--r--services/contentsuggestions/java/com/android/server/contentsuggestions/ContentSuggestionsPerUserService.java19
-rw-r--r--services/core/java/com/android/server/AlarmManagerInternal.java8
-rw-r--r--services/core/java/com/android/server/AlarmManagerService.java31
-rw-r--r--services/core/java/com/android/server/BinderCallsStatsService.java10
-rw-r--r--services/core/java/com/android/server/DeviceIdleController.java196
-rw-r--r--services/core/java/com/android/server/MasterClearReceiver.java2
-rw-r--r--services/core/java/com/android/server/UiModeManagerService.java120
-rw-r--r--services/core/java/com/android/server/am/ActivityManagerService.java4
-rw-r--r--services/core/java/com/android/server/am/CarUserSwitchingDialog.java1
-rw-r--r--services/core/java/com/android/server/am/OWNERS2
-rw-r--r--services/core/java/com/android/server/am/PendingIntentController.java3
-rw-r--r--services/core/java/com/android/server/am/ProcessList.java2
-rw-r--r--services/core/java/com/android/server/am/UserController.java74
-rw-r--r--services/core/java/com/android/server/am/UserSwitchingDialog.java13
-rw-r--r--services/core/java/com/android/server/display/BrightnessTracker.java5
-rw-r--r--services/core/java/com/android/server/display/color/ColorDisplayService.java68
-rw-r--r--services/core/java/com/android/server/display/color/TintController.java6
-rw-r--r--services/core/java/com/android/server/location/GnssLocationProvider.java43
-rw-r--r--services/core/java/com/android/server/notification/NotificationComparator.java6
-rwxr-xr-xservices/core/java/com/android/server/notification/NotificationManagerService.java62
-rw-r--r--services/core/java/com/android/server/notification/NotificationShellCmd.java105
-rw-r--r--services/core/java/com/android/server/pm/PackageInstallerSession.java4
-rw-r--r--services/core/java/com/android/server/pm/PackageManagerService.java7
-rw-r--r--services/core/java/com/android/server/pm/PackageManagerShellCommand.java16
-rw-r--r--services/core/java/com/android/server/pm/Settings.java4
-rw-r--r--services/core/java/com/android/server/pm/UserManagerService.java393
-rw-r--r--services/core/java/com/android/server/policy/PhoneWindowManager.java5
-rw-r--r--services/core/java/com/android/server/wallpaper/GLHelper.java148
-rw-r--r--services/core/java/com/android/server/wallpaper/WallpaperManagerService.java157
-rw-r--r--services/core/java/com/android/server/wm/DisplayPolicy.java6
-rw-r--r--services/java/com/android/server/SystemServer.java2
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/AlarmManagerServiceTest.java32
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/DeviceIdleControllerTest.java130
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/am/PendingIntentControllerTest.java130
-rw-r--r--services/tests/servicestests/Android.bp1
-rw-r--r--services/tests/servicestests/src/com/android/server/am/UserControllerTest.java52
-rw-r--r--services/tests/servicestests/src/com/android/server/contentsuggestions/ContentSuggestionsPerUserServiceTest.java90
-rw-r--r--services/tests/servicestests/src/com/android/server/display/BrightnessTrackerTest.java33
-rw-r--r--services/tests/servicestests/src/com/android/server/pm/UserManagerServiceUserInfoTest.java2
-rw-r--r--services/tests/servicestests/src/com/android/server/usage/AppIdleHistoryTests.java18
-rw-r--r--services/tests/uiservicestests/src/com/android/server/UiModeManagerServiceTest.java161
-rw-r--r--services/tests/uiservicestests/src/com/android/server/notification/NotificationComparatorTest.java15
-rw-r--r--services/tests/uiservicestests/src/com/android/server/notification/NotificationListenerServiceTest.java15
-rwxr-xr-xservices/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java14
-rw-r--r--services/tests/uiservicestests/src/com/android/server/notification/NotificationShellCmdTest.java4
-rw-r--r--services/usage/java/com/android/server/usage/AppIdleHistory.java9
-rw-r--r--telephony/java/android/telephony/CarrierConfigManager.java55
-rw-r--r--telephony/java/android/telephony/SubscriptionInfo.java6
-rw-r--r--telephony/java/android/telephony/SubscriptionManager.java31
-rw-r--r--telephony/java/android/telephony/ims/ImsReasonInfo.java7
-rw-r--r--telephony/java/com/android/internal/telephony/gsm/GsmSmsCbMessage.java2
-rw-r--r--tests/testables/src/android/testing/TestableSettingsProvider.java6
-rw-r--r--wifi/java/android/net/wifi/WifiManager.java77
-rw-r--r--wifi/java/android/net/wifi/aware/WifiAwareNetworkInfo.java9
-rw-r--r--wifi/java/android/net/wifi/rtt/package.html4
234 files changed, 6396 insertions, 1898 deletions
diff --git a/cmds/statsd/Android.bp b/cmds/statsd/Android.bp
index 8944dedd6c96..e12749e6b9fe 100644
--- a/cmds/statsd/Android.bp
+++ b/cmds/statsd/Android.bp
@@ -50,6 +50,7 @@ cc_defaults {
srcs: [
":statsd_aidl",
+ ":ICarStatsService.aidl",
"src/active_config_list.proto",
"src/statsd_config.proto",
"src/uid_data.proto",
@@ -69,6 +70,7 @@ cc_defaults {
"src/config/ConfigKey.cpp",
"src/config/ConfigListener.cpp",
"src/config/ConfigManager.cpp",
+ "src/external/CarStatsPuller.cpp",
"src/external/GpuStatsPuller.cpp",
"src/external/Perfetto.cpp",
"src/external/StatsPuller.cpp",
diff --git a/cmds/statsd/src/atoms.proto b/cmds/statsd/src/atoms.proto
index 4440bf89ed65..552a426e5341 100644
--- a/cmds/statsd/src/atoms.proto
+++ b/cmds/statsd/src/atoms.proto
@@ -326,6 +326,7 @@ message Atom {
228 [(allow_from_any_uid) = true];
PerfettoUploaded perfetto_uploaded =
229 [(log_from_module) = "perfetto"];
+ VmsClientConnectionStateChanged vms_client_connection_state_changed = 230;
}
// Pulled events will start at field 10000.
@@ -393,6 +394,7 @@ message Atom {
CoolingDevice cooling_device = 10059;
AppOps app_ops = 10060;
ProcessSystemIonHeapSize process_system_ion_heap_size = 10061;
+ VmsClientStats vms_client_stats = 10065;
}
// DO NOT USE field numbers above 100,000 in AOSP.
@@ -3713,6 +3715,33 @@ message RoleRequestResultReported {
optional Result result = 9;
}
+/**
+ * Logs when a Vehicle Maps Service client's connection state has changed
+ *
+ * Logged from:
+ * packages/services/Car/service/src/com/android/car/stats/VmsClientLog.java
+ */
+message VmsClientConnectionStateChanged {
+ // The UID of the VMS client app
+ optional int32 uid = 1 [(is_uid) = true];
+
+ enum State {
+ UNKNOWN = 0;
+ // Attempting to connect to the client
+ CONNECTING = 1;
+ // Client connection established
+ CONNECTED = 2;
+ // Client connection closed unexpectedly
+ DISCONNECTED = 3;
+ // Client connection closed by VMS
+ TERMINATED = 4;
+ // Error establishing the client connection
+ CONNECTION_ERROR = 5;
+ }
+
+ optional State state = 2;
+}
+
//////////////////////////////////////////////////////////////////////
// Pulled atoms below this line //
//////////////////////////////////////////////////////////////////////
@@ -6858,7 +6887,6 @@ message AppCompatibilityChangeReported {
// Where it was logged from.
optional Source source = 4;
-
}
/**
@@ -6894,3 +6922,32 @@ message PerfettoUploaded {
optional int64 trace_uuid_lsb = 2;
optional int64 trace_uuid_msb = 3;
}
+
+/**
+ * Pulls client metrics on data transferred via Vehicle Maps Service.
+ * Metrics are keyed by uid + layer.
+ *
+ * Pulled from:
+ * packages/services/Car/service/src/com/android/car/stats/CarStatsService.java
+ */
+message VmsClientStats {
+ // UID of the VMS client app
+ optional int32 uid = 1 [(is_uid) = true];
+
+ // VMS layer definition
+ optional int32 layer_type = 2;
+ optional int32 layer_channel = 3;
+ optional int32 layer_version = 4;
+
+ // Bytes and packets sent by the client for the layer
+ optional int64 tx_bytes = 5;
+ optional int64 tx_packets = 6;
+
+ // Bytes and packets received by the client for the layer
+ optional int64 rx_bytes = 7;
+ optional int64 rx_packets = 8;
+
+ // Bytes and packets dropped due to client error
+ optional int64 dropped_bytes = 9;
+ optional int64 dropped_packets = 10;
+}
diff --git a/cmds/statsd/src/external/CarStatsPuller.cpp b/cmds/statsd/src/external/CarStatsPuller.cpp
new file mode 100644
index 000000000000..70c0456b5eb4
--- /dev/null
+++ b/cmds/statsd/src/external/CarStatsPuller.cpp
@@ -0,0 +1,96 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define DEBUG false
+#include "Log.h"
+
+#include <binder/IServiceManager.h>
+#include <com/android/internal/car/ICarStatsService.h>
+
+#include "CarStatsPuller.h"
+#include "logd/LogEvent.h"
+#include "stats_log_util.h"
+
+using android::binder::Status;
+using com::android::internal::car::ICarStatsService;
+
+namespace android {
+namespace os {
+namespace statsd {
+
+static std::mutex gCarStatsMutex;
+static sp<ICarStatsService> gCarStats = nullptr;
+
+class CarStatsDeathRecipient : public android::IBinder::DeathRecipient {
+ public:
+ CarStatsDeathRecipient() = default;
+ ~CarStatsDeathRecipient() override = default;
+
+ // android::IBinder::DeathRecipient override:
+ void binderDied(const android::wp<android::IBinder>& /* who */) override {
+ ALOGE("Car service has died");
+ std::lock_guard<std::mutex> lock(gCarStatsMutex);
+ if (gCarStats) {
+ sp<IBinder> binder = IInterface::asBinder(gCarStats);
+ binder->unlinkToDeath(this);
+ gCarStats = nullptr;
+ }
+ }
+};
+
+static sp<CarStatsDeathRecipient> gDeathRecipient = new CarStatsDeathRecipient();
+
+static sp<ICarStatsService> getCarService() {
+ std::lock_guard<std::mutex> lock(gCarStatsMutex);
+ if (!gCarStats) {
+ const sp<IBinder> binder = defaultServiceManager()->checkService(String16("car_stats"));
+ if (!binder) {
+ ALOGW("Car service is unavailable");
+ return nullptr;
+ }
+ gCarStats = interface_cast<ICarStatsService>(binder);
+ binder->linkToDeath(gDeathRecipient);
+ }
+ return gCarStats;
+}
+
+CarStatsPuller::CarStatsPuller(const int tagId) : StatsPuller(tagId) {
+}
+
+bool CarStatsPuller::PullInternal(std::vector<std::shared_ptr<LogEvent>>* data) {
+ const sp<ICarStatsService> carService = getCarService();
+ if (!carService) {
+ return false;
+ }
+
+ vector<StatsLogEventWrapper> returned_value;
+ Status status = carService->pullData(mTagId, &returned_value);
+ if (!status.isOk()) {
+ ALOGW("CarStatsPuller::pull failed for %d", mTagId);
+ return false;
+ }
+
+ data->clear();
+ for (const StatsLogEventWrapper& it : returned_value) {
+ LogEvent::createLogEvents(it, *data);
+ }
+ VLOG("CarStatsPuller::pull succeeded for %d", mTagId);
+ return true;
+}
+
+} // namespace statsd
+} // namespace os
+} // namespace android
diff --git a/cmds/statsd/src/external/CarStatsPuller.h b/cmds/statsd/src/external/CarStatsPuller.h
new file mode 100644
index 000000000000..ca0f1a9c9a17
--- /dev/null
+++ b/cmds/statsd/src/external/CarStatsPuller.h
@@ -0,0 +1,36 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include "StatsPuller.h"
+
+namespace android {
+namespace os {
+namespace statsd {
+
+/**
+ * Pull atoms from CarService.
+ */
+class CarStatsPuller : public StatsPuller {
+public:
+ explicit CarStatsPuller(const int tagId);
+ bool PullInternal(std::vector<std::shared_ptr<LogEvent>>* data) override;
+};
+
+} // namespace statsd
+} // namespace os
+} // namespace android
diff --git a/cmds/statsd/src/external/StatsPullerManager.cpp b/cmds/statsd/src/external/StatsPullerManager.cpp
index 69e6a11b9ad7..246df404d69e 100644
--- a/cmds/statsd/src/external/StatsPullerManager.cpp
+++ b/cmds/statsd/src/external/StatsPullerManager.cpp
@@ -27,6 +27,7 @@
#include "../logd/LogEvent.h"
#include "../stats_log_util.h"
#include "../statscompanion_util.h"
+#include "CarStatsPuller.h"
#include "GpuStatsPuller.h"
#include "PowerStatsPuller.h"
#include "ResourceHealthManagerPuller.h"
@@ -267,6 +268,10 @@ std::map<int, PullAtomInfo> StatsPullerManager::kAllPullAtomInfo = {
// App ops
{android::util::APP_OPS,
{.puller = new StatsCompanionServicePuller(android::util::APP_OPS)}},
+ // VmsClientStats
+ {android::util::VMS_CLIENT_STATS,
+ {.additiveFields = {5, 6, 7, 8, 9, 10},
+ .puller = new CarStatsPuller(android::util::VMS_CLIENT_STATS)}},
};
StatsPullerManager::StatsPullerManager() : mNextPullTimeNs(NO_ALARM_UPDATE) {
diff --git a/core/java/Android.bp b/core/java/Android.bp
index fb27f74211fb..9a8e130436f8 100644
--- a/core/java/Android.bp
+++ b/core/java/Android.bp
@@ -7,3 +7,8 @@ filegroup {
name: "IDropBoxManagerService.aidl",
srcs: ["com/android/internal/os/IDropBoxManagerService.aidl"],
}
+
+filegroup {
+ name: "ICarStatsService.aidl",
+ srcs: ["com/android/internal/car/ICarStatsService.aidl"],
+}
diff --git a/core/java/android/accounts/AccountManager.java b/core/java/android/accounts/AccountManager.java
index c80be8e5c3fa..2d7adf3cfef2 100644
--- a/core/java/android/accounts/AccountManager.java
+++ b/core/java/android/accounts/AccountManager.java
@@ -563,6 +563,21 @@ public class AccountManager {
* account, or the AbstractAcccountAuthenticator managing the account did so or because the
* client shares a signature with the managing AbstractAccountAuthenticator.
*
+ * <div class="caution"><p><b>Caution: </b>This method returns personal and sensitive user data.
+ * If your app accesses, collects, uses, or shares personal and sensitive data, you must clearly
+ * disclose that fact to users. For apps published on Google Play, policies protecting user data
+ * require that you do the following:</p>
+ * <ul>
+ * <li>Disclose to the user how your app accesses, collects, uses, or shares personal and
+ * sensitive data. Learn more about
+ * <a href="https://play.google.com/about/privacy-security-deception/user-data/#!#personal-sensitive">acceptable
+ * disclosure and consent</a>.</li>
+ * <li>Provide a privacy policy that describes your use of this data on- and off-device.</li>
+ * </ul>
+ * <p>To learn more, visit the
+ * <a href="https://play.google.com/about/privacy-security-deception/user-data">Google Play
+ * Policy regarding user data</a>.</p></div>
+ *
* <p>
* It is safe to call this method from the main thread.
*
@@ -649,6 +664,22 @@ public class AccountManager {
* the account. For example, there are types corresponding to Google and Facebook. The exact
* string token to use will be published somewhere associated with the authenticator in
* question.
+ * </p>
+ *
+ * <div class="caution"><p><b>Caution: </b>This method returns personal and sensitive user data.
+ * If your app accesses, collects, uses, or shares personal and sensitive data, you must clearly
+ * disclose that fact to users. For apps published on Google Play, policies protecting user data
+ * require that you do the following:</p>
+ * <ul>
+ * <li>Disclose to the user how your app accesses, collects, uses, or shares personal and
+ * sensitive data. Learn more about
+ * <a href="https://play.google.com/about/privacy-security-deception/user-data/#!#personal-sensitive">acceptable
+ * disclosure and consent</a>.</li>
+ * <li>Provide a privacy policy that describes your use of this data on- and off-device.</li>
+ * </ul>
+ * <p>To learn more, visit the
+ * <a href="https://play.google.com/about/privacy-security-deception/user-data">Google Play
+ * Policy regarding user data</a>.</p></div>
*
* <p>
* It is safe to call this method from the main thread.
diff --git a/core/java/android/animation/FloatEvaluator.java b/core/java/android/animation/FloatEvaluator.java
index 9463aa12d116..ae90e37d4c71 100644
--- a/core/java/android/animation/FloatEvaluator.java
+++ b/core/java/android/animation/FloatEvaluator.java
@@ -24,7 +24,7 @@ public class FloatEvaluator implements TypeEvaluator<Number> {
/**
* This function returns the result of linearly interpolating the start and end values, with
* <code>fraction</code> representing the proportion between the start and end values. The
- * calculation is a simple parametric calculation: <code>result = x0 + t * (v1 - v0)</code>,
+ * calculation is a simple parametric calculation: <code>result = x0 + t * (x1 - x0)</code>,
* where <code>x0</code> is <code>startValue</code>, <code>x1</code> is <code>endValue</code>,
* and <code>t</code> is <code>fraction</code>.
*
diff --git a/core/java/android/app/DisabledWallpaperManager.java b/core/java/android/app/DisabledWallpaperManager.java
new file mode 100644
index 000000000000..518594191e6c
--- /dev/null
+++ b/core/java/android/app/DisabledWallpaperManager.java
@@ -0,0 +1,346 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.app;
+
+import android.annotation.NonNull;
+import android.content.ComponentName;
+import android.content.Intent;
+import android.graphics.Bitmap;
+import android.graphics.Rect;
+import android.graphics.drawable.Drawable;
+import android.net.Uri;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.ParcelFileDescriptor;
+import android.util.Log;
+
+import java.io.IOException;
+import java.io.InputStream;
+
+/**
+ * A no-op implementation of {@link WallpaperManager}.
+ */
+final class DisabledWallpaperManager extends WallpaperManager {
+
+ private static final String TAG = DisabledWallpaperManager.class.getSimpleName();
+
+ // Don't need to worry about synchronization
+ private static DisabledWallpaperManager sInstance;
+
+ // TODO(b/138939803): STOPSHIP changed to false and/or remove it
+ private static final boolean DEBUG = true;
+
+ @NonNull
+ static DisabledWallpaperManager getInstance() {
+ if (sInstance == null) {
+ sInstance = new DisabledWallpaperManager();
+ }
+ return sInstance;
+ }
+
+ private DisabledWallpaperManager() {
+ super(null, null, null);
+ }
+
+ @Override
+ public boolean isWallpaperSupported() {
+ return false;
+ }
+
+ @Override
+ public boolean isSetWallpaperAllowed() {
+ return false;
+ }
+
+ // TODO(b/138939803): STOPSHIP methods below should not be necessary,
+ // callers should check if isWallpaperSupported(), consider removing them to keep this class
+ // simpler
+
+ private static <T> T unsupported() {
+ if (DEBUG) Log.w(TAG, "unsupported method called; returning null", new Exception());
+ return null;
+ }
+
+ private static boolean unsupportedBoolean() {
+ if (DEBUG) Log.w(TAG, "unsupported method called; returning false", new Exception());
+ return false;
+ }
+
+ @Override
+ public Drawable getDrawable() {
+ return unsupported();
+ }
+
+ @Override
+ public Drawable getBuiltInDrawable() {
+ return unsupported();
+ }
+
+ @Override
+ public Drawable getBuiltInDrawable(int which) {
+ return unsupported();
+ }
+
+ @Override
+ public Drawable getBuiltInDrawable(int outWidth, int outHeight, boolean scaleToFit,
+ float horizontalAlignment, float verticalAlignment) {
+ return unsupported();
+ }
+
+ @Override
+ public Drawable getBuiltInDrawable(int outWidth, int outHeight, boolean scaleToFit,
+ float horizontalAlignment, float verticalAlignment, int which) {
+ return unsupported();
+ }
+
+ @Override
+ public Drawable peekDrawable() {
+ return unsupported();
+ }
+
+ @Override
+ public Drawable getFastDrawable() {
+ return unsupported();
+ }
+
+ @Override
+ public Drawable peekFastDrawable() {
+ return unsupported();
+ }
+
+ @Override
+ public Bitmap getBitmap() {
+ return unsupported();
+ }
+
+ @Override
+ public Bitmap getBitmap(boolean hardware) {
+ return unsupported();
+ }
+
+ @Override
+ public Bitmap getBitmapAsUser(int userId, boolean hardware) {
+ return unsupported();
+ }
+
+ @Override
+ public ParcelFileDescriptor getWallpaperFile(int which) {
+ return unsupported();
+ }
+
+ @Override
+ public void addOnColorsChangedListener(OnColorsChangedListener listener, Handler handler) {
+ unsupported();
+ }
+
+ @Override
+ public void addOnColorsChangedListener(OnColorsChangedListener listener, Handler handler,
+ int userId) {
+ unsupported();
+ }
+
+ @Override
+ public void removeOnColorsChangedListener(OnColorsChangedListener callback) {
+ unsupported();
+ }
+
+ @Override
+ public void removeOnColorsChangedListener(OnColorsChangedListener callback, int userId) {
+ unsupported();
+ }
+
+ @Override
+ public WallpaperColors getWallpaperColors(int which) {
+ return unsupported();
+ }
+
+ @Override
+ public WallpaperColors getWallpaperColors(int which, int userId) {
+ return unsupported();
+ }
+
+ @Override
+ public ParcelFileDescriptor getWallpaperFile(int which, int userId) {
+ return unsupported();
+ }
+
+ @Override
+ public void forgetLoadedWallpaper() {
+ unsupported();
+ }
+
+ @Override
+ public WallpaperInfo getWallpaperInfo() {
+ return unsupported();
+ }
+
+ @Override
+ public WallpaperInfo getWallpaperInfo(int userId) {
+ return unsupported();
+ }
+
+ @Override
+ public int getWallpaperId(int which) {
+ return unsupported();
+ }
+
+ @Override
+ public int getWallpaperIdForUser(int which, int userId) {
+ return unsupported();
+ }
+
+ @Override
+ public Intent getCropAndSetWallpaperIntent(Uri imageUri) {
+ return unsupported();
+ }
+
+ @Override
+ public void setResource(int resid) throws IOException {
+ unsupported();
+ }
+
+ @Override
+ public int setResource(int resid, int which) throws IOException {
+ return unsupported();
+ }
+
+ @Override
+ public void setBitmap(Bitmap bitmap) throws IOException {
+ unsupported();
+ }
+
+ @Override
+ public int setBitmap(Bitmap fullImage, Rect visibleCropHint, boolean allowBackup)
+ throws IOException {
+ return unsupported();
+ }
+
+ @Override
+ public int setBitmap(Bitmap fullImage, Rect visibleCropHint, boolean allowBackup, int which)
+ throws IOException {
+ return unsupported();
+ }
+
+ @Override
+ public int setBitmap(Bitmap fullImage, Rect visibleCropHint, boolean allowBackup, int which,
+ int userId) throws IOException {
+ return unsupported();
+ }
+
+ @Override
+ public void setStream(InputStream bitmapData) throws IOException {
+ unsupported();
+ }
+
+ @Override
+ public int setStream(InputStream bitmapData, Rect visibleCropHint, boolean allowBackup)
+ throws IOException {
+ return unsupported();
+ }
+
+ @Override
+ public int setStream(InputStream bitmapData, Rect visibleCropHint, boolean allowBackup,
+ int which) throws IOException {
+ return unsupported();
+ }
+
+ @Override
+ public boolean hasResourceWallpaper(int resid) {
+ return unsupportedBoolean();
+ }
+
+ @Override
+ public int getDesiredMinimumWidth() {
+ return unsupported();
+ }
+
+ @Override
+ public int getDesiredMinimumHeight() {
+ return unsupported();
+ }
+
+ @Override
+ public void suggestDesiredDimensions(int minimumWidth, int minimumHeight) {
+ unsupported();
+ }
+
+ @Override
+ public void setDisplayPadding(Rect padding) {
+ unsupported();
+ }
+
+ @Override
+ public void setDisplayOffset(IBinder windowToken, int x, int y) {
+ unsupported();
+ }
+
+ @Override
+ public void clearWallpaper() {
+ unsupported();
+ }
+
+ @Override
+ public void clearWallpaper(int which, int userId) {
+ unsupported();
+ }
+
+ @Override
+ public boolean setWallpaperComponent(ComponentName name) {
+ return unsupportedBoolean();
+ }
+
+ @Override
+ public boolean setWallpaperComponent(ComponentName name, int userId) {
+ return unsupportedBoolean();
+ }
+
+ @Override
+ public void setWallpaperOffsets(IBinder windowToken, float xOffset, float yOffset) {
+ unsupported();
+ }
+
+ @Override
+ public void setWallpaperOffsetSteps(float xStep, float yStep) {
+ unsupported();
+ }
+
+ @Override
+ public void sendWallpaperCommand(IBinder windowToken, String action, int x, int y, int z,
+ Bundle extras) {
+ unsupported();
+ }
+
+ @Override
+ public void clearWallpaperOffsets(IBinder windowToken) {
+ unsupported();
+ }
+
+ @Override
+ public void clear() throws IOException {
+ unsupported();
+ }
+
+ @Override
+ public void clear(int which) throws IOException {
+ unsupported();
+ }
+
+ @Override
+ public boolean isWallpaperBackupEligible(int which) {
+ return unsupportedBoolean();
+ }
+}
diff --git a/core/java/android/app/IUiModeManager.aidl b/core/java/android/app/IUiModeManager.aidl
index a3e0845af0ce..f5809ba627ff 100644
--- a/core/java/android/app/IUiModeManager.aidl
+++ b/core/java/android/app/IUiModeManager.aidl
@@ -68,4 +68,9 @@ interface IUiModeManager {
* Tells if Night mode is locked or not.
*/
boolean isNightModeLocked();
+
+ /**
+ * @hide
+ */
+ boolean setNightModeActivated(boolean active);
}
diff --git a/core/java/android/app/PendingIntent.java b/core/java/android/app/PendingIntent.java
index 6f7a0607cbdb..7b29bcd11ab9 100644
--- a/core/java/android/app/PendingIntent.java
+++ b/core/java/android/app/PendingIntent.java
@@ -1257,7 +1257,12 @@ public final class PendingIntent implements Parcelable {
return b != null ? new PendingIntent(b, in.getClassCookie(PendingIntent.class)) : null;
}
- /*package*/ PendingIntent(IIntentSender target) {
+ /**
+ * Creates a PendingIntent with the given target.
+ * @param target the backing IIntentSender
+ * @hide
+ */
+ public PendingIntent(IIntentSender target) {
mTarget = target;
}
diff --git a/core/java/android/app/SystemServiceRegistry.java b/core/java/android/app/SystemServiceRegistry.java
index d8ccc6244775..54d6e000ae2b 100644
--- a/core/java/android/app/SystemServiceRegistry.java
+++ b/core/java/android/app/SystemServiceRegistry.java
@@ -694,11 +694,22 @@ final class SystemServiceRegistry {
@Override
public WallpaperManager createService(ContextImpl ctx)
throws ServiceNotFoundException {
- final IBinder b;
- if (ctx.getApplicationInfo().targetSdkVersion >= Build.VERSION_CODES.P) {
- b = ServiceManager.getServiceOrThrow(Context.WALLPAPER_SERVICE);
- } else {
- b = ServiceManager.getService(Context.WALLPAPER_SERVICE);
+ final IBinder b = ServiceManager.getService(Context.WALLPAPER_SERVICE);
+ if (b == null) {
+ // There are 2 reason service can be null:
+ // 1.Device doesn't support it - that's fine
+ // 2.App is running on instant mode - should fail
+ final boolean enabled = Resources.getSystem()
+ .getBoolean(com.android.internal.R.bool.config_enableWallpaperService);
+ if (!enabled) {
+ // Life moves on...
+ return DisabledWallpaperManager.getInstance();
+ }
+ if (ctx.getApplicationInfo().targetSdkVersion >= Build.VERSION_CODES.P) {
+ // Instant app
+ throw new ServiceNotFoundException(Context.WALLPAPER_SERVICE);
+ }
+ // Bad state - WallpaperManager methods will throw exception
}
IWallpaperManager service = IWallpaperManager.Stub.asInterface(b);
return new WallpaperManager(service, ctx.getOuterContext(),
diff --git a/core/java/android/app/UiModeManager.java b/core/java/android/app/UiModeManager.java
index 83247875c8a5..d8c030d7aacf 100644
--- a/core/java/android/app/UiModeManager.java
+++ b/core/java/android/app/UiModeManager.java
@@ -473,4 +473,18 @@ public class UiModeManager {
}
return true;
}
+
+ /**
+ * @hide*
+ */
+ public boolean setNightModeActivated(boolean active) {
+ if (mService != null) {
+ try {
+ return mService.setNightModeActivated(active);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+ return false;
+ }
}
diff --git a/core/java/android/app/WallpaperManager.java b/core/java/android/app/WallpaperManager.java
index 325a54bffbfb..d607ed67d0f1 100644
--- a/core/java/android/app/WallpaperManager.java
+++ b/core/java/android/app/WallpaperManager.java
@@ -510,7 +510,9 @@ public class WallpaperManager {
/*package*/ WallpaperManager(IWallpaperManager service, Context context, Handler handler) {
mContext = context;
- initGlobals(service, context.getMainLooper());
+ if (service != null) {
+ initGlobals(service, context.getMainLooper());
+ }
}
/**
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index a17b2ddd7215..08527c79c242 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -4032,9 +4032,17 @@ public class DevicePolicyManager {
* Make the device lock immediately, as if the lock screen timeout has expired at the point of
* this call.
* <p>
+ * This method secures the device in response to an urgent situation, such as a lost or stolen
+ * device. After this method is called, the device must be unlocked using strong authentication
+ * (PIN, pattern, or password). This API is intended for use only by device admins.
+ * <p>
* The calling device admin must have requested {@link DeviceAdminInfo#USES_POLICY_FORCE_LOCK}
* to be able to call this method; if it has not, a security exception will be thrown.
* <p>
+ * If there's no lock type set, this method forces the device to go to sleep but doesn't lock
+ * the device. Device admins who find the device in this state can lock an otherwise-insecure
+ * device by first calling {@link #resetPassword} to set the password and then lock the device.
+ * <p>
* This method can be called on the {@link DevicePolicyManager} instance returned by
* {@link #getParentProfileInstance(ComponentName)} in order to lock the parent profile.
* <p>
@@ -4051,9 +4059,17 @@ public class DevicePolicyManager {
* Make the device lock immediately, as if the lock screen timeout has expired at the point of
* this call.
* <p>
+ * This method secures the device in response to an urgent situation, such as a lost or stolen
+ * device. After this method is called, the device must be unlocked using strong authentication
+ * (PIN, pattern, or password). This API is intended for use only by device admins.
+ * <p>
* The calling device admin must have requested {@link DeviceAdminInfo#USES_POLICY_FORCE_LOCK}
* to be able to call this method; if it has not, a security exception will be thrown.
* <p>
+ * If there's no lock type set, this method forces the device to go to sleep but doesn't lock
+ * the device. Device admins who find the device in this state can lock an otherwise-insecure
+ * device by first calling {@link #resetPassword} to set the password and then lock the device.
+ * <p>
* This method can be called on the {@link DevicePolicyManager} instance returned by
* {@link #getParentProfileInstance(ComponentName)} in order to lock the parent profile.
*
@@ -8080,7 +8096,7 @@ public class DevicePolicyManager {
* Sets which system features are enabled when the device runs in lock task mode. This method
* doesn't affect the features when lock task mode is inactive. Any system features not included
* in {@code flags} are implicitly disabled when calling this method. By default, only
- * {@link #LOCK_TASK_FEATURE_GLOBAL_ACTIONS} is enabled—all the other features are disabled. To
+ * {@link #LOCK_TASK_FEATURE_GLOBAL_ACTIONS} is enabled; all the other features are disabled. To
* disable the global actions dialog, call this method omitting
* {@link #LOCK_TASK_FEATURE_GLOBAL_ACTIONS}.
*
diff --git a/core/java/android/app/backup/WallpaperBackupHelper.java b/core/java/android/app/backup/WallpaperBackupHelper.java
index 36f5f9673236..5c0ddc1859db 100644
--- a/core/java/android/app/backup/WallpaperBackupHelper.java
+++ b/core/java/android/app/backup/WallpaperBackupHelper.java
@@ -85,6 +85,10 @@ public class WallpaperBackupHelper extends FileBackupHelperBase implements Backu
*/
@Override
public void restoreEntity(BackupDataInputStream data) {
+ if (mWpm == null) {
+ Slog.w(TAG, "restoreEntity(): no wallpaper service");
+ return;
+ }
final String key = data.getKey();
if (isKeyInList(key, mKeys)) {
if (key.equals(WALLPAPER_IMAGE_KEY)) {
diff --git a/core/java/android/app/contentsuggestions/ContentSuggestionsManager.java b/core/java/android/app/contentsuggestions/ContentSuggestionsManager.java
index 1bb81b1487af..1e6ab4136187 100644
--- a/core/java/android/app/contentsuggestions/ContentSuggestionsManager.java
+++ b/core/java/android/app/contentsuggestions/ContentSuggestionsManager.java
@@ -45,6 +45,17 @@ import java.util.concurrent.Executor;
*/
@SystemApi
public final class ContentSuggestionsManager {
+ /**
+ * Key into the extras Bundle passed to {@link #provideContextImage(int, Bundle)}.
+ * This can be used to provide the bitmap to
+ * {@link android.service.contentsuggestions.ContentSuggestionsService}.
+ * The value must be a {@link android.graphics.Bitmap} with the
+ * config {@link android.graphics.Bitmap.Config.HARDWARE}.
+ *
+ * @hide
+ */
+ public static final String EXTRA_BITMAP = "android.contentsuggestions.extra.BITMAP";
+
private static final String TAG = ContentSuggestionsManager.class.getSimpleName();
/**
@@ -70,7 +81,7 @@ public final class ContentSuggestionsManager {
* system content suggestions service.
*
* @param taskId of the task to snapshot.
- * @param imageContextRequestExtras sent with with request to provide implementation specific
+ * @param imageContextRequestExtras sent with request to provide implementation specific
* extra information.
*/
public void provideContextImage(
diff --git a/core/java/android/app/slice/SliceManager.java b/core/java/android/app/slice/SliceManager.java
index 955093d3380e..90ecce2a2170 100644
--- a/core/java/android/app/slice/SliceManager.java
+++ b/core/java/android/app/slice/SliceManager.java
@@ -390,6 +390,8 @@ public class SliceManager {
}
Bundle extras = new Bundle();
extras.putParcelable(SliceProvider.EXTRA_INTENT, intent);
+ extras.putParcelableArrayList(SliceProvider.EXTRA_SUPPORTED_SPECS,
+ new ArrayList<>(supportedSpecs));
final Bundle res = provider.call(SliceProvider.METHOD_MAP_INTENT, null, extras);
if (res == null) {
return null;
diff --git a/core/java/android/bluetooth/BluetoothDevice.java b/core/java/android/bluetooth/BluetoothDevice.java
index 49187dcde342..9efaca1e1abc 100644
--- a/core/java/android/bluetooth/BluetoothDevice.java
+++ b/core/java/android/bluetooth/BluetoothDevice.java
@@ -1095,6 +1095,24 @@ public final class BluetoothDevice implements Parcelable {
}
/**
+ * Get the Bluetooth alias of the remote device.
+ * If Alias is null, get the Bluetooth name instead.
+ *
+ * @return the Bluetooth alias, or null if no alias or there was a problem
+ * @hide
+ * @see #getAlias()
+ * @see #getName()
+ */
+ @UnsupportedAppUsage(publicAlternatives = "Use {@link #getName()} instead.")
+ public String getAliasName() {
+ String name = getAlias();
+ if (name == null) {
+ name = getName();
+ }
+ return name;
+ }
+
+ /**
* Get the most recent identified battery level of this Bluetooth device
* <p>Requires {@link android.Manifest.permission#BLUETOOTH}
*
diff --git a/core/java/android/bluetooth/le/ScanFilter.java b/core/java/android/bluetooth/le/ScanFilter.java
index 038994fb5535..7511fd051e41 100644
--- a/core/java/android/bluetooth/le/ScanFilter.java
+++ b/core/java/android/bluetooth/le/ScanFilter.java
@@ -671,8 +671,6 @@ public final class ScanFilter implements Parcelable {
/**
* 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.
*/
diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java
index 2a17800dd446..9e011ac33240 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -2677,6 +2677,9 @@ public class Intent implements Parcelable, Cloneable {
* that application is first launched (that is the first time it is moved
* out of the stopped state). The data contains the name of the package.
*
+ * <p>When the application is first launched, the application itself doesn't receive this
+ * broadcast.</p>
+ *
* <p class="note">This is a protected intent that can only be sent
* by the system.
*/
diff --git a/core/java/android/content/SyncStats.java b/core/java/android/content/SyncStats.java
index 03b2250edee1..9596a6016c44 100644
--- a/core/java/android/content/SyncStats.java
+++ b/core/java/android/content/SyncStats.java
@@ -58,7 +58,7 @@ public class SyncStats implements Parcelable {
* attempted to update or delete a version of a resource on the server. This is expected
* to clear itself automatically once the new state is retrieved from the server,
* though it may remain until the user intervenes manually, perhaps by clearing the
- * local storage and starting over frmo scratch. This is considered a hard error.
+ * local storage and starting over from scratch. This is considered a hard error.
*/
public long numConflictDetectedExceptions;
diff --git a/core/java/android/content/pm/PackageInstaller.java b/core/java/android/content/pm/PackageInstaller.java
index 1099d8bdc7dd..69ce3bd55071 100644
--- a/core/java/android/content/pm/PackageInstaller.java
+++ b/core/java/android/content/pm/PackageInstaller.java
@@ -101,19 +101,6 @@ import java.util.concurrent.Executor;
* <p>
* The ApiDemos project contains examples of using this API:
* <code>ApiDemos/src/com/example/android/apis/content/InstallApk*.java</code>.
- * <p>
- * On Android Q or above, an app installed notification will be posted
- * by system after a new app is installed.
- * To customize installer's notification icon, you should declare the following in the manifest
- * &lt;application> as follows: </p>
- * <pre>
- * &lt;meta-data android:name="com.android.packageinstaller.notification.smallIcon"
- * android:resource="@drawable/installer_notification_icon"/>
- * </pre>
- * <pre>
- * &lt;meta-data android:name="com.android.packageinstaller.notification.color"
- * android:resource="@color/installer_notification_color"/>
- * </pre>
*/
public class PackageInstaller {
private static final String TAG = "PackageInstaller";
diff --git a/core/java/android/content/pm/UserInfo.java b/core/java/android/content/pm/UserInfo.java
index 2b1b32ed3df2..56a62854d09e 100644
--- a/core/java/android/content/pm/UserInfo.java
+++ b/core/java/android/content/pm/UserInfo.java
@@ -16,12 +16,17 @@
package android.content.pm;
+import android.annotation.IntDef;
import android.annotation.UnsupportedAppUsage;
import android.os.Parcel;
import android.os.Parcelable;
import android.os.SystemProperties;
import android.os.UserHandle;
import android.os.UserManager;
+import android.util.DebugUtils;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
/**
* Per-user information.
@@ -94,6 +99,25 @@ public class UserInfo implements Parcelable {
*/
public static final int FLAG_DEMO = 0x00000200;
+ /**
+ * @hide
+ */
+ @IntDef(flag = true, prefix = "FLAG_", value = {
+ FLAG_PRIMARY,
+ FLAG_ADMIN,
+ FLAG_GUEST,
+ FLAG_RESTRICTED,
+ FLAG_INITIALIZED,
+ FLAG_MANAGED_PROFILE,
+ FLAG_DISABLED,
+ FLAG_QUIET_MODE,
+ FLAG_EPHEMERAL,
+ FLAG_DEMO
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface UserInfoFlag {
+ }
+
public static final int NO_PROFILE_GROUP_ID = UserHandle.USER_NULL;
@UnsupportedAppUsage
@@ -128,6 +152,18 @@ public class UserInfo implements Parcelable {
@UnsupportedAppUsage
public boolean guestToRemove;
+ /**
+ * This is used to optimize the creation of an user, i.e. OEMs might choose to pre-create a
+ * number of users at the first boot, so the actual creation later is faster.
+ *
+ * <p>A {@code preCreated} user is not a real user yet, so it should not show up on regular
+ * user operations (other than user creation per se).
+ *
+ * <p>Once the pre-created is used to create a "real" user later on, {@code preCreate} is set to
+ * {@code false}.
+ */
+ public boolean preCreated;
+
@UnsupportedAppUsage
public UserInfo(int id, String name, int flags) {
this(id, name, null, flags);
@@ -155,6 +191,13 @@ public class UserInfo implements Parcelable {
@UnsupportedAppUsage
public boolean isGuest() {
+ return isGuest(flags);
+ }
+
+ /**
+ * Checks if the flag denotes a guest user.
+ */
+ public static boolean isGuest(@UserInfoFlag int flags) {
return (flags & FLAG_GUEST) == FLAG_GUEST;
}
@@ -165,6 +208,13 @@ public class UserInfo implements Parcelable {
@UnsupportedAppUsage
public boolean isManagedProfile() {
+ return isManagedProfile(flags);
+ }
+
+ /**
+ * Checks if the flag denotes a managed profile.
+ */
+ public static boolean isManagedProfile(@UserInfoFlag int flags) {
return (flags & FLAG_MANAGED_PROFILE) == FLAG_MANAGED_PROFILE;
}
@@ -252,6 +302,7 @@ public class UserInfo implements Parcelable {
lastLoggedInTime = orig.lastLoggedInTime;
lastLoggedInFingerprint = orig.lastLoggedInFingerprint;
partial = orig.partial;
+ preCreated = orig.preCreated;
profileGroupId = orig.profileGroupId;
restrictedProfileParentId = orig.restrictedProfileParentId;
guestToRemove = orig.guestToRemove;
@@ -268,6 +319,22 @@ public class UserInfo implements Parcelable {
return "UserInfo{" + id + ":" + name + ":" + Integer.toHexString(flags) + "}";
}
+ /** @hide */
+ public String toFullString() {
+ return "UserInfo[id=" + id
+ + ", name=" + name
+ + ", flags=" + flagsToString(flags)
+ + (preCreated ? " (pre-created)" : "")
+ + (partial ? " (partial)" : "")
+ + "]";
+ }
+
+ /** @hide */
+ public static String flagsToString(int flags) {
+ return DebugUtils.flagsToString(UserInfo.class, "FLAG_", flags);
+ }
+
+ @Override
public int describeContents() {
return 0;
}
@@ -281,9 +348,10 @@ public class UserInfo implements Parcelable {
dest.writeLong(creationTime);
dest.writeLong(lastLoggedInTime);
dest.writeString(lastLoggedInFingerprint);
- dest.writeInt(partial ? 1 : 0);
+ dest.writeBoolean(partial);
+ dest.writeBoolean(preCreated);
dest.writeInt(profileGroupId);
- dest.writeInt(guestToRemove ? 1 : 0);
+ dest.writeBoolean(guestToRemove);
dest.writeInt(restrictedProfileParentId);
dest.writeInt(profileBadge);
}
@@ -308,9 +376,10 @@ public class UserInfo implements Parcelable {
creationTime = source.readLong();
lastLoggedInTime = source.readLong();
lastLoggedInFingerprint = source.readString();
- partial = source.readInt() != 0;
+ partial = source.readBoolean();
+ preCreated = source.readBoolean();
profileGroupId = source.readInt();
- guestToRemove = source.readInt() != 0;
+ guestToRemove = source.readBoolean();
restrictedProfileParentId = source.readInt();
profileBadge = source.readInt();
}
diff --git a/core/java/android/content/res/TypedArray.java b/core/java/android/content/res/TypedArray.java
index b79cf6566987..38df317575d7 100644
--- a/core/java/android/content/res/TypedArray.java
+++ b/core/java/android/content/res/TypedArray.java
@@ -360,8 +360,9 @@ public class TypedArray {
/**
* Retrieve the boolean value for the attribute at <var>index</var>.
* <p>
- * If the attribute is an integer value, this method will return whether
- * it is equal to zero. If the attribute is not a boolean or integer value,
+ * If the attribute is an integer value, this method returns false if the
+ * attribute is equal to zero, and true otherwise.
+ * If the attribute is not a boolean or integer value,
* this method will attempt to coerce it to an integer using
* {@link Integer#decode(String)} and return whether it is equal to zero.
*
diff --git a/core/java/android/net/nsd/NsdManager.java b/core/java/android/net/nsd/NsdManager.java
index 535bf675cd0e..64f20b839a63 100644
--- a/core/java/android/net/nsd/NsdManager.java
+++ b/core/java/android/net/nsd/NsdManager.java
@@ -49,8 +49,8 @@ import java.util.concurrent.CountDownLatch;
* limited to a local network over Multicast DNS. DNS service discovery is described at
* http://files.dns-sd.org/draft-cheshire-dnsext-dns-sd.txt
*
- * <p> The API is asynchronous and responses to requests from an application are on listener
- * callbacks on a seperate internal thread.
+ * <p> The API is asynchronous, and responses to requests from an application are on listener
+ * callbacks on a separate internal thread.
*
* <p> There are three main operations the API supports - registration, discovery and resolution.
* <pre>
diff --git a/core/java/android/os/Build.java b/core/java/android/os/Build.java
index 1eda4d9749a8..772109a339f5 100755
--- a/core/java/android/os/Build.java
+++ b/core/java/android/os/Build.java
@@ -244,7 +244,8 @@ public class Build {
public static final String BASE_OS = SystemProperties.get("ro.build.version.base_os", "");
/**
- * The user-visible security patch level.
+ * The user-visible security patch level. This value represents the date when the device
+ * most recently applied a security patch.
*/
public static final String SECURITY_PATCH = SystemProperties.get(
"ro.build.version.security_patch", "");
diff --git a/core/java/android/os/FileObserver.java b/core/java/android/os/FileObserver.java
index 4d9ebc2b5923..5b715c0dcb3c 100644
--- a/core/java/android/os/FileObserver.java
+++ b/core/java/android/os/FileObserver.java
@@ -32,7 +32,7 @@ import java.util.List;
/**
* Monitors files (using <a href="http://en.wikipedia.org/wiki/Inotify">inotify</a>)
- * to fire an event after files are accessed or changed by by any process on
+ * to fire an event after files are accessed or changed by any process on
* the device (including this one). FileObserver is an abstract class;
* subclasses must implement the event handler {@link #onEvent(int, String)}.
*
diff --git a/core/java/android/os/IUserManager.aidl b/core/java/android/os/IUserManager.aidl
index 63641e538b8e..c30491a3965c 100644
--- a/core/java/android/os/IUserManager.aidl
+++ b/core/java/android/os/IUserManager.aidl
@@ -41,6 +41,7 @@ interface IUserManager {
*/
UserInfo createUser(in String name, int flags);
+ UserInfo preCreateUser(int flags);
UserInfo createProfileForUser(in String name, int flags, int userHandle,
in String[] disallowedPackages);
UserInfo createRestrictedProfile(String name, int parentUserHandle);
@@ -53,7 +54,7 @@ interface IUserManager {
void setUserIcon(int userHandle, in Bitmap icon);
ParcelFileDescriptor getUserIcon(int userHandle);
UserInfo getPrimaryUser();
- List<UserInfo> getUsers(boolean excludeDying);
+ List<UserInfo> getUsers(boolean excludePartial, boolean excludeDying, boolean excludePreCreated);
List<UserInfo> getProfiles(int userHandle, boolean enabledOnly);
int[] getProfileIds(int userId, boolean enabledOnly);
boolean canAddMoreManagedProfiles(int userHandle, boolean allowedToRemoveOne);
@@ -92,6 +93,7 @@ interface IUserManager {
boolean someUserHasSeedAccount(in String accountName, in String accountType);
boolean isManagedProfile(int userId);
boolean isDemoUser(int userId);
+ boolean isPreCreated(int userId);
UserInfo createProfileForUserEvenWhenDisallowed(in String name, int flags, int userHandle,
in String[] disallowedPackages);
boolean isUserUnlockingOrUnlocked(int userId);
diff --git a/core/java/android/os/UserManager.java b/core/java/android/os/UserManager.java
index c15618bdaa78..1880321c3390 100644
--- a/core/java/android/os/UserManager.java
+++ b/core/java/android/os/UserManager.java
@@ -37,6 +37,7 @@ import android.content.Intent;
import android.content.IntentFilter;
import android.content.IntentSender;
import android.content.pm.UserInfo;
+import android.content.pm.UserInfo.UserInfoFlag;
import android.content.res.Configuration;
import android.content.res.Resources;
import android.graphics.Bitmap;
@@ -2006,18 +2007,20 @@ public class UserManager {
/**
* Creates a user with the specified name and options. For non-admin users, default user
- * restrictions are going to be applied.
- * Requires {@link android.Manifest.permission#MANAGE_USERS} permission.
+ * restrictions will be applied.
+ *
+ * <p>Requires {@link android.Manifest.permission#MANAGE_USERS} permission.
*
* @param name the user's name
- * @param flags flags that identify the type of user and other properties.
+ * @param flags UserInfo flags that identify the type of user and other properties.
* @see UserInfo
*
- * @return the UserInfo object for the created user, or null if the user could not be created.
+ * @return the UserInfo object for the created user, or {@code null} if the user could not be
+ * created.
* @hide
*/
@UnsupportedAppUsage
- public UserInfo createUser(String name, int flags) {
+ public @Nullable UserInfo createUser(@Nullable String name, @UserInfoFlag int flags) {
UserInfo user = null;
try {
user = mService.createUser(name, flags);
@@ -2034,6 +2037,44 @@ public class UserManager {
}
/**
+ * Pre-creates a user with the specified name and options. For non-admin users, default user
+ * restrictions will be applied.
+ *
+ * <p>This method can be used by OEMs to "warm" up the user creation by pre-creating some users
+ * at the first boot, so they when the "real" user is created (for example,
+ * by {@link #createUser(String, int)} or {@link #createGuest(Context, String)}), it takes
+ * less time.
+ *
+ * <p>This method completes the majority of work necessary for user creation: it
+ * creates user data, CE and DE encryption keys, app data directories, initializes the user and
+ * grants default permissions. When pre-created users become "real" users, only then are
+ * components notified of new user creation by firing user creation broadcasts.
+ *
+ * <p>All pre-created users are removed during system upgrade.
+ *
+ * <p>Requires {@link android.Manifest.permission#MANAGE_USERS} permission.
+ *
+ * @param flags UserInfo flags that identify the type of user and other properties.
+ * @see UserInfo
+ *
+ * @return the UserInfo object for the created user, or {@code null} if the user could not be
+ * created.
+ *
+ * @throw {@link IllegalArgumentException} if {@code flags} contains
+ * {@link UserInfo#FLAG_MANAGED_PROFILE}.
+ *
+ * @hide
+ */
+ @RequiresPermission(android.Manifest.permission.MANAGE_USERS)
+ public @Nullable UserInfo preCreateUser(@UserInfoFlag int flags) {
+ try {
+ return mService.preCreateUser(flags);
+ } catch (RemoteException re) {
+ throw re.rethrowFromSystemServer();
+ }
+ }
+
+ /**
* Creates a guest user and configures it.
* @param context an application context
* @param name the name to set for the user
@@ -2340,6 +2381,8 @@ public class UserManager {
/**
* Return the number of users currently created on the device.
+ * <p>This API is not for use by third-party apps. It requires the {@code MANAGE_USERS}
+ * permission.</p>
*/
public int getUserCount() {
List<UserInfo> users = getUsers();
@@ -2349,15 +2392,26 @@ public class UserManager {
/**
* Returns information for all users on this device, including ones marked for deletion.
* To retrieve only users that are alive, use {@link #getUsers(boolean)}.
- * <p>
- * Requires {@link android.Manifest.permission#MANAGE_USERS} permission.
+ *
* @return the list of users that exist on the device.
* @hide
*/
@UnsupportedAppUsage
+ @RequiresPermission(android.Manifest.permission.MANAGE_USERS)
public List<UserInfo> getUsers() {
+ return getUsers(/* excludeDying= */ false);
+ }
+
+ /**
+ * Returns information for all users on this device, based on the filtering parameters.
+ *
+ * @hide
+ */
+ @RequiresPermission(android.Manifest.permission.MANAGE_USERS)
+ public List<UserInfo> getUsers(boolean excludePartial, boolean excludeDying,
+ boolean excludePreCreated) {
try {
- return mService.getUsers(false);
+ return mService.getUsers(excludePartial, excludeDying, excludePreCreated);
} catch (RemoteException re) {
throw re.rethrowFromSystemServer();
}
@@ -2373,16 +2427,12 @@ public class UserManager {
@SystemApi
@RequiresPermission(android.Manifest.permission.MANAGE_USERS)
public long[] getSerialNumbersOfUsers(boolean excludeDying) {
- try {
- List<UserInfo> users = mService.getUsers(excludeDying);
- long[] result = new long[users.size()];
- for (int i = 0; i < result.length; i++) {
- result[i] = users.get(i).serialNumber;
- }
- return result;
- } catch (RemoteException re) {
- throw re.rethrowFromSystemServer();
+ List<UserInfo> users = getUsers(excludeDying);
+ long[] result = new long[users.size()];
+ for (int i = 0; i < result.length; i++) {
+ result[i] = users.get(i).serialNumber;
}
+ return result;
}
/**
@@ -2768,11 +2818,8 @@ public class UserManager {
*/
@UnsupportedAppUsage
public @NonNull List<UserInfo> getUsers(boolean excludeDying) {
- try {
- return mService.getUsers(excludeDying);
- } catch (RemoteException re) {
- throw re.rethrowFromSystemServer();
- }
+ return getUsers(/*excludePartial= */ true, excludeDying,
+ /* excludePreCreated= */ true);
}
/**
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index da4bd27995c6..b393cdbca28a 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -7857,6 +7857,19 @@ public final class Settings {
NON_NEGATIVE_INTEGER_VALIDATOR;
/**
+ * Number of successful "Motion Sense" tap gestures to pause media.
+ * @hide
+ */
+ public static final String AWARE_TAP_PAUSE_GESTURE_COUNT = "aware_tap_pause_gesture_count";
+
+ /**
+ * Number of touch interactions to pause media when a "Motion Sense" gesture could
+ * have been used.
+ * @hide
+ */
+ public static final String AWARE_TAP_PAUSE_TOUCH_COUNT = "aware_tap_pause_touch_count";
+
+ /**
* The current night mode that has been selected by the user. Owned
* and controlled by UiModeManagerService. Constants are as per
* UiModeManager.
@@ -8937,6 +8950,14 @@ public final class Settings {
new SettingsValidators.DiscreteValueValidator(new String[] {"0", "1", "2"});
/**
+ * Current provider of proximity-based sharing services.
+ * Default value in @string/config_defaultNearbySharingComponent.
+ * No VALIDATOR as this setting will not be backed up.
+ * @hide
+ */
+ public static final String NEARBY_SHARING_COMPONENT = "nearby_sharing_component";
+
+ /**
* Controls whether aware is enabled.
* @hide
*/
@@ -8953,6 +8974,14 @@ public final class Settings {
private static final Validator AWARE_LOCK_ENABLED_VALIDATOR = BOOLEAN_VALIDATOR;
/**
+ * Controls whether tap gesture is enabled.
+ * @hide
+ */
+ public static final String TAP_GESTURE = "tap_gesture";
+
+ private static final Validator TAP_GESTURE_VALIDATOR = BOOLEAN_VALIDATOR;
+
+ /**
* This are the settings to be backed up.
*
* NOTE: Settings are backed up and restored in the order they appear
@@ -9034,8 +9063,6 @@ public final class Settings {
DOZE_PICK_UP_GESTURE,
DOZE_DOUBLE_TAP_GESTURE,
DOZE_TAP_SCREEN_GESTURE,
- DOZE_WAKE_LOCK_SCREEN_GESTURE,
- DOZE_WAKE_DISPLAY_GESTURE,
NFC_PAYMENT_DEFAULT_COMPONENT,
AUTOMATIC_STORAGE_MANAGER_DAYS_TO_RETAIN,
FACE_UNLOCK_KEYGUARD_ENABLED,
@@ -9043,9 +9070,6 @@ public final class Settings {
FACE_UNLOCK_DISMISSES_KEYGUARD,
FACE_UNLOCK_APP_ENABLED,
FACE_UNLOCK_ALWAYS_REQUIRE_CONFIRMATION,
- ASSIST_GESTURE_ENABLED,
- ASSIST_GESTURE_SILENCE_ALERTS_ENABLED,
- ASSIST_GESTURE_WAKE_ENABLED,
VR_DISPLAY_MODE,
NOTIFICATION_BADGING,
NOTIFICATION_DISMISS_RTL,
@@ -9078,12 +9102,9 @@ public final class Settings {
TRUST_AGENTS_EXTEND_UNLOCK,
UI_NIGHT_MODE,
LOCK_SCREEN_WHEN_TRUST_LOST,
- SKIP_GESTURE,
SKIP_DIRECTION,
- SILENCE_GESTURE,
THEME_CUSTOMIZATION_OVERLAY_PACKAGES,
NAVIGATION_MODE,
- AWARE_ENABLED,
SKIP_GESTURE_COUNT,
SKIP_TOUCH_COUNT,
SILENCE_ALARMS_GESTURE_COUNT,
@@ -9094,7 +9115,9 @@ public final class Settings {
SILENCE_TIMER_TOUCH_COUNT,
DARK_MODE_DIALOG_SEEN,
GLOBAL_ACTIONS_PANEL_ENABLED,
- AWARE_LOCK_ENABLED
+ AWARE_LOCK_ENABLED,
+ AWARE_TAP_PAUSE_GESTURE_COUNT,
+ AWARE_TAP_PAUSE_TOUCH_COUNT
};
/**
@@ -9289,6 +9312,9 @@ public final class Settings {
VALIDATORS.put(UI_NIGHT_MODE, UI_NIGHT_MODE_VALIDATOR);
VALIDATORS.put(GLOBAL_ACTIONS_PANEL_ENABLED, GLOBAL_ACTIONS_PANEL_ENABLED_VALIDATOR);
VALIDATORS.put(AWARE_LOCK_ENABLED, AWARE_LOCK_ENABLED_VALIDATOR);
+ VALIDATORS.put(AWARE_TAP_PAUSE_GESTURE_COUNT, NON_NEGATIVE_INTEGER_VALIDATOR);
+ VALIDATORS.put(AWARE_TAP_PAUSE_TOUCH_COUNT, NON_NEGATIVE_INTEGER_VALIDATOR);
+ VALIDATORS.put(TAP_GESTURE, TAP_GESTURE_VALIDATOR);
}
/**
diff --git a/core/java/android/service/contentsuggestions/ContentSuggestionsService.java b/core/java/android/service/contentsuggestions/ContentSuggestionsService.java
index efc8e877f3c3..306b4830932e 100644
--- a/core/java/android/service/contentsuggestions/ContentSuggestionsService.java
+++ b/core/java/android/service/contentsuggestions/ContentSuggestionsService.java
@@ -64,14 +64,23 @@ public abstract class ContentSuggestionsService extends Service {
@Override
public void provideContextImage(int taskId, GraphicBuffer contextImage,
int colorSpaceId, Bundle imageContextRequestExtras) {
+ if (imageContextRequestExtras.containsKey(ContentSuggestionsManager.EXTRA_BITMAP)
+ && contextImage != null) {
+ throw new IllegalArgumentException("Two bitmaps provided; expected one.");
+ }
Bitmap wrappedBuffer = null;
- if (contextImage != null) {
- ColorSpace colorSpace = null;
- if (colorSpaceId >= 0 && colorSpaceId < ColorSpace.Named.values().length) {
- colorSpace = ColorSpace.get(ColorSpace.Named.values()[colorSpaceId]);
+ if (imageContextRequestExtras.containsKey(ContentSuggestionsManager.EXTRA_BITMAP)) {
+ wrappedBuffer = imageContextRequestExtras.getParcelable(
+ ContentSuggestionsManager.EXTRA_BITMAP);
+ } else {
+ if (contextImage != null) {
+ ColorSpace colorSpace = null;
+ if (colorSpaceId >= 0 && colorSpaceId < ColorSpace.Named.values().length) {
+ colorSpace = ColorSpace.get(ColorSpace.Named.values()[colorSpaceId]);
+ }
+ wrappedBuffer = Bitmap.wrapHardwareBuffer(contextImage, colorSpace);
}
- wrappedBuffer = Bitmap.wrapHardwareBuffer(contextImage, colorSpace);
}
mHandler.sendMessage(
diff --git a/core/java/android/service/notification/NotificationListenerService.java b/core/java/android/service/notification/NotificationListenerService.java
index b44c9d59ebe5..93e3ea44f7c7 100644
--- a/core/java/android/service/notification/NotificationListenerService.java
+++ b/core/java/android/service/notification/NotificationListenerService.java
@@ -1514,6 +1514,7 @@ public abstract class NotificationListenerService extends Service {
private ArrayList<Notification.Action> mSmartActions;
private ArrayList<CharSequence> mSmartReplies;
private boolean mCanBubble;
+ private boolean mVisuallyInterruptive;
private static final int PARCEL_VERSION = 2;
@@ -1545,6 +1546,7 @@ public abstract class NotificationListenerService extends Service {
out.writeTypedList(mSmartActions, flags);
out.writeCharSequenceList(mSmartReplies);
out.writeBoolean(mCanBubble);
+ out.writeBoolean(mVisuallyInterruptive);
}
/** @hide */
@@ -1577,6 +1579,7 @@ public abstract class NotificationListenerService extends Service {
mSmartActions = in.createTypedArrayList(Notification.Action.CREATOR);
mSmartReplies = in.readCharSequenceList();
mCanBubble = in.readBoolean();
+ mVisuallyInterruptive = in.readBoolean();
}
@@ -1764,6 +1767,11 @@ public abstract class NotificationListenerService extends Service {
}
/** @hide */
+ public boolean visuallyInterruptive() {
+ return mVisuallyInterruptive;
+ }
+
+ /** @hide */
public boolean isNoisy() {
return mNoisy;
}
@@ -1779,7 +1787,8 @@ public abstract class NotificationListenerService extends Service {
ArrayList<SnoozeCriterion> snoozeCriteria, boolean showBadge,
int userSentiment, boolean hidden, long lastAudiblyAlertedMs,
boolean noisy, ArrayList<Notification.Action> smartActions,
- ArrayList<CharSequence> smartReplies, boolean canBubble) {
+ ArrayList<CharSequence> smartReplies, boolean canBubble,
+ boolean visuallyInterruptive) {
mKey = key;
mRank = rank;
mIsAmbient = importance < NotificationManager.IMPORTANCE_LOW;
@@ -1800,6 +1809,7 @@ public abstract class NotificationListenerService extends Service {
mSmartActions = smartActions;
mSmartReplies = smartReplies;
mCanBubble = canBubble;
+ mVisuallyInterruptive = visuallyInterruptive;
}
/**
@@ -1824,7 +1834,8 @@ public abstract class NotificationListenerService extends Service {
other.mNoisy,
other.mSmartActions,
other.mSmartReplies,
- other.mCanBubble);
+ other.mCanBubble,
+ other.mVisuallyInterruptive);
}
/**
diff --git a/core/java/android/util/DisplayMetrics.java b/core/java/android/util/DisplayMetrics.java
index 7c7223c04b59..451a669d98fd 100755
--- a/core/java/android/util/DisplayMetrics.java
+++ b/core/java/android/util/DisplayMetrics.java
@@ -23,9 +23,8 @@ import android.os.SystemProperties;
/**
* A structure describing general information about a display, such as its
* size, density, and font scaling.
- * <p>To access the DisplayMetrics members, initialize an object like this:</p>
- * <pre> DisplayMetrics metrics = new DisplayMetrics();
- * getWindowManager().getDefaultDisplay().getMetrics(metrics);</pre>
+ * <p>To access the DisplayMetrics members, retrieve display metrics like this:</p>
+ * <pre>context.getResources().getDisplayMetrics();</pre>
*/
public class DisplayMetrics {
/**
@@ -245,7 +244,7 @@ public class DisplayMetrics {
* this density value will be 1; on a 120 dpi screen it would be .75; etc.
*
* <p>This value does not exactly follow the real screen size (as given by
- * {@link #xdpi} and {@link #ydpi}, but rather is used to scale the size of
+ * {@link #xdpi} and {@link #ydpi}), but rather is used to scale the size of
* the overall UI in steps based on gross changes in the display dpi. For
* example, a 240x320 screen will have a density of 1 even if its width is
* 1.8", 1.3", etc. However, if the screen resolution is increased to
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index 9d2040c5bdb4..b7a128d6f810 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -492,7 +492,7 @@ import java.util.function.Predicate;
*
* <p>
* To initiate a layout, call {@link #requestLayout}. This method is typically
- * called by a view on itself when it believes that is can no longer fit within
+ * called by a view on itself when it believes that it can no longer fit within
* its current bounds.
* </p>
*
@@ -2850,7 +2850,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
/**
* Default for the root view. The gravity determines the text alignment, ALIGN_NORMAL,
- * ALIGN_CENTER, or ALIGN_OPPOSITE, which are relative to each paragraph’s text direction.
+ * ALIGN_CENTER, or ALIGN_OPPOSITE, which are relative to each paragraph's text direction.
*
* Use with {@link #setTextAlignment(int)}
*/
@@ -2878,7 +2878,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
public static final int TEXT_ALIGNMENT_CENTER = 4;
/**
- * Align to the start of the view, which is ALIGN_LEFT if the view’s resolved
+ * Align to the start of the view, which is ALIGN_LEFT if the view's resolved
* layoutDirection is LTR, and ALIGN_RIGHT otherwise.
*
* Use with {@link #setTextAlignment(int)}
@@ -2886,7 +2886,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
public static final int TEXT_ALIGNMENT_VIEW_START = 5;
/**
- * Align to the end of the view, which is ALIGN_RIGHT if the view’s resolved
+ * Align to the end of the view, which is ALIGN_RIGHT if the view's resolved
* layoutDirection is LTR, and ALIGN_LEFT otherwise.
*
* Use with {@link #setTextAlignment(int)}
@@ -3689,7 +3689,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
* if the user swipes from the top of the screen.
* <p>When system bars are hidden in immersive mode, they can be revealed temporarily with
* system gestures, such as swiping from the top of the screen. These transient system bars
- * will overlay app’s content, may have some degree of transparency, and will automatically
+ * will overlay app's content, may have some degree of transparency, and will automatically
* hide after a short timeout.
* </p><p>Since this flag is a modifier for {@link #SYSTEM_UI_FLAG_FULLSCREEN} and
* {@link #SYSTEM_UI_FLAG_HIDE_NAVIGATION}, it only has an effect when used in combination
@@ -10298,7 +10298,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
}
/**
- * Gets the unique identifier of the window in which this View reseides.
+ * Gets the unique identifier of the window in which this View resides.
*
* @return The window accessibility id.
*
@@ -26397,7 +26397,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
/**
* Returns the over-scroll mode for this view. The result will be
- * one of {@link #OVER_SCROLL_ALWAYS} (default), {@link #OVER_SCROLL_IF_CONTENT_SCROLLS}
+ * one of {@link #OVER_SCROLL_ALWAYS}, {@link #OVER_SCROLL_IF_CONTENT_SCROLLS}
* (allow over-scrolling only if the view content is larger than the container),
* or {@link #OVER_SCROLL_NEVER}.
*
@@ -26414,7 +26414,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
/**
* Set the over-scroll mode for this view. Valid over-scroll modes are
- * {@link #OVER_SCROLL_ALWAYS} (default), {@link #OVER_SCROLL_IF_CONTENT_SCROLLS}
+ * {@link #OVER_SCROLL_ALWAYS}, {@link #OVER_SCROLL_IF_CONTENT_SCROLLS}
* (allow over-scrolling only if the view content is larger than the container),
* or {@link #OVER_SCROLL_NEVER}.
*
diff --git a/core/java/android/view/ViewDebug.java b/core/java/android/view/ViewDebug.java
index 8a1fd62c0201..c67fca59fe97 100644
--- a/core/java/android/view/ViewDebug.java
+++ b/core/java/android/view/ViewDebug.java
@@ -870,6 +870,94 @@ public class ViewDebug {
return null;
}
+ private static class StreamingPictureCallbackHandler implements AutoCloseable,
+ HardwareRenderer.PictureCapturedCallback, Runnable {
+ private final HardwareRenderer mRenderer;
+ private final Callable<OutputStream> mCallback;
+ private final Executor mExecutor;
+ private final ReentrantLock mLock = new ReentrantLock(false);
+ private final ArrayDeque<byte[]> mQueue = new ArrayDeque<>(3);
+ private final ByteArrayOutputStream mByteStream = new ByteArrayOutputStream();
+ private boolean mStopListening;
+ private Thread mRenderThread;
+
+ private StreamingPictureCallbackHandler(HardwareRenderer renderer,
+ Callable<OutputStream> callback, Executor executor) {
+ mRenderer = renderer;
+ mCallback = callback;
+ mExecutor = executor;
+ mRenderer.setPictureCaptureCallback(this);
+ }
+
+ @Override
+ public void close() {
+ mLock.lock();
+ mStopListening = true;
+ mLock.unlock();
+ mRenderer.setPictureCaptureCallback(null);
+ }
+
+ @Override
+ public void onPictureCaptured(Picture picture) {
+ mLock.lock();
+ if (mStopListening) {
+ mLock.unlock();
+ mRenderer.setPictureCaptureCallback(null);
+ return;
+ }
+ if (mRenderThread == null) {
+ mRenderThread = Thread.currentThread();
+ }
+ boolean needsInvoke = true;
+ if (mQueue.size() == 3) {
+ mQueue.removeLast();
+ needsInvoke = false;
+ }
+ picture.writeToStream(mByteStream);
+ mQueue.add(mByteStream.toByteArray());
+ mByteStream.reset();
+ mLock.unlock();
+
+ if (needsInvoke) {
+ mExecutor.execute(this);
+ }
+ }
+
+ @Override
+ public void run() {
+ mLock.lock();
+ final byte[] picture = mQueue.poll();
+ final boolean isStopped = mStopListening;
+ mLock.unlock();
+ if (Thread.currentThread() == mRenderThread) {
+ close();
+ throw new IllegalStateException(
+ "ViewDebug#startRenderingCommandsCapture must be given an executor that "
+ + "invokes asynchronously");
+ }
+ if (isStopped) {
+ return;
+ }
+ OutputStream stream = null;
+ try {
+ stream = mCallback.call();
+ } catch (Exception ex) {
+ Log.w("ViewDebug", "Aborting rendering commands capture "
+ + "because callback threw exception", ex);
+ }
+ if (stream != null) {
+ try {
+ stream.write(picture);
+ } catch (IOException ex) {
+ Log.w("ViewDebug", "Aborting rendering commands capture "
+ + "due to IOException writing to output stream", ex);
+ }
+ } else {
+ close();
+ }
+ }
+ }
+
/**
* Begins capturing the entire rendering commands for the view tree referenced by the given
* view. The view passed may be any View in the tree as long as it is attached. That is,
@@ -915,18 +1003,7 @@ public class ViewDebug {
}
final HardwareRenderer renderer = attachInfo.mThreadedRenderer;
if (renderer != null) {
- return new PictureCallbackHandler(renderer, (picture -> {
- try {
- OutputStream stream = callback.call();
- if (stream != null) {
- picture.writeToStream(stream);
- return true;
- }
- } catch (Exception ex) {
- // fall through
- }
- return false;
- }), executor);
+ return new StreamingPictureCallbackHandler(renderer, callback, executor);
}
return null;
}
diff --git a/core/java/android/view/animation/AnimationUtils.java b/core/java/android/view/animation/AnimationUtils.java
index c877b9cec812..f5b074674454 100644
--- a/core/java/android/view/animation/AnimationUtils.java
+++ b/core/java/android/view/animation/AnimationUtils.java
@@ -127,7 +127,7 @@ public class AnimationUtils {
*
* @param context Application context used to access resources
* @param id The resource id of the animation to load
- * @return The animation object reference by the specified id
+ * @return The animation object referenced by the specified id
* @throws NotFoundException when the animation cannot be loaded
*/
public static Animation loadAnimation(Context context, @AnimRes int id)
@@ -208,7 +208,7 @@ public class AnimationUtils {
*
* @param context Application context used to access resources
* @param id The resource id of the animation to load
- * @return The animation object reference by the specified id
+ * @return The animation controller object referenced by the specified id
* @throws NotFoundException when the layout animation controller cannot be loaded
*/
public static LayoutAnimationController loadLayoutAnimation(Context context, @AnimRes int id)
@@ -331,7 +331,7 @@ public class AnimationUtils {
*
* @param context Application context used to access resources
* @param id The resource id of the animation to load
- * @return The animation object reference by the specified id
+ * @return The interpolator object referenced by the specified id
* @throws NotFoundException
*/
public static Interpolator loadInterpolator(Context context, @AnimRes @InterpolatorRes int id)
@@ -361,7 +361,7 @@ public class AnimationUtils {
*
* @param res The resources
* @param id The resource id of the animation to load
- * @return The interpolator object reference by the specified id
+ * @return The interpolator object referenced by the specified id
* @throws NotFoundException
* @hide
*/
diff --git a/core/java/android/view/inputmethod/InputMethodManager.java b/core/java/android/view/inputmethod/InputMethodManager.java
index 032af1c5c7b5..d3618adca6c4 100644
--- a/core/java/android/view/inputmethod/InputMethodManager.java
+++ b/core/java/android/view/inputmethod/InputMethodManager.java
@@ -92,7 +92,10 @@ import java.util.Comparator;
import java.util.List;
import java.util.Map;
import java.util.Objects;
+import java.util.concurrent.CancellationException;
+import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
/**
@@ -422,6 +425,13 @@ public final class InputMethodManager {
int mCursorCandEnd;
/**
+ * Initial startInput with {@link StartInputReason.WINDOW_FOCUS_GAIN} is executed
+ * in a background thread. Later, if there is an actual startInput it will wait on
+ * main thread till the background thread completes.
+ */
+ private CompletableFuture<Void> mWindowFocusGainFuture;
+
+ /**
* The instance that has previously been sent to the input method.
*/
private CursorAnchorInfo mCursorAnchorInfo = null;
@@ -645,14 +655,14 @@ public final class InputMethodManager {
} catch (RemoteException e) {
}
}
- // Check focus again in case that "onWindowFocus" is called before
- // handling this message.
- if (mServedView != null && canStartInput(mServedView)) {
- if (checkFocusNoStartInput(mRestartOnNextWindowFocus)) {
- final int reason = active ? StartInputReason.ACTIVATED_BY_IMMS
- : StartInputReason.DEACTIVATED_BY_IMMS;
- startInputInner(reason, null, 0, 0, 0);
- }
+ }
+ // Check focus again in case that "onWindowFocus" is called before
+ // handling this message.
+ if (mServedView != null && canStartInput(mServedView)) {
+ if (checkFocusNoStartInput(mRestartOnNextWindowFocus)) {
+ final int reason = active ? StartInputReason.ACTIVATED_BY_IMMS
+ : StartInputReason.DEACTIVATED_BY_IMMS;
+ startInputInner(reason, null, 0, 0, 0);
}
}
return;
@@ -1215,6 +1225,10 @@ public final class InputMethodManager {
*/
void clearBindingLocked() {
if (DEBUG) Log.v(TAG, "Clearing binding!");
+ if (mWindowFocusGainFuture != null) {
+ mWindowFocusGainFuture.cancel(false /* mayInterruptIfRunning */);
+ mWindowFocusGainFuture = null;
+ }
clearConnectionLocked();
setInputChannelLocked(null);
mBindSequence = -1;
@@ -1598,6 +1612,18 @@ public final class InputMethodManager {
boolean startInputInner(@StartInputReason int startInputReason,
@Nullable IBinder windowGainingFocus, @StartInputFlags int startInputFlags,
@SoftInputModeFlags int softInputMode, int windowFlags) {
+ if (startInputReason != StartInputReason.WINDOW_FOCUS_GAIN
+ && mWindowFocusGainFuture != null) {
+ try {
+ mWindowFocusGainFuture.get();
+ } catch (ExecutionException | InterruptedException e) {
+ // do nothing
+ } catch (CancellationException e) {
+ // window no longer has focus.
+ return true;
+ }
+ }
+
final View view;
synchronized (mH) {
view = mServedView;
@@ -1951,31 +1977,38 @@ public final class InputMethodManager {
startInputFlags |= StartInputFlags.FIRST_WINDOW_FOCUS_GAIN;
}
- if (checkFocusNoStartInput(forceNewFocus)) {
- // We need to restart input on the current focus view. This
- // should be done in conjunction with telling the system service
- // about the window gaining focus, to help make the transition
- // smooth.
- if (startInputInner(StartInputReason.WINDOW_FOCUS_GAIN, rootView.getWindowToken(),
- startInputFlags, softInputMode, windowFlags)) {
- return;
- }
+ final boolean forceNewFocus1 = forceNewFocus;
+ final int startInputFlags1 = startInputFlags;
+ if (mWindowFocusGainFuture != null) {
+ mWindowFocusGainFuture.cancel(false/* mayInterruptIfRunning */);
}
+ mWindowFocusGainFuture = CompletableFuture.runAsync(() -> {
+ if (checkFocusNoStartInput(forceNewFocus1)) {
+ // We need to restart input on the current focus view. This
+ // should be done in conjunction with telling the system service
+ // about the window gaining focus, to help make the transition
+ // smooth.
+ if (startInputInner(StartInputReason.WINDOW_FOCUS_GAIN, rootView.getWindowToken(),
+ startInputFlags1, softInputMode, windowFlags)) {
+ return;
+ }
+ }
- // For some reason we didn't do a startInput + windowFocusGain, so
- // we'll just do a window focus gain and call it a day.
- synchronized (mH) {
- try {
- if (DEBUG) Log.v(TAG, "Reporting focus gain, without startInput");
- mService.startInputOrWindowGainedFocus(
- StartInputReason.WINDOW_FOCUS_GAIN_REPORT_ONLY, mClient,
- rootView.getWindowToken(), startInputFlags, softInputMode, windowFlags,
- null, null, 0 /* missingMethodFlags */,
- rootView.getContext().getApplicationInfo().targetSdkVersion);
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
+ // For some reason we didn't do a startInput + windowFocusGain, so
+ // we'll just do a window focus gain and call it a day.
+ synchronized (mH) {
+ try {
+ if (DEBUG) Log.v(TAG, "Reporting focus gain, without startInput");
+ mService.startInputOrWindowGainedFocus(
+ StartInputReason.WINDOW_FOCUS_GAIN_REPORT_ONLY, mClient,
+ rootView.getWindowToken(), startInputFlags1, softInputMode, windowFlags,
+ null, null, 0 /* missingMethodFlags */,
+ rootView.getContext().getApplicationInfo().targetSdkVersion);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
}
- }
+ });
}
/** @hide */
@@ -1990,6 +2023,10 @@ public final class InputMethodManager {
// If the mCurRootView is losing window focus, release the strong reference to it
// so as not to prevent it from being garbage-collected.
mCurRootView = null;
+ if (mWindowFocusGainFuture != null) {
+ mWindowFocusGainFuture.cancel(false /* mayInterruptIfRunning */);
+ mWindowFocusGainFuture = null;
+ }
} else {
if (DEBUG) {
Log.v(TAG, "Ignoring onPreWindowFocus()."
diff --git a/core/java/android/webkit/CookieManager.java b/core/java/android/webkit/CookieManager.java
index 23d12374453f..3824c22a40a7 100644
--- a/core/java/android/webkit/CookieManager.java
+++ b/core/java/android/webkit/CookieManager.java
@@ -22,7 +22,10 @@ import android.net.WebAddress;
/**
* Manages the cookies used by an application's {@link WebView} instances.
- * Cookies are manipulated according to RFC2109.
+ * <p>
+ * CookieManager represents cookies as strings in the same format as the
+ * HTTP {@code Cookie} and {@code Set-Cookie} header fields (defined in
+ * <a href="https://tools.ietf.org/html/draft-ietf-httpbis-rfc6265bis-03">RFC6265bis</a>).
*/
public abstract class CookieManager {
/**
diff --git a/core/java/android/webkit/WebChromeClient.java b/core/java/android/webkit/WebChromeClient.java
index 4db630808ef1..f8522edb7dcd 100644
--- a/core/java/android/webkit/WebChromeClient.java
+++ b/core/java/android/webkit/WebChromeClient.java
@@ -71,11 +71,24 @@ public class WebChromeClient {
}
/**
- * Notify the host application that the current page has entered full
- * screen mode. The host application must show the custom View which
- * contains the web contents &mdash; video or other HTML content &mdash;
- * in full screen mode. Also see "Full screen support" documentation on
- * {@link WebView}.
+ * Notify the host application that the current page has entered full screen mode. After this
+ * call, web content will no longer be rendered in the WebView, but will instead be rendered
+ * in {@code view}. The host application should add this View to a Window which is configured
+ * with {@link android.view.WindowManager.LayoutParams#FLAG_FULLSCREEN} flag in order to
+ * actually display this web content full screen.
+ *
+ * <p>The application may explicitly exit fullscreen mode by invoking {@code callback} (ex. when
+ * the user presses the back button). However, this is generally not necessary as the web page
+ * will often show its own UI to close out of fullscreen. Regardless of how the WebView exits
+ * fullscreen mode, WebView will invoke {@link #onHideCustomView()}, signaling for the
+ * application to remove the custom View.
+ *
+ * <p>If this method is not overridden, WebView will report to the web page it does not support
+ * fullscreen mode and will not honor the web page's request to run in fullscreen mode.
+ *
+ * <p class="note"><b>Note:</b> if overriding this method, the application must also override
+ * {@link #onHideCustomView()}.
+ *
* @param view is the View object to be shown.
* @param callback invoke this callback to request the page to exit
* full screen mode.
@@ -98,10 +111,13 @@ public class WebChromeClient {
CustomViewCallback callback) {};
/**
- * Notify the host application that the current page has exited full
- * screen mode. The host application must hide the custom View, ie. the
- * View passed to {@link #onShowCustomView} when the content entered fullscreen.
- * Also see "Full screen support" documentation on {@link WebView}.
+ * Notify the host application that the current page has exited full screen mode. The host
+ * application must hide the custom View (the View which was previously passed to {@link
+ * #onShowCustomView(View, CustomViewCallback) onShowCustomView()}). After this call, web
+ * content will render in the original WebView again.
+ *
+ * <p class="note"><b>Note:</b> if overriding this method, the application must also override
+ * {@link #onShowCustomView(View, CustomViewCallback) onShowCustomView()}.
*/
public void onHideCustomView() {}
diff --git a/core/java/android/webkit/WebSettings.java b/core/java/android/webkit/WebSettings.java
index 7282008f7e3a..2895621f962a 100644
--- a/core/java/android/webkit/WebSettings.java
+++ b/core/java/android/webkit/WebSettings.java
@@ -328,6 +328,9 @@ public abstract class WebSettings {
* <p>
* The built-in mechanisms are the only currently supported zoom
* mechanisms, so it is recommended that this setting is always enabled.
+ * However, on-screen zoom controls are deprecated in Android (see
+ * {@link android.widget.ZoomButtonsController}) so it's recommended to
+ * disable {@link #setDisplayZoomControls}.
*
* @param enabled whether the WebView should use its built-in zoom mechanisms
*/
@@ -347,7 +350,9 @@ public abstract class WebSettings {
/**
* Sets whether the WebView should display on-screen zoom controls when
* using the built-in zoom mechanisms. See {@link #setBuiltInZoomControls}.
- * The default is {@code true}.
+ * The default is {@code true}. However, on-screen zoom controls are deprecated
+ * in Android (see {@link android.widget.ZoomButtonsController}) so it's
+ * recommended to set this to {@code false}.
*
* @param enabled whether the WebView should display on-screen zoom controls
*/
diff --git a/core/java/android/widget/ArrayAdapter.java b/core/java/android/widget/ArrayAdapter.java
index 2bf1ba5cf017..f7225d036e15 100644
--- a/core/java/android/widget/ArrayAdapter.java
+++ b/core/java/android/widget/ArrayAdapter.java
@@ -49,9 +49,6 @@ import java.util.List;
* To customize what type of view is used for the data object,
* override {@link #getView(int, View, ViewGroup)}
* and inflate a view resource.
- * For a code example, see
- * the <a href="https://github.com/googlesamples/android-CustomChoiceList/#readme">
- * CustomChoiceList</a> sample.
* </p>
* <p>
* For an example of using an array adapter with a ListView, see the
diff --git a/core/java/android/widget/ListView.java b/core/java/android/widget/ListView.java
index 2f44d6ee88b1..b732b7eeaa01 100644
--- a/core/java/android/widget/ListView.java
+++ b/core/java/android/widget/ListView.java
@@ -120,10 +120,6 @@ import java.util.function.Predicate;
* <a href="{@docRoot}training/improving-layouts/smooth-scrolling.html">
* Making ListView Scrolling Smooth</a> for more ways to ensure a smooth user experience.</p>
*
- * <p>For a more complete example of creating a custom adapter, see the
- * <a href="{@docRoot}samples/CustomChoiceList/index.html">
- * Custom Choice List</a> sample app.</p>
- *
* <p>To specify an action when a user clicks or taps on a single list item, see
* <a href="{@docRoot}guide/topics/ui/declaring-layout.html#HandlingUserSelections">
* Handling click events</a>.</p>
diff --git a/core/java/android/widget/NumberPicker.java b/core/java/android/widget/NumberPicker.java
index b385534f1850..4640c19bc16c 100644
--- a/core/java/android/widget/NumberPicker.java
+++ b/core/java/android/widget/NumberPicker.java
@@ -1268,12 +1268,12 @@ public class NumberPicker extends LinearLayout {
* current value is set to the {@link NumberPicker#getMaxValue()} value.
* </p>
* <p>
- * If the argument is less than the {@link NumberPicker#getMaxValue()} and
+ * If the argument is more than the {@link NumberPicker#getMaxValue()} and
* {@link NumberPicker#getWrapSelectorWheel()} is <code>false</code> the
* current value is set to the {@link NumberPicker#getMaxValue()} value.
* </p>
* <p>
- * If the argument is less than the {@link NumberPicker#getMaxValue()} and
+ * If the argument is more than the {@link NumberPicker#getMaxValue()} and
* {@link NumberPicker#getWrapSelectorWheel()} is <code>true</code> the
* current value is set to the {@link NumberPicker#getMinValue()} value.
* </p>
diff --git a/core/java/android/widget/RelativeLayout.java b/core/java/android/widget/RelativeLayout.java
index b9da3071d895..facb7d09f250 100644
--- a/core/java/android/widget/RelativeLayout.java
+++ b/core/java/android/widget/RelativeLayout.java
@@ -72,7 +72,7 @@ import java.util.TreeSet;
*
* <p>This behavior has been preserved for apps that set <code>android:targetSdkVersion="17"</code>
* or older in their manifest's <code>uses-sdk</code> tag for compatibility. Apps targeting SDK
- * version 18 or newer will receive the correct behavior</p>
+ * version 18 or newer will receive the correct behavior.</p>
*
* <p>See the <a href="{@docRoot}guide/topics/ui/layout/relative.html">Relative
* Layout</a> guide.</p>
diff --git a/core/java/com/android/internal/app/AbstractResolverComparator.java b/core/java/com/android/internal/app/AbstractResolverComparator.java
index 9ac979b54716..3a6a71d8eab6 100644
--- a/core/java/com/android/internal/app/AbstractResolverComparator.java
+++ b/core/java/com/android/internal/app/AbstractResolverComparator.java
@@ -30,6 +30,7 @@ import android.util.Log;
import com.android.internal.app.ResolverActivity.ResolvedComponentInfo;
+import java.text.Collator;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
@@ -37,7 +38,7 @@ import java.util.List;
/**
* Used to sort resolved activities in {@link ResolverListController}.
*/
-abstract class AbstractResolverComparator implements Comparator<ResolvedComponentInfo> {
+public abstract class AbstractResolverComparator implements Comparator<ResolvedComponentInfo> {
private static final int NUM_OF_TOP_ANNOTATIONS_TO_USE = 3;
private static final boolean DEBUG = false;
@@ -62,6 +63,8 @@ abstract class AbstractResolverComparator implements Comparator<ResolvedComponen
// predicting ranking scores.
private static final int WATCHDOG_TIMEOUT_MILLIS = 500;
+ private final Comparator<ResolveInfo> mAzComparator;
+
protected final Handler mHandler = new Handler(Looper.getMainLooper()) {
public void handleMessage(Message msg) {
switch (msg.what) {
@@ -90,7 +93,7 @@ abstract class AbstractResolverComparator implements Comparator<ResolvedComponen
}
};
- AbstractResolverComparator(Context context, Intent intent) {
+ public AbstractResolverComparator(Context context, Intent intent) {
String scheme = intent.getScheme();
mHttp = "http".equals(scheme) || "https".equals(scheme);
mContentType = intent.getType();
@@ -100,6 +103,7 @@ abstract class AbstractResolverComparator implements Comparator<ResolvedComponen
mDefaultBrowserPackageName = mHttp
? mPm.getDefaultBrowserPackageNameAsUser(UserHandle.myUserId())
: null;
+ mAzComparator = new AzInfoComparator(context);
}
// get annotations of content from intent.
@@ -168,6 +172,20 @@ abstract class AbstractResolverComparator implements Comparator<ResolvedComponen
return lhsSpecific ? -1 : 1;
}
}
+
+ final boolean lPinned = lhsp.isPinned();
+ final boolean rPinned = rhsp.isPinned();
+
+ // Pinned items always receive priority.
+ if (lPinned && !rPinned) {
+ return -1;
+ } else if (!lPinned && rPinned) {
+ return 1;
+ } else if (lPinned && rPinned) {
+ // If both items are pinned, resolve the tie alphabetically.
+ return mAzComparator.compare(lhsp.getResolveInfoAt(0), rhsp.getResolveInfoAt(0));
+ }
+
return compare(lhs, rhs);
}
@@ -258,4 +276,25 @@ abstract class AbstractResolverComparator implements Comparator<ResolvedComponen
}
return false;
}
+
+ /**
+ * Sort intents alphabetically based on package name.
+ */
+ class AzInfoComparator implements Comparator<ResolveInfo> {
+ Collator mCollator;
+ AzInfoComparator(Context context) {
+ mCollator = Collator.getInstance(context.getResources().getConfiguration().locale);
+ }
+
+ @Override
+ public int compare(ResolveInfo lhsp, ResolveInfo rhsp) {
+ if (lhsp == null) {
+ return -1;
+ } else if (rhsp == null) {
+ return 1;
+ }
+ return mCollator.compare(lhsp.activityInfo.packageName, rhsp.activityInfo.packageName);
+ }
+ }
+
}
diff --git a/core/java/com/android/internal/app/ChooserActivity.java b/core/java/com/android/internal/app/ChooserActivity.java
index 102ba5c2ec5e..dfb266827903 100644
--- a/core/java/com/android/internal/app/ChooserActivity.java
+++ b/core/java/com/android/internal/app/ChooserActivity.java
@@ -44,6 +44,7 @@ import android.content.IntentFilter;
import android.content.IntentSender;
import android.content.IntentSender.SendIntentException;
import android.content.ServiceConnection;
+import android.content.SharedPreferences;
import android.content.pm.ActivityInfo;
import android.content.pm.ApplicationInfo;
import android.content.pm.LabeledIntent;
@@ -69,6 +70,7 @@ import android.metrics.LogMaker;
import android.net.Uri;
import android.os.AsyncTask;
import android.os.Bundle;
+import android.os.Environment;
import android.os.Handler;
import android.os.IBinder;
import android.os.Message;
@@ -78,10 +80,12 @@ import android.os.RemoteException;
import android.os.ResultReceiver;
import android.os.UserHandle;
import android.os.UserManager;
+import android.os.storage.StorageManager;
import android.provider.DeviceConfig;
import android.provider.DocumentsContract;
import android.provider.Downloads;
import android.provider.OpenableColumns;
+import android.provider.Settings;
import android.service.chooser.ChooserTarget;
import android.service.chooser.ChooserTargetService;
import android.service.chooser.IChooserTargetResult;
@@ -104,6 +108,7 @@ import android.view.animation.AccelerateInterpolator;
import android.view.animation.DecelerateInterpolator;
import android.widget.AbsListView;
import android.widget.BaseAdapter;
+import android.widget.Button;
import android.widget.ImageView;
import android.widget.ListView;
import android.widget.TextView;
@@ -120,9 +125,11 @@ import com.android.internal.widget.ResolverDrawerLayout;
import com.google.android.collect.Lists;
+import java.io.File;
import java.io.IOException;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
+import java.net.URISyntaxException;
import java.text.Collator;
import java.util.ArrayList;
import java.util.Arrays;
@@ -156,6 +163,9 @@ public class ChooserActivity extends ResolverActivity {
private static final String PREF_NUM_SHEET_EXPANSIONS = "pref_num_sheet_expansions";
+ private static final String CHIP_LABEL_METADATA_KEY = "android.service.chooser.chip_label";
+ private static final String CHIP_ICON_METADATA_KEY = "android.service.chooser.chip_icon";
+
private static final boolean DEBUG = false;
/**
@@ -256,6 +266,9 @@ public class ChooserActivity extends ResolverActivity {
private static final int MAX_EXTRA_INITIAL_INTENTS = 2;
private static final int MAX_EXTRA_CHOOSER_TARGETS = 2;
+ private SharedPreferences mPinnedSharedPrefs;
+ private static final String PINNED_SHARED_PREFS_NAME = "chooser_pin_settings";
+
private boolean mListViewDataChanged = false;
@Retention(SOURCE)
@@ -519,6 +532,15 @@ public class ChooserActivity extends ResolverActivity {
mIsSuccessfullySelected = false;
Intent intent = getIntent();
Parcelable targetParcelable = intent.getParcelableExtra(Intent.EXTRA_INTENT);
+ if (targetParcelable instanceof Uri) {
+ try {
+ targetParcelable = Intent.parseUri(targetParcelable.toString(),
+ Intent.URI_INTENT_SCHEME);
+ } catch (URISyntaxException ex) {
+ // doesn't parse as an intent; let the next test fail and error out
+ }
+ }
+
if (!(targetParcelable instanceof Intent)) {
Log.w("ChooserActivity", "Target is not an intent: " + targetParcelable);
finish();
@@ -600,6 +622,8 @@ public class ChooserActivity extends ResolverActivity {
Intent.EXTRA_CHOOSER_REFINEMENT_INTENT_SENDER);
setSafeForwardingMode(true);
+ mPinnedSharedPrefs = getPinnedSharedPrefs(this);
+
pa = intent.getParcelableArrayExtra(Intent.EXTRA_EXCLUDE_COMPONENTS);
if (pa != null) {
ComponentName[] names = new ComponentName[pa.length];
@@ -731,6 +755,23 @@ public class ChooserActivity extends ResolverActivity {
}
}
+
+ static SharedPreferences getPinnedSharedPrefs(Context context) {
+ // The code below is because in the android:ui process, no one can hear you scream.
+ // The package info in the context isn't initialized in the way it is for normal apps,
+ // so the standard, name-based context.getSharedPreferences doesn't work. Instead, we
+ // build the path manually below using the same policy that appears in ContextImpl.
+ // This fails silently under the hood if there's a problem, so if we find ourselves in
+ // the case where we don't have access to credential encrypted storage we just won't
+ // have our pinned target info.
+ final File prefsFile = new File(new File(
+ Environment.getDataUserCePackageDirectory(StorageManager.UUID_PRIVATE_INTERNAL,
+ context.getUserId(), context.getPackageName()),
+ "shared_prefs"),
+ PINNED_SHARED_PREFS_NAME + ".xml");
+ return context.getSharedPreferences(prefsFile, MODE_PRIVATE);
+ }
+
/**
* Returns true if app prediction service is defined and the component exists on device.
*/
@@ -868,6 +909,85 @@ public class ChooserActivity extends ResolverActivity {
}
}
+ private ComponentName getNearbySharingComponent() {
+ String nearbyComponent = Settings.Secure.getString(
+ getContentResolver(),
+ Settings.Secure.NEARBY_SHARING_COMPONENT);
+ if (TextUtils.isEmpty(nearbyComponent)) {
+ nearbyComponent = getString(R.string.config_defaultNearbySharingComponent);
+ }
+ if (TextUtils.isEmpty(nearbyComponent)) {
+ return null;
+ }
+ return ComponentName.unflattenFromString(nearbyComponent);
+ }
+
+ private TargetInfo getNearbySharingTarget(Intent originalIntent) {
+ final ComponentName cn = getNearbySharingComponent();
+ if (cn == null) return null;
+
+ final Intent resolveIntent = new Intent();
+ resolveIntent.setComponent(cn);
+ final ResolveInfo ri = getPackageManager().resolveActivity(resolveIntent, 0);
+ if (ri == null) {
+ Log.e(TAG, "Device-specified nearby sharing component (" + cn
+ + ") not available");
+ return null;
+ }
+
+ // TODO(b/144290152): CHIP_LABEL_METADATA_KEY / CHIP_ICON_METADATA_KEY
+
+ CharSequence name = ri.loadLabel(getPackageManager());
+
+ final DisplayResolveInfo dri = new DisplayResolveInfo(
+ originalIntent, ri, name, "", null);
+ dri.setDisplayIcon(ri.loadIcon(getPackageManager()));
+ return dri;
+ }
+
+ private Button createActionButton(Drawable icon, CharSequence title, View.OnClickListener r) {
+ Button b = (Button) LayoutInflater.from(this).inflate(R.layout.chooser_action_button, null);
+ if (icon != null) {
+ final int size = getResources()
+ .getDimensionPixelSize(R.dimen.chooser_action_button_icon_size);
+ icon.setBounds(0, 0, size, size);
+ b.setCompoundDrawablesRelative(icon, null, null, null);
+ }
+ b.setText(title);
+ b.setOnClickListener(r);
+ return b;
+ }
+
+ private Button createCopyButton() {
+ final Button b = createActionButton(
+ getDrawable(R.drawable.ic_menu_copy_material),
+ getString(R.string.copy), this::onCopyButtonClicked);
+ b.setId(R.id.chooser_copy_button);
+ return b;
+ }
+
+ private @Nullable Button createNearbyButton(Intent originalIntent) {
+ final TargetInfo ti = getNearbySharingTarget(originalIntent);
+ if (ti == null) return null;
+
+ return createActionButton(
+ ti.getDisplayIcon(),
+ ti.getDisplayLabel(),
+ (View unused) -> safelyStartActivity(ti)
+ );
+ }
+
+ private void addActionButton(ViewGroup parent, Button b) {
+ if (b == null) return;
+ final ViewGroup.MarginLayoutParams lp = new ViewGroup.MarginLayoutParams(
+ LayoutParams.WRAP_CONTENT,
+ LayoutParams.WRAP_CONTENT
+ );
+ final int gap = getResources().getDimensionPixelSize(R.dimen.resolver_icon_margin) / 2;
+ lp.setMarginsRelative(gap, 0, gap, 0);
+ parent.addView(b, lp);
+ }
+
private ViewGroup displayContentPreview(@ContentPreviewType int previewType,
Intent targetIntent, LayoutInflater layoutInflater, ViewGroup convertView,
ViewGroup parent) {
@@ -901,8 +1021,10 @@ public class ChooserActivity extends ResolverActivity {
ViewGroup contentPreviewLayout = (ViewGroup) layoutInflater.inflate(
R.layout.chooser_grid_preview_text, parent, false);
- contentPreviewLayout.findViewById(R.id.copy_button).setOnClickListener(
- this::onCopyButtonClicked);
+ final ViewGroup actionRow =
+ (ViewGroup) contentPreviewLayout.findViewById(R.id.chooser_action_row);
+ addActionButton(actionRow, createCopyButton());
+ addActionButton(actionRow, createNearbyButton(targetIntent));
CharSequence sharingText = targetIntent.getCharSequenceExtra(Intent.EXTRA_TEXT);
if (sharingText == null) {
@@ -1060,7 +1182,8 @@ public class ChooserActivity extends ResolverActivity {
// TODO(b/120417119): Disable file copy until after moving to sysui,
// due to permissions issues
- contentPreviewLayout.findViewById(R.id.file_copy_button).setVisibility(View.GONE);
+ //((ViewGroup) contentPreviewLayout.findViewById(R.id.chooser_action_row))
+ // .addView(createCopyButton());
String action = targetIntent.getAction();
if (Intent.ACTION_SEND.equals(action)) {
@@ -1277,9 +1400,10 @@ public class ChooserActivity extends ResolverActivity {
}
ComponentName name = ri.activityInfo.getComponentName();
+ boolean pinned = mPinnedSharedPrefs.getBoolean(name.flattenToString(), false);
ResolverTargetActionsDialogFragment f =
new ResolverTargetActionsDialogFragment(ri.loadLabel(getPackageManager()),
- name);
+ name, pinned);
f.show(getFragmentManager(), TARGET_DETAILS_FRAGMENT_TAG);
}
@@ -1511,7 +1635,7 @@ public class ChooserActivity extends ResolverActivity {
}
return new IntentFilter(intent.getAction(), dataString);
} catch (Exception e) {
- Log.e(TAG, "failed to get target intent filter " + e);
+ Log.e(TAG, "failed to get target intent filter", e);
return null;
}
}
@@ -1956,6 +2080,12 @@ public class ChooserActivity extends ResolverActivity {
}
return false;
}
+
+ @Override
+ public boolean isComponentPinned(ComponentName name) {
+ return mPinnedSharedPrefs.getBoolean(name.flattenToString(), false);
+ }
+
}
@Override
@@ -2089,6 +2219,10 @@ public class ChooserActivity extends ResolverActivity {
public boolean isSuspended() {
return false;
}
+
+ public boolean isPinned() {
+ return false;
+ }
}
final class PlaceHolderTargetInfo extends NotSelectableTargetInfo {
@@ -2177,6 +2311,10 @@ public class ChooserActivity extends ResolverActivity {
return mIsSuspended;
}
+ public boolean isPinned() {
+ return mSourceInfo != null && mSourceInfo.isPinned();
+ }
+
/**
* Since ShortcutInfos are returned by ShortcutManager, we can cache the shortcuts and skip
* the call to LauncherApps#getShortcuts(ShortcutQuery).
diff --git a/core/java/com/android/internal/app/ResolverActivity.java b/core/java/com/android/internal/app/ResolverActivity.java
index 7d19eb6f54cd..77f37e31a087 100644
--- a/core/java/com/android/internal/app/ResolverActivity.java
+++ b/core/java/com/android/internal/app/ResolverActivity.java
@@ -392,21 +392,24 @@ public class ResolverActivity extends Activity {
mResolverDrawerLayout.setPadding(mSystemWindowInsets.left, mSystemWindowInsets.top,
mSystemWindowInsets.right, 0);
- View emptyView = findViewById(R.id.empty);
- if (emptyView != null) {
- emptyView.setPadding(0, 0, 0, mSystemWindowInsets.bottom
- + getResources().getDimensionPixelSize(
- R.dimen.chooser_edge_margin_normal) * 2);
- }
-
- if (mFooterSpacer == null) {
- mFooterSpacer = new Space(getApplicationContext());
+ // Need extra padding so the list can fully scroll up
+ if (useLayoutWithDefault()) {
+ if (mFooterSpacer == null) {
+ mFooterSpacer = new Space(getApplicationContext());
+ } else {
+ ((ListView) mAdapterView).removeFooterView(mFooterSpacer);
+ }
+ mFooterSpacer.setLayoutParams(new AbsListView.LayoutParams(LayoutParams.MATCH_PARENT,
+ mSystemWindowInsets.bottom));
+ ((ListView) mAdapterView).addFooterView(mFooterSpacer);
} else {
- ((ListView) mAdapterView).removeFooterView(mFooterSpacer);
+ View emptyView = findViewById(R.id.empty);
+ if (emptyView != null) {
+ emptyView.setPadding(0, 0, 0, mSystemWindowInsets.bottom
+ + getResources().getDimensionPixelSize(
+ R.dimen.chooser_edge_margin_normal) * 2);
+ }
}
- mFooterSpacer.setLayoutParams(new AbsListView.LayoutParams(LayoutParams.MATCH_PARENT,
- mSystemWindowInsets.bottom));
- ((ListView) mAdapterView).addFooterView(mFooterSpacer);
resetButtonBar();
@@ -565,7 +568,7 @@ public class ResolverActivity extends Activity {
intent.getData().getHost(),
mAdapter.getFilteredItem().getDisplayLabel());
} else if (mAdapter.areAllTargetsBrowsers()) {
- dialogTitle = getString(ActionTitle.BROWSABLE_TITLE_RES);
+ dialogTitle = getString(ActionTitle.BROWSABLE_TITLE_RES);
} else {
dialogTitle = getString(ActionTitle.BROWSABLE_HOST_TITLE_RES,
intent.getData().getHost());
@@ -1308,6 +1311,7 @@ public class ResolverActivity extends Activity {
// In case this method is called again (due to activity recreation), avoid adding a new
// header if one is already present.
if (useHeader && listView != null && listView.getHeaderViewsCount() == 0) {
+ listView.setHeaderDividersEnabled(true);
listView.addHeaderView(LayoutInflater.from(this).inflate(
R.layout.resolver_different_item_header, listView, false));
}
@@ -1350,11 +1354,13 @@ public class ResolverActivity extends Activity {
final ViewGroup buttonLayout = findViewById(R.id.button_bar);
if (buttonLayout != null) {
buttonLayout.setVisibility(View.VISIBLE);
- int inset = mSystemWindowInsets != null ? mSystemWindowInsets.bottom : 0;
- buttonLayout.setPadding(buttonLayout.getPaddingLeft(), buttonLayout.getPaddingTop(),
- buttonLayout.getPaddingRight(), getResources().getDimensionPixelSize(
- R.dimen.resolver_button_bar_spacing) + inset);
+ if (!useLayoutWithDefault()) {
+ int inset = mSystemWindowInsets != null ? mSystemWindowInsets.bottom : 0;
+ buttonLayout.setPadding(buttonLayout.getPaddingLeft(), buttonLayout.getPaddingTop(),
+ buttonLayout.getPaddingRight(), getResources().getDimensionPixelSize(
+ R.dimen.resolver_button_bar_spacing) + inset);
+ }
mOnceButton = (Button) buttonLayout.findViewById(R.id.button_once);
mAlwaysButton = (Button) buttonLayout.findViewById(R.id.button_always);
@@ -1411,6 +1417,7 @@ public class ResolverActivity extends Activity {
private final Intent mResolvedIntent;
private final List<Intent> mSourceIntents = new ArrayList<>();
private boolean mIsSuspended;
+ private boolean mPinned = false;
public DisplayResolveInfo(Intent originalIntent, ResolveInfo pri, CharSequence pLabel,
CharSequence pInfo, Intent pOrigIntent) {
@@ -1514,6 +1521,15 @@ public class ResolverActivity extends Activity {
public boolean isSuspended() {
return mIsSuspended;
}
+
+ @Override
+ public boolean isPinned() {
+ return mPinned;
+ }
+
+ public void setPinned(boolean pinned) {
+ mPinned = pinned;
+ }
}
List<DisplayResolveInfo> getDisplayList() {
@@ -1614,6 +1630,11 @@ public class ResolverActivity extends Activity {
* @return true if this target can be selected by the user
*/
boolean isSuspended();
+
+ /**
+ * @return true if this target should be pinned to the front by the request of the user
+ */
+ boolean isPinned();
}
public class ResolveListAdapter extends BaseAdapter {
@@ -1920,6 +1941,10 @@ public class ResolverActivity extends Activity {
final Intent replaceIntent = getReplacementIntent(add.activityInfo, intent);
final DisplayResolveInfo dri = new DisplayResolveInfo(intent, add, roLabel,
extraInfo, replaceIntent);
+ dri.setPinned(rci.isPinned());
+ if (rci.isPinned()) {
+ Log.i(TAG, "Pinned item: " + rci.name);
+ }
addResolveInfo(dri);
if (replaceIntent == intent) {
// Only add alternates if we didn't get a specific replacement from
@@ -2061,7 +2086,9 @@ public class ResolverActivity extends Activity {
CharSequence subLabel = info.getExtendedInfo();
if (TextUtils.equals(label, subLabel)) subLabel = null;
- if (!TextUtils.equals(holder.text2.getText(), subLabel)) {
+ if (!TextUtils.equals(holder.text2.getText(), subLabel)
+ && !TextUtils.isEmpty(subLabel)) {
+ holder.text2.setVisibility(View.VISIBLE);
holder.text2.setText(subLabel);
}
@@ -2086,6 +2113,7 @@ public class ResolverActivity extends Activity {
public final ComponentName name;
private final List<Intent> mIntents = new ArrayList<>();
private final List<ResolveInfo> mResolveInfos = new ArrayList<>();
+ private boolean mPinned;
public ResolvedComponentInfo(ComponentName name, Intent intent, ResolveInfo info) {
this.name = name;
@@ -2126,6 +2154,15 @@ public class ResolverActivity extends Activity {
}
return -1;
}
+
+ public boolean isPinned() {
+ return mPinned;
+ }
+
+ public void setPinned(boolean pinned) {
+ mPinned = pinned;
+ }
+
}
static class ViewHolder {
diff --git a/core/java/com/android/internal/app/ResolverListController.java b/core/java/com/android/internal/app/ResolverListController.java
index 5f92cddbaa38..7efd5e16392c 100644
--- a/core/java/com/android/internal/app/ResolverListController.java
+++ b/core/java/com/android/internal/app/ResolverListController.java
@@ -156,11 +156,22 @@ public class ResolverListController {
newInfo.activityInfo.packageName, newInfo.activityInfo.name);
final ResolverActivity.ResolvedComponentInfo rci =
new ResolverActivity.ResolvedComponentInfo(name, intent, newInfo);
+ rci.setPinned(isComponentPinned(name));
into.add(rci);
}
}
}
+
+ /**
+ * Whether this component is pinned by the user. Always false for resolver; overridden in
+ * Chooser.
+ */
+ public boolean isComponentPinned(ComponentName name) {
+ return false;
+ }
+
+
// Filter out any activities that the launched uid does not have permission for.
// To preserve the inputList, optionally will return the original list if any modification has
// been made.
diff --git a/core/java/com/android/internal/app/ResolverTargetActionsDialogFragment.java b/core/java/com/android/internal/app/ResolverTargetActionsDialogFragment.java
index a49240cd0019..df91c4a1f88d 100644
--- a/core/java/com/android/internal/app/ResolverTargetActionsDialogFragment.java
+++ b/core/java/com/android/internal/app/ResolverTargetActionsDialogFragment.java
@@ -23,6 +23,7 @@ import android.app.DialogFragment;
import android.content.ComponentName;
import android.content.DialogInterface;
import android.content.Intent;
+import android.content.SharedPreferences;
import android.net.Uri;
import android.os.Bundle;
import android.provider.Settings;
@@ -36,26 +37,33 @@ public class ResolverTargetActionsDialogFragment extends DialogFragment
implements DialogInterface.OnClickListener {
private static final String NAME_KEY = "componentName";
private static final String TITLE_KEY = "title";
+ private static final String PINNED_KEY = "pinned";
// Sync with R.array.resolver_target_actions_* resources
- private static final int APP_INFO_INDEX = 0;
+ private static final int TOGGLE_PIN_INDEX = 0;
+ private static final int APP_INFO_INDEX = 1;
public ResolverTargetActionsDialogFragment() {
}
- public ResolverTargetActionsDialogFragment(CharSequence title, ComponentName name) {
+ public ResolverTargetActionsDialogFragment(CharSequence title, ComponentName name,
+ boolean pinned) {
Bundle args = new Bundle();
args.putCharSequence(TITLE_KEY, title);
args.putParcelable(NAME_KEY, name);
+ args.putBoolean(PINNED_KEY, pinned);
setArguments(args);
}
@Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
final Bundle args = getArguments();
+ final int itemRes = args.getBoolean(PINNED_KEY, false)
+ ? R.array.resolver_target_actions_unpin
+ : R.array.resolver_target_actions_pin;
return new Builder(getContext())
.setCancelable(true)
- .setItems(R.array.resolver_target_actions, this)
+ .setItems(itemRes, this)
.setTitle(args.getCharSequence(TITLE_KEY))
.create();
}
@@ -65,6 +73,19 @@ public class ResolverTargetActionsDialogFragment extends DialogFragment
final Bundle args = getArguments();
ComponentName name = args.getParcelable(NAME_KEY);
switch (which) {
+ case TOGGLE_PIN_INDEX:
+ SharedPreferences sp = ChooserActivity.getPinnedSharedPrefs(getContext());
+ final String key = name.flattenToString();
+ boolean currentVal = sp.getBoolean(name.flattenToString(), false);
+ if (currentVal) {
+ sp.edit().remove(key).apply();
+ } else {
+ sp.edit().putBoolean(key, true).apply();
+ }
+
+ // Force the chooser to requery and resort things
+ getActivity().recreate();
+ break;
case APP_INFO_INDEX:
Intent in = new Intent().setAction(Settings.ACTION_APPLICATION_DETAILS_SETTINGS)
.setData(Uri.fromParts("package", name.getPackageName(), null))
diff --git a/core/java/com/android/internal/car/ICarStatsService.aidl b/core/java/com/android/internal/car/ICarStatsService.aidl
new file mode 100644
index 000000000000..170b448ba33f
--- /dev/null
+++ b/core/java/com/android/internal/car/ICarStatsService.aidl
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.car;
+
+import android.os.StatsLogEventWrapper;
+
+/**
+ * Interface for pulling statsd atoms from automotive devices.
+ *
+ * @hide
+ */
+interface ICarStatsService {
+ /**
+ * Pull the specified atom. Results will be sent to statsd when complete.
+ */
+ StatsLogEventWrapper[] pullData(int atomId);
+}
diff --git a/core/java/com/android/internal/colorextraction/ColorExtractor.java b/core/java/com/android/internal/colorextraction/ColorExtractor.java
index 3003ce80cbc1..f0da0d5decd7 100644
--- a/core/java/com/android/internal/colorextraction/ColorExtractor.java
+++ b/core/java/com/android/internal/colorextraction/ColorExtractor.java
@@ -73,8 +73,10 @@ public class ColorExtractor implements WallpaperManager.OnColorsChangedListener
}
mOnColorsChangedListeners = new ArrayList<>();
- wallpaperManager.addOnColorsChangedListener(this, null /* handler */);
- initExtractColors(wallpaperManager, immediately);
+ if (wallpaperManager.isWallpaperSupported()) {
+ wallpaperManager.addOnColorsChangedListener(this, null /* handler */);
+ initExtractColors(wallpaperManager, immediately);
+ }
}
private void initExtractColors(WallpaperManager wallpaperManager, boolean immediately) {
diff --git a/core/java/com/android/internal/config/sysui/SystemUiDeviceConfigFlags.java b/core/java/com/android/internal/config/sysui/SystemUiDeviceConfigFlags.java
index 33b532537686..334e61a24193 100644
--- a/core/java/com/android/internal/config/sysui/SystemUiDeviceConfigFlags.java
+++ b/core/java/com/android/internal/config/sysui/SystemUiDeviceConfigFlags.java
@@ -47,6 +47,20 @@ public final class SystemUiDeviceConfigFlags {
*/
public static final String NAS_MAX_SUGGESTIONS = "nas_max_suggestions";
+ // Flags related to screenshot intelligence
+
+ /**
+ * (bool) Whether to enable smart actions in screenshot notifications.
+ */
+ public static final String ENABLE_SCREENSHOT_NOTIFICATION_SMART_ACTIONS =
+ "enable_screenshot_notification_smart_actions";
+
+ /**
+ * (int) Timeout value in ms to get smart actions for screenshot notification.
+ */
+ public static final String SCREENSHOT_NOTIFICATION_SMART_ACTIONS_TIMEOUT_MS =
+ "screenshot_notification_smart_actions_timeout_ms";
+
// Flags related to Smart Suggestions - these are read in SmartReplyConstants.
/** (boolean) Whether to enable smart suggestions in notifications. */
diff --git a/core/java/com/android/internal/util/ScreenshotHelper.java b/core/java/com/android/internal/util/ScreenshotHelper.java
index cac691cf7d45..d24d78c6f3da 100644
--- a/core/java/com/android/internal/util/ScreenshotHelper.java
+++ b/core/java/com/android/internal/util/ScreenshotHelper.java
@@ -6,6 +6,7 @@ import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
+import android.net.Uri;
import android.os.Handler;
import android.os.IBinder;
import android.os.Message;
@@ -58,7 +59,7 @@ public class ScreenshotHelper {
*/
public void takeScreenshot(final int screenshotType, final boolean hasStatus,
final boolean hasNav, @NonNull Handler handler,
- @Nullable Consumer<Boolean> completionConsumer) {
+ @Nullable Consumer<Uri> completionConsumer) {
takeScreenshot(screenshotType, hasStatus, hasNav, SCREENSHOT_TIMEOUT_MS, handler,
completionConsumer);
}
@@ -83,12 +84,12 @@ public class ScreenshotHelper {
* the screenshot attempt will be cancelled and `completionConsumer`
* will be run.
* @param handler A handler used in case the screenshot times out
- * @param completionConsumer Consumes `false` if a screenshot was not taken, and `true` if the
- * screenshot was taken.
+ * @param completionConsumer Consumes `null` if a screenshot was not taken, and the URI of the
+ * screenshot if the screenshot was taken.
*/
public void takeScreenshot(final int screenshotType, final boolean hasStatus,
final boolean hasNav, long timeoutMs, @NonNull Handler handler,
- @Nullable Consumer<Boolean> completionConsumer) {
+ @Nullable Consumer<Uri> completionConsumer) {
synchronized (mScreenshotLock) {
if (mScreenshotConnection != null) {
return;
@@ -108,7 +109,7 @@ public class ScreenshotHelper {
}
}
if (completionConsumer != null) {
- completionConsumer.accept(false);
+ completionConsumer.accept(null);
}
}
};
@@ -134,8 +135,9 @@ public class ScreenshotHelper {
handler.removeCallbacks(mScreenshotTimeout);
}
}
+
if (completionConsumer != null) {
- completionConsumer.accept(true);
+ completionConsumer.accept((Uri) msg.obj);
}
}
};
@@ -148,7 +150,7 @@ public class ScreenshotHelper {
} catch (RemoteException e) {
Log.e(TAG, "Couldn't take screenshot: " + e);
if (completionConsumer != null) {
- completionConsumer.accept(false);
+ completionConsumer.accept(null);
}
}
}
diff --git a/core/jni/android/graphics/ByteBufferStreamAdaptor.cpp b/core/jni/android/graphics/ByteBufferStreamAdaptor.cpp
index 173818b13837..d443fd8cdf14 100644
--- a/core/jni/android/graphics/ByteBufferStreamAdaptor.cpp
+++ b/core/jni/android/graphics/ByteBufferStreamAdaptor.cpp
@@ -1,6 +1,7 @@
#include "ByteBufferStreamAdaptor.h"
#include "core_jni_helpers.h"
#include "Utils.h"
+#include <jni.h>
#include <SkStream.h>
@@ -9,6 +10,24 @@ using namespace android;
static jmethodID gByteBuffer_getMethodID;
static jmethodID gByteBuffer_setPositionMethodID;
+/**
+ * Helper method for accessing the JNI interface pointer.
+ *
+ * Image decoding (which this supports) is started on a thread that is already
+ * attached to the Java VM. But an AnimatedImageDrawable continues decoding on
+ * the AnimatedImageThread, which is not attached. This will attach if
+ * necessary.
+ */
+static JNIEnv* requireEnv(JavaVM* jvm) {
+ JNIEnv* env;
+ if (jvm->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION_1_6) != JNI_OK) {
+ if (jvm->AttachCurrentThreadAsDaemon(&env, nullptr) != JNI_OK) {
+ LOG_ALWAYS_FATAL("Failed to AttachCurrentThread!");
+ }
+ }
+ return env;
+}
+
class ByteBufferStream : public SkStreamAsset {
private:
ByteBufferStream(JavaVM* jvm, jobject jbyteBuffer, size_t initialPosition, size_t length,
@@ -46,7 +65,7 @@ public:
}
~ByteBufferStream() override {
- auto* env = get_env_or_die(mJvm);
+ auto* env = requireEnv(mJvm);
env->DeleteGlobalRef(mByteBuffer);
env->DeleteGlobalRef(mStorage);
}
@@ -63,7 +82,7 @@ public:
return this->setPosition(mPosition + size) ? size : 0;
}
- auto* env = get_env_or_die(mJvm);
+ auto* env = requireEnv(mJvm);
size_t bytesRead = 0;
do {
const size_t requested = (size > kStorageSize) ? kStorageSize : size;
@@ -146,7 +165,7 @@ private:
// Range has already been checked by the caller.
bool setPosition(size_t newPosition) {
- auto* env = get_env_or_die(mJvm);
+ auto* env = requireEnv(mJvm);
env->CallObjectMethod(mByteBuffer, gByteBuffer_setPositionMethodID,
newPosition + mInitialPosition);
if (env->ExceptionCheck()) {
@@ -185,7 +204,7 @@ public:
}
~ByteArrayStream() override {
- auto* env = get_env_or_die(mJvm);
+ auto* env = requireEnv(mJvm);
env->DeleteGlobalRef(mByteArray);
}
@@ -197,7 +216,7 @@ public:
return 0;
}
- auto* env = get_env_or_die(mJvm);
+ auto* env = requireEnv(mJvm);
if (buffer) {
env->GetByteArrayRegion(mByteArray, mPosition + mOffset, size,
reinterpret_cast<jbyte*>(buffer));
diff --git a/core/jni/android_view_Surface.cpp b/core/jni/android_view_Surface.cpp
index ff14a2acc4d7..ca10c8d5a252 100644
--- a/core/jni/android_view_Surface.cpp
+++ b/core/jni/android_view_Surface.cpp
@@ -488,7 +488,7 @@ static jlong create(JNIEnv* env, jclass clazz, jlong rootNodePtr, jlong surfaceP
proxy->setWideGamut(true);
}
proxy->setSwapBehavior(SwapBehavior::kSwap_discardBuffer);
- proxy->setSurface(surface);
+ proxy->setSurface(surface, false);
// Shadows can't be used via this interface, so just set the light source
// to all 0s.
proxy->setLightAlpha(0, 0);
diff --git a/core/proto/android/app/settings_enums.proto b/core/proto/android/app/settings_enums.proto
index 2b702bacd14e..2cedd847e97d 100644
--- a/core/proto/android/app/settings_enums.proto
+++ b/core/proto/android/app/settings_enums.proto
@@ -2407,8 +2407,13 @@ enum PageId {
// OS: Q
SETTINGS_AWARE_DISPLAY = 1750;
+ // OPEN: Settings > System > Input & Gesture > tap gesture
+ // CATEGORY: SETTINGS
+ // OS: Q
+ SETTINGS_GESTURE_TAP = 1751;
+
// OPEN: Settings > Developer Options > Platform Compat
// CATEGORY: SETTINGS
// OS: R
- SETTINGS_PLATFORM_COMPAT_DASHBOARD = 1805;
+ SETTINGS_PLATFORM_COMPAT_DASHBOARD = 1805;
}
diff --git a/core/proto/android/providers/settings/secure.proto b/core/proto/android/providers/settings/secure.proto
index 61799eefdca6..ef413b9b04cf 100644
--- a/core/proto/android/providers/settings/secure.proto
+++ b/core/proto/android/providers/settings/secure.proto
@@ -211,6 +211,11 @@ message SecureSettingsProto {
optional SettingProto silence_timer_touch_count = 11 [ (android.privacy).dest =
DEST_AUTOMATIC ];
optional SettingProto skip_touch_count = 12 [ (android.privacy).dest = DEST_AUTOMATIC ];
+ optional SettingProto aware_tap_pause_gesture_count = 13 [
+ (android.privacy).dest =
+ DEST_AUTOMATIC ];
+ optional SettingProto aware_tap_pause_touch_count = 14 [ (android.privacy).dest =
+ DEST_AUTOMATIC ];
}
optional Gesture gesture = 74;
diff --git a/core/res/res/drawable/chooser_action_button_bg.xml b/core/res/res/drawable/chooser_action_button_bg.xml
new file mode 100644
index 000000000000..a434c0b9b6a9
--- /dev/null
+++ b/core/res/res/drawable/chooser_action_button_bg.xml
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2019 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License
+ -->
+<ripple xmlns:android="http://schemas.android.com/apk/res/android"
+ android:color="@color/lighter_gray">
+ <item>
+ <inset
+ android:insetLeft="0dp"
+ android:insetTop="8dp"
+ android:insetRight="0dp"
+ android:insetBottom="8dp">
+ <shape android:shape="rectangle">
+ <corners android:radius="16dp"></corners>
+ <stroke android:width="1dp"
+ android:color="?attr/textColorSecondary" />
+ <solid android:color="?attr/colorBackground" />
+ </shape>
+ </inset>
+ </item>
+</ripple>
diff --git a/core/res/res/drawable/ic_content_copy_gm2.xml b/core/res/res/drawable/ic_content_copy_gm2.xml
deleted file mode 100644
index ee58738b75d0..000000000000
--- a/core/res/res/drawable/ic_content_copy_gm2.xml
+++ /dev/null
@@ -1,25 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2019 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-
-<vector xmlns:android="http://schemas.android.com/apk/res/android"
- android:width="24dp"
- android:height="24dp"
- android:viewportWidth="24"
- android:viewportHeight="24">
- <path
- android:fillColor="?android:attr/textColorSecondary"
- android:pathData="M18,21L4,21L4,7L2,7v14c0,1.1 0.9,2 2,2h14v-2zM21,17L21,3c0,-1.1 -0.9,-2 -2,-2L8,1c-1.1,0 -2,0.9 -2,2v14c0,1.1 0.9,2 2,2h11c1.1,0 2,-0.9 2,-2zM19,17L8,17L8,3h11v14z"/>
-</vector>
diff --git a/core/res/res/drawable/ic_menu_copy_material.xml b/core/res/res/drawable/ic_menu_copy_material.xml
index c03723b1fb33..ee58738b75d0 100644
--- a/core/res/res/drawable/ic_menu_copy_material.xml
+++ b/core/res/res/drawable/ic_menu_copy_material.xml
@@ -1,26 +1,25 @@
-<!--
-Copyright (C) 2014 The Android Open Source Project
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2019 The Android Open Source Project
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
- http://www.apache.org/licenses/LICENSE-2.0
+ http://www.apache.org/licenses/LICENSE-2.0
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
-->
+
<vector xmlns:android="http://schemas.android.com/apk/res/android"
- android:width="24dp"
- android:height="24dp"
- android:viewportWidth="24.0"
- android:viewportHeight="24.0"
- android:autoMirrored="true"
- android:tint="?attr/colorControlNormal">
- <path
- android:pathData="M16,1L4,1C2.9,1 2,1.9 2,3l0,14l2,0L4,3l12,0L16,1zM19,5L8,5C6.9,5 6,5.9 6,7l0,14c0,1.1 0.9,2 2,2l11,0c1.1,0 2,-0.9 2,-2L21,7C21,5.9 20.1,5 19,5zM19,21L8,21L8,7l11,0L19,21z"
- android:fillColor="@color/white"/>
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24"
+ android:viewportHeight="24">
+ <path
+ android:fillColor="?android:attr/textColorSecondary"
+ android:pathData="M18,21L4,21L4,7L2,7v14c0,1.1 0.9,2 2,2h14v-2zM21,17L21,3c0,-1.1 -0.9,-2 -2,-2L8,1c-1.1,0 -2,0.9 -2,2v14c0,1.1 0.9,2 2,2h11c1.1,0 2,-0.9 2,-2zM19,17L8,17L8,3h11v14z"/>
</vector>
diff --git a/core/res/res/layout/chooser_action_button.xml b/core/res/res/layout/chooser_action_button.xml
new file mode 100644
index 000000000000..562f188bbc6b
--- /dev/null
+++ b/core/res/res/layout/chooser_action_button.xml
@@ -0,0 +1,28 @@
+<!--
+ ~ Copyright (C) 2019 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License
+ -->
+
+<Button xmlns:android="http://schemas.android.com/apk/res/android"
+ android:gravity="center_vertical|start"
+ android:paddingStart="12dp"
+ android:paddingEnd="12dp"
+ android:drawablePadding="8dp"
+ android:textColor="?android:textColorSecondary"
+ android:textSize="12sp"
+ android:maxWidth="192dp"
+ android:singleLine="true"
+ android:clickable="true"
+ android:background="@drawable/chooser_action_button_bg"
+ />
diff --git a/core/res/res/layout/chooser_action_row.xml b/core/res/res/layout/chooser_action_row.xml
new file mode 100644
index 000000000000..ea7561124181
--- /dev/null
+++ b/core/res/res/layout/chooser_action_row.xml
@@ -0,0 +1,26 @@
+<!--
+ ~ Copyright (C) 2019 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License
+ -->
+
+<LinearLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:paddingLeft="@dimen/chooser_edge_margin_normal"
+ android:paddingRight="@dimen/chooser_edge_margin_normal"
+ android:gravity="center"
+ >
+
+</LinearLayout>
diff --git a/core/res/res/layout/chooser_grid_preview_file.xml b/core/res/res/layout/chooser_grid_preview_file.xml
index f7d60c91052d..860544399288 100644
--- a/core/res/res/layout/chooser_grid_preview_file.xml
+++ b/core/res/res/layout/chooser_grid_preview_file.xml
@@ -65,13 +65,14 @@
android:gravity="start|top"
android:paddingRight="24dp"
android:singleLine="true"/>
- <Button
- android:id="@+id/file_copy_button"
- android:layout_width="24dp"
- android:layout_height="24dp"
- android:gravity="center"
- android:layout_gravity="center_vertical"
- android:background="@drawable/ic_content_copy_gm2"/>
</LinearLayout>
+
+ <include
+ android:id="@+id/chooser_action_row"
+ layout="@layout/chooser_action_row"
+ android:layout_width="@dimen/chooser_preview_width"
+ android:layout_height="wrap_content"
+ android:layout_marginBottom="@dimen/chooser_view_spacing"
+ />
</LinearLayout>
diff --git a/core/res/res/layout/chooser_grid_preview_image.xml b/core/res/res/layout/chooser_grid_preview_image.xml
index 79a0de4b271f..29b7fb428194 100644
--- a/core/res/res/layout/chooser_grid_preview_image.xml
+++ b/core/res/res/layout/chooser_grid_preview_image.xml
@@ -78,5 +78,14 @@
android:scaleType="centerCrop"/>
</RelativeLayout>
+
+ <include
+ android:id="@+id/chooser_action_row"
+ layout="@layout/chooser_action_row"
+ android:layout_width="@dimen/chooser_preview_width"
+ android:layout_height="wrap_content"
+ android:layout_marginBottom="@dimen/chooser_view_spacing"
+ />
+
</LinearLayout>
diff --git a/core/res/res/layout/chooser_grid_preview_text.xml b/core/res/res/layout/chooser_grid_preview_text.xml
index 9c725b93eec6..5998cff71dfd 100644
--- a/core/res/res/layout/chooser_grid_preview_text.xml
+++ b/core/res/res/layout/chooser_grid_preview_text.xml
@@ -37,50 +37,25 @@
<TextView
android:id="@+id/content_preview_text"
- android:layout_width="wrap_content"
+ android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentStart="true"
- android:layout_toStartOf="@id/copy_button"
android:layout_centerVertical="true"
android:ellipsize="end"
android:fontFamily="@android:string/config_headlineFontFamily"
android:textColor="?android:attr/textColorPrimary"
android:maxLines="2"/>
- <LinearLayout
- android:id="@+id/copy_button"
- android:orientation="vertical"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_centerVertical="true"
- android:layout_alignParentEnd="true"
- android:layout_marginStart="@dimen/chooser_view_spacing"
- android:gravity="center"
- android:minWidth="48dp"
- android:minHeight="48dp"
- android:clickable="true"
- style="?attr/borderlessButtonStyle">
-
- <ImageView
- android:layout_width="24dp"
- android:layout_height="24dp"
- android:gravity="top|center_horizontal"
- android:src="@drawable/ic_content_copy_gm2" />
-
- <TextView
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_marginTop="4dp"
- android:gravity="center_horizontal"
- android:text="@string/copy"
- android:textColor="?android:textColorSecondary"
- android:textSize="12sp"
- android:maxWidth="72dp"
- android:maxLines="2"
- android:ellipsize="end" />
- </LinearLayout>
</RelativeLayout>
+ <include
+ android:id="@+id/chooser_action_row"
+ layout="@layout/chooser_action_row"
+ android:layout_width="@dimen/chooser_preview_width"
+ android:layout_height="wrap_content"
+ android:layout_marginBottom="@dimen/chooser_view_spacing"
+ />
+
<!-- Required sub-layout so we can get the nice rounded corners-->
<!-- around this section -->
<LinearLayout
diff --git a/core/res/res/layout/resolve_list_item.xml b/core/res/res/layout/resolve_list_item.xml
index 0bdb25a8d307..485709523e66 100644
--- a/core/res/res/layout/resolve_list_item.xml
+++ b/core/res/res/layout/resolve_list_item.xml
@@ -22,8 +22,6 @@
android:layout_height="wrap_content"
android:layout_width="match_parent"
android:minHeight="?attr/listPreferredItemHeightSmall"
- android:paddingTop="4dp"
- android:paddingBottom="4dp"
android:background="?attr/activatedBackgroundIndicator">
<!-- Activity icon when presenting dialog
@@ -32,7 +30,8 @@
android:layout_width="@dimen/resolver_icon_size"
android:layout_height="@dimen/resolver_icon_size"
android:layout_gravity="start|center_vertical"
- android:layout_marginStart="?attr/listPreferredItemPaddingStart"
+ android:layout_marginStart="@dimen/resolver_icon_margin"
+ android:layout_marginEnd="@dimen/resolver_icon_margin"
android:layout_marginTop="12dp"
android:layout_marginBottom="12dp"
android:scaleType="fitCenter" />
@@ -40,8 +39,7 @@
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:gravity="start|center_vertical"
android:orientation="vertical"
- android:paddingStart="?attr/listPreferredItemPaddingStart"
- android:paddingEnd="?attr/listPreferredItemPaddingEnd"
+ android:paddingEnd="@dimen/resolver_edge_margin"
android:layout_height="wrap_content"
android:layout_width="wrap_content"
android:layout_gravity="start|center_vertical">
@@ -49,14 +47,20 @@
<TextView android:id="@android:id/text1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:textAppearance="?attr/textAppearanceMedium"
- android:textColor="?attr/textColorPrimary"
+ android:layout_gravity="start|center_vertical"
+ android:textColor="?android:attr/textColorPrimary"
+ android:fontFamily="@android:string/config_bodyFontFamily"
+ android:textSize="16sp"
android:minLines="1"
android:maxLines="1"
android:ellipsize="marquee" />
<!-- Extended activity info to distinguish between duplicate activity names -->
<TextView android:id="@android:id/text2"
- android:textAppearance="?android:attr/textAppearanceSmall"
+ android:textColor="?android:attr/textColorSecondary"
+ android:fontFamily="@android:string/config_bodyFontFamily"
+ android:layout_gravity="start|center_vertical"
+ android:textSize="14sp"
+ android:visibility="gone"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:minLines="1"
diff --git a/core/res/res/layout/resolver_different_item_header.xml b/core/res/res/layout/resolver_different_item_header.xml
index 7d9ffd72870d..0a35edc42329 100644
--- a/core/res/res/layout/resolver_different_item_header.xml
+++ b/core/res/res/layout/resolver_different_item_header.xml
@@ -22,12 +22,12 @@
android:layout_height="wrap_content"
android:layout_alwaysShow="true"
android:text="@string/use_a_different_app"
- android:minHeight="56dp"
- android:textAppearance="?android:attr/textAppearanceMedium"
+ android:textColor="?android:attr/textColorPrimary"
+ android:fontFamily="@android:string/config_headlineFontFamilyMedium"
+ android:textSize="16sp"
android:gravity="start|center_vertical"
- android:paddingStart="16dp"
- android:paddingEnd="16dp"
- android:paddingTop="8dp"
- android:paddingBottom="8dp"
- android:elevation="8dp"
- />
+ android:paddingStart="@dimen/resolver_edge_margin"
+ android:paddingEnd="@dimen/resolver_edge_margin"
+ android:paddingTop="@dimen/resolver_small_margin"
+ android:paddingBottom="@dimen/resolver_edge_margin"
+ android:elevation="1dp" />
diff --git a/core/res/res/layout/resolver_list.xml b/core/res/res/layout/resolver_list.xml
index 1dd420746e8a..6e45e7a4c509 100644
--- a/core/res/res/layout/resolver_list.xml
+++ b/core/res/res/layout/resolver_list.xml
@@ -29,16 +29,18 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alwaysShow="true"
- android:elevation="8dp"
- android:background="?attr/colorBackgroundFloating">
+ android:elevation="@dimen/resolver_elevation"
+ android:paddingTop="@dimen/resolver_small_margin"
+ android:paddingStart="@dimen/resolver_edge_margin"
+ android:paddingEnd="@dimen/resolver_edge_margin"
+ android:paddingBottom="@dimen/resolver_edge_margin"
+ android:background="@drawable/bottomsheet_background">
<TextView
android:id="@+id/profile_button"
android:layout_width="wrap_content"
android:layout_height="48dp"
android:layout_marginEnd="8dp"
- android:paddingStart="8dp"
- android:paddingEnd="8dp"
android:visibility="gone"
style="?attr/borderlessButtonStyle"
android:textAppearance="?attr/textAppearanceButton"
@@ -50,36 +52,49 @@
<TextView
android:id="@+id/title"
- android:layout_width="wrap_content"
+ android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:minHeight="56dp"
- android:textAppearance="?attr/textAppearanceMedium"
- android:gravity="start|center_vertical"
- android:paddingStart="?attr/dialogPreferredPadding"
- android:paddingEnd="?attr/dialogPreferredPadding"
- android:paddingTop="8dp"
android:layout_below="@id/profile_button"
android:layout_alignParentStart="true"
- android:paddingBottom="8dp" />
+ android:textColor="?android:attr/textColorPrimary"
+ android:fontFamily="@android:string/config_headlineFontFamilyMedium"
+ android:textSize="16sp"
+ android:gravity="start|center_vertical" />
</RelativeLayout>
+ <View
+ android:layout_alwaysShow="true"
+ android:layout_width="match_parent"
+ android:layout_height="1dp"
+ android:background="?attr/colorBackgroundFloating"
+ android:foreground="?attr/dividerVertical" />
<ListView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="@+id/resolver_list"
android:clipToPadding="false"
- android:scrollbarStyle="outsideOverlay"
android:background="?attr/colorBackgroundFloating"
- android:elevation="8dp"
+ android:elevation="@dimen/resolver_elevation"
android:nestedScrollingEnabled="true"
+ android:scrollbarStyle="outsideOverlay"
android:scrollIndicators="top|bottom"
- android:divider="@null" />
+ android:divider="?attr/dividerVertical"
+ android:footerDividersEnabled="false"
+ android:headerDividersEnabled="false"
+ android:dividerHeight="1dp" />
+ <View
+ android:layout_alwaysShow="true"
+ android:layout_width="match_parent"
+ android:layout_height="1dp"
+ android:background="?attr/colorBackgroundFloating"
+ android:foreground="?attr/dividerVertical" />
+
<TextView android:id="@+id/empty"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="?attr/colorBackgroundFloating"
- android:elevation="8dp"
+ android:elevation="@dimen/resolver_elevation"
android:layout_alwaysShow="true"
android:text="@string/noApplications"
android:padding="32dp"
@@ -102,18 +117,19 @@
android:background="?attr/colorBackgroundFloating"
android:paddingTop="@dimen/resolver_button_bar_spacing"
android:paddingBottom="@dimen/resolver_button_bar_spacing"
- android:paddingStart="12dp"
- android:paddingEnd="12dp"
- android:elevation="8dp">
+ android:paddingStart="@dimen/resolver_edge_margin"
+ android:paddingEnd="@dimen/resolver_small_margin"
+ android:elevation="@dimen/resolver_elevation">
<Button
android:id="@+id/button_once"
android:layout_width="wrap_content"
android:layout_gravity="start"
android:maxLines="2"
- style="?attr/buttonBarNegativeButtonStyle"
- android:minHeight="@dimen/alert_dialog_button_bar_height"
+ style="?attr/buttonBarButtonStyle"
+ android:fontFamily="@android:string/config_headlineFontFamilyMedium"
android:layout_height="wrap_content"
+ android:textAllCaps="false"
android:enabled="false"
android:text="@string/activity_resolver_use_once"
android:onClick="onButtonClick" />
@@ -123,8 +139,9 @@
android:layout_width="wrap_content"
android:layout_gravity="end"
android:maxLines="2"
- android:minHeight="@dimen/alert_dialog_button_bar_height"
- style="?attr/buttonBarPositiveButtonStyle"
+ style="?attr/buttonBarButtonStyle"
+ android:fontFamily="@android:string/config_headlineFontFamilyMedium"
+ android:textAllCaps="false"
android:layout_height="wrap_content"
android:enabled="false"
android:text="@string/activity_resolver_use_always"
diff --git a/core/res/res/layout/resolver_list_with_default.xml b/core/res/res/layout/resolver_list_with_default.xml
index 740a7eb9374e..dbba0b7bcc25 100644
--- a/core/res/res/layout/resolver_list_with_default.xml
+++ b/core/res/res/layout/resolver_list_with_default.xml
@@ -29,22 +29,22 @@
android:layout_height="wrap_content"
android:layout_alwaysShow="true"
android:orientation="vertical"
- android:background="?attr/colorBackgroundFloating"
- android:elevation="8dp">
+ android:background="@drawable/bottomsheet_background"
+ android:paddingTop="@dimen/resolver_small_margin"
+ android:elevation="@dimen/resolver_elevation">
<LinearLayout
android:layout_width="match_parent"
- android:layout_height="64dp"
- android:orientation="horizontal">
-
+ android:layout_height="wrap_content"
+ android:orientation="horizontal"
+ android:paddingBottom="@dimen/resolver_edge_margin"
+ android:paddingEnd="@dimen/resolver_edge_margin">
<ImageView
android:id="@+id/icon"
- android:layout_width="24dp"
- android:layout_height="24dp"
+ android:layout_width="@dimen/resolver_icon_size"
+ android:layout_height="@dimen/resolver_icon_size"
android:layout_gravity="start|top"
- android:layout_marginStart="16dp"
- android:layout_marginEnd="16dp"
- android:layout_marginTop="20dp"
+ android:layout_marginStart="@dimen/resolver_icon_margin"
android:src="@drawable/resolver_icon_placeholder"
android:scaleType="fitCenter" />
@@ -52,9 +52,11 @@
android:id="@+id/title"
android:layout_width="0dp"
android:layout_weight="1"
- android:layout_height="?attr/listPreferredItemHeight"
- android:layout_marginStart="16dp"
- android:textAppearance="?attr/textAppearanceMedium"
+ android:layout_height="wrap_content"
+ android:layout_marginStart="@dimen/resolver_icon_margin"
+ android:textColor="?android:attr/textColorPrimary"
+ android:fontFamily="@android:string/config_headlineFontFamilyMedium"
+ android:textSize="16sp"
android:gravity="start|center_vertical"
android:paddingEnd="16dp" />
@@ -107,21 +109,22 @@
android:orientation="horizontal"
android:layoutDirection="locale"
android:measureWithLargestChild="true"
- android:paddingTop="8dp"
- android:paddingBottom="8dp"
- android:paddingStart="12dp"
- android:paddingEnd="12dp"
- android:elevation="8dp">
+ android:paddingTop="@dimen/resolver_button_bar_spacing"
+ android:paddingBottom="@dimen/resolver_button_bar_spacing"
+ android:paddingStart="@dimen/resolver_edge_margin"
+ android:paddingEnd="@dimen/resolver_small_margin"
+ android:elevation="@dimen/resolver_elevation">
<Button
android:id="@+id/button_once"
android:layout_width="wrap_content"
android:layout_gravity="start"
android:maxLines="2"
- style="?attr/buttonBarNegativeButtonStyle"
- android:minHeight="@dimen/alert_dialog_button_bar_height"
+ style="?attr/buttonBarButtonStyle"
+ android:fontFamily="@android:string/config_headlineFontFamilyMedium"
android:layout_height="wrap_content"
android:enabled="false"
+ android:textAllCaps="false"
android:text="@string/activity_resolver_use_once"
android:onClick="onButtonClick" />
@@ -130,29 +133,40 @@
android:layout_width="wrap_content"
android:layout_gravity="end"
android:maxLines="2"
- android:minHeight="@dimen/alert_dialog_button_bar_height"
- style="?attr/buttonBarPositiveButtonStyle"
+ style="?attr/buttonBarButtonStyle"
+ android:fontFamily="@android:string/config_headlineFontFamilyMedium"
android:layout_height="wrap_content"
android:enabled="false"
+ android:textAllCaps="false"
android:text="@string/activity_resolver_use_always"
android:onClick="onButtonClick" />
</LinearLayout>
-
- <View
- android:layout_width="match_parent"
- android:layout_height="1dp"
- android:background="?attr/dividerVertical" />
</LinearLayout>
+ <View
+ android:layout_alwaysShow="true"
+ android:layout_width="match_parent"
+ android:layout_height="1dp"
+ android:background="?attr/colorBackgroundFloating"
+ android:foreground="?attr/dividerVertical" />
<ListView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="@+id/resolver_list"
android:clipToPadding="false"
- android:scrollbarStyle="outsideOverlay"
android:background="?attr/colorBackgroundFloating"
- android:elevation="8dp"
+ android:elevation="@dimen/resolver_elevation"
android:nestedScrollingEnabled="true"
- android:divider="@null" />
-
+ android:scrollbarStyle="outsideOverlay"
+ android:scrollIndicators="top|bottom"
+ android:divider="?attr/dividerVertical"
+ android:footerDividersEnabled="false"
+ android:headerDividersEnabled="false"
+ android:dividerHeight="1dp" />
+ <View
+ android:layout_alwaysShow="true"
+ android:layout_width="match_parent"
+ android:layout_height="1dp"
+ android:background="?attr/colorBackgroundFloating"
+ android:foreground="?attr/dividerVertical" />
</com.android.internal.widget.ResolverDrawerLayout>
diff --git a/core/res/res/values-mcc310-mnc170/config.xml b/core/res/res/values-mcc310-mnc170/config.xml
index 26b9192e0cc3..12e448cd6b21 100644
--- a/core/res/res/values-mcc310-mnc170/config.xml
+++ b/core/res/res/values-mcc310-mnc170/config.xml
@@ -22,5 +22,9 @@
<resources>
<!-- Enable 5 bar signal strength icon -->
<bool name="config_inflateSignalStrength">true</bool>
+
+ <!-- Boolean indicating whether frameworks needs to reset cell broadcast geo-fencing
+ check after reboot or airplane mode toggling -->
+ <bool translatable="false" name="reset_geo_fencing_check_after_boot_or_apm">true</bool>
</resources>
diff --git a/core/res/res/values-mcc310-mnc380/config.xml b/core/res/res/values-mcc310-mnc380/config.xml
index 26b9192e0cc3..12e448cd6b21 100644
--- a/core/res/res/values-mcc310-mnc380/config.xml
+++ b/core/res/res/values-mcc310-mnc380/config.xml
@@ -22,5 +22,9 @@
<resources>
<!-- Enable 5 bar signal strength icon -->
<bool name="config_inflateSignalStrength">true</bool>
+
+ <!-- Boolean indicating whether frameworks needs to reset cell broadcast geo-fencing
+ check after reboot or airplane mode toggling -->
+ <bool translatable="false" name="reset_geo_fencing_check_after_boot_or_apm">true</bool>
</resources>
diff --git a/core/res/res/values-mcc310-mnc410/config.xml b/core/res/res/values-mcc310-mnc410/config.xml
index 3fb3f0f7e9ff..22b8fefcecf4 100644
--- a/core/res/res/values-mcc310-mnc410/config.xml
+++ b/core/res/res/values-mcc310-mnc410/config.xml
@@ -52,4 +52,7 @@
<!-- Enable 5 bar signal strength icon -->
<bool name="config_inflateSignalStrength">true</bool>
+ <!-- Boolean indicating whether frameworks needs to reset cell broadcast geo-fencing
+ check after reboot or airplane mode toggling -->
+ <bool translatable="false" name="reset_geo_fencing_check_after_boot_or_apm">true</bool>
</resources>
diff --git a/core/res/res/values-mcc310-mnc560/config.xml b/core/res/res/values-mcc310-mnc560/config.xml
index 26b9192e0cc3..12e448cd6b21 100644
--- a/core/res/res/values-mcc310-mnc560/config.xml
+++ b/core/res/res/values-mcc310-mnc560/config.xml
@@ -22,5 +22,9 @@
<resources>
<!-- Enable 5 bar signal strength icon -->
<bool name="config_inflateSignalStrength">true</bool>
+
+ <!-- Boolean indicating whether frameworks needs to reset cell broadcast geo-fencing
+ check after reboot or airplane mode toggling -->
+ <bool translatable="false" name="reset_geo_fencing_check_after_boot_or_apm">true</bool>
</resources>
diff --git a/core/res/res/values-mcc311-mnc180/config.xml b/core/res/res/values-mcc311-mnc180/config.xml
index 26b9192e0cc3..12e448cd6b21 100644
--- a/core/res/res/values-mcc311-mnc180/config.xml
+++ b/core/res/res/values-mcc311-mnc180/config.xml
@@ -22,5 +22,9 @@
<resources>
<!-- Enable 5 bar signal strength icon -->
<bool name="config_inflateSignalStrength">true</bool>
+
+ <!-- Boolean indicating whether frameworks needs to reset cell broadcast geo-fencing
+ check after reboot or airplane mode toggling -->
+ <bool translatable="false" name="reset_geo_fencing_check_after_boot_or_apm">true</bool>
</resources>
diff --git a/core/res/res/values-mcc313-mnc100/config.xml b/core/res/res/values-mcc313-mnc100/config.xml
index ccd03f10616a..a8e481a6a3a4 100644
--- a/core/res/res/values-mcc313-mnc100/config.xml
+++ b/core/res/res/values-mcc313-mnc100/config.xml
@@ -42,4 +42,8 @@
<item>"#8"</item>
<item>"#9"</item>
</string-array>
+
+ <!-- Boolean indicating whether frameworks needs to reset cell broadcast geo-fencing
+ check after reboot or airplane mode toggling -->
+ <bool translatable="false" name="reset_geo_fencing_check_after_boot_or_apm">true</bool>
</resources>
diff --git a/core/res/res/values/arrays.xml b/core/res/res/values/arrays.xml
index f05898561b8a..dca9c7234e13 100644
--- a/core/res/res/values/arrays.xml
+++ b/core/res/res/values/arrays.xml
@@ -175,7 +175,15 @@
</array>
<!-- Used in ResolverTargetActionsDialogFragment -->
- <string-array name="resolver_target_actions">
+
+ <!-- Used in ResolverTargetActionsDialogFragment -->
+ <string-array name="resolver_target_actions_pin">
+ <item>@string/pin_target</item>
+ <item>@string/app_info</item>
+ </string-array>
+
+ <string-array name="resolver_target_actions_unpin">
+ <item>@string/unpin_target</item>
<item>@string/app_info</item>
</string-array>
diff --git a/core/res/res/values/attrs_manifest.xml b/core/res/res/values/attrs_manifest.xml
index 17045d812f4b..01fb7d432605 100644
--- a/core/res/res/values/attrs_manifest.xml
+++ b/core/res/res/values/attrs_manifest.xml
@@ -352,10 +352,10 @@
will be given a single shared user ID, so they can for example run
in the same process. Note that for them to actually get the same
user ID, they must also be signed with the same signature.
- @deprecated Shared user id's cause non-deterministic behaviour within the
- package manager. As such, it's use is discouraged, deprecated, and will
- be removed altogether in a future version of Android. Instead, proper
- communication mechanisms such as services and providers should be used
+ @deprecated Shared user IDs cause non-deterministic behavior within the
+ package manager. As such, its use is strongly discouraged and may be
+ removed in a future version of Android. Instead, apps should use proper
+ communication mechanisms, such as services and content providers,
to facilitate interoperability between shared components. -->
<attr name="sharedUserId" format="string" />
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 4c25a7a08cf5..ee4c509c1c1e 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -4314,4 +4314,12 @@
create additional screen real estate outside beyond the keyboard. Note that the user needs
to have a confirmed way to dismiss the keyboard when desired. -->
<bool name="config_automotiveHideNavBarForKeyboard">false</bool>
+
+ <!-- Component name that accepts ACTION_SEND intents for nearby (proximity-based) sharing.
+ Used by ChooserActivity. -->
+ <string translatable="false" name="config_defaultNearbySharingComponent"></string>
+
+ <!-- Boolean indicating whether frameworks needs to reset cell broadcast geo-fencing
+ check after reboot or airplane mode toggling -->
+ <bool translatable="false" name="reset_geo_fencing_check_after_boot_or_apm">false</bool>
</resources>
diff --git a/core/res/res/values/dimens.xml b/core/res/res/values/dimens.xml
index 609659b62948..4fdb498451ae 100644
--- a/core/res/res/values/dimens.xml
+++ b/core/res/res/values/dimens.xml
@@ -750,7 +750,7 @@
<dimen name="seekbar_thumb_exclusion_max_size">48dp</dimen>
- <!-- chooser (sharesheet) spacing -->
+ <!-- chooser/resolver (sharesheet) spacing -->
<dimen name="chooser_corner_radius">8dp</dimen>
<dimen name="chooser_row_text_option_translate">25dp</dimen>
<dimen name="chooser_view_spacing">18dp</dimen>
@@ -759,11 +759,16 @@
<dimen name="chooser_preview_image_font_size">20sp</dimen>
<dimen name="chooser_preview_image_border">1dp</dimen>
<dimen name="chooser_preview_width">-1px</dimen>
- <dimen name="resolver_icon_size">42dp</dimen>
- <dimen name="resolver_button_bar_spacing">8dp</dimen>
- <dimen name="resolver_badge_size">18dp</dimen>
<dimen name="chooser_target_width">90dp</dimen>
<dimen name="chooser_header_scroll_elevation">4dp</dimen>
<dimen name="chooser_max_collapsed_height">288dp</dimen>
<dimen name="chooser_direct_share_label_placeholder_max_width">72dp</dimen>
+ <dimen name="resolver_icon_size">32dp</dimen>
+ <dimen name="resolver_button_bar_spacing">8dp</dimen>
+ <dimen name="resolver_badge_size">18dp</dimen>
+ <dimen name="resolver_icon_margin">16dp</dimen>
+ <dimen name="resolver_small_margin">18dp</dimen>
+ <dimen name="resolver_edge_margin">24dp</dimen>
+ <dimen name="resolver_elevation">1dp</dimen>
+ <dimen name="chooser_action_button_icon_size">18dp</dimen>
</resources>
diff --git a/core/res/res/values/ids.xml b/core/res/res/values/ids.xml
index 2b0c86b49577..ba64bf00c63a 100644
--- a/core/res/res/values/ids.xml
+++ b/core/res/res/values/ids.xml
@@ -193,4 +193,7 @@
<!-- A tag used to save the index where the custom view is stored -->
<item type="id" name="notification_custom_view_index_tag" />
+
+ <!-- Marks the "copy to clipboard" button in the ChooserActivity -->
+ <item type="id" name="chooser_copy_button" />
</resources>
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index fdfedea4bd13..2ee7d37e8447 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -4846,10 +4846,10 @@
<string name="confirm_battery_saver">OK</string>
<!-- [CHAR_LIMIT=NONE] Battery saver: Feature description, with a "learn more" link. -->
- <string name="battery_saver_description_with_learn_more">Battery Saver turns off or restricts background activity, some visual effects \u0026 other high-power features to extend battery life. <annotation id="url">Learn More</annotation></string>
+ <string name="battery_saver_description_with_learn_more">To extend battery life, Battery Saver:\n&#183;Turns on Dark theme\n&#183;Turns off or restricts background activity, some visual effects, and other features like \u201cHey Google\u201d\n\n<annotation id="url">Learn more</annotation></string>
<!-- [CHAR_LIMIT=NONE] Battery saver: Feature description, without a "learn more" link. -->
- <string name="battery_saver_description">Battery Saver turns off or restricts background activity, some visual effects \u0026 other high-power features to extend battery life.</string>
+ <string name="battery_saver_description">To extend battery life, Battery Saver:\n&#183;Turns on Dark theme\n&#183;Turns off or restricts background activity, some visual effects, and other features like \u201cHey Google\u201d</string>
<!-- [CHAR_LIMIT=NONE] Data saver: Feature description -->
<string name="data_saver_description">To help reduce data usage, Data Saver prevents some apps from sending or receiving data in the background. An app you’re currently using can access data, but may do so less frequently. This may mean, for example, that images don’t display until you tap them.</string>
@@ -5079,6 +5079,10 @@
<string name="usb_mtp_launch_notification_description">Tap to view files</string>
<!-- Resolver target actions strings -->
+ <!-- Pin this app to the top of the Sharesheet app list. [CHAR LIMIT=60]-->
+ <string name="pin_target">Pin</string>
+ <!-- Un-pin this app in the Sharesheet, so that it is sorted normally. [CHAR LIMIT=60]-->
+ <string name="unpin_target">Unpin</string>
<!-- View application info for a target. -->
<string name="app_info">App info</string>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index a1f66d352bcf..d9052b018b0c 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -66,8 +66,7 @@
<java-symbol type="id" name="content_preview_text_layout" />
<java-symbol type="id" name="content_preview_title" />
<java-symbol type="id" name="content_preview_title_layout" />
- <java-symbol type="id" name="copy_button" />
- <java-symbol type="id" name="file_copy_button" />
+ <java-symbol type="id" name="chooser_action_row" />
<java-symbol type="id" name="current_scene" />
<java-symbol type="id" name="scene_layoutid_cache" />
<java-symbol type="id" name="customPanel" />
@@ -3052,7 +3051,8 @@
<java-symbol type="color" name="notification_material_background_color" />
<!-- Resolver target actions -->
- <java-symbol type="array" name="resolver_target_actions" />
+ <java-symbol type="array" name="resolver_target_actions_pin" />
+ <java-symbol type="array" name="resolver_target_actions_unpin" />
<java-symbol type="array" name="non_removable_euicc_slots" />
@@ -3809,6 +3809,10 @@
<java-symbol type="dimen" name="resolver_icon_size"/>
<java-symbol type="dimen" name="resolver_badge_size"/>
<java-symbol type="dimen" name="resolver_button_bar_spacing"/>
+ <java-symbol type="dimen" name="resolver_icon_margin"/>
+ <java-symbol type="dimen" name="resolver_small_margin"/>
+ <java-symbol type="dimen" name="resolver_edge_margin"/>
+ <java-symbol type="dimen" name="resolver_elevation"/>
<!-- For DropBox -->
<java-symbol type="integer" name="config_dropboxLowPriorityBroadcastRateLimitPeriod" />
@@ -3841,5 +3845,11 @@
<java-symbol type="drawable" name="android_logotype" />
<java-symbol type="layout" name="platlogo_layout" />
+ <java-symbol type="id" name="chooser_copy_button" />
+ <java-symbol type="layout" name="chooser_action_button" />
+ <java-symbol type="dimen" name="chooser_action_button_icon_size" />
+ <java-symbol type="string" name="config_defaultNearbySharingComponent" />
+
<java-symbol type="bool" name="config_automotiveHideNavBarForKeyboard" />
+ <java-symbol type="bool" name="reset_geo_fencing_check_after_boot_or_apm" />
</resources>
diff --git a/core/res/res/values/themes_device_defaults.xml b/core/res/res/values/themes_device_defaults.xml
index 628926231c65..2b37a0c1af6b 100644
--- a/core/res/res/values/themes_device_defaults.xml
+++ b/core/res/res/values/themes_device_defaults.xml
@@ -1733,6 +1733,7 @@ easier.
<item name="colorBackground">@color/background_device_default_light</item>
<item name="colorBackgroundFloating">@color/background_device_default_light</item>
<item name="layout_gravity">center</item>
+ <item name="windowAnimationStyle">@style/Animation.DeviceDefault.Dialog</item>
</style>
<style name="Theme.DeviceDefault.Notification" parent="@style/Theme.Material.Notification">
diff --git a/core/tests/coretests/src/android/provider/SettingsBackupTest.java b/core/tests/coretests/src/android/provider/SettingsBackupTest.java
index e60e5555cc9d..b7b02a3d61f1 100644
--- a/core/tests/coretests/src/android/provider/SettingsBackupTest.java
+++ b/core/tests/coretests/src/android/provider/SettingsBackupTest.java
@@ -595,7 +595,10 @@ public class SettingsBackupTest {
Settings.Secure.ANR_SHOW_BACKGROUND,
Settings.Secure.ASSISTANT,
Settings.Secure.ASSIST_DISCLOSURE_ENABLED,
+ Settings.Secure.ASSIST_GESTURE_ENABLED,
Settings.Secure.ASSIST_GESTURE_SENSITIVITY,
+ Settings.Secure.ASSIST_GESTURE_WAKE_ENABLED,
+ Settings.Secure.ASSIST_GESTURE_SILENCE_ALERTS_ENABLED,
Settings.Secure.ASSIST_GESTURE_SETUP_COMPLETE,
Settings.Secure.ASSIST_SCREENSHOT_ENABLED,
Settings.Secure.ASSIST_STRUCTURE_ENABLED,
@@ -723,6 +726,12 @@ public class SettingsBackupTest {
Settings.Secure.BIOMETRIC_DEBUG_ENABLED,
Settings.Secure.FACE_UNLOCK_ATTENTION_REQUIRED,
Settings.Secure.FACE_UNLOCK_DIVERSITY_REQUIRED,
+ Settings.Secure.AWARE_ENABLED,
+ Settings.Secure.SKIP_GESTURE,
+ Settings.Secure.SILENCE_GESTURE,
+ Settings.Secure.DOZE_WAKE_LOCK_SCREEN_GESTURE,
+ Settings.Secure.DOZE_WAKE_DISPLAY_GESTURE,
+ Settings.Secure.TAP_GESTURE,
Settings.Secure.FACE_UNLOCK_RE_ENROLL);
@Test
diff --git a/core/tests/coretests/src/com/android/internal/app/AbstractResolverComparatorTest.java b/core/tests/coretests/src/com/android/internal/app/AbstractResolverComparatorTest.java
new file mode 100644
index 000000000000..36dd3e4e72b9
--- /dev/null
+++ b/core/tests/coretests/src/com/android/internal/app/AbstractResolverComparatorTest.java
@@ -0,0 +1,105 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.app;
+
+import static junit.framework.Assert.assertEquals;
+
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.ActivityInfo;
+import android.content.pm.ResolveInfo;
+import android.os.Message;
+
+import androidx.test.InstrumentationRegistry;
+
+import org.junit.Test;
+
+import java.util.List;
+
+public class AbstractResolverComparatorTest {
+
+ @Test
+ public void testPinned() {
+ ResolverActivity.ResolvedComponentInfo r1 = new ResolverActivity.ResolvedComponentInfo(
+ new ComponentName("package", "class"), new Intent(), new ResolveInfo()
+ );
+ r1.setPinned(true);
+
+ ResolverActivity.ResolvedComponentInfo r2 = new ResolverActivity.ResolvedComponentInfo(
+ new ComponentName("zackage", "zlass"), new Intent(), new ResolveInfo()
+ );
+
+ Context context = InstrumentationRegistry.getTargetContext();
+ AbstractResolverComparator comparator = getTestComparator(context);
+
+ assertEquals("Pinned ranks over unpinned", -1, comparator.compare(r1, r2));
+ assertEquals("Unpinned ranks under pinned", 1, comparator.compare(r2, r1));
+ }
+
+
+ @Test
+ public void testBothPinned() {
+ ResolveInfo pmInfo1 = new ResolveInfo();
+ pmInfo1.activityInfo = new ActivityInfo();
+ pmInfo1.activityInfo.packageName = "aaa";
+
+ ResolverActivity.ResolvedComponentInfo r1 = new ResolverActivity.ResolvedComponentInfo(
+ new ComponentName("package", "class"), new Intent(), pmInfo1);
+ r1.setPinned(true);
+
+ ResolveInfo pmInfo2 = new ResolveInfo();
+ pmInfo2.activityInfo = new ActivityInfo();
+ pmInfo2.activityInfo.packageName = "zzz";
+ ResolverActivity.ResolvedComponentInfo r2 = new ResolverActivity.ResolvedComponentInfo(
+ new ComponentName("zackage", "zlass"), new Intent(), pmInfo2);
+ r2.setPinned(true);
+
+ Context context = InstrumentationRegistry.getTargetContext();
+ AbstractResolverComparator comparator = getTestComparator(context);
+
+ assertEquals("Both pinned should rank alphabetically", -1, comparator.compare(r1, r2));
+ }
+
+ private AbstractResolverComparator getTestComparator(Context context) {
+ Intent intent = new Intent();
+
+ AbstractResolverComparator testComparator =
+ new AbstractResolverComparator(context, intent) {
+
+ @Override
+ int compare(ResolveInfo lhs, ResolveInfo rhs) {
+ // Used for testing pinning, so we should never get here --- the overrides should
+ // determine the result instead.
+ return 1;
+ }
+
+ @Override
+ void doCompute(List<ResolverActivity.ResolvedComponentInfo> targets) {}
+
+ @Override
+ float getScore(ComponentName name) {
+ return 0;
+ }
+
+ @Override
+ void handleResultMessage(Message message) {}
+ };
+ return testComparator;
+ }
+
+}
diff --git a/core/tests/coretests/src/com/android/internal/app/ChooserActivityTest.java b/core/tests/coretests/src/com/android/internal/app/ChooserActivityTest.java
index c44b7d81868d..4dfa9bfa7e21 100644
--- a/core/tests/coretests/src/com/android/internal/app/ChooserActivityTest.java
+++ b/core/tests/coretests/src/com/android/internal/app/ChooserActivityTest.java
@@ -469,8 +469,8 @@ public class ChooserActivityTest {
.launchActivity(Intent.createChooser(sendIntent, null));
waitForIdle();
- onView(withId(R.id.copy_button)).check(matches(isDisplayed()));
- onView(withId(R.id.copy_button)).perform(click());
+ onView(withId(R.id.chooser_copy_button)).check(matches(isDisplayed()));
+ onView(withId(R.id.chooser_copy_button)).perform(click());
ClipboardManager clipboard = (ClipboardManager) activity.getSystemService(
Context.CLIPBOARD_SERVICE);
ClipData clipData = clipboard.getPrimaryClip();
diff --git a/core/tests/screenshothelpertests/src/com/android/internal/util/ScreenshotHelperTest.java b/core/tests/screenshothelpertests/src/com/android/internal/util/ScreenshotHelperTest.java
index 848364584ef3..e16d1caa98eb 100644
--- a/core/tests/screenshothelpertests/src/com/android/internal/util/ScreenshotHelperTest.java
+++ b/core/tests/screenshothelpertests/src/com/android/internal/util/ScreenshotHelperTest.java
@@ -19,7 +19,7 @@ package com.android.internal.util;
import static android.view.WindowManager.TAKE_SCREENSHOT_FULLSCREEN;
import static android.view.WindowManager.TAKE_SCREENSHOT_SELECTED_REGION;
-import static junit.framework.Assert.assertFalse;
+import static junit.framework.Assert.assertNull;
import static junit.framework.Assert.fail;
import static org.mockito.ArgumentMatchers.any;
@@ -80,8 +80,8 @@ public final class ScreenshotHelperTest {
CountDownLatch lock = new CountDownLatch(1);
mScreenshotHelper.takeScreenshot(TAKE_SCREENSHOT_FULLSCREEN, false, false, timeoutMs,
mHandler,
- worked -> {
- assertFalse(worked);
+ uri -> {
+ assertNull(uri);
lock.countDown();
});
diff --git a/graphics/java/android/graphics/Canvas.java b/graphics/java/android/graphics/Canvas.java
index 5648b854db40..a815f20293c5 100644
--- a/graphics/java/android/graphics/Canvas.java
+++ b/graphics/java/android/graphics/Canvas.java
@@ -2095,9 +2095,11 @@ public class Canvas extends BaseCanvas {
/**
* Draw the text, with origin at (x,y), using the specified paint, along the specified path. The
- * paint's Align setting determins where along the path to start the text.
+ * paint's Align setting determines where along the path to start the text.
*
* @param text The text to be drawn
+ * @param index The starting index within the text to be drawn
+ * @param count Starting from index, the number of characters to draw
* @param path The path the text should follow for its baseline
* @param hOffset The distance along the path to add to the text's starting position
* @param vOffset The distance above(-) or below(+) the path to position the text
@@ -2110,7 +2112,7 @@ public class Canvas extends BaseCanvas {
/**
* Draw the text, with origin at (x,y), using the specified paint, along the specified path. The
- * paint's Align setting determins where along the path to start the text.
+ * paint's Align setting determines where along the path to start the text.
*
* @param text The text to be drawn
* @param path The path the text should follow for its baseline
diff --git a/graphics/java/android/graphics/Paint.java b/graphics/java/android/graphics/Paint.java
index b7316ab03618..109d8631284d 100644
--- a/graphics/java/android/graphics/Paint.java
+++ b/graphics/java/android/graphics/Paint.java
@@ -1114,7 +1114,7 @@ public class Paint {
* Return the width for stroking.
* <p />
* A value of 0 strokes in hairline mode.
- * Hairlines always draws a single pixel independent of the canva's matrix.
+ * Hairlines always draws a single pixel independent of the canvas's matrix.
*
* @return the paint's stroke width, used whenever the paint's style is
* Stroke or StrokeAndFill.
@@ -1126,7 +1126,7 @@ public class Paint {
/**
* Set the width for stroking.
* Pass 0 to stroke in hairline mode.
- * Hairlines always draws a single pixel independent of the canva's matrix.
+ * Hairlines always draws a single pixel independent of the canvas's matrix.
*
* @param width set the paint's stroke width, used whenever the paint's
* style is Stroke or StrokeAndFill.
@@ -1958,8 +1958,8 @@ public class Paint {
* <code>
* Paint paint = new Paint();
* paint.setStartHyphenEdit(Paint.START_HYPHEN_EDIT_INSERT_HYPHEN);
- * paint.measureText("abc", 0, 3); // Returns the width of "‐abc"
- * Canvas.drawText("abc", 0, 3, 0f, 0f, paint); // Draws "‐abc"
+ * paint.measureText("abc", 0, 3); // Returns the width of "-abc"
+ * Canvas.drawText("abc", 0, 3, 0f, 0f, paint); // Draws "-abc"
* </code>
* </pre>
*
@@ -1985,8 +1985,8 @@ public class Paint {
* <code>
* Paint paint = new Paint();
* paint.setEndHyphenEdit(Paint.END_HYPHEN_EDIT_INSERT_HYPHEN);
- * paint.measureText("abc", 0, 3); // Returns the width of "abc‐"
- * Canvas.drawText("abc", 0, 3, 0f, 0f, paint); // Draws "abc‐"
+ * paint.measureText("abc", 0, 3); // Returns the width of "abc-"
+ * Canvas.drawText("abc", 0, 3, 0f, 0f, paint); // Draws "abc-"
* </code>
* </pre>
*
diff --git a/libs/hwui/renderthread/CanvasContext.cpp b/libs/hwui/renderthread/CanvasContext.cpp
index 9898a1c30856..827cced2883b 100644
--- a/libs/hwui/renderthread/CanvasContext.cpp
+++ b/libs/hwui/renderthread/CanvasContext.cpp
@@ -143,13 +143,15 @@ void CanvasContext::destroy() {
mAnimationContext->destroy();
}
-void CanvasContext::setSurface(sp<Surface>&& surface) {
+void CanvasContext::setSurface(sp<Surface>&& surface, bool enableTimeout) {
ATRACE_CALL();
if (surface) {
mNativeSurface = new ReliableSurface{std::move(surface)};
- // TODO: Fix error handling & re-shorten timeout
- mNativeSurface->setDequeueTimeout(4000_ms);
+ if (enableTimeout) {
+ // TODO: Fix error handling & re-shorten timeout
+ mNativeSurface->setDequeueTimeout(4000_ms);
+ }
} else {
mNativeSurface = nullptr;
}
diff --git a/libs/hwui/renderthread/CanvasContext.h b/libs/hwui/renderthread/CanvasContext.h
index 982c087b031a..a0233ca357aa 100644
--- a/libs/hwui/renderthread/CanvasContext.h
+++ b/libs/hwui/renderthread/CanvasContext.h
@@ -110,7 +110,7 @@ public:
// Won't take effect until next EGLSurface creation
void setSwapBehavior(SwapBehavior swapBehavior);
- void setSurface(sp<Surface>&& surface);
+ void setSurface(sp<Surface>&& surface, bool enableTimeout = true);
bool pauseSurface();
void setStopped(bool stopped);
bool hasSurface() const { return mNativeSurface.get(); }
diff --git a/libs/hwui/renderthread/RenderProxy.cpp b/libs/hwui/renderthread/RenderProxy.cpp
index 1a1b9dac37f6..edb82f4db16d 100644
--- a/libs/hwui/renderthread/RenderProxy.cpp
+++ b/libs/hwui/renderthread/RenderProxy.cpp
@@ -82,9 +82,10 @@ void RenderProxy::setName(const char* name) {
mRenderThread.queue().runSync([this, name]() { mContext->setName(std::string(name)); });
}
-void RenderProxy::setSurface(const sp<Surface>& surface) {
- mRenderThread.queue().post(
- [this, surf = surface]() mutable { mContext->setSurface(std::move(surf)); });
+void RenderProxy::setSurface(const sp<Surface>& surface, bool enableTimeout) {
+ mRenderThread.queue().post([this, surf = surface, enableTimeout]() mutable {
+ mContext->setSurface(std::move(surf), enableTimeout);
+ });
}
void RenderProxy::allocateBuffers() {
diff --git a/libs/hwui/renderthread/RenderProxy.h b/libs/hwui/renderthread/RenderProxy.h
index a0f08cbd26f9..76cd0ee2a2ce 100644
--- a/libs/hwui/renderthread/RenderProxy.h
+++ b/libs/hwui/renderthread/RenderProxy.h
@@ -69,7 +69,7 @@ public:
ANDROID_API bool loadSystemProperties();
ANDROID_API void setName(const char* name);
- ANDROID_API void setSurface(const sp<Surface>& surface);
+ ANDROID_API void setSurface(const sp<Surface>& surface, bool enableTimeout = true);
ANDROID_API void allocateBuffers();
ANDROID_API bool pause();
ANDROID_API void setStopped(bool stopped);
diff --git a/location/java/android/location/Location.java b/location/java/android/location/Location.java
index 9c36d76cf370..db06399f7376 100644
--- a/location/java/android/location/Location.java
+++ b/location/java/android/location/Location.java
@@ -158,7 +158,10 @@ public class Location implements Parcelable {
* <p>By default time, latitude and longitude are 0, and the location
* has no bearing, altitude, speed, accuracy or extras.
*
- * @param provider the name of the provider that generated this location
+ * @param provider the source that provides the location. It can be of type
+ * {@link LocationManager#GPS_PROVIDER}, {@link LocationManager#NETWORK_PROVIDER},
+ * or {@link LocationManager#PASSIVE_PROVIDER}. You can also define your own
+ * provider string, in which case an empty string is a valid provider.
*/
public Location(String provider) {
mProvider = provider;
diff --git a/location/java/android/location/OnNmeaMessageListener.java b/location/java/android/location/OnNmeaMessageListener.java
index ccf6ce854317..05647bc5237b 100644
--- a/location/java/android/location/OnNmeaMessageListener.java
+++ b/location/java/android/location/OnNmeaMessageListener.java
@@ -28,7 +28,9 @@ public interface OnNmeaMessageListener {
/**
* Called when an NMEA message is received.
* @param message NMEA message
- * @param timestamp milliseconds since January 1, 1970.
+ * @param timestamp Date and time of the location fix, as reported by the GNSS
+ * chipset. The value is specified in milliseconds since 0:00
+ * UTC 1 January 1970.
*/
void onNmeaMessage(String message, long timestamp);
}
diff --git a/media/Android.bp b/media/Android.bp
index a768b81731eb..2f75e4458ef5 100644
--- a/media/Android.bp
+++ b/media/Android.bp
@@ -33,6 +33,8 @@ java_library {
"framework_media_annotation",
"android_system_stubs_current",
],
+
+ plugins: ["java_api_finder"],
}
filegroup {
diff --git a/media/java/android/media/ExifInterface.java b/media/java/android/media/ExifInterface.java
index 5645ba5d7dac..6c723b3a7006 100644
--- a/media/java/android/media/ExifInterface.java
+++ b/media/java/android/media/ExifInterface.java
@@ -1345,7 +1345,9 @@ public class ExifInterface {
private ByteOrder mExifByteOrder = ByteOrder.BIG_ENDIAN;
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
private boolean mHasThumbnail;
- // The following values used for indicating a thumbnail position.
+ private boolean mHasThumbnailStrips;
+ private boolean mAreThumbnailStripsConsecutive;
+ // Used to indicate the position of the thumbnail (includes offset to EXIF data segment).
private int mThumbnailOffset;
private int mThumbnailLength;
private byte[] mThumbnailBytes;
@@ -2043,10 +2045,12 @@ public class ExifInterface {
/**
* Returns the offset and length of thumbnail inside the image file, or
- * {@code null} if there is no thumbnail.
+ * {@code null} if either there is no thumbnail or the thumbnail bytes are stored
+ * non-consecutively.
*
* @return two-element array, the offset in the first value, and length in
- * the second, or {@code null} if no thumbnail was found.
+ * the second, or {@code null} if no thumbnail was found or the thumbnail strips are
+ * not placed consecutively.
* @throws IllegalStateException if {@link #saveAttributes()} has been
* called since the underlying file was initially parsed, since
* that means offsets may have changed.
@@ -2058,10 +2062,12 @@ public class ExifInterface {
}
if (mHasThumbnail) {
+ if (mHasThumbnailStrips && !mAreThumbnailStripsConsecutive) {
+ return null;
+ }
return new long[] { mThumbnailOffset, mThumbnailLength };
- } else {
- return null;
}
+ return null;
}
/**
@@ -2536,10 +2542,9 @@ public class ExifInterface {
final byte[] value = Arrays.copyOfRange(bytes,
IDENTIFIER_EXIF_APP1.length, bytes.length);
- readExifSegment(value, imageType);
-
// Save offset values for createJpegThumbnailBitmap() function
mExifOffset = (int) offset;
+ readExifSegment(value, imageType);
} else if (ArrayUtils.startsWith(bytes, IDENTIFIER_XMP_APP1)) {
// See XMP Specification Part 3: Storage in Files, 1.1.3 JPEG, Table 6
final long offset = start + IDENTIFIER_XMP_APP1.length;
@@ -2843,6 +2848,8 @@ public class ExifInterface {
if (in.read(bytes) != length) {
throw new IOException("Can't read exif");
}
+ // Save offset values for handling thumbnail and attribute offsets.
+ mExifOffset = offset;
readExifSegment(bytes, IFD_TYPE_PRIMARY);
}
@@ -2988,7 +2995,7 @@ public class ExifInterface {
// Write EXIF APP1 segment
dataOutputStream.writeByte(MARKER);
dataOutputStream.writeByte(MARKER_APP1);
- writeExifSegment(dataOutputStream, 6);
+ writeExifSegment(dataOutputStream);
byte[] bytes = new byte[4096];
@@ -3319,7 +3326,7 @@ public class ExifInterface {
continue;
}
- final int bytesOffset = dataInputStream.peek();
+ final int bytesOffset = dataInputStream.peek() + mExifOffset;
final byte[] bytes = new byte[(int) byteCount];
dataInputStream.readFully(bytes);
ExifAttribute attribute = new ExifAttribute(dataFormat, numberOfComponents,
@@ -3451,31 +3458,28 @@ public class ExifInterface {
// The following code limits the size of thumbnail size not to overflow EXIF data area.
thumbnailLength = Math.min(thumbnailLength, in.getLength() - thumbnailOffset);
- if (mMimeType == IMAGE_TYPE_JPEG || mMimeType == IMAGE_TYPE_RAF
- || mMimeType == IMAGE_TYPE_RW2) {
- thumbnailOffset += mExifOffset;
- } else if (mMimeType == IMAGE_TYPE_ORF) {
+ if (mMimeType == IMAGE_TYPE_ORF) {
// Update offset value since RAF files have IFD data preceding MakerNote data.
thumbnailOffset += mOrfMakerNoteOffset;
}
- if (DEBUG) {
- Log.d(TAG, "Setting thumbnail attributes with offset: " + thumbnailOffset
- + ", length: " + thumbnailLength);
- }
if (thumbnailOffset > 0 && thumbnailLength > 0) {
mHasThumbnail = true;
- mThumbnailOffset = thumbnailOffset;
+ mThumbnailOffset = thumbnailOffset + mExifOffset;
mThumbnailLength = thumbnailLength;
mThumbnailCompression = DATA_JPEG;
if (mFilename == null && mAssetInputStream == null
&& mSeekableFileDescriptor == null) {
// Save the thumbnail in memory if the input doesn't support reading again.
- byte[] thumbnailBytes = new byte[thumbnailLength];
- in.seek(thumbnailOffset);
+ byte[] thumbnailBytes = new byte[mThumbnailLength];
+ in.seek(mThumbnailOffset);
in.readFully(thumbnailBytes);
mThumbnailBytes = thumbnailBytes;
}
+ if (DEBUG) {
+ Log.d(TAG, "Setting thumbnail attributes with offset: " + thumbnailOffset
+ + ", length: " + thumbnailLength);
+ }
}
}
}
@@ -3494,12 +3498,16 @@ public class ExifInterface {
long[] stripByteCounts =
convertToLongArray(stripByteCountsAttribute.getValue(mExifByteOrder));
- if (stripOffsets == null) {
- Log.w(TAG, "stripOffsets should not be null.");
+ if (stripOffsets == null || stripOffsets.length == 0) {
+ Log.w(TAG, "stripOffsets should not be null or have zero length.");
return;
}
- if (stripByteCounts == null) {
- Log.w(TAG, "stripByteCounts should not be null.");
+ if (stripByteCounts == null || stripByteCounts.length == 0) {
+ Log.w(TAG, "stripByteCounts should not be null or have zero length.");
+ return;
+ }
+ if (stripOffsets.length != stripByteCounts.length) {
+ Log.w(TAG, "stripOffsets and stripByteCounts should have same length.");
return;
}
@@ -3509,10 +3517,18 @@ public class ExifInterface {
int bytesRead = 0;
int bytesAdded = 0;
+ mHasThumbnail = mHasThumbnailStrips = mAreThumbnailStripsConsecutive = true;
for (int i = 0; i < stripOffsets.length; i++) {
int stripOffset = (int) stripOffsets[i];
int stripByteCount = (int) stripByteCounts[i];
+ // Check if strips are consecutive
+ // TODO: Add test for non-consecutive thumbnail image
+ if (i < stripOffsets.length - 1
+ && stripOffset + stripByteCount != stripOffsets[i + 1]) {
+ mAreThumbnailStripsConsecutive = false;
+ }
+
// Skip to offset
int skipBytes = stripOffset - bytesRead;
if (skipBytes < 0) {
@@ -3531,10 +3547,13 @@ public class ExifInterface {
stripBytes.length);
bytesAdded += stripBytes.length;
}
-
- mHasThumbnail = true;
mThumbnailBytes = totalStripBytes;
- mThumbnailLength = totalStripBytes.length;
+
+ if (mAreThumbnailStripsConsecutive) {
+ // Need to add mExifOffset, which is the offset to the EXIF data segment
+ mThumbnailOffset = (int) stripOffsets[0] + mExifOffset;
+ mThumbnailLength = totalStripBytes.length;
+ }
}
}
@@ -3691,8 +3710,7 @@ public class ExifInterface {
}
// Writes an Exif segment into the given output stream.
- private int writeExifSegment(ByteOrderedDataOutputStream dataOutputStream,
- int exifOffsetFromBeginning) throws IOException {
+ private int writeExifSegment(ByteOrderedDataOutputStream dataOutputStream) throws IOException {
// The following variables are for calculating each IFD tag group size in bytes.
int[] ifdOffsets = new int[EXIF_TAGS.length];
int[] ifdDataSizes = new int[EXIF_TAGS.length];
@@ -3751,6 +3769,8 @@ public class ExifInterface {
}
// Calculate IFD offsets.
+ // 8 bytes are for TIFF headers: 2 bytes (byte order) + 2 bytes (identifier) + 4 bytes
+ // (offset of IFDs)
int position = 8;
for (int ifdType = 0; ifdType < EXIF_TAGS.length; ++ifdType) {
if (!mAttributes[ifdType].isEmpty()) {
@@ -3762,7 +3782,8 @@ public class ExifInterface {
int thumbnailOffset = position;
mAttributes[IFD_TYPE_THUMBNAIL].put(JPEG_INTERCHANGE_FORMAT_TAG.name,
ExifAttribute.createULong(thumbnailOffset, mExifByteOrder));
- mThumbnailOffset = exifOffsetFromBeginning + thumbnailOffset;
+ // Need to add mExifOffset, which is the offset to the EXIF data segment
+ mThumbnailOffset = thumbnailOffset + mExifOffset;
position += mThumbnailLength;
}
diff --git a/media/java/android/media/Image.java b/media/java/android/media/Image.java
index 70a343f4de01..7ba122baeca1 100644
--- a/media/java/android/media/Image.java
+++ b/media/java/android/media/Image.java
@@ -395,7 +395,7 @@ public abstract class Image implements AutoCloseable {
* <p>The row stride for this color plane, in bytes.</p>
*
* <p>This is the distance between the start of two consecutive rows of
- * pixels in the image. Note that row stried is undefined for some formats
+ * pixels in the image. Note that row stride is undefined for some formats
* such as
* {@link android.graphics.ImageFormat#RAW_PRIVATE RAW_PRIVATE},
* and calling getRowStride on images of these formats will
diff --git a/media/java/android/media/ImageWriter.java b/media/java/android/media/ImageWriter.java
index f813d1b68419..7bc2b31b16fa 100644
--- a/media/java/android/media/ImageWriter.java
+++ b/media/java/android/media/ImageWriter.java
@@ -192,13 +192,15 @@ public class ImageWriter implements AutoCloseable {
mMaxImages = maxImages;
- if (format == ImageFormat.UNKNOWN) {
- format = SurfaceUtils.getSurfaceFormat(surface);
- }
// Note that the underlying BufferQueue is working in synchronous mode
// to avoid dropping any buffers.
mNativeContext = nativeInit(new WeakReference<>(this), surface, maxImages, format);
+ // nativeInit internally overrides UNKNOWN format. So does surface format query after
+ // nativeInit and before getEstimatedNativeAllocBytes().
+ if (format == ImageFormat.UNKNOWN) {
+ format = SurfaceUtils.getSurfaceFormat(surface);
+ }
// Estimate the native buffer allocation size and register it so it gets accounted for
// during GC. Note that this doesn't include the buffers required by the buffer queue
// itself and the buffers requested by the producer.
diff --git a/packages/CarSystemUI/res/layout/car_top_navigation_bar_unprovisioned.xml b/packages/CarSystemUI/res/layout/car_top_navigation_bar_unprovisioned.xml
new file mode 100644
index 000000000000..8247211dcb32
--- /dev/null
+++ b/packages/CarSystemUI/res/layout/car_top_navigation_bar_unprovisioned.xml
@@ -0,0 +1,147 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2019 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License
+ -->
+
+<com.android.systemui.statusbar.car.CarNavigationBarView
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:systemui="http://schemas.android.com/apk/res-auto"
+ android:id="@+id/car_top_bar"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:background="@drawable/system_bar_background"
+ android:orientation="vertical">
+
+ <RelativeLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_weight="1">
+
+ <FrameLayout
+ android:id="@+id/left_hvac_container"
+ android:layout_width="wrap_content"
+ android:layout_height="match_parent"
+ android:layout_alignParentStart="true"
+ >
+
+ <com.android.systemui.statusbar.car.CarNavigationButton
+ android:id="@+id/hvacleft"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:background="@null"
+ systemui:broadcast="true"
+ systemui:intent="intent:#Intent;action=android.car.intent.action.TOGGLE_HVAC_CONTROLS;end"
+ />
+
+ <com.android.systemui.statusbar.hvac.AnimatedTemperatureView
+ android:id="@+id/lefttext"
+ android:layout_width="wrap_content"
+ android:layout_height="match_parent"
+ android:paddingStart="@*android:dimen/car_padding_4"
+ android:paddingEnd="16dp"
+ android:gravity="center_vertical|start"
+ android:minEms="4"
+ android:textAppearance="@style/TextAppearance.CarStatus"
+ systemui:hvacAreaId="49"
+ systemui:hvacMaxText="@string/hvac_max_text"
+ systemui:hvacMaxValue="@dimen/hvac_max_value"
+ systemui:hvacMinText="@string/hvac_min_text"
+ systemui:hvacMinValue="@dimen/hvac_min_value"
+ systemui:hvacPivotOffset="60dp"
+ systemui:hvacPropertyId="358614275"
+ systemui:hvacTempFormat="%.0f\u00B0"
+ />
+ </FrameLayout>
+
+ <FrameLayout
+ android:id="@+id/clock_container"
+ android:layout_width="wrap_content"
+ android:layout_height="match_parent"
+ android:layout_centerInParent="true">
+ <com.android.systemui.statusbar.car.CarNavigationButton
+ android:id="@+id/qs"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:background="@null"/>
+ <com.android.systemui.statusbar.policy.Clock
+ android:id="@+id/clock"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center"
+ android:elevation="5dp"
+ android:singleLine="true"
+ android:textAppearance="@style/TextAppearance.StatusBar.Clock"/>
+ </FrameLayout>
+
+ <LinearLayout
+ android:id="@+id/system_icon_area"
+ android:layout_width="wrap_content"
+ android:layout_height="match_parent"
+ android:layout_centerHorizontal="true"
+ android:layout_centerVertical="true"
+ android:layout_toEndOf="@+id/clock_container"
+ android:paddingStart="@*android:dimen/car_padding_1"
+ android:gravity="center_vertical"
+ android:orientation="horizontal"
+ >
+
+ <include
+ layout="@layout/system_icons"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:paddingStart="4dp"
+ android:gravity="center_vertical"
+ />
+ </LinearLayout>
+
+ <FrameLayout
+ android:id="@+id/right_hvac_container"
+ android:layout_width="wrap_content"
+ android:layout_height="match_parent"
+ android:layout_alignParentEnd="true"
+ >
+
+ <com.android.systemui.statusbar.car.CarNavigationButton
+ android:id="@+id/hvacright"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:background="@null"
+ systemui:broadcast="true"
+ systemui:intent="intent:#Intent;action=android.car.intent.action.TOGGLE_HVAC_CONTROLS;end"
+ />
+
+ <com.android.systemui.statusbar.hvac.AnimatedTemperatureView
+ android:id="@+id/righttext"
+ android:layout_width="wrap_content"
+ android:layout_height="match_parent"
+ android:paddingStart="16dp"
+ android:paddingEnd="@*android:dimen/car_padding_4"
+ android:gravity="center_vertical|end"
+ android:minEms="4"
+ android:textAppearance="@style/TextAppearance.CarStatus"
+ systemui:hvacAreaId="68"
+ systemui:hvacMaxText="@string/hvac_max_text"
+ systemui:hvacMaxValue="@dimen/hvac_max_value"
+ systemui:hvacMinText="@string/hvac_min_text"
+ systemui:hvacMinValue="@dimen/hvac_min_value"
+ systemui:hvacPivotOffset="60dp"
+ systemui:hvacPropertyId="358614275"
+ systemui:hvacTempFormat="%.0f\u00B0"
+ />
+ </FrameLayout>
+ </RelativeLayout>
+
+</com.android.systemui.statusbar.car.CarNavigationBarView>
diff --git a/packages/CarSystemUI/res/layout/super_status_bar.xml b/packages/CarSystemUI/res/layout/super_status_bar.xml
index e1bcc2e5f86c..7fee8051c472 100644
--- a/packages/CarSystemUI/res/layout/super_status_bar.xml
+++ b/packages/CarSystemUI/res/layout/super_status_bar.xml
@@ -42,14 +42,6 @@
</com.android.systemui.statusbar.BackDropView>
<com.android.systemui.statusbar.ScrimView
- android:id="@+id/scrim_for_bubble"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:importantForAccessibility="no"
- sysui:ignoreRightInset="true"
- />
-
- <com.android.systemui.statusbar.ScrimView
android:id="@+id/scrim_behind"
android:layout_width="match_parent"
android:layout_height="match_parent"
@@ -69,10 +61,10 @@
android:visibility="gone"
/>
- <include layout="@layout/car_top_navigation_bar"
+ <FrameLayout
+ android:id="@+id/car_top_navigation_bar_container"
android:layout_width="match_parent"
- android:layout_height="wrap_content"
- />
+ android:layout_height="wrap_content"/>
</LinearLayout>
<include layout="@layout/brightness_mirror"/>
diff --git a/packages/CarSystemUI/res/values/config.xml b/packages/CarSystemUI/res/values/config.xml
index 467c4a41893d..cbf22870af96 100644
--- a/packages/CarSystemUI/res/values/config.xml
+++ b/packages/CarSystemUI/res/values/config.xml
@@ -40,4 +40,19 @@
slots that may be reused for things like IME control. -->
<integer name="config_maxNotificationIcons">0</integer>
+ <!--
+ Initial alpha percent value for the background when the notification
+ shade is open. Should be a number between, and inclusive, 0 and 100.
+ If the number is 0, then the background alpha starts off fully
+ transparent. If the number if 100, then the background alpha starts off
+ fully opaque. -->
+ <integer name="config_initialNotificationBackgroundAlpha">0</integer>
+ <!--
+ Final alpha percent value for the background when the notification
+ shade is fully open. Should be a number between, and inclusive, 0 and
+ 100. If this value is smaller than
+ config_initialNotificationBackgroundAlpha, the background will default
+ to a constant alpha percent value using the initial alpha. -->
+ <integer name="config_finalNotificationBackgroundAlpha">100</integer>
+
</resources>
diff --git a/packages/CarSystemUI/res/values/integers_car.xml b/packages/CarSystemUI/res/values/integers_car.xml
index fb67b302a4ae..ba3d329ef32a 100644
--- a/packages/CarSystemUI/res/values/integers_car.xml
+++ b/packages/CarSystemUI/res/values/integers_car.xml
@@ -34,4 +34,9 @@
<!-- The delay before the unlock dialog pops up -->
<integer name="unlock_dialog_delay_ms">0</integer>
+ <!-- Timeout values in milliseconds for displaying volume dialog-->
+ <integer name="car_volume_dialog_display_normal_timeout">3000</integer>
+ <integer name="car_volume_dialog_display_hovering_timeout">16000</integer>
+ <integer name="car_volume_dialog_display_expanded_normal_timeout">6000</integer>
+ <integer name="car_volume_dialog_display_expanded_hovering_timeout">32000</integer>
</resources>
diff --git a/packages/CarSystemUI/src/com/android/systemui/CarSystemUIFactory.java b/packages/CarSystemUI/src/com/android/systemui/CarSystemUIFactory.java
index c7654e81e0b1..a4235540e337 100644
--- a/packages/CarSystemUI/src/com/android/systemui/CarSystemUIFactory.java
+++ b/packages/CarSystemUI/src/com/android/systemui/CarSystemUIFactory.java
@@ -20,6 +20,7 @@ import android.content.Context;
import com.android.internal.widget.LockPatternUtils;
import com.android.keyguard.ViewMediatorCallback;
+import com.android.systemui.car.CarServiceProvider;
import com.android.systemui.statusbar.car.CarFacetButtonController;
import com.android.systemui.statusbar.car.CarStatusBarKeyguardViewManager;
import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager;
@@ -36,6 +37,7 @@ import dagger.Component;
public class CarSystemUIFactory extends SystemUIFactory {
private CarDependencyComponent mCarDependencyComponent;
+ private CarServiceProvider mCarServiceProvider;
@Override
protected SystemUIRootComponent buildSystemUIRootComponent(Context context) {
@@ -48,6 +50,14 @@ public class CarSystemUIFactory extends SystemUIFactory {
.build();
}
+ /** Gets a {@link CarServiceProvider}. */
+ public CarServiceProvider getCarServiceProvider(Context context) {
+ if (mCarServiceProvider == null) {
+ mCarServiceProvider = new CarServiceProvider(context);
+ }
+ return mCarServiceProvider;
+ }
+
public CarDependencyComponent getCarDependencyComponent() {
return mCarDependencyComponent;
}
diff --git a/packages/CarSystemUI/src/com/android/systemui/car/CarNotificationInterruptionStateProvider.java b/packages/CarSystemUI/src/com/android/systemui/car/CarNotificationInterruptionStateProvider.java
index afd722ba0091..447e579ece42 100644
--- a/packages/CarSystemUI/src/com/android/systemui/car/CarNotificationInterruptionStateProvider.java
+++ b/packages/CarSystemUI/src/com/android/systemui/car/CarNotificationInterruptionStateProvider.java
@@ -22,6 +22,7 @@ import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.statusbar.notification.NotificationFilter;
import com.android.systemui.statusbar.notification.NotificationInterruptionStateProvider;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
+import com.android.systemui.statusbar.policy.BatteryController;
import javax.inject.Inject;
import javax.inject.Singleton;
@@ -34,8 +35,9 @@ public class CarNotificationInterruptionStateProvider extends
@Inject
public CarNotificationInterruptionStateProvider(Context context,
NotificationFilter filter,
- StatusBarStateController stateController) {
- super(context, filter, stateController);
+ StatusBarStateController stateController,
+ BatteryController batteryController) {
+ super(context, filter, stateController, batteryController);
}
@Override
diff --git a/packages/CarSystemUI/src/com/android/systemui/car/CarServiceProvider.java b/packages/CarSystemUI/src/com/android/systemui/car/CarServiceProvider.java
new file mode 100644
index 000000000000..9ee368ee497f
--- /dev/null
+++ b/packages/CarSystemUI/src/com/android/systemui/car/CarServiceProvider.java
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.systemui.car;
+
+import android.car.Car;
+import android.car.Car.CarServiceLifecycleListener;
+import android.content.Context;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Connects to the car service a single time for shared use across all of system ui.
+ */
+public class CarServiceProvider {
+
+ private final Context mContext;
+ private final List<CarServiceLifecycleListener> mListeners = new ArrayList<>();
+ private Car mCar;
+
+ public CarServiceProvider(Context context) {
+ mContext = context;
+ mCar = Car.createCar(mContext, /* handler= */ null, Car.CAR_WAIT_TIMEOUT_DO_NOT_WAIT,
+ (car, ready) -> {
+ mCar = car;
+
+ synchronized (mListeners) {
+ for (CarServiceLifecycleListener listener : mListeners) {
+ listener.onLifecycleChanged(mCar, ready);
+ }
+ }
+ });
+ }
+
+ /**
+ * Let's other components hook into the connection to the car service. If we're already
+ * connected
+ * to the car service, the callback is immediately triggered.
+ */
+ public void addListener(CarServiceLifecycleListener listener) {
+ if (mCar.isConnected()) {
+ listener.onLifecycleChanged(mCar, /* ready= */ true);
+ }
+ mListeners.add(listener);
+ }
+}
diff --git a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarBatteryController.java b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarBatteryController.java
index 58f80a4ed968..d79849ccafc6 100644
--- a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarBatteryController.java
+++ b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarBatteryController.java
@@ -257,6 +257,11 @@ public class CarBatteryController extends BroadcastReceiver implements BatteryCo
return false;
}
+ @Override
+ public boolean isAodPowerSave() {
+ return false;
+ }
+
private void notifyBatteryLevelChanged() {
for (int i = 0, size = mChangeCallbacks.size(); i < size; i++) {
mChangeCallbacks.get(i)
diff --git a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java
index 25191f6a9617..db98c36b8c1f 100644
--- a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java
+++ b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java
@@ -23,16 +23,20 @@ import android.annotation.Nullable;
import android.app.ActivityManager;
import android.app.ActivityTaskManager;
import android.car.Car;
+import android.car.CarNotConnectedException;
import android.car.drivingstate.CarDrivingStateEvent;
import android.car.drivingstate.CarUxRestrictionsManager;
import android.car.hardware.power.CarPowerManager.CarPowerStateListener;
-import android.car.trust.CarTrustAgentEnrollmentManager;
+import android.car.media.CarAudioManager;
import android.content.Context;
import android.graphics.PixelFormat;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
import android.inputmethodservice.InputMethodService;
+import android.media.AudioManager;
+import android.os.Handler;
import android.os.IBinder;
+import android.os.Looper;
import android.util.Log;
import android.view.Display;
import android.view.GestureDetector;
@@ -61,26 +65,46 @@ import com.android.keyguard.KeyguardUpdateMonitor;
import com.android.systemui.BatteryMeterView;
import com.android.systemui.CarSystemUIFactory;
import com.android.systemui.Dependency;
+import com.android.systemui.ForegroundServiceController;
import com.android.systemui.Prefs;
import com.android.systemui.R;
import com.android.systemui.SystemUIFactory;
import com.android.systemui.classifier.FalsingLog;
+import com.android.systemui.colorextraction.SysuiColorExtractor;
import com.android.systemui.fragments.FragmentHostManager;
import com.android.systemui.keyguard.ScreenLifecycle;
+import com.android.systemui.keyguard.WakefulnessLifecycle;
import com.android.systemui.plugins.FalsingManager;
import com.android.systemui.plugins.qs.QS;
import com.android.systemui.qs.car.CarQSFragment;
import com.android.systemui.shared.system.ActivityManagerWrapper;
import com.android.systemui.shared.system.TaskStackChangeListener;
import com.android.systemui.statusbar.FlingAnimationUtils;
+import com.android.systemui.statusbar.NavigationBarController;
+import com.android.systemui.statusbar.NotificationListener;
+import com.android.systemui.statusbar.NotificationLockscreenUserManager;
+import com.android.systemui.statusbar.NotificationMediaManager;
+import com.android.systemui.statusbar.NotificationRemoteInputManager;
+import com.android.systemui.statusbar.NotificationViewHierarchyManager;
import com.android.systemui.statusbar.StatusBarState;
import com.android.systemui.statusbar.car.hvac.HvacController;
import com.android.systemui.statusbar.car.hvac.TemperatureView;
+import com.android.systemui.statusbar.notification.NotificationEntryManager;
+import com.android.systemui.statusbar.notification.VisualStabilityManager;
+import com.android.systemui.statusbar.notification.logging.NotificationLogger;
+import com.android.systemui.statusbar.notification.row.NotificationGutsManager;
import com.android.systemui.statusbar.phone.CollapsedStatusBarFragment;
+import com.android.systemui.statusbar.phone.LightBarController;
+import com.android.systemui.statusbar.phone.NotificationGroupAlertTransferHelper;
+import com.android.systemui.statusbar.phone.NotificationGroupManager;
import com.android.systemui.statusbar.phone.StatusBar;
+import com.android.systemui.statusbar.phone.StatusBarIconController;
import com.android.systemui.statusbar.policy.BatteryController;
import com.android.systemui.statusbar.policy.DeviceProvisionedController;
+import com.android.systemui.statusbar.policy.KeyguardMonitor;
import com.android.systemui.statusbar.policy.UserSwitcherController;
+import com.android.systemui.statusbar.policy.ZenModeController;
+import com.android.systemui.volume.VolumeUI;
import java.io.FileDescriptor;
import java.io.PrintWriter;
@@ -101,6 +125,9 @@ public class CarStatusBar extends StatusBar implements CarBatteryController.Batt
private float mOpeningVelocity = DEFAULT_FLING_VELOCITY;
private float mClosingVelocity = DEFAULT_FLING_VELOCITY;
+ private float mBackgroundAlphaDiff;
+ private float mInitialBackgroundAlpha;
+
private TaskStackListenerImpl mTaskStackListener;
private FullscreenUserSwitcher mFullscreenUserSwitcher;
@@ -109,9 +136,11 @@ public class CarStatusBar extends StatusBar implements CarBatteryController.Batt
private BatteryMeterView mBatteryMeterView;
private Drawable mNotificationPanelBackground;
+ private ViewGroup mTopNavigationBarContainer;
private ViewGroup mNavigationBarWindow;
private ViewGroup mLeftNavigationBarWindow;
private ViewGroup mRightNavigationBarWindow;
+ private CarNavigationBarView mTopNavigationBarView;
private CarNavigationBarView mNavigationBarView;
private CarNavigationBarView mLeftNavigationBarView;
private CarNavigationBarView mRightNavigationBarView;
@@ -123,7 +152,7 @@ public class CarStatusBar extends StatusBar implements CarBatteryController.Batt
private CarFacetButtonController mCarFacetButtonController;
private ActivityManagerWrapper mActivityManagerWrapper;
private DeviceProvisionedController mDeviceProvisionedController;
- private boolean mDeviceIsProvisioned = true;
+ private boolean mDeviceIsSetUpForUser = true;
private HvacController mHvacController;
private DrivingStateHelper mDrivingStateHelper;
private PowerManagerHelper mPowerManagerHelper;
@@ -132,6 +161,8 @@ public class CarStatusBar extends StatusBar implements CarBatteryController.Batt
private NotificationDataManager mNotificationDataManager;
private NotificationClickHandlerFactory mNotificationClickHandlerFactory;
private ScreenLifecycle mScreenLifecycle;
+ private CarAudioManager mCarAudioManager;
+ private VolumeUI mVolumeUI;
// The container for the notifications.
private CarNotificationView mNotificationView;
@@ -144,6 +175,9 @@ public class CarStatusBar extends StatusBar implements CarBatteryController.Batt
private boolean mNotificationListAtBottom;
// Was the notification list at the bottom when the user first touched the screen
private boolean mNotificationListAtBottomAtTimeOfTouch;
+ // To be attached to the top navigation bar (i.e. status bar) to pull down the notification
+ // panel.
+ private View.OnTouchListener mTopNavBarNotificationTouchListener;
// To be attached to the navigation bars such that they can close the notification panel if
// it's open.
private View.OnTouchListener mNavBarNotificationTouchListener;
@@ -174,6 +208,8 @@ public class CarStatusBar extends StatusBar implements CarBatteryController.Batt
private boolean mHideNavBarForKeyboard;
private boolean mBottomNavBarVisible;
+ private CarUxRestrictionManagerWrapper mCarUxRestrictionManagerWrapper;
+
private final CarPowerStateListener mCarPowerStateListener =
(int state) -> {
// When the car powers on, clear all notifications and mute/unread states.
@@ -188,12 +224,38 @@ public class CarStatusBar extends StatusBar implements CarBatteryController.Batt
}
};
+ private final CarAudioManager.CarVolumeCallback mVolumeChangeCallback =
+ new CarAudioManager.CarVolumeCallback() {
+ @Override
+ public void onGroupVolumeChanged(int zoneId, int groupId, int flags) {
+ if (mVolumeUI == null && (flags & AudioManager.FLAG_SHOW_UI) != 0) {
+ new Handler(Looper.getMainLooper()).post(() -> {
+ // Initialize Volume UI
+ mVolumeUI = new VolumeUI();
+ mVolumeUI.mComponents = mComponents;
+ mVolumeUI.mContext = mContext;
+ mVolumeUI.start();
+ });
+ mCarAudioManager.unregisterCarVolumeCallback(mVolumeChangeCallback);
+ }
+ }
+
+ @Override
+ public void onMasterMuteChanged(int zoneId, int flags) {
+ // ignored
+ }
+ };
+
@Override
public void start() {
+ // Non blocking call to connect to car service. Call this early so that we'll be connected
+ // asap.
+ ((CarSystemUIFactory) SystemUIFactory.getInstance()).getCarServiceProvider(mContext);
+
// get the provisioned state before calling the parent class since it's that flow that
// builds the nav bar
mDeviceProvisionedController = Dependency.get(DeviceProvisionedController.class);
- mDeviceIsProvisioned = mDeviceProvisionedController.isDeviceProvisioned();
+ mDeviceIsSetUpForUser = mDeviceProvisionedController.isCurrentUserSetup();
// Keyboard related setup, before nav bars are created.
mHideNavBarForKeyboard = mContext.getResources().getBoolean(
@@ -205,6 +267,29 @@ public class CarStatusBar extends StatusBar implements CarBatteryController.Batt
mScreenLifecycle = Dependency.get(ScreenLifecycle.class);
mScreenLifecycle.addObserver(mScreenObserver);
+ // Need to initialize HVAC controller before calling super.start - before system bars are
+ // created.
+ mHvacController = new HvacController(mContext);
+
+ // Notification bar related setup.
+ mInitialBackgroundAlpha = (float) mContext.getResources().getInteger(
+ R.integer.config_initialNotificationBackgroundAlpha) / 100;
+ if (mInitialBackgroundAlpha < 0 || mInitialBackgroundAlpha > 100) {
+ throw new RuntimeException(
+ "Unable to setup notification bar due to incorrect initial background alpha"
+ + " percentage");
+ }
+ float finalBackgroundAlpha = Math.max(
+ mInitialBackgroundAlpha,
+ (float) mContext.getResources().getInteger(
+ R.integer.config_finalNotificationBackgroundAlpha) / 100);
+ if (finalBackgroundAlpha < 0 || finalBackgroundAlpha > 100) {
+ throw new RuntimeException(
+ "Unable to setup notification bar due to incorrect final background alpha"
+ + " percentage");
+ }
+ mBackgroundAlphaDiff = finalBackgroundAlpha - mInitialBackgroundAlpha;
+
super.start();
mTaskStackListener = new TaskStackListenerImpl();
mActivityManagerWrapper = ActivityManagerWrapper.getInstance();
@@ -223,25 +308,22 @@ public class CarStatusBar extends StatusBar implements CarBatteryController.Batt
mHvacController.connectToCarService();
- CarSystemUIFactory factory = SystemUIFactory.getInstance();
- if (!mDeviceIsProvisioned) {
- mDeviceProvisionedController.addCallback(
- new DeviceProvisionedController.DeviceProvisionedListener() {
- @Override
- public void onDeviceProvisionedChanged() {
- mHandler.post(() -> {
- // on initial boot we are getting a call even though the value
- // is the same so we are confirming the reset is needed
- boolean deviceProvisioned =
- mDeviceProvisionedController.isDeviceProvisioned();
- if (mDeviceIsProvisioned != deviceProvisioned) {
- mDeviceIsProvisioned = deviceProvisioned;
- restartNavBars();
- }
- });
- }
- });
- }
+ mDeviceProvisionedController.addCallback(
+ new DeviceProvisionedController.DeviceProvisionedListener() {
+ @Override
+ public void onUserSetupChanged() {
+ mHandler.post(() -> restartNavBarsIfNecessary());
+ }
+
+ @Override
+ public void onUserSwitched() {
+ mHandler.post(() -> restartNavBarsIfNecessary());
+ }
+ });
+
+ // Used by onDrivingStateChanged and it can be called inside
+ // DrivingStateHelper.connectToCarService()
+ mSwitchToGuestTimer = new SwitchToGuestTimer(mContext);
// Register a listener for driving state changes.
mDrivingStateHelper = new DrivingStateHelper(mContext, this::onDrivingStateChanged);
@@ -250,7 +332,68 @@ public class CarStatusBar extends StatusBar implements CarBatteryController.Batt
mPowerManagerHelper = new PowerManagerHelper(mContext, mCarPowerStateListener);
mPowerManagerHelper.connectToCarService();
- mSwitchToGuestTimer = new SwitchToGuestTimer(mContext);
+ ((CarSystemUIFactory) SystemUIFactory.getInstance()).getCarServiceProvider(mContext)
+ .addListener((car, ready) -> {
+ if (!ready || mCarAudioManager != null) {
+ return;
+ }
+ try {
+ mCarAudioManager = (CarAudioManager) car.getCarManager(Car.AUDIO_SERVICE);
+ Log.d(TAG, "Registering mVolumeChangeCallback.");
+ // This volume call back is never unregistered because CarStatusBar is
+ // never destroyed.
+ mCarAudioManager.registerCarVolumeCallback(mVolumeChangeCallback);
+ } catch (CarNotConnectedException e) {
+ Log.wtf(TAG, " mVolumeChangeCallback failed to connect to car ", e);
+ }
+ });
+ }
+
+ private void restartNavBarsIfNecessary() {
+ boolean currentUserSetup = mDeviceProvisionedController.isCurrentUserSetup();
+ if (mDeviceIsSetUpForUser != currentUserSetup) {
+ mDeviceIsSetUpForUser = currentUserSetup;
+ restartNavBars();
+ }
+ }
+
+ @Override
+ protected void getDependencies() {
+ // Keyguard
+ mKeyguardMonitor = Dependency.get(KeyguardMonitor.class);
+ mWakefulnessLifecycle = Dependency.get(WakefulnessLifecycle.class);
+ mScreenLifecycle = Dependency.get(ScreenLifecycle.class);
+
+ // Policy
+ mZenController = Dependency.get(ZenModeController.class);
+
+ // Icon
+ mIconController = Dependency.get(StatusBarIconController.class);
+ mLightBarController = Dependency.get(LightBarController.class);
+
+ // Notifications
+ mEntryManager = Dependency.get(NotificationEntryManager.class);
+ mForegroundServiceController = Dependency.get(ForegroundServiceController.class);
+ mGroupAlertTransferHelper = Dependency.get(NotificationGroupAlertTransferHelper.class);
+ mGroupManager = Dependency.get(NotificationGroupManager.class);
+ mGutsManager = Dependency.get(NotificationGutsManager.class);
+ mLockscreenUserManager = Dependency.get(NotificationLockscreenUserManager.class);
+ mMediaManager = Dependency.get(NotificationMediaManager.class);
+ mNotificationListener = Dependency.get(NotificationListener.class);
+ mNotificationLogger = Dependency.get(NotificationLogger.class);
+ mRemoteInputManager = Dependency.get(NotificationRemoteInputManager.class);
+ mViewHierarchyManager = Dependency.get(NotificationViewHierarchyManager.class);
+ mVisualStabilityManager = Dependency.get(VisualStabilityManager.class);
+
+ // Others
+ mColorExtractor = Dependency.get(SysuiColorExtractor.class);
+ mNavigationBarController = Dependency.get(NavigationBarController.class);
+ mUserSwitcherController = Dependency.get(UserSwitcherController.class);
+ }
+
+ @Override
+ protected void setUpQuickSettingsTilePanel() {
+ // ignore.
}
/**
@@ -261,8 +404,8 @@ public class CarStatusBar extends StatusBar implements CarBatteryController.Batt
// remove and reattach all hvac components such that we don't keep a reference to unused
// ui elements
mHvacController.removeAllComponents();
- addTemperatureViewToController(mStatusBarWindow);
mCarFacetButtonController.removeAll();
+
if (mNavigationBarWindow != null) {
mNavigationBarWindow.removeAllViews();
mNavigationBarView = null;
@@ -356,7 +499,6 @@ public class CarStatusBar extends StatusBar implements CarBatteryController.Batt
@Override
protected void makeStatusBarView(@Nullable RegisterStatusBarResult result) {
super.makeStatusBarView(result);
- mHvacController = new HvacController(mContext);
CarSystemUIFactory factory = SystemUIFactory.getInstance();
mCarFacetButtonController = factory.getCarDependencyComponent()
@@ -381,7 +523,8 @@ public class CarStatusBar extends StatusBar implements CarBatteryController.Batt
* touch listeners needed for opening and closing the notification panel
*/
private void connectNotificationsUI() {
- // Attached to the status bar to detect pull down of the notification shade.
+ // Attached to the top navigation bar (i.e. status bar) to detect pull down of the
+ // notification shade.
GestureDetector openGestureDetector = new GestureDetector(mContext,
new OpenNotificationGestureListener() {
@Override
@@ -414,6 +557,18 @@ public class CarStatusBar extends StatusBar implements CarBatteryController.Batt
GestureDetector handleBarCloseNotificationGestureDetector = new GestureDetector(mContext,
new HandleBarCloseNotificationGestureListener());
+ mTopNavBarNotificationTouchListener = (v, event) -> {
+ if (!mDeviceIsSetUpForUser) {
+ return true;
+ }
+ boolean consumed = openGestureDetector.onTouchEvent(event);
+ if (consumed) {
+ return true;
+ }
+ maybeCompleteAnimation(event);
+ return true;
+ };
+
mNavBarNotificationTouchListener =
(v, event) -> {
boolean consumed = navBarCloseNotificationGestureDetector.onTouchEvent(event);
@@ -424,21 +579,6 @@ public class CarStatusBar extends StatusBar implements CarBatteryController.Batt
return true;
};
- // The following are the ui elements that the user would call the status bar.
- // This will set the status bar so it they can make call backs.
- CarNavigationBarView topBar = mStatusBarWindow.findViewById(R.id.car_top_bar);
- topBar.setStatusBar(this);
- topBar.setStatusBarWindowTouchListener((v1, event1) -> {
-
- boolean consumed = openGestureDetector.onTouchEvent(event1);
- if (consumed) {
- return true;
- }
- maybeCompleteAnimation(event1);
- return true;
- }
- );
-
mNotificationClickHandlerFactory = new NotificationClickHandlerFactory(
mBarService,
launchResult -> {
@@ -447,33 +587,12 @@ public class CarStatusBar extends StatusBar implements CarBatteryController.Batt
animateCollapsePanels();
}
});
- Car car = Car.createCar(mContext);
- CarUxRestrictionsManager carUxRestrictionsManager = (CarUxRestrictionsManager)
- car.getCarManager(Car.CAR_UX_RESTRICTION_SERVICE);
+
CarNotificationListener carNotificationListener = new CarNotificationListener();
- CarUxRestrictionManagerWrapper carUxRestrictionManagerWrapper =
- new CarUxRestrictionManagerWrapper();
- carUxRestrictionManagerWrapper.setCarUxRestrictionsManager(carUxRestrictionsManager);
+ mCarUxRestrictionManagerWrapper = new CarUxRestrictionManagerWrapper();
mNotificationDataManager = new NotificationDataManager();
- mNotificationDataManager.setOnUnseenCountUpdateListener(
- () -> {
- if (mNavigationBarView != null && mNotificationDataManager != null) {
- Boolean hasUnseen =
- mNotificationDataManager.getUnseenNotificationCount() > 0;
- if (mNavigationBarView != null) {
- mNavigationBarView.toggleNotificationUnseenIndicator(hasUnseen);
- }
-
- if (mLeftNavigationBarView != null) {
- mLeftNavigationBarView.toggleNotificationUnseenIndicator(hasUnseen);
- }
-
- if (mRightNavigationBarView != null) {
- mRightNavigationBarView.toggleNotificationUnseenIndicator(hasUnseen);
- }
- }
- });
+ mNotificationDataManager.setOnUnseenCountUpdateListener(this::onUnseenCountUpdate);
mEnableHeadsUpNotificationWhenNotificationShadeOpen = mContext.getResources().getBoolean(
R.bool.config_enableHeadsUpNotificationWhenNotificationShadeOpen);
@@ -482,7 +601,7 @@ public class CarStatusBar extends StatusBar implements CarBatteryController.Batt
mNotificationClickHandlerFactory, mNotificationDataManager);
mNotificationClickHandlerFactory.setNotificationDataManager(mNotificationDataManager);
- carNotificationListener.registerAsSystemService(mContext, carUxRestrictionManagerWrapper,
+ carNotificationListener.registerAsSystemService(mContext, mCarUxRestrictionManagerWrapper,
carHeadsUpNotificationManager, mNotificationDataManager);
mNotificationView = mStatusBarWindow.findViewById(R.id.notification_view);
@@ -577,14 +696,48 @@ public class CarStatusBar extends StatusBar implements CarBatteryController.Batt
return handled || isTracking;
}
});
+ ((CarSystemUIFactory) SystemUIFactory.getInstance()).getCarServiceProvider(mContext)
+ .addListener((car, ready) -> {
+ if (!ready) {
+ return;
+ }
+ CarUxRestrictionsManager carUxRestrictionsManager =
+ (CarUxRestrictionsManager)
+ car.getCarManager(Car.CAR_UX_RESTRICTION_SERVICE);
+ mCarUxRestrictionManagerWrapper.setCarUxRestrictionsManager(
+ carUxRestrictionsManager);
+
+ mNotificationViewController = new NotificationViewController(
+ mNotificationView,
+ PreprocessingManager.getInstance(mContext),
+ carNotificationListener,
+ mCarUxRestrictionManagerWrapper,
+ mNotificationDataManager);
+ mNotificationViewController.enable();
+ });
+ }
- mNotificationViewController = new NotificationViewController(
- mNotificationView,
- PreprocessingManager.getInstance(mContext),
- carNotificationListener,
- carUxRestrictionManagerWrapper,
- mNotificationDataManager);
- mNotificationViewController.enable();
+ /**
+ * This method is called whenever there is an update to the number of unseen notifications.
+ * This method can be extended by OEMs to customize the desired logic.
+ */
+ protected void onUnseenCountUpdate() {
+ if (mNavigationBarView != null && mNotificationDataManager != null) {
+ Boolean hasUnseen =
+ mNotificationDataManager.getUnseenNotificationCount() > 0;
+
+ if (mNavigationBarView != null) {
+ mNavigationBarView.toggleNotificationUnseenIndicator(hasUnseen);
+ }
+
+ if (mLeftNavigationBarView != null) {
+ mLeftNavigationBarView.toggleNotificationUnseenIndicator(hasUnseen);
+ }
+
+ if (mRightNavigationBarView != null) {
+ mRightNavigationBarView.toggleNotificationUnseenIndicator(hasUnseen);
+ }
+ }
}
/**
@@ -745,23 +898,30 @@ public class CarStatusBar extends StatusBar implements CarBatteryController.Batt
}
private void buildNavBarContent() {
+ // Always build top bar.
+ buildTopBar((mDeviceIsSetUpForUser) ? R.layout.car_top_navigation_bar :
+ R.layout.car_top_navigation_bar_unprovisioned);
+
if (mShowBottom) {
- buildBottomBar((mDeviceIsProvisioned) ? R.layout.car_navigation_bar :
+ buildBottomBar((mDeviceIsSetUpForUser) ? R.layout.car_navigation_bar :
R.layout.car_navigation_bar_unprovisioned);
}
if (mShowLeft) {
- buildLeft((mDeviceIsProvisioned) ? R.layout.car_left_navigation_bar :
+ buildLeft((mDeviceIsSetUpForUser) ? R.layout.car_left_navigation_bar :
R.layout.car_left_navigation_bar_unprovisioned);
}
if (mShowRight) {
- buildRight((mDeviceIsProvisioned) ? R.layout.car_right_navigation_bar :
+ buildRight((mDeviceIsSetUpForUser) ? R.layout.car_right_navigation_bar :
R.layout.car_right_navigation_bar_unprovisioned);
}
}
private void buildNavBarWindows() {
+ mTopNavigationBarContainer = mStatusBarWindow
+ .findViewById(R.id.car_top_navigation_bar_container);
+
if (mShowBottom) {
mNavigationBarWindow = (ViewGroup) View.inflate(mContext,
R.layout.navigation_bar_window, null);
@@ -794,15 +954,25 @@ public class CarStatusBar extends StatusBar implements CarBatteryController.Batt
}
boolean isKeyboardVisible = (vis & InputMethodService.IME_VISIBLE) != 0;
- if (!isKeyboardVisible) {
- attachBottomNavBarWindow();
- } else {
- detachBottomNavBarWindow();
- }
+ showBottomNavBarWindow(isKeyboardVisible);
}
private void attachNavBarWindows() {
- attachBottomNavBarWindow();
+ if (mShowBottom && !mBottomNavBarVisible) {
+ mBottomNavBarVisible = true;
+
+ WindowManager.LayoutParams lp = new WindowManager.LayoutParams(
+ LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT,
+ WindowManager.LayoutParams.TYPE_NAVIGATION_BAR,
+ WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
+ | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
+ | WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH
+ | WindowManager.LayoutParams.FLAG_SPLIT_TOUCH,
+ PixelFormat.TRANSLUCENT);
+ lp.setTitle("CarNavigationBar");
+ lp.windowAnimations = 0;
+ mWindowManager.addView(mNavigationBarWindow, lp);
+ }
if (mShowLeft) {
int width = mContext.getResources().getDimensionPixelSize(
@@ -840,47 +1010,32 @@ public class CarStatusBar extends StatusBar implements CarBatteryController.Batt
}
}
- /**
- * Attaches the bottom nav bar window. Can be extended to modify the specific behavior of
- * attaching the bottom nav bar.
- */
- protected void attachBottomNavBarWindow() {
+ private void showBottomNavBarWindow(boolean isKeyboardVisible) {
if (!mShowBottom) {
return;
}
- if (mBottomNavBarVisible) {
+ // If keyboard is visible and bottom nav bar not visible, this is the correct state, so do
+ // nothing. Same with if keyboard is not visible and bottom nav bar is visible.
+ if (isKeyboardVisible ^ mBottomNavBarVisible) {
return;
}
- mBottomNavBarVisible = true;
-
- WindowManager.LayoutParams lp = new WindowManager.LayoutParams(
- LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT,
- WindowManager.LayoutParams.TYPE_NAVIGATION_BAR,
- WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
- | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
- | WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH
- | WindowManager.LayoutParams.FLAG_SPLIT_TOUCH,
- PixelFormat.TRANSLUCENT);
- lp.setTitle("CarNavigationBar");
- lp.windowAnimations = 0;
- mWindowManager.addView(mNavigationBarWindow, lp);
- }
- /**
- * Detaches the bottom nav bar window. Can be extended to modify the specific behavior of
- * detaching the bottom nav bar.
- */
- protected void detachBottomNavBarWindow() {
- if (!mShowBottom) {
- return;
- }
+ mNavigationBarWindow.setVisibility(isKeyboardVisible ? View.GONE : View.VISIBLE);
+ mBottomNavBarVisible = !isKeyboardVisible;
+ }
- if (!mBottomNavBarVisible) {
- return;
- }
- mBottomNavBarVisible = false;
- mWindowManager.removeView(mNavigationBarWindow);
+ private void buildTopBar(int layout) {
+ mTopNavigationBarContainer.removeAllViews();
+ View.inflate(mContext, layout, mTopNavigationBarContainer);
+ mTopNavigationBarView = (CarNavigationBarView) mTopNavigationBarContainer.getChildAt(0);
+ if (mTopNavigationBarView == null) {
+ Log.e(TAG, "CarStatusBar failed inflate for R.layout.car_top_navigation_bar");
+ throw new RuntimeException("Unable to build top nav bar due to missing layout");
+ }
+ mTopNavigationBarView.setStatusBar(this);
+ addTemperatureViewToController(mTopNavigationBarView);
+ mTopNavigationBarView.setStatusBarWindowTouchListener(mTopNavBarNotificationTouchListener);
}
private void buildBottomBar(int layout) {
@@ -891,7 +1046,7 @@ public class CarStatusBar extends StatusBar implements CarBatteryController.Batt
mNavigationBarView = (CarNavigationBarView) mNavigationBarWindow.getChildAt(0);
if (mNavigationBarView == null) {
Log.e(TAG, "CarStatusBar failed inflate for R.layout.car_navigation_bar");
- throw new RuntimeException("Unable to build botom nav bar due to missing layout");
+ throw new RuntimeException("Unable to build bottom nav bar due to missing layout");
}
mNavigationBarView.setStatusBar(this);
addTemperatureViewToController(mNavigationBarView);
@@ -902,7 +1057,7 @@ public class CarStatusBar extends StatusBar implements CarBatteryController.Batt
View.inflate(mContext, layout, mLeftNavigationBarWindow);
mLeftNavigationBarView = (CarNavigationBarView) mLeftNavigationBarWindow.getChildAt(0);
if (mLeftNavigationBarView == null) {
- Log.e(TAG, "CarStatusBar failed inflate for R.layout.car_navigation_bar");
+ Log.e(TAG, "CarStatusBar failed inflate for R.layout.car_left_navigation_bar");
throw new RuntimeException("Unable to build left nav bar due to missing layout");
}
mLeftNavigationBarView.setStatusBar(this);
@@ -915,7 +1070,7 @@ public class CarStatusBar extends StatusBar implements CarBatteryController.Batt
View.inflate(mContext, layout, mRightNavigationBarWindow);
mRightNavigationBarView = (CarNavigationBarView) mRightNavigationBarWindow.getChildAt(0);
if (mRightNavigationBarView == null) {
- Log.e(TAG, "CarStatusBar failed inflate for R.layout.car_navigation_bar");
+ Log.e(TAG, "CarStatusBar failed inflate for R.layout.car_right_navigation_bar");
throw new RuntimeException("Unable to build right nav bar due to missing layout");
}
mRightNavigationBarView.setStatusBar(this);
@@ -1030,12 +1185,8 @@ public class CarStatusBar extends StatusBar implements CarBatteryController.Batt
UserSwitcherController userSwitcherController =
Dependency.get(UserSwitcherController.class);
if (userSwitcherController.useFullscreenUserSwitcher()) {
- Car car = Car.createCar(mContext);
- CarTrustAgentEnrollmentManager enrollmentManager = (CarTrustAgentEnrollmentManager) car
- .getCarManager(Car.CAR_TRUST_AGENT_ENROLLMENT_SERVICE);
mFullscreenUserSwitcher = new FullscreenUserSwitcher(this,
- mStatusBarWindow.findViewById(R.id.fullscreen_user_switcher_stub),
- enrollmentManager, mContext);
+ mStatusBarWindow.findViewById(R.id.fullscreen_user_switcher_stub), mContext);
} else {
super.createUserSwitcher();
}
@@ -1146,17 +1297,22 @@ public class CarStatusBar extends StatusBar implements CarBatteryController.Batt
mHandleBar.setTranslationY(height - mHandleBar.getHeight() - lp.bottomMargin);
}
if (mNotificationView.getHeight() > 0) {
- // Calculates the alpha value for the background based on how much of the notification
- // shade is visible to the user. When the notification shade is completely open then
- // alpha value will be 1.
- float alpha = (float) height / mNotificationView.getHeight();
Drawable background = mNotificationView.getBackground().mutate();
-
- background.setAlpha((int) (alpha * 255));
+ background.setAlpha((int) (getBackgroundAlpha(height) * 255));
mNotificationView.setBackground(background);
}
}
+ /**
+ * Calculates the alpha value for the background based on how much of the notification
+ * shade is visible to the user. When the notification shade is completely open then
+ * alpha value will be 1.
+ */
+ private float getBackgroundAlpha(int height) {
+ return mInitialBackgroundAlpha +
+ ((float) height / mNotificationView.getHeight() * mBackgroundAlphaDiff);
+ }
+
private void calculatePercentageFromBottom(float height) {
if (mNotificationView.getHeight() > 0) {
mPercentageFromBottom = (int) Math.abs(
diff --git a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/DrivingStateHelper.java b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/DrivingStateHelper.java
index a4424260fef5..76ad04f6716f 100644
--- a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/DrivingStateHelper.java
+++ b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/DrivingStateHelper.java
@@ -17,18 +17,18 @@
package com.android.systemui.statusbar.car;
import android.car.Car;
-import android.car.CarNotConnectedException;
+import android.car.Car.CarServiceLifecycleListener;
import android.car.drivingstate.CarDrivingStateEvent;
import android.car.drivingstate.CarDrivingStateManager;
import android.car.drivingstate.CarDrivingStateManager.CarDrivingStateEventListener;
-import android.content.ComponentName;
import android.content.Context;
-import android.content.ServiceConnection;
-import android.os.IBinder;
import android.util.Log;
import androidx.annotation.NonNull;
+import com.android.systemui.CarSystemUIFactory;
+import com.android.systemui.SystemUIFactory;
+
/**
* Helper class for connecting to the {@link CarDrivingStateManager} and listening for driving state
* changes.
@@ -38,7 +38,6 @@ public class DrivingStateHelper {
private final Context mContext;
private CarDrivingStateManager mDrivingStateManager;
- private Car mCar;
private CarDrivingStateEventListener mDrivingStateHandler;
public DrivingStateHelper(Context context,
@@ -55,16 +54,11 @@ public class DrivingStateHelper {
if (mDrivingStateManager == null) {
return false;
}
- try {
- CarDrivingStateEvent currentState = mDrivingStateManager.getCurrentCarDrivingState();
- if (currentState != null) {
- return currentState.eventValue == CarDrivingStateEvent.DRIVING_STATE_IDLING
- || currentState.eventValue == CarDrivingStateEvent.DRIVING_STATE_MOVING;
- }
- } catch (CarNotConnectedException e) {
- Log.e(TAG, "Cannot determine current driving state. Car not connected", e);
+ CarDrivingStateEvent currentState = mDrivingStateManager.getCurrentCarDrivingState();
+ if (currentState != null) {
+ return currentState.eventValue == CarDrivingStateEvent.DRIVING_STATE_IDLING
+ || currentState.eventValue == CarDrivingStateEvent.DRIVING_STATE_MOVING;
}
-
return false; // Default to false.
}
@@ -72,55 +66,25 @@ public class DrivingStateHelper {
* Establishes connection with the Car service.
*/
public void connectToCarService() {
- mCar = Car.createCar(mContext, mCarConnectionListener);
- if (mCar != null) {
- mCar.connect();
- }
+ ((CarSystemUIFactory) SystemUIFactory.getInstance()).getCarServiceProvider(mContext)
+ .addListener(mCarServiceLifecycleListener);
}
- /**
- * Disconnects from Car service and cleans up listeners.
- */
- public void disconnectFromCarService() {
- if (mCar != null) {
- mCar.disconnect();
+ private final CarServiceLifecycleListener mCarServiceLifecycleListener = (car, ready) -> {
+ if (!ready) {
+ return;
}
- }
-
- private final ServiceConnection mCarConnectionListener =
- new ServiceConnection() {
- public void onServiceConnected(ComponentName name, IBinder service) {
- logD("Car Service connected");
- try {
- mDrivingStateManager = (CarDrivingStateManager) mCar.getCarManager(
- Car.CAR_DRIVING_STATE_SERVICE);
- if (mDrivingStateManager != null) {
- mDrivingStateManager.registerListener(mDrivingStateHandler);
- mDrivingStateHandler.onDrivingStateChanged(
- mDrivingStateManager.getCurrentCarDrivingState());
- } else {
- Log.e(TAG, "CarDrivingStateService service not available");
- }
- } catch (CarNotConnectedException e) {
- Log.e(TAG, "Car not connected", e);
- }
- }
-
- @Override
- public void onServiceDisconnected(ComponentName name) {
- destroyDrivingStateManager();
- }
- };
-
- private void destroyDrivingStateManager() {
- try {
- if (mDrivingStateManager != null) {
- mDrivingStateManager.unregisterListener();
- }
- } catch (CarNotConnectedException e) {
- Log.e(TAG, "Error unregistering listeners", e);
+ logD("Car Service connected");
+ mDrivingStateManager = (CarDrivingStateManager) car.getCarManager(
+ Car.CAR_DRIVING_STATE_SERVICE);
+ if (mDrivingStateManager != null) {
+ mDrivingStateManager.registerListener(mDrivingStateHandler);
+ mDrivingStateHandler.onDrivingStateChanged(
+ mDrivingStateManager.getCurrentCarDrivingState());
+ } else {
+ Log.e(TAG, "CarDrivingStateService service not available");
}
- }
+ };
private void logD(String message) {
if (Log.isLoggable(TAG, Log.DEBUG)) {
diff --git a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/FullscreenUserSwitcher.java b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/FullscreenUserSwitcher.java
index 0f7c1ee8ea7e..f74dbe925349 100644
--- a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/FullscreenUserSwitcher.java
+++ b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/FullscreenUserSwitcher.java
@@ -18,6 +18,7 @@ package com.android.systemui.statusbar.car;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
+import android.car.Car;
import android.car.trust.CarTrustAgentEnrollmentManager;
import android.car.userlib.CarUserManagerHelper;
import android.content.BroadcastReceiver;
@@ -33,7 +34,9 @@ import android.view.ViewStub;
import androidx.recyclerview.widget.GridLayoutManager;
+import com.android.systemui.CarSystemUIFactory;
import com.android.systemui.R;
+import com.android.systemui.SystemUIFactory;
import com.android.systemui.statusbar.car.CarTrustAgentUnlockDialogHelper.OnHideListener;
import com.android.systemui.statusbar.car.UserGridRecyclerView.UserRecord;
@@ -42,15 +45,13 @@ import com.android.systemui.statusbar.car.UserGridRecyclerView.UserRecord;
*/
public class FullscreenUserSwitcher {
private static final String TAG = FullscreenUserSwitcher.class.getSimpleName();
- // Because user 0 is headless, user count for single user is 2
- private static final int NUMBER_OF_BACKGROUND_USERS = 1;
private final UserGridRecyclerView mUserGridView;
private final View mParent;
private final int mShortAnimDuration;
private final CarStatusBar mStatusBar;
private final Context mContext;
private final UserManager mUserManager;
- private final CarTrustAgentEnrollmentManager mEnrollmentManager;
+ private CarTrustAgentEnrollmentManager mEnrollmentManager;
private CarTrustAgentUnlockDialogHelper mUnlockDialogHelper;
private UserGridRecyclerView.UserRecord mSelectedUser;
private CarUserManagerHelper mCarUserManagerHelper;
@@ -65,12 +66,9 @@ public class FullscreenUserSwitcher {
}
};
-
- public FullscreenUserSwitcher(CarStatusBar statusBar, ViewStub containerStub,
- CarTrustAgentEnrollmentManager enrollmentManager, Context context) {
+ public FullscreenUserSwitcher(CarStatusBar statusBar, ViewStub containerStub, Context context) {
mStatusBar = statusBar;
mParent = containerStub.inflate();
- mEnrollmentManager = enrollmentManager;
mContext = context;
View container = mParent.findViewById(R.id.container);
@@ -86,6 +84,15 @@ public class FullscreenUserSwitcher {
mUnlockDialogHelper = new CarTrustAgentUnlockDialogHelper(mContext);
mUserManager = mContext.getSystemService(UserManager.class);
+ ((CarSystemUIFactory) SystemUIFactory.getInstance()).getCarServiceProvider(mContext)
+ .addListener((car, ready) -> {
+ if (!ready) {
+ return;
+ }
+ mEnrollmentManager = (CarTrustAgentEnrollmentManager) car
+ .getCarManager(Car.CAR_TRUST_AGENT_ENROLLMENT_SERVICE);
+ });
+
mShortAnimDuration = container.getResources()
.getInteger(android.R.integer.config_shortAnimTime);
IntentFilter filter = new IntentFilter(Intent.ACTION_USER_UNLOCKED);
@@ -109,27 +116,18 @@ public class FullscreenUserSwitcher {
/* isStartGuestSession= */ false,
/* isAddUser= */ false,
/* isForeground= */ true);
- // For single user without trusted device, hide the user switcher.
- if (!hasMultipleUsers() && !hasTrustedDevice(initialUser)) {
- dismissUserSwitcher();
- return;
- }
- // Show unlock dialog for initial user
+
+ // If the initial user has trusted device, display the unlock dialog on the keyguard.
if (hasTrustedDevice(initialUser)) {
mUnlockDialogHelper.showUnlockDialogAfterDelay(initialUser,
mOnHideListener);
+ } else {
+ // If no trusted device, dismiss the keyguard.
+ dismissUserSwitcher();
}
}
/**
- * Check if there is only one possible user to login in.
- * In a Multi-User system there is always one background user (user 0)
- */
- private boolean hasMultipleUsers() {
- return mUserManager.getUserCount() > NUMBER_OF_BACKGROUND_USERS + 1;
- }
-
- /**
* Makes user grid visible.
*/
public void show() {
@@ -201,6 +199,9 @@ public class FullscreenUserSwitcher {
}
private boolean hasTrustedDevice(int uid) {
+ if (mEnrollmentManager == null) { // car service not ready, so it cannot be available.
+ return false;
+ }
return !mEnrollmentManager.getEnrolledDeviceInfoForUser(uid).isEmpty();
}
diff --git a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/PowerManagerHelper.java b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/PowerManagerHelper.java
index 8de1439c3306..d87b54c39fd7 100644
--- a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/PowerManagerHelper.java
+++ b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/PowerManagerHelper.java
@@ -18,15 +18,15 @@ package com.android.systemui.statusbar.car;
import android.annotation.NonNull;
import android.car.Car;
-import android.car.CarNotConnectedException;
+import android.car.Car.CarServiceLifecycleListener;
import android.car.hardware.power.CarPowerManager;
import android.car.hardware.power.CarPowerManager.CarPowerStateListener;
-import android.content.ComponentName;
import android.content.Context;
-import android.content.ServiceConnection;
-import android.os.IBinder;
import android.util.Log;
+import com.android.systemui.CarSystemUIFactory;
+import com.android.systemui.SystemUIFactory;
+
/**
* Helper class for connecting to the {@link CarPowerManager} and listening for power state changes.
*/
@@ -36,58 +36,32 @@ public class PowerManagerHelper {
private final Context mContext;
private final CarPowerStateListener mCarPowerStateListener;
- private Car mCar;
private CarPowerManager mCarPowerManager;
- private final ServiceConnection mCarConnectionListener =
- new ServiceConnection() {
- public void onServiceConnected(ComponentName name, IBinder service) {
- Log.d(TAG, "Car Service connected");
- try {
- mCarPowerManager = (CarPowerManager) mCar.getCarManager(Car.POWER_SERVICE);
- if (mCarPowerManager != null) {
- mCarPowerManager.setListener(mCarPowerStateListener);
- } else {
- Log.e(TAG, "CarPowerManager service not available");
- }
- } catch (CarNotConnectedException e) {
- Log.e(TAG, "Car not connected", e);
- }
- }
-
- @Override
- public void onServiceDisconnected(ComponentName name) {
- destroyCarPowerManager();
- }
- };
+ private final CarServiceLifecycleListener mCarServiceLifecycleListener;
PowerManagerHelper(Context context, @NonNull CarPowerStateListener listener) {
mContext = context;
mCarPowerStateListener = listener;
+ mCarServiceLifecycleListener = (car, ready) -> {
+ if (!ready) {
+ return;
+ }
+ Log.d(TAG, "Car Service connected");
+ mCarPowerManager = (CarPowerManager) car.getCarManager(Car.POWER_SERVICE);
+ if (mCarPowerManager != null) {
+ mCarPowerManager.setListener(mCarPowerStateListener);
+ } else {
+ Log.e(TAG, "CarPowerManager service not available");
+ }
+ };
}
/**
* Connect to Car service.
*/
void connectToCarService() {
- mCar = Car.createCar(mContext, mCarConnectionListener);
- if (mCar != null) {
- mCar.connect();
- }
- }
-
- /**
- * Disconnects from Car service.
- */
- void disconnectFromCarService() {
- if (mCar != null) {
- mCar.disconnect();
- }
- }
-
- private void destroyCarPowerManager() {
- if (mCarPowerManager != null) {
- mCarPowerManager.clearListener();
- }
+ ((CarSystemUIFactory) SystemUIFactory.getInstance()).getCarServiceProvider(mContext)
+ .addListener(mCarServiceLifecycleListener);
}
}
diff --git a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/hvac/HvacController.java b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/hvac/HvacController.java
index 30429eda7be7..1d9675034bc5 100644
--- a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/hvac/HvacController.java
+++ b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/hvac/HvacController.java
@@ -20,17 +20,17 @@ import static android.car.VehicleAreaType.VEHICLE_AREA_TYPE_GLOBAL;
import static android.car.VehiclePropertyIds.HVAC_TEMPERATURE_DISPLAY_UNITS;
import android.car.Car;
+import android.car.Car.CarServiceLifecycleListener;
import android.car.VehicleUnit;
import android.car.hardware.CarPropertyValue;
import android.car.hardware.hvac.CarHvacManager;
import android.car.hardware.hvac.CarHvacManager.CarHvacEventCallback;
-import android.content.ComponentName;
import android.content.Context;
-import android.content.ServiceConnection;
-import android.os.Handler;
-import android.os.IBinder;
import android.util.Log;
+import com.android.systemui.CarSystemUIFactory;
+import com.android.systemui.SystemUIFactory;
+
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
@@ -43,15 +43,12 @@ import java.util.Objects;
* {@link TemperatureView}s
*/
public class HvacController {
-
public static final String TAG = "HvacController";
- public static final int BIND_TO_HVAC_RETRY_DELAY = 5000;
private Context mContext;
- private Handler mHandler;
- private Car mCar;
private CarHvacManager mHvacManager;
private HashMap<HvacKey, List<TemperatureView>> mTempComponents = new HashMap<>();
+
/**
* Callback for getting changes from {@link CarHvacManager} and setting the UI elements to
* match.
@@ -83,39 +80,17 @@ public class HvacController {
+ " zone: " + zone);
}
};
- /**
- * If the connection to car service goes away then restart it.
- */
- private final IBinder.DeathRecipient mRestart = new IBinder.DeathRecipient() {
- @Override
- public void binderDied() {
- Log.d(TAG, "Death of HVAC triggering a restart");
- if (mCar != null) {
- mCar.disconnect();
- }
- destroyHvacManager();
- mHandler.postDelayed(() -> mCar.connect(), BIND_TO_HVAC_RETRY_DELAY);
- }
- };
- /**
- * Registers callbacks and initializes components upon connection.
- */
- private ServiceConnection mServiceConnection = new ServiceConnection() {
- @Override
- public void onServiceConnected(ComponentName name, IBinder service) {
- try {
- service.linkToDeath(mRestart, 0);
- mHvacManager = (CarHvacManager) mCar.getCarManager(Car.HVAC_SERVICE);
- mHvacManager.registerCallback(mHardwareCallback);
- initComponents();
- } catch (Exception e) {
- Log.e(TAG, "Failed to correctly connect to HVAC", e);
- }
- }
- @Override
- public void onServiceDisconnected(ComponentName name) {
- destroyHvacManager();
+ private final CarServiceLifecycleListener mCarServiceLifecycleListener = (car, ready) -> {
+ if (!ready) {
+ return;
+ }
+ try {
+ mHvacManager = (CarHvacManager) car.getCarManager(Car.HVAC_SERVICE);
+ mHvacManager.registerCallback(mHardwareCallback);
+ initComponents();
+ } catch (Exception e) {
+ Log.e(TAG, "Failed to correctly connect to HVAC", e);
}
};
@@ -128,19 +103,8 @@ public class HvacController {
* ({@link CarHvacManager}) will happen on the same thread this method was called from.
*/
public void connectToCarService() {
- mHandler = new Handler();
- mCar = Car.createCar(mContext, mServiceConnection, mHandler);
- if (mCar != null) {
- // note: this connect call handles the retries
- mCar.connect();
- }
- }
-
- private void destroyHvacManager() {
- if (mHvacManager != null) {
- mHvacManager.unregisterCallback(mHardwareCallback);
- mHvacManager = null;
- }
+ ((CarSystemUIFactory) SystemUIFactory.getInstance()).getCarServiceProvider(mContext)
+ .addListener(mCarServiceLifecycleListener);
}
/**
diff --git a/packages/CarSystemUI/src/com/android/systemui/volume/CarVolumeDialogComponent.java b/packages/CarSystemUI/src/com/android/systemui/volume/CarVolumeDialogComponent.java
index 71cc19b63ac1..aa78b9604285 100644
--- a/packages/CarSystemUI/src/com/android/systemui/volume/CarVolumeDialogComponent.java
+++ b/packages/CarSystemUI/src/com/android/systemui/volume/CarVolumeDialogComponent.java
@@ -31,6 +31,10 @@ public class CarVolumeDialogComponent extends VolumeDialogComponent {
}
protected VolumeDialog createDefault() {
- return new CarVolumeDialogImpl(mContext);
+ CarVolumeDialogImpl carVolumeDialog = new CarVolumeDialogImpl(mContext);
+ // Since VolumeUI is initialized when the first Volume Up/Down event is received we need to
+ // show the dialog on initialization too.
+ carVolumeDialog.show(Events.SHOW_REASON_VOLUME_CHANGED);
+ return carVolumeDialog;
}
}
diff --git a/packages/CarSystemUI/src/com/android/systemui/volume/CarVolumeDialogImpl.java b/packages/CarSystemUI/src/com/android/systemui/volume/CarVolumeDialogImpl.java
index d0a63f058291..f91c90ec636f 100644
--- a/packages/CarSystemUI/src/com/android/systemui/volume/CarVolumeDialogImpl.java
+++ b/packages/CarSystemUI/src/com/android/systemui/volume/CarVolumeDialogImpl.java
@@ -24,12 +24,10 @@ import android.annotation.Nullable;
import android.app.Dialog;
import android.app.KeyguardManager;
import android.car.Car;
-import android.car.CarNotConnectedException;
+import android.car.Car.CarServiceLifecycleListener;
import android.car.media.CarAudioManager;
-import android.content.ComponentName;
import android.content.Context;
import android.content.DialogInterface;
-import android.content.ServiceConnection;
import android.content.res.TypedArray;
import android.content.res.XmlResourceParser;
import android.graphics.Color;
@@ -39,7 +37,6 @@ import android.graphics.drawable.Drawable;
import android.media.AudioManager;
import android.os.Debug;
import android.os.Handler;
-import android.os.IBinder;
import android.os.Looper;
import android.os.Message;
import android.util.AttributeSet;
@@ -58,14 +55,15 @@ import android.widget.SeekBar.OnSeekBarChangeListener;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
+import com.android.systemui.CarSystemUIFactory;
import com.android.systemui.R;
+import com.android.systemui.SystemUIFactory;
import com.android.systemui.plugins.VolumeDialog;
import org.xmlpull.v1.XmlPullParserException;
import java.io.IOException;
import java.util.ArrayList;
-import java.util.Iterator;
import java.util.List;
/**
@@ -79,8 +77,6 @@ public class CarVolumeDialogImpl implements VolumeDialog {
private static final String XML_TAG_VOLUME_ITEMS = "carVolumeItems";
private static final String XML_TAG_VOLUME_ITEM = "item";
- private static final int HOVERING_TIMEOUT = 16000;
- private static final int NORMAL_TIMEOUT = 3000;
private static final int LISTVIEW_ANIMATION_DURATION_IN_MILLIS = 250;
private static final int DISMISS_DELAY_IN_MILLIS = 50;
private static final int ARROW_FADE_IN_START_DELAY_IN_MILLIS = 100;
@@ -94,12 +90,24 @@ public class CarVolumeDialogImpl implements VolumeDialog {
// Volume items in the RecyclerView.
private final List<CarVolumeItem> mCarVolumeLineItems = new ArrayList<>();
private final KeyguardManager mKeyguard;
+ private final int mNormalTimeout;
+ private final int mHoveringTimeout;
+ private final int mExpNormalTimeout;
+ private final int mExpHoveringTimeout;
+
private Window mWindow;
private CustomDialog mDialog;
private RecyclerView mListView;
private CarVolumeItemAdapter mVolumeItemsAdapter;
- private Car mCar;
private CarAudioManager mCarAudioManager;
+ private boolean mHovering;
+ private int mCurrentlyDisplayingGroupId;
+ private int mPreviouslyDisplayingGroupId;
+ private boolean mShowing;
+ private boolean mDismissing;
+ private boolean mExpanded;
+ private View mExpandIcon;
+
private final CarAudioManager.CarVolumeCallback mVolumeChangeCallback =
new CarAudioManager.CarVolumeCallback() {
@Override
@@ -129,6 +137,7 @@ public class CarVolumeDialogImpl implements VolumeDialog {
volumeItem.progress = value;
}
if ((flags & AudioManager.FLAG_SHOW_UI) != 0) {
+ mPreviouslyDisplayingGroupId = mCurrentlyDisplayingGroupId;
mCurrentlyDisplayingGroupId = groupId;
mHandler.obtainMessage(H.SHOW,
Events.SHOW_REASON_VOLUME_CHANGED).sendToTarget();
@@ -140,80 +149,51 @@ public class CarVolumeDialogImpl implements VolumeDialog {
// ignored
}
};
- private boolean mHovering;
- private int mCurrentlyDisplayingGroupId;
- private boolean mShowing;
- private boolean mExpanded;
- private View mExpandIcon;
- private final ServiceConnection mServiceConnection = new ServiceConnection() {
- @Override
- public void onServiceConnected(ComponentName name, IBinder service) {
- try {
- mExpanded = false;
- mCarAudioManager = (CarAudioManager) mCar.getCarManager(Car.AUDIO_SERVICE);
- int volumeGroupCount = mCarAudioManager.getVolumeGroupCount();
- // Populates volume slider items from volume groups to UI.
- for (int groupId = 0; groupId < volumeGroupCount; groupId++) {
- VolumeItem volumeItem = getVolumeItemForUsages(
- mCarAudioManager.getUsagesForVolumeGroupId(groupId));
- mAvailableVolumeItems.add(volumeItem);
- // The first one is the default item.
- if (groupId == 0) {
- setuptListItem(0);
- }
- }
- // If list is already initiated, update its content.
- if (mVolumeItemsAdapter != null) {
- mVolumeItemsAdapter.notifyDataSetChanged();
- }
- mCarAudioManager.registerCarVolumeCallback(mVolumeChangeCallback);
- } catch (CarNotConnectedException e) {
- Log.e(TAG, "Car is not connected!", e);
+ private final CarServiceLifecycleListener mCarServiceLifecycleListener = (car, ready) -> {
+ if (!ready) {
+ return;
+ }
+ mExpanded = false;
+ mCarAudioManager = (CarAudioManager) car.getCarManager(Car.AUDIO_SERVICE);
+ int volumeGroupCount = mCarAudioManager.getVolumeGroupCount();
+ // Populates volume slider items from volume groups to UI.
+ for (int groupId = 0; groupId < volumeGroupCount; groupId++) {
+ VolumeItem volumeItem = getVolumeItemForUsages(
+ mCarAudioManager.getUsagesForVolumeGroupId(groupId));
+ mAvailableVolumeItems.add(volumeItem);
+ // The first one is the default item.
+ if (groupId == 0) {
+ clearAllAndSetupDefaultCarVolumeLineItem(0);
}
}
- /**
- * This does not get called when service is properly disconnected.
- * So we need to also handle cleanups in destroy().
- */
- @Override
- public void onServiceDisconnected(ComponentName name) {
- cleanupAudioManager();
+ // If list is already initiated, update its content.
+ if (mVolumeItemsAdapter != null) {
+ mVolumeItemsAdapter.notifyDataSetChanged();
}
+ mCarAudioManager.registerCarVolumeCallback(mVolumeChangeCallback);
};
- private void setuptListItem(int groupId) {
- mCarVolumeLineItems.clear();
- VolumeItem volumeItem = mAvailableVolumeItems.get(groupId);
- volumeItem.defaultItem = true;
- addCarVolumeListItem(volumeItem, /* volumeGroupId = */ groupId,
- R.drawable.car_ic_keyboard_arrow_down, new ExpandIconListener()
- );
- }
-
public CarVolumeDialogImpl(Context context) {
mContext = context;
mKeyguard = (KeyguardManager) mContext.getSystemService(Context.KEYGUARD_SERVICE);
- mCar = Car.createCar(mContext, mServiceConnection);
+ mNormalTimeout = mContext.getResources().getInteger(
+ R.integer.car_volume_dialog_display_normal_timeout);
+ mHoveringTimeout = mContext.getResources().getInteger(
+ R.integer.car_volume_dialog_display_hovering_timeout);
+ mExpNormalTimeout = mContext.getResources().getInteger(
+ R.integer.car_volume_dialog_display_expanded_normal_timeout);
+ mExpHoveringTimeout = mContext.getResources().getInteger(
+ R.integer.car_volume_dialog_display_expanded_hovering_timeout);
}
private static int getSeekbarValue(CarAudioManager carAudioManager, int volumeGroupId) {
- try {
- return carAudioManager.getGroupVolume(volumeGroupId);
- } catch (CarNotConnectedException e) {
- Log.e(TAG, "Car is not connected!", e);
- }
- return 0;
+ return carAudioManager.getGroupVolume(volumeGroupId);
}
private static int getMaxSeekbarValue(CarAudioManager carAudioManager, int volumeGroupId) {
- try {
- return carAudioManager.getGroupMaxVolume(volumeGroupId);
- } catch (CarNotConnectedException e) {
- Log.e(TAG, "Car is not connected!", e);
- }
- return 0;
+ return carAudioManager.getGroupMaxVolume(volumeGroupId);
}
/**
@@ -224,17 +204,15 @@ public class CarVolumeDialogImpl implements VolumeDialog {
public void init(int windowType, Callback callback) {
initDialog();
- mCar.connect();
+ ((CarSystemUIFactory) SystemUIFactory.getInstance()).getCarServiceProvider(mContext)
+ .addListener(mCarServiceLifecycleListener);
}
@Override
public void destroy() {
- mHandler.removeCallbacksAndMessages(null);
+ mHandler.removeCallbacksAndMessages(/* token= */ null);
cleanupAudioManager();
- // unregisterVolumeCallback is not being called when disconnect car, so we manually cleanup
- // audio manager beforehand.
- mCar.disconnect();
}
private void initDialog() {
@@ -244,6 +222,7 @@ public class CarVolumeDialogImpl implements VolumeDialog {
mHovering = false;
mShowing = false;
+ mDismissing = false;
mExpanded = false;
mWindow = mDialog.getWindow();
mWindow.requestFeature(Window.FEATURE_NO_TITLE);
@@ -293,6 +272,19 @@ public class CarVolumeDialogImpl implements VolumeDialog {
mListView.setLayoutManager(new LinearLayoutManager(mContext));
}
+ /**
+ * Reveals volume dialog.
+ */
+ public void show(int reason) {
+ mHandler.obtainMessage(H.SHOW, reason).sendToTarget();
+ }
+
+ /**
+ * Hides volume dialog.
+ */
+ public void dismiss(int reason) {
+ mHandler.obtainMessage(H.DISMISS, reason).sendToTarget();
+ }
private void showH(int reason) {
if (D.BUG) {
@@ -301,19 +293,36 @@ public class CarVolumeDialogImpl implements VolumeDialog {
mHandler.removeMessages(H.SHOW);
mHandler.removeMessages(H.DISMISS);
+
rescheduleTimeoutH();
+
// Refresh the data set before showing.
mVolumeItemsAdapter.notifyDataSetChanged();
+
if (mShowing) {
+ if (mPreviouslyDisplayingGroupId == mCurrentlyDisplayingGroupId || mExpanded) {
+ return;
+ }
+
+ clearAllAndSetupDefaultCarVolumeLineItem(mCurrentlyDisplayingGroupId);
return;
}
+
mShowing = true;
- setuptListItem(mCurrentlyDisplayingGroupId);
+ clearAllAndSetupDefaultCarVolumeLineItem(mCurrentlyDisplayingGroupId);
mDialog.show();
Events.writeEvent(mContext, Events.EVENT_SHOW_DIALOG, reason, mKeyguard.isKeyguardLocked());
}
- private void rescheduleTimeoutH() {
+ private void clearAllAndSetupDefaultCarVolumeLineItem(int groupId) {
+ mCarVolumeLineItems.clear();
+ VolumeItem volumeItem = mAvailableVolumeItems.get(groupId);
+ volumeItem.defaultItem = true;
+ addCarVolumeListItem(volumeItem, /* volumeGroupId = */ groupId,
+ R.drawable.car_ic_keyboard_arrow_down, new ExpandIconListener());
+ }
+
+ protected void rescheduleTimeoutH() {
mHandler.removeMessages(H.DISMISS);
final int timeout = computeTimeoutH();
mHandler.sendMessageDelayed(mHandler
@@ -325,7 +334,11 @@ public class CarVolumeDialogImpl implements VolumeDialog {
}
private int computeTimeoutH() {
- return mHovering ? HOVERING_TIMEOUT : NORMAL_TIMEOUT;
+ if (mExpanded) {
+ return mHovering ? mExpHoveringTimeout : mExpNormalTimeout;
+ } else {
+ return mHovering ? mHoveringTimeout : mNormalTimeout;
+ }
}
private void dismissH(int reason) {
@@ -335,14 +348,11 @@ public class CarVolumeDialogImpl implements VolumeDialog {
mHandler.removeMessages(H.DISMISS);
mHandler.removeMessages(H.SHOW);
- if (!mShowing) {
+ if (!mShowing || mDismissing) {
return;
}
- mListView.animate().cancel();
-
- mListView.setTranslationY(0);
- mListView.setAlpha(1);
+ mDismissing = true;
mListView.animate()
.alpha(0)
.translationY(-mListView.getHeight())
@@ -354,7 +364,7 @@ public class CarVolumeDialogImpl implements VolumeDialog {
}
mDialog.dismiss();
mShowing = false;
- mShowing = false;
+ mDismissing = false;
// if mExpandIcon is null that means user never clicked on the expanded arrow
// which implies that the dialog is still not expanded. In that case we do
// not want to reset the state
@@ -390,12 +400,13 @@ public class CarVolumeDialogImpl implements VolumeDialog {
if (XML_TAG_VOLUME_ITEM.equals(parser.getName())) {
TypedArray item = mContext.getResources().obtainAttributes(
attrs, R.styleable.carVolumeItems_item);
- int usage = item.getInt(R.styleable.carVolumeItems_item_usage, -1);
+ int usage = item.getInt(R.styleable.carVolumeItems_item_usage,
+ /* defValue= */ -1);
if (usage >= 0) {
VolumeItem volumeItem = new VolumeItem();
volumeItem.rank = rank;
- volumeItem.icon = item.getResourceId(R.styleable.carVolumeItems_item_icon,
- 0);
+ volumeItem.icon = item.getResourceId(
+ R.styleable.carVolumeItems_item_icon, /* defValue= */ 0);
mVolumeItems.put(usage, volumeItem);
rank++;
}
@@ -420,22 +431,22 @@ public class CarVolumeDialogImpl implements VolumeDialog {
return result;
}
- private CarVolumeItem addCarVolumeListItem(VolumeItem volumeItem, int volumeGroupId,
- int supplementalIconId,
+ private CarVolumeItem createCarVolumeListItem(VolumeItem volumeItem, int volumeGroupId,
+ Drawable supplementalIcon, int seekbarProgressValue,
@Nullable View.OnClickListener supplementalIconOnClickListener) {
CarVolumeItem carVolumeItem = new CarVolumeItem();
carVolumeItem.setMax(getMaxSeekbarValue(mCarAudioManager, volumeGroupId));
- int color = mContext.getResources().getColor(R.color.car_volume_dialog_tint);
- int progress = getSeekbarValue(mCarAudioManager, volumeGroupId);
- carVolumeItem.setProgress(progress);
+ carVolumeItem.setProgress(seekbarProgressValue);
carVolumeItem.setOnSeekBarChangeListener(
new CarVolumeDialogImpl.VolumeSeekBarChangeListener(volumeGroupId,
mCarAudioManager));
- Drawable primaryIcon = mContext.getResources().getDrawable(volumeItem.icon);
+ carVolumeItem.setGroupId(volumeGroupId);
+
+ int color = mContext.getColor(R.color.car_volume_dialog_tint);
+ Drawable primaryIcon = mContext.getDrawable(volumeItem.icon);
primaryIcon.mutate().setTint(color);
carVolumeItem.setPrimaryIcon(primaryIcon);
- if (supplementalIconId != 0) {
- Drawable supplementalIcon = mContext.getResources().getDrawable(supplementalIconId);
+ if (supplementalIcon != null) {
supplementalIcon.mutate().setTint(color);
carVolumeItem.setSupplementalIcon(supplementalIcon,
/* showSupplementalIconDivider= */ true);
@@ -444,21 +455,23 @@ public class CarVolumeDialogImpl implements VolumeDialog {
carVolumeItem.setSupplementalIcon(/* drawable= */ null,
/* showSupplementalIconDivider= */ false);
}
- carVolumeItem.setGroupId(volumeGroupId);
- mCarVolumeLineItems.add(carVolumeItem);
+
volumeItem.carVolumeItem = carVolumeItem;
- volumeItem.progress = progress;
+ volumeItem.progress = seekbarProgressValue;
+
return carVolumeItem;
}
- private VolumeItem findVolumeItem(CarVolumeItem targetItem) {
- for (int i = 0; i < mVolumeItems.size(); ++i) {
- VolumeItem volumeItem = mVolumeItems.valueAt(i);
- if (volumeItem.carVolumeItem == targetItem) {
- return volumeItem;
- }
- }
- return null;
+ private CarVolumeItem addCarVolumeListItem(VolumeItem volumeItem, int volumeGroupId,
+ int supplementalIconId,
+ @Nullable View.OnClickListener supplementalIconOnClickListener) {
+ int seekbarProgressValue = getSeekbarValue(mCarAudioManager, volumeGroupId);
+ Drawable supplementalIcon = supplementalIconId == 0 ? null : mContext.getDrawable(
+ supplementalIconId);
+ CarVolumeItem carVolumeItem = createCarVolumeListItem(volumeItem, volumeGroupId,
+ supplementalIcon, seekbarProgressValue, supplementalIconOnClickListener);
+ mCarVolumeLineItems.add(carVolumeItem);
+ return carVolumeItem;
}
private void cleanupAudioManager() {
@@ -544,6 +557,7 @@ public class CarVolumeDialogImpl implements VolumeDialog {
public void onClick(final View v) {
mExpandIcon = v;
toggleDialogExpansion(true);
+ rescheduleTimeoutH();
}
}
@@ -554,21 +568,15 @@ public class CarVolumeDialogImpl implements VolumeDialog {
for (int groupId = 0; groupId < mAvailableVolumeItems.size(); ++groupId) {
if (groupId != mCurrentlyDisplayingGroupId) {
VolumeItem volumeItem = mAvailableVolumeItems.get(groupId);
- addCarVolumeListItem(volumeItem, groupId, 0, null);
+ addCarVolumeListItem(volumeItem, groupId, /* supplementalIconId= */ 0,
+ /* supplementalIconOnClickListener= */ null);
}
}
inAnimator = AnimatorInflater.loadAnimator(
mContext, R.anim.car_arrow_fade_in_rotate_up);
} else {
- // Only keeping the default stream if it is not expended.
- Iterator itr = mCarVolumeLineItems.iterator();
- while (itr.hasNext()) {
- CarVolumeItem carVolumeItem = (CarVolumeItem) itr.next();
- if (carVolumeItem.getGroupId() != mCurrentlyDisplayingGroupId) {
- itr.remove();
- }
- }
+ clearAllAndSetupDefaultCarVolumeLineItem(mCurrentlyDisplayingGroupId);
inAnimator = AnimatorInflater.loadAnimator(
mContext, R.anim.car_arrow_fade_in_rotate_down);
}
@@ -606,18 +614,14 @@ public class CarVolumeDialogImpl implements VolumeDialog {
// sent back down again.
return;
}
- try {
- if (mCarAudioManager == null) {
- Log.w(TAG, "Ignoring volume change event because the car isn't connected");
- return;
- }
- mAvailableVolumeItems.get(mVolumeGroupId).progress = progress;
- mAvailableVolumeItems.get(
- mVolumeGroupId).carVolumeItem.setProgress(progress);
- mCarAudioManager.setGroupVolume(mVolumeGroupId, progress, 0);
- } catch (CarNotConnectedException e) {
- Log.e(TAG, "Car is not connected!", e);
+ if (mCarAudioManager == null) {
+ Log.w(TAG, "Ignoring volume change event because the car isn't connected");
+ return;
}
+ mAvailableVolumeItems.get(mVolumeGroupId).progress = progress;
+ mAvailableVolumeItems.get(
+ mVolumeGroupId).carVolumeItem.setProgress(progress);
+ mCarAudioManager.setGroupVolume(mVolumeGroupId, progress, 0);
}
@Override
diff --git a/packages/SettingsLib/src/com/android/settingslib/utils/ThreadUtils.java b/packages/SettingsLib/src/com/android/settingslib/utils/ThreadUtils.java
index 5c9a06f91e6a..69f1c17f97b1 100644
--- a/packages/SettingsLib/src/com/android/settingslib/utils/ThreadUtils.java
+++ b/packages/SettingsLib/src/com/android/settingslib/utils/ThreadUtils.java
@@ -18,6 +18,7 @@ package com.android.settingslib.utils;
import android.os.Handler;
import android.os.Looper;
+import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
@@ -26,7 +27,7 @@ public class ThreadUtils {
private static volatile Thread sMainThread;
private static volatile Handler sMainThreadHandler;
- private static volatile ExecutorService sSingleThreadExecutor;
+ private static volatile ExecutorService sThreadExecutor;
/**
* Returns true if the current thread is the UI thread.
@@ -64,10 +65,16 @@ public class ThreadUtils {
* @Return A future of the task that can be monitored for updates or cancelled.
*/
public static Future postOnBackgroundThread(Runnable runnable) {
- if (sSingleThreadExecutor == null) {
- sSingleThreadExecutor = Executors.newSingleThreadExecutor();
- }
- return sSingleThreadExecutor.submit(runnable);
+ return getThreadExecutor().submit(runnable);
+ }
+
+ /**
+ * Posts callable in background using shared background thread pool.
+ *
+ * @Return A future of the task that can be monitored for updates or cancelled.
+ */
+ public static Future postOnBackgroundThread(Callable callable) {
+ return getThreadExecutor().submit(callable);
}
/**
@@ -77,4 +84,11 @@ public class ThreadUtils {
getUiThreadHandler().post(runnable);
}
+ private static synchronized ExecutorService getThreadExecutor() {
+ if (sThreadExecutor == null) {
+ sThreadExecutor = Executors.newFixedThreadPool(
+ Runtime.getRuntime().availableProcessors());
+ }
+ return sThreadExecutor;
+ }
}
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/utils/ThreadUtilsTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/utils/ThreadUtilsTest.java
index 26db124c0041..5114b00d2711 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/utils/ThreadUtilsTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/utils/ThreadUtilsTest.java
@@ -50,7 +50,7 @@ public class ThreadUtilsTest {
}
@Test
- public void testPostOnMainThread_shouldRunOnMainTread() {
+ public void testPostOnMainThread_shouldRunOnMainThread() {
TestRunnable cr = new TestRunnable();
ShadowLooper.pauseMainLooper();
ThreadUtils.postOnMainThread(cr);
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
index f8c9bcce1b1c..894ba9cd2f06 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
@@ -1969,6 +1969,12 @@ class SettingsProtoDumpUtil {
dumpSetting(s, p,
Settings.Secure.SKIP_TOUCH_COUNT,
SecureSettingsProto.Gesture.SKIP_TOUCH_COUNT);
+ dumpSetting(s, p,
+ Settings.Secure.AWARE_TAP_PAUSE_GESTURE_COUNT,
+ SecureSettingsProto.Gesture.AWARE_TAP_PAUSE_GESTURE_COUNT);
+ dumpSetting(s, p,
+ Settings.Secure.AWARE_TAP_PAUSE_TOUCH_COUNT,
+ SecureSettingsProto.Gesture.AWARE_TAP_PAUSE_TOUCH_COUNT);
p.end(gestureToken);
dumpSetting(s, p,
diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml
index 403e894a68e4..862abfda6a65 100644
--- a/packages/SystemUI/AndroidManifest.xml
+++ b/packages/SystemUI/AndroidManifest.xml
@@ -60,6 +60,9 @@
<uses-permission android:name="android.permission.GET_APP_OPS_STATS" />
<uses-permission android:name="android.permission.USE_RESERVED_DISK" />
+ <!-- to invoke ContentSuggestionsService -->
+ <uses-permission android:name="android.permission.MANAGE_CONTENT_SUGGESTIONS"/>
+
<!-- Networking and telephony -->
<uses-permission android:name="android.permission.BLUETOOTH" />
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
@@ -366,6 +369,10 @@
<receiver android:name=".screenshot.GlobalScreenshot$DeleteScreenshotReceiver"
android:exported="false" />
+ <!-- Callback for invoking a smart action from the screenshot notification. -->
+ <receiver android:name=".screenshot.GlobalScreenshot$SmartActionsReceiver"
+ android:exported="false"/>
+
<!-- started from UsbDeviceSettingsManager -->
<activity android:name=".usb.UsbConfirmActivity"
android:exported="true"
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index d58f83fb8a3b..236583896289 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -894,6 +894,10 @@
<string name="quick_settings_ui_mode_night_label">Dark theme</string>
<!-- QuickSettings: Label for the dark theme tile when enabled by battery saver. [CHAR LIMIT=40] -->
<string name="quick_settings_ui_mode_night_label_battery_saver">Dark theme\nBattery saver</string>
+ <!-- QuickSettings: Secondary text for when the Dark Mode will be enabled at sunset. [CHAR LIMIT=20] -->
+ <string name="quick_settings_dark_mode_secondary_label_on_at_sunset">On at sunset</string>
+ <!-- QuickSettings: Secondary text for when the Dark Mode will be on until sunrise. [CHAR LIMIT=20] -->
+ <string name="quick_settings_dark_mode_secondary_label_until_sunrise">Until sunrise</string>
<!-- QuickSettings: NFC tile [CHAR LIMIT=NONE] -->
<string name="quick_settings_nfc_label">NFC</string>
@@ -964,6 +968,12 @@
<!-- Message shown when face authentication fails and the pin pad is visible. [CHAR LIMIT=60] -->
<string name="keyguard_retry">Swipe up to try again</string>
+ <!-- Indication when device is slow charging due to misalignment on the dock. [CHAR LIMIT=60] -->
+ <string name="dock_alignment_slow_charging" product="default">Realign phone for faster charging</string>
+
+ <!-- Indication when device is not charging due to bad placement on the dock. [CHAR LIMIT=60] -->
+ <string name="dock_alignment_not_charging" product="default">Realign phone to charge wirelessly</string>
+
<!-- Text on keyguard screen and in Quick Settings footer indicating that the device is enterprise-managed by a Device Owner [CHAR LIMIT=60] -->
<string name="do_disclosure_generic">This device is managed by your organization</string>
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java
index ca7cd0d666ad..ace24a3b7ce1 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java
@@ -44,6 +44,7 @@ import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.internal.widget.LockPatternUtils;
import com.android.keyguard.KeyguardSecurityModel.SecurityMode;
+import com.android.settingslib.utils.ThreadUtils;
import com.android.systemui.Dependency;
import com.android.systemui.R;
import com.android.systemui.SystemUIFactory;
@@ -615,6 +616,15 @@ public class KeyguardSecurityContainer extends FrameLayout implements KeyguardSe
StatsLog.write(StatsLog.KEYGUARD_BOUNCER_PASSWORD_ENTERED,
StatsLog.KEYGUARD_BOUNCER_PASSWORD_ENTERED__RESULT__SUCCESS);
mLockPatternUtils.reportSuccessfulPasswordAttempt(userId);
+ // Force a garbage collection in an attempt to erase any lockscreen password left in
+ // memory. Do it asynchronously with a 5-sec delay to avoid making the keyguard
+ // dismiss animation janky.
+ ThreadUtils.postOnBackgroundThread(() -> {
+ try {
+ Thread.sleep(5000);
+ } catch (InterruptedException ignored) { }
+ Runtime.getRuntime().gc();
+ });
} else {
StatsLog.write(StatsLog.KEYGUARD_BOUNCER_PASSWORD_ENTERED,
StatsLog.KEYGUARD_BOUNCER_PASSWORD_ENTERED__RESULT__FAILURE);
diff --git a/packages/SystemUI/src/com/android/systemui/ForegroundServiceLifetimeExtender.java b/packages/SystemUI/src/com/android/systemui/ForegroundServiceLifetimeExtender.java
deleted file mode 100644
index 05acdd080aa5..000000000000
--- a/packages/SystemUI/src/com/android/systemui/ForegroundServiceLifetimeExtender.java
+++ /dev/null
@@ -1,90 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui;
-
-import android.annotation.NonNull;
-import android.app.Notification;
-import android.os.Handler;
-import android.os.Looper;
-import android.util.ArraySet;
-
-import com.android.internal.annotations.VisibleForTesting;
-import com.android.systemui.statusbar.NotificationLifetimeExtender;
-import com.android.systemui.statusbar.notification.collection.NotificationEntry;
-
-/**
- * Extends the lifetime of foreground notification services such that they show for at least
- * five seconds
- */
-public class ForegroundServiceLifetimeExtender implements NotificationLifetimeExtender {
-
- private static final String TAG = "FGSLifetimeExtender";
- @VisibleForTesting
- static final int MIN_FGS_TIME_MS = 5000;
-
- private NotificationSafeToRemoveCallback mNotificationSafeToRemoveCallback;
- private ArraySet<NotificationEntry> mManagedEntries = new ArraySet<>();
- private Handler mHandler = new Handler(Looper.getMainLooper());
-
- public ForegroundServiceLifetimeExtender() {
- }
-
- @Override
- public void setCallback(@NonNull NotificationSafeToRemoveCallback callback) {
- mNotificationSafeToRemoveCallback = callback;
- }
-
- @Override
- public boolean shouldExtendLifetime(@NonNull NotificationEntry entry) {
- if ((entry.notification.getNotification().flags
- & Notification.FLAG_FOREGROUND_SERVICE) == 0) {
- return false;
- }
-
- long currentTime = System.currentTimeMillis();
- return currentTime - entry.notification.getPostTime() < MIN_FGS_TIME_MS;
- }
-
- @Override
- public boolean shouldExtendLifetimeForPendingNotification(
- @NonNull NotificationEntry entry) {
- return shouldExtendLifetime(entry);
- }
-
- @Override
- public void setShouldManageLifetime(
- @NonNull NotificationEntry entry, boolean shouldManage) {
- if (!shouldManage) {
- mManagedEntries.remove(entry);
- return;
- }
-
- mManagedEntries.add(entry);
-
- Runnable r = () -> {
- if (mManagedEntries.contains(entry)) {
- mManagedEntries.remove(entry);
- if (mNotificationSafeToRemoveCallback != null) {
- mNotificationSafeToRemoveCallback.onSafeToRemove(entry.key);
- }
- }
- };
- long delayAmt = MIN_FGS_TIME_MS
- - (System.currentTimeMillis() - entry.notification.getPostTime());
- mHandler.postDelayed(r, delayAmt);
- }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/ForegroundServiceNotificationListener.java b/packages/SystemUI/src/com/android/systemui/ForegroundServiceNotificationListener.java
index 0162deb55143..96b62ac918ab 100644
--- a/packages/SystemUI/src/com/android/systemui/ForegroundServiceNotificationListener.java
+++ b/packages/SystemUI/src/com/android/systemui/ForegroundServiceNotificationListener.java
@@ -66,9 +66,6 @@ public class ForegroundServiceNotificationListener {
removeNotification(entry.notification);
}
});
-
- notificationEntryManager.addNotificationLifetimeExtender(
- new ForegroundServiceLifetimeExtender());
}
/**
diff --git a/packages/SystemUI/src/com/android/systemui/ImageWallpaper.java b/packages/SystemUI/src/com/android/systemui/ImageWallpaper.java
index bd91333100bd..c0c14fb01222 100644
--- a/packages/SystemUI/src/com/android/systemui/ImageWallpaper.java
+++ b/packages/SystemUI/src/com/android/systemui/ImageWallpaper.java
@@ -20,6 +20,7 @@ import android.app.ActivityManager;
import android.content.Context;
import android.graphics.Rect;
import android.os.HandlerThread;
+import android.os.Trace;
import android.service.wallpaper.WallpaperService;
import android.util.Log;
import android.util.Size;
@@ -48,6 +49,7 @@ public class ImageWallpaper extends WallpaperService {
private static final int DELAY_FINISH_RENDERING = 1000;
private static final int INTERVAL_WAIT_FOR_RENDERING = 100;
private static final int PATIENCE_WAIT_FOR_RENDERING = 10;
+ private static final boolean DEBUG = true;
private HandlerThread mWorker;
@Override
@@ -125,6 +127,10 @@ public class ImageWallpaper extends WallpaperService {
@Override
public void onAmbientModeChanged(boolean inAmbientMode, long animationDuration) {
if (!mNeedTransition) return;
+ if (DEBUG) {
+ Log.d(TAG, "onAmbientModeChanged: inAmbient=" + inAmbientMode
+ + ", duration=" + animationDuration);
+ }
mWorker.getThreadHandler().post(
() -> mRenderer.updateAmbientMode(inAmbientMode, animationDuration));
if (inAmbientMode && animationDuration == 0) {
@@ -177,6 +183,10 @@ public class ImageWallpaper extends WallpaperService {
@Override
public void onSurfaceChanged(SurfaceHolder holder, int format, int width, int height) {
mWorker.getThreadHandler().post(() -> {
+ if (DEBUG) {
+ Log.d(TAG, "onSurfaceChanged: w=" + width + ", h=" + height);
+ }
+
mRenderer.onSurfaceChanged(width, height);
mNeedRedraw = true;
});
@@ -185,16 +195,31 @@ public class ImageWallpaper extends WallpaperService {
@Override
public void onSurfaceRedrawNeeded(SurfaceHolder holder) {
mWorker.getThreadHandler().post(() -> {
+ if (DEBUG) {
+ Log.d(TAG, "onSurfaceRedrawNeeded: mNeedRedraw=" + mNeedRedraw);
+ }
+
if (mNeedRedraw) {
- preRender();
- requestRender();
- postRender();
+ drawFrame();
mNeedRedraw = false;
}
});
}
@Override
+ public void onVisibilityChanged(boolean visible) {
+ if (DEBUG) {
+ Log.d(TAG, "wallpaper visibility changes to: " + visible);
+ }
+ }
+
+ private void drawFrame() {
+ preRender();
+ requestRender();
+ postRender();
+ }
+
+ @Override
public void onStatePostChange() {
// When back to home, we try to release EGL, which is preserved in lock screen or aod.
if (mController.getState() == StatusBarState.SHADE) {
@@ -204,8 +229,18 @@ public class ImageWallpaper extends WallpaperService {
@Override
public void preRender() {
+ if (DEBUG) {
+ Log.d(TAG, "preRender start");
+ }
+
// This method should only be invoked from worker thread.
+ Trace.beginSection("ImageWallpaper#preRender");
preRenderInternal();
+ Trace.endSection();
+
+ if (DEBUG) {
+ Log.d(TAG, "preRender end");
+ }
}
private void preRenderInternal() {
@@ -240,7 +275,9 @@ public class ImageWallpaper extends WallpaperService {
@Override
public void requestRender() {
// This method should only be invoked from worker thread.
+ Trace.beginSection("ImageWallpaper#requestRender");
requestRenderInternal();
+ Trace.endSection();
}
private void requestRenderInternal() {
@@ -262,9 +299,19 @@ public class ImageWallpaper extends WallpaperService {
@Override
public void postRender() {
+ if (DEBUG) {
+ Log.d(TAG, "postRender start");
+ }
+
// This method should only be invoked from worker thread.
+ Trace.beginSection("ImageWallpaper#postRender");
notifyWaitingThread();
scheduleFinishRendering();
+ Trace.endSection();
+
+ if (DEBUG) {
+ Log.d(TAG, "postRender end");
+ }
}
private void notifyWaitingThread() {
@@ -289,12 +336,18 @@ public class ImageWallpaper extends WallpaperService {
}
private void finishRendering() {
+ if (DEBUG) {
+ Log.d(TAG, "finishRendering, preserve=" + needPreserveEglContext());
+ }
+
+ Trace.beginSection("ImageWallpaper#finishRendering");
if (mEglHelper != null) {
mEglHelper.destroyEglSurface();
if (!needPreserveEglContext()) {
mEglHelper.destroyEglContext();
}
}
+ Trace.endSection();
}
private boolean needPreserveEglContext() {
diff --git a/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java b/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java
index 48127a75b86e..9252e5767996 100644
--- a/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java
+++ b/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java
@@ -142,7 +142,7 @@ public class SystemUIApplication extends Application implements SysUiServiceProv
public void startServicesIfNeeded() {
String[] names = getResources().getStringArray(R.array.config_systemUIServiceComponents);
- startServicesIfNeeded(names);
+ startServicesIfNeeded(/* metricsPrefix= */ "StartServices", names);
}
/**
@@ -154,10 +154,10 @@ public class SystemUIApplication extends Application implements SysUiServiceProv
void startSecondaryUserServicesIfNeeded() {
String[] names =
getResources().getStringArray(R.array.config_systemUIServiceComponentsPerUser);
- startServicesIfNeeded(names);
+ startServicesIfNeeded(/* metricsPrefix= */ "StartSecondaryServices", names);
}
- private void startServicesIfNeeded(String[] services) {
+ private void startServicesIfNeeded(String metricsPrefix, String[] services) {
if (mServicesStarted) {
return;
}
@@ -176,14 +176,16 @@ public class SystemUIApplication extends Application implements SysUiServiceProv
Log.v(TAG, "Starting SystemUI services for user " +
Process.myUserHandle().getIdentifier() + ".");
+
TimingsTraceLog log = new TimingsTraceLog("SystemUIBootTiming",
Trace.TRACE_TAG_APP);
- log.traceBegin("StartServices");
+ log.traceBegin(metricsPrefix);
+
final int N = services.length;
for (int i = 0; i < N; i++) {
String clsName = services[i];
if (DEBUG) Log.d(TAG, "loading: " + clsName);
- log.traceBegin("StartServices" + clsName);
+ log.traceBegin(metricsPrefix + clsName);
long ti = System.currentTimeMillis();
Class cls;
try {
diff --git a/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java b/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java
index 0899d955a1ac..2531b6007c34 100644
--- a/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java
+++ b/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java
@@ -31,6 +31,7 @@ import com.android.keyguard.ViewMediatorCallback;
import com.android.systemui.keyguard.DismissCallbackRegistry;
import com.android.systemui.plugins.FalsingManager;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
+import com.android.systemui.screenshot.ScreenshotNotificationSmartActionsProvider;
import com.android.systemui.statusbar.KeyguardIndicationController;
import com.android.systemui.statusbar.NotificationMediaManager;
import com.android.systemui.statusbar.ScrimView;
@@ -49,6 +50,7 @@ import com.android.systemui.statusbar.phone.UnlockMethodCache;
import com.android.systemui.statusbar.policy.KeyguardMonitor;
import com.android.systemui.volume.VolumeDialogComponent;
+import java.util.concurrent.Executor;
import java.util.function.Consumer;
import dagger.Module;
@@ -110,6 +112,17 @@ public class SystemUIFactory {
return new StatusBarKeyguardViewManager(context, viewMediatorCallback, lockPatternUtils);
}
+ /**
+ * Creates an instance of ScreenshotNotificationSmartActionsProvider.
+ * This method is overridden in vendor specific implementation of Sys UI.
+ */
+ public ScreenshotNotificationSmartActionsProvider
+ createScreenshotNotificationSmartActionsProvider(Context context,
+ Executor executor,
+ Handler uiHandler) {
+ return new ScreenshotNotificationSmartActionsProvider();
+ }
+
public KeyguardBouncer createKeyguardBouncer(Context context, ViewMediatorCallback callback,
LockPatternUtils lockPatternUtils, ViewGroup container,
DismissCallbackRegistry dismissCallbackRegistry,
diff --git a/packages/SystemUI/src/com/android/systemui/SystemUIService.java b/packages/SystemUI/src/com/android/systemui/SystemUIService.java
index 1c5e80005d84..150a40abde7f 100644
--- a/packages/SystemUI/src/com/android/systemui/SystemUIService.java
+++ b/packages/SystemUI/src/com/android/systemui/SystemUIService.java
@@ -16,6 +16,7 @@
package com.android.systemui;
+import android.annotation.NonNull;
import android.app.Service;
import android.content.Intent;
import android.os.Build;
@@ -66,7 +67,13 @@ public class SystemUIService extends Service {
@Override
protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+ if (args != null && args.length > 0 && args[0].equals("--config")) {
+ dumpConfig(pw);
+ return;
+ }
+
dumpServices(((SystemUIApplication) getApplication()).getServices(), fd, pw, args);
+ dumpConfig(pw);
}
static void dumpServices(
@@ -95,5 +102,29 @@ public class SystemUIService extends Service {
}
}
}
+
+ private void dumpConfig(@NonNull PrintWriter pw) {
+ pw.println("SystemUiServiceComponents configuration:");
+
+ pw.print("vendor component: ");
+ pw.println(getResources().getString(R.string.config_systemUIVendorServiceComponent));
+
+ dumpConfig(pw, "global", R.array.config_systemUIServiceComponents);
+ dumpConfig(pw, "per-user", R.array.config_systemUIServiceComponentsPerUser);
+ }
+
+ private void dumpConfig(@NonNull PrintWriter pw, @NonNull String type, int resId) {
+ final String[] services = getResources().getStringArray(resId);
+ pw.print(type); pw.print(": ");
+ if (services == null) {
+ pw.println("N/A");
+ return;
+ }
+ pw.print(services.length);
+ pw.println(" services");
+ for (int i = 0; i < services.length; i++) {
+ pw.print(" "); pw.print(i); pw.print(": "); pw.println(services[i]);
+ }
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/assist/AssistHandleBehaviorController.java b/packages/SystemUI/src/com/android/systemui/assist/AssistHandleBehaviorController.java
index 738ec80a40c4..8a8c6d1f1ec8 100644
--- a/packages/SystemUI/src/com/android/systemui/assist/AssistHandleBehaviorController.java
+++ b/packages/SystemUI/src/com/android/systemui/assist/AssistHandleBehaviorController.java
@@ -72,7 +72,7 @@ public final class AssistHandleBehaviorController implements AssistHandleCallbac
private final Runnable mHideHandles = this::hideHandles;
private final Runnable mShowAndGo = this::showAndGoInternal;
private final Provider<ScreenDecorations> mScreenDecorations;
- private final PhenotypeHelper mPhenotypeHelper;
+ private final DeviceConfigHelper mDeviceConfigHelper;
private final Map<AssistHandleBehavior, BehaviorController> mBehaviorMap;
private boolean mHandlesShowing = false;
@@ -91,7 +91,7 @@ public final class AssistHandleBehaviorController implements AssistHandleCallbac
AssistUtils assistUtils,
@Named(ASSIST_HANDLE_THREAD_NAME) Handler handler,
Provider<ScreenDecorations> screenDecorations,
- PhenotypeHelper phenotypeHelper,
+ DeviceConfigHelper deviceConfigHelper,
Map<AssistHandleBehavior, BehaviorController> behaviorMap,
NavigationModeController navigationModeController,
DumpController dumpController) {
@@ -99,14 +99,14 @@ public final class AssistHandleBehaviorController implements AssistHandleCallbac
mAssistUtils = assistUtils;
mHandler = handler;
mScreenDecorations = screenDecorations;
- mPhenotypeHelper = phenotypeHelper;
+ mDeviceConfigHelper = deviceConfigHelper;
mBehaviorMap = behaviorMap;
mInGesturalMode = QuickStepContract.isGesturalMode(
navigationModeController.addListener(this::handleNavigationModeChange));
setBehavior(getBehaviorMode());
- mPhenotypeHelper.addOnPropertiesChangedListener(
+ mDeviceConfigHelper.addOnPropertiesChangedListener(
mHandler::post,
(properties) -> {
if (properties.getKeyset().contains(
@@ -206,19 +206,19 @@ public final class AssistHandleBehaviorController implements AssistHandleCallbac
}
private long getShownFrequencyThreshold() {
- return mPhenotypeHelper.getLong(
+ return mDeviceConfigHelper.getLong(
SystemUiDeviceConfigFlags.ASSIST_HANDLES_SHOWN_FREQUENCY_THRESHOLD_MS,
DEFAULT_SHOWN_FREQUENCY_THRESHOLD_MS);
}
private long getShowAndGoDuration() {
- return mPhenotypeHelper.getLong(
+ return mDeviceConfigHelper.getLong(
SystemUiDeviceConfigFlags.ASSIST_HANDLES_SHOW_AND_GO_DURATION_MS,
DEFAULT_SHOW_AND_GO_DURATION_MS);
}
private String getBehaviorMode() {
- return mPhenotypeHelper.getString(
+ return mDeviceConfigHelper.getString(
SystemUiDeviceConfigFlags.ASSIST_HANDLES_BEHAVIOR_MODE,
DEFAULT_BEHAVIOR.toString());
}
diff --git a/packages/SystemUI/src/com/android/systemui/assist/AssistHandleReminderExpBehavior.java b/packages/SystemUI/src/com/android/systemui/assist/AssistHandleReminderExpBehavior.java
index 039404800fbb..b1fcd9d74980 100644
--- a/packages/SystemUI/src/com/android/systemui/assist/AssistHandleReminderExpBehavior.java
+++ b/packages/SystemUI/src/com/android/systemui/assist/AssistHandleReminderExpBehavior.java
@@ -156,7 +156,7 @@ final class AssistHandleReminderExpBehavior implements BehaviorController {
private final Clock mClock;
private final Handler mHandler;
- private final PhenotypeHelper mPhenotypeHelper;
+ private final DeviceConfigHelper mDeviceConfigHelper;
private final Lazy<StatusBarStateController> mStatusBarStateController;
private final Lazy<ActivityManagerWrapper> mActivityManagerWrapper;
private final Lazy<OverviewProxyService> mOverviewProxyService;
@@ -188,7 +188,7 @@ final class AssistHandleReminderExpBehavior implements BehaviorController {
AssistHandleReminderExpBehavior(
@Named(UPTIME_NAME) Clock clock,
@Named(ASSIST_HANDLE_THREAD_NAME) Handler handler,
- PhenotypeHelper phenotypeHelper,
+ DeviceConfigHelper deviceConfigHelper,
Lazy<StatusBarStateController> statusBarStateController,
Lazy<ActivityManagerWrapper> activityManagerWrapper,
Lazy<OverviewProxyService> overviewProxyService,
@@ -196,7 +196,7 @@ final class AssistHandleReminderExpBehavior implements BehaviorController {
Lazy<PackageManagerWrapper> packageManagerWrapper) {
mClock = clock;
mHandler = handler;
- mPhenotypeHelper = phenotypeHelper;
+ mDeviceConfigHelper = deviceConfigHelper;
mStatusBarStateController = statusBarStateController;
mActivityManagerWrapper = activityManagerWrapper;
mOverviewProxyService = overviewProxyService;
@@ -457,55 +457,55 @@ final class AssistHandleReminderExpBehavior implements BehaviorController {
}
private long getLearningTimeMs() {
- return mPhenotypeHelper.getLong(
+ return mDeviceConfigHelper.getLong(
SystemUiDeviceConfigFlags.ASSIST_HANDLES_LEARN_TIME_MS,
DEFAULT_LEARNING_TIME_MS);
}
private int getLearningCount() {
- return mPhenotypeHelper.getInt(
+ return mDeviceConfigHelper.getInt(
SystemUiDeviceConfigFlags.ASSIST_HANDLES_LEARN_COUNT,
DEFAULT_LEARNING_COUNT);
}
private long getShowAndGoDelayedShortDelayMs() {
- return mPhenotypeHelper.getLong(
+ return mDeviceConfigHelper.getLong(
SystemUiDeviceConfigFlags.ASSIST_HANDLES_SHOW_AND_GO_DELAYED_SHORT_DELAY_MS,
DEFAULT_SHOW_AND_GO_DELAYED_SHORT_DELAY_MS);
}
private long getShowAndGoDelayedLongDelayMs() {
- return mPhenotypeHelper.getLong(
+ return mDeviceConfigHelper.getLong(
SystemUiDeviceConfigFlags.ASSIST_HANDLES_SHOW_AND_GO_DELAYED_LONG_DELAY_MS,
DEFAULT_SHOW_AND_GO_DELAYED_LONG_DELAY_MS);
}
private long getShowAndGoDelayResetTimeoutMs() {
- return mPhenotypeHelper.getLong(
+ return mDeviceConfigHelper.getLong(
SystemUiDeviceConfigFlags.ASSIST_HANDLES_SHOW_AND_GO_DELAY_RESET_TIMEOUT_MS,
DEFAULT_SHOW_AND_GO_DELAY_RESET_TIMEOUT_MS);
}
private boolean getSuppressOnLockscreen() {
- return mPhenotypeHelper.getBoolean(
+ return mDeviceConfigHelper.getBoolean(
SystemUiDeviceConfigFlags.ASSIST_HANDLES_SUPPRESS_ON_LOCKSCREEN,
DEFAULT_SUPPRESS_ON_LOCKSCREEN);
}
private boolean getSuppressOnLauncher() {
- return mPhenotypeHelper.getBoolean(
+ return mDeviceConfigHelper.getBoolean(
SystemUiDeviceConfigFlags.ASSIST_HANDLES_SUPPRESS_ON_LAUNCHER,
DEFAULT_SUPPRESS_ON_LAUNCHER);
}
private boolean getSuppressOnApps() {
- return mPhenotypeHelper.getBoolean(
+ return mDeviceConfigHelper.getBoolean(
SystemUiDeviceConfigFlags.ASSIST_HANDLES_SUPPRESS_ON_APPS,
DEFAULT_SUPPRESS_ON_APPS);
}
private boolean getShowWhenTaught() {
- return mPhenotypeHelper.getBoolean(
+ return mDeviceConfigHelper.getBoolean(
SystemUiDeviceConfigFlags.ASSIST_HANDLES_SHOW_WHEN_TAUGHT,
DEFAULT_SHOW_WHEN_TAUGHT);
}
diff --git a/packages/SystemUI/src/com/android/systemui/assist/PhenotypeHelper.java b/packages/SystemUI/src/com/android/systemui/assist/DeviceConfigHelper.java
index ff76adfd5b6a..3005c97b46f0 100644
--- a/packages/SystemUI/src/com/android/systemui/assist/PhenotypeHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/assist/DeviceConfigHelper.java
@@ -26,15 +26,15 @@ import javax.inject.Inject;
import javax.inject.Singleton;
/**
- * Wrapper class for retrieving phenotype flag values.
+ * Wrapper class for retrieving System UI device configuration values.
*
* Can be mocked in tests for ease of testing the effects of particular values.
*/
@Singleton
-public class PhenotypeHelper {
+public class DeviceConfigHelper {
@Inject
- public PhenotypeHelper() {}
+ public DeviceConfigHelper() {}
public long getLong(String name, long defaultValue) {
return DeviceConfig.getLong(DeviceConfig.NAMESPACE_SYSTEMUI, name, defaultValue);
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/BiometricDialogView.java b/packages/SystemUI/src/com/android/systemui/biometrics/BiometricDialogView.java
index ce67577ea483..cbbd3a0b0562 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/BiometricDialogView.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/BiometricDialogView.java
@@ -499,6 +499,8 @@ public abstract class BiometricDialogView extends LinearLayout {
if (newState == STATE_PENDING_CONFIRMATION || newState == STATE_AUTHENTICATED) {
mNegativeButton.setText(R.string.cancel);
mNegativeButton.setContentDescription(getResources().getString(R.string.cancel));
+ } else {
+ mNegativeButton.setText(mBundle.getCharSequence(BiometricPrompt.KEY_NEGATIVE_TEXT));
}
updateIcon(mState, newState);
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleData.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleData.java
index eb826e54d340..d43e030ed9eb 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleData.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleData.java
@@ -184,6 +184,8 @@ public class BubbleData {
Log.d(TAG, "notificationEntryUpdated: " + entry);
}
Bubble bubble = getBubbleWithKey(entry.key);
+ suppressFlyout = !entry.isVisuallyInterruptive || suppressFlyout;
+
if (bubble == null) {
// Create a new bubble
bubble = new Bubble(mContext, entry);
@@ -193,8 +195,10 @@ public class BubbleData {
} else {
// Updates an existing bubble
bubble.updateEntry(entry);
+ bubble.setSuppressFlyout(suppressFlyout);
doUpdate(bubble);
}
+
if (bubble.shouldAutoExpand()) {
setSelectedBubbleInternal(bubble);
if (!mExpanded) {
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java
index b68b7627fe8c..31cf853dce04 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java
@@ -169,7 +169,7 @@ public class BubbleStackView extends FrameLayout {
* Callback to run after the flyout hides. Also called if a new flyout is shown before the
* previous one animates out.
*/
- private Runnable mAfterFlyoutHides;
+ private Runnable mFlyoutOnHide;
/** Layout change listener that moves the stack to the nearest valid position on rotation. */
private OnLayoutChangeListener mOrientationChangedListener;
@@ -1401,111 +1401,106 @@ public class BubbleStackView extends FrameLayout {
@VisibleForTesting
void animateInFlyoutForBubble(Bubble bubble) {
final CharSequence updateMessage = bubble.getUpdateMessage(getContext());
-
if (!bubble.showFlyoutForBubble()) {
// In case flyout was suppressed for this update, reset now.
bubble.setSuppressFlyout(false);
return;
}
-
if (updateMessage == null
|| isExpanded()
|| mIsExpansionAnimating
|| mIsGestureInProgress
- || mBubbleToExpandAfterFlyoutCollapse != null) {
+ || mBubbleToExpandAfterFlyoutCollapse != null
+ || bubble.getIconView() == null) {
// Skip the message if none exists, we're expanded or animating expansion, or we're
- // about to expand a bubble from the previous tapped flyout.
+ // about to expand a bubble from the previous tapped flyout, or if bubble view is null.
return;
}
-
- if (bubble.getIconView() != null) {
- // Temporarily suppress the dot while the flyout is visible.
- bubble.getIconView().setSuppressDot(
- true /* suppressDot */, false /* animate */);
-
- mFlyout.removeCallbacks(mAnimateInFlyout);
- mFlyoutDragDeltaX = 0f;
-
- if (mAfterFlyoutHides != null) {
- mAfterFlyoutHides.run();
+ mFlyoutDragDeltaX = 0f;
+ clearFlyoutOnHide();
+ mFlyoutOnHide = () -> {
+ resetDot(bubble);
+ if (mBubbleToExpandAfterFlyoutCollapse == null) {
+ return;
}
-
- mAfterFlyoutHides = () -> {
- final boolean suppressDot = !bubble.showBubbleDot();
- // If we're going to suppress the dot, make it visible first so it'll
- // visibly animate away.
- if (suppressDot) {
- bubble.getIconView().setSuppressDot(
- false /* suppressDot */, false /* animate */);
- }
- // Reset dot suppression. If we're not suppressing due to DND, then
- // stop suppressing it with no animation (since the flyout has
- // transformed into the dot). If we are suppressing due to DND, animate
- // it away.
- bubble.getIconView().setSuppressDot(
- suppressDot /* suppressDot */,
- suppressDot /* animate */);
-
- if (mBubbleToExpandAfterFlyoutCollapse != null) {
- mBubbleData.setSelectedBubble(mBubbleToExpandAfterFlyoutCollapse);
- mBubbleData.setExpanded(true);
- mBubbleToExpandAfterFlyoutCollapse = null;
- }
- };
-
- mFlyout.setVisibility(INVISIBLE);
-
- // Post in case layout isn't complete and getWidth returns 0.
- post(() -> {
- // An auto-expanding bubble could have been posted during the time it takes to
- // layout.
- if (isExpanded()) {
- return;
- }
-
- final Runnable afterShow = () -> {
- mAnimateInFlyout = () -> {
- mFlyout.setVisibility(VISIBLE);
- bubble.getIconView().setSuppressDot(
- true /* suppressDot */, false /* animate */);
- mFlyoutDragDeltaX =
- mStackAnimationController.isStackOnLeftSide()
- ? -mFlyout.getWidth()
- : mFlyout.getWidth();
- animateFlyoutCollapsed(false /* collapsed */, 0 /* velX */);
- mFlyout.postDelayed(mHideFlyout, FLYOUT_HIDE_AFTER);
- };
-
- mFlyout.postDelayed(mAnimateInFlyout, 200);
+ mBubbleData.setSelectedBubble(mBubbleToExpandAfterFlyoutCollapse);
+ mBubbleData.setExpanded(true);
+ mBubbleToExpandAfterFlyoutCollapse = null;
+ };
+ mFlyout.setVisibility(INVISIBLE);
+
+ // Temporarily suppress the dot while the flyout is visible.
+ bubble.getIconView().setSuppressDot(
+ true /* suppressDot */, false /* animate */);
+
+ // Start flyout expansion. Post in case layout isn't complete and getWidth returns 0.
+ post(() -> {
+ // An auto-expanding bubble could have been posted during the time it takes to
+ // layout.
+ if (isExpanded()) {
+ return;
+ }
+ final Runnable expandFlyoutAfterDelay = () -> {
+ mAnimateInFlyout = () -> {
+ mFlyout.setVisibility(VISIBLE);
+ mFlyoutDragDeltaX =
+ mStackAnimationController.isStackOnLeftSide()
+ ? -mFlyout.getWidth()
+ : mFlyout.getWidth();
+ animateFlyoutCollapsed(false /* collapsed */, 0 /* velX */);
+ mFlyout.postDelayed(mHideFlyout, FLYOUT_HIDE_AFTER);
};
-
- mFlyout.setupFlyoutStartingAsDot(
- updateMessage, mStackAnimationController.getStackPosition(), getWidth(),
- mStackAnimationController.isStackOnLeftSide(),
- bubble.getIconView().getBadgeColor(),
- afterShow,
- mAfterFlyoutHides,
- bubble.getIconView().getDotCenter());
- mFlyout.bringToFront();
- });
- }
-
+ mFlyout.postDelayed(mAnimateInFlyout, 200);
+ };
+ mFlyout.setupFlyoutStartingAsDot(
+ updateMessage, mStackAnimationController.getStackPosition(), getWidth(),
+ mStackAnimationController.isStackOnLeftSide(),
+ bubble.getIconView().getBadgeColor() /* dotColor */,
+ expandFlyoutAfterDelay /* onLayoutComplete */,
+ mFlyoutOnHide,
+ bubble.getIconView().getDotCenter());
+ mFlyout.bringToFront();
+ });
mFlyout.removeCallbacks(mHideFlyout);
mFlyout.postDelayed(mHideFlyout, FLYOUT_HIDE_AFTER);
logBubbleEvent(bubble, StatsLog.BUBBLE_UICHANGED__ACTION__FLYOUT);
}
- /** Hide the flyout immediately and cancel any pending hide runnables. */
- private void hideFlyoutImmediate() {
- if (mAfterFlyoutHides != null) {
- mAfterFlyoutHides.run();
+ private void resetDot(Bubble bubble) {
+ final boolean suppressDot = !bubble.showBubbleDot();
+ // If we're going to suppress the dot, make it visible first so it'll
+ // visibly animate away.
+
+ if (suppressDot) {
+ bubble.getIconView().setSuppressDot(
+ false /* suppressDot */, false /* animate */);
}
+ // Reset dot suppression. If we're not suppressing due to DND, then
+ // stop suppressing it with no animation (since the flyout has
+ // transformed into the dot). If we are suppressing due to DND, animate
+ // it away.
+ bubble.getIconView().setSuppressDot(
+ suppressDot /* suppressDot */,
+ suppressDot /* animate */);
+ }
+ /** Hide the flyout immediately and cancel any pending hide runnables. */
+ private void hideFlyoutImmediate() {
+ clearFlyoutOnHide();
mFlyout.removeCallbacks(mAnimateInFlyout);
mFlyout.removeCallbacks(mHideFlyout);
mFlyout.hideFlyout();
}
+ private void clearFlyoutOnHide() {
+ mFlyout.removeCallbacks(mAnimateInFlyout);
+ if (mFlyoutOnHide == null) {
+ return;
+ }
+ mFlyoutOnHide.run();
+ mFlyoutOnHide = null;
+ }
+
@Override
public void getBoundsOnScreen(Rect outRect) {
if (!mIsExpanded) {
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleView.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleView.java
index 603c4169c169..4512aa822e3b 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleView.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleView.java
@@ -61,7 +61,7 @@ public class BubbleView extends FrameLayout {
// mBubbleIconFactory cannot be static because it depends on Context.
private BubbleIconFactory mBubbleIconFactory;
- private boolean mSuppressDot = false;
+ private boolean mSuppressDot;
private Bubble mBubble;
@@ -140,6 +140,7 @@ public class BubbleView extends FrameLayout {
public void setAppIcon(Drawable appIcon) {
mUserBadgedAppIcon = appIcon;
}
+
/**
* @return the {@link ExpandableNotificationRow} view to display notification content when the
* bubble is expanded.
@@ -154,7 +155,6 @@ public class BubbleView extends FrameLayout {
updateDotVisibility(animate, null /* after */);
}
-
/**
* Sets whether or not to hide the dot even if we'd otherwise show it. This is used while the
* flyout is visible or animating, to hide the dot until the flyout visually transforms into it.
@@ -166,7 +166,7 @@ public class BubbleView extends FrameLayout {
/** Sets the position of the 'new' dot, animating it out and back in if requested. */
void setDotPosition(boolean onLeft, boolean animate) {
- if (animate && onLeft != mBadgedImageView.getDotOnLeft() && !mSuppressDot) {
+ if (animate && onLeft != mBadgedImageView.getDotOnLeft() && shouldShowDot()) {
animateDot(false /* showDot */, () -> {
mBadgedImageView.setDotOnLeft(onLeft);
animateDot(true /* showDot */, null);
@@ -190,12 +190,12 @@ public class BubbleView extends FrameLayout {
* after animation if requested.
*/
private void updateDotVisibility(boolean animate, Runnable after) {
- boolean showDot = mBubble.showBubbleDot() && !mSuppressDot;
-
+ final boolean showDot = shouldShowDot();
if (animate) {
animateDot(showDot, after);
} else {
mBadgedImageView.setShowDot(showDot);
+ mBadgedImageView.setDotScale(showDot ? 1f : 0f);
}
}
@@ -203,27 +203,25 @@ public class BubbleView extends FrameLayout {
* Animates the badge to show or hide.
*/
private void animateDot(boolean showDot, Runnable after) {
- if (mBadgedImageView.isShowingDot() != showDot) {
- if (showDot) {
- mBadgedImageView.setShowDot(true);
- }
- mBadgedImageView.clearAnimation();
- mBadgedImageView.animate().setDuration(200)
- .setInterpolator(Interpolators.FAST_OUT_SLOW_IN)
- .setUpdateListener((valueAnimator) -> {
- float fraction = valueAnimator.getAnimatedFraction();
- fraction = showDot ? fraction : 1f - fraction;
- mBadgedImageView.setDotScale(fraction);
- }).withEndAction(() -> {
- if (!showDot) {
- mBadgedImageView.setShowDot(false);
- }
-
- if (after != null) {
- after.run();
- }
- }).start();
+ if (mBadgedImageView.isShowingDot() == showDot) {
+ return;
}
+ // Do NOT wait until after animation ends to setShowDot
+ // to avoid overriding more recent showDot states.
+ mBadgedImageView.setShowDot(showDot);
+ mBadgedImageView.clearAnimation();
+ mBadgedImageView.animate().setDuration(200)
+ .setInterpolator(Interpolators.FAST_OUT_SLOW_IN)
+ .setUpdateListener((valueAnimator) -> {
+ float fraction = valueAnimator.getAnimatedFraction();
+ fraction = showDot ? fraction : 1f - fraction;
+ mBadgedImageView.setDotScale(fraction);
+ }).withEndAction(() -> {
+ mBadgedImageView.setDotScale(showDot ? 1f : 0f);
+ if (after != null) {
+ after.run();
+ }
+ }).start();
}
void updateViews() {
@@ -273,7 +271,11 @@ public class BubbleView extends FrameLayout {
iconPath.transform(matrix);
mBadgedImageView.drawDot(iconPath);
- animateDot(mBubble.showBubbleDot() /* showDot */, null /* after */);
+ animateDot(shouldShowDot(), null /* after */);
+ }
+
+ boolean shouldShowDot() {
+ return mBubble.showBubbleDot() && !mSuppressDot;
}
int getBadgeColor() {
diff --git a/packages/SystemUI/src/com/android/systemui/colorextraction/SysuiColorExtractor.java b/packages/SystemUI/src/com/android/systemui/colorextraction/SysuiColorExtractor.java
index d3e8b3d3236e..3ca1f59fd793 100644
--- a/packages/SystemUI/src/com/android/systemui/colorextraction/SysuiColorExtractor.java
+++ b/packages/SystemUI/src/com/android/systemui/colorextraction/SysuiColorExtractor.java
@@ -68,9 +68,11 @@ public class SysuiColorExtractor extends ColorExtractor implements Dumpable,
mBackdropColors.setMainColor(Color.BLACK);
// Listen to all users instead of only the current one.
- wallpaperManager.removeOnColorsChangedListener(this);
- wallpaperManager.addOnColorsChangedListener(this, null /* handler */,
- UserHandle.USER_ALL);
+ if (wallpaperManager.isWallpaperSupported()) {
+ wallpaperManager.removeOnColorsChangedListener(this);
+ wallpaperManager.addOnColorsChangedListener(this, null /* handler */,
+ UserHandle.USER_ALL);
+ }
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/dock/DockManager.java b/packages/SystemUI/src/com/android/systemui/dock/DockManager.java
index d332f59a4500..4ee2ed3e34c0 100644
--- a/packages/SystemUI/src/com/android/systemui/dock/DockManager.java
+++ b/packages/SystemUI/src/com/android/systemui/dock/DockManager.java
@@ -17,12 +17,12 @@
package com.android.systemui.dock;
/**
- * Allows an app to handle dock events
+ * Allows an app to handle dock events.
*/
public interface DockManager {
/**
- * Uninitialized / undocking dock states
+ * Uninitialized / undocking dock states.
*/
int STATE_NONE = 0;
/**
@@ -30,34 +30,81 @@ public interface DockManager {
*/
int STATE_DOCKED = 1;
/**
- * The state for docking without showing UI
+ * The state for docking without showing UI.
*/
int STATE_DOCKED_HIDE = 2;
/**
- * Add a dock event listener into manager
+ * Indicates there's no alignment info. This could happen when the device is unable to decide
+ * its alignment condition.
+ */
+ int ALIGN_STATE_UNKNOWN = -1;
+
+ /**
+ * Indicates there's no alignment issue.
+ */
+ int ALIGN_STATE_GOOD = 0;
+
+ /**
+ * Indicates it's slightly not aligned with dock. Normally combines with slow charging issue.
+ */
+ int ALIGN_STATE_POOR = 1;
+
+ /**
+ * Indicates it's not aligned with dock. Normally combines with not charging issue.
+ */
+ int ALIGN_STATE_TERRIBLE = 2;
+
+ /**
+ * Adds a dock event listener into manager.
*
* @param callback A {@link DockEventListener} which want to add
*/
void addListener(DockEventListener callback);
/**
- * Remove the added listener from dock manager
+ * Removes the added listener from dock manager
*
* @param callback A {@link DockEventListener} which want to remove
*/
void removeListener(DockEventListener callback);
/**
+ * Adds a alignment listener into manager.
+ *
+ * @param listener A {@link AlignmentStateListener} which want to add
+ */
+ void addAlignmentStateListener(AlignmentStateListener listener);
+
+ /**
+ * Removes the added alignment listener from dock manager.
+ *
+ * @param listener A {@link AlignmentStateListener} which want to remove
+ */
+ void removeAlignmentStateListener(AlignmentStateListener listener);
+
+ /**
* Returns true if the device is in docking state.
*/
boolean isDocked();
- /** Callback for receiving dock events */
+ /**
+ * Listens to dock events.
+ */
interface DockEventListener {
/**
- * Override to handle dock events
+ * Override to handle dock events.
*/
void onEvent(int event);
}
+
+ /**
+ * Listens to dock alignment state changed.
+ */
+ interface AlignmentStateListener {
+ /**
+ * Override to handle alignment state changes.
+ */
+ void onAlignmentStateChanged(int alignState);
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/dock/DockManagerImpl.java b/packages/SystemUI/src/com/android/systemui/dock/DockManagerImpl.java
index fa7f5032ca16..f6d24e8a35c7 100644
--- a/packages/SystemUI/src/com/android/systemui/dock/DockManagerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/dock/DockManagerImpl.java
@@ -35,6 +35,14 @@ public class DockManagerImpl implements DockManager {
}
@Override
+ public void addAlignmentStateListener(AlignmentStateListener listener) {
+ }
+
+ @Override
+ public void removeAlignmentStateListener(AlignmentStateListener listener) {
+ }
+
+ @Override
public boolean isDocked() {
return false;
}
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeFactory.java b/packages/SystemUI/src/com/android/systemui/doze/DozeFactory.java
index 90cb05a8e6ee..0e124e48f165 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeFactory.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeFactory.java
@@ -33,6 +33,7 @@ import com.android.systemui.keyguard.WakefulnessLifecycle;
import com.android.systemui.plugins.FalsingManager;
import com.android.systemui.statusbar.phone.BiometricUnlockController;
import com.android.systemui.statusbar.phone.DozeParameters;
+import com.android.systemui.statusbar.policy.BatteryController;
import com.android.systemui.util.AsyncSensorManager;
import com.android.systemui.util.wakelock.DelayedWakeLock;
import com.android.systemui.util.wakelock.WakeLock;
@@ -64,7 +65,7 @@ public class DozeFactory {
params);
DozeMachine machine = new DozeMachine(wrappedService, config, wakeLock,
- wakefulnessLifecycle);
+ wakefulnessLifecycle, Dependency.get(BatteryController.class));
machine.setParts(new DozeMachine.Part[]{
new DozePauser(handler, machine, alarmManager, params.getPolicy()),
new DozeFalsingManagerAdapter(falsingManager),
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeMachine.java b/packages/SystemUI/src/com/android/systemui/doze/DozeMachine.java
index 93a51cc20db2..7f2d52780a23 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeMachine.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeMachine.java
@@ -27,6 +27,7 @@ import com.android.internal.util.Preconditions;
import com.android.systemui.keyguard.WakefulnessLifecycle;
import com.android.systemui.keyguard.WakefulnessLifecycle.Wakefulness;
import com.android.systemui.statusbar.phone.DozeParameters;
+import com.android.systemui.statusbar.policy.BatteryController;
import com.android.systemui.util.Assert;
import com.android.systemui.util.wakelock.WakeLock;
@@ -121,6 +122,7 @@ public class DozeMachine {
private final WakeLock mWakeLock;
private final AmbientDisplayConfiguration mConfig;
private final WakefulnessLifecycle mWakefulnessLifecycle;
+ private final BatteryController mBatteryController;
private Part[] mParts;
private final ArrayList<State> mQueuedRequests = new ArrayList<>();
@@ -129,11 +131,13 @@ public class DozeMachine {
private boolean mWakeLockHeldForCurrentState = false;
public DozeMachine(Service service, AmbientDisplayConfiguration config,
- WakeLock wakeLock, WakefulnessLifecycle wakefulnessLifecycle) {
+ WakeLock wakeLock, WakefulnessLifecycle wakefulnessLifecycle,
+ BatteryController batteryController) {
mDozeService = service;
mConfig = config;
mWakefulnessLifecycle = wakefulnessLifecycle;
mWakeLock = wakeLock;
+ mBatteryController = batteryController;
}
/** Initializes the set of {@link Part}s. Must be called exactly once after construction. */
@@ -316,6 +320,9 @@ public class DozeMachine {
Log.i(TAG, "Dropping pulse done because current state is already done: " + mState);
return mState;
}
+ if (requestedState == State.DOZE_AOD && mBatteryController.isAodPowerSave()) {
+ return State.DOZE;
+ }
if (requestedState == State.DOZE_REQUEST_PULSE && !mState.canPulse()) {
Log.i(TAG, "Dropping pulse request because current state can't pulse: " + mState);
return mState;
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java b/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java
index bab64db4519c..6199a0deb31f 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java
@@ -345,7 +345,6 @@ public class DozeTriggers implements DozeMachine.Part {
private void checkTriggersAtInit() {
if (mUiModeManager.getCurrentModeType() == Configuration.UI_MODE_TYPE_CAR
- || mDozeHost.isPowerSaveActive()
|| mDozeHost.isBlockingDoze()
|| !mDozeHost.isProvisioned()) {
mMachine.requestState(DozeMachine.State.FINISH);
@@ -574,8 +573,8 @@ public class DozeTriggers implements DozeMachine.Part {
@Override
public void onPowerSaveChanged(boolean active) {
- if (active) {
- mMachine.requestState(DozeMachine.State.FINISH);
+ if (mDozeHost.isPowerSaveActive()) {
+ mMachine.requestState(DozeMachine.State.DOZE);
}
}
};
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeUi.java b/packages/SystemUI/src/com/android/systemui/doze/DozeUi.java
index 1f33af8c3f55..3869e77294e7 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeUi.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeUi.java
@@ -83,7 +83,8 @@ public class DozeUi implements DozeMachine.Part {
*/
private void updateAnimateScreenOff() {
if (mCanAnimateTransition) {
- final boolean controlScreenOff = mDozeParameters.getAlwaysOn() && mKeyguardShowing;
+ final boolean controlScreenOff = mDozeParameters.getAlwaysOn() && mKeyguardShowing
+ && !mHost.isPowerSaveActive();
mDozeParameters.setControlScreenOffAnimation(controlScreenOff);
mHost.setAnimateScreenOff(controlScreenOff);
}
diff --git a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java
index ff02e71147fd..a48b9e6caf47 100644
--- a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java
+++ b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java
@@ -86,6 +86,7 @@ import com.android.internal.util.ScreenRecordHelper;
import com.android.internal.util.ScreenshotHelper;
import com.android.internal.view.RotationPolicy;
import com.android.internal.widget.LockPatternUtils;
+import com.android.keyguard.KeyguardUpdateMonitor;
import com.android.systemui.Dependency;
import com.android.systemui.Interpolators;
import com.android.systemui.MultiListLayout;
@@ -213,11 +214,13 @@ public class GlobalActionsDialog implements DialogInterface.OnDismissListener,
Dependency.get(ConfigurationController.class).addCallback(this);
mActivityStarter = Dependency.get(ActivityStarter.class);
+ KeyguardUpdateMonitor keyguardUpdateMonitor = KeyguardUpdateMonitor.getInstance(context);
UnlockMethodCache unlockMethodCache = UnlockMethodCache.getInstance(context);
unlockMethodCache.addListener(
() -> {
if (mDialog != null && mDialog.mPanelController != null) {
- boolean locked = !unlockMethodCache.canSkipBouncer();
+ boolean locked = !unlockMethodCache.canSkipBouncer()
+ && keyguardUpdateMonitor.isKeyguardVisible();
mDialog.mPanelController.onDeviceLockStateChanged(locked);
}
});
@@ -414,9 +417,7 @@ public class GlobalActionsDialog implements DialogInterface.OnDismissListener,
new GlobalActionsPanelPlugin.Callbacks() {
@Override
public void dismissGlobalActionsMenu() {
- if (mDialog != null) {
- mDialog.dismiss();
- }
+ dismissDialog();
}
@Override
@@ -912,6 +913,9 @@ public class GlobalActionsDialog implements DialogInterface.OnDismissListener,
/** {@inheritDoc} */
public void onDismiss(DialogInterface dialog) {
+ if (mDialog == dialog) {
+ mDialog = null;
+ }
mWindowManagerFuncs.onGlobalActionsHidden();
if (mShowSilentToggle) {
try {
diff --git a/packages/SystemUI/src/com/android/systemui/glwallpaper/ImageRevealHelper.java b/packages/SystemUI/src/com/android/systemui/glwallpaper/ImageRevealHelper.java
index b154e66a846e..6474b390f5f8 100644
--- a/packages/SystemUI/src/com/android/systemui/glwallpaper/ImageRevealHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/glwallpaper/ImageRevealHelper.java
@@ -19,6 +19,7 @@ package com.android.systemui.glwallpaper;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.ValueAnimator;
+import android.util.Log;
import com.android.systemui.Interpolators;
@@ -30,6 +31,7 @@ class ImageRevealHelper {
private static final String TAG = ImageRevealHelper.class.getSimpleName();
private static final float MAX_REVEAL = 0f;
private static final float MIN_REVEAL = 1f;
+ private static final boolean DEBUG = true;
private final ValueAnimator mAnimator;
private final RevealStateListener mRevealListener;
@@ -56,8 +58,13 @@ class ImageRevealHelper {
@Override
public void onAnimationEnd(Animator animation) {
- if (!mIsCanceled && mRevealListener != null) {
- mRevealListener.onRevealEnd();
+ if (mRevealListener != null) {
+ if (DEBUG) {
+ Log.d(TAG, "transition end, cancel=" + mIsCanceled + ", reveal=" + mReveal);
+ }
+ if (!mIsCanceled) {
+ mRevealListener.onRevealEnd();
+ }
}
mIsCanceled = false;
}
@@ -65,25 +72,25 @@ class ImageRevealHelper {
@Override
public void onAnimationStart(Animator animation) {
if (mRevealListener != null) {
+ if (DEBUG) {
+ Log.d(TAG, "transition start");
+ }
mRevealListener.onRevealStart(true /* animate */);
}
}
});
}
- private void animate() {
- mAnimator.cancel();
- mAnimator.setFloatValues(mReveal, mAwake ? MAX_REVEAL : MIN_REVEAL);
- mAnimator.start();
- }
-
public float getReveal() {
return mReveal;
}
void updateAwake(boolean awake, long duration) {
+ if (DEBUG) {
+ Log.d(TAG, "updateAwake: awake=" + awake + ", duration=" + duration);
+ }
+ mAnimator.cancel();
mAwake = awake;
- mAnimator.setDuration(duration);
if (duration == 0) {
// We are transiting from home to aod or aod to home directly,
// we don't need to do transition in these cases.
@@ -92,7 +99,9 @@ class ImageRevealHelper {
mRevealListener.onRevealStateChanged();
mRevealListener.onRevealEnd();
} else {
- animate();
+ mAnimator.setDuration(duration);
+ mAnimator.setFloatValues(mReveal, mAwake ? MAX_REVEAL : MIN_REVEAL);
+ mAnimator.start();
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/glwallpaper/ImageWallpaperRenderer.java b/packages/SystemUI/src/com/android/systemui/glwallpaper/ImageWallpaperRenderer.java
index 29606347f009..be6f7bfe2587 100644
--- a/packages/SystemUI/src/com/android/systemui/glwallpaper/ImageWallpaperRenderer.java
+++ b/packages/SystemUI/src/com/android/systemui/glwallpaper/ImageWallpaperRenderer.java
@@ -46,6 +46,7 @@ public class ImageWallpaperRenderer implements GLWallpaperRenderer,
private static final String TAG = ImageWallpaperRenderer.class.getSimpleName();
private static final float SCALE_VIEWPORT_MIN = 1f;
private static final float SCALE_VIEWPORT_MAX = 1.1f;
+ private static final boolean DEBUG = true;
private final WallpaperManager mWallpaperManager;
private final ImageGLProgram mProgram;
@@ -107,6 +108,9 @@ public class ImageWallpaperRenderer implements GLWallpaperRenderer,
}
private boolean loadBitmap() {
+ if (DEBUG) {
+ Log.d(TAG, "loadBitmap: mBitmap=" + mBitmap);
+ }
if (mWallpaperManager != null && mBitmap == null) {
mBitmap = mWallpaperManager.getBitmap();
mWallpaperManager.forgetLoadedWallpaper();
@@ -119,6 +123,9 @@ public class ImageWallpaperRenderer implements GLWallpaperRenderer,
mSurfaceSize.set(0, 0, surfaceWidth, surfaceHeight);
}
}
+ if (DEBUG) {
+ Log.d(TAG, "loadBitmap done, surface size=" + mSurfaceSize);
+ }
return mBitmap != null;
}
@@ -193,16 +200,28 @@ public class ImageWallpaperRenderer implements GLWallpaperRenderer,
@Override
public void onRevealStart(boolean animate) {
+ if (DEBUG) {
+ Log.v(TAG, "onRevealStart: start, anim=" + animate);
+ }
+
if (animate) {
mScissorMode = true;
// Use current display area of texture.
mWallpaper.adjustTextureCoordinates(mSurfaceSize, mScissor, mXOffset, mYOffset);
}
mProxy.preRender();
+
+ if (DEBUG) {
+ Log.v(TAG, "onRevealStart: done");
+ }
}
@Override
public void onRevealEnd() {
+ if (DEBUG) {
+ Log.v(TAG, "onRevealEnd: start, mScissorMode=" + mScissorMode);
+ }
+
if (mScissorMode) {
mScissorMode = false;
// reset texture coordinates to use full texture.
@@ -211,6 +230,10 @@ public class ImageWallpaperRenderer implements GLWallpaperRenderer,
mProxy.requestRender();
}
mProxy.postRender();
+
+ if (DEBUG) {
+ Log.v(TAG, "onRevealEnd: done");
+ }
}
@Override
@@ -223,6 +246,7 @@ public class ImageWallpaperRenderer implements GLWallpaperRenderer,
out.print(prefix); out.print("mXOffset="); out.print(mXOffset);
out.print(prefix); out.print("mYOffset="); out.print(mYOffset);
out.print(prefix); out.print("threshold="); out.print(mImageProcessHelper.getThreshold());
+ out.print(prefix); out.print("mReveal="); out.print(mImageRevealHelper.getReveal());
mWallpaper.dump(prefix, fd, out, args);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardSliceProvider.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardSliceProvider.java
index 48f32cf04fb2..e408745699ed 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardSliceProvider.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardSliceProvider.java
@@ -103,8 +103,8 @@ public class KeyguardSliceProvider extends SliceProvider implements
protected final Uri mMediaUri;
private final Date mCurrentTime = new Date();
private final Handler mHandler;
+ private final Handler mMediaHandler;
private final AlarmManager.OnAlarmListener mUpdateNextAlarm = this::updateNextAlarm;
- private final Object mMediaToken = new Object();
private DozeParameters mDozeParameters;
@VisibleForTesting
protected SettableWakeLock mMediaWakeLock;
@@ -169,17 +169,13 @@ public class KeyguardSliceProvider extends SliceProvider implements
}
};
- public KeyguardSliceProvider() {
- this(new Handler());
- }
-
public static KeyguardSliceProvider getAttachedInstance() {
return KeyguardSliceProvider.sInstance;
}
- @VisibleForTesting
- KeyguardSliceProvider(Handler handler) {
- mHandler = handler;
+ public KeyguardSliceProvider() {
+ mHandler = new Handler();
+ mMediaHandler = new Handler();
mSliceUri = Uri.parse(KEYGUARD_SLICE_URI);
mHeaderUri = Uri.parse(KEYGUARD_HEADER_URI);
mDateUri = Uri.parse(KEYGUARD_DATE_URI);
@@ -462,16 +458,18 @@ public class KeyguardSliceProvider extends SliceProvider implements
public void onMetadataOrStateChanged(MediaMetadata metadata, @PlaybackState.State int state) {
synchronized (this) {
boolean nextVisible = NotificationMediaManager.isPlayingState(state);
- mHandler.removeCallbacksAndMessages(mMediaToken);
+ mMediaHandler.removeCallbacksAndMessages(null);
if (mMediaIsVisible && !nextVisible && mStatusBarState != StatusBarState.SHADE) {
// We need to delay this event for a few millis when stopping to avoid jank in the
// animation. The media app might not send its update when buffering, and the slice
// would end up without a header for 0.5 second.
mMediaWakeLock.setAcquired(true);
- mHandler.postDelayed(() -> {
- updateMediaStateLocked(metadata, state);
- mMediaWakeLock.setAcquired(false);
- }, mMediaToken, 2000);
+ mMediaHandler.postDelayed(() -> {
+ synchronized (this) {
+ updateMediaStateLocked(metadata, state);
+ mMediaWakeLock.setAcquired(false);
+ }
+ }, 2000);
} else {
mMediaWakeLock.setAcquired(false);
updateMediaStateLocked(metadata, state);
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/UiModeNightTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/UiModeNightTile.java
index dd0ea5ef17f4..dc9a2ce1beef 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/UiModeNightTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/UiModeNightTile.java
@@ -21,7 +21,7 @@ import android.content.Intent;
import android.content.res.Configuration;
import android.provider.Settings;
import android.service.quicksettings.Tile;
-import android.widget.Switch;
+import android.text.TextUtils;
import com.android.internal.logging.nano.MetricsProto;
import com.android.systemui.R;
@@ -79,24 +79,33 @@ public class UiModeNightTile extends QSTileImpl<QSTile.BooleanState> implements
return;
}
boolean newState = !mState.value;
- mUiModeManager.setNightMode(newState ? UiModeManager.MODE_NIGHT_YES
- : UiModeManager.MODE_NIGHT_NO);
+ mUiModeManager.setNightModeActivated(newState);
refreshState(newState);
}
@Override
protected void handleUpdateState(BooleanState state, Object arg) {
+ int uiMode = mUiModeManager.getNightMode();
boolean powerSave = mBatteryController.isPowerSave();
+ boolean isAuto = uiMode == UiModeManager.MODE_NIGHT_AUTO;
boolean nightMode = (mContext.getResources().getConfiguration().uiMode
- & Configuration.UI_MODE_NIGHT_MASK) == Configuration.UI_MODE_NIGHT_YES;
+ & Configuration.UI_MODE_NIGHT_MASK) == Configuration.UI_MODE_NIGHT_YES;
+ if (isAuto) {
+ state.secondaryLabel = mContext.getResources().getString(nightMode
+ ? R.string.quick_settings_dark_mode_secondary_label_until_sunrise
+ : R.string.quick_settings_dark_mode_secondary_label_on_at_sunset);
+ } else {
+ state.secondaryLabel = null;
+ }
state.value = nightMode;
state.label = mContext.getString(powerSave
? R.string.quick_settings_ui_mode_night_label_battery_saver
: R.string.quick_settings_ui_mode_night_label);
- state.contentDescription = state.label;
state.icon = mIcon;
- state.expandedAccessibilityClassName = Switch.class.getName();
+ state.contentDescription = TextUtils.isEmpty(state.secondaryLabel)
+ ? state.label
+ : TextUtils.concat(state.label, ", ", state.secondaryLabel);
if (powerSave) {
state.state = Tile.STATE_UNAVAILABLE;
} else {
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java b/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java
index 11ca94f6f4e6..9b30a77003cd 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java
@@ -17,6 +17,7 @@
package com.android.systemui.screenshot;
import static android.content.Context.NOTIFICATION_SERVICE;
+import static android.os.AsyncTask.THREAD_POOL_EXECUTOR;
import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
import static com.android.systemui.screenshot.GlobalScreenshot.EXTRA_ACTION_INTENT;
@@ -29,7 +30,9 @@ import android.animation.AnimatorListenerAdapter;
import android.animation.AnimatorSet;
import android.animation.ValueAnimator;
import android.animation.ValueAnimator.AnimatorUpdateListener;
+import android.app.ActivityManager;
import android.app.ActivityOptions;
+import android.app.ActivityTaskManager;
import android.app.Notification;
import android.app.Notification.BigPictureStyle;
import android.app.NotificationManager;
@@ -42,6 +45,7 @@ import android.content.ComponentName;
import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
+import android.content.pm.UserInfo;
import android.content.res.Configuration;
import android.content.res.Resources;
import android.graphics.Bitmap;
@@ -58,10 +62,16 @@ import android.graphics.Rect;
import android.media.MediaActionSound;
import android.net.Uri;
import android.os.AsyncTask;
+import android.os.Bundle;
import android.os.Environment;
+import android.os.Handler;
import android.os.PowerManager;
import android.os.Process;
+import android.os.RemoteException;
+import android.os.SystemClock;
import android.os.UserHandle;
+import android.os.UserManager;
+import android.provider.DeviceConfig;
import android.provider.MediaStore;
import android.text.TextUtils;
import android.util.DisplayMetrics;
@@ -77,10 +87,13 @@ import android.view.animation.Interpolator;
import android.widget.ImageView;
import android.widget.Toast;
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.config.sysui.SystemUiDeviceConfigFlags;
import com.android.internal.messages.nano.SystemMessageProto.SystemMessage;
import com.android.systemui.R;
import com.android.systemui.SysUiServiceProvider;
import com.android.systemui.SystemUI;
+import com.android.systemui.SystemUIFactory;
import com.android.systemui.shared.system.ActivityManagerWrapper;
import com.android.systemui.statusbar.phone.StatusBar;
import com.android.systemui.util.NotificationChannels;
@@ -91,11 +104,17 @@ import java.io.IOException;
import java.io.OutputStream;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
+import java.util.ArrayList;
+import java.util.Collections;
import java.util.Date;
+import java.util.List;
+import java.util.Random;
+import java.util.UUID;
+import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
-
+import java.util.function.Consumer;
/**
@@ -105,7 +124,7 @@ class SaveImageInBackgroundData {
Context context;
Bitmap image;
Uri imageUri;
- Runnable finisher;
+ Consumer<Uri> finisher;
int iconSize;
int previewWidth;
int previewheight;
@@ -128,6 +147,7 @@ class SaveImageInBackgroundTask extends AsyncTask<Void, Void, Void> {
private static final String TAG = "SaveImageInBackgroundTask";
private static final String SCREENSHOT_FILE_NAME_TEMPLATE = "Screenshot_%s.png";
+ private static final String SCREENSHOT_ID_TEMPLATE = "Screenshot_%s";
private static final String SCREENSHOT_SHARE_SUBJECT_TEMPLATE = "Screenshot (%s)";
private final SaveImageInBackgroundData mParams;
@@ -138,6 +158,10 @@ class SaveImageInBackgroundTask extends AsyncTask<Void, Void, Void> {
private final BigPictureStyle mNotificationStyle;
private final int mImageWidth;
private final int mImageHeight;
+ private final ScreenshotNotificationSmartActionsProvider mSmartActionsProvider;
+ private final String mScreenshotId;
+ private final boolean mSmartActionsEnabled;
+ private final Random mRandom = new Random();
SaveImageInBackgroundTask(Context context, SaveImageInBackgroundData data,
NotificationManager nManager) {
@@ -148,6 +172,20 @@ class SaveImageInBackgroundTask extends AsyncTask<Void, Void, Void> {
mImageTime = System.currentTimeMillis();
String imageDate = new SimpleDateFormat("yyyyMMdd-HHmmss").format(new Date(mImageTime));
mImageFileName = String.format(SCREENSHOT_FILE_NAME_TEMPLATE, imageDate);
+ mScreenshotId = String.format(SCREENSHOT_ID_TEMPLATE, UUID.randomUUID());
+
+ // Initialize screenshot notification smart actions provider.
+ mSmartActionsEnabled = DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_SYSTEMUI,
+ SystemUiDeviceConfigFlags.ENABLE_SCREENSHOT_NOTIFICATION_SMART_ACTIONS, false);
+ if (mSmartActionsEnabled) {
+ mSmartActionsProvider =
+ SystemUIFactory.getInstance()
+ .createScreenshotNotificationSmartActionsProvider(
+ context, THREAD_POOL_EXECUTOR, new Handler());
+ } else {
+ // If smart actions is not enabled use empty implementation.
+ mSmartActionsProvider = new ScreenshotNotificationSmartActionsProvider();
+ }
// Create the large notification icon
mImageWidth = data.image.getWidth();
@@ -222,6 +260,55 @@ class SaveImageInBackgroundTask extends AsyncTask<Void, Void, Void> {
mNotificationStyle.bigLargeIcon((Bitmap) null);
}
+ private List<Notification.Action> buildSmartActions(
+ List<Notification.Action> actions, Context context) {
+ List<Notification.Action> broadcastActions = new ArrayList<>();
+ for (Notification.Action action : actions) {
+ // Proxy smart actions through {@link GlobalScreenshot.SmartActionsReceiver}
+ // for logging smart actions.
+ Bundle extras = action.getExtras();
+ String actionType = extras.getString(
+ ScreenshotNotificationSmartActionsProvider.ACTION_TYPE,
+ ScreenshotNotificationSmartActionsProvider.DEFAULT_ACTION_TYPE);
+ Intent intent = new Intent(context,
+ GlobalScreenshot.SmartActionsReceiver.class).putExtra(
+ GlobalScreenshot.EXTRA_ACTION_INTENT, action.actionIntent);
+ addIntentExtras(mScreenshotId, intent, actionType, mSmartActionsEnabled);
+ PendingIntent broadcastIntent = PendingIntent.getBroadcast(context,
+ mRandom.nextInt(),
+ intent,
+ PendingIntent.FLAG_CANCEL_CURRENT);
+ broadcastActions.add(new Notification.Action.Builder(action.getIcon(), action.title,
+ broadcastIntent).setContextual(true).addExtras(extras).build());
+ }
+ return broadcastActions;
+ }
+
+ private static void addIntentExtras(String screenshotId, Intent intent, String actionType,
+ boolean smartActionsEnabled) {
+ intent
+ .putExtra(GlobalScreenshot.EXTRA_ACTION_TYPE, actionType)
+ .putExtra(GlobalScreenshot.EXTRA_ID, screenshotId)
+ .putExtra(GlobalScreenshot.EXTRA_SMART_ACTIONS_ENABLED, smartActionsEnabled);
+ }
+
+ private int getUserHandleOfForegroundApplication(Context context) {
+ // This logic matches
+ // com.android.systemui.statusbar.phone.PhoneStatusBarPolicy#updateManagedProfile
+ try {
+ return ActivityTaskManager.getService().getLastResumedActivityUserId();
+ } catch (RemoteException e) {
+ Slog.w(TAG, "getUserHandleOfForegroundApplication: ", e);
+ return context.getUserId();
+ }
+ }
+
+ private boolean isManagedProfile(Context context) {
+ UserManager manager = UserManager.get(context);
+ UserInfo info = manager.getUserInfo(getUserHandleOfForegroundApplication(context));
+ return info.isManagedProfile();
+ }
+
/**
* Generates a new hardware bitmap with specified values, copying the content from the passed
* in bitmap.
@@ -251,6 +338,10 @@ class SaveImageInBackgroundTask extends AsyncTask<Void, Void, Void> {
Resources r = context.getResources();
try {
+ CompletableFuture<List<Notification.Action>> smartActionsFuture =
+ GlobalScreenshot.getSmartActionsFuture(mScreenshotId, image,
+ mSmartActionsProvider, mSmartActionsEnabled, isManagedProfile(context));
+
// Save the screenshot to the MediaStore
final MediaStore.PendingParams params = new MediaStore.PendingParams(
MediaStore.Images.Media.EXTERNAL_CONTENT_URI, mImageFileName, "image/png");
@@ -273,77 +364,7 @@ class SaveImageInBackgroundTask extends AsyncTask<Void, Void, Void> {
IoUtils.closeQuietly(session);
}
- // Note: Both the share and edit actions are proxied through ActionProxyReceiver in
- // order to do some common work like dismissing the keyguard and sending
- // closeSystemWindows
-
- // Create a share intent, this will always go through the chooser activity first which
- // should not trigger auto-enter PiP
- String subjectDate = DateFormat.getDateTimeInstance().format(new Date(mImageTime));
- String subject = String.format(SCREENSHOT_SHARE_SUBJECT_TEMPLATE, subjectDate);
- Intent sharingIntent = new Intent(Intent.ACTION_SEND);
- sharingIntent.setType("image/png");
- sharingIntent.putExtra(Intent.EXTRA_STREAM, uri);
- // Include URI in ClipData also, so that grantPermission picks it up.
- // We don't use setData here because some apps interpret this as "to:".
- ClipData clipdata = new ClipData(new ClipDescription("content",
- new String[]{ClipDescription.MIMETYPE_TEXT_PLAIN}),
- new ClipData.Item(uri));
- sharingIntent.setClipData(clipdata);
- sharingIntent.putExtra(Intent.EXTRA_SUBJECT, subject);
- sharingIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
-
- PendingIntent chooserAction = PendingIntent.getBroadcast(context, 0,
- new Intent(context, GlobalScreenshot.TargetChosenReceiver.class),
- PendingIntent.FLAG_CANCEL_CURRENT | PendingIntent.FLAG_ONE_SHOT);
- Intent sharingChooserIntent = Intent.createChooser(sharingIntent, null,
- chooserAction.getIntentSender())
- .addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK | Intent.FLAG_ACTIVITY_NEW_TASK)
- .addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
-
- // Create a share action for the notification
- PendingIntent shareAction = PendingIntent.getBroadcastAsUser(context, 0,
- new Intent(context, GlobalScreenshot.ActionProxyReceiver.class)
- .putExtra(EXTRA_ACTION_INTENT, sharingChooserIntent)
- .putExtra(EXTRA_DISALLOW_ENTER_PIP, true),
- PendingIntent.FLAG_CANCEL_CURRENT, UserHandle.SYSTEM);
- Notification.Action.Builder shareActionBuilder = new Notification.Action.Builder(
- R.drawable.ic_screenshot_share,
- r.getString(com.android.internal.R.string.share), shareAction);
- mNotificationBuilder.addAction(shareActionBuilder.build());
-
- // Create an edit intent, if a specific package is provided as the editor, then launch
- // that directly
- String editorPackage = context.getString(R.string.config_screenshotEditor);
- Intent editIntent = new Intent(Intent.ACTION_EDIT);
- if (!TextUtils.isEmpty(editorPackage)) {
- editIntent.setComponent(ComponentName.unflattenFromString(editorPackage));
- }
- editIntent.setType("image/png");
- editIntent.setData(uri);
- editIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
- editIntent.addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
-
- // Create a edit action
- PendingIntent editAction = PendingIntent.getBroadcastAsUser(context, 1,
- new Intent(context, GlobalScreenshot.ActionProxyReceiver.class)
- .putExtra(EXTRA_ACTION_INTENT, editIntent)
- .putExtra(EXTRA_CANCEL_NOTIFICATION, editIntent.getComponent() != null),
- PendingIntent.FLAG_CANCEL_CURRENT, UserHandle.SYSTEM);
- Notification.Action.Builder editActionBuilder = new Notification.Action.Builder(
- R.drawable.ic_screenshot_edit,
- r.getString(com.android.internal.R.string.screenshot_edit), editAction);
- mNotificationBuilder.addAction(editActionBuilder.build());
-
- // Create a delete action for the notification
- PendingIntent deleteAction = PendingIntent.getBroadcast(context, 0,
- new Intent(context, GlobalScreenshot.DeleteScreenshotReceiver.class)
- .putExtra(GlobalScreenshot.SCREENSHOT_URI_ID, uri.toString()),
- PendingIntent.FLAG_CANCEL_CURRENT | PendingIntent.FLAG_ONE_SHOT);
- Notification.Action.Builder deleteActionBuilder = new Notification.Action.Builder(
- R.drawable.ic_screenshot_delete,
- r.getString(com.android.internal.R.string.delete), deleteAction);
- mNotificationBuilder.addAction(deleteActionBuilder.build());
+ populateNotificationActions(context, r, uri, smartActionsFuture, mNotificationBuilder);
mParams.imageUri = uri;
mParams.image = null;
@@ -364,6 +385,105 @@ class SaveImageInBackgroundTask extends AsyncTask<Void, Void, Void> {
return null;
}
+ @VisibleForTesting
+ void populateNotificationActions(Context context, Resources r, Uri uri,
+ CompletableFuture<List<Notification.Action>> smartActionsFuture,
+ Notification.Builder notificationBuilder) {
+ // Note: Both the share and edit actions are proxied through ActionProxyReceiver in
+ // order to do some common work like dismissing the keyguard and sending
+ // closeSystemWindows
+
+ // Create a share intent, this will always go through the chooser activity first which
+ // should not trigger auto-enter PiP
+ String subjectDate = DateFormat.getDateTimeInstance().format(new Date(mImageTime));
+ String subject = String.format(SCREENSHOT_SHARE_SUBJECT_TEMPLATE, subjectDate);
+ Intent sharingIntent = new Intent(Intent.ACTION_SEND);
+ sharingIntent.setType("image/png");
+ sharingIntent.putExtra(Intent.EXTRA_STREAM, uri);
+ // Include URI in ClipData also, so that grantPermission picks it up.
+ // We don't use setData here because some apps interpret this as "to:".
+ ClipData clipdata = new ClipData(new ClipDescription("content",
+ new String[]{ClipDescription.MIMETYPE_TEXT_PLAIN}),
+ new ClipData.Item(uri));
+ sharingIntent.setClipData(clipdata);
+ sharingIntent.putExtra(Intent.EXTRA_SUBJECT, subject);
+ sharingIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
+
+ PendingIntent chooserAction = PendingIntent.getBroadcast(context, 0,
+ new Intent(context, GlobalScreenshot.TargetChosenReceiver.class),
+ PendingIntent.FLAG_CANCEL_CURRENT | PendingIntent.FLAG_ONE_SHOT);
+ Intent sharingChooserIntent = Intent.createChooser(sharingIntent, null,
+ chooserAction.getIntentSender())
+ .addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK | Intent.FLAG_ACTIVITY_NEW_TASK)
+ .addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
+
+ // Create a share action for the notification
+ PendingIntent shareAction = PendingIntent.getBroadcastAsUser(context, 0,
+ new Intent(context, GlobalScreenshot.ActionProxyReceiver.class)
+ .putExtra(EXTRA_ACTION_INTENT, sharingChooserIntent)
+ .putExtra(EXTRA_DISALLOW_ENTER_PIP, true)
+ .putExtra(GlobalScreenshot.EXTRA_ID, mScreenshotId)
+ .putExtra(GlobalScreenshot.EXTRA_SMART_ACTIONS_ENABLED,
+ mSmartActionsEnabled),
+ PendingIntent.FLAG_CANCEL_CURRENT, UserHandle.SYSTEM);
+ Notification.Action.Builder shareActionBuilder = new Notification.Action.Builder(
+ R.drawable.ic_screenshot_share,
+ r.getString(com.android.internal.R.string.share), shareAction);
+ notificationBuilder.addAction(shareActionBuilder.build());
+
+ // Create an edit intent, if a specific package is provided as the editor, then launch
+ // that directly
+ String editorPackage = context.getString(R.string.config_screenshotEditor);
+ Intent editIntent = new Intent(Intent.ACTION_EDIT);
+ if (!TextUtils.isEmpty(editorPackage)) {
+ editIntent.setComponent(ComponentName.unflattenFromString(editorPackage));
+ }
+ editIntent.setType("image/png");
+ editIntent.setData(uri);
+ editIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
+ editIntent.addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
+
+ // Create a edit action
+ PendingIntent editAction = PendingIntent.getBroadcastAsUser(context, 1,
+ new Intent(context, GlobalScreenshot.ActionProxyReceiver.class)
+ .putExtra(EXTRA_ACTION_INTENT, editIntent)
+ .putExtra(EXTRA_CANCEL_NOTIFICATION, editIntent.getComponent() != null)
+ .putExtra(GlobalScreenshot.EXTRA_ID, mScreenshotId)
+ .putExtra(GlobalScreenshot.EXTRA_SMART_ACTIONS_ENABLED,
+ mSmartActionsEnabled),
+ PendingIntent.FLAG_CANCEL_CURRENT, UserHandle.SYSTEM);
+ Notification.Action.Builder editActionBuilder = new Notification.Action.Builder(
+ R.drawable.ic_screenshot_edit,
+ r.getString(com.android.internal.R.string.screenshot_edit), editAction);
+ notificationBuilder.addAction(editActionBuilder.build());
+
+ // Create a delete action for the notification
+ PendingIntent deleteAction = PendingIntent.getBroadcast(context, 0,
+ new Intent(context, GlobalScreenshot.DeleteScreenshotReceiver.class)
+ .putExtra(GlobalScreenshot.SCREENSHOT_URI_ID, uri.toString())
+ .putExtra(GlobalScreenshot.EXTRA_ID, mScreenshotId)
+ .putExtra(GlobalScreenshot.EXTRA_SMART_ACTIONS_ENABLED,
+ mSmartActionsEnabled),
+ PendingIntent.FLAG_CANCEL_CURRENT | PendingIntent.FLAG_ONE_SHOT);
+ Notification.Action.Builder deleteActionBuilder = new Notification.Action.Builder(
+ R.drawable.ic_screenshot_delete,
+ r.getString(com.android.internal.R.string.delete), deleteAction);
+ notificationBuilder.addAction(deleteActionBuilder.build());
+
+ if (mSmartActionsEnabled) {
+ int timeoutMs = DeviceConfig.getInt(DeviceConfig.NAMESPACE_SYSTEMUI,
+ SystemUiDeviceConfigFlags
+ .SCREENSHOT_NOTIFICATION_SMART_ACTIONS_TIMEOUT_MS,
+ 1000);
+ List<Notification.Action> smartActions = GlobalScreenshot.getSmartActions(mScreenshotId,
+ smartActionsFuture, timeoutMs, mSmartActionsProvider);
+ smartActions = buildSmartActions(smartActions, context);
+ for (Notification.Action action : smartActions) {
+ notificationBuilder.addAction(action);
+ }
+ }
+ }
+
@Override
protected void onPostExecute(Void params) {
if (mParams.errorMsgResId != 0) {
@@ -406,7 +526,7 @@ class SaveImageInBackgroundTask extends AsyncTask<Void, Void, Void> {
mNotificationManager.notify(SystemMessage.NOTE_GLOBAL_SCREENSHOT,
mNotificationBuilder.build());
}
- mParams.finisher.run();
+ mParams.finisher.accept(mParams.imageUri);
mParams.clearContext();
}
@@ -415,7 +535,7 @@ class SaveImageInBackgroundTask extends AsyncTask<Void, Void, Void> {
// If we are cancelled while the task is running in the background, we may get null params.
// The finisher is expected to always be called back, so just use the baked-in params from
// the ctor in any case.
- mParams.finisher.run();
+ mParams.finisher.accept(null);
mParams.clearImage();
mParams.clearContext();
@@ -446,6 +566,15 @@ class DeleteImageInBackgroundTask extends AsyncTask<Uri, Void, Void> {
}
class GlobalScreenshot {
+ // These strings are used for communicating the action invoked to
+ // ScreenshotNotificationSmartActionsProvider.
+ static final String EXTRA_ACTION_TYPE = "android:screenshot_action_type";
+ static final String EXTRA_ID = "android:screenshot_id";
+ static final String ACTION_TYPE_DELETE = "Delete";
+ static final String ACTION_TYPE_SHARE = "Share";
+ static final String ACTION_TYPE_EDIT = "Edit";
+ static final String EXTRA_SMART_ACTIONS_ENABLED = "android:smart_actions_enabled";
+
static final String SCREENSHOT_URI_ID = "android:screenshot_uri_id";
static final String EXTRA_ACTION_INTENT = "android:screenshot_action_intent";
static final String EXTRA_CANCEL_NOTIFICATION = "android:screenshot_cancel_notification";
@@ -566,7 +695,7 @@ class GlobalScreenshot {
/**
* Creates a new worker thread and saves the screenshot to the media store.
*/
- private void saveScreenshotInWorkerThread(Runnable finisher) {
+ private void saveScreenshotInWorkerThread(Consumer<Uri> finisher) {
SaveImageInBackgroundData data = new SaveImageInBackgroundData();
data.context = mContext;
data.image = mScreenBitmap;
@@ -584,8 +713,8 @@ class GlobalScreenshot {
/**
* Takes a screenshot of the current display and shows an animation.
*/
- private void takeScreenshot(Runnable finisher, boolean statusBarVisible, boolean navBarVisible,
- Rect crop) {
+ private void takeScreenshot(Consumer<Uri> finisher, boolean statusBarVisible,
+ boolean navBarVisible, Rect crop) {
int rot = mDisplay.getRotation();
int width = crop.width();
int height = crop.height();
@@ -595,7 +724,7 @@ class GlobalScreenshot {
if (mScreenBitmap == null) {
notifyScreenshotError(mContext, mNotificationManager,
R.string.screenshot_failed_to_capture_text);
- finisher.run();
+ finisher.accept(null);
return;
}
@@ -608,7 +737,7 @@ class GlobalScreenshot {
statusBarVisible, navBarVisible);
}
- void takeScreenshot(Runnable finisher, boolean statusBarVisible, boolean navBarVisible) {
+ void takeScreenshot(Consumer<Uri> finisher, boolean statusBarVisible, boolean navBarVisible) {
mDisplay.getRealMetrics(mDisplayMetrics);
takeScreenshot(finisher, statusBarVisible, navBarVisible,
new Rect(0, 0, mDisplayMetrics.widthPixels, mDisplayMetrics.heightPixels));
@@ -617,7 +746,7 @@ class GlobalScreenshot {
/**
* Displays a screenshot selector
*/
- void takeScreenshotPartial(final Runnable finisher, final boolean statusBarVisible,
+ void takeScreenshotPartial(final Consumer<Uri> finisher, final boolean statusBarVisible,
final boolean navBarVisible) {
mWindowManager.addView(mScreenshotLayout, mWindowLayoutParams);
mScreenshotSelectorView.setOnTouchListener(new View.OnTouchListener() {
@@ -677,8 +806,8 @@ class GlobalScreenshot {
/**
* Starts the animation after taking the screenshot
*/
- private void startAnimation(final Runnable finisher, int w, int h, boolean statusBarVisible,
- boolean navBarVisible) {
+ private void startAnimation(final Consumer<Uri> finisher, int w, int h,
+ boolean statusBarVisible, boolean navBarVisible) {
// If power save is on, show a toast so there is some visual indication that a screenshot
// has been taken.
PowerManager powerManager = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
@@ -904,6 +1033,100 @@ class GlobalScreenshot {
nManager.notify(SystemMessage.NOTE_GLOBAL_SCREENSHOT, n);
}
+ @VisibleForTesting
+ static CompletableFuture<List<Notification.Action>> getSmartActionsFuture(String screenshotId,
+ Bitmap image, ScreenshotNotificationSmartActionsProvider smartActionsProvider,
+ boolean smartActionsEnabled, boolean isManagedProfile) {
+ if (!smartActionsEnabled) {
+ Slog.i(TAG, "Screenshot Intelligence not enabled, returning empty list.");
+ return CompletableFuture.completedFuture(Collections.emptyList());
+ }
+ if (image.getConfig() != Bitmap.Config.HARDWARE) {
+ Slog.w(TAG, String.format(
+ "Bitmap expected: Hardware, Bitmap found: %s. Returning empty list.",
+ image.getConfig()));
+ return CompletableFuture.completedFuture(Collections.emptyList());
+ }
+
+ Slog.d(TAG, "Screenshot from a managed profile: " + isManagedProfile);
+ CompletableFuture<List<Notification.Action>> smartActionsFuture;
+ long startTimeMs = SystemClock.uptimeMillis();
+ try {
+ ActivityManager.RunningTaskInfo runningTask =
+ ActivityManagerWrapper.getInstance().getRunningTask();
+ ComponentName componentName =
+ (runningTask != null && runningTask.topActivity != null)
+ ? runningTask.topActivity
+ : new ComponentName("", "");
+ smartActionsFuture = smartActionsProvider.getActions(screenshotId, image,
+ componentName,
+ isManagedProfile);
+ } catch (Throwable e) {
+ long waitTimeMs = SystemClock.uptimeMillis() - startTimeMs;
+ smartActionsFuture = CompletableFuture.completedFuture(Collections.emptyList());
+ Slog.e(TAG, "Failed to get future for screenshot notification smart actions.", e);
+ notifyScreenshotOp(screenshotId, smartActionsProvider,
+ ScreenshotNotificationSmartActionsProvider.ScreenshotOp.REQUEST_SMART_ACTIONS,
+ ScreenshotNotificationSmartActionsProvider.ScreenshotOpStatus.ERROR,
+ waitTimeMs);
+ }
+ return smartActionsFuture;
+ }
+
+ @VisibleForTesting
+ static List<Notification.Action> getSmartActions(String screenshotId,
+ CompletableFuture<List<Notification.Action>> smartActionsFuture, int timeoutMs,
+ ScreenshotNotificationSmartActionsProvider smartActionsProvider) {
+ long startTimeMs = SystemClock.uptimeMillis();
+ try {
+ List<Notification.Action> actions = smartActionsFuture.get(timeoutMs,
+ TimeUnit.MILLISECONDS);
+ long waitTimeMs = SystemClock.uptimeMillis() - startTimeMs;
+ Slog.d(TAG, String.format("Got %d smart actions. Wait time: %d ms",
+ actions.size(), waitTimeMs));
+ notifyScreenshotOp(screenshotId, smartActionsProvider,
+ ScreenshotNotificationSmartActionsProvider.ScreenshotOp.WAIT_FOR_SMART_ACTIONS,
+ ScreenshotNotificationSmartActionsProvider.ScreenshotOpStatus.SUCCESS,
+ waitTimeMs);
+ return actions;
+ } catch (Throwable e) {
+ long waitTimeMs = SystemClock.uptimeMillis() - startTimeMs;
+ Slog.e(TAG, String.format("Error getting smart actions. Wait time: %d ms", waitTimeMs),
+ e);
+ ScreenshotNotificationSmartActionsProvider.ScreenshotOpStatus status =
+ (e instanceof TimeoutException)
+ ? ScreenshotNotificationSmartActionsProvider.ScreenshotOpStatus.TIMEOUT
+ : ScreenshotNotificationSmartActionsProvider.ScreenshotOpStatus.ERROR;
+ notifyScreenshotOp(screenshotId, smartActionsProvider,
+ ScreenshotNotificationSmartActionsProvider.ScreenshotOp.WAIT_FOR_SMART_ACTIONS,
+ status, waitTimeMs);
+ return Collections.emptyList();
+ }
+ }
+
+ static void notifyScreenshotOp(String screenshotId,
+ ScreenshotNotificationSmartActionsProvider smartActionsProvider,
+ ScreenshotNotificationSmartActionsProvider.ScreenshotOp op,
+ ScreenshotNotificationSmartActionsProvider.ScreenshotOpStatus status, long durationMs) {
+ try {
+ smartActionsProvider.notifyOp(screenshotId, op, status, durationMs);
+ } catch (Throwable e) {
+ Slog.e(TAG, "Error in notifyScreenshotOp: ", e);
+ }
+ }
+
+ static void notifyScreenshotAction(Context context, String screenshotId, String action,
+ boolean isSmartAction) {
+ try {
+ ScreenshotNotificationSmartActionsProvider provider =
+ SystemUIFactory.getInstance().createScreenshotNotificationSmartActionsProvider(
+ context, THREAD_POOL_EXECUTOR, new Handler());
+ provider.notifyAction(screenshotId, action, isSmartAction);
+ } catch (Throwable e) {
+ Slog.e(TAG, "Error in notifyScreenshotAction: ", e);
+ }
+ }
+
/**
* Receiver to proxy the share or edit intent, used to clean up the notification and send
* appropriate signals to the system (ie. to dismiss the keyguard if necessary).
@@ -913,6 +1136,7 @@ class GlobalScreenshot {
@Override
public void onReceive(Context context, final Intent intent) {
+ Intent actionIntent = intent.getParcelableExtra(EXTRA_ACTION_INTENT);
Runnable startActivityRunnable = () -> {
try {
ActivityManagerWrapper.getInstance().closeSystemWindows(
@@ -923,7 +1147,6 @@ class GlobalScreenshot {
return;
}
- Intent actionIntent = intent.getParcelableExtra(EXTRA_ACTION_INTENT);
if (intent.getBooleanExtra(EXTRA_CANCEL_NOTIFICATION, false)) {
cancelScreenshotNotification(context);
}
@@ -935,6 +1158,14 @@ class GlobalScreenshot {
StatusBar statusBar = SysUiServiceProvider.getComponent(context, StatusBar.class);
statusBar.executeRunnableDismissingKeyguard(startActivityRunnable, null,
true /* dismissShade */, true /* afterKeyguardGone */, true /* deferred */);
+
+ if (intent.getBooleanExtra(EXTRA_SMART_ACTIONS_ENABLED, false)) {
+ String actionType = Intent.ACTION_EDIT.equals(actionIntent.getAction())
+ ? ACTION_TYPE_EDIT
+ : ACTION_TYPE_SHARE;
+ notifyScreenshotAction(context, intent.getStringExtra(EXTRA_ID),
+ actionType, false);
+ }
}
}
@@ -965,6 +1196,31 @@ class GlobalScreenshot {
// And delete the image from the media store
final Uri uri = Uri.parse(intent.getStringExtra(SCREENSHOT_URI_ID));
new DeleteImageInBackgroundTask(context).execute(uri);
+ if (intent.getBooleanExtra(EXTRA_SMART_ACTIONS_ENABLED, false)) {
+ notifyScreenshotAction(context, intent.getStringExtra(EXTRA_ID),
+ ACTION_TYPE_DELETE,
+ false);
+ }
+ }
+ }
+
+ /**
+ * Executes the smart action tapped by the user in the notification.
+ */
+ public static class SmartActionsReceiver extends BroadcastReceiver {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ PendingIntent pendingIntent = intent.getParcelableExtra(EXTRA_ACTION_INTENT);
+ Intent actionIntent = pendingIntent.getIntent();
+ String actionType = intent.getStringExtra(EXTRA_ACTION_TYPE);
+ Slog.d(TAG, "Executing smart action [" + actionType + "]:" + actionIntent);
+ ActivityOptions opts = ActivityOptions.makeBasic();
+ context.startActivityAsUser(actionIntent, opts.toBundle(),
+ UserHandle.CURRENT);
+
+ notifyScreenshotAction(context, intent.getStringExtra(EXTRA_ID),
+ actionType,
+ true);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotNotificationSmartActionsProvider.java b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotNotificationSmartActionsProvider.java
new file mode 100644
index 000000000000..b6f5447d2867
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotNotificationSmartActionsProvider.java
@@ -0,0 +1,101 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.screenshot;
+
+import android.app.Notification;
+import android.content.ComponentName;
+import android.graphics.Bitmap;
+import android.util.Log;
+
+import java.util.Collections;
+import java.util.List;
+import java.util.concurrent.CompletableFuture;
+
+/**
+ * This class can be overridden by a vendor-specific sys UI implementation,
+ * in order to provide smart actions in the screenshot notification.
+ */
+public class ScreenshotNotificationSmartActionsProvider {
+ /* Key provided in the notification action to get the type of smart action. */
+ public static final String ACTION_TYPE = "action_type";
+ public static final String DEFAULT_ACTION_TYPE = "Smart Action";
+
+ /* Define phases of screenshot execution. */
+ protected enum ScreenshotOp {
+ OP_UNKNOWN,
+ RETRIEVE_SMART_ACTIONS,
+ REQUEST_SMART_ACTIONS,
+ WAIT_FOR_SMART_ACTIONS
+ }
+
+ /* Enum to report success or failure for screenshot execution phases. */
+ protected enum ScreenshotOpStatus {
+ OP_STATUS_UNKNOWN,
+ SUCCESS,
+ ERROR,
+ TIMEOUT
+ }
+
+ private static final String TAG = "ScreenshotActions";
+
+ /**
+ * Default implementation that returns an empty list.
+ * This method is overridden in vendor-specific Sys UI implementation.
+ *
+ * @param screenshotId A generated random unique id for the screenshot.
+ * @param bitmap The bitmap of the screenshot. The bitmap config must be {@link
+ * HARDWARE}.
+ * @param componentName Contains package and activity class names where the screenshot was
+ * taken. This is used as an additional signal to generate and rank more
+ * relevant actions.
+ * @param isManagedProfile The screenshot was taken for a work profile app.
+ */
+ public CompletableFuture<List<Notification.Action>> getActions(
+ String screenshotId,
+ Bitmap bitmap,
+ ComponentName componentName,
+ boolean isManagedProfile) {
+ Log.d(TAG, "Returning empty smart action list.");
+ return CompletableFuture.completedFuture(Collections.emptyList());
+ }
+
+ /**
+ * Notify exceptions and latency encountered during generating smart actions.
+ * This method is overridden in vendor-specific Sys UI implementation.
+ *
+ * @param screenshotId Unique id of the screenshot.
+ * @param op screenshot execution phase defined in {@link ScreenshotOp}
+ * @param status {@link ScreenshotOpStatus} to report success or failure.
+ * @param durationMs latency experienced in different phases of screenshots.
+ */
+ public void notifyOp(String screenshotId, ScreenshotOp op, ScreenshotOpStatus status,
+ long durationMs) {
+ Log.d(TAG, "Return without notify.");
+ }
+
+ /**
+ * Notify screenshot notification action invoked.
+ * This method is overridden in vendor-specific Sys UI implementation.
+ *
+ * @param screenshotId Unique id of the screenshot.
+ * @param action type of notification action invoked.
+ * @param isSmartAction whether action invoked was a smart action.
+ */
+ public void notifyAction(String screenshotId, String action, boolean isSmartAction) {
+ Log.d(TAG, "Return without notify.");
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/TakeScreenshotService.java b/packages/SystemUI/src/com/android/systemui/screenshot/TakeScreenshotService.java
index 34b8bfe59e7e..330a6b541098 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/TakeScreenshotService.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/TakeScreenshotService.java
@@ -18,6 +18,7 @@ package com.android.systemui.screenshot;
import android.app.Service;
import android.content.Intent;
+import android.net.Uri;
import android.os.Handler;
import android.os.IBinder;
import android.os.Message;
@@ -27,6 +28,8 @@ import android.os.UserManager;
import android.util.Log;
import android.view.WindowManager;
+import java.util.function.Consumer;
+
public class TakeScreenshotService extends Service {
private static final String TAG = "TakeScreenshotService";
@@ -36,10 +39,10 @@ public class TakeScreenshotService extends Service {
@Override
public void handleMessage(Message msg) {
final Messenger callback = msg.replyTo;
- Runnable finisher = new Runnable() {
+ Consumer<Uri> finisher = new Consumer<Uri>() {
@Override
- public void run() {
- Message reply = Message.obtain(null, 1);
+ public void accept(Uri uri) {
+ Message reply = Message.obtain(null, 1, uri);
try {
callback.send(reply);
} catch (RemoteException e) {
@@ -52,7 +55,7 @@ public class TakeScreenshotService extends Service {
// animation and error notification.
if (!getSystemService(UserManager.class).isUserUnlocked()) {
Log.w(TAG, "Skipping screenshot because storage is locked!");
- post(finisher);
+ post(() -> finisher.accept(null));
return;
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java
index 6f87b2939cfb..8ee7305d99c5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java
@@ -50,6 +50,7 @@ import com.android.settingslib.Utils;
import com.android.systemui.Dependency;
import com.android.systemui.Interpolators;
import com.android.systemui.R;
+import com.android.systemui.dock.DockManager;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.plugins.statusbar.StatusBarStateController.StateListener;
import com.android.systemui.statusbar.phone.KeyguardIndicationTextView;
@@ -96,6 +97,7 @@ public class KeyguardIndicationController implements StateListener,
private final IBatteryStats mBatteryInfo;
private final SettableWakeLock mWakeLock;
private final LockPatternUtils mLockPatternUtils;
+ private final DockManager mDockManager;
private final int mSlowThreshold;
private final int mFastThreshold;
@@ -104,10 +106,12 @@ public class KeyguardIndicationController implements StateListener,
private LockscreenGestureLogger mLockscreenGestureLogger = new LockscreenGestureLogger();
private String mRestingIndication;
+ private String mAlignmentIndication = "";
private CharSequence mTransientIndication;
private ColorStateList mTransientTextColorState;
private ColorStateList mInitialTextColorState;
private boolean mVisible;
+ private boolean mHideTransientMessageOnScreenOff;
private boolean mPowerPluggedIn;
private boolean mPowerPluggedInWired;
@@ -140,7 +144,8 @@ public class KeyguardIndicationController implements StateListener,
Dependency.get(AccessibilityController.class),
UnlockMethodCache.getInstance(context),
Dependency.get(StatusBarStateController.class),
- KeyguardUpdateMonitor.getInstance(context));
+ KeyguardUpdateMonitor.getInstance(context),
+ Dependency.get(DockManager.class));
}
/**
@@ -151,7 +156,8 @@ public class KeyguardIndicationController implements StateListener,
LockPatternUtils lockPatternUtils, WakeLock wakeLock, ShadeController shadeController,
AccessibilityController accessibilityController, UnlockMethodCache unlockMethodCache,
StatusBarStateController statusBarStateController,
- KeyguardUpdateMonitor keyguardUpdateMonitor) {
+ KeyguardUpdateMonitor keyguardUpdateMonitor,
+ DockManager dockManager) {
mContext = context;
mLockIcon = lockIcon;
mShadeController = shadeController;
@@ -159,6 +165,8 @@ public class KeyguardIndicationController implements StateListener,
mUnlockMethodCache = unlockMethodCache;
mStatusBarStateController = statusBarStateController;
mKeyguardUpdateMonitor = keyguardUpdateMonitor;
+ mDockManager = dockManager;
+ mDockManager.addAlignmentStateListener(this::handleAlignStateChanged);
// lock icon is not used on all form factors.
if (mLockIcon != null) {
mLockIcon.setOnLongClickListener(this::handleLockLongClick);
@@ -212,6 +220,21 @@ public class KeyguardIndicationController implements StateListener,
mShadeController.animateCollapsePanels(CommandQueue.FLAG_EXCLUDE_NONE, true /* force */);
}
+ private void handleAlignStateChanged(int alignState) {
+ String alignmentIndication = "";
+ if (alignState == DockManager.ALIGN_STATE_POOR) {
+ alignmentIndication =
+ mContext.getResources().getString(R.string.dock_alignment_slow_charging);
+ } else if (alignState == DockManager.ALIGN_STATE_TERRIBLE) {
+ alignmentIndication =
+ mContext.getResources().getString(R.string.dock_alignment_not_charging);
+ }
+ if (!alignmentIndication.equals(mAlignmentIndication)) {
+ mAlignmentIndication = alignmentIndication;
+ updateIndication(false);
+ }
+ }
+
/**
* Gets the {@link KeyguardUpdateMonitorCallback} instance associated with this
* {@link KeyguardIndicationController}.
@@ -255,7 +278,7 @@ public class KeyguardIndicationController implements StateListener,
if (visible) {
// If this is called after an error message was already shown, we should not clear it.
// Otherwise the error message won't be shown
- if (!mHandler.hasMessages(MSG_HIDE_TRANSIENT)) {
+ if (!mHandler.hasMessages(MSG_HIDE_TRANSIENT)) {
hideTransientIndication();
}
updateIndication(false);
@@ -317,15 +340,17 @@ public class KeyguardIndicationController implements StateListener,
* Shows {@param transientIndication} until it is hidden by {@link #hideTransientIndication}.
*/
public void showTransientIndication(CharSequence transientIndication) {
- showTransientIndication(transientIndication, mInitialTextColorState);
+ showTransientIndication(transientIndication, mInitialTextColorState,
+ false /* hideOnScreenOff */);
}
/**
* Shows {@param transientIndication} until it is hidden by {@link #hideTransientIndication}.
*/
- public void showTransientIndication(CharSequence transientIndication,
- ColorStateList textColorState) {
+ private void showTransientIndication(CharSequence transientIndication,
+ ColorStateList textColorState, boolean hideOnScreenOff) {
mTransientIndication = transientIndication;
+ mHideTransientMessageOnScreenOff = hideOnScreenOff && transientIndication != null;
mTransientTextColorState = textColorState;
mHandler.removeMessages(MSG_HIDE_TRANSIENT);
mHandler.removeMessages(MSG_SWIPE_UP_TO_UNLOCK);
@@ -344,6 +369,7 @@ public class KeyguardIndicationController implements StateListener,
public void hideTransientIndication() {
if (mTransientIndication != null) {
mTransientIndication = null;
+ mHideTransientMessageOnScreenOff = false;
mHandler.removeMessages(MSG_HIDE_TRANSIENT);
updateIndication(false);
}
@@ -363,6 +389,9 @@ public class KeyguardIndicationController implements StateListener,
mTextView.setTextColor(Color.WHITE);
if (!TextUtils.isEmpty(mTransientIndication)) {
mTextView.switchIndication(mTransientIndication);
+ } else if (!TextUtils.isEmpty(mAlignmentIndication)) {
+ mTextView.switchIndication(mAlignmentIndication);
+ mTextView.setTextColor(Utils.getColorError(mContext));
} else if (mPowerPluggedIn) {
String indication = computePowerIndication();
if (animate) {
@@ -391,6 +420,9 @@ public class KeyguardIndicationController implements StateListener,
&& mKeyguardUpdateMonitor.getUserHasTrust(userId)) {
mTextView.switchIndication(trustGrantedIndication);
mTextView.setTextColor(mInitialTextColorState);
+ } else if (!TextUtils.isEmpty(mAlignmentIndication)) {
+ mTextView.switchIndication(mAlignmentIndication);
+ mTextView.setTextColor(Utils.getColorError(mContext));
} else if (mPowerPluggedIn) {
String indication = computePowerIndication();
if (DEBUG_CHARGING_SPEED) {
@@ -566,7 +598,8 @@ public class KeyguardIndicationController implements StateListener,
String message = mContext.getString(R.string.keyguard_retry);
mStatusBarKeyguardViewManager.showBouncerMessage(message, mInitialTextColorState);
} else if (mKeyguardUpdateMonitor.isScreenOn()) {
- showTransientIndication(mContext.getString(R.string.keyguard_unlock));
+ showTransientIndication(mContext.getString(R.string.keyguard_unlock),
+ mInitialTextColorState, true /* hideOnScreenOff */);
hideTransientIndicationDelayed(BaseKeyguardCallback.HIDE_DELAY_MS);
}
}
@@ -576,7 +609,11 @@ public class KeyguardIndicationController implements StateListener,
return;
}
mDozing = dozing;
- updateIndication(false);
+ if (mHideTransientMessageOnScreenOff && mDozing) {
+ hideTransientIndication();
+ } else {
+ updateIndication(false);
+ }
updateDisclosure();
}
@@ -646,8 +683,7 @@ public class KeyguardIndicationController implements StateListener,
@Override
public void onBiometricHelp(int msgId, String helpString,
BiometricSourceType biometricSourceType) {
- KeyguardUpdateMonitor updateMonitor = KeyguardUpdateMonitor.getInstance(mContext);
- if (!updateMonitor.isUnlockingWithBiometricAllowed()) {
+ if (!mKeyguardUpdateMonitor.isUnlockingWithBiometricAllowed()) {
return;
}
boolean showSwipeToUnlock =
@@ -655,8 +691,8 @@ public class KeyguardIndicationController implements StateListener,
if (mStatusBarKeyguardViewManager.isBouncerShowing()) {
mStatusBarKeyguardViewManager.showBouncerMessage(helpString,
mInitialTextColorState);
- } else if (updateMonitor.isScreenOn()) {
- showTransientIndication(helpString);
+ } else if (mKeyguardUpdateMonitor.isScreenOn()) {
+ showTransientIndication(helpString, mInitialTextColorState, showSwipeToUnlock);
if (!showSwipeToUnlock) {
hideTransientIndicationDelayed(TRANSIENT_BIOMETRIC_ERROR_TIMEOUT);
}
@@ -670,8 +706,7 @@ public class KeyguardIndicationController implements StateListener,
@Override
public void onBiometricError(int msgId, String errString,
BiometricSourceType biometricSourceType) {
- KeyguardUpdateMonitor updateMonitor = KeyguardUpdateMonitor.getInstance(mContext);
- if (shouldSuppressBiometricError(msgId, biometricSourceType, updateMonitor)) {
+ if (shouldSuppressBiometricError(msgId, biometricSourceType, mKeyguardUpdateMonitor)) {
return;
}
animatePadlockError();
@@ -681,7 +716,7 @@ public class KeyguardIndicationController implements StateListener,
showSwipeUpToUnlock();
} else if (mStatusBarKeyguardViewManager.isBouncerShowing()) {
mStatusBarKeyguardViewManager.showBouncerMessage(errString, mInitialTextColorState);
- } else if (updateMonitor.isScreenOn()) {
+ } else if (mKeyguardUpdateMonitor.isScreenOn()) {
showTransientIndication(errString);
// We want to keep this message around in case the screen was off
hideTransientIndicationDelayed(HIDE_DELAY_MS);
@@ -721,13 +756,15 @@ public class KeyguardIndicationController implements StateListener,
@Override
public void onTrustAgentErrorMessage(CharSequence message) {
- showTransientIndication(message, Utils.getColorError(mContext));
+ showTransientIndication(message, Utils.getColorError(mContext),
+ false /* hideOnScreenOff */);
}
@Override
public void onScreenTurnedOn() {
if (mMessageToShowOnScreenOn != null) {
- showTransientIndication(mMessageToShowOnScreenOn, Utils.getColorError(mContext));
+ showTransientIndication(mMessageToShowOnScreenOn, Utils.getColorError(mContext),
+ false /* hideOnScreenOff */);
// We want to keep this message around in case the screen was off
hideTransientIndicationDelayed(HIDE_DELAY_MS);
mMessageToShowOnScreenOn = null;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLifetimeExtender.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLifetimeExtender.java
index 48e2923c97d9..0f295ba75fe4 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLifetimeExtender.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLifetimeExtender.java
@@ -27,18 +27,6 @@ public interface NotificationLifetimeExtender {
boolean shouldExtendLifetime(@NonNull NotificationEntry entry);
/**
- * It's possible that a notification was canceled before it ever became visible. This callback
- * gives lifetime extenders a chance to make sure it shows up. For example if a foreground
- * service is canceled too quickly but we still want to make sure a FGS notification shows.
- * @param pendingEntry the canceled (but pending) entry
- * @return true if the notification lifetime should be extended
- */
- default boolean shouldExtendLifetimeForPendingNotification(
- @NonNull NotificationEntry pendingEntry) {
- return false;
- }
-
- /**
* Sets whether or not the lifetime should be managed by the extender. In practice, if
* shouldManage is true, this is where the extender starts managing the entry internally and is
* now responsible for calling {@link NotificationSafeToRemoveCallback#onSafeToRemove(String)}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java
index a37367e4bb25..f8fef7d4778c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java
@@ -281,24 +281,10 @@ public class NotificationEntryManager implements
}
final NotificationEntry entry = mNotificationData.get(key);
- boolean lifetimeExtended = false;
- // Notification was canceled before it got inflated
- if (entry == null) {
- NotificationEntry pendingEntry = mPendingNotifications.get(key);
- if (pendingEntry != null) {
- for (NotificationLifetimeExtender extender : mNotificationLifetimeExtenders) {
- if (extender.shouldExtendLifetimeForPendingNotification(pendingEntry)) {
- extendLifetime(pendingEntry, extender);
- lifetimeExtended = true;
- }
- }
- }
- }
+ abortExistingInflation(key);
- if (!lifetimeExtended) {
- abortExistingInflation(key);
- }
+ boolean lifetimeExtended = false;
if (entry != null) {
// If a manager needs to keep the notification around for whatever reason, we
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationInterruptionStateProvider.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationInterruptionStateProvider.java
index 150667b86828..ef09434aa395 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationInterruptionStateProvider.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationInterruptionStateProvider.java
@@ -39,6 +39,7 @@ import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.statusbar.NotificationPresenter;
import com.android.systemui.statusbar.StatusBarState;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
+import com.android.systemui.statusbar.policy.BatteryController;
import com.android.systemui.statusbar.policy.HeadsUpManager;
import javax.inject.Inject;
@@ -63,6 +64,7 @@ public class NotificationInterruptionStateProvider {
private final Context mContext;
private final PowerManager mPowerManager;
private final IDreamManager mDreamManager;
+ private final BatteryController mBatteryController;
private NotificationPresenter mPresenter;
private HeadsUpManager mHeadsUpManager;
@@ -75,13 +77,14 @@ public class NotificationInterruptionStateProvider {
@Inject
public NotificationInterruptionStateProvider(Context context, NotificationFilter filter,
- StatusBarStateController stateController) {
+ StatusBarStateController stateController, BatteryController batteryController) {
this(context,
(PowerManager) context.getSystemService(Context.POWER_SERVICE),
IDreamManager.Stub.asInterface(
ServiceManager.checkService(DreamService.DREAM_SERVICE)),
new AmbientDisplayConfiguration(context),
filter,
+ batteryController,
stateController);
}
@@ -92,10 +95,12 @@ public class NotificationInterruptionStateProvider {
IDreamManager dreamManager,
AmbientDisplayConfiguration ambientDisplayConfiguration,
NotificationFilter notificationFilter,
+ BatteryController batteryController,
StatusBarStateController statusBarStateController) {
mContext = context;
mPowerManager = powerManager;
mDreamManager = dreamManager;
+ mBatteryController = batteryController;
mAmbientDisplayConfiguration = ambientDisplayConfiguration;
mNotificationFilter = notificationFilter;
mStatusBarStateController = statusBarStateController;
@@ -293,6 +298,13 @@ public class NotificationInterruptionStateProvider {
return false;
}
+ if (mBatteryController.isAodPowerSave()) {
+ if (DEBUG_HEADS_UP) {
+ Log.d(TAG, "No pulsing: disabled by battery saver: " + sbn.getKey());
+ }
+ return false;
+ }
+
if (!canAlertCommon(entry)) {
if (DEBUG_HEADS_UP) {
Log.d(TAG, "No pulsing: notification shouldn't alert: " + sbn.getKey());
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationData.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationData.java
index 00092929fd49..299511c8f2da 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationData.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationData.java
@@ -57,7 +57,6 @@ public class NotificationData {
private final ArrayMap<String, NotificationEntry> mEntries = new ArrayMap<>();
private final ArrayList<NotificationEntry> mSortedAndFiltered = new ArrayList<>();
- private final ArrayList<NotificationEntry> mFilteredForUser = new ArrayList<>();
private final NotificationGroupManager mGroupManager =
Dependency.get(NotificationGroupManager.class);
@@ -166,20 +165,20 @@ public class NotificationData {
}
public ArrayList<NotificationEntry> getNotificationsForCurrentUser() {
- mFilteredForUser.clear();
-
synchronized (mEntries) {
final int len = mEntries.size();
+ ArrayList<NotificationEntry> filteredForUser = new ArrayList<>(len);
+
for (int i = 0; i < len; i++) {
NotificationEntry entry = mEntries.valueAt(i);
final StatusBarNotification sbn = entry.notification;
if (!getEnvironment().isNotificationForCurrentProfiles(sbn)) {
continue;
}
- mFilteredForUser.add(entry);
+ filteredForUser.add(entry);
}
+ return filteredForUser;
}
- return mFilteredForUser;
}
public NotificationEntry get(String key) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java
index 027e8e426c4b..121508b877d2 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java
@@ -155,6 +155,12 @@ public final class NotificationEntry {
public boolean canBubble;
/**
+ * Whether this notification has changed in visual appearance since the previous post.
+ * New notifications are interruptive by default.
+ */
+ public boolean isVisuallyInterruptive;
+
+ /**
* Whether this notification is shown to the user as a high priority notification: visible on
* the lock screen/status bar and in the top section in the shade.
*/
@@ -196,6 +202,7 @@ public final class NotificationEntry {
suppressedVisualEffects = ranking.getSuppressedVisualEffects();
suspended = ranking.isSuspended();
canBubble = ranking.canBubble();
+ isVisuallyInterruptive = ranking.visuallyInterruptive();
}
public void setInterruption() {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGuts.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGuts.java
index 4700baae8fab..18d436ff7659 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGuts.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGuts.java
@@ -421,7 +421,7 @@ public class NotificationGuts extends FrameLayout {
}
/** Listener for animations executed in {@link #animateClose(int, int, boolean)}. */
- private static class AnimateCloseListener extends AnimatorListenerAdapter {
+ private class AnimateCloseListener extends AnimatorListenerAdapter {
final View mView;
private final GutsContent mGutsContent;
@@ -433,8 +433,10 @@ public class NotificationGuts extends FrameLayout {
@Override
public void onAnimationEnd(Animator animation) {
super.onAnimationEnd(animation);
- mView.setVisibility(View.GONE);
- mGutsContent.onFinishedClosing();
+ if (!isExposed()) {
+ mView.setVisibility(View.GONE);
+ mGutsContent.onFinishedClosing();
+ }
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGutsManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGutsManager.java
index 8f7671a5dd96..719ec3215d42 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGutsManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGutsManager.java
@@ -100,6 +100,7 @@ public class NotificationGutsManager implements Dumpable, NotificationLifetimeEx
protected String mKeyToRemoveOnGutsClosed;
private StatusBar mStatusBar;
+ private Runnable mOpenRunnable;
@Inject
public NotificationGutsManager(
@@ -343,6 +344,7 @@ public class NotificationGutsManager implements Dumpable, NotificationLifetimeEx
public void closeAndSaveGuts(boolean removeLeavebehinds, boolean force, boolean removeControls,
int x, int y, boolean resetMenu) {
if (mNotificationGutsExposed != null) {
+ mNotificationGutsExposed.removeCallbacks(mOpenRunnable);
mNotificationGutsExposed.closeControls(removeLeavebehinds, removeControls, x, y, force);
}
if (resetMenu) {
@@ -445,7 +447,7 @@ public class NotificationGutsManager implements Dumpable, NotificationLifetimeEx
// ensure that it's laid but not visible until actually laid out
guts.setVisibility(View.INVISIBLE);
// Post to ensure the the guts are properly laid out.
- guts.post(new Runnable() {
+ mOpenRunnable = new Runnable() {
@Override
public void run() {
if (row.getWindowToken() == null) {
@@ -470,7 +472,8 @@ public class NotificationGutsManager implements Dumpable, NotificationLifetimeEx
mListContainer.onHeightChanged(row, true /* needsAnimation */);
mGutsMenuItem = menuItem;
}
- });
+ };
+ guts.post(mOpenRunnable);
return true;
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithm.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithm.java
index 179375e31dd3..4e91e4c84e99 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithm.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithm.java
@@ -240,7 +240,7 @@ public class KeyguardClockPositionAlgorithm {
* @return Alpha from 0 to 1.
*/
private float getClockAlpha(int y) {
- float alphaKeyguard = Math.max(0, y / Math.max(1f, getExpandedPreferredClockY()));
+ float alphaKeyguard = Math.max(0, y / Math.max(1f, getClockY(1f)));
alphaKeyguard = Interpolators.ACCELERATE.getInterpolation(alphaKeyguard);
return MathUtils.lerp(alphaKeyguard, 1f, mDarkAmount);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardLiftController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardLiftController.kt
index f4635d1270a8..f7b8a2e29129 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardLiftController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardLiftController.kt
@@ -67,6 +67,9 @@ class KeyguardLiftController constructor(
}
private fun updateListeningState() {
+ if (pickupSensor == null) {
+ return
+ }
val onKeyguard = keyguardUpdateMonitor.isKeyguardVisible &&
!statusBarStateController.isDozing
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockscreenWallpaper.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockscreenWallpaper.java
index d2023ec49ebc..dcb349ba9c8d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockscreenWallpaper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockscreenWallpaper.java
@@ -79,10 +79,13 @@ public class LockscreenWallpaper extends IWallpaperManagerCallback.Stub implemen
IWallpaperManager service = IWallpaperManager.Stub.asInterface(
ServiceManager.getService(Context.WALLPAPER_SERVICE));
- try {
- service.setLockWallpaperCallback(this);
- } catch (RemoteException e) {
- Log.e(TAG, "System dead?" + e);
+ if (service != null) {
+ // Service is disabled on some devices like Automotive
+ try {
+ service.setLockWallpaperCallback(this);
+ } catch (RemoteException e) {
+ Log.e(TAG, "System dead?" + e);
+ }
}
}
@@ -108,6 +111,11 @@ public class LockscreenWallpaper extends IWallpaperManagerCallback.Stub implemen
public LoaderResult loadBitmap(int currentUserId, UserHandle selectedUser) {
// May be called on any thread - only use thread safe operations.
+ if (!mWallpaperManager.isWallpaperSupported()) {
+ // When wallpaper is not supported, show the system wallpaper
+ return LoaderResult.success(null);
+ }
+
// Prefer the selected user (when specified) over the current user for the FLAG_SET_LOCK
// wallpaper.
final int lockWallpaperUserId =
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
index 559df18ef478..7f11759e4f4e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
@@ -229,7 +229,6 @@ import com.android.systemui.statusbar.policy.UserInfoController;
import com.android.systemui.statusbar.policy.UserInfoControllerImpl;
import com.android.systemui.statusbar.policy.UserSwitcherController;
import com.android.systemui.statusbar.policy.ZenModeController;
-import com.android.systemui.tuner.TunerService;
import com.android.systemui.util.InjectionInflationController;
import com.android.systemui.volume.VolumeComponent;
@@ -344,7 +343,7 @@ public class StatusBar extends SystemUI implements DemoMode,
private BrightnessMirrorController mBrightnessMirrorController;
private boolean mBrightnessMirrorVisible;
protected BiometricUnlockController mBiometricUnlockController;
- private LightBarController mLightBarController;
+ protected LightBarController mLightBarController;
protected LockscreenWallpaper mLockscreenWallpaper;
@VisibleForTesting
protected AutoHideController mAutoHideController;
@@ -410,17 +409,17 @@ public class StatusBar extends SystemUI implements DemoMode,
private final int[] mAbsPos = new int[2];
private final ArrayList<Runnable> mPostCollapseRunnables = new ArrayList<>();
- private NotificationGutsManager mGutsManager;
+ protected NotificationGutsManager mGutsManager;
protected NotificationLogger mNotificationLogger;
protected NotificationEntryManager mEntryManager;
- private NotificationListController mNotificationListController;
- private NotificationInterruptionStateProvider mNotificationInterruptionStateProvider;
+ protected NotificationListController mNotificationListController;
+ protected NotificationInterruptionStateProvider mNotificationInterruptionStateProvider;
protected NotificationViewHierarchyManager mViewHierarchyManager;
protected ForegroundServiceController mForegroundServiceController;
protected AppOpsController mAppOpsController;
protected KeyguardViewMediator mKeyguardViewMediator;
- private ZenModeController mZenController;
- private final NotificationAlertingManager mNotificationAlertingManager =
+ protected ZenModeController mZenController;
+ protected final NotificationAlertingManager mNotificationAlertingManager =
Dependency.get(NotificationAlertingManager.class);
// for disabling the status bar
@@ -483,18 +482,20 @@ public class StatusBar extends SystemUI implements DemoMode,
protected boolean mDozing;
private boolean mDozingRequested;
- private NotificationMediaManager mMediaManager;
+ protected NotificationMediaManager mMediaManager;
protected NotificationLockscreenUserManager mLockscreenUserManager;
protected NotificationRemoteInputManager mRemoteInputManager;
+ private boolean mWallpaperSupported;
private final BroadcastReceiver mWallpaperChangedReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
- WallpaperManager wallpaperManager = context.getSystemService(WallpaperManager.class);
- if (wallpaperManager == null) {
- Log.w(TAG, "WallpaperManager not available");
+ if (!mWallpaperSupported) {
+ // Receiver should not have been registered at all...
+ Log.wtf(TAG, "WallpaperManager not supported");
return;
}
+ WallpaperManager wallpaperManager = context.getSystemService(WallpaperManager.class);
WallpaperInfo info = wallpaperManager.getWallpaperInfo(UserHandle.USER_CURRENT);
final boolean deviceSupportsAodWallpaper = mContext.getResources().getBoolean(
com.android.internal.R.bool.config_dozeSupportsAodWallpaper);
@@ -555,18 +556,19 @@ public class StatusBar extends SystemUI implements DemoMode,
private KeyguardUserSwitcher mKeyguardUserSwitcher;
protected UserSwitcherController mUserSwitcherController;
- private NetworkController mNetworkController;
- private KeyguardMonitor mKeyguardMonitor = Dependency.get(KeyguardMonitor.class);
- private BatteryController mBatteryController;
+ protected NetworkController mNetworkController;
+ protected KeyguardMonitor mKeyguardMonitor;
+ protected BatteryController mBatteryController;
protected boolean mPanelExpanded;
private UiModeManager mUiModeManager;
protected boolean mIsKeyguard;
private LogMaker mStatusBarStateLog;
protected NotificationIconAreaController mNotificationIconAreaController;
@Nullable private View mAmbientIndicationContainer;
- private SysuiColorExtractor mColorExtractor;
- private ScreenLifecycle mScreenLifecycle;
- @VisibleForTesting WakefulnessLifecycle mWakefulnessLifecycle;
+ protected SysuiColorExtractor mColorExtractor;
+ protected ScreenLifecycle mScreenLifecycle;
+ @VisibleForTesting
+ protected WakefulnessLifecycle mWakefulnessLifecycle;
private final View.OnClickListener mGoToLockedShadeListener = v -> {
if (mState == StatusBarState.KEYGUARD) {
@@ -598,7 +600,7 @@ public class StatusBar extends SystemUI implements DemoMode,
private HeadsUpAppearanceController mHeadsUpAppearanceController;
private boolean mVibrateOnOpening;
- private VibratorHelper mVibratorHelper;
+ protected VibratorHelper mVibratorHelper;
private ActivityLaunchAnimator mActivityLaunchAnimator;
protected StatusBarNotificationPresenter mPresenter;
private NotificationActivityStarter mNotificationActivityStarter;
@@ -628,40 +630,24 @@ public class StatusBar extends SystemUI implements DemoMode,
@Override
public void start() {
- mGroupManager = Dependency.get(NotificationGroupManager.class);
- mGroupAlertTransferHelper = Dependency.get(NotificationGroupAlertTransferHelper.class);
- mVisualStabilityManager = Dependency.get(VisualStabilityManager.class);
- mNotificationLogger = Dependency.get(NotificationLogger.class);
- mRemoteInputManager = Dependency.get(NotificationRemoteInputManager.class);
- mNotificationListener = Dependency.get(NotificationListener.class);
+ getDependencies();
+ if (mScreenLifecycle != null && mScreenObserver != null) {
+ mScreenLifecycle.addObserver(mScreenObserver);
+ }
+
+ if (mWakefulnessLifecycle != null && mWakefulnessObserver != null) {
+ mWakefulnessLifecycle.addObserver(mWakefulnessObserver);
+ }
+
mNotificationListener.registerAsSystemService();
- mNetworkController = Dependency.get(NetworkController.class);
- mUserSwitcherController = Dependency.get(UserSwitcherController.class);
- mScreenLifecycle = Dependency.get(ScreenLifecycle.class);
- mScreenLifecycle.addObserver(mScreenObserver);
- mWakefulnessLifecycle = Dependency.get(WakefulnessLifecycle.class);
- mWakefulnessLifecycle.addObserver(mWakefulnessObserver);
- mBatteryController = Dependency.get(BatteryController.class);
- mAssistManager = Dependency.get(AssistManager.class);
+ if (mBubbleController != null) {
+ mBubbleController.setExpandListener(mBubbleExpandListener);
+ }
+
mUiModeManager = mContext.getSystemService(UiModeManager.class);
- mLockscreenUserManager = Dependency.get(NotificationLockscreenUserManager.class);
- mGutsManager = Dependency.get(NotificationGutsManager.class);
- mMediaManager = Dependency.get(NotificationMediaManager.class);
- mEntryManager = Dependency.get(NotificationEntryManager.class);
- mBypassHeadsUpNotifier.setUp(mEntryManager);
- mNotificationInterruptionStateProvider =
- Dependency.get(NotificationInterruptionStateProvider.class);
- mViewHierarchyManager = Dependency.get(NotificationViewHierarchyManager.class);
- mForegroundServiceController = Dependency.get(ForegroundServiceController.class);
- mAppOpsController = Dependency.get(AppOpsController.class);
- mZenController = Dependency.get(ZenModeController.class);
mKeyguardViewMediator = getComponent(KeyguardViewMediator.class);
- mColorExtractor = Dependency.get(SysuiColorExtractor.class);
- mDeviceProvisionedController = Dependency.get(DeviceProvisionedController.class);
- mNavigationBarController = Dependency.get(NavigationBarController.class);
- mBubbleController = Dependency.get(BubbleController.class);
- mBubbleController.setExpandListener(mBubbleExpandListener);
mActivityIntentHelper = new ActivityIntentHelper(mContext);
+
KeyguardSliceProvider sliceProvider = KeyguardSliceProvider.getAttachedInstance();
if (sliceProvider != null) {
sliceProvider.initDependencies(mMediaManager, mStatusBarStateController,
@@ -721,11 +707,18 @@ public class StatusBar extends SystemUI implements DemoMode,
createAndAddWindows(result);
- // Make sure we always have the most current wallpaper info.
- IntentFilter wallpaperChangedFilter = new IntentFilter(Intent.ACTION_WALLPAPER_CHANGED);
- mContext.registerReceiverAsUser(mWallpaperChangedReceiver, UserHandle.ALL,
- wallpaperChangedFilter, null /* broadcastPermission */, null /* scheduler */);
- mWallpaperChangedReceiver.onReceive(mContext, null);
+ mWallpaperSupported =
+ mContext.getSystemService(WallpaperManager.class).isWallpaperSupported();
+
+ if (mWallpaperSupported) {
+ // Make sure we always have the most current wallpaper info.
+ IntentFilter wallpaperChangedFilter = new IntentFilter(Intent.ACTION_WALLPAPER_CHANGED);
+ mContext.registerReceiverAsUser(mWallpaperChangedReceiver, UserHandle.ALL,
+ wallpaperChangedFilter, null /* broadcastPermission */, null /* scheduler */);
+ mWallpaperChangedReceiver.onReceive(mContext, null);
+ } else if (DEBUG) {
+ Log.v(TAG, "start(): no wallpaper service ");
+ }
// Set up the initial notification state. This needs to happen before CommandQueue.disable()
setUpPresenter();
@@ -760,12 +753,14 @@ public class StatusBar extends SystemUI implements DemoMode,
mContext.registerReceiver(mBannerActionBroadcastReceiver, internalFilter, PERMISSION_SELF,
null);
- IWallpaperManager wallpaperManager = IWallpaperManager.Stub.asInterface(
- ServiceManager.getService(Context.WALLPAPER_SERVICE));
- try {
- wallpaperManager.setInAmbientMode(false /* ambientMode */, 0L /* duration */);
- } catch (RemoteException e) {
- // Just pass, nothing critical.
+ if (mWallpaperSupported) {
+ IWallpaperManager wallpaperManager = IWallpaperManager.Stub.asInterface(
+ ServiceManager.getService(Context.WALLPAPER_SERVICE));
+ try {
+ wallpaperManager.setInAmbientMode(false /* ambientMode */, 0L /* duration */);
+ } catch (RemoteException e) {
+ // Just pass, nothing critical.
+ }
}
// end old BaseStatusBar.start().
@@ -910,20 +905,22 @@ public class StatusBar extends SystemUI implements DemoMode,
R.id.ambient_indication_container);
// TODO: Find better place for this callback.
- mBatteryController.addCallback(new BatteryStateChangeCallback() {
- @Override
- public void onPowerSaveChanged(boolean isPowerSave) {
- mHandler.post(mCheckBarModes);
- if (mDozeServiceHost != null) {
- mDozeServiceHost.firePowerSaveChanged(isPowerSave);
+ if (mBatteryController != null) {
+ mBatteryController.addCallback(new BatteryStateChangeCallback() {
+ @Override
+ public void onPowerSaveChanged(boolean isPowerSave) {
+ mHandler.post(mCheckBarModes);
+ if (mDozeServiceHost != null) {
+ mDozeServiceHost.firePowerSaveChanged(isPowerSave);
+ }
}
- }
- @Override
- public void onBatteryLevelChanged(int level, boolean pluggedIn, boolean charging) {
- // noop
- }
- });
+ @Override
+ public void onBatteryLevelChanged(int level, boolean pluggedIn, boolean charging) {
+ // noop
+ }
+ });
+ }
mAutoHideController = Dependency.get(AutoHideController.class);
mAutoHideController.setStatusBar(this);
@@ -965,28 +962,7 @@ public class StatusBar extends SystemUI implements DemoMode,
mStatusBarWindow::onShowingLaunchAffordanceChanged);
// Set up the quick settings tile panel
- View container = mStatusBarWindow.findViewById(R.id.qs_frame);
- if (container != null) {
- FragmentHostManager fragmentHostManager = FragmentHostManager.get(container);
- ExtensionFragmentListener.attachExtensonToFragment(container, QS.TAG, R.id.qs_frame,
- Dependency.get(ExtensionController.class)
- .newExtension(QS.class)
- .withPlugin(QS.class)
- .withDefault(this::createDefaultQSFragment)
- .build());
- mBrightnessMirrorController = new BrightnessMirrorController(mStatusBarWindow,
- (visible) -> {
- mBrightnessMirrorVisible = visible;
- updateScrimController();
- });
- fragmentHostManager.addTagListener(QS.TAG, (tag, f) -> {
- QS qs = (QS) f;
- if (qs instanceof QSFragment) {
- mQSPanel = ((QSFragment) qs).getQsPanel();
- mQSPanel.setBrightnessMirror(mBrightnessMirrorController);
- }
- });
- }
+ setUpQuickSettingsTilePanel();
mReportRejectedTouch = mStatusBarWindow.findViewById(R.id.report_rejected_touch);
if (mReportRejectedTouch != null) {
@@ -1086,7 +1062,9 @@ public class StatusBar extends SystemUI implements DemoMode,
mForegroundServiceController,
mDeviceProvisionedController);
- mAppOpsController.addCallback(APP_OPS, this);
+ if (mAppOpsController != null) {
+ mAppOpsController.addCallback(APP_OPS, this);
+ }
mNotificationShelf.setOnActivatedListener(mPresenter);
mRemoteInputManager.getController().addCallback(mStatusBarWindowController);
@@ -1116,6 +1094,73 @@ public class StatusBar extends SystemUI implements DemoMode,
mNotificationListController.bind();
}
+ protected void getDependencies() {
+ // Icons
+ mIconController = Dependency.get(StatusBarIconController.class);
+ mLightBarController = Dependency.get(LightBarController.class);
+
+ // Keyguard
+ mKeyguardMonitor = Dependency.get(KeyguardMonitor.class);
+ mScreenLifecycle = Dependency.get(ScreenLifecycle.class);
+ mWakefulnessLifecycle = Dependency.get(WakefulnessLifecycle.class);
+
+ // Notifications
+ mEntryManager = Dependency.get(NotificationEntryManager.class);
+ mForegroundServiceController = Dependency.get(ForegroundServiceController.class);
+ mGroupAlertTransferHelper = Dependency.get(NotificationGroupAlertTransferHelper.class);
+ mGroupManager = Dependency.get(NotificationGroupManager.class);
+ mGutsManager = Dependency.get(NotificationGutsManager.class);
+ mLockscreenUserManager = Dependency.get(NotificationLockscreenUserManager.class);
+ mMediaManager = Dependency.get(NotificationMediaManager.class);
+ mNotificationInterruptionStateProvider =
+ Dependency.get(NotificationInterruptionStateProvider.class);
+ mNotificationListener = Dependency.get(NotificationListener.class);
+ mNotificationLogger = Dependency.get(NotificationLogger.class);
+ mRemoteInputManager = Dependency.get(NotificationRemoteInputManager.class);
+ mViewHierarchyManager = Dependency.get(NotificationViewHierarchyManager.class);
+ mVisualStabilityManager = Dependency.get(VisualStabilityManager.class);
+
+ // Policy
+ mBatteryController = Dependency.get(BatteryController.class);
+ mNetworkController = Dependency.get(NetworkController.class);
+ mZenController = Dependency.get(ZenModeController.class);
+
+ // Others
+ mAppOpsController = Dependency.get(AppOpsController.class);
+ mAssistManager = Dependency.get(AssistManager.class);
+ mBubbleController = Dependency.get(BubbleController.class);
+ mColorExtractor = Dependency.get(SysuiColorExtractor.class);
+ mNavigationBarController = Dependency.get(NavigationBarController.class);
+ mUserSwitcherController = Dependency.get(UserSwitcherController.class);
+ mVibratorHelper = Dependency.get(VibratorHelper.class);
+ }
+
+ protected void setUpQuickSettingsTilePanel() {
+ View container = mStatusBarWindow.findViewById(R.id.qs_frame);
+ if (container != null) {
+ FragmentHostManager fragmentHostManager = FragmentHostManager.get(container);
+ ExtensionFragmentListener.attachExtensonToFragment(container, QS.TAG, R.id.qs_frame,
+ Dependency.get(ExtensionController.class)
+ .newExtension(QS.class)
+ .withPlugin(QS.class)
+ .withDefault(this::createDefaultQSFragment)
+ .build());
+ mBrightnessMirrorController = new BrightnessMirrorController(mStatusBarWindow,
+ (visible) -> {
+ mBrightnessMirrorVisible = visible;
+ updateScrimController();
+ });
+ fragmentHostManager.addTagListener(QS.TAG, (tag, f) -> {
+ QS qs = (QS) f;
+ if (qs instanceof QSFragment) {
+ mQSPanel = ((QSFragment) qs).getQsPanel();
+ mQSPanel.setBrightnessMirror(mBrightnessMirrorController);
+ }
+ });
+ }
+ }
+
+
/**
* Post-init task of {@link #start()}
* @param state1 disable1 flags
@@ -1936,7 +1981,9 @@ public class StatusBar extends SystemUI implements DemoMode,
mStatusBarWindow.cancelExpandHelper();
mStatusBarView.collapsePanel(true /* animate */, delayed, speedUpFactor);
} else {
- mBubbleController.collapseStack();
+ if (mBubbleController != null) {
+ mBubbleController.collapseStack();
+ }
}
}
@@ -2318,6 +2365,7 @@ public class StatusBar extends SystemUI implements DemoMode,
pw.println(Settings.Global.zenModeToString(Settings.Global.getInt(
mContext.getContentResolver(), Settings.Global.ZEN_MODE,
Settings.Global.ZEN_MODE_OFF)));
+ pw.print(" mWallpaperSupported= "); pw.println(mWallpaperSupported);
if (mStatusBarView != null) {
dumpBarTransitions(pw, "mStatusBarView", mStatusBarView.getBarTransitions());
@@ -2590,7 +2638,7 @@ public class StatusBar extends SystemUI implements DemoMode,
if (mRemoteInputManager.getController() != null) {
mRemoteInputManager.getController().closeRemoteInputs();
}
- if (mBubbleController.isStackExpanded()) {
+ if (mBubbleController != null && mBubbleController.isStackExpanded()) {
mBubbleController.collapseStack();
}
if (mLockscreenUserManager.isCurrentProfile(getSendingUserId())) {
@@ -2606,7 +2654,7 @@ public class StatusBar extends SystemUI implements DemoMode,
if (mStatusBarWindowController != null) {
mStatusBarWindowController.setNotTouchable(false);
}
- if (mBubbleController.isStackExpanded()) {
+ if (mBubbleController != null && mBubbleController.isStackExpanded()) {
mBubbleController.collapseStack();
}
finishBarAnimations();
@@ -2702,7 +2750,9 @@ public class StatusBar extends SystemUI implements DemoMode,
public void setLockscreenUser(int newUserId) {
mLockscreenWallpaper.setCurrentUser(newUserId);
mScrimController.setCurrentUser(newUserId);
- mWallpaperChangedReceiver.onReceive(mContext, null);
+ if (mWallpaperSupported) {
+ mWallpaperChangedReceiver.onReceive(mContext, null);
+ }
}
/**
@@ -2984,7 +3034,9 @@ public class StatusBar extends SystemUI implements DemoMode,
mStatusBarStateController.setLeaveOpenOnKeyguardHide(false);
mPendingRemoteInputView = null;
updateIsKeyguard();
- mAssistManager.onLockscreenShown();
+ if (mAssistManager != null) {
+ mAssistManager.onLockscreenShown();
+ }
}
public boolean hideKeyguard() {
@@ -3030,7 +3082,7 @@ public class StatusBar extends SystemUI implements DemoMode,
public void showKeyguardImpl() {
mIsKeyguard = true;
- if (mKeyguardMonitor.isLaunchTransitionFadingAway()) {
+ if (mKeyguardMonitor != null && mKeyguardMonitor.isLaunchTransitionFadingAway()) {
mNotificationPanel.animate().cancel();
onLaunchTransitionFadingEnded();
}
@@ -3334,7 +3386,9 @@ public class StatusBar extends SystemUI implements DemoMode,
if (mNotificationPanel.canPanelBeCollapsed()) {
animateCollapsePanels();
} else {
- mBubbleController.performBackPressIfNeeded();
+ if (mBubbleController != null) {
+ mBubbleController.performBackPressIfNeeded();
+ }
}
return true;
}
@@ -3495,9 +3549,11 @@ public class StatusBar extends SystemUI implements DemoMode,
}
private void updateKeyguardState() {
- mKeyguardMonitor.notifyKeyguardState(mStatusBarKeyguardViewManager.isShowing(),
- mUnlockMethodCache.isMethodSecure(),
- mStatusBarKeyguardViewManager.isOccluded());
+ if (mKeyguardMonitor != null) {
+ mKeyguardMonitor.notifyKeyguardState(mStatusBarKeyguardViewManager.isShowing(),
+ mUnlockMethodCache.isMethodSecure(),
+ mStatusBarKeyguardViewManager.isOccluded());
+ }
}
public void onActivationReset() {
@@ -3886,7 +3942,7 @@ public class StatusBar extends SystemUI implements DemoMode,
// We don't want to end up in KEYGUARD state when we're unlocking with
// fingerprint from doze. We should cross fade directly from black.
boolean unlocking = mBiometricUnlockController.isWakeAndUnlock()
- || mKeyguardMonitor.isKeyguardFadingAway();
+ || (mKeyguardMonitor != null && mKeyguardMonitor.isKeyguardFadingAway());
// Do not animate the scrim expansion when triggered by the fingerprint sensor.
mScrimController.setExpansionAffectsAlpha(
@@ -3915,7 +3971,7 @@ public class StatusBar extends SystemUI implements DemoMode,
mScrimController.transitionTo(ScrimState.AOD);
} else if (mIsKeyguard && !unlocking) {
mScrimController.transitionTo(ScrimState.KEYGUARD);
- } else if (mBubbleController.isStackExpanded()) {
+ } else if (mBubbleController != null && mBubbleController.isStackExpanded()) {
mScrimController.transitionTo(ScrimState.BUBBLE_EXPANDED);
} else {
mScrimController.transitionTo(ScrimState.UNLOCKED, mUnlockScrimCallback);
@@ -4217,8 +4273,8 @@ public class StatusBar extends SystemUI implements DemoMode,
protected StatusBarKeyguardViewManager mStatusBarKeyguardViewManager;
protected KeyguardManager mKeyguardManager;
- private DeviceProvisionedController mDeviceProvisionedController
- = Dependency.get(DeviceProvisionedController.class);
+ protected DeviceProvisionedController mDeviceProvisionedController =
+ Dependency.get(DeviceProvisionedController.class);
protected NavigationBarController mNavigationBarController;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
index 0c47d1468a7f..68ee8bbbb69b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
@@ -62,6 +62,8 @@ import com.android.systemui.statusbar.policy.KeyguardMonitorImpl;
import java.io.PrintWriter;
import java.util.ArrayList;
+import androidx.annotation.VisibleForTesting;
+
/**
* Manages creating, showing, hiding and resetting the keyguard within the status bar. Calls back
* via {@link ViewMediatorCallback} to poke the wake lock and report that the keyguard is done,
@@ -161,6 +163,7 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb
private boolean mLastLockVisible;
private OnDismissAction mAfterKeyguardGoneAction;
+ private Runnable mKeyguardGoneCancelAction;
private final ArrayList<Runnable> mAfterKeyguardGoneRunnables = new ArrayList<>();
// Dismiss action to be launched when we stop dozing or the keyguard is gone.
@@ -328,10 +331,20 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb
return false;
}
- private void hideBouncer(boolean destroyView) {
+ @VisibleForTesting
+ void hideBouncer(boolean destroyView) {
if (mBouncer == null) {
return;
}
+ if (mShowing) {
+ // If we were showing the bouncer and then aborting, we need to also clear out any
+ // potential actions unless we actually unlocked.
+ mAfterKeyguardGoneAction = null;
+ if (mKeyguardGoneCancelAction != null) {
+ mKeyguardGoneCancelAction.run();
+ mKeyguardGoneCancelAction = null;
+ }
+ }
mBouncer.hide(destroyView);
cancelPendingWakeupAction();
}
@@ -364,6 +377,7 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb
mBouncer.showWithDismissAction(r, cancelAction);
} else {
mAfterKeyguardGoneAction = r;
+ mKeyguardGoneCancelAction = cancelAction;
mBouncer.show(false /* resetSecuritySelection */);
}
}
@@ -671,6 +685,7 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb
mAfterKeyguardGoneAction.onDismiss();
mAfterKeyguardGoneAction = null;
}
+ mKeyguardGoneCancelAction = null;
for (int i = 0; i < mAfterKeyguardGoneRunnables.size(); i++) {
mAfterKeyguardGoneRunnables.get(i).run();
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java
index 069219802cc3..a870590c08ac 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java
@@ -224,7 +224,7 @@ public class StatusBarNotificationPresenter implements NotificationPresenter,
mVisualStabilityManager.setUpWithPresenter(this);
mGutsManager.setUpWithPresenter(this,
notifListContainer, mCheckSaveListener, mOnSettingsClickListener);
- // ForegroundServiceNotificationListener adds its listener in its constructor
+ // ForegroundServiceControllerListener adds its listener in its constructor
// but we need to request it here in order for it to be instantiated.
// TODO: figure out how to do this correctly once Dependency.get() is gone.
Dependency.get(ForegroundServiceNotificationListener.class);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryController.java
index 111cdd2cc32a..738d076e13c5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryController.java
@@ -45,9 +45,7 @@ public interface BatteryController extends DemoMode, Dumpable,
/**
* Returns {@code true} if AOD was disabled by power saving policies.
*/
- default boolean isAodPowerSave() {
- return isPowerSave();
- }
+ boolean isAodPowerSave();
/**
* A listener that will be notified whenever a change in battery level or power save mode has
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/SmartReplyView.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/SmartReplyView.java
index ed0b9d929466..919ca12648ed 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/SmartReplyView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/SmartReplyView.java
@@ -22,6 +22,7 @@ import android.text.TextPaint;
import android.text.method.TransformationMethod;
import android.util.AttributeSet;
import android.util.Log;
+import android.view.ContextThemeWrapper;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
@@ -238,13 +239,15 @@ public class SmartReplyView extends ViewGroup {
public List<Button> inflateSmartActions(Context packageContext,
@NonNull SmartActions smartActions, SmartReplyController smartReplyController,
NotificationEntry entry, HeadsUpManager headsUpManager, boolean delayOnClickListener) {
+ Context themedPackageContext = new ContextThemeWrapper(packageContext, mContext.getTheme());
List<Button> buttons = new ArrayList<>();
int numSmartActions = smartActions.actions.size();
for (int n = 0; n < numSmartActions; n++) {
Notification.Action action = smartActions.actions.get(n);
if (action.actionIntent != null) {
buttons.add(inflateActionButton(
- this, getContext(), packageContext, n, smartActions, smartReplyController,
+ this, getContext(), themedPackageContext, n, smartActions,
+ smartReplyController,
entry, headsUpManager, delayOnClickListener));
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/ForegroundServiceLifetimeExtenderTest.java b/packages/SystemUI/tests/src/com/android/systemui/ForegroundServiceLifetimeExtenderTest.java
deleted file mode 100644
index b1dabdda2241..000000000000
--- a/packages/SystemUI/tests/src/com/android/systemui/ForegroundServiceLifetimeExtenderTest.java
+++ /dev/null
@@ -1,82 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui;
-
-import static com.android.systemui.ForegroundServiceLifetimeExtender.MIN_FGS_TIME_MS;
-
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertTrue;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.when;
-
-import android.app.Notification;
-import android.service.notification.StatusBarNotification;
-
-import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
-
-import com.android.systemui.statusbar.notification.collection.NotificationEntry;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-@RunWith(AndroidJUnit4.class)
-@SmallTest
-public class ForegroundServiceLifetimeExtenderTest extends SysuiTestCase {
- private ForegroundServiceLifetimeExtender mExtender = new ForegroundServiceLifetimeExtender();
- private StatusBarNotification mSbn;
- private NotificationEntry mEntry;
- private Notification mNotif;
-
- @Before
- public void setup() {
- mNotif = new Notification.Builder(mContext, "")
- .setSmallIcon(R.drawable.ic_person)
- .setContentTitle("Title")
- .setContentText("Text")
- .build();
-
- mSbn = mock(StatusBarNotification.class);
- when(mSbn.getNotification()).thenReturn(mNotif);
-
- mEntry = new NotificationEntry(mSbn);
- }
-
- @Test
- public void testShouldExtendLifetime_should_foreground() {
- // Extend the lifetime of a FGS notification iff it has not been visible
- // for the minimum time
- mNotif.flags |= Notification.FLAG_FOREGROUND_SERVICE;
- when(mSbn.getPostTime()).thenReturn(System.currentTimeMillis());
- assertTrue(mExtender.shouldExtendLifetime(mEntry));
- }
-
- @Test
- public void testShouldExtendLifetime_shouldNot_foreground() {
- mNotif.flags |= Notification.FLAG_FOREGROUND_SERVICE;
- when(mSbn.getPostTime()).thenReturn(System.currentTimeMillis() - MIN_FGS_TIME_MS - 1);
- assertFalse(mExtender.shouldExtendLifetime(mEntry));
- }
-
- @Test
- public void testShouldExtendLifetime_shouldNot_notForeground() {
- mNotif.flags = 0;
- when(mSbn.getPostTime()).thenReturn(System.currentTimeMillis() - MIN_FGS_TIME_MS - 1);
- assertFalse(mExtender.shouldExtendLifetime(mEntry));
- }
-}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/assist/AssistHandleBehaviorControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/assist/AssistHandleBehaviorControllerTest.java
index 20983fc16080..caca6631f85b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/assist/AssistHandleBehaviorControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/assist/AssistHandleBehaviorControllerTest.java
@@ -67,7 +67,7 @@ public class AssistHandleBehaviorControllerTest extends SysuiTestCase {
@Mock private ScreenDecorations mMockScreenDecorations;
@Mock private AssistUtils mMockAssistUtils;
@Mock private Handler mMockHandler;
- @Mock private PhenotypeHelper mMockPhenotypeHelper;
+ @Mock private DeviceConfigHelper mMockDeviceConfigHelper;
@Mock private AssistHandleOffBehavior mMockOffBehavior;
@Mock private AssistHandleLikeHomeBehavior mMockLikeHomeBehavior;
@Mock private AssistHandleReminderExpBehavior mMockReminderExpBehavior;
@@ -97,7 +97,7 @@ public class AssistHandleBehaviorControllerTest extends SysuiTestCase {
mMockAssistUtils,
mMockHandler,
() -> mMockScreenDecorations,
- mMockPhenotypeHelper,
+ mMockDeviceConfigHelper,
behaviorMap,
mMockNavigationModeController,
mMockDumpController);
@@ -216,7 +216,7 @@ public class AssistHandleBehaviorControllerTest extends SysuiTestCase {
public void showAndGo_doesNothingIfRecentlyHidden() {
// Arrange
when(mMockAssistUtils.getAssistComponentForUser(anyInt())).thenReturn(COMPONENT_NAME);
- when(mMockPhenotypeHelper.getLong(
+ when(mMockDeviceConfigHelper.getLong(
eq(SystemUiDeviceConfigFlags.ASSIST_HANDLES_SHOWN_FREQUENCY_THRESHOLD_MS),
anyLong())).thenReturn(10000L);
mAssistHandleBehaviorController.showAndGo();
@@ -297,7 +297,7 @@ public class AssistHandleBehaviorControllerTest extends SysuiTestCase {
public void showAndGoDelayed_doesNothingIfRecentlyHidden() {
// Arrange
when(mMockAssistUtils.getAssistComponentForUser(anyInt())).thenReturn(COMPONENT_NAME);
- when(mMockPhenotypeHelper.getLong(
+ when(mMockDeviceConfigHelper.getLong(
eq(SystemUiDeviceConfigFlags.ASSIST_HANDLES_SHOWN_FREQUENCY_THRESHOLD_MS),
anyLong())).thenReturn(10000L);
mAssistHandleBehaviorController.showAndGo();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleControllerTest.java
index ba434d4fd0bd..5a4e6c903650 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleControllerTest.java
@@ -74,6 +74,7 @@ import com.android.systemui.statusbar.phone.DozeParameters;
import com.android.systemui.statusbar.phone.NotificationGroupManager;
import com.android.systemui.statusbar.phone.KeyguardBypassController;
import com.android.systemui.statusbar.phone.StatusBarWindowController;
+import com.android.systemui.statusbar.policy.BatteryController;
import com.android.systemui.statusbar.policy.ConfigurationController;
import com.android.systemui.statusbar.policy.HeadsUpManager;
import com.android.systemui.statusbar.policy.ZenModeController;
@@ -173,7 +174,8 @@ public class BubbleControllerTest extends SysuiTestCase {
TestableNotificationInterruptionStateProvider interruptionStateProvider =
new TestableNotificationInterruptionStateProvider(mContext,
mock(NotificationFilter.class),
- mock(StatusBarStateController.class));
+ mock(StatusBarStateController.class),
+ mock(BatteryController.class));
interruptionStateProvider.setUpWithPresenter(
mock(NotificationPresenter.class),
mock(HeadsUpManager.class),
@@ -659,8 +661,9 @@ public class BubbleControllerTest extends SysuiTestCase {
NotificationInterruptionStateProvider {
TestableNotificationInterruptionStateProvider(Context context,
- NotificationFilter filter, StatusBarStateController controller) {
- super(context, filter, controller);
+ NotificationFilter filter, StatusBarStateController controller,
+ BatteryController batteryController) {
+ super(context, filter, controller, batteryController);
mUseHeadsUp = true;
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/dock/DockManagerFake.java b/packages/SystemUI/tests/src/com/android/systemui/dock/DockManagerFake.java
index 839b5e4472c6..fd48d34ebea6 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/dock/DockManagerFake.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/dock/DockManagerFake.java
@@ -21,6 +21,7 @@ package com.android.systemui.dock;
*/
public class DockManagerFake implements DockManager {
DockEventListener mCallback;
+ AlignmentStateListener mAlignmentListener;
@Override
public void addListener(DockEventListener callback) {
@@ -33,6 +34,16 @@ public class DockManagerFake implements DockManager {
}
@Override
+ public void addAlignmentStateListener(AlignmentStateListener listener) {
+ mAlignmentListener = listener;
+ }
+
+ @Override
+ public void removeAlignmentStateListener(AlignmentStateListener listener) {
+ mAlignmentListener = listener;
+ }
+
+ @Override
public boolean isDocked() {
return false;
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeMachineTest.java b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeMachineTest.java
index 1e18e51bc079..1eb75aaacbfb 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeMachineTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeMachineTest.java
@@ -46,6 +46,7 @@ import androidx.test.filters.SmallTest;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.keyguard.WakefulnessLifecycle;
+import com.android.systemui.statusbar.policy.BatteryController;
import com.android.systemui.util.wakelock.WakeLockFake;
import org.junit.Before;
@@ -76,8 +77,8 @@ public class DozeMachineTest extends SysuiTestCase {
mConfigMock = mock(AmbientDisplayConfiguration.class);
mPartMock = mock(DozeMachine.Part.class);
- mMachine = new DozeMachine(mServiceFake, mConfigMock, mWakeLockFake, mWakefulnessLifecycle);
-
+ mMachine = new DozeMachine(mServiceFake, mConfigMock, mWakeLockFake,
+ mWakefulnessLifecycle, mock(BatteryController.class));
mMachine.setParts(new DozeMachine.Part[]{mPartMock});
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/screenshot/ScreenshotNotificationSmartActionsTest.java b/packages/SystemUI/tests/src/com/android/systemui/screenshot/ScreenshotNotificationSmartActionsTest.java
new file mode 100644
index 000000000000..d46d7a271e0a
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/screenshot/ScreenshotNotificationSmartActionsTest.java
@@ -0,0 +1,211 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.screenshot;
+
+import static android.content.Context.NOTIFICATION_SERVICE;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyLong;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.doThrow;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.app.Notification;
+import android.app.NotificationManager;
+import android.content.Intent;
+import android.graphics.Bitmap;
+import android.net.Uri;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.Looper;
+import android.testing.AndroidTestingRunner;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.systemui.SystemUIFactory;
+import com.android.systemui.SysuiTestCase;
+import com.android.systemui.util.NotificationChannels;
+
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.Collections;
+import java.util.List;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * Tests for exception handling and bitmap configuration in adding smart actions to Screenshot
+ * Notification.
+ */
+@SmallTest
+@RunWith(AndroidTestingRunner.class)
+public class ScreenshotNotificationSmartActionsTest extends SysuiTestCase {
+ private ScreenshotNotificationSmartActionsProvider mSmartActionsProvider;
+ private Handler mHandler;
+
+ @Before
+ public void setup() {
+ mSmartActionsProvider = mock(
+ ScreenshotNotificationSmartActionsProvider.class);
+ mHandler = mock(Handler.class);
+ }
+
+ // Tests any exception thrown in getting smart actions future does not affect regular
+ // screenshot flow.
+ @Test
+ public void testExceptionHandlingInGetSmartActionsFuture()
+ throws Exception {
+ Bitmap bitmap = mock(Bitmap.class);
+ when(bitmap.getConfig()).thenReturn(Bitmap.Config.HARDWARE);
+ ScreenshotNotificationSmartActionsProvider smartActionsProvider = mock(
+ ScreenshotNotificationSmartActionsProvider.class);
+ when(smartActionsProvider.getActions(any(), any(), any(),
+ eq(false))).thenThrow(RuntimeException.class);
+ CompletableFuture<List<Notification.Action>> smartActionsFuture =
+ GlobalScreenshot.getSmartActionsFuture("", bitmap,
+ smartActionsProvider, true, false);
+ Assert.assertNotNull(smartActionsFuture);
+ List<Notification.Action> smartActions = smartActionsFuture.get(5, TimeUnit.MILLISECONDS);
+ Assert.assertEquals(Collections.emptyList(), smartActions);
+ }
+
+ // Tests any exception thrown in waiting for smart actions future to complete does
+ // not affect regular screenshot flow.
+ @Test
+ public void testExceptionHandlingInGetSmartActions()
+ throws Exception {
+ CompletableFuture<List<Notification.Action>> smartActionsFuture = mock(
+ CompletableFuture.class);
+ int timeoutMs = 1000;
+ when(smartActionsFuture.get(timeoutMs, TimeUnit.MILLISECONDS)).thenThrow(
+ RuntimeException.class);
+ List<Notification.Action> actions = GlobalScreenshot.getSmartActions(
+ "", smartActionsFuture, timeoutMs, mSmartActionsProvider);
+ Assert.assertEquals(Collections.emptyList(), actions);
+ }
+
+ // Tests any exception thrown in notifying feedback does not affect regular screenshot flow.
+ @Test
+ public void testExceptionHandlingInNotifyingFeedback() {
+ doThrow(RuntimeException.class).when(mSmartActionsProvider).notifyOp(any(), any(), any(),
+ anyLong());
+ GlobalScreenshot.notifyScreenshotOp(null, mSmartActionsProvider, null, null, -1);
+ }
+
+ // Tests for a non-hardware bitmap, ScreenshotNotificationSmartActionsProvider is never invoked
+ // and a completed future is returned.
+ @Test
+ public void testUnsupportedBitmapConfiguration()
+ throws Exception {
+ Bitmap bitmap = mock(Bitmap.class);
+ when(bitmap.getConfig()).thenReturn(Bitmap.Config.RGB_565);
+ CompletableFuture<List<Notification.Action>> smartActionsFuture =
+ GlobalScreenshot.getSmartActionsFuture("", bitmap,
+ mSmartActionsProvider, true, true);
+ verify(mSmartActionsProvider, never()).getActions(any(), any(), any(),
+ eq(false));
+ Assert.assertNotNull(smartActionsFuture);
+ List<Notification.Action> smartActions = smartActionsFuture.get(5, TimeUnit.MILLISECONDS);
+ Assert.assertEquals(Collections.emptyList(), smartActions);
+ }
+
+ // Tests for a hardware bitmap, ScreenshotNotificationSmartActionsProvider is invoked once.
+ @Test
+ public void testScreenshotNotificationSmartActionsProviderInvokedOnce() {
+ Bitmap bitmap = mock(Bitmap.class);
+ when(bitmap.getConfig()).thenReturn(Bitmap.Config.HARDWARE);
+ GlobalScreenshot.getSmartActionsFuture("", bitmap, mSmartActionsProvider,
+ true, true);
+ verify(mSmartActionsProvider, times(1))
+ .getActions(any(), any(), any(), eq(true));
+ }
+
+ // Tests for a hardware bitmap, a completed future is returned.
+ @Test
+ public void testSupportedBitmapConfiguration()
+ throws Exception {
+ Bitmap bitmap = mock(Bitmap.class);
+ when(bitmap.getConfig()).thenReturn(Bitmap.Config.HARDWARE);
+ ScreenshotNotificationSmartActionsProvider actionsProvider =
+ SystemUIFactory.getInstance().createScreenshotNotificationSmartActionsProvider(
+ mContext, null, mHandler);
+ CompletableFuture<List<Notification.Action>> smartActionsFuture =
+ GlobalScreenshot.getSmartActionsFuture("", bitmap,
+ actionsProvider,
+ true, true);
+ Assert.assertNotNull(smartActionsFuture);
+ List<Notification.Action> smartActions = smartActionsFuture.get(5, TimeUnit.MILLISECONDS);
+ Assert.assertEquals(smartActions.size(), 0);
+ }
+
+ // Tests for notification action extras.
+ @Test
+ public void testNotificationActionExtras() {
+ if (Looper.myLooper() == null) {
+ Looper.prepare();
+ }
+ NotificationManager notificationManager =
+ (NotificationManager) mContext.getSystemService(NOTIFICATION_SERVICE);
+ SaveImageInBackgroundData data = new SaveImageInBackgroundData();
+ data.context = mContext;
+ data.image = Bitmap.createBitmap(100, 100, Bitmap.Config.ARGB_8888);
+ data.iconSize = 10;
+ data.finisher = null;
+ data.previewWidth = 10;
+ data.previewheight = 10;
+ SaveImageInBackgroundTask task = new SaveImageInBackgroundTask(mContext, data,
+ notificationManager);
+ Uri uri = Uri.parse("Screenshot_123.png");
+ Notification.Builder notificationBuilder = new Notification.Builder(mContext,
+ NotificationChannels.SCREENSHOTS_HEADSUP);
+ task.populateNotificationActions(mContext, mContext.getResources(),
+ uri,
+ CompletableFuture.completedFuture(Collections.emptyList()), notificationBuilder);
+
+ Notification notification = notificationBuilder.build();
+ Assert.assertEquals(notification.actions.length, 3);
+ boolean isShareFound = false;
+ boolean isEditFound = false;
+ boolean isDeleteFound = false;
+ for (Notification.Action action : notification.actions) {
+ Intent intent = action.actionIntent.getIntent();
+ Intent actionIntent = intent.getParcelableExtra(GlobalScreenshot.EXTRA_ACTION_INTENT);
+ Assert.assertNotNull(intent);
+ Bundle bundle = intent.getExtras();
+ Assert.assertTrue(bundle.containsKey(GlobalScreenshot.EXTRA_ID));
+ Assert.assertTrue(bundle.containsKey(GlobalScreenshot.EXTRA_SMART_ACTIONS_ENABLED));
+ if (uri.toString().equals(bundle.getString(GlobalScreenshot.SCREENSHOT_URI_ID))) {
+ isDeleteFound = true;
+ } else if (Intent.ACTION_EDIT.equals(actionIntent.getAction())) {
+ isEditFound = true;
+ } else if (Intent.ACTION_CHOOSER.equals(actionIntent.getAction())) {
+ isShareFound = true;
+ }
+ }
+
+ Assert.assertTrue(isEditFound);
+ Assert.assertTrue(isShareFound);
+ Assert.assertTrue(isDeleteFound);
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyguardIndicationControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyguardIndicationControllerTest.java
index daee55bd3d61..2fe51d35c490 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyguardIndicationControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyguardIndicationControllerTest.java
@@ -35,8 +35,11 @@ import android.app.admin.DevicePolicyManager;
import android.app.trust.TrustManager;
import android.content.Context;
import android.graphics.Color;
+import android.hardware.biometrics.BiometricSourceType;
+import android.hardware.face.FaceManager;
import android.hardware.fingerprint.FingerprintManager;
import android.os.Looper;
+import android.os.UserManager;
import android.view.View;
import android.view.ViewGroup;
@@ -47,12 +50,15 @@ import androidx.test.runner.AndroidJUnit4;
import com.android.internal.widget.LockPatternUtils;
import com.android.keyguard.KeyguardUpdateMonitor;
import com.android.keyguard.KeyguardUpdateMonitorCallback;
+import com.android.settingslib.Utils;
import com.android.systemui.R;
import com.android.systemui.SysuiTestCase;
+import com.android.systemui.dock.DockManager;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.statusbar.phone.KeyguardIndicationTextView;
import com.android.systemui.statusbar.phone.LockIcon;
import com.android.systemui.statusbar.phone.ShadeController;
+import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager;
import com.android.systemui.statusbar.phone.UnlockMethodCache;
import com.android.systemui.statusbar.policy.AccessibilityController;
import com.android.systemui.util.wakelock.WakeLockFake;
@@ -61,6 +67,7 @@ import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
+import org.mockito.Captor;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
@@ -92,6 +99,14 @@ public class KeyguardIndicationControllerTest extends SysuiTestCase {
private StatusBarStateController mStatusBarStateController;
@Mock
private KeyguardUpdateMonitor mKeyguardUpdateMonitor;
+ @Mock
+ private StatusBarKeyguardViewManager mStatusBarKeyguardViewManager;
+ @Mock
+ private UserManager mUserManager;
+ @Mock
+ private DockManager mDockManager;
+ @Captor
+ private ArgumentCaptor<DockManager.AlignmentStateListener> mAlignmentListener;
private KeyguardIndicationTextView mTextView;
private KeyguardIndicationController mController;
@@ -105,14 +120,18 @@ public class KeyguardIndicationControllerTest extends SysuiTestCase {
mTextView = new KeyguardIndicationTextView(mContext);
mContext.addMockSystemService(Context.DEVICE_POLICY_SERVICE, mDevicePolicyManager);
+ mContext.addMockSystemService(UserManager.class, mUserManager);
mContext.addMockSystemService(Context.TRUST_SERVICE, mock(TrustManager.class));
mContext.addMockSystemService(Context.FINGERPRINT_SERVICE, mock(FingerprintManager.class));
mDisclosureWithOrganization = mContext.getString(R.string.do_disclosure_with_name,
ORGANIZATION_NAME);
+ when(mKeyguardUpdateMonitor.isUnlockingWithBiometricAllowed()).thenReturn(true);
+ when(mKeyguardUpdateMonitor.isScreenOn()).thenReturn(true);
when(mIndicationArea.findViewById(R.id.keyguard_indication_enterprise_disclosure))
.thenReturn(mDisclosure);
when(mIndicationArea.findViewById(R.id.keyguard_indication_text)).thenReturn(mTextView);
+ when(mUserManager.isUserUnlocked(anyInt())).thenReturn(true);
mWakeLock = new WakeLockFake();
}
@@ -123,7 +142,9 @@ public class KeyguardIndicationControllerTest extends SysuiTestCase {
}
mController = new KeyguardIndicationController(mContext, mIndicationArea, mLockIcon,
mLockPatternUtils, mWakeLock, mShadeController, mAccessibilityController,
- mUnlockMethodCache, mStatusBarStateController, mKeyguardUpdateMonitor);
+ mUnlockMethodCache, mStatusBarStateController, mKeyguardUpdateMonitor,
+ mDockManager);
+ mController.setStatusBarKeyguardViewManager(mStatusBarKeyguardViewManager);
}
@Test
@@ -193,6 +214,74 @@ public class KeyguardIndicationControllerTest extends SysuiTestCase {
}
@Test
+ public void createController_addsAlignmentListener() {
+ createController();
+
+ verify(mDockManager).addAlignmentStateListener(
+ any(DockManager.AlignmentStateListener.class));
+ }
+
+ @Test
+ public void onAlignmentStateChanged_showsSlowChargingIndication() {
+ createController();
+ verify(mDockManager).addAlignmentStateListener(mAlignmentListener.capture());
+ mController.setVisible(true);
+
+ mAlignmentListener.getValue().onAlignmentStateChanged(
+ DockManager.ALIGN_STATE_POOR);
+
+ assertThat(mTextView.getText()).isEqualTo(
+ mContext.getResources().getString(R.string.dock_alignment_slow_charging));
+ assertThat(mTextView.getCurrentTextColor()).isEqualTo(
+ Utils.getColorError(mContext).getDefaultColor());
+ }
+
+ @Test
+ public void onAlignmentStateChanged_showsNotChargingIndication() {
+ createController();
+ verify(mDockManager).addAlignmentStateListener(mAlignmentListener.capture());
+ mController.setVisible(true);
+
+ mAlignmentListener.getValue().onAlignmentStateChanged(DockManager.ALIGN_STATE_TERRIBLE);
+
+ assertThat(mTextView.getText()).isEqualTo(
+ mContext.getResources().getString(R.string.dock_alignment_not_charging));
+ assertThat(mTextView.getCurrentTextColor()).isEqualTo(
+ Utils.getColorError(mContext).getDefaultColor());
+ }
+
+ @Test
+ public void onAlignmentStateChanged_whileDozing_showsSlowChargingIndication() {
+ createController();
+ verify(mDockManager).addAlignmentStateListener(mAlignmentListener.capture());
+ mController.setVisible(true);
+ mController.setDozing(true);
+
+ mAlignmentListener.getValue().onAlignmentStateChanged(
+ DockManager.ALIGN_STATE_POOR);
+
+ assertThat(mTextView.getText()).isEqualTo(
+ mContext.getResources().getString(R.string.dock_alignment_slow_charging));
+ assertThat(mTextView.getCurrentTextColor()).isEqualTo(
+ Utils.getColorError(mContext).getDefaultColor());
+ }
+
+ @Test
+ public void onAlignmentStateChanged_whileDozing_showsNotChargingIndication() {
+ createController();
+ verify(mDockManager).addAlignmentStateListener(mAlignmentListener.capture());
+ mController.setVisible(true);
+ mController.setDozing(true);
+
+ mAlignmentListener.getValue().onAlignmentStateChanged(DockManager.ALIGN_STATE_TERRIBLE);
+
+ assertThat(mTextView.getText()).isEqualTo(
+ mContext.getResources().getString(R.string.dock_alignment_not_charging));
+ assertThat(mTextView.getCurrentTextColor()).isEqualTo(
+ Utils.getColorError(mContext).getDefaultColor());
+ }
+
+ @Test
public void transientIndication_holdsWakeLock_whenDozing() {
createController();
@@ -245,6 +334,49 @@ public class KeyguardIndicationControllerTest extends SysuiTestCase {
}
@Test
+ public void transientIndication_visibleWhenDozing_unlessSwipeUp_fromHelp() {
+ createController();
+ String message = "A message";
+
+ mController.setVisible(true);
+ mController.getKeyguardCallback().onBiometricHelp(
+ KeyguardUpdateMonitor.BIOMETRIC_HELP_FACE_NOT_RECOGNIZED, message,
+ BiometricSourceType.FACE);
+ assertThat(mTextView.getText()).isEqualTo(message);
+ mController.setDozing(true);
+
+ assertThat(mTextView.getText()).isNotEqualTo(message);
+ }
+
+ @Test
+ public void transientIndication_visibleWhenDozing_unlessSwipeUp_fromError() {
+ createController();
+ String message = mContext.getString(R.string.keyguard_unlock);
+
+ mController.setVisible(true);
+ mController.getKeyguardCallback().onBiometricError(FaceManager.FACE_ERROR_TIMEOUT,
+ "A message", BiometricSourceType.FACE);
+
+ assertThat(mTextView.getText()).isEqualTo(message);
+ mController.setDozing(true);
+
+ assertThat(mTextView.getText()).isNotEqualTo(message);
+ }
+
+ @Test
+ public void transientIndication_swipeUpToRetry() {
+ createController();
+ String message = mContext.getString(R.string.keyguard_retry);
+ when(mStatusBarKeyguardViewManager.isBouncerShowing()).thenReturn(true);
+
+ mController.setVisible(true);
+ mController.getKeyguardCallback().onBiometricError(FaceManager.FACE_ERROR_TIMEOUT,
+ "A message", BiometricSourceType.FACE);
+
+ verify(mStatusBarKeyguardViewManager).showBouncerMessage(eq(message), any());
+ }
+
+ @Test
public void lockIcon_click() {
createController();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationInterruptionStateProviderTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationInterruptionStateProviderTest.java
index a66cf843bbc3..0db1f681a7ac 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationInterruptionStateProviderTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationInterruptionStateProviderTest.java
@@ -53,6 +53,7 @@ import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.statusbar.notification.NotificationFilter;
import com.android.systemui.statusbar.notification.NotificationInterruptionStateProvider;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
+import com.android.systemui.statusbar.policy.BatteryController;
import com.android.systemui.statusbar.policy.HeadsUpManager;
import org.junit.Before;
@@ -85,6 +86,8 @@ public class NotificationInterruptionStateProviderTest extends SysuiTestCase {
HeadsUpManager mHeadsUpManager;
@Mock
NotificationInterruptionStateProvider.HeadsUpSuppressor mHeadsUpSuppressor;
+ @Mock
+ BatteryController mBatteryController;
private NotificationInterruptionStateProvider mNotifInterruptionStateProvider;
@@ -98,7 +101,8 @@ public class NotificationInterruptionStateProviderTest extends SysuiTestCase {
mDreamManager,
mAmbientDisplayConfiguration,
mNotificationFilter,
- mStatusBarStateController);
+ mStatusBarStateController,
+ mBatteryController);
mNotifInterruptionStateProvider.setUpWithPresenter(
mPresenter,
@@ -573,17 +577,17 @@ public class NotificationInterruptionStateProviderTest extends SysuiTestCase {
/**
* Testable class overriding constructor.
*/
- public class TestableNotificationInterruptionStateProvider extends
+ public static class TestableNotificationInterruptionStateProvider extends
NotificationInterruptionStateProvider {
TestableNotificationInterruptionStateProvider(Context context,
PowerManager powerManager, IDreamManager dreamManager,
AmbientDisplayConfiguration ambientDisplayConfiguration,
NotificationFilter notificationFilter,
- StatusBarStateController statusBarStateController) {
+ StatusBarStateController statusBarStateController,
+ BatteryController batteryController) {
super(context, powerManager, dreamManager, ambientDisplayConfiguration,
- notificationFilter,
- statusBarStateController);
+ notificationFilter, batteryController, statusBarStateController);
}
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationEntryManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationEntryManagerTest.java
index 2ca1b0611cd6..b07ac5ff7ea8 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationEntryManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationEntryManagerTest.java
@@ -182,7 +182,7 @@ public class NotificationEntryManagerTest extends SysuiTestCase {
0,
NotificationManager.IMPORTANCE_DEFAULT,
null, null,
- null, null, null, true, sentiment, false, -1, false, null, null, false);
+ null, null, null, true, sentiment, false, -1, false, null, null, false, false);
return true;
}).when(mRankingMap).getRanking(eq(key), any(NotificationListenerService.Ranking.class));
}
@@ -201,7 +201,7 @@ public class NotificationEntryManagerTest extends SysuiTestCase {
null, null,
null, null, null, true,
NotificationListenerService.Ranking.USER_SENTIMENT_NEUTRAL, false, -1,
- false, smartActions, null, false);
+ false, smartActions, null, false, false);
return true;
}).when(mRankingMap).getRanking(eq(key), any(NotificationListenerService.Ranking.class));
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotificationDataTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotificationDataTest.java
index e2d8e5698daf..cf0c7185f040 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotificationDataTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotificationDataTest.java
@@ -623,6 +623,7 @@ public class NotificationDataTest extends SysuiTestCase {
public static final String OVERRIDE_SMART_ACTIONS = "sa";
public static final String OVERRIDE_SMART_REPLIES = "sr";
public static final String OVERRIDE_BUBBLE = "cb";
+ public static final String OVERRIDE_VISUALLY_INTERRUPTIVE = "vi";
public Map<String, Bundle> rankingOverrides = new HashMap<>();
@@ -683,7 +684,9 @@ public class NotificationDataTest extends SysuiTestCase {
overrides.containsKey(OVERRIDE_SMART_REPLIES)
? overrides.getCharSequenceArrayList(OVERRIDE_SMART_REPLIES)
: currentReplies,
- overrides.getBoolean(OVERRIDE_BUBBLE, outRanking.canBubble()));
+ overrides.getBoolean(OVERRIDE_BUBBLE, outRanking.canBubble()),
+ overrides.getBoolean(OVERRIDE_VISUALLY_INTERRUPTIVE,
+ outRanking.visuallyInterruptive()));
}
return true;
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java
index 63f653b0b303..0da0e7647707 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java
@@ -221,6 +221,31 @@ public class StatusBarKeyguardViewManagerTest extends SysuiTestCase {
verify(mStatusBar, never()).animateKeyguardUnoccluding();
}
+ @Test
+ public void testHiding_cancelsGoneRunnable() {
+ OnDismissAction action = mock(OnDismissAction.class);
+ Runnable cancelAction = mock(Runnable.class);
+ mStatusBarKeyguardViewManager.dismissWithAction(action, cancelAction,
+ true /* afterKeyguardGone */);
+
+ mStatusBarKeyguardViewManager.hideBouncer(true);
+ mStatusBarKeyguardViewManager.hide(0, 30);
+ verify(action, never()).onDismiss();
+ verify(cancelAction).run();
+ }
+
+ @Test
+ public void testHiding_doesntCancelWhenShowing() {
+ OnDismissAction action = mock(OnDismissAction.class);
+ Runnable cancelAction = mock(Runnable.class);
+ mStatusBarKeyguardViewManager.dismissWithAction(action, cancelAction,
+ true /* afterKeyguardGone */);
+
+ mStatusBarKeyguardViewManager.hide(0, 30);
+ verify(action).onDismiss();
+ verify(cancelAction, never()).run();
+ }
+
private class TestableStatusBarKeyguardViewManager extends StatusBarKeyguardViewManager {
public TestableStatusBarKeyguardViewManager(Context context,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java
index 178ff22eede2..02215a984203 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java
@@ -110,6 +110,7 @@ import com.android.systemui.statusbar.notification.logging.NotificationLogger;
import com.android.systemui.statusbar.notification.row.NotificationGutsManager;
import com.android.systemui.statusbar.notification.stack.NotificationListContainer;
import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout;
+import com.android.systemui.statusbar.policy.BatteryController;
import com.android.systemui.statusbar.policy.DeviceProvisionedController;
import com.android.systemui.statusbar.policy.KeyguardMonitor;
import com.android.systemui.statusbar.policy.UserSwitcherController;
@@ -156,6 +157,7 @@ public class StatusBarTest extends SysuiTestCase {
@Mock private NotificationRemoteInputManager mRemoteInputManager;
@Mock private RemoteInputController mRemoteInputController;
@Mock private StatusBarStateControllerImpl mStatusBarStateController;
+ @Mock private BatteryController mBatteryController;
@Mock private DeviceProvisionedController mDeviceProvisionedController;
@Mock private StatusBarNotificationPresenter mNotificationPresenter;
@Mock
@@ -209,7 +211,7 @@ public class StatusBarTest extends SysuiTestCase {
mNotificationInterruptionStateProvider =
new TestableNotificationInterruptionStateProvider(mContext, mPowerManager,
mDreamManager, mAmbientDisplayConfiguration, mNotificationFilter,
- mStatusBarStateController);
+ mStatusBarStateController, mBatteryController);
mDependency.injectTestDependency(NotificationInterruptionStateProvider.class,
mNotificationInterruptionStateProvider);
mDependency.injectMockDependency(NavigationBarController.class);
@@ -873,9 +875,10 @@ public class StatusBarTest extends SysuiTestCase {
IDreamManager dreamManager,
AmbientDisplayConfiguration ambientDisplayConfiguration,
NotificationFilter filter,
- StatusBarStateController controller) {
+ StatusBarStateController controller,
+ BatteryController batteryController) {
super(context, powerManager, dreamManager, ambientDisplayConfiguration, filter,
- controller);
+ batteryController, controller);
mUseHeadsUp = true;
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeBatteryController.java b/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeBatteryController.java
index a843cca498a0..df76f01494f3 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeBatteryController.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeBatteryController.java
@@ -48,4 +48,9 @@ public class FakeBatteryController extends BaseLeakChecker<BatteryStateChangeCal
public boolean isPowerSave() {
return false;
}
+
+ @Override
+ public boolean isAodPowerSave() {
+ return false;
+ }
}
diff --git a/services/contentsuggestions/java/com/android/server/contentsuggestions/ContentSuggestionsManagerService.java b/services/contentsuggestions/java/com/android/server/contentsuggestions/ContentSuggestionsManagerService.java
index ecea251cc1ac..9cdb58d8c019 100644
--- a/services/contentsuggestions/java/com/android/server/contentsuggestions/ContentSuggestionsManagerService.java
+++ b/services/contentsuggestions/java/com/android/server/contentsuggestions/ContentSuggestionsManagerService.java
@@ -16,7 +16,6 @@
package com.android.server.contentsuggestions;
-import static android.Manifest.permission.BIND_CONTENT_SUGGESTIONS_SERVICE;
import static android.Manifest.permission.MANAGE_CONTENT_SUGGESTIONS;
import static android.content.pm.PackageManager.PERMISSION_GRANTED;
@@ -96,7 +95,7 @@ public class ContentSuggestionsManagerService extends
private void enforceCaller(int userId, String func) {
Context ctx = getContext();
- if (ctx.checkCallingPermission(BIND_CONTENT_SUGGESTIONS_SERVICE) == PERMISSION_GRANTED
+ if (ctx.checkCallingPermission(MANAGE_CONTENT_SUGGESTIONS) == PERMISSION_GRANTED
|| mServiceNameResolver.isTemporary(userId)
|| mActivityTaskManagerInternal.isCallerRecents(Binder.getCallingUid())) {
return;
diff --git a/services/contentsuggestions/java/com/android/server/contentsuggestions/ContentSuggestionsPerUserService.java b/services/contentsuggestions/java/com/android/server/contentsuggestions/ContentSuggestionsPerUserService.java
index 06d9395cd7d6..7828050223f4 100644
--- a/services/contentsuggestions/java/com/android/server/contentsuggestions/ContentSuggestionsPerUserService.java
+++ b/services/contentsuggestions/java/com/android/server/contentsuggestions/ContentSuggestionsPerUserService.java
@@ -22,6 +22,7 @@ import android.annotation.Nullable;
import android.app.ActivityManager;
import android.app.AppGlobals;
import android.app.contentsuggestions.ClassificationsRequest;
+import android.app.contentsuggestions.ContentSuggestionsManager;
import android.app.contentsuggestions.IClassificationsCallback;
import android.app.contentsuggestions.ISelectionsCallback;
import android.app.contentsuggestions.SelectionsRequest;
@@ -97,15 +98,19 @@ public final class ContentSuggestionsPerUserService extends
void provideContextImageLocked(int taskId, @NonNull Bundle imageContextRequestExtras) {
RemoteContentSuggestionsService service = ensureRemoteServiceLocked();
if (service != null) {
- ActivityManager.TaskSnapshot snapshot =
- mActivityTaskManagerInternal.getTaskSnapshotNoRestore(taskId, false);
GraphicBuffer snapshotBuffer = null;
int colorSpaceId = 0;
- if (snapshot != null) {
- snapshotBuffer = snapshot.getSnapshot();
- ColorSpace colorSpace = snapshot.getColorSpace();
- if (colorSpace != null) {
- colorSpaceId = colorSpace.getId();
+
+ // Skip taking TaskSnapshot when bitmap is provided.
+ if (!imageContextRequestExtras.containsKey(ContentSuggestionsManager.EXTRA_BITMAP)) {
+ ActivityManager.TaskSnapshot snapshot =
+ mActivityTaskManagerInternal.getTaskSnapshotNoRestore(taskId, false);
+ if (snapshot != null) {
+ snapshotBuffer = snapshot.getSnapshot();
+ ColorSpace colorSpace = snapshot.getColorSpace();
+ if (colorSpace != null) {
+ colorSpaceId = colorSpace.getId();
+ }
}
}
diff --git a/services/core/java/com/android/server/AlarmManagerInternal.java b/services/core/java/com/android/server/AlarmManagerInternal.java
index 5b0de5e2aae0..0a735029eead 100644
--- a/services/core/java/com/android/server/AlarmManagerInternal.java
+++ b/services/core/java/com/android/server/AlarmManagerInternal.java
@@ -16,6 +16,8 @@
package com.android.server;
+import android.app.PendingIntent;
+
public interface AlarmManagerInternal {
// Some other components in the system server need to know about
// broadcast alarms currently in flight
@@ -30,4 +32,10 @@ public interface AlarmManagerInternal {
boolean isIdling();
public void removeAlarmsForUid(int uid);
public void registerInFlightListener(InFlightListener callback);
+
+ /**
+ * Removes any alarm with the given pending intent with equality determined using
+ * {@link android.app.PendingIntent#equals(java.lang.Object) PendingIntent.equals}
+ */
+ void remove(PendingIntent rec);
}
diff --git a/services/core/java/com/android/server/AlarmManagerService.java b/services/core/java/com/android/server/AlarmManagerService.java
index d16244167c62..386b49ebaa11 100644
--- a/services/core/java/com/android/server/AlarmManagerService.java
+++ b/services/core/java/com/android/server/AlarmManagerService.java
@@ -210,7 +210,6 @@ class AlarmManagerService extends SystemService {
IAlarmListener mTimeTickTrigger;
PendingIntent mDateChangeSender;
Random mRandom;
- PendingIntent.CancelListener mOperationCancelListener;
boolean mInteractive = true;
long mNonInteractiveStartTime;
long mNonInteractiveTime;
@@ -1498,7 +1497,6 @@ class AlarmManagerService extends SystemService {
synchronized (mLock) {
mHandler = new AlarmHandler();
- mOperationCancelListener = (intent) -> removeImpl(intent, null);
mConstants = new Constants(mHandler);
mAppWakeupHistory = new AppWakeupHistory(Constants.DEFAULT_APP_STANDBY_WINDOW);
@@ -1750,9 +1748,6 @@ class AlarmManagerService extends SystemService {
} else {
maxElapsed = triggerElapsed + windowLength;
}
- if (operation != null) {
- operation.registerCancelListener(mOperationCancelListener);
- }
synchronized (mLock) {
if (DEBUG_BATCH) {
Slog.v(TAG, "set(" + operation + ") : type=" + type
@@ -1765,8 +1760,6 @@ class AlarmManagerService extends SystemService {
"Maximum limit of concurrent alarms " + mConstants.MAX_ALARMS_PER_UID
+ " reached for uid: " + UserHandle.formatUid(callingUid)
+ ", callingPackage: " + callingPackage;
- mHandler.obtainMessage(AlarmHandler.UNREGISTER_CANCEL_LISTENER,
- operation).sendToTarget();
Slog.w(TAG, errorMsg);
throw new IllegalStateException(errorMsg);
}
@@ -1787,8 +1780,6 @@ class AlarmManagerService extends SystemService {
if (ActivityManager.getService().isAppStartModeDisabled(callingUid, callingPackage)) {
Slog.w(TAG, "Not setting alarm from " + callingUid + ":" + a
+ " -- package not allowed to start");
- mHandler.obtainMessage(AlarmHandler.UNREGISTER_CANCEL_LISTENER,
- operation).sendToTarget();
return;
}
} catch (RemoteException e) {
@@ -2042,6 +2033,11 @@ class AlarmManagerService extends SystemService {
}
@Override
+ public void remove(PendingIntent pi) {
+ mHandler.obtainMessage(AlarmHandler.REMOVE_FOR_CANCELED, pi).sendToTarget();
+ }
+
+ @Override
public void registerInFlightListener(InFlightListener callback) {
synchronized (mLock) {
mInFlightListeners.add(callback);
@@ -2146,8 +2142,6 @@ class AlarmManagerService extends SystemService {
synchronized (mLock) {
removeLocked(operation, listener);
}
- mHandler.obtainMessage(AlarmHandler.UNREGISTER_CANCEL_LISTENER,
- operation).sendToTarget();
}
@Override
@@ -4151,7 +4145,7 @@ class AlarmManagerService extends SystemService {
public static final int APP_STANDBY_BUCKET_CHANGED = 5;
public static final int APP_STANDBY_PAROLE_CHANGED = 6;
public static final int REMOVE_FOR_STOPPED = 7;
- public static final int UNREGISTER_CANCEL_LISTENER = 8;
+ public static final int REMOVE_FOR_CANCELED = 8;
AlarmHandler() {
super(Looper.myLooper());
@@ -4234,10 +4228,10 @@ class AlarmManagerService extends SystemService {
}
break;
- case UNREGISTER_CANCEL_LISTENER:
- final PendingIntent pi = (PendingIntent) msg.obj;
- if (pi != null) {
- pi.unregisterCancelListener(mOperationCancelListener);
+ case REMOVE_FOR_CANCELED:
+ final PendingIntent operation = (PendingIntent) msg.obj;
+ synchronized (mLock) {
+ removeLocked(operation, null);
}
break;
@@ -4696,11 +4690,6 @@ class AlarmManagerService extends SystemService {
Intent.EXTRA_ALARM_COUNT, alarm.count),
mDeliveryTracker, mHandler, null,
allowWhileIdle ? mIdleOptions : null);
- if (alarm.repeatInterval == 0) {
- // Keep the listener for repeating alarms until they get cancelled
- mHandler.obtainMessage(AlarmHandler.UNREGISTER_CANCEL_LISTENER,
- alarm.operation).sendToTarget();
- }
} catch (PendingIntent.CanceledException e) {
if (alarm.repeatInterval > 0) {
// This IntentSender is no longer valid, but this
diff --git a/services/core/java/com/android/server/BinderCallsStatsService.java b/services/core/java/com/android/server/BinderCallsStatsService.java
index e51025943df4..f2ce444a8b90 100644
--- a/services/core/java/com/android/server/BinderCallsStatsService.java
+++ b/services/core/java/com/android/server/BinderCallsStatsService.java
@@ -19,6 +19,7 @@ package com.android.server;
import static android.content.pm.PackageManager.MATCH_DIRECT_BOOT_AWARE;
import static android.content.pm.PackageManager.MATCH_DIRECT_BOOT_UNAWARE;
+import android.app.ActivityThread;
import android.content.Context;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
@@ -40,6 +41,7 @@ import com.android.internal.os.BackgroundThread;
import com.android.internal.os.BinderCallsStats;
import com.android.internal.os.BinderInternal;
import com.android.internal.os.CachedDeviceState;
+import com.android.internal.util.DumpUtils;
import java.io.FileDescriptor;
import java.io.PrintWriter;
@@ -49,6 +51,7 @@ import java.util.List;
public class BinderCallsStatsService extends Binder {
private static final String TAG = "BinderCallsStatsService";
+ private static final String SERVICE_NAME = "binder_calls_stats";
private static final String PERSIST_SYS_BINDER_CALLS_DETAILED_TRACKING
= "persist.sys.binder_calls_detailed_tracking";
@@ -246,7 +249,7 @@ public class BinderCallsStatsService extends Binder {
mService = new BinderCallsStatsService(
mBinderCallsStats, mWorkSourceProvider);
publishLocalService(Internal.class, new Internal(mBinderCallsStats));
- publishBinderService("binder_calls_stats", mService);
+ publishBinderService(SERVICE_NAME, mService);
boolean detailedTrackingEnabled = SystemProperties.getBoolean(
PERSIST_SYS_BINDER_CALLS_DETAILED_TRACKING, false);
@@ -293,6 +296,11 @@ public class BinderCallsStatsService extends Binder {
@Override
protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+ if (!DumpUtils.checkDumpAndUsageStatsPermission(ActivityThread.currentApplication(),
+ SERVICE_NAME, pw)) {
+ return;
+ }
+
boolean verbose = false;
if (args != null) {
for (final String arg : args) {
diff --git a/services/core/java/com/android/server/DeviceIdleController.java b/services/core/java/com/android/server/DeviceIdleController.java
index 2ab46e65e77f..4aaf0b7b9b36 100644
--- a/services/core/java/com/android/server/DeviceIdleController.java
+++ b/services/core/java/com/android/server/DeviceIdleController.java
@@ -291,6 +291,7 @@ public class DeviceIdleController extends SystemService
private boolean mLightEnabled;
private boolean mDeepEnabled;
private boolean mQuickDozeActivated;
+ private boolean mQuickDozeActivatedWhileIdling;
private boolean mForceIdle;
private boolean mNetworkConnected;
private boolean mScreenOn;
@@ -302,6 +303,10 @@ public class DeviceIdleController extends SystemService
private boolean mHasNetworkLocation;
private Location mLastGenericLocation;
private Location mLastGpsLocation;
+
+ /** Time in the elapsed realtime timebase when this listener last received a motion event. */
+ private long mLastMotionEventElapsed;
+
// Current locked state of the screen
private boolean mScreenLocked;
private int mNumBlockingConstraints = 0;
@@ -547,6 +552,9 @@ public class DeviceIdleController extends SystemService
*/
private ArrayMap<String, Integer> mRemovedFromSystemWhitelistApps = new ArrayMap<>();
+ private final ArraySet<StationaryListener> mStationaryListeners =
+ new ArraySet<>();
+
private static final int EVENT_NULL = 0;
private static final int EVENT_NORMAL = 1;
private static final int EVENT_LIGHT_IDLE = 2;
@@ -605,6 +613,21 @@ public class DeviceIdleController extends SystemService
}
};
+ private final AlarmManager.OnAlarmListener mMotionTimeoutAlarmListener = () -> {
+ synchronized (DeviceIdleController.this) {
+ if (!isStationaryLocked()) {
+ // If the device keeps registering motion, then the alarm should be
+ // rescheduled, so this shouldn't go off until the device is stationary.
+ // This case may happen in a race condition (alarm goes off right before
+ // motion is detected, but handleMotionDetectedLocked is called before
+ // we enter this block).
+ Slog.w(TAG, "motion timeout went off and device isn't stationary");
+ return;
+ }
+ }
+ postStationaryStatusUpdated();
+ };
+
private final AlarmManager.OnAlarmListener mSensingTimeoutAlarmListener
= new AlarmManager.OnAlarmListener() {
@Override
@@ -654,12 +677,70 @@ public class DeviceIdleController extends SystemService
}
};
+ /** Post stationary status only to this listener. */
+ private void postStationaryStatus(StationaryListener listener) {
+ mHandler.obtainMessage(MSG_REPORT_STATIONARY_STATUS, listener).sendToTarget();
+ }
+
+ /** Post stationary status to all registered listeners. */
+ private void postStationaryStatusUpdated() {
+ mHandler.sendEmptyMessage(MSG_REPORT_STATIONARY_STATUS);
+ }
+
+ private boolean isStationaryLocked() {
+ final long now = mInjector.getElapsedRealtime();
+ return mMotionListener.active
+ // Listening for motion for long enough and last motion was long enough ago.
+ && now - Math.max(mMotionListener.activatedTimeElapsed, mLastMotionEventElapsed)
+ >= mConstants.MOTION_INACTIVE_TIMEOUT;
+ }
+
+ @VisibleForTesting
+ void registerStationaryListener(StationaryListener listener) {
+ synchronized (this) {
+ if (!mStationaryListeners.add(listener)) {
+ // Listener already registered.
+ return;
+ }
+ postStationaryStatus(listener);
+ if (mMotionListener.active) {
+ if (!isStationaryLocked() && mStationaryListeners.size() == 1) {
+ // First listener to be registered and the device isn't stationary, so we
+ // need to register the alarm to report the device is stationary.
+ scheduleMotionTimeoutAlarmLocked();
+ }
+ } else {
+ startMonitoringMotionLocked();
+ scheduleMotionTimeoutAlarmLocked();
+ }
+ }
+ }
+
+ private void unregisterStationaryListener(StationaryListener listener) {
+ synchronized (this) {
+ if (mStationaryListeners.remove(listener) && mStationaryListeners.size() == 0
+ // Motion detection is started when transitioning from INACTIVE to IDLE_PENDING
+ // and so doesn't need to be on for ACTIVE or INACTIVE states.
+ // Motion detection isn't needed when idling due to Quick Doze.
+ && (mState == STATE_ACTIVE || mState == STATE_INACTIVE
+ || mQuickDozeActivated)) {
+ maybeStopMonitoringMotionLocked();
+ }
+ }
+ }
+
@VisibleForTesting
final class MotionListener extends TriggerEventListener
implements SensorEventListener {
boolean active = false;
+ /**
+ * Time in the elapsed realtime timebase when this listener was activated. Only valid if
+ * {@link #active} is true.
+ */
+ long activatedTimeElapsed;
+
public boolean isActive() {
return active;
}
@@ -667,7 +748,6 @@ public class DeviceIdleController extends SystemService
@Override
public void onTrigger(TriggerEvent event) {
synchronized (DeviceIdleController.this) {
- active = false;
motionLocked();
}
}
@@ -675,8 +755,6 @@ public class DeviceIdleController extends SystemService
@Override
public void onSensorChanged(SensorEvent event) {
synchronized (DeviceIdleController.this) {
- mSensorManager.unregisterListener(this, mMotionSensor);
- active = false;
motionLocked();
}
}
@@ -694,6 +772,7 @@ public class DeviceIdleController extends SystemService
}
if (success) {
active = true;
+ activatedTimeElapsed = mInjector.getElapsedRealtime();
} else {
Slog.e(TAG, "Unable to register for " + mMotionSensor);
}
@@ -1307,6 +1386,8 @@ public class DeviceIdleController extends SystemService
private static final int MSG_SEND_CONSTRAINT_MONITORING = 10;
private static final int MSG_UPDATE_PRE_IDLE_TIMEOUT_FACTOR = 11;
private static final int MSG_RESET_PRE_IDLE_TIMEOUT_FACTOR = 12;
+ @VisibleForTesting
+ static final int MSG_REPORT_STATIONARY_STATUS = 13;
final class MyHandler extends Handler {
MyHandler(Looper looper) {
@@ -1443,6 +1524,30 @@ public class DeviceIdleController extends SystemService
updatePreIdleFactor();
maybeDoImmediateMaintenance();
} break;
+ case MSG_REPORT_STATIONARY_STATUS: {
+ final StationaryListener newListener = (StationaryListener) msg.obj;
+ final StationaryListener[] listeners;
+ final boolean isStationary;
+ synchronized (DeviceIdleController.this) {
+ isStationary = isStationaryLocked();
+ if (newListener == null) {
+ // Only notify all listeners if we aren't directing to one listener.
+ listeners = mStationaryListeners.toArray(
+ new StationaryListener[mStationaryListeners.size()]);
+ } else {
+ listeners = null;
+ }
+ }
+ if (listeners != null) {
+ for (StationaryListener listener : listeners) {
+ listener.onDeviceStationaryChanged(isStationary);
+ }
+ }
+ if (newListener != null) {
+ newListener.onDeviceStationaryChanged(isStationary);
+ }
+ }
+ break;
}
}
}
@@ -1628,6 +1733,19 @@ public class DeviceIdleController extends SystemService
}
}
+ /**
+ * Listener to be notified when DeviceIdleController determines that the device has
+ * moved or is stationary.
+ */
+ public interface StationaryListener {
+ /**
+ * Called when DeviceIdleController has determined that the device is stationary or moving.
+ *
+ * @param isStationary true if the device is stationary, false otherwise
+ */
+ void onDeviceStationaryChanged(boolean isStationary);
+ }
+
public class LocalService {
public void onConstraintStateChanged(IDeviceIdleConstraint constraint, boolean active) {
synchronized (DeviceIdleController.this) {
@@ -1693,6 +1811,24 @@ public class DeviceIdleController extends SystemService
public int[] getPowerSaveTempWhitelistAppIds() {
return DeviceIdleController.this.getAppIdTempWhitelistInternal();
}
+
+ /**
+ * Registers a listener that will be notified when the system has detected that the device
+ * is
+ * stationary or in motion.
+ */
+ public void registerStationaryListener(StationaryListener listener) {
+ DeviceIdleController.this.registerStationaryListener(listener);
+ }
+
+ /**
+ * Unregisters a registered stationary listener from being notified when the system has
+ * detected
+ * that the device is stationary or in motion.
+ */
+ public void unregisterStationaryListener(StationaryListener listener) {
+ DeviceIdleController.this.unregisterStationaryListener(listener);
+ }
}
static class Injector {
@@ -1734,6 +1870,11 @@ public class DeviceIdleController extends SystemService
return mConstants;
}
+ /** Returns the current elapsed realtime in milliseconds. */
+ long getElapsedRealtime() {
+ return SystemClock.elapsedRealtime();
+ }
+
LocationManager getLocationManager() {
if (mLocationManager == null) {
mLocationManager = mContext.getSystemService(LocationManager.class);
@@ -2601,6 +2742,8 @@ public class DeviceIdleController extends SystemService
void updateQuickDozeFlagLocked(boolean enabled) {
if (DEBUG) Slog.i(TAG, "updateQuickDozeFlagLocked: enabled=" + enabled);
mQuickDozeActivated = enabled;
+ mQuickDozeActivatedWhileIdling =
+ mQuickDozeActivated && (mState == STATE_IDLE || mState == STATE_IDLE_MAINTENANCE);
if (enabled) {
// If Quick Doze is enabled, see if we should go straight into it.
becomeInactiveIfAppropriateLocked();
@@ -2767,10 +2910,11 @@ public class DeviceIdleController extends SystemService
mNextIdleDelay = 0;
mNextLightIdleDelay = 0;
mIdleStartTime = 0;
+ mQuickDozeActivatedWhileIdling = false;
cancelAlarmLocked();
cancelSensingTimeoutAlarmLocked();
cancelLocatingLocked();
- stopMonitoringMotionLocked();
+ maybeStopMonitoringMotionLocked();
mAnyMotionDetector.stop();
updateActiveConstraintsLocked();
}
@@ -3270,11 +3414,23 @@ public class DeviceIdleController extends SystemService
void motionLocked() {
if (DEBUG) Slog.d(TAG, "motionLocked()");
- // The motion sensor will have been disabled at this point
+ mLastMotionEventElapsed = mInjector.getElapsedRealtime();
handleMotionDetectedLocked(mConstants.MOTION_INACTIVE_TIMEOUT, "motion");
}
void handleMotionDetectedLocked(long timeout, String type) {
+ if (mStationaryListeners.size() > 0) {
+ postStationaryStatusUpdated();
+ scheduleMotionTimeoutAlarmLocked();
+ }
+ if (mQuickDozeActivated && !mQuickDozeActivatedWhileIdling) {
+ // Don't exit idle due to motion if quick doze is enabled.
+ // However, if the device started idling due to the normal progression (going through
+ // all the states) and then had quick doze activated, come out briefly on motion so the
+ // user can get slightly fresher content.
+ return;
+ }
+ maybeStopMonitoringMotionLocked();
// The device is not yet active, so we want to go back to the pending idle
// state to wait again for no motion. Note that we only monitor for motion
// after moving out of the inactive state, so no need to worry about that.
@@ -3326,10 +3482,15 @@ public class DeviceIdleController extends SystemService
}
}
- void stopMonitoringMotionLocked() {
- if (DEBUG) Slog.d(TAG, "stopMonitoringMotionLocked()");
- if (mMotionSensor != null && mMotionListener.active) {
+ /**
+ * Stops motion monitoring. Will not stop monitoring if there are registered stationary
+ * listeners.
+ */
+ private void maybeStopMonitoringMotionLocked() {
+ if (DEBUG) Slog.d(TAG, "maybeStopMonitoringMotionLocked()");
+ if (mMotionSensor != null && mMotionListener.active && mStationaryListeners.size() == 0) {
mMotionListener.unregisterLocked();
+ cancelMotionTimeoutAlarmLocked();
}
}
@@ -3356,6 +3517,10 @@ public class DeviceIdleController extends SystemService
}
}
+ private void cancelMotionTimeoutAlarmLocked() {
+ mAlarmManager.cancel(mMotionTimeoutAlarmListener);
+ }
+
void cancelSensingTimeoutAlarmLocked() {
if (mNextSensingTimeoutAlarmTime != 0) {
mNextSensingTimeoutAlarmTime = 0;
@@ -3402,6 +3567,14 @@ public class DeviceIdleController extends SystemService
mNextLightAlarmTime, "DeviceIdleController.light", mLightAlarmListener, mHandler);
}
+ private void scheduleMotionTimeoutAlarmLocked() {
+ if (DEBUG) Slog.d(TAG, "scheduleMotionAlarmLocked");
+ long nextMotionTimeoutAlarmTime =
+ mInjector.getElapsedRealtime() + mConstants.MOTION_INACTIVE_TIMEOUT;
+ mAlarmManager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP, nextMotionTimeoutAlarmTime,
+ "DeviceIdleController.motion", mMotionTimeoutAlarmListener, mHandler);
+ }
+
void scheduleSensingTimeoutAlarmLocked(long delay) {
if (DEBUG) Slog.d(TAG, "scheduleSensingAlarmLocked(" + delay + ")");
mNextSensingTimeoutAlarmTime = SystemClock.elapsedRealtime() + delay;
@@ -4322,9 +4495,14 @@ public class DeviceIdleController extends SystemService
}
pw.println(" }");
}
- if (mUseMotionSensor) {
+ if (mUseMotionSensor || mStationaryListeners.size() > 0) {
pw.print(" mMotionActive="); pw.println(mMotionListener.active);
pw.print(" mNotMoving="); pw.println(mNotMoving);
+ pw.print(" mMotionListener.activatedTimeElapsed=");
+ pw.println(mMotionListener.activatedTimeElapsed);
+ pw.print(" mLastMotionEventElapsed="); pw.println(mLastMotionEventElapsed);
+ pw.print(" "); pw.print(mStationaryListeners.size());
+ pw.println(" stationary listeners registered");
}
pw.print(" mLocating="); pw.print(mLocating); pw.print(" mHasGps=");
pw.print(mHasGps); pw.print(" mHasNetwork=");
diff --git a/services/core/java/com/android/server/MasterClearReceiver.java b/services/core/java/com/android/server/MasterClearReceiver.java
index 06c46b908b7a..6a9246dacc51 100644
--- a/services/core/java/com/android/server/MasterClearReceiver.java
+++ b/services/core/java/com/android/server/MasterClearReceiver.java
@@ -82,7 +82,7 @@ public class MasterClearReceiver extends BroadcastReceiver {
}
};
- if (mWipeExternalStorage || mWipeEsims) {
+ if (mWipeExternalStorage) {
// thr will be started at the end of this task.
new WipeDataTask(context, thr).execute();
} else {
diff --git a/services/core/java/com/android/server/UiModeManagerService.java b/services/core/java/com/android/server/UiModeManagerService.java
index 9d71896ef08b..e6d303b8f5b9 100644
--- a/services/core/java/com/android/server/UiModeManagerService.java
+++ b/services/core/java/com/android/server/UiModeManagerService.java
@@ -56,8 +56,8 @@ import android.service.vr.IVrManager;
import android.service.vr.IVrStateCallbacks;
import android.util.ArraySet;
import android.util.Slog;
-
import com.android.internal.R;
+import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.app.DisableCarModeActivity;
import com.android.internal.messages.nano.SystemMessageProto.SystemMessage;
import com.android.internal.notification.SystemNotificationChannels;
@@ -65,6 +65,7 @@ import com.android.internal.util.DumpUtils;
import com.android.server.twilight.TwilightListener;
import com.android.server.twilight.TwilightManager;
import com.android.server.twilight.TwilightState;
+import com.android.server.wm.WindowManagerInternal;
import java.io.FileDescriptor;
import java.io.PrintWriter;
@@ -72,6 +73,8 @@ import java.util.HashMap;
import java.util.Map;
import java.util.Set;
+import static android.content.Intent.ACTION_SCREEN_OFF;
+
final class UiModeManagerService extends SystemService {
private static final String TAG = UiModeManager.class.getSimpleName();
private static final boolean LOG = false;
@@ -85,6 +88,10 @@ final class UiModeManagerService extends SystemService {
private int mLastBroadcastState = Intent.EXTRA_DOCK_STATE_UNDOCKED;
private int mNightMode = UiModeManager.MODE_NIGHT_NO;
+ // we use the override auto mode
+ // for example: force night mode off in the night time while in auto mode
+ private int mNightModeOverride = mNightMode;
+ protected static final String OVERRIDE_NIGHT_MODE = Secure.UI_NIGHT_MODE + "_override";
private Map<Integer, String> mCarModePackagePriority = new HashMap<>();
private boolean mCarModeEnabled = false;
@@ -119,6 +126,7 @@ final class UiModeManagerService extends SystemService {
private TwilightManager mTwilightManager;
private NotificationManager mNotificationManager;
private StatusBarManager mStatusBarManager;
+ private WindowManagerInternal mWindowManager;
private PowerManager.WakeLock mWakeLock;
@@ -128,6 +136,17 @@ final class UiModeManagerService extends SystemService {
super(context);
}
+ @VisibleForTesting
+ protected UiModeManagerService(Context context, WindowManagerInternal wm,
+ PowerManager.WakeLock wl, TwilightManager tm,
+ boolean setupWizardComplete) {
+ super(context);
+ mWindowManager = wm;
+ mWakeLock = wl;
+ mTwilightManager = tm;
+ mSetupWizardComplete = setupWizardComplete;
+ }
+
private static Intent buildHomeIntent(String category) {
Intent intent = new Intent(Intent.ACTION_MAIN);
intent.addCategory(category);
@@ -189,8 +208,23 @@ final class UiModeManagerService extends SystemService {
public void onTwilightStateChanged(@Nullable TwilightState state) {
synchronized (mLock) {
if (mNightMode == UiModeManager.MODE_NIGHT_AUTO) {
- updateComputedNightModeLocked();
- updateLocked(0, 0);
+ final IntentFilter intentFilter =
+ new IntentFilter(ACTION_SCREEN_OFF);
+ getContext().registerReceiver(mOnScreenOffHandler, intentFilter);
+ }
+ }
+ }
+ };
+
+ private final BroadcastReceiver mOnScreenOffHandler = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ synchronized (mLock) {
+ updateLocked(0, 0);
+ try {
+ getContext().unregisterReceiver(mOnScreenOffHandler);
+ } catch (IllegalArgumentException e) {
+ // we ignore this exception if the receiver is unregistered already.
}
}
}
@@ -227,8 +261,10 @@ final class UiModeManagerService extends SystemService {
private final ContentObserver mDarkThemeObserver = new ContentObserver(mHandler) {
@Override
public void onChange(boolean selfChange, Uri uri) {
- final int mode = Secure.getIntForUser(getContext().getContentResolver(),
- Secure.UI_NIGHT_MODE, mNightMode, 0);
+ int mode = Secure.getIntForUser(getContext().getContentResolver(), Secure.UI_NIGHT_MODE,
+ mNightMode, 0);
+ mode = mode == UiModeManager.MODE_NIGHT_AUTO
+ ? UiModeManager.MODE_NIGHT_YES : UiModeManager.MODE_NIGHT_NO;
SystemProperties.set(SYSTEM_PROPERTY_DEVICE_THEME, Integer.toString(mode));
}
};
@@ -247,6 +283,7 @@ final class UiModeManagerService extends SystemService {
final PowerManager powerManager =
(PowerManager) context.getSystemService(Context.POWER_SERVICE);
mWakeLock = powerManager.newWakeLock(PowerManager.FULL_WAKE_LOCK, TAG);
+ mWindowManager = LocalServices.getService(WindowManagerInternal.class);
// If setup isn't complete for this user listen for completion so we can unblock
// being able to send a night mode configuration change event
@@ -313,6 +350,16 @@ final class UiModeManagerService extends SystemService {
false, mDarkThemeObserver, 0);
}
+ @VisibleForTesting
+ protected IUiModeManager getService() {
+ return mService;
+ }
+
+ @VisibleForTesting
+ protected Configuration getConfiguration() {
+ return mConfiguration;
+ }
+
// Records whether setup wizard has happened or not and adds an observer for this user if not.
private void verifySetupWizardCompleted() {
final Context context = getContext();
@@ -347,8 +394,11 @@ final class UiModeManagerService extends SystemService {
if (mSetupWizardComplete) {
mNightMode = Secure.getIntForUser(context.getContentResolver(),
Secure.UI_NIGHT_MODE, defaultNightMode, userId);
+ mNightModeOverride = Secure.getIntForUser(context.getContentResolver(),
+ OVERRIDE_NIGHT_MODE, defaultNightMode, userId);
} else {
mNightMode = defaultNightMode;
+ mNightModeOverride = defaultNightMode;
}
return oldNightMode != mNightMode;
@@ -474,14 +524,30 @@ final class UiModeManagerService extends SystemService {
try {
synchronized (mLock) {
if (mNightMode != mode) {
+ if (mNightMode == UiModeManager.MODE_NIGHT_AUTO) {
+ try {
+ getContext().unregisterReceiver(mOnScreenOffHandler);
+ } catch (IllegalArgumentException e) {
+ // we ignore this exception if the receiver is unregistered already.
+ }
+ }
// Only persist setting if not in car mode
if (!mCarModeEnabled) {
Secure.putIntForUser(getContext().getContentResolver(),
Secure.UI_NIGHT_MODE, mode, user);
+ Secure.putIntForUser(getContext().getContentResolver(),
+ OVERRIDE_NIGHT_MODE, mNightModeOverride, user);
}
mNightMode = mode;
- updateLocked(0, 0);
+ mNightModeOverride = mode;
+ //on screen off will update configuration instead
+ if (mNightMode != UiModeManager.MODE_NIGHT_AUTO) {
+ updateLocked(0, 0);
+ } else {
+ getContext().registerReceiver(
+ mOnScreenOffHandler, new IntentFilter(ACTION_SCREEN_OFF));
+ }
}
}
} finally {
@@ -521,6 +587,34 @@ final class UiModeManagerService extends SystemService {
if (!DumpUtils.checkDumpPermission(getContext(), TAG, pw)) return;
dumpImpl(pw);
}
+
+ @Override
+ public boolean setNightModeActivated(boolean active) {
+ synchronized (mLock) {
+ final long ident = Binder.clearCallingIdentity();
+ try {
+ if (mNightMode == UiModeManager.MODE_NIGHT_AUTO) {
+ try {
+ getContext().unregisterReceiver(mOnScreenOffHandler);
+ } catch (IllegalArgumentException e) {
+ }
+ mNightModeOverride = active
+ ? UiModeManager.MODE_NIGHT_YES : UiModeManager.MODE_NIGHT_NO;
+ } else if (mNightMode == UiModeManager.MODE_NIGHT_NO
+ && active) {
+ mNightMode = UiModeManager.MODE_NIGHT_YES;
+ } else if (mNightMode == UiModeManager.MODE_NIGHT_YES
+ && !active) {
+ mNightMode = UiModeManager.MODE_NIGHT_NO;
+ }
+ updateConfigurationLocked();
+ sendConfigurationLocked();
+ return true;
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
+ }
+ }
};
void dumpImpl(PrintWriter pw) {
@@ -1022,6 +1116,20 @@ final class UiModeManagerService extends SystemService {
if (state != null) {
mComputedNightMode = state.isNight();
}
+ if (mNightModeOverride == UiModeManager.MODE_NIGHT_YES && !mComputedNightMode) {
+ mComputedNightMode = true;
+ return;
+ }
+ if (mNightModeOverride == UiModeManager.MODE_NIGHT_NO && mComputedNightMode) {
+ mComputedNightMode = false;
+ return;
+ }
+
+ mNightModeOverride = mNightMode;
+ final int user = UserHandle.getCallingUserId();
+ Secure.putIntForUser(getContext().getContentResolver(),
+ OVERRIDE_NIGHT_MODE, mNightModeOverride, user);
+
}
}
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 5ffdf02ef11e..0ee29f33faef 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -5283,7 +5283,7 @@ public class ActivityManagerService extends IActivityManager.Stub
storageManager.commitChanges();
} catch (Exception e) {
PowerManager pm = (PowerManager)
- mInjector.getContext().getSystemService(Context.POWER_SERVICE);
+ mContext.getSystemService(Context.POWER_SERVICE);
pm.reboot("Checkpoint commit failed");
}
@@ -18030,7 +18030,7 @@ public class ActivityManagerService extends IActivityManager.Stub
@Override
public int getCurrentUserId() {
- return mUserController.getCurrentUserIdLU();
+ return mUserController.getCurrentUserId();
}
@Override
diff --git a/services/core/java/com/android/server/am/CarUserSwitchingDialog.java b/services/core/java/com/android/server/am/CarUserSwitchingDialog.java
index c7de7b17b1c2..ebfc2a011e88 100644
--- a/services/core/java/com/android/server/am/CarUserSwitchingDialog.java
+++ b/services/core/java/com/android/server/am/CarUserSwitchingDialog.java
@@ -37,6 +37,7 @@ import android.view.LayoutInflater;
import android.view.View;
import android.widget.ImageView;
import android.widget.TextView;
+
import com.android.internal.R;
diff --git a/services/core/java/com/android/server/am/OWNERS b/services/core/java/com/android/server/am/OWNERS
index 2f9a5c952659..7cc2e8eb2954 100644
--- a/services/core/java/com/android/server/am/OWNERS
+++ b/services/core/java/com/android/server/am/OWNERS
@@ -35,3 +35,5 @@ michaelwr@google.com
narayan@google.com
per-file SettingsToPropertiesMapper.java = omakoto@google.com, svetoslavganov@google.com, yamasani@google.com
+
+per-file CarUserSwitchingDialog.java = keunyoung@google.com, felipeal@google.com, gurunagarajan@google.com
diff --git a/services/core/java/com/android/server/am/PendingIntentController.java b/services/core/java/com/android/server/am/PendingIntentController.java
index d75591cc7432..df76713d58a6 100644
--- a/services/core/java/com/android/server/am/PendingIntentController.java
+++ b/services/core/java/com/android/server/am/PendingIntentController.java
@@ -43,6 +43,7 @@ import android.util.Slog;
import com.android.internal.os.IResultReceiver;
import com.android.internal.util.function.pooled.PooledLambda;
+import com.android.server.AlarmManagerInternal;
import com.android.server.LocalServices;
import com.android.server.wm.ActivityTaskManagerInternal;
import com.android.server.wm.SafeActivityOptions;
@@ -293,6 +294,8 @@ public class PendingIntentController {
PendingIntentController::handlePendingIntentCancelled, this, callbacks);
mH.sendMessage(m);
}
+ final AlarmManagerInternal ami = LocalServices.getService(AlarmManagerInternal.class);
+ ami.remove(new PendingIntent(rec));
}
private void handlePendingIntentCancelled(RemoteCallbackList<IResultReceiver> callbacks) {
diff --git a/services/core/java/com/android/server/am/ProcessList.java b/services/core/java/com/android/server/am/ProcessList.java
index a2670d8dd424..1d099c81a1e7 100644
--- a/services/core/java/com/android/server/am/ProcessList.java
+++ b/services/core/java/com/android/server/am/ProcessList.java
@@ -1329,7 +1329,7 @@ public final class ProcessList {
final int procCount = procs.size();
for (int i = 0; i < procCount; i++) {
final int procUid = procs.keyAt(i);
- if (UserHandle.isApp(procUid) || !UserHandle.isSameUser(procUid, uid)) {
+ if (!UserHandle.isCore(procUid) || !UserHandle.isSameUser(procUid, uid)) {
// Don't use an app process or different user process for system component.
continue;
}
diff --git a/services/core/java/com/android/server/am/UserController.java b/services/core/java/com/android/server/am/UserController.java
index b311233694ce..598a68e90aa8 100644
--- a/services/core/java/com/android/server/am/UserController.java
+++ b/services/core/java/com/android/server/am/UserController.java
@@ -134,12 +134,12 @@ class UserController implements Handler.Callback {
static final int CONTINUE_USER_SWITCH_MSG = 20;
static final int USER_SWITCH_TIMEOUT_MSG = 30;
static final int START_PROFILES_MSG = 40;
- static final int SYSTEM_USER_START_MSG = 50;
- static final int SYSTEM_USER_CURRENT_MSG = 60;
+ static final int USER_START_MSG = 50;
+ static final int USER_CURRENT_MSG = 60;
static final int FOREGROUND_PROFILE_CHANGED_MSG = 70;
static final int REPORT_USER_SWITCH_COMPLETE_MSG = 80;
static final int USER_SWITCH_CALLBACKS_TIMEOUT_MSG = 90;
- static final int SYSTEM_USER_UNLOCK_MSG = 100;
+ static final int USER_UNLOCK_MSG = 100;
static final int REPORT_LOCKED_BOOT_COMPLETE_MSG = 110;
static final int START_USER_SWITCH_FG_MSG = 120;
@@ -369,16 +369,18 @@ class UserController implements Handler.Callback {
}
}
- mHandler.sendMessage(mHandler.obtainMessage(REPORT_LOCKED_BOOT_COMPLETE_MSG,
- userId, 0));
- Intent intent = new Intent(Intent.ACTION_LOCKED_BOOT_COMPLETED, null);
- intent.putExtra(Intent.EXTRA_USER_HANDLE, userId);
- intent.addFlags(Intent.FLAG_RECEIVER_NO_ABORT
- | Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND);
- mInjector.broadcastIntent(intent, null, resultTo, 0, null, null,
- new String[]{android.Manifest.permission.RECEIVE_BOOT_COMPLETED},
- AppOpsManager.OP_NONE, null, true, false, MY_PID, SYSTEM_UID,
- Binder.getCallingUid(), Binder.getCallingPid(), userId);
+ if (!mInjector.getUserManager().isPreCreated(userId)) {
+ mHandler.sendMessage(mHandler.obtainMessage(REPORT_LOCKED_BOOT_COMPLETE_MSG,
+ userId, 0));
+ Intent intent = new Intent(Intent.ACTION_LOCKED_BOOT_COMPLETED, null);
+ intent.putExtra(Intent.EXTRA_USER_HANDLE, userId);
+ intent.addFlags(Intent.FLAG_RECEIVER_NO_ABORT
+ | Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND);
+ mInjector.broadcastIntent(intent, null, resultTo, 0, null, null,
+ new String[]{android.Manifest.permission.RECEIVE_BOOT_COMPLETED},
+ AppOpsManager.OP_NONE, null, true, false, MY_PID, SYSTEM_UID,
+ Binder.getCallingUid(), Binder.getCallingPid(), userId);
+ }
}
// We need to delay unlocking managed profiles until the parent user
@@ -439,8 +441,7 @@ class UserController implements Handler.Callback {
// Dispatch unlocked to system services; when fully dispatched,
// that calls through to the next "unlocked" phase
- mHandler.obtainMessage(SYSTEM_USER_UNLOCK_MSG, userId, 0, uss)
- .sendToTarget();
+ mHandler.obtainMessage(USER_UNLOCK_MSG, userId, 0, uss).sendToTarget();
});
return true;
}
@@ -556,6 +557,17 @@ class UserController implements Handler.Callback {
}
}
+ if (userInfo.preCreated) {
+ Slog.i(TAG, "Stopping pre-created user " + userInfo.toFullString());
+ // Pre-created user was started right after creation so services could properly
+ // intialize it; it should be stopped right away as it's not really a "real" user.
+ // TODO(b/140750212): in the long-term, we should add a onCreateUser() callback
+ // on SystemService instead.
+ stopUser(userInfo.id, /* force= */ true, /* stopUserCallback= */ null,
+ /* keyEvictedCallback= */ null);
+ return;
+ }
+
// Spin up app widgets prior to boot-complete, so they can be ready promptly
mInjector.startUserWidgets(userId);
@@ -808,7 +820,8 @@ class UserController implements Handler.Callback {
mInjector.systemServiceManagerCleanupUser(userId);
mInjector.stackSupervisorRemoveUser(userId);
// Remove the user if it is ephemeral.
- if (getUserInfo(userId).isEphemeral()) {
+ UserInfo userInfo = getUserInfo(userId);
+ if (userInfo.isEphemeral() && !userInfo.preCreated) {
mInjector.getUserManager().removeUserEvenWhenDisallowed(userId);
}
@@ -985,11 +998,13 @@ class UserController implements Handler.Callback {
* <ul>
* <li>{@link Intent#ACTION_USER_STARTED} - sent to registered receivers of the new user
* <li>{@link Intent#ACTION_USER_BACKGROUND} - sent to registered receivers of the outgoing
- * user and all profiles of this user. Sent only if {@code foreground} parameter is true
+ * user and all profiles of this user. Sent only if {@code foreground} parameter is
+ * {@code false}
* <li>{@link Intent#ACTION_USER_FOREGROUND} - sent to registered receivers of the new
- * user and all profiles of this user. Sent only if {@code foreground} parameter is true
+ * user and all profiles of this user. Sent only if {@code foreground} parameter is
+ * {@code true}
* <li>{@link Intent#ACTION_USER_SWITCHED} - sent to registered receivers of the new user.
- * Sent only if {@code foreground} parameter is true
+ * Sent only if {@code foreground} parameter is {@code true}
* <li>{@link Intent#ACTION_USER_STARTING} - ordered broadcast sent to registered receivers
* of the new fg user
* <li>{@link Intent#ACTION_LOCKED_BOOT_COMPLETED} - ordered broadcast sent to receivers of
@@ -1063,6 +1078,11 @@ class UserController implements Handler.Callback {
return false;
}
+ if (foreground && userInfo.preCreated) {
+ Slog.w(TAG, "Cannot start pre-created user #" + userId + " as foreground");
+ return false;
+ }
+
if (foreground && mUserSwitchUiEnabled) {
mInjector.getWindowManager().startFreezingScreen(
R.anim.screen_user_exit, R.anim.screen_user_enter);
@@ -1157,13 +1177,11 @@ class UserController implements Handler.Callback {
// Booting up a new user, need to tell system services about it.
// Note that this is on the same handler as scheduling of broadcasts,
// which is important because it needs to go first.
- mHandler.sendMessage(
- mHandler.obtainMessage(SYSTEM_USER_START_MSG, userId, 0));
+ mHandler.sendMessage(mHandler.obtainMessage(USER_START_MSG, userId, 0));
}
if (foreground) {
- mHandler.sendMessage(mHandler.obtainMessage(SYSTEM_USER_CURRENT_MSG, userId,
- oldUserId));
+ mHandler.sendMessage(mHandler.obtainMessage(USER_CURRENT_MSG, userId, oldUserId));
mHandler.removeMessages(REPORT_USER_SWITCH_MSG);
mHandler.removeMessages(USER_SWITCH_TIMEOUT_MSG);
mHandler.sendMessage(mHandler.obtainMessage(REPORT_USER_SWITCH_MSG,
@@ -1172,6 +1190,10 @@ class UserController implements Handler.Callback {
oldUserId, userId, uss), USER_SWITCH_TIMEOUT_MS);
}
+ if (userInfo.preCreated) {
+ needStart = false;
+ }
+
if (needStart) {
// Send USER_STARTED broadcast
Intent intent = new Intent(Intent.ACTION_USER_STARTED);
@@ -2129,13 +2151,13 @@ class UserController implements Handler.Callback {
case START_PROFILES_MSG:
startProfiles();
break;
- case SYSTEM_USER_START_MSG:
+ case USER_START_MSG:
mInjector.batteryStatsServiceNoteEvent(
BatteryStats.HistoryItem.EVENT_USER_RUNNING_START,
Integer.toString(msg.arg1), msg.arg1);
mInjector.getSystemServiceManager().startUser(msg.arg1);
break;
- case SYSTEM_USER_UNLOCK_MSG:
+ case USER_UNLOCK_MSG:
final int userId = msg.arg1;
mInjector.getSystemServiceManager().unlockUser(userId);
// Loads recents on a worker thread that allows disk I/O
@@ -2144,7 +2166,7 @@ class UserController implements Handler.Callback {
});
finishUserUnlocked((UserState) msg.obj);
break;
- case SYSTEM_USER_CURRENT_MSG:
+ case USER_CURRENT_MSG:
mInjector.batteryStatsServiceNoteEvent(
BatteryStats.HistoryItem.EVENT_USER_FOREGROUND_FINISH,
Integer.toString(msg.arg2), msg.arg2);
diff --git a/services/core/java/com/android/server/am/UserSwitchingDialog.java b/services/core/java/com/android/server/am/UserSwitchingDialog.java
index 98f5557903d6..0ec4454cc4ea 100644
--- a/services/core/java/com/android/server/am/UserSwitchingDialog.java
+++ b/services/core/java/com/android/server/am/UserSwitchingDialog.java
@@ -24,6 +24,7 @@ import android.os.Handler;
import android.os.Message;
import android.os.UserHandle;
import android.os.UserManager;
+import android.util.Slog;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewTreeObserver;
@@ -46,6 +47,9 @@ class UserSwitchingDialog extends AlertDialog
// Time to wait for the onWindowShown() callback before continuing the user switch
private static final int WINDOW_SHOWN_TIMEOUT_MS = 3000;
+ // User switching doesn't happen that frequently, so it doesn't hurt to have it always on
+ protected static final boolean DEBUG = true;
+
private final ActivityManagerService mService;
private final int mUserId;
private static final int MSG_START_USER = 1;
@@ -118,7 +122,7 @@ class UserSwitchingDialog extends AlertDialog
@Override
public void show() {
- // Slog.v(TAG, "show called");
+ if (DEBUG) Slog.d(TAG, "show called");
super.show();
final View decorView = getWindow().getDecorView();
if (decorView != null) {
@@ -132,13 +136,14 @@ class UserSwitchingDialog extends AlertDialog
@Override
public void onWindowShown() {
- // Slog.v(TAG, "onWindowShown called");
+ if (DEBUG) Slog.d(TAG, "onWindowShown called");
startUser();
}
void startUser() {
synchronized (this) {
if (!mStartedUser) {
+ Slog.i(TAG, "starting user " + mUserId);
mService.mUserController.startUserInForeground(mUserId);
dismiss();
mStartedUser = true;
@@ -147,6 +152,8 @@ class UserSwitchingDialog extends AlertDialog
decorView.getViewTreeObserver().removeOnWindowShownListener(this);
}
mHandler.removeMessages(MSG_START_USER);
+ } else {
+ Slog.i(TAG, "user " + mUserId + " already started");
}
}
}
@@ -156,6 +163,8 @@ class UserSwitchingDialog extends AlertDialog
public void handleMessage(Message msg) {
switch (msg.what) {
case MSG_START_USER:
+ Slog.w(TAG, "user switch window not shown in "
+ + WINDOW_SHOWN_TIMEOUT_MS + " ms");
startUser();
break;
}
diff --git a/services/core/java/com/android/server/display/BrightnessTracker.java b/services/core/java/com/android/server/display/BrightnessTracker.java
index 126beeffbb96..765fdb63bc7d 100644
--- a/services/core/java/com/android/server/display/BrightnessTracker.java
+++ b/services/core/java/com/android/server/display/BrightnessTracker.java
@@ -94,6 +94,8 @@ public class BrightnessTracker {
static final String TAG = "BrightnessTracker";
static final boolean DEBUG = false;
+ @VisibleForTesting
+ static final boolean ENABLE_COLOR_SAMPLING = false;
private static final String EVENTS_FILE = "brightness_events.xml";
private static final String AMBIENT_BRIGHTNESS_STATS_FILE = "ambient_brightness_stats.xml";
@@ -757,7 +759,8 @@ public class BrightnessTracker {
}
private void enableColorSampling() {
- if (!mInjector.isBrightnessModeAutomatic(mContentResolver)
+ if (!ENABLE_COLOR_SAMPLING
+ || !mInjector.isBrightnessModeAutomatic(mContentResolver)
|| !mInjector.isInteractive(mContext)
|| mColorSamplingEnabled) {
return;
diff --git a/services/core/java/com/android/server/display/color/ColorDisplayService.java b/services/core/java/com/android/server/display/color/ColorDisplayService.java
index 0bf43b6d1b9c..2dc2cf0d8e90 100644
--- a/services/core/java/com/android/server/display/color/ColorDisplayService.java
+++ b/services/core/java/com/android/server/display/color/ColorDisplayService.java
@@ -589,16 +589,18 @@ public final class ColorDisplayService extends SystemService {
if (immediate) {
dtm.setColorMatrix(tintController.getLevel(), to);
} else {
- tintController.setAnimator(ValueAnimator.ofObject(COLOR_MATRIX_EVALUATOR,
- from == null ? MATRIX_IDENTITY : from, to));
- tintController.getAnimator().setDuration(TRANSITION_DURATION);
- tintController.getAnimator().setInterpolator(AnimationUtils.loadInterpolator(
+ TintValueAnimator valueAnimator = TintValueAnimator.ofMatrix(COLOR_MATRIX_EVALUATOR,
+ from == null ? MATRIX_IDENTITY : from, to);
+ tintController.setAnimator(valueAnimator);
+ valueAnimator.setDuration(TRANSITION_DURATION);
+ valueAnimator.setInterpolator(AnimationUtils.loadInterpolator(
getContext(), android.R.interpolator.fast_out_slow_in));
- tintController.getAnimator().addUpdateListener((ValueAnimator animator) -> {
+ valueAnimator.addUpdateListener((ValueAnimator animator) -> {
final float[] value = (float[]) animator.getAnimatedValue();
dtm.setColorMatrix(tintController.getLevel(), value);
+ ((TintValueAnimator) animator).updateMinMaxComponents();
});
- tintController.getAnimator().addListener(new AnimatorListenerAdapter() {
+ valueAnimator.addListener(new AnimatorListenerAdapter() {
private boolean mIsCancelled;
@@ -609,9 +611,14 @@ public final class ColorDisplayService extends SystemService {
@Override
public void onAnimationEnd(Animator animator) {
+ TintValueAnimator t = (TintValueAnimator) animator;
Slog.d(TAG, tintController.getClass().getSimpleName()
+ " Animation cancelled: " + mIsCancelled
- + " to matrix: " + TintController.matrixToString(to, 16));
+ + " to matrix: " + TintController.matrixToString(to, 16)
+ + " min matrix coefficients: "
+ + TintController.matrixToString(t.getMin(), 16)
+ + " max matrix coefficients: "
+ + TintController.matrixToString(t.getMax(), 16));
if (!mIsCancelled) {
// Ensure final color matrix is set at the end of the animation. If the
// animation is cancelled then don't set the final color matrix so the new
@@ -621,7 +628,7 @@ public final class ColorDisplayService extends SystemService {
tintController.setAnimator(null);
}
});
- tintController.getAnimator().start();
+ valueAnimator.start();
}
}
@@ -1109,6 +1116,51 @@ public final class ColorDisplayService extends SystemService {
}
/**
+ * Only animates matrices and saves min and max coefficients for logging.
+ */
+ static class TintValueAnimator extends ValueAnimator {
+ private float[] min;
+ private float[] max;
+
+ public static TintValueAnimator ofMatrix(ColorMatrixEvaluator evaluator,
+ Object... values) {
+ TintValueAnimator anim = new TintValueAnimator();
+ anim.setObjectValues(values);
+ anim.setEvaluator(evaluator);
+ if (values == null || values.length == 0) {
+ return null;
+ }
+ float[] m = (float[]) values[0];
+ anim.min = new float[m.length];
+ anim.max = new float[m.length];
+ for (int i = 0; i < m.length; ++i) {
+ anim.min[i] = Float.MAX_VALUE;
+ anim.max[i] = Float.MIN_VALUE;
+ }
+ return anim;
+ }
+
+ public void updateMinMaxComponents() {
+ float[] value = (float[]) getAnimatedValue();
+ if (value == null) {
+ return;
+ }
+ for (int i = 0; i < value.length; ++i) {
+ min[i] = Math.min(min[i], value[i]);
+ max[i] = Math.max(max[i], value[i]);
+ }
+ }
+
+ public float[] getMin() {
+ return min;
+ }
+
+ public float[] getMax() {
+ return max;
+ }
+ }
+
+ /**
* Interpolates between two 4x4 color transform matrices (in column-major order).
*/
private static class ColorMatrixEvaluator implements TypeEvaluator<float[]> {
diff --git a/services/core/java/com/android/server/display/color/TintController.java b/services/core/java/com/android/server/display/color/TintController.java
index 8d8b9b2af04e..422dd328d2b6 100644
--- a/services/core/java/com/android/server/display/color/TintController.java
+++ b/services/core/java/com/android/server/display/color/TintController.java
@@ -24,14 +24,14 @@ import java.io.PrintWriter;
abstract class TintController {
- private ValueAnimator mAnimator;
+ private ColorDisplayService.TintValueAnimator mAnimator;
private Boolean mIsActivated;
- public ValueAnimator getAnimator() {
+ public ColorDisplayService.TintValueAnimator getAnimator() {
return mAnimator;
}
- public void setAnimator(ValueAnimator animator) {
+ public void setAnimator(ColorDisplayService.TintValueAnimator animator) {
mAnimator = animator;
}
diff --git a/services/core/java/com/android/server/location/GnssLocationProvider.java b/services/core/java/com/android/server/location/GnssLocationProvider.java
index 181a43549313..4b79677b475b 100644
--- a/services/core/java/com/android/server/location/GnssLocationProvider.java
+++ b/services/core/java/com/android/server/location/GnssLocationProvider.java
@@ -75,6 +75,8 @@ import com.android.internal.location.ProviderProperties;
import com.android.internal.location.ProviderRequest;
import com.android.internal.location.gnssmetrics.GnssMetrics;
import com.android.internal.telephony.TelephonyIntents;
+import com.android.server.DeviceIdleController;
+import com.android.server.LocalServices;
import com.android.server.location.GnssSatelliteBlacklistHelper.GnssSatelliteBlacklistCallback;
import com.android.server.location.NtpTimeHelper.InjectNtpTimeCallback;
@@ -178,6 +180,7 @@ public class GnssLocationProvider extends AbstractLocationProvider implements
private static final int AGPS_SUPL_MODE_MSA = 0x02;
private static final int AGPS_SUPL_MODE_MSB = 0x01;
+ private static final int UPDATE_LOW_POWER_MODE = 1;
private static final int SET_REQUEST = 3;
private static final int INJECT_NTP_TIME = 5;
// PSDS stands for Predicted Satellite Data Service
@@ -371,6 +374,12 @@ public class GnssLocationProvider extends AbstractLocationProvider implements
private boolean mDisableGpsForPowerManager = false;
/**
+ * True if the device idle controller has determined that the device is stationary. This is only
+ * updated when the device enters idle mode.
+ */
+ private volatile boolean mIsDeviceStationary = false;
+
+ /**
* Properties loaded from PROPERTIES_FILE.
* It must be accessed only inside {@link #mHandler}.
*/
@@ -462,6 +471,15 @@ public class GnssLocationProvider extends AbstractLocationProvider implements
public GnssNavigationMessageProvider getGnssNavigationMessageProvider() {
return mGnssNavigationMessageProvider;
}
+
+ private final DeviceIdleController.StationaryListener mDeviceIdleStationaryListener =
+ isStationary -> {
+ mIsDeviceStationary = isStationary;
+ // Call updateLowPowerMode on handler thread so it's always called from the same
+ // thread.
+ mHandler.sendEmptyMessage(UPDATE_LOW_POWER_MODE);
+ };
+
private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
@@ -478,11 +496,22 @@ public class GnssLocationProvider extends AbstractLocationProvider implements
case ALARM_TIMEOUT:
hibernate();
break;
- case PowerManager.ACTION_POWER_SAVE_MODE_CHANGED:
case PowerManager.ACTION_DEVICE_IDLE_MODE_CHANGED:
+ DeviceIdleController.LocalService deviceIdleService = LocalServices.getService(
+ DeviceIdleController.LocalService.class);
+ if (mPowerManager.isDeviceIdleMode()) {
+ deviceIdleService.registerStationaryListener(mDeviceIdleStationaryListener);
+ } else {
+ deviceIdleService.unregisterStationaryListener(
+ mDeviceIdleStationaryListener);
+ }
+ // Intentional fall-through.
+ case PowerManager.ACTION_POWER_SAVE_MODE_CHANGED:
case Intent.ACTION_SCREEN_OFF:
case Intent.ACTION_SCREEN_ON:
- updateLowPowerMode();
+ // Call updateLowPowerMode on handler thread so it's always called from the
+ // same thread.
+ mHandler.sendEmptyMessage(UPDATE_LOW_POWER_MODE);
break;
case CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED:
case TelephonyIntents.ACTION_DEFAULT_DATA_SUBSCRIPTION_CHANGED:
@@ -540,10 +569,9 @@ public class GnssLocationProvider extends AbstractLocationProvider implements
}
private void updateLowPowerMode() {
- // Disable GPS if we are in device idle mode.
- boolean disableGpsForPowerManager = mPowerManager.isDeviceIdleMode();
- final PowerSaveState result =
- mPowerManager.getPowerSaveState(ServiceType.LOCATION);
+ // Disable GPS if we are in device idle mode and the device is stationary.
+ boolean disableGpsForPowerManager = mPowerManager.isDeviceIdleMode() && mIsDeviceStationary;
+ final PowerSaveState result = mPowerManager.getPowerSaveState(ServiceType.LOCATION);
switch (result.locationMode) {
case PowerManager.LOCATION_MODE_GPS_DISABLED_WHEN_SCREEN_OFF:
case PowerManager.LOCATION_MODE_ALL_DISABLED_WHEN_SCREEN_OFF:
@@ -2048,6 +2076,9 @@ public class GnssLocationProvider extends AbstractLocationProvider implements
case REPORT_SV_STATUS:
handleReportSvStatus((SvStatusInfo) msg.obj);
break;
+ case UPDATE_LOW_POWER_MODE:
+ updateLowPowerMode();
+ break;
}
if (msg.arg2 == 1) {
// wakelock was taken for this message, release it
diff --git a/services/core/java/com/android/server/notification/NotificationComparator.java b/services/core/java/com/android/server/notification/NotificationComparator.java
index 9b9f4de7a18f..bc051547a53f 100644
--- a/services/core/java/com/android/server/notification/NotificationComparator.java
+++ b/services/core/java/com/android/server/notification/NotificationComparator.java
@@ -129,6 +129,12 @@ public class NotificationComparator
return -1 * Integer.compare(leftPriority, rightPriority);
}
+ final boolean leftInterruptive = left.isInterruptive();
+ final boolean rightInterruptive = right.isInterruptive();
+ if (leftInterruptive != rightInterruptive) {
+ return -1 * Boolean.compare(leftInterruptive, rightInterruptive);
+ }
+
// then break ties by time, most recent first
return -1 * Long.compare(left.getRankingTimeMs(), right.getRankingTimeMs());
}
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index 6f45825e81fe..6452fbf8f93c 100755
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -5356,16 +5356,8 @@ public class NotificationManagerService extends SystemService {
}
synchronized (mNotificationLock) {
- // If the notification is currently enqueued, repost this runnable so it has a
- // chance to notify listeners
- if ((findNotificationByListLocked(mEnqueuedNotifications, mPkg, mTag, mId, mUserId))
- != null) {
- mHandler.post(this);
- return;
- }
- // Look for the notification in the posted list, since we already checked enqueued.
- NotificationRecord r =
- findNotificationByListLocked(mNotificationList, mPkg, mTag, mId, mUserId);
+ // Look for the notification, searching both the posted and enqueued lists.
+ NotificationRecord r = findNotificationLocked(mPkg, mTag, mId, mUserId);
if (r != null) {
// The notification was found, check if it should be removed.
@@ -5550,7 +5542,9 @@ public class NotificationManagerService extends SystemService {
notification.flags |=
old.getNotification().flags & FLAG_FOREGROUND_SERVICE;
r.isUpdate = true;
- r.setTextChanged(isVisuallyInterruptive(old, r));
+ final boolean isInterruptive = isVisuallyInterruptive(old, r);
+ r.setTextChanged(isInterruptive);
+ r.setInterruptive(isInterruptive);
}
mNotificationsByKey.put(n.getKey(), r);
@@ -5649,7 +5643,6 @@ public class NotificationManagerService extends SystemService {
Notification oldN = old.sbn.getNotification();
Notification newN = r.sbn.getNotification();
-
if (oldN.extras == null || newN.extras == null) {
if (DEBUG_INTERRUPTIVENESS) {
Slog.v(TAG, "INTERRUPTIVENESS: "
@@ -5681,6 +5674,7 @@ public class NotificationManagerService extends SystemService {
}
return true;
}
+
// Do not compare Spannables (will always return false); compare unstyled Strings
final String oldText = String.valueOf(oldN.extras.get(Notification.EXTRA_TEXT));
final String newText = String.valueOf(newN.extras.get(Notification.EXTRA_TEXT));
@@ -5695,6 +5689,7 @@ public class NotificationManagerService extends SystemService {
}
return true;
}
+
if (oldN.hasCompletedProgress() != newN.hasCompletedProgress()) {
if (DEBUG_INTERRUPTIVENESS) {
Slog.v(TAG, "INTERRUPTIVENESS: "
@@ -5702,6 +5697,16 @@ public class NotificationManagerService extends SystemService {
}
return true;
}
+
+ // Fields below are invisible to bubbles.
+ if (r.canBubble()) {
+ if (DEBUG_INTERRUPTIVENESS) {
+ Slog.v(TAG, "INTERRUPTIVENESS: "
+ + r.getKey() + " is not interruptive: bubble");
+ }
+ return false;
+ }
+
// Actions
if (Notification.areActionsVisiblyDifferent(oldN, newN)) {
if (DEBUG_INTERRUPTIVENESS) {
@@ -5735,7 +5740,6 @@ public class NotificationManagerService extends SystemService {
} catch (Exception e) {
Slog.w(TAG, "error recovering builder", e);
}
-
return false;
}
@@ -5930,12 +5934,17 @@ public class NotificationManagerService extends SystemService {
Slog.v(TAG, "INTERRUPTIVENESS: "
+ record.getKey() + " is not interruptive: summary");
}
+ } else if (record.canBubble()) {
+ if (DEBUG_INTERRUPTIVENESS) {
+ Slog.v(TAG, "INTERRUPTIVENESS: "
+ + record.getKey() + " is not interruptive: bubble");
+ }
} else {
+ record.setInterruptive(true);
if (DEBUG_INTERRUPTIVENESS) {
Slog.v(TAG, "INTERRUPTIVENESS: "
+ record.getKey() + " is interruptive: alerted");
}
- record.setInterruptive(true);
}
MetricsLogger.action(record.getLogMaker()
.setCategory(MetricsEvent.NOTIFICATION_ALERT)
@@ -6294,15 +6303,21 @@ public class NotificationManagerService extends SystemService {
int indexBefore = findNotificationRecordIndexLocked(record);
boolean interceptBefore = record.isIntercepted();
int visibilityBefore = record.getPackageVisibilityOverride();
+ boolean interruptiveBefore = record.isInterruptive();
+
recon.applyChangesLocked(record);
applyZenModeLocked(record);
mRankingHelper.sort(mNotificationList);
- int indexAfter = findNotificationRecordIndexLocked(record);
- boolean interceptAfter = record.isIntercepted();
- int visibilityAfter = record.getPackageVisibilityOverride();
- changed = indexBefore != indexAfter || interceptBefore != interceptAfter
- || visibilityBefore != visibilityAfter;
- if (interceptBefore && !interceptAfter
+ boolean indexChanged = indexBefore != findNotificationRecordIndexLocked(record);
+ boolean interceptChanged = interceptBefore != record.isIntercepted();
+ boolean visibilityChanged = visibilityBefore != record.getPackageVisibilityOverride();
+
+ // Broadcast isInterruptive changes for bubbles.
+ boolean interruptiveChanged =
+ record.canBubble() && (interruptiveBefore != record.isInterruptive());
+
+ changed = indexChanged || interceptChanged || visibilityChanged || interruptiveChanged;
+ if (interceptBefore && !record.isIntercepted()
&& record.isNewEnoughForAlerting(System.currentTimeMillis())) {
buzzBeepBlinkLocked(record);
}
@@ -7433,7 +7448,8 @@ public class NotificationManagerService extends SystemService {
record.getSound() != null || record.getVibration() != null,
record.getSystemGeneratedSmartActions(),
record.getSmartReplies(),
- record.canBubble()
+ record.canBubble(),
+ record.isInterruptive()
);
rankings.add(ranking);
}
@@ -8621,6 +8637,7 @@ public class NotificationManagerService extends SystemService {
@VisibleForTesting
void resetAssistantUserSet(int userId) {
+ checkCallerIsSystemOrShell();
mAssistants.setUserSet(userId, false);
handleSavePolicyFile();
}
@@ -8628,12 +8645,14 @@ public class NotificationManagerService extends SystemService {
@VisibleForTesting
@Nullable
ComponentName getApprovedAssistant(int userId) {
+ checkCallerIsSystemOrShell();
List<ComponentName> allowedComponents = mAssistants.getAllowedComponents(userId);
return CollectionUtils.firstOrNull(allowedComponents);
}
@VisibleForTesting
protected void simulatePackageSuspendBroadcast(boolean suspend, String pkg) {
+ checkCallerIsSystemOrShell();
// only use for testing: mimic receive broadcast that package is (un)suspended
// but does not actually (un)suspend the package
final Bundle extras = new Bundle();
@@ -8650,6 +8669,7 @@ public class NotificationManagerService extends SystemService {
@VisibleForTesting
protected void simulatePackageDistractionBroadcast(int flag, String[] pkgs) {
+ checkCallerIsSystemOrShell();
// only use for testing: mimic receive broadcast that package is (un)distracting
// but does not actually register that info with packagemanager
final Bundle extras = new Bundle();
diff --git a/services/core/java/com/android/server/notification/NotificationShellCmd.java b/services/core/java/com/android/server/notification/NotificationShellCmd.java
index dd0f420b6df5..c6c98965d668 100644
--- a/services/core/java/com/android/server/notification/NotificationShellCmd.java
+++ b/services/core/java/com/android/server/notification/NotificationShellCmd.java
@@ -16,6 +16,12 @@
package com.android.server.notification;
+import static android.app.NotificationManager.INTERRUPTION_FILTER_ALARMS;
+import static android.app.NotificationManager.INTERRUPTION_FILTER_ALL;
+import static android.app.NotificationManager.INTERRUPTION_FILTER_NONE;
+import static android.app.NotificationManager.INTERRUPTION_FILTER_PRIORITY;
+import static android.app.NotificationManager.INTERRUPTION_FILTER_UNKNOWN;
+
import android.app.ActivityManager;
import android.app.INotificationManager;
import android.app.Notification;
@@ -26,6 +32,7 @@ import android.app.Person;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
+import android.content.pm.PackageManager;
import android.content.pm.ParceledListSlice;
import android.content.res.Resources;
import android.graphics.drawable.BitmapDrawable;
@@ -47,8 +54,8 @@ import java.util.Collections;
* Implementation of `cmd notification` in NotificationManagerService.
*/
public class NotificationShellCmd extends ShellCommand {
- private static final String USAGE =
- "usage: cmd notification SUBCMD [args]\n\n"
+ private static final String TAG = "NotifShellCmd";
+ private static final String USAGE = "usage: cmd notification SUBCMD [args]\n\n"
+ "SUBCMDs:\n"
+ " allow_listener COMPONENT [user_id (current user if not specified)]\n"
+ " disallow_listener COMPONENT [user_id (current user if not specified)]\n"
@@ -89,18 +96,19 @@ public class NotificationShellCmd extends ShellCommand {
+ "an <intentspec> is (broadcast|service|activity) <args>\n"
+ " <args> are as described in `am start`";
- public static final int NOTIFICATION_ID = 1138;
- public static final String NOTIFICATION_PACKAGE = "com.android.shell";
- public static final String CHANNEL_ID = "shellcmd";
+ public static final int NOTIFICATION_ID = 2020;
+ public static final String CHANNEL_ID = "shell_cmd";
public static final String CHANNEL_NAME = "Shell command";
public static final int CHANNEL_IMP = NotificationManager.IMPORTANCE_DEFAULT;
private final NotificationManagerService mDirectService;
private final INotificationManager mBinderService;
+ private final PackageManager mPm;
public NotificationShellCmd(NotificationManagerService service) {
mDirectService = service;
mBinderService = service.getBinderService();
+ mPm = mDirectService.getContext().getPackageManager();
}
@Override
@@ -108,9 +116,44 @@ public class NotificationShellCmd extends ShellCommand {
if (cmd == null) {
return handleDefaultCommands(cmd);
}
+ String callingPackage = null;
+ final int callingUid = Binder.getCallingUid();
+ long identity = Binder.clearCallingIdentity();
+ try {
+ String[] packages = mPm.getPackagesForUid(callingUid);
+ if (packages != null && packages.length > 0) {
+ callingPackage = packages[0];
+ }
+ } catch (Exception e) {
+ Slog.e(TAG, "failed to get caller pkg", e);
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
final PrintWriter pw = getOutPrintWriter();
try {
switch (cmd.replace('-', '_')) {
+ case "set_dnd": {
+ String mode = getNextArgRequired();
+ int interruptionFilter = INTERRUPTION_FILTER_UNKNOWN;
+ switch(mode) {
+ case "none":
+ case "on":
+ interruptionFilter = INTERRUPTION_FILTER_NONE;
+ break;
+ case "priority":
+ interruptionFilter = INTERRUPTION_FILTER_PRIORITY;
+ break;
+ case "alarms":
+ interruptionFilter = INTERRUPTION_FILTER_ALARMS;
+ break;
+ case "all":
+ case "off":
+ interruptionFilter = INTERRUPTION_FILTER_ALL;
+ }
+ final int filter = interruptionFilter;
+ mBinderService.setInterruptionFilter(callingPackage, filter);
+ }
+ break;
case "allow_dnd": {
String packageName = getNextArgRequired();
int userId = ActivityManager.getCurrentUser();
@@ -226,7 +269,7 @@ public class NotificationShellCmd extends ShellCommand {
}
case "post":
case "notify":
- doNotify(pw);
+ doNotify(pw, callingPackage, callingUid);
break;
default:
return handleDefaultCommands(cmd);
@@ -238,27 +281,14 @@ public class NotificationShellCmd extends ShellCommand {
return 0;
}
- void ensureChannel() throws RemoteException {
- final int uid = Binder.getCallingUid();
- final int userid = UserHandle.getCallingUserId();
- final long token = Binder.clearCallingIdentity();
- try {
- if (mBinderService.getNotificationChannelForPackage(NOTIFICATION_PACKAGE,
- uid, CHANNEL_ID, false) == null) {
- final NotificationChannel chan = new NotificationChannel(CHANNEL_ID, CHANNEL_NAME,
- CHANNEL_IMP);
- Slog.v(NotificationManagerService.TAG,
- "creating shell channel for user " + userid + " uid " + uid + ": " + chan);
- mBinderService.createNotificationChannelsForPackage(NOTIFICATION_PACKAGE, uid,
- new ParceledListSlice<NotificationChannel>(
- Collections.singletonList(chan)));
- Slog.v(NotificationManagerService.TAG, "created channel: "
- + mBinderService.getNotificationChannelForPackage(NOTIFICATION_PACKAGE,
- uid, CHANNEL_ID, false));
- }
- } finally {
- Binder.restoreCallingIdentity(token);
- }
+ void ensureChannel(String callingPackage, int callingUid) throws RemoteException {
+ final NotificationChannel channel =
+ new NotificationChannel(CHANNEL_ID, CHANNEL_NAME, CHANNEL_IMP);
+ mBinderService.createNotificationChannels(callingPackage,
+ new ParceledListSlice<>(Collections.singletonList(channel)));
+ Slog.v(NotificationManagerService.TAG, "created channel: "
+ + mBinderService.getNotificationChannel(callingPackage,
+ UserHandle.getUserId(callingUid), callingPackage, CHANNEL_ID));
}
Icon parseIcon(Resources res, String encoded) throws IllegalArgumentException {
@@ -287,7 +317,8 @@ public class NotificationShellCmd extends ShellCommand {
return null;
}
- private int doNotify(PrintWriter pw) throws RemoteException, URISyntaxException {
+ private int doNotify(PrintWriter pw, String callingPackage, int callingUid)
+ throws RemoteException, URISyntaxException {
final Context context = mDirectService.getContext();
final Resources res = context.getResources();
final Notification.Builder builder = new Notification.Builder(context, CHANNEL_ID);
@@ -481,26 +512,18 @@ public class NotificationShellCmd extends ShellCommand {
builder.setSmallIcon(smallIcon);
}
- ensureChannel();
+ ensureChannel(callingPackage, callingUid);
final Notification n = builder.build();
pw.println("posting:\n " + n);
Slog.v("NotificationManager", "posting: " + n);
- final int userId = UserHandle.getCallingUserId();
- final long token = Binder.clearCallingIdentity();
- try {
- mBinderService.enqueueNotificationWithTag(
- NOTIFICATION_PACKAGE, "android",
- tag, NOTIFICATION_ID,
- n, userId);
- } finally {
- Binder.restoreCallingIdentity(token);
- }
+ mBinderService.enqueueNotificationWithTag(callingPackage, callingPackage, tag,
+ NOTIFICATION_ID, n, UserHandle.getUserId(callingUid));
if (verbose) {
NotificationRecord nr = mDirectService.findNotificationLocked(
- NOTIFICATION_PACKAGE, tag, NOTIFICATION_ID, userId);
+ callingPackage, tag, NOTIFICATION_ID, UserHandle.getUserId(callingUid));
for (int tries = 3; tries-- > 0; ) {
if (nr != null) break;
try {
@@ -509,7 +532,7 @@ public class NotificationShellCmd extends ShellCommand {
} catch (InterruptedException e) {
}
nr = mDirectService.findNotificationLocked(
- NOTIFICATION_PACKAGE, tag, NOTIFICATION_ID, userId);
+ callingPackage, tag, NOTIFICATION_ID, UserHandle.getUserId(callingUid));
}
if (nr == null) {
pw.println("warning: couldn't find notification after enqueueing");
diff --git a/services/core/java/com/android/server/pm/PackageInstallerSession.java b/services/core/java/com/android/server/pm/PackageInstallerSession.java
index 4eddb9301a69..7091c7ca7316 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerSession.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerSession.java
@@ -2209,8 +2209,10 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
final boolean success = (returnCode == PackageManager.INSTALL_SUCCEEDED);
// Send broadcast to default launcher only if it's a new install
+ // TODO(b/144270665): Secure the usage of this broadcast.
final boolean isNewInstall = extras == null || !extras.getBoolean(Intent.EXTRA_REPLACING);
- if (success && isNewInstall && mPm.mInstallerService.okToSendBroadcasts()) {
+ if (success && isNewInstall && mPm.mInstallerService.okToSendBroadcasts()
+ && (params.installFlags & PackageManager.INSTALL_DRY_RUN) == 0) {
mPm.sendSessionCommitBroadcast(generateInfo(), userId);
}
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index cec5c6d75014..65c5236cb8ff 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -23119,6 +23119,13 @@ public class PackageManagerService extends IPackageManager.Stub
}
}
+ boolean readPermissionStateForUser(@UserIdInt int userId) {
+ synchronized (mPackages) {
+ mSettings.readPermissionStateForUserSyncLPr(userId);
+ return mSettings.areDefaultRuntimePermissionsGrantedLPr(userId);
+ }
+ }
+
@Override
public VerifierDeviceIdentity getVerifierDeviceIdentity() throws RemoteException {
mContext.enforceCallingOrSelfPermission(
diff --git a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
index e991a91fe8ba..85bf4d94cd3c 100644
--- a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
+++ b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
@@ -2376,6 +2376,7 @@ class PackageManagerShellCommand extends ShellCommand {
int userId = -1;
int flags = 0;
String opt;
+ boolean preCreateOnly = false;
while ((opt = getNextOption()) != null) {
if ("--profileOf".equals(opt)) {
userId = UserHandle.parseUserArg(getNextArgRequired());
@@ -2389,16 +2390,22 @@ class PackageManagerShellCommand extends ShellCommand {
flags |= UserInfo.FLAG_GUEST;
} else if ("--demo".equals(opt)) {
flags |= UserInfo.FLAG_DEMO;
+ } else if ("--pre-create-only".equals(opt)) {
+ preCreateOnly = true;
} else {
getErrPrintWriter().println("Error: unknown option " + opt);
return 1;
}
}
String arg = getNextArg();
- if (arg == null) {
+ if (arg == null && !preCreateOnly) {
getErrPrintWriter().println("Error: no user name specified.");
return 1;
}
+ if (arg != null && preCreateOnly) {
+ getErrPrintWriter().println("Warning: name is ignored for pre-created users");
+ }
+
name = arg;
UserInfo info;
IUserManager um = IUserManager.Stub.asInterface(
@@ -2412,7 +2419,7 @@ class PackageManagerShellCommand extends ShellCommand {
accm.addSharedAccountsFromParentUser(parentUserId, userId,
(Process.myUid() == Process.ROOT_UID) ? "root" : "com.android.shell");
} else if (userId < 0) {
- info = um.createUser(name, flags);
+ info = preCreateOnly ? um.preCreateUser(flags) : um.createUser(name, flags);
} else {
info = um.createProfileForUser(name, flags, userId, null);
}
@@ -3307,8 +3314,11 @@ class PackageManagerShellCommand extends ShellCommand {
pw.println(" trim-caches DESIRED_FREE_SPACE [internal|UUID]");
pw.println(" Trim cache files to reach the given free space.");
pw.println("");
+ pw.println(" list users");
+ pw.println(" Lists the current users.");
+ pw.println("");
pw.println(" create-user [--profileOf USER_ID] [--managed] [--restricted] [--ephemeral]");
- pw.println(" [--guest] USER_NAME");
+ pw.println(" [--guest] [--pre-create-only] USER_NAME");
pw.println(" Create a new user with the given USER_NAME, printing the new user identifier");
pw.println(" of the user.");
pw.println("");
diff --git a/services/core/java/com/android/server/pm/Settings.java b/services/core/java/com/android/server/pm/Settings.java
index d1e4537b9235..994fca80e672 100644
--- a/services/core/java/com/android/server/pm/Settings.java
+++ b/services/core/java/com/android/server/pm/Settings.java
@@ -3140,6 +3140,10 @@ public final class Settings {
return true;
}
+ void readPermissionStateForUserSyncLPr(@UserIdInt int userId) {
+ mRuntimePermissionsPersistence.readStateForUserSyncLPr(userId);
+ }
+
void applyDefaultPreferredAppsLPw(int userId) {
// First pull data from any pre-installed apps.
final PackageManagerInternal pmInternal =
diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java
index 31960e47ffce..cc42f6215899 100644
--- a/services/core/java/com/android/server/pm/UserManagerService.java
+++ b/services/core/java/com/android/server/pm/UserManagerService.java
@@ -41,6 +41,7 @@ import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
import android.content.pm.ShortcutServiceInternal;
import android.content.pm.UserInfo;
+import android.content.pm.UserInfo.UserInfoFlag;
import android.content.res.Resources;
import android.graphics.Bitmap;
import android.os.Binder;
@@ -66,6 +67,7 @@ import android.os.ShellCallback;
import android.os.ShellCommand;
import android.os.SystemClock;
import android.os.SystemProperties;
+import android.os.Trace;
import android.os.UserHandle;
import android.os.UserManager;
import android.os.UserManager.EnforcingUser;
@@ -83,6 +85,7 @@ import android.util.SparseArray;
import android.util.SparseBooleanArray;
import android.util.SparseIntArray;
import android.util.TimeUtils;
+import android.util.TimingsTraceLog;
import android.util.Xml;
import com.android.internal.annotations.GuardedBy;
@@ -155,6 +158,7 @@ public class UserManagerService extends IUserManager.Stub {
private static final String ATTR_SERIAL_NO = "serialNumber";
private static final String ATTR_NEXT_SERIAL_NO = "nextSerialNumber";
private static final String ATTR_PARTIAL = "partial";
+ private static final String ATTR_PRE_CREATED = "preCreated";
private static final String ATTR_GUEST_TO_REMOVE = "guestToRemove";
private static final String ATTR_USER_VERSION = "version";
private static final String ATTR_PROFILE_GROUP_ID = "profileGroupId";
@@ -475,6 +479,10 @@ public class UserManagerService extends IUserManager.Stub {
public void onBootPhase(int phase) {
if (phase == SystemService.PHASE_ACTIVITY_MANAGER_READY) {
mUms.cleanupPartialUsers();
+
+ if (mUms.mPm.isDeviceUpgrading()) {
+ mUms.cleanupPreCreatedUsers();
+ }
}
}
@@ -582,7 +590,8 @@ public class UserManagerService extends IUserManager.Stub {
final int userSize = mUsers.size();
for (int i = 0; i < userSize; i++) {
UserInfo ui = mUsers.valueAt(i).info;
- if ((ui.partial || ui.guestToRemove || ui.isEphemeral()) && i != 0) {
+ if ((ui.partial || ui.guestToRemove || (ui.isEphemeral() && !ui.preCreated))
+ && i != 0) {
partials.add(ui);
addRemovingUserIdLocked(ui.id);
ui.partial = true;
@@ -598,6 +607,33 @@ public class UserManagerService extends IUserManager.Stub {
}
}
+ /**
+ * Removes any pre-created users from the system. Should be invoked after OTAs, to ensure
+ * pre-created users are not stale. New pre-created pool can be re-created after the update.
+ */
+ void cleanupPreCreatedUsers() {
+ final ArrayList<UserInfo> preCreatedUsers;
+ synchronized (mUsersLock) {
+ final int userSize = mUsers.size();
+ preCreatedUsers = new ArrayList<>(userSize);
+ for (int i = 0; i < userSize; i++) {
+ UserInfo ui = mUsers.valueAt(i).info;
+ if (ui.preCreated) {
+ preCreatedUsers.add(ui);
+ addRemovingUserIdLocked(ui.id);
+ ui.flags |= UserInfo.FLAG_DISABLED;
+ ui.partial = true;
+ }
+ }
+ }
+ final int preCreatedSize = preCreatedUsers.size();
+ for (int i = 0; i < preCreatedSize; i++) {
+ UserInfo ui = preCreatedUsers.get(i);
+ Slog.i(LOG_TAG, "Removing pre-created user " + ui.id);
+ removeUserState(ui.id);
+ }
+ }
+
@Override
public String getUserAccount(int userId) {
checkManageUserAndAcrossUsersFullPermission("get user account");
@@ -645,20 +681,25 @@ public class UserManagerService extends IUserManager.Stub {
return null;
}
- @Override
public @NonNull List<UserInfo> getUsers(boolean excludeDying) {
+ return getUsers(/*excludePartial= */ true, excludeDying, /* excludePreCreated= */ true);
+ }
+
+ @Override
+ public @NonNull List<UserInfo> getUsers(boolean excludePartial, boolean excludeDying,
+ boolean excludePreCreated) {
checkManageOrCreateUsersPermission("query users");
synchronized (mUsersLock) {
ArrayList<UserInfo> users = new ArrayList<UserInfo>(mUsers.size());
final int userSize = mUsers.size();
for (int i = 0; i < userSize; i++) {
UserInfo ui = mUsers.valueAt(i).info;
- if (ui.partial) {
+ if ((excludePartial && ui.partial)
+ || (excludeDying && mRemovingUserIds.get(ui.id))
+ || (excludePreCreated && ui.preCreated)) {
continue;
}
- if (!excludeDying || !mRemovingUserIds.get(ui.id)) {
- users.add(userWithName(ui));
- }
+ users.add(userWithName(ui));
}
return users;
}
@@ -1179,8 +1220,9 @@ public class UserManagerService extends IUserManager.Stub {
}
}
- private void checkManageOrInteractPermIfCallerInOtherProfileGroup(int userId, String name) {
- int callingUserId = UserHandle.getCallingUserId();
+ private void checkManageOrInteractPermIfCallerInOtherProfileGroup(@UserIdInt int userId,
+ String name) {
+ final int callingUserId = UserHandle.getCallingUserId();
if (callingUserId == userId || isSameProfileGroupNoChecks(callingUserId, userId) ||
hasManageUsersPermission()) {
return;
@@ -1193,8 +1235,8 @@ public class UserManagerService extends IUserManager.Stub {
}
@Override
- public boolean isDemoUser(int userId) {
- int callingUserId = UserHandle.getCallingUserId();
+ public boolean isDemoUser(@UserIdInt int userId) {
+ final int callingUserId = UserHandle.getCallingUserId();
if (callingUserId != userId && !hasManageUsersPermission()) {
throw new SecurityException("You need MANAGE_USERS permission to query if u=" + userId
+ " is a demo user");
@@ -1206,6 +1248,19 @@ public class UserManagerService extends IUserManager.Stub {
}
@Override
+ public boolean isPreCreated(@UserIdInt int userId) {
+ final int callingUserId = UserHandle.getCallingUserId();
+ if (callingUserId != userId && !hasManageUsersPermission()) {
+ throw new SecurityException("You need MANAGE_USERS permission to query if u=" + userId
+ + " is pre-created");
+ }
+ synchronized (mUsersLock) {
+ UserInfo userInfo = getUserInfoLU(userId);
+ return userInfo != null && userInfo.preCreated;
+ }
+ }
+
+ @Override
public boolean isRestricted() {
synchronized (mUsersLock) {
return getUserInfoLU(UserHandle.getCallingUserId()).isRestricted();
@@ -1859,7 +1914,7 @@ public class UserManagerService extends IUserManager.Stub {
// Skip over users being removed
for (int i = 0; i < totalUserCount; i++) {
UserInfo user = mUsers.valueAt(i).info;
- if (!mRemovingUserIds.get(user.id) && !user.isGuest()) {
+ if (!mRemovingUserIds.get(user.id) && !user.isGuest() && !user.preCreated) {
aliveUserCount++;
}
}
@@ -2320,6 +2375,9 @@ public class UserManagerService extends IUserManager.Stub {
if (userInfo.partial) {
serializer.attribute(null, ATTR_PARTIAL, "true");
}
+ if (userInfo.preCreated) {
+ serializer.attribute(null, ATTR_PRE_CREATED, "true");
+ }
if (userInfo.guestToRemove) {
serializer.attribute(null, ATTR_GUEST_TO_REMOVE, "true");
}
@@ -2476,6 +2534,7 @@ public class UserManagerService extends IUserManager.Stub {
int profileBadge = 0;
int restrictedProfileParentId = UserInfo.NO_PROFILE_GROUP_ID;
boolean partial = false;
+ boolean preCreated = false;
boolean guestToRemove = false;
boolean persistSeedData = false;
String seedAccountName = null;
@@ -2520,6 +2579,10 @@ public class UserManagerService extends IUserManager.Stub {
if ("true".equals(valueString)) {
partial = true;
}
+ valueString = parser.getAttributeValue(null, ATTR_PRE_CREATED);
+ if ("true".equals(valueString)) {
+ preCreated = true;
+ }
valueString = parser.getAttributeValue(null, ATTR_GUEST_TO_REMOVE);
if ("true".equals(valueString)) {
guestToRemove = true;
@@ -2573,6 +2636,7 @@ public class UserManagerService extends IUserManager.Stub {
userInfo.lastLoggedInTime = lastLoggedInTime;
userInfo.lastLoggedInFingerprint = lastLoggedInFingerprint;
userInfo.partial = partial;
+ userInfo.preCreated = preCreated;
userInfo.guestToRemove = guestToRemove;
userInfo.profileGroupId = profileGroupId;
userInfo.profileBadge = profileBadge;
@@ -2644,7 +2708,8 @@ public class UserManagerService extends IUserManager.Stub {
public UserInfo createProfileForUserEvenWhenDisallowed(String name, int flags, int userId,
String[] disallowedPackages) {
checkManageOrCreateUsersPermission(flags);
- return createUserInternalUnchecked(name, flags, userId, disallowedPackages);
+ return createUserInternalUnchecked(name, flags, userId, /* preCreate= */ false,
+ disallowedPackages);
}
@Override
@@ -2659,12 +2724,27 @@ public class UserManagerService extends IUserManager.Stub {
return createUserInternal(name, flags, UserHandle.USER_NULL);
}
- private UserInfo createUserInternal(String name, int flags, int parentId) {
+ @Override
+ public UserInfo preCreateUser(int flags) {
+ checkManageOrCreateUsersPermission(flags);
+
+ Preconditions.checkArgument(!UserInfo.isManagedProfile(flags),
+ "cannot pre-create managed profiles");
+
+ Slog.i(LOG_TAG, "Pre-creating user with flags " + UserInfo.flagsToString(flags));
+
+ return createUserInternalUnchecked(/* name= */ null, flags,
+ /* parentId= */ UserHandle.USER_NULL, /* preCreate= */ true,
+ /* disallowedPackages= */ null);
+ }
+
+ private UserInfo createUserInternal(@Nullable String name, @UserInfoFlag int flags,
+ @UserIdInt int parentId) {
return createUserInternal(name, flags, parentId, null);
}
- private UserInfo createUserInternal(String name, int flags, int parentId,
- String[] disallowedPackages) {
+ private UserInfo createUserInternal(@Nullable String name, @UserInfoFlag int flags,
+ @UserIdInt int parentId, @Nullable String[] disallowedPackages) {
String restriction = ((flags & UserInfo.FLAG_MANAGED_PROFILE) != 0)
? UserManager.DISALLOW_ADD_MANAGED_PROFILE
: UserManager.DISALLOW_ADD_USER;
@@ -2672,19 +2752,80 @@ public class UserManagerService extends IUserManager.Stub {
Log.w(LOG_TAG, "Cannot add user. " + restriction + " is enabled.");
return null;
}
- return createUserInternalUnchecked(name, flags, parentId, disallowedPackages);
+ return createUserInternalUnchecked(name, flags, parentId, /* preCreate= */ false,
+ disallowedPackages);
}
- private UserInfo createUserInternalUnchecked(String name, int flags, int parentId,
- String[] disallowedPackages) {
+ private UserInfo createUserInternalUnchecked(@Nullable String name, @UserInfoFlag int flags,
+ @UserIdInt int parentId, boolean preCreate,
+ @Nullable String[] disallowedPackages) {
+ final TimingsTraceLog t = new TimingsTraceLog(LOG_TAG, Trace.TRACE_TAG_SYSTEM_SERVER);
+ t.traceBegin("createUser-" + flags);
+ try {
+ return createUserInternalUncheckedNoTracing(name, flags, parentId, preCreate,
+ disallowedPackages, t);
+ } finally {
+ t.traceEnd();
+ }
+ }
+
+ private UserInfo createUserInternalUncheckedNoTracing(@Nullable String name,
+ @UserInfoFlag int flags, @UserIdInt int parentId, boolean preCreate,
+ @Nullable String[] disallowedPackages, @NonNull TimingsTraceLog t) {
+
+ // First try to use a pre-created user (if available).
+ // NOTE: currently we don't support pre-created managed profiles
+ if (!preCreate && (parentId < 0 && !UserInfo.isManagedProfile(flags))) {
+ final UserData preCreatedUserData;
+ synchronized (mUsersLock) {
+ preCreatedUserData = getPreCreatedUserLU(flags);
+ }
+ if (preCreatedUserData != null) {
+ final UserInfo preCreatedUser = preCreatedUserData.info;
+ if (UserInfo.isGuest(flags) && areGuestUsersEphemeral()) {
+ // TODO(b/143092698): this pre-created user has (persisted) storage keys
+ // that will be removed when the user is stopped and ideally we should
+ // remove them from storage right now, but that's not possible with the
+ // current StorageManager APIs (there are just a
+ // createUserKey(userId, serial, isEphemeral) and destroyUserKey(userId)
+ // methods; we would need a makeUserKeyEphemeral(userId) method)
+ preCreatedUserData.info.flags |= UserInfo.FLAG_EPHEMERAL;
+ }
+ Log.i(LOG_TAG, "Reusing pre-created user " + preCreatedUser.id + " for flags "
+ + UserInfo.flagsToString(flags) + "; new flags: "
+ + UserInfo.flagsToString(preCreatedUserData.info.flags));
+ if (DBG) {
+ Log.d(LOG_TAG, "pre-created user flags: "
+ + UserInfo.flagsToString(preCreatedUser.flags)
+ + " new-user flags: " + UserInfo.flagsToString(flags));
+ }
+ preCreatedUser.name = name;
+ preCreatedUser.preCreated = false;
+ preCreatedUser.creationTime = getCreationTime();
+
+ synchronized (mPackagesLock) {
+ writeUserLP(preCreatedUserData);
+ writeUserListLP();
+ }
+
+ updateUserIds();
+ if (!mPm.readPermissionStateForUser(preCreatedUser.id)) {
+ // Could not read the existing permissions, re-grant them.
+ mPm.onNewUserCreated(preCreatedUser.id);
+ }
+ dispatchUserAddedIntent(preCreatedUser);
+ return preCreatedUser;
+ }
+ }
+
DeviceStorageMonitorInternal dsm = LocalServices
.getService(DeviceStorageMonitorInternal.class);
if (dsm.isMemoryLow()) {
Log.w(LOG_TAG, "Cannot add user. Not enough space on disk.");
return null;
}
- final boolean isGuest = (flags & UserInfo.FLAG_GUEST) != 0;
- final boolean isManagedProfile = (flags & UserInfo.FLAG_MANAGED_PROFILE) != 0;
+ final boolean isGuest = UserInfo.isGuest(flags);
+ final boolean isManagedProfile = UserInfo.isManagedProfile(flags);
final boolean isRestricted = (flags & UserInfo.FLAG_RESTRICTED) != 0;
final boolean isDemo = (flags & UserInfo.FLAG_DEMO) != 0;
final long ident = Binder.clearCallingIdentity();
@@ -2705,13 +2846,13 @@ public class UserManagerService extends IUserManager.Stub {
return null;
}
if (!isGuest && !isManagedProfile && !isDemo && isUserLimitReached()) {
- // If we're not adding a guest/demo user or a managed profile and the limit has
- // been reached, cannot add a user.
+ // If we're not adding a guest/demo user or a managed profile,
+ // and the limit has been reached, cannot add a user.
Log.e(LOG_TAG, "Cannot add user. Maximum user limit is reached.");
return null;
}
// If we're adding a guest and there already exists one, bail.
- if (isGuest && findCurrentGuestUser() != null) {
+ if (isGuest && !preCreate && findCurrentGuestUser() != null) {
Log.e(LOG_TAG, "Cannot add guest user. Guest user already exists.");
return null;
}
@@ -2747,21 +2888,21 @@ public class UserManagerService extends IUserManager.Stub {
userId = getNextAvailableId();
Environment.getUserSystemDirectory(userId).mkdirs();
- boolean ephemeralGuests = Resources.getSystem()
- .getBoolean(com.android.internal.R.bool.config_guestUserEphemeral);
+ boolean ephemeralGuests = areGuestUsersEphemeral();
synchronized (mUsersLock) {
// Add ephemeral flag to guests/users if required. Also inherit it from parent.
- if ((isGuest && ephemeralGuests) || mForceEphemeralUsers
- || (parent != null && parent.info.isEphemeral())) {
+ if (!preCreate && ((isGuest && ephemeralGuests)
+ || mForceEphemeralUsers
+ || (parent != null && parent.info.isEphemeral()))) {
flags |= UserInfo.FLAG_EPHEMERAL;
}
userInfo = new UserInfo(userId, name, null, flags);
userInfo.serialNumber = mNextSerialNumber++;
- long now = System.currentTimeMillis();
- userInfo.creationTime = (now > EPOCH_PLUS_30_YEARS) ? now : 0;
+ userInfo.creationTime = getCreationTime();
userInfo.partial = true;
+ userInfo.preCreated = preCreate;
userInfo.lastLoggedInFingerprint = Build.FINGERPRINT;
if (isManagedProfile && parentId != UserHandle.USER_NULL) {
userInfo.profileBadge = getFreeProfileBadgeLU(parentId);
@@ -2807,19 +2948,95 @@ public class UserManagerService extends IUserManager.Stub {
synchronized (mRestrictionsLock) {
mBaseUserRestrictions.append(userId, restrictions);
}
+
+ t.traceBegin("PM.onNewUserCreated-" + userId);
mPm.onNewUserCreated(userId);
- Intent addedIntent = new Intent(Intent.ACTION_USER_ADDED);
- addedIntent.putExtra(Intent.EXTRA_USER_HANDLE, userId);
- mContext.sendBroadcastAsUser(addedIntent, UserHandle.ALL,
- android.Manifest.permission.MANAGE_USERS);
- MetricsLogger.count(mContext, isGuest ? TRON_GUEST_CREATED
- : (isDemo ? TRON_DEMO_CREATED : TRON_USER_CREATED), 1);
+ t.traceEnd();
+ if (preCreate) {
+ // Must start user (which will be stopped right away, through
+ // UserController.finishUserUnlockedCompleted) so services can properly
+ // intialize it.
+ // TODO(b/140750212): in the long-term, we should add a onCreateUser() callback
+ // on SystemService instead.
+ Slog.i(LOG_TAG, "starting pre-created user " + userInfo.toFullString());
+ final IActivityManager am = ActivityManager.getService();
+ try {
+ am.startUserInBackground(userId);
+ } catch (RemoteException e) {
+ Slog.w(LOG_TAG, "could not start pre-created user " + userId, e);
+ }
+ } else {
+ dispatchUserAddedIntent(userInfo);
+ }
+
} finally {
Binder.restoreCallingIdentity(ident);
}
+
+ // TODO(b/140750212): it's possible to reach "max users overflow" when the user is created
+ // "from scratch" (i.e., not from a pre-created user) and reaches the maximum number of
+ // users without counting the pre-created one. Then when the pre-created is converted, the
+ // "effective" number of max users is exceeds. Example:
+ // Max: 3 Current: 2 full (u0 and u10) + 1 pre-created (u11)
+ // Step 1: create(/* flags doesn't match u11 */): u12 is created, "effective max" is now 3
+ // (u0, u10, u12) but "real" max is 4 (u0, u10, u11, u12)
+ // Step 2: create(/* flags match u11 */): u11 is converted, now "effective max" is also 4
+ // (u0, u10, u11, u12)
+ // One way to avoid this issue is by removing a pre-created user from the pool when the
+ // "real" max exceeds the max here.
+
return userInfo;
}
+ private long getCreationTime() {
+ final long now = System.currentTimeMillis();
+ return (now > EPOCH_PLUS_30_YEARS) ? now : 0;
+ }
+
+ private void dispatchUserAddedIntent(@NonNull UserInfo userInfo) {
+ Intent addedIntent = new Intent(Intent.ACTION_USER_ADDED);
+ addedIntent.putExtra(Intent.EXTRA_USER_HANDLE, userInfo.id);
+ mContext.sendBroadcastAsUser(addedIntent, UserHandle.ALL,
+ android.Manifest.permission.MANAGE_USERS);
+ MetricsLogger.count(mContext, userInfo.isGuest() ? TRON_GUEST_CREATED
+ : (userInfo.isDemo() ? TRON_DEMO_CREATED : TRON_USER_CREATED), 1);
+ }
+
+ private boolean areGuestUsersEphemeral() {
+ return Resources.getSystem()
+ .getBoolean(com.android.internal.R.bool.config_guestUserEphemeral);
+ }
+
+ /**
+ * Gets a pre-created user for the given flag.
+ *
+ * <p>Should be used only during user creation, so the pre-created user can be used (instead of
+ * creating and initializing a new user from scratch).
+ */
+ // TODO(b/140750212): add unit test
+ @GuardedBy("mUsersLock")
+ private @Nullable UserData getPreCreatedUserLU(@UserInfoFlag int flags) {
+ if (DBG) {
+ Slog.d(LOG_TAG, "getPreCreatedUser(): flags= " + UserInfo.flagsToString(flags));
+ }
+ final int userSize = mUsers.size();
+ for (int i = 0; i < userSize; i++) {
+ final UserData user = mUsers.valueAt(i);
+ if (DBG) Slog.d(LOG_TAG, i + ":" + user.info.toFullString());
+ if (user.info.preCreated
+ && (user.info.flags & ~UserInfo.FLAG_INITIALIZED) == flags) {
+ if (!user.info.isInitialized()) {
+ Slog.w(LOG_TAG, "found pre-created user for flags "
+ + "" + UserInfo.flagsToString(flags)
+ + ", but it's not initialized yet: " + user.info.toFullString());
+ continue;
+ }
+ return user;
+ }
+ }
+ return null;
+ }
+
@VisibleForTesting
UserData putUserInfo(UserInfo userInfo) {
final UserData userData = new UserData();
@@ -2872,7 +3089,8 @@ public class UserManagerService extends IUserManager.Stub {
final int size = mUsers.size();
for (int i = 0; i < size; i++) {
final UserInfo user = mUsers.valueAt(i).info;
- if (user.isGuest() && !user.guestToRemove && !mRemovingUserIds.get(user.id)) {
+ if (user.isGuest() && !user.guestToRemove && !user.preCreated
+ && !mRemovingUserIds.get(user.id)) {
return user;
}
}
@@ -3426,14 +3644,16 @@ public class UserManagerService extends IUserManager.Stub {
synchronized (mUsersLock) {
final int userSize = mUsers.size();
for (int i = 0; i < userSize; i++) {
- if (!mUsers.valueAt(i).info.partial) {
+ UserInfo userInfo = mUsers.valueAt(i).info;
+ if (!userInfo.partial && !userInfo.preCreated) {
num++;
}
}
final int[] newUsers = new int[num];
int n = 0;
for (int i = 0; i < userSize; i++) {
- if (!mUsers.valueAt(i).info.partial) {
+ UserInfo userInfo = mUsers.valueAt(i).info;
+ if (!userInfo.partial && !userInfo.preCreated) {
newUsers[n++] = mUsers.keyAt(i);
}
}
@@ -3485,7 +3705,10 @@ public class UserManagerService extends IUserManager.Stub {
* recycled.
*/
void reconcileUsers(String volumeUuid) {
- mUserDataPreparer.reconcileUsers(volumeUuid, getUsers(true /* excludeDying */));
+ mUserDataPreparer.reconcileUsers(volumeUuid, getUsers(
+ /* excludePartial= */ true,
+ /* excludeDying= */ true,
+ /* excludePreCreated= */ false));
}
/**
@@ -3651,7 +3874,7 @@ public class UserManagerService extends IUserManager.Stub {
try {
switch(cmd) {
case "list":
- return runList(pw);
+ return runList(pw, shell);
default:
return shell.handleDefaultCommands(cmd);
}
@@ -3661,17 +3884,58 @@ public class UserManagerService extends IUserManager.Stub {
return -1;
}
- private int runList(PrintWriter pw) throws RemoteException {
+ private int runList(PrintWriter pw, Shell shell) throws RemoteException {
+ boolean all = false;
+ boolean verbose = false;
+ String opt;
+ while ((opt = shell.getNextOption()) != null) {
+ switch (opt) {
+ case "-v":
+ verbose = true;
+ break;
+ case "--all":
+ all = true;
+ break;
+ default:
+ pw.println("Invalid option: " + opt);
+ return -1;
+ }
+ }
final IActivityManager am = ActivityManager.getService();
- final List<UserInfo> users = getUsers(false);
+ final List<UserInfo> users = getUsers(/* excludePartial= */ !all,
+ /* excludingDying=*/ false, /* excludePreCreated= */ !all);
if (users == null) {
pw.println("Error: couldn't get users");
return 1;
} else {
- pw.println("Users:");
- for (int i = 0; i < users.size(); i++) {
- String running = am.isUserRunning(users.get(i).id, 0) ? " running" : "";
- pw.println("\t" + users.get(i).toString() + running);
+ final int size = users.size();
+ int currentUser = UserHandle.USER_NULL;
+ if (verbose) {
+ pw.printf("%d users:\n\n", size);
+ currentUser = am.getCurrentUser().id;
+ } else {
+ // NOTE: the standard "list users" command is used by integration tests and
+ // hence should not be changed. If you need to add more info, use the
+ // verbose option.
+ pw.println("Users:");
+ }
+ for (int i = 0; i < size; i++) {
+ final UserInfo user = users.get(i);
+ final boolean running = am.isUserRunning(user.id, 0);
+ final boolean current = user.id == currentUser;
+ if (verbose) {
+ pw.printf("%d: id=%d, name=%s, flags=%s%s%s%s%s\n", i, user.id, user.name,
+ UserInfo.flagsToString(user.flags),
+ running ? " (running)" : "",
+ user.partial ? " (partial)" : "",
+ user.preCreated ? " (pre-created)" : "",
+ current ? " (current)" : "");
+ } else {
+ // NOTE: the standard "list users" command is used by integration tests and
+ // hence should not be changed. If you need to add more info, use the
+ // verbose option.
+ pw.printf("\t%s%s\n", user, running ? " running" : "");
+ }
}
return 0;
}
@@ -3683,6 +3947,16 @@ public class UserManagerService extends IUserManager.Stub {
long now = System.currentTimeMillis();
final long nowRealtime = SystemClock.elapsedRealtime();
+
+ final ActivityManagerInternal amInternal = LocalServices
+ .getService(ActivityManagerInternal.class);
+ pw.print("Current user: ");
+ if (amInternal != null) {
+ pw.println(amInternal.getCurrentUserId());
+ } else {
+ pw.println("N/A");
+ }
+
StringBuilder sb = new StringBuilder();
synchronized (mPackagesLock) {
synchronized (mUsersLock) {
@@ -3696,13 +3970,19 @@ public class UserManagerService extends IUserManager.Stub {
final int userId = userInfo.id;
pw.print(" "); pw.print(userInfo);
pw.print(" serialNo="); pw.print(userInfo.serialNumber);
+ pw.print(" isPrimary="); pw.print(userInfo.isPrimary());
if (mRemovingUserIds.get(userId)) {
pw.print(" <removing> ");
}
if (userInfo.partial) {
pw.print(" <partial>");
}
+ if (userInfo.preCreated) {
+ pw.print(" <pre-created>");
+ }
pw.println();
+ pw.print(" Flags: "); pw.print(userInfo.flags); pw.print(" (");
+ pw.print(UserInfo.flagsToString(userInfo.flags)); pw.println(")");
pw.print(" State: ");
final int state;
synchronized (mUserStates) {
@@ -3778,13 +4058,16 @@ public class UserManagerService extends IUserManager.Stub {
synchronized (mUserStates) {
pw.println(" Started users state: " + mUserStates);
}
- // Dump some capabilities
- pw.println();
- pw.println(" Max users: " + UserManager.getMaxSupportedUsers());
- pw.println(" Supports switchable users: " + UserManager.supportsMultipleUsers());
- pw.println(" All guests ephemeral: " + Resources.getSystem().getBoolean(
- com.android.internal.R.bool.config_guestUserEphemeral));
- }
+ } // synchronized (mPackagesLock)
+
+ // Dump some capabilities
+ pw.println();
+ pw.print(" Max users: " + UserManager.getMaxSupportedUsers());
+ pw.println(" (limit reached: " + isUserLimitReached() + ")");
+ pw.println(" Supports switchable users: " + UserManager.supportsMultipleUsers());
+ pw.println(" All guests ephemeral: " + areGuestUsersEphemeral());
+ pw.println(" Is split-system user: " + UserManager.isSplitSystemUser());
+ pw.println(" User version: " + mUserVersion);
}
private static void dumpTimeAgo(PrintWriter pw, StringBuilder sb, long nowTime, long time) {
@@ -3969,7 +4252,7 @@ public class UserManagerService extends IUserManager.Stub {
public UserInfo createUserEvenWhenDisallowed(String name, int flags,
String[] disallowedPackages) {
UserInfo user = createUserInternalUnchecked(name, flags, UserHandle.USER_NULL,
- disallowedPackages);
+ /* preCreated= */ false, disallowedPackages);
// Keep this in sync with UserManager.createUser
if (user != null && !user.isAdmin() && !user.isDemo()) {
setUserRestriction(UserManager.DISALLOW_SMS, true, user.id);
@@ -4134,7 +4417,7 @@ public class UserManagerService extends IUserManager.Stub {
pw.println(" help");
pw.println(" Print this help text.");
pw.println("");
- pw.println(" list");
+ pw.println(" list [-v] [-all]");
pw.println(" Prints all users on the system.");
}
}
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index 48056b47e1bd..72706169cacb 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -23,6 +23,7 @@ import static android.app.AppOpsManager.OP_TOAST_WINDOW;
import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
import static android.content.Context.CONTEXT_RESTRICTED;
import static android.content.Context.WINDOW_SERVICE;
+import static android.content.pm.PackageManager.FEATURE_AUTOMOTIVE;
import static android.content.pm.PackageManager.FEATURE_HDMI_CEC;
import static android.content.pm.PackageManager.FEATURE_LEANBACK;
import static android.content.pm.PackageManager.FEATURE_PICTURE_IN_PICTURE;
@@ -384,6 +385,7 @@ public class PhoneWindowManager implements WindowManagerPolicy {
BurnInProtectionHelper mBurnInProtectionHelper;
private DisplayFoldController mDisplayFoldController;
AppOpsManager mAppOpsManager;
+ private boolean mHasFeatureAuto;
private boolean mHasFeatureWatch;
private boolean mHasFeatureLeanback;
private boolean mHasFeatureHdmiCec;
@@ -1752,6 +1754,7 @@ public class PhoneWindowManager implements WindowManagerPolicy {
mDisplayManager = mContext.getSystemService(DisplayManager.class);
mHasFeatureWatch = mContext.getPackageManager().hasSystemFeature(FEATURE_WATCH);
mHasFeatureLeanback = mContext.getPackageManager().hasSystemFeature(FEATURE_LEANBACK);
+ mHasFeatureAuto = mContext.getPackageManager().hasSystemFeature(FEATURE_AUTOMOTIVE);
mHasFeatureHdmiCec = mContext.getPackageManager().hasSystemFeature(FEATURE_HDMI_CEC);
mAccessibilityShortcutController =
new AccessibilityShortcutController(mContext, new Handler(), mCurrentUserId);
@@ -5215,7 +5218,7 @@ public class PhoneWindowManager implements WindowManagerPolicy {
awakenDreams();
}
- if (!isUserSetupComplete()) {
+ if (!mHasFeatureAuto && !isUserSetupComplete()) {
Slog.i(TAG, "Not going home because user setup is in progress.");
return;
}
diff --git a/services/core/java/com/android/server/wallpaper/GLHelper.java b/services/core/java/com/android/server/wallpaper/GLHelper.java
new file mode 100644
index 000000000000..1d733f53f055
--- /dev/null
+++ b/services/core/java/com/android/server/wallpaper/GLHelper.java
@@ -0,0 +1,148 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.wallpaper;
+
+import static android.opengl.EGL14.EGL_ALPHA_SIZE;
+import static android.opengl.EGL14.EGL_BLUE_SIZE;
+import static android.opengl.EGL14.EGL_CONFIG_CAVEAT;
+import static android.opengl.EGL14.EGL_CONTEXT_CLIENT_VERSION;
+import static android.opengl.EGL14.EGL_DEFAULT_DISPLAY;
+import static android.opengl.EGL14.EGL_DEPTH_SIZE;
+import static android.opengl.EGL14.EGL_GREEN_SIZE;
+import static android.opengl.EGL14.EGL_HEIGHT;
+import static android.opengl.EGL14.EGL_NONE;
+import static android.opengl.EGL14.EGL_NO_CONTEXT;
+import static android.opengl.EGL14.EGL_NO_DISPLAY;
+import static android.opengl.EGL14.EGL_NO_SURFACE;
+import static android.opengl.EGL14.EGL_OPENGL_ES2_BIT;
+import static android.opengl.EGL14.EGL_RED_SIZE;
+import static android.opengl.EGL14.EGL_RENDERABLE_TYPE;
+import static android.opengl.EGL14.EGL_STENCIL_SIZE;
+import static android.opengl.EGL14.EGL_WIDTH;
+import static android.opengl.EGL14.eglChooseConfig;
+import static android.opengl.EGL14.eglCreateContext;
+import static android.opengl.EGL14.eglCreatePbufferSurface;
+import static android.opengl.EGL14.eglDestroyContext;
+import static android.opengl.EGL14.eglDestroySurface;
+import static android.opengl.EGL14.eglGetDisplay;
+import static android.opengl.EGL14.eglGetError;
+import static android.opengl.EGL14.eglInitialize;
+import static android.opengl.EGL14.eglMakeCurrent;
+import static android.opengl.EGL14.eglTerminate;
+import static android.opengl.GLES20.GL_MAX_TEXTURE_SIZE;
+import static android.opengl.GLES20.glGetIntegerv;
+
+import android.opengl.EGLConfig;
+import android.opengl.EGLContext;
+import android.opengl.EGLDisplay;
+import android.opengl.EGLSurface;
+import android.opengl.GLUtils;
+import android.os.SystemProperties;
+import android.util.Log;
+
+class GLHelper {
+ private static final String TAG = GLHelper.class.getSimpleName();
+ private static final int sMaxTextureSize;
+
+ static {
+ int maxTextureSize = SystemProperties.getInt("sys.max_texture_size", 0);
+ sMaxTextureSize = maxTextureSize > 0 ? maxTextureSize : retrieveTextureSizeFromGL();
+ }
+
+ private static int retrieveTextureSizeFromGL() {
+ try {
+ String err;
+
+ // Before we can retrieve info from GL,
+ // we have to create EGLContext, EGLConfig and EGLDisplay first.
+ // We will fail at querying info from GL once one of above failed.
+ // When this happens, we will use defValue instead.
+ EGLDisplay eglDisplay = eglGetDisplay(EGL_DEFAULT_DISPLAY);
+ if (eglDisplay == null || eglDisplay == EGL_NO_DISPLAY) {
+ err = "eglGetDisplay failed: " + GLUtils.getEGLErrorString(eglGetError());
+ throw new RuntimeException(err);
+ }
+
+ if (!eglInitialize(eglDisplay, null, 0 /* majorOffset */, null, 1 /* minorOffset */)) {
+ err = "eglInitialize failed: " + GLUtils.getEGLErrorString(eglGetError());
+ throw new RuntimeException(err);
+ }
+
+ EGLConfig eglConfig = null;
+ int[] configsCount = new int[1];
+ EGLConfig[] configs = new EGLConfig[1];
+ int[] configSpec = new int[] {
+ EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
+ EGL_RED_SIZE, 8,
+ EGL_GREEN_SIZE, 8,
+ EGL_BLUE_SIZE, 8,
+ EGL_ALPHA_SIZE, 0,
+ EGL_DEPTH_SIZE, 0,
+ EGL_STENCIL_SIZE, 0,
+ EGL_CONFIG_CAVEAT, EGL_NONE,
+ EGL_NONE
+ };
+
+ if (!eglChooseConfig(eglDisplay, configSpec, 0 /* attrib_listOffset */,
+ configs, 0 /* configOffset */, 1 /* config_size */,
+ configsCount, 0 /* num_configOffset */)) {
+ err = "eglChooseConfig failed: " + GLUtils.getEGLErrorString(eglGetError());
+ throw new RuntimeException(err);
+ } else if (configsCount[0] > 0) {
+ eglConfig = configs[0];
+ }
+
+ if (eglConfig == null) {
+ throw new RuntimeException("eglConfig not initialized!");
+ }
+
+ int[] attr_list = new int[] {EGL_CONTEXT_CLIENT_VERSION, 2, EGL_NONE};
+ EGLContext eglContext = eglCreateContext(
+ eglDisplay, eglConfig, EGL_NO_CONTEXT, attr_list, 0 /* offset */);
+
+ if (eglContext == null || eglContext == EGL_NO_CONTEXT) {
+ err = "eglCreateContext failed: " + GLUtils.getEGLErrorString(eglGetError());
+ throw new RuntimeException(err);
+ }
+
+ // We create a push buffer temporarily for querying info from GL.
+ int[] attrs = {EGL_WIDTH, 1, EGL_HEIGHT, 1, EGL_NONE};
+ EGLSurface eglSurface =
+ eglCreatePbufferSurface(eglDisplay, eglConfig, attrs, 0 /* offset */);
+ eglMakeCurrent(eglDisplay, eglSurface, eglSurface, eglContext);
+
+ // Now, we are ready to query the info from GL.
+ int[] maxSize = new int[1];
+ glGetIntegerv(GL_MAX_TEXTURE_SIZE, maxSize, 0 /* offset */);
+
+ // We have got the info we want, release all egl resources.
+ eglMakeCurrent(eglDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
+ eglDestroySurface(eglDisplay, eglSurface);
+ eglDestroyContext(eglDisplay, eglContext);
+ eglTerminate(eglDisplay);
+ return maxSize[0];
+ } catch (RuntimeException e) {
+ Log.w(TAG, "Retrieve from GL failed", e);
+ return Integer.MAX_VALUE;
+ }
+ }
+
+ static int getMaxTextureSize() {
+ return sMaxTextureSize;
+ }
+}
+
diff --git a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
index b0f1e5d69be4..4e136af0fdc3 100644
--- a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
+++ b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
@@ -134,6 +134,9 @@ public class WallpaperManagerService extends IWallpaperManager.Stub
private static final boolean DEBUG = false;
private static final boolean DEBUG_LIVE = true;
+ // This 100MB limitation is defined in RecordingCanvas.
+ private static final int MAX_BITMAP_SIZE = 100 * 1024 * 1024;
+
public static class Lifecycle extends SystemService {
private IWallpaperManagerService mService;
@@ -572,7 +575,9 @@ public class WallpaperManagerService extends IWallpaperManager.Stub
// Only generate crop for default display.
final DisplayData wpData = getDisplayDataOrCreate(DEFAULT_DISPLAY);
- Rect cropHint = new Rect(wallpaper.cropHint);
+ final Rect cropHint = new Rect(wallpaper.cropHint);
+ final DisplayInfo displayInfo = new DisplayInfo();
+ mDisplayManager.getDisplay(DEFAULT_DISPLAY).getDisplayInfo(displayInfo);
if (DEBUG) {
Slog.v(TAG, "Generating crop for new wallpaper(s): 0x"
@@ -618,12 +623,12 @@ public class WallpaperManagerService extends IWallpaperManager.Stub
}
// scale if the crop height winds up not matching the recommended metrics
- needScale = (wpData.mHeight != cropHint.height());
+ needScale = wpData.mHeight != cropHint.height()
+ || cropHint.height() > GLHelper.getMaxTextureSize()
+ || cropHint.width() > GLHelper.getMaxTextureSize();
//make sure screen aspect ratio is preserved if width is scaled under screen size
if (needScale) {
- final DisplayInfo displayInfo = new DisplayInfo();
- mDisplayManager.getDisplay(DEFAULT_DISPLAY).getDisplayInfo(displayInfo);
final float scaleByHeight = (float) wpData.mHeight / (float) cropHint.height();
final int newWidth = (int) (cropHint.width() * scaleByHeight);
if (newWidth < displayInfo.logicalWidth) {
@@ -644,14 +649,29 @@ public class WallpaperManagerService extends IWallpaperManager.Stub
if (!needCrop && !needScale) {
// Simple case: the nominal crop fits what we want, so we take
// the whole thing and just copy the image file directly.
- if (DEBUG) {
- Slog.v(TAG, "Null crop of new wallpaper; copying");
+
+ // TODO: It is not accurate to estimate bitmap size without decoding it,
+ // may be we can try to remove this optimized way in the future,
+ // that means, we will always go into the 'else' block.
+
+ // This is just a quick estimation, may be smaller than it is.
+ long estimateSize = options.outWidth * options.outHeight * 4;
+
+ // A bitmap over than MAX_BITMAP_SIZE will make drawBitmap() fail.
+ // Please see: RecordingCanvas#throwIfCannotDraw.
+ if (estimateSize < MAX_BITMAP_SIZE) {
+ success = FileUtils.copyFile(wallpaper.wallpaperFile, wallpaper.cropFile);
}
- success = FileUtils.copyFile(wallpaper.wallpaperFile, wallpaper.cropFile);
+
if (!success) {
wallpaper.cropFile.delete();
// TODO: fall back to default wallpaper in this case
}
+
+ if (DEBUG) {
+ Slog.v(TAG, "Null crop of new wallpaper, estimate size="
+ + estimateSize + ", success=" + success);
+ }
} else {
// Fancy case: crop and scale. First, we decode and scale down if appropriate.
FileOutputStream f = null;
@@ -665,49 +685,78 @@ public class WallpaperManagerService extends IWallpaperManager.Stub
// We calculate the largest power-of-two under the actual ratio rather than
// just let the decode take care of it because we also want to remap where the
// cropHint rectangle lies in the decoded [super]rect.
- final BitmapFactory.Options scaler;
final int actualScale = cropHint.height() / wpData.mHeight;
int scale = 1;
- while (2*scale < actualScale) {
+ while (2 * scale <= actualScale) {
scale *= 2;
}
- if (scale > 1) {
- scaler = new BitmapFactory.Options();
- scaler.inSampleSize = scale;
+ options.inSampleSize = scale;
+ options.inJustDecodeBounds = false;
+
+ final Rect estimateCrop = new Rect(cropHint);
+ estimateCrop.scale(1f / options.inSampleSize);
+ final float hRatio = (float) wpData.mHeight / estimateCrop.height();
+ final int destHeight = (int) (estimateCrop.height() * hRatio);
+ final int destWidth = (int) (estimateCrop.width() * hRatio);
+
+ // We estimated an invalid crop, try to adjust the cropHint to get a valid one.
+ if (destWidth > GLHelper.getMaxTextureSize()) {
+ int newHeight = (int) (wpData.mHeight / hRatio);
+ int newWidth = (int) (wpData.mWidth / hRatio);
+
if (DEBUG) {
- Slog.v(TAG, "Downsampling cropped rect with scale " + scale);
+ Slog.v(TAG, "Invalid crop dimensions, trying to adjust.");
}
- } else {
- scaler = null;
+
+ estimateCrop.set(cropHint);
+ estimateCrop.left += (cropHint.width() - newWidth) / 2;
+ estimateCrop.top += (cropHint.height() - newHeight) / 2;
+ estimateCrop.right = estimateCrop.left + newWidth;
+ estimateCrop.bottom = estimateCrop.top + newHeight;
+ cropHint.set(estimateCrop);
+ estimateCrop.scale(1f / options.inSampleSize);
+ }
+
+ // We've got the safe cropHint; now we want to scale it properly to
+ // the desired rectangle.
+ // That's a height-biased operation: make it fit the hinted height.
+ final int safeHeight = (int) (estimateCrop.height() * hRatio);
+ final int safeWidth = (int) (estimateCrop.width() * hRatio);
+
+ if (DEBUG) {
+ Slog.v(TAG, "Decode parameters:");
+ Slog.v(TAG, " cropHint=" + cropHint + ", estimateCrop=" + estimateCrop);
+ Slog.v(TAG, " down sampling=" + options.inSampleSize
+ + ", hRatio=" + hRatio);
+ Slog.v(TAG, " dest=" + destWidth + "x" + destHeight);
+ Slog.v(TAG, " safe=" + safeWidth + "x" + safeHeight);
+ Slog.v(TAG, " maxTextureSize=" + GLHelper.getMaxTextureSize());
}
- Bitmap cropped = decoder.decodeRegion(cropHint, scaler);
+
+ Bitmap cropped = decoder.decodeRegion(cropHint, options);
decoder.recycle();
if (cropped == null) {
Slog.e(TAG, "Could not decode new wallpaper");
} else {
- // We've got the extracted crop; now we want to scale it properly to
- // the desired rectangle. That's a height-biased operation: make it
- // fit the hinted height, and accept whatever width we end up with.
- cropHint.offsetTo(0, 0);
- cropHint.right /= scale; // adjust by downsampling factor
- cropHint.bottom /= scale;
- final float heightR =
- ((float) wpData.mHeight) / ((float) cropHint.height());
- if (DEBUG) {
- Slog.v(TAG, "scale " + heightR + ", extracting " + cropHint);
- }
- final int destWidth = (int)(cropHint.width() * heightR);
+ // We are safe to create final crop with safe dimensions now.
final Bitmap finalCrop = Bitmap.createScaledBitmap(cropped,
- destWidth, wpData.mHeight, true);
+ safeWidth, safeHeight, true);
if (DEBUG) {
Slog.v(TAG, "Final extract:");
Slog.v(TAG, " dims: w=" + wpData.mWidth
+ " h=" + wpData.mHeight);
- Slog.v(TAG, " out: w=" + finalCrop.getWidth()
+ Slog.v(TAG, " out: w=" + finalCrop.getWidth()
+ " h=" + finalCrop.getHeight());
}
+ // A bitmap over than MAX_BITMAP_SIZE will make drawBitmap() fail.
+ // Please see: RecordingCanvas#throwIfCannotDraw.
+ if (finalCrop.getByteCount() > MAX_BITMAP_SIZE) {
+ throw new RuntimeException(
+ "Too large bitmap, limit=" + MAX_BITMAP_SIZE);
+ }
+
f = new FileOutputStream(wallpaper.cropFile);
bos = new BufferedOutputStream(f, 32*1024);
finalCrop.compress(Bitmap.CompressFormat.JPEG, 100, bos);
@@ -1237,6 +1286,7 @@ public class WallpaperManagerService extends IWallpaperManager.Stub
saveSettingsLocked(mWallpaper.userId);
}
FgThread.getHandler().removeCallbacks(mResetRunnable);
+ mContext.getMainThreadHandler().removeCallbacks(this::tryToRebind);
}
}
}
@@ -1279,6 +1329,34 @@ public class WallpaperManagerService extends IWallpaperManager.Stub
}
}
+ private void tryToRebind() {
+ synchronized (mLock) {
+ if (mWallpaper.wallpaperUpdating) {
+ return;
+ }
+ final ComponentName wpService = mWallpaper.wallpaperComponent;
+ // The broadcast of package update could be delayed after service disconnected. Try
+ // to re-bind the service for 10 seconds.
+ if (bindWallpaperComponentLocked(
+ wpService, true, false, mWallpaper, null)) {
+ mWallpaper.connection.scheduleTimeoutLocked();
+ } else if (SystemClock.uptimeMillis() - mWallpaper.lastDiedTime
+ < WALLPAPER_RECONNECT_TIMEOUT_MS) {
+ // Bind fail without timeout, schedule rebind
+ Slog.w(TAG, "Rebind fail! Try again later");
+ mContext.getMainThreadHandler().postDelayed(this::tryToRebind, 1000);
+ } else {
+ // Timeout
+ Slog.w(TAG, "Reverting to built-in wallpaper!");
+ clearWallpaperLocked(true, FLAG_SYSTEM, mWallpaper.userId, null);
+ final String flattened = wpService.flattenToString();
+ EventLog.writeEvent(EventLogTags.WP_WALLPAPER_CRASHED,
+ flattened.substring(0, Math.min(flattened.length(),
+ MAX_WALLPAPER_COMPONENT_LOG_LENGTH)));
+ }
+ }
+ }
+
private void processDisconnect(final ServiceConnection connection) {
synchronized (mLock) {
// The wallpaper disappeared. If this isn't a system-default one, track
@@ -1302,20 +1380,8 @@ public class WallpaperManagerService extends IWallpaperManager.Stub
clearWallpaperLocked(true, FLAG_SYSTEM, mWallpaper.userId, null);
} else {
mWallpaper.lastDiedTime = SystemClock.uptimeMillis();
-
- clearWallpaperComponentLocked(mWallpaper);
- if (bindWallpaperComponentLocked(
- wpService, false, false, mWallpaper, null)) {
- mWallpaper.connection.scheduleTimeoutLocked();
- } else {
- Slog.w(TAG, "Reverting to built-in wallpaper!");
- clearWallpaperLocked(true, FLAG_SYSTEM, mWallpaper.userId, null);
- }
+ tryToRebind();
}
- final String flattened = wpService.flattenToString();
- EventLog.writeEvent(EventLogTags.WP_WALLPAPER_CRASHED,
- flattened.substring(0, Math.min(flattened.length(),
- MAX_WALLPAPER_COMPONENT_LOG_LENGTH)));
}
} else {
if (DEBUG_LIVE) {
@@ -1981,6 +2047,11 @@ public class WallpaperManagerService extends IWallpaperManager.Stub
if (!isWallpaperSupported(callingPackage)) {
return;
}
+
+ // Make sure both width and height are not larger than max texture size.
+ width = Math.min(width, GLHelper.getMaxTextureSize());
+ height = Math.min(height, GLHelper.getMaxTextureSize());
+
synchronized (mLock) {
int userId = UserHandle.getCallingUserId();
WallpaperData wallpaper = getWallpaperSafeLocked(userId, FLAG_SYSTEM);
diff --git a/services/core/java/com/android/server/wm/DisplayPolicy.java b/services/core/java/com/android/server/wm/DisplayPolicy.java
index 99a9db316c63..b37c779fcc67 100644
--- a/services/core/java/com/android/server/wm/DisplayPolicy.java
+++ b/services/core/java/com/android/server/wm/DisplayPolicy.java
@@ -2812,7 +2812,11 @@ public class DisplayPolicy {
mHandler.post(() -> {
final int displayId = getDisplayId();
getStatusBarManagerInternal().onDisplayReady(displayId);
- LocalServices.getService(WallpaperManagerInternal.class).onDisplayReady(displayId);
+ final WallpaperManagerInternal wpMgr = LocalServices
+ .getService(WallpaperManagerInternal.class);
+ if (wpMgr != null) {
+ wpMgr.onDisplayReady(displayId);
+ }
});
}
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index e09e88fa7af7..fceffb4437c2 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -1498,6 +1498,8 @@ public final class SystemServer {
traceBeginAndSlog("StartWallpaperManagerService");
mSystemServiceManager.startService(WALLPAPER_SERVICE_CLASS);
traceEnd();
+ } else {
+ Slog.i(TAG, "Wallpaper service disabled by config");
}
traceBeginAndSlog("StartAudioService");
diff --git a/services/tests/mockingservicestests/src/com/android/server/AlarmManagerServiceTest.java b/services/tests/mockingservicestests/src/com/android/server/AlarmManagerServiceTest.java
index 1e29ed6ba5f3..fafd4e83c542 100644
--- a/services/tests/mockingservicestests/src/com/android/server/AlarmManagerServiceTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/AlarmManagerServiceTest.java
@@ -24,6 +24,7 @@ import static android.app.usage.UsageStatsManager.STANDBY_BUCKET_FREQUENT;
import static android.app.usage.UsageStatsManager.STANDBY_BUCKET_RARE;
import static android.app.usage.UsageStatsManager.STANDBY_BUCKET_WORKING_SET;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.doCallRealMethod;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doNothing;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doThrow;
@@ -35,6 +36,7 @@ import static com.android.dx.mockito.inline.extended.ExtendedMockito.when;
import static com.android.server.AlarmManagerService.ACTIVE_INDEX;
import static com.android.server.AlarmManagerService.AlarmHandler.APP_STANDBY_BUCKET_CHANGED;
import static com.android.server.AlarmManagerService.AlarmHandler.APP_STANDBY_PAROLE_CHANGED;
+import static com.android.server.AlarmManagerService.AlarmHandler.REMOVE_FOR_CANCELED;
import static com.android.server.AlarmManagerService.Constants.KEY_ALLOW_WHILE_IDLE_LONG_TIME;
import static com.android.server.AlarmManagerService.Constants.KEY_ALLOW_WHILE_IDLE_SHORT_TIME;
import static com.android.server.AlarmManagerService.Constants.KEY_ALLOW_WHILE_IDLE_WHITELIST_DURATION;
@@ -79,9 +81,9 @@ import android.provider.Settings;
import android.util.Log;
import android.util.SparseArray;
-import androidx.test.filters.FlakyTest;
import androidx.test.runner.AndroidJUnit4;
+import com.android.dx.mockito.inline.extended.MockedVoidMethod;
import com.android.internal.annotations.GuardedBy;
import org.junit.After;
@@ -166,7 +168,6 @@ public class AlarmManagerServiceTest {
}
public class Injector extends AlarmManagerService.Injector {
- boolean mIsAutomotiveOverride;
Injector(Context context) {
super(context);
@@ -256,6 +257,9 @@ public class AlarmManagerServiceTest {
.when(() -> LocalServices.getService(DeviceIdleController.LocalService.class));
doReturn(mUsageStatsManagerInternal).when(
() -> LocalServices.getService(UsageStatsManagerInternal.class));
+ doCallRealMethod().when((MockedVoidMethod) () ->
+ LocalServices.addService(eq(AlarmManagerInternal.class), any()));
+ doCallRealMethod().when(() -> LocalServices.getService(AlarmManagerInternal.class));
when(mUsageStatsManagerInternal.getAppStandbyBucket(eq(TEST_CALLING_PACKAGE),
eq(UserHandle.getUserId(TEST_CALLING_UID)), anyLong()))
.thenReturn(STANDBY_BUCKET_ACTIVE);
@@ -443,7 +447,6 @@ public class AlarmManagerServiceTest {
assertEquals(expectedTriggerTime, mTestTimer.getElapsed());
}
- @FlakyTest(bugId = 130313408)
@Test
public void testEarliestAlarmSet() {
final PendingIntent pi6 = getNewMockPendingIntent();
@@ -661,11 +664,15 @@ public class AlarmManagerServiceTest {
anyLong())).thenReturn(bucket);
mAppStandbyListener.onAppIdleStateChanged(TEST_CALLING_PACKAGE,
UserHandle.getUserId(TEST_CALLING_UID), false, bucket, 0);
+ assertAndHandleMessageSync(APP_STANDBY_BUCKET_CHANGED);
+ }
+
+ private void assertAndHandleMessageSync(int what) {
final ArgumentCaptor<Message> messageCaptor = ArgumentCaptor.forClass(Message.class);
verify(mService.mHandler, atLeastOnce()).sendMessage(messageCaptor.capture());
final Message lastMessage = messageCaptor.getValue();
assertEquals("Unexpected message send to handler", lastMessage.what,
- APP_STANDBY_BUCKET_CHANGED);
+ what);
mService.mHandler.handleMessage(lastMessage);
}
@@ -725,12 +732,7 @@ public class AlarmManagerServiceTest {
private void assertAndHandleParoleChanged(boolean parole) {
mAppStandbyListener.onParoleStateChanged(parole);
- final ArgumentCaptor<Message> messageCaptor = ArgumentCaptor.forClass(Message.class);
- verify(mService.mHandler, atLeastOnce()).sendMessage(messageCaptor.capture());
- final Message lastMessage = messageCaptor.getValue();
- assertEquals("Unexpected message send to handler", lastMessage.what,
- APP_STANDBY_PAROLE_CHANGED);
- mService.mHandler.handleMessage(lastMessage);
+ assertAndHandleMessageSync(APP_STANDBY_PAROLE_CHANGED);
}
@Test
@@ -1033,12 +1035,13 @@ public class AlarmManagerServiceTest {
}
@Test
- public void alarmCountOnPendingIntentCancel() {
+ public void alarmCountOnRemoveForCanceled() {
+ final AlarmManagerInternal ami = LocalServices.getService(AlarmManagerInternal.class);
final PendingIntent pi = getNewMockPendingIntent();
- setTestAlarm(ELAPSED_REALTIME_WAKEUP, mNowElapsedTest + 123, pi);
- verify(pi).registerCancelListener(mService.mOperationCancelListener);
+ setTestAlarm(ELAPSED_REALTIME, mNowElapsedTest + 12345, pi);
assertEquals(1, mService.mAlarmsPerUid.get(TEST_CALLING_UID));
- mService.mOperationCancelListener.onCancelled(pi);
+ ami.remove(pi);
+ assertAndHandleMessageSync(REMOVE_FOR_CANCELED);
assertEquals(0, mService.mAlarmsPerUid.get(TEST_CALLING_UID));
}
@@ -1047,5 +1050,6 @@ public class AlarmManagerServiceTest {
if (mMockingSession != null) {
mMockingSession.finishMocking();
}
+ LocalServices.removeServiceForTest(AlarmManagerInternal.class);
}
}
diff --git a/services/tests/mockingservicestests/src/com/android/server/DeviceIdleControllerTest.java b/services/tests/mockingservicestests/src/com/android/server/DeviceIdleControllerTest.java
index e51ee947cba1..88273b7bc2cf 100644
--- a/services/tests/mockingservicestests/src/com/android/server/DeviceIdleControllerTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/DeviceIdleControllerTest.java
@@ -17,6 +17,7 @@ package com.android.server;
import static androidx.test.InstrumentationRegistry.getContext;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.doAnswer;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doNothing;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.inOrder;
@@ -31,6 +32,7 @@ import static com.android.server.DeviceIdleController.LIGHT_STATE_INACTIVE;
import static com.android.server.DeviceIdleController.LIGHT_STATE_OVERRIDE;
import static com.android.server.DeviceIdleController.LIGHT_STATE_PRE_IDLE;
import static com.android.server.DeviceIdleController.LIGHT_STATE_WAITING_FOR_NETWORK;
+import static com.android.server.DeviceIdleController.MSG_REPORT_STATIONARY_STATUS;
import static com.android.server.DeviceIdleController.STATE_ACTIVE;
import static com.android.server.DeviceIdleController.STATE_IDLE;
import static com.android.server.DeviceIdleController.STATE_IDLE_MAINTENANCE;
@@ -51,6 +53,7 @@ import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyLong;
import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.ArgumentMatchers.argThat;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.reset;
@@ -70,6 +73,7 @@ import android.net.ConnectivityManager;
import android.net.NetworkInfo;
import android.os.Handler;
import android.os.Looper;
+import android.os.Message;
import android.os.PowerManager;
import android.os.PowerManagerInternal;
import android.os.PowerSaveState;
@@ -85,11 +89,13 @@ import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
-import org.mockito.Answers;
+import org.mockito.ArgumentCaptor;
import org.mockito.InOrder;
import org.mockito.Mock;
import org.mockito.MockitoSession;
+import org.mockito.invocation.InvocationOnMock;
import org.mockito.quality.Strictness;
+import org.mockito.stubbing.Answer;
/**
* Tests for {@link com.android.server.DeviceIdleController}.
@@ -97,6 +103,7 @@ import org.mockito.quality.Strictness;
@RunWith(AndroidJUnit4.class)
public class DeviceIdleControllerTest {
private DeviceIdleController mDeviceIdleController;
+ private DeviceIdleController.MyHandler mHandler;
private AnyMotionDetectorForTest mAnyMotionDetector;
private AppStateTrackerForTest mAppStateTracker;
private DeviceIdleController.Constants mConstants;
@@ -110,8 +117,6 @@ public class DeviceIdleControllerTest {
@Mock
private ContentResolver mContentResolver;
@Mock
- private DeviceIdleController.MyHandler mHandler;
- @Mock
private IActivityManager mIActivityManager;
@Mock
private LocationManager mLocationManager;
@@ -128,6 +133,8 @@ public class DeviceIdleControllerTest {
ConnectivityService connectivityService;
LocationManager locationManager;
ConstraintController constraintController;
+ // Freeze time for testing.
+ long nowElapsed;
InjectorForTest(Context ctx) {
super(ctx);
@@ -155,12 +162,34 @@ public class DeviceIdleControllerTest {
}
@Override
+ long getElapsedRealtime() {
+ return nowElapsed;
+ }
+
+ @Override
LocationManager getLocationManager() {
return locationManager;
}
@Override
DeviceIdleController.MyHandler getHandler(DeviceIdleController controller) {
+ if (mHandler == null) {
+ mHandler = controller.new MyHandler(getContext().getMainLooper());
+ spyOn(mHandler);
+ doNothing().when(mHandler).handleMessage(argThat((message) ->
+ message.what != MSG_REPORT_STATIONARY_STATUS));
+ doAnswer(new Answer<Boolean>() {
+ @Override
+ public Boolean answer(InvocationOnMock invocation) throws Throwable {
+ Message msg = invocation.getArgument(0);
+ mHandler.handleMessage(msg);
+ return true;
+ }
+ }).when(mHandler).sendMessageDelayed(
+ argThat((message) -> message.what == MSG_REPORT_STATIONARY_STATUS),
+ anyLong());
+ }
+
return mHandler;
}
@@ -226,6 +255,19 @@ public class DeviceIdleControllerTest {
}
}
+ private class StationaryListenerForTest implements DeviceIdleController.StationaryListener {
+ boolean motionExpected = false;
+ boolean isStationary = false;
+
+ @Override
+ public void onDeviceStationaryChanged(boolean isStationary) {
+ if (isStationary == motionExpected) {
+ fail("Unexpected device stationary status: " + isStationary);
+ }
+ this.isStationary = isStationary;
+ }
+ }
+
@Before
public void setUp() {
mMockingSession = mockitoSession()
@@ -255,8 +297,6 @@ public class DeviceIdleControllerTest {
doReturn(true).when(mSensorManager).registerListener(any(), any(), anyInt());
mAppStateTracker = new AppStateTrackerForTest(getContext(), Looper.getMainLooper());
mAnyMotionDetector = new AnyMotionDetectorForTest();
- mHandler = mock(DeviceIdleController.MyHandler.class, Answers.RETURNS_DEEP_STUBS);
- doNothing().when(mHandler).handleMessage(any());
mInjector = new InjectorForTest(getContext());
doNothing().when(mContentResolver).registerContentObserver(any(), anyBoolean(), any());
@@ -1607,6 +1647,86 @@ public class DeviceIdleControllerTest {
1.0f, curfactor, delta);
}
+ @Test
+ public void testStationaryDetection_QuickDozeOff() {
+ setQuickDozeEnabled(false);
+ enterDeepState(STATE_IDLE);
+ // Regular progression through states, so time should have increased appropriately.
+ mInjector.nowElapsed += mConstants.IDLE_AFTER_INACTIVE_TIMEOUT + mConstants.SENSING_TIMEOUT
+ + mConstants.LOCATING_TIMEOUT;
+
+ StationaryListenerForTest stationaryListener = new StationaryListenerForTest();
+
+ mDeviceIdleController.registerStationaryListener(stationaryListener);
+
+ // Go to IDLE_MAINTENANCE
+ mDeviceIdleController.stepIdleStateLocked("testing");
+
+ // Back to IDLE
+ mDeviceIdleController.stepIdleStateLocked("testing");
+ assertTrue(stationaryListener.isStationary);
+
+ // Test motion
+ stationaryListener.motionExpected = true;
+ mDeviceIdleController.mMotionListener.onTrigger(null);
+ assertFalse(stationaryListener.isStationary);
+ }
+
+ @Test
+ public void testStationaryDetection_QuickDozeOn() {
+ setAlarmSoon(false);
+ enterDeepState(STATE_QUICK_DOZE_DELAY);
+ mDeviceIdleController.stepIdleStateLocked("testing");
+ verifyStateConditions(STATE_IDLE);
+ // Quick doze progression through states, so time should have increased appropriately.
+ mInjector.nowElapsed += mConstants.QUICK_DOZE_DELAY_TIMEOUT;
+ final ArgumentCaptor<AlarmManager.OnAlarmListener> alarmListener = ArgumentCaptor
+ .forClass(AlarmManager.OnAlarmListener.class);
+ doNothing().when(mAlarmManager).set(anyInt(), anyLong(), eq("DeviceIdleController.motion"),
+ alarmListener.capture(), any());
+
+ StationaryListenerForTest stationaryListener = new StationaryListenerForTest();
+
+ stationaryListener.motionExpected = true;
+ mDeviceIdleController.registerStationaryListener(stationaryListener);
+ assertFalse(stationaryListener.isStationary);
+
+ // Go to IDLE_MAINTENANCE
+ mDeviceIdleController.stepIdleStateLocked("testing");
+
+ mInjector.nowElapsed += mConstants.MOTION_INACTIVE_TIMEOUT / 2;
+
+ // Back to IDLE
+ mDeviceIdleController.stepIdleStateLocked("testing");
+
+ // Now enough time has passed.
+ mInjector.nowElapsed += mConstants.MOTION_INACTIVE_TIMEOUT / 2;
+ stationaryListener.motionExpected = false;
+ alarmListener.getValue().onAlarm();
+ assertTrue(stationaryListener.isStationary);
+
+ stationaryListener.motionExpected = true;
+ mDeviceIdleController.mMotionListener.onSensorChanged(null);
+ assertFalse(stationaryListener.isStationary);
+
+ // Since we're in quick doze, the device shouldn't stop idling.
+ verifyStateConditions(STATE_IDLE);
+
+ // Go to IDLE_MAINTENANCE
+ mDeviceIdleController.stepIdleStateLocked("testing");
+
+ mInjector.nowElapsed += mConstants.MOTION_INACTIVE_TIMEOUT / 2;
+
+ // Back to IDLE
+ mDeviceIdleController.stepIdleStateLocked("testing");
+
+ // Now enough time has passed.
+ mInjector.nowElapsed += mConstants.MOTION_INACTIVE_TIMEOUT / 2;
+ stationaryListener.motionExpected = false;
+ alarmListener.getValue().onAlarm();
+ assertTrue(stationaryListener.isStationary);
+ }
+
private void enterDeepState(int state) {
switch (state) {
case STATE_ACTIVE:
diff --git a/services/tests/mockingservicestests/src/com/android/server/am/PendingIntentControllerTest.java b/services/tests/mockingservicestests/src/com/android/server/am/PendingIntentControllerTest.java
new file mode 100644
index 000000000000..3975f0baa22a
--- /dev/null
+++ b/services/tests/mockingservicestests/src/com/android/server/am/PendingIntentControllerTest.java
@@ -0,0 +1,130 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.am;
+
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
+
+import static org.junit.Assert.assertEquals;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.when;
+
+import android.app.ActivityManager;
+import android.app.ActivityManagerInternal;
+import android.app.AppGlobals;
+import android.app.PendingIntent;
+import android.content.Intent;
+import android.content.pm.IPackageManager;
+import android.os.Looper;
+
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.server.AlarmManagerInternal;
+import com.android.server.LocalServices;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mock;
+import org.mockito.MockitoSession;
+import org.mockito.quality.Strictness;
+
+@RunWith(AndroidJUnit4.class)
+public class PendingIntentControllerTest {
+ private static final String TEST_PACKAGE_NAME = "test-package-1";
+ private static final int TEST_CALLING_UID = android.os.Process.myUid();
+ private static final Intent[] TEST_INTENTS = new Intent[]{new Intent("com.test.intent")};
+
+ @Mock
+ private UserController mUserController;
+ @Mock
+ private AlarmManagerInternal mAlarmManagerInternal;
+ @Mock
+ private ActivityManagerInternal mActivityManagerInternal;
+ @Mock
+ private IPackageManager mIPackageManager;
+
+ private MockitoSession mMockingSession;
+ private PendingIntentController mPendingIntentController;
+
+ @Before
+ public void setUp() throws Exception {
+ mMockingSession = mockitoSession()
+ .initMocks(this)
+ .mockStatic(LocalServices.class)
+ .mockStatic(AppGlobals.class)
+ .strictness(Strictness.LENIENT) // Needed to stub LocalServices.getService twice
+ .startMocking();
+ doReturn(mAlarmManagerInternal).when(
+ () -> LocalServices.getService(AlarmManagerInternal.class));
+ doReturn(mActivityManagerInternal).when(
+ () -> LocalServices.getService(ActivityManagerInternal.class));
+ doReturn(mIPackageManager).when(() -> AppGlobals.getPackageManager());
+ when(mIPackageManager.getPackageUid(eq(TEST_PACKAGE_NAME), anyInt(), anyInt())).thenReturn(
+ TEST_CALLING_UID);
+ mPendingIntentController = new PendingIntentController(Looper.getMainLooper(),
+ mUserController);
+ mPendingIntentController.onActivityManagerInternalAdded();
+ }
+
+ private PendingIntentRecord createPendingIntentRecord(int flags) {
+ return mPendingIntentController.getIntentSender(ActivityManager.INTENT_SENDER_BROADCAST,
+ TEST_PACKAGE_NAME, TEST_CALLING_UID, 0, null, null, 0, TEST_INTENTS, null, flags,
+ null);
+ }
+
+ @Test
+ public void alarmsRemovedOnCancel() {
+ final PendingIntentRecord pir = createPendingIntentRecord(0);
+ mPendingIntentController.cancelIntentSender(pir);
+ final ArgumentCaptor<PendingIntent> piCaptor = ArgumentCaptor.forClass(PendingIntent.class);
+ verify(mAlarmManagerInternal).remove(piCaptor.capture());
+ assertEquals("Wrong target for pending intent passed to alarm manager", pir,
+ piCaptor.getValue().getTarget());
+ }
+
+ @Test
+ public void alarmsRemovedOnRecreateWithCancelCurrent() {
+ final PendingIntentRecord pir = createPendingIntentRecord(0);
+ createPendingIntentRecord(PendingIntent.FLAG_CANCEL_CURRENT);
+ final ArgumentCaptor<PendingIntent> piCaptor = ArgumentCaptor.forClass(PendingIntent.class);
+ verify(mAlarmManagerInternal).remove(piCaptor.capture());
+ assertEquals("Wrong target for pending intent passed to alarm manager", pir,
+ piCaptor.getValue().getTarget());
+ }
+
+ @Test
+ public void alarmsRemovedOnSendingOneShot() {
+ final PendingIntentRecord pir = createPendingIntentRecord(PendingIntent.FLAG_ONE_SHOT);
+ pir.send(0, null, null, null, null, null, null);
+ final ArgumentCaptor<PendingIntent> piCaptor = ArgumentCaptor.forClass(PendingIntent.class);
+ verify(mAlarmManagerInternal).remove(piCaptor.capture());
+ assertEquals("Wrong target for pending intent passed to alarm manager", pir,
+ piCaptor.getValue().getTarget());
+ }
+
+ @After
+ public void tearDown() {
+ if (mMockingSession != null) {
+ mMockingSession.finishMocking();
+ }
+ }
+}
diff --git a/services/tests/servicestests/Android.bp b/services/tests/servicestests/Android.bp
index eecdeed2cd25..f1ceb3ca1673 100644
--- a/services/tests/servicestests/Android.bp
+++ b/services/tests/servicestests/Android.bp
@@ -22,6 +22,7 @@ android_test {
"services.appwidget",
"services.autofill",
"services.backup",
+ "services.contentsuggestions",
"services.core",
"services.devicepolicy",
"services.net",
diff --git a/services/tests/servicestests/src/com/android/server/am/UserControllerTest.java b/services/tests/servicestests/src/com/android/server/am/UserControllerTest.java
index 231025c61c8a..a7c943e0a2ac 100644
--- a/services/tests/servicestests/src/com/android/server/am/UserControllerTest.java
+++ b/services/tests/servicestests/src/com/android/server/am/UserControllerTest.java
@@ -25,8 +25,8 @@ import static com.android.server.am.UserController.CONTINUE_USER_SWITCH_MSG;
import static com.android.server.am.UserController.REPORT_LOCKED_BOOT_COMPLETE_MSG;
import static com.android.server.am.UserController.REPORT_USER_SWITCH_COMPLETE_MSG;
import static com.android.server.am.UserController.REPORT_USER_SWITCH_MSG;
-import static com.android.server.am.UserController.SYSTEM_USER_CURRENT_MSG;
-import static com.android.server.am.UserController.SYSTEM_USER_START_MSG;
+import static com.android.server.am.UserController.USER_CURRENT_MSG;
+import static com.android.server.am.UserController.USER_START_MSG;
import static com.android.server.am.UserController.USER_SWITCH_TIMEOUT_MSG;
import static com.google.android.collect.Lists.newArrayList;
@@ -53,11 +53,13 @@ import static org.mockito.Mockito.validateMockitoUsage;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
+import android.annotation.UserIdInt;
import android.app.IUserSwitchObserver;
import android.content.Context;
import android.content.IIntentReceiver;
import android.content.Intent;
import android.content.pm.UserInfo;
+import android.content.pm.UserInfo.UserInfoFlag;
import android.os.Binder;
import android.os.Bundle;
import android.os.Handler;
@@ -107,6 +109,10 @@ public class UserControllerTest {
private static final int TEST_USER_ID1 = 101;
private static final int TEST_USER_ID2 = 102;
private static final int NONEXIST_USER_ID = 2;
+ private static final int TEST_PRE_CREATED_USER_ID = 103;
+
+ private static final int NO_USERINFO_FLAGS = 0;
+
private static final String TAG = UserControllerTest.class.getSimpleName();
private static final long HANDLER_WAIT_TIME_MS = 100;
@@ -128,11 +134,11 @@ public class UserControllerTest {
private static final Set<Integer> START_FOREGROUND_USER_MESSAGE_CODES = newHashSet(
REPORT_USER_SWITCH_MSG,
USER_SWITCH_TIMEOUT_MSG,
- SYSTEM_USER_START_MSG,
- SYSTEM_USER_CURRENT_MSG);
+ USER_START_MSG,
+ USER_CURRENT_MSG);
private static final Set<Integer> START_BACKGROUND_USER_MESSAGE_CODES = newHashSet(
- SYSTEM_USER_START_MSG,
+ USER_START_MSG,
REPORT_LOCKED_BOOT_COMPLETE_MSG);
@Before
@@ -149,7 +155,8 @@ public class UserControllerTest {
doNothing().when(mInjector).clearBroadcastQueueForUser(anyInt());
doNothing().when(mInjector).stackSupervisorRemoveUser(anyInt());
mUserController = new UserController(mInjector);
- setUpUser(TEST_USER_ID, 0);
+ setUpUser(TEST_USER_ID, NO_USERINFO_FLAGS);
+ setUpUser(TEST_PRE_CREATED_USER_ID, NO_USERINFO_FLAGS, /* preCreated=*/ true);
});
}
@@ -190,6 +197,31 @@ public class UserControllerTest {
startForegroundUserAssertions();
}
+ @Test
+ public void testStartPreCreatedUser_foreground() {
+ assertFalse(mUserController.startUser(TEST_PRE_CREATED_USER_ID, /* foreground= */ true));
+ }
+
+ @Test
+ public void testStartPreCreatedUser_background() throws Exception {
+ assertTrue(mUserController.startUser(TEST_PRE_CREATED_USER_ID, /* foreground= */ false));
+
+ verify(mInjector.getWindowManager(), never()).startFreezingScreen(anyInt(), anyInt());
+ verify(mInjector.getWindowManager(), never()).setSwitchingUser(anyBoolean());
+ verify(mInjector, never()).clearAllLockedTasks(anyString());
+
+ assertWithMessage("should not have received intents")
+ .that(getActions(mInjector.mSentIntents)).isEmpty();
+ // TODO(b/140868593): should have received a USER_UNLOCK_MSG message as well, but it doesn't
+ // because StorageManager.isUserKeyUnlocked(TEST_PRE_CREATED_USER_ID) returns false - to
+ // properly fix it, we'd need to move this class to FrameworksMockingServicesTests so we can
+ // mock static methods (but moving this class would involve changing the presubmit tests,
+ // and the cascade effect goes on...). In fact, a better approach would to not assert the
+ // binder calls, but their side effects (in this case, that the user is stopped right away)
+ assertWithMessage("wrong binder message calls").that(mInjector.mHandler.getMessageCodes())
+ .containsExactly(USER_START_MSG);
+ }
+
private void startUserAssertions(
List<String> expectedActions, Set<Integer> expectedMessageCodes) {
assertEquals(expectedActions, getActions(mInjector.mSentIntents));
@@ -469,9 +501,15 @@ public class UserControllerTest {
continueUserSwitchAssertions(newUserId, expectOldUserStopping);
}
- private void setUpUser(int userId, int flags) {
+ private void setUpUser(@UserIdInt int userId, @UserInfoFlag int flags) {
+ setUpUser(userId, flags, /* preCreated= */ false);
+ }
+
+ private void setUpUser(@UserIdInt int userId, @UserInfoFlag int flags, boolean preCreated) {
UserInfo userInfo = new UserInfo(userId, "User" + userId, flags);
+ userInfo.preCreated = preCreated;
when(mInjector.mUserManagerMock.getUserInfo(eq(userId))).thenReturn(userInfo);
+ when(mInjector.mUserManagerMock.isPreCreated(userId)).thenReturn(preCreated);
}
private static List<String> getActions(List<Intent> intents) {
diff --git a/services/tests/servicestests/src/com/android/server/contentsuggestions/ContentSuggestionsPerUserServiceTest.java b/services/tests/servicestests/src/com/android/server/contentsuggestions/ContentSuggestionsPerUserServiceTest.java
new file mode 100644
index 000000000000..80cf6ad6d88e
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/contentsuggestions/ContentSuggestionsPerUserServiceTest.java
@@ -0,0 +1,90 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.contentsuggestions;
+
+import static androidx.test.InstrumentationRegistry.getContext;
+
+import static org.mockito.ArgumentMatchers.anyBoolean;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+
+import android.app.contentsuggestions.ContentSuggestionsManager;
+import android.graphics.Bitmap;
+import android.os.Bundle;
+import android.os.UserManagerInternal;
+import android.testing.AndroidTestingRunner;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.server.LocalServices;
+import com.android.server.wm.ActivityTaskManagerInternal;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@SmallTest
+@RunWith(AndroidTestingRunner.class)
+public class ContentSuggestionsPerUserServiceTest {
+ private int mUserId;
+ private ContentSuggestionsPerUserService mPerUserService;
+ private ActivityTaskManagerInternal mActivityTaskManagerInternal;
+
+ @Before
+ public void setup() {
+ UserManagerInternal umi = mock(UserManagerInternal.class);
+ LocalServices.removeServiceForTest(UserManagerInternal.class);
+ LocalServices.addService(UserManagerInternal.class, umi);
+
+ mActivityTaskManagerInternal = mock(ActivityTaskManagerInternal.class);
+ LocalServices.removeServiceForTest(ActivityTaskManagerInternal.class);
+ LocalServices.addService(ActivityTaskManagerInternal.class, mActivityTaskManagerInternal);
+
+ ContentSuggestionsManagerService contentSuggestionsManagerService =
+ new ContentSuggestionsManagerService(getContext());
+ mUserId = 1;
+ mPerUserService = new ContentSuggestionsPerUserService(contentSuggestionsManagerService,
+ new Object(),
+ mUserId);
+ }
+
+ // Tests TaskSnapshot is taken when the key ContentSuggestionsManager.EXTRA_BITMAP is missing
+ // from imageContextRequestExtras provided.
+ @Test
+ public void testProvideContextImageLocked_noBitmapInBundle() {
+ Bundle imageContextRequestExtras = Bundle.EMPTY;
+ mPerUserService.provideContextImageLocked(mUserId, imageContextRequestExtras);
+ verify(mActivityTaskManagerInternal, times(1)).getTaskSnapshotNoRestore(anyInt(),
+ anyBoolean());
+ }
+
+ // Tests TaskSnapshot is not taken when the key ContentSuggestionsManager.EXTRA_BITMAP is
+ // provided in imageContextRequestExtras.
+ @Test
+ public void testProvideContextImageLocked_bitmapInBundle() {
+ Bundle imageContextRequestExtras = new Bundle();
+ imageContextRequestExtras.putParcelable(ContentSuggestionsManager.EXTRA_BITMAP,
+ Bitmap.createBitmap(100, 100, Bitmap.Config.ARGB_8888));
+ mPerUserService.provideContextImageLocked(mUserId, imageContextRequestExtras);
+ verify(mActivityTaskManagerInternal, times(0))
+ .getTaskSnapshotNoRestore(anyInt(), anyBoolean());
+ }
+}
+
+
diff --git a/services/tests/servicestests/src/com/android/server/display/BrightnessTrackerTest.java b/services/tests/servicestests/src/com/android/server/display/BrightnessTrackerTest.java
index 7081d2e3b370..75fac7c62213 100644
--- a/services/tests/servicestests/src/com/android/server/display/BrightnessTrackerTest.java
+++ b/services/tests/servicestests/src/com/android/server/display/BrightnessTrackerTest.java
@@ -120,7 +120,7 @@ public class BrightnessTrackerTest {
assertTrue(mInjector.mIdleScheduled);
mInjector.sendScreenChange(/*screen on */ true);
assertNotNull(mInjector.mSensorListener);
- assertTrue(mInjector.mColorSamplingEnabled);
+ assertEquals(BrightnessTracker.ENABLE_COLOR_SAMPLING, mInjector.mColorSamplingEnabled);
mInjector.sendScreenChange(/*screen on */ false);
assertNull(mInjector.mSensorListener);
@@ -141,7 +141,7 @@ public class BrightnessTrackerTest {
// Turn on screen while brightness mode is automatic.
mInjector.sendScreenChange(/*screen on */ true);
assertNotNull(mInjector.mSensorListener);
- assertTrue(mInjector.mColorSamplingEnabled);
+ assertEquals(BrightnessTracker.ENABLE_COLOR_SAMPLING, mInjector.mColorSamplingEnabled);
mTracker.stop();
assertNull(mInjector.mSensorListener);
@@ -184,6 +184,9 @@ public class BrightnessTrackerTest {
@Test
public void testColorSampling_FrameRateChange() {
+ if (!BrightnessTracker.ENABLE_COLOR_SAMPLING) {
+ return;
+ }
startTracker(mTracker);
assertTrue(mInjector.mColorSamplingEnabled);
assertNotNull(mInjector.mDisplayListener);
@@ -211,8 +214,10 @@ public class BrightnessTrackerTest {
mInjector.setBrightnessMode(/*isBrightnessModeAutomatic*/ true);
assertNotNull(mInjector.mSensorListener);
- assertTrue(mInjector.mColorSamplingEnabled);
- assertNotNull(mInjector.mDisplayListener);
+ assertEquals(BrightnessTracker.ENABLE_COLOR_SAMPLING, mInjector.mColorSamplingEnabled);
+ if (BrightnessTracker.ENABLE_COLOR_SAMPLING) {
+ assertNotNull(mInjector.mDisplayListener);
+ }
SensorEventListener listener = mInjector.mSensorListener;
DisplayManager.DisplayListener displayListener = mInjector.mDisplayListener;
@@ -226,8 +231,10 @@ public class BrightnessTrackerTest {
assertFalse(mInjector.mColorSamplingEnabled);
assertNull(mInjector.mDisplayListener);
mInjector.mSensorListener = listener;
- mInjector.mDisplayListener = displayListener;
- mInjector.mColorSamplingEnabled = true;
+ if (BrightnessTracker.ENABLE_COLOR_SAMPLING) {
+ mInjector.mDisplayListener = displayListener;
+ mInjector.mColorSamplingEnabled = true;
+ }
mInjector.setBrightnessMode(/*isBrightnessModeAutomatic*/ false);
assertNull(mInjector.mSensorListener);
@@ -301,8 +308,11 @@ public class BrightnessTrackerTest {
assertEquals(3333, event.colorTemperature);
assertEquals("a.package", event.packageName);
assertEquals(0, event.userId);
- assertArrayEquals(new long[] {1, 10, 100, 1000, 300, 30, 10, 1}, event.colorValueBuckets);
- assertEquals(10000, event.colorSampleDuration);
+ if (BrightnessTracker.ENABLE_COLOR_SAMPLING) {
+ assertArrayEquals(new long[]{1, 10, 100, 1000, 300, 30, 10, 1},
+ event.colorValueBuckets);
+ assertEquals(10000, event.colorSampleDuration);
+ }
assertEquals(1, eventsNoPackage.size());
assertNull(eventsNoPackage.get(0).packageName);
@@ -559,8 +569,11 @@ public class BrightnessTrackerTest {
assertEquals(0.5f, event.powerBrightnessFactor, FLOAT_DELTA);
assertTrue(event.isUserSetBrightness);
assertFalse(event.isDefaultBrightnessConfig);
- assertArrayEquals(new long[] {1, 10, 100, 1000, 300, 30, 10, 1}, event.colorValueBuckets);
- assertEquals(10000, event.colorSampleDuration);
+ if (BrightnessTracker.ENABLE_COLOR_SAMPLING) {
+ assertArrayEquals(new long[]{1, 10, 100, 1000, 300, 30, 10, 1},
+ event.colorValueBuckets);
+ assertEquals(10000, event.colorSampleDuration);
+ }
}
@Test
diff --git a/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceUserInfoTest.java b/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceUserInfoTest.java
index 806c71a7a9b8..6d5b994a63bb 100644
--- a/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceUserInfoTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceUserInfoTest.java
@@ -132,6 +132,7 @@ public class UserManagerServiceUserInfoTest {
user.profileBadge = 2;
user.partial = true;
user.guestToRemove = true;
+ user.preCreated = true;
return user;
}
@@ -147,5 +148,6 @@ public class UserManagerServiceUserInfoTest {
assertEquals("profile badge not preseved", one.profileBadge, two.profileBadge);
assertEquals("partial not preseved", one.partial, two.partial);
assertEquals("guestToRemove not preseved", one.guestToRemove, two.guestToRemove);
+ assertEquals("preCreated not preseved", one.preCreated, two.preCreated);
}
}
diff --git a/services/tests/servicestests/src/com/android/server/usage/AppIdleHistoryTests.java b/services/tests/servicestests/src/com/android/server/usage/AppIdleHistoryTests.java
index 36504ac7ec65..4a13dce5642b 100644
--- a/services/tests/servicestests/src/com/android/server/usage/AppIdleHistoryTests.java
+++ b/services/tests/servicestests/src/com/android/server/usage/AppIdleHistoryTests.java
@@ -28,7 +28,6 @@ import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
-import android.app.usage.UsageStatsManager;
import android.os.FileUtils;
import android.test.AndroidTestCase;
@@ -150,4 +149,21 @@ public class AppIdleHistoryTests extends AndroidTestCase {
aih = new AppIdleHistory(mStorageDir, 5000);
assertEquals(REASON_MAIN_TIMEOUT, aih.getAppStandbyReason(PACKAGE_1, USER_ID, 5000));
}
+
+ public void testNullPackage() throws Exception {
+ AppIdleHistory aih = new AppIdleHistory(mStorageDir, 1000);
+ // Report usage of a package
+ aih.reportUsage(PACKAGE_1, USER_ID, STANDBY_BUCKET_ACTIVE,
+ REASON_SUB_USAGE_MOVE_TO_FOREGROUND, 2000, 0);
+ // "Accidentally" report usage against a null named package
+ aih.reportUsage(null, USER_ID, STANDBY_BUCKET_ACTIVE,
+ REASON_SUB_USAGE_MOVE_TO_FOREGROUND, 2000, 0);
+ // Persist data
+ aih.writeAppIdleTimes(USER_ID);
+ // Recover data from disk
+ aih = new AppIdleHistory(mStorageDir, 5000);
+ // Verify data is intact
+ assertEquals(REASON_MAIN_USAGE | REASON_SUB_USAGE_MOVE_TO_FOREGROUND,
+ aih.getAppStandbyReason(PACKAGE_1, USER_ID, 3000));
+ }
} \ No newline at end of file
diff --git a/services/tests/uiservicestests/src/com/android/server/UiModeManagerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/UiModeManagerServiceTest.java
new file mode 100644
index 000000000000..338f837b9b44
--- /dev/null
+++ b/services/tests/uiservicestests/src/com/android/server/UiModeManagerServiceTest.java
@@ -0,0 +1,161 @@
+/*
+ * Copyright (C) 20019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server;
+
+import android.app.IUiModeManager;
+import android.content.BroadcastReceiver;
+import android.content.ContentResolver;
+import android.content.Context;
+import android.content.pm.PackageManager;
+import android.content.res.Configuration;
+import android.content.res.Resources;
+import android.os.PowerManager;
+import android.os.RemoteException;
+import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper;
+import com.android.server.twilight.TwilightManager;
+import com.android.server.wm.WindowManagerInternal;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+
+import java.util.HashSet;
+import java.util.Set;
+
+import static android.app.UiModeManager.MODE_NIGHT_AUTO;
+import static android.app.UiModeManager.MODE_NIGHT_NO;
+import static android.app.UiModeManager.MODE_NIGHT_YES;
+import static junit.framework.TestCase.assertFalse;
+import static junit.framework.TestCase.assertTrue;
+import static org.junit.Assert.assertEquals;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.BDDMockito.given;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+@RunWith(AndroidTestingRunner.class)
+@TestableLooper.RunWithLooper
+public class UiModeManagerServiceTest extends UiServiceTestCase {
+ private UiModeManagerService mUiManagerService;
+ private IUiModeManager mService;
+ @Mock
+ private ContentResolver mContentResolver;
+ @Mock
+ private WindowManagerInternal mWindowManager;
+ @Mock
+ private Context mContext;
+ @Mock
+ private Resources mResources;
+ @Mock
+ TwilightManager mTwilightManager;
+ @Mock
+ PowerManager.WakeLock mWakeLock;
+ private Set<BroadcastReceiver> mScreenOffRecievers;
+
+ @Before
+ public void setUp() {
+ mUiManagerService = new UiModeManagerService(mContext, mWindowManager, mWakeLock,
+ mTwilightManager, true);
+ mScreenOffRecievers = new HashSet<>();
+ mService = mUiManagerService.getService();
+ when(mContext.checkCallingOrSelfPermission(anyString()))
+ .thenReturn(PackageManager.PERMISSION_GRANTED);
+ when(mContext.getResources()).thenReturn(mResources);
+ when(mContext.getContentResolver()).thenReturn(mContentResolver);
+ when(mContext.registerReceiver(any(), any())).then(inv -> {
+ mScreenOffRecievers.add(inv.getArgument(0));
+ return null;
+ });
+ }
+
+ @Test
+ public void setAutoMode_screenOffRegistered() throws RemoteException {
+ try {
+ mService.setNightMode(MODE_NIGHT_NO);
+ } catch (SecurityException e) { /* we should ignore this update config exception*/ }
+ mService.setNightMode(MODE_NIGHT_AUTO);
+ verify(mContext).registerReceiver(any(BroadcastReceiver.class), any());
+ }
+
+ @Test
+ public void setAutoMode_screenOffUnRegistered() throws RemoteException {
+ try {
+ mService.setNightMode(MODE_NIGHT_AUTO);
+ } catch (SecurityException e) { /* we should ignore this update config exception*/ }
+ try {
+ mService.setNightMode(MODE_NIGHT_NO);
+ } catch (SecurityException e) { /*we should ignore this update config exception*/ }
+ given(mContext.registerReceiver(any(), any())).willThrow(SecurityException.class);
+ verify(mContext).unregisterReceiver(any(BroadcastReceiver.class));
+ }
+
+ @Test
+ public void setNightModeActive_fromNightModeYesToNoWhenFalse() throws RemoteException {
+ try {
+ mService.setNightMode(MODE_NIGHT_YES);
+ } catch (SecurityException e) { /* we should ignore this update config exception*/ }
+ try {
+ mService.setNightModeActivated(false);
+ } catch (SecurityException e) { /* we should ignore this update config exception*/ }
+ assertEquals(MODE_NIGHT_NO, mService.getNightMode());
+ }
+
+ @Test
+ public void setNightModeActive_fromNightModeNoToYesWhenTrue() throws RemoteException {
+ try {
+ mService.setNightMode(MODE_NIGHT_NO);
+ } catch (SecurityException e) { /* we should ignore this update config exception*/ }
+ try {
+ mService.setNightModeActivated(true);
+ } catch (SecurityException e) { /* we should ignore this update config exception*/ }
+ assertEquals(MODE_NIGHT_YES, mService.getNightMode());
+ }
+
+ @Test
+ public void setNightModeActive_autoNightModeNoChanges() throws RemoteException {
+ try {
+ mService.setNightMode(MODE_NIGHT_AUTO);
+ } catch (SecurityException e) { /* we should ignore this update config exception*/ }
+ try {
+ mService.setNightModeActivated(true);
+ } catch (SecurityException e) { /* we should ignore this update config exception*/ }
+ assertEquals(MODE_NIGHT_AUTO, mService.getNightMode());
+ }
+
+ @Test
+ public void isNightModeActive_nightModeYes() throws RemoteException {
+ try {
+ mService.setNightMode(MODE_NIGHT_YES);
+ } catch (SecurityException e) { /* we should ignore this update config exception*/ }
+ assertTrue(isNightModeActivated());
+ }
+
+ @Test
+ public void isNightModeActive_nightModeNo() throws RemoteException {
+ try {
+ mService.setNightMode(MODE_NIGHT_NO);
+ } catch (SecurityException e) { /* we should ignore this update config exception*/ }
+ assertFalse(isNightModeActivated());
+ }
+
+ private boolean isNightModeActivated() {
+ return (mUiManagerService.getConfiguration().uiMode
+ & Configuration.UI_MODE_NIGHT_YES) != 0;
+ }
+}
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationComparatorTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationComparatorTest.java
index e15af3dbecc4..0b4760d89686 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationComparatorTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationComparatorTest.java
@@ -68,6 +68,7 @@ public class NotificationComparatorTest extends UiServiceTestCase {
private final int uid2 = 1111111;
private static final String TEST_CHANNEL_ID = "test_channel_id";
+ private NotificationRecord mRecordMinCallNonInterruptive;
private NotificationRecord mRecordMinCall;
private NotificationRecord mRecordHighCall;
private NotificationRecord mRecordDefaultMedia;
@@ -105,6 +106,18 @@ public class NotificationComparatorTest extends UiServiceTestCase {
smsPkg = Settings.Secure.getString(mContext.getContentResolver(),
Settings.Secure.SMS_DEFAULT_APPLICATION);
+ Notification nonInterruptiveNotif = new Notification.Builder(mContext, TEST_CHANNEL_ID)
+ .setCategory(Notification.CATEGORY_CALL)
+ .setFlag(Notification.FLAG_FOREGROUND_SERVICE, true)
+ .build();
+ mRecordMinCallNonInterruptive = new NotificationRecord(mContext,
+ new StatusBarNotification(callPkg,
+ callPkg, 1, "mRecordMinCallNonInterruptive", callUid, callUid,
+ nonInterruptiveNotif,
+ new UserHandle(userId), "", 2000), getDefaultChannel());
+ mRecordMinCallNonInterruptive.setSystemImportance(NotificationManager.IMPORTANCE_MIN);
+ mRecordMinCallNonInterruptive.setInterruptive(false);
+
Notification n1 = new Notification.Builder(mContext, TEST_CHANNEL_ID)
.setCategory(Notification.CATEGORY_CALL)
.setFlag(Notification.FLAG_FOREGROUND_SERVICE, true)
@@ -113,6 +126,7 @@ public class NotificationComparatorTest extends UiServiceTestCase {
callPkg, 1, "minCall", callUid, callUid, n1,
new UserHandle(userId), "", 2000), getDefaultChannel());
mRecordMinCall.setSystemImportance(NotificationManager.IMPORTANCE_MIN);
+ mRecordMinCall.setInterruptive(true);
Notification n2 = new Notification.Builder(mContext, TEST_CHANNEL_ID)
.setCategory(Notification.CATEGORY_CALL)
@@ -245,6 +259,7 @@ public class NotificationComparatorTest extends UiServiceTestCase {
expected.add(mRecordCheater);
expected.add(mRecordCheaterColorized);
expected.add(mRecordMinCall);
+ expected.add(mRecordMinCallNonInterruptive);
List<NotificationRecord> actual = new ArrayList<>();
actual.addAll(expected);
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationListenerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationListenerServiceTest.java
index 397d2155beeb..a9fe1a62b558 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationListenerServiceTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationListenerServiceTest.java
@@ -51,6 +51,8 @@ import android.service.notification.NotificationRankingUpdate;
import android.service.notification.SnoozeCriterion;
import android.test.suitebuilder.annotation.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
import com.android.server.UiServiceTestCase;
import org.junit.After;
@@ -61,8 +63,6 @@ import org.junit.runner.RunWith;
import java.util.ArrayList;
import java.util.List;
-import androidx.test.runner.AndroidJUnit4;
-
@SmallTest
@RunWith(AndroidJUnit4.class)
public class NotificationListenerServiceTest extends UiServiceTestCase {
@@ -116,6 +116,7 @@ public class NotificationListenerServiceTest extends UiServiceTestCase {
assertActionsEqual(getSmartActions(key, i), ranking.getSmartActions());
assertEquals(getSmartReplies(key, i), ranking.getSmartReplies());
assertEquals(canBubble(i), ranking.canBubble());
+ assertEquals(visuallyInterruptive(i), ranking.visuallyInterruptive());
}
}
@@ -182,7 +183,8 @@ public class NotificationListenerServiceTest extends UiServiceTestCase {
tweak.isNoisy(),
(ArrayList) tweak.getSmartActions(),
(ArrayList) tweak.getSmartReplies(),
- tweak.canBubble()
+ tweak.canBubble(),
+ tweak.visuallyInterruptive()
);
assertNotEquals(nru, nru2);
}
@@ -258,7 +260,8 @@ public class NotificationListenerServiceTest extends UiServiceTestCase {
getNoisy(i),
getSmartActions(key, i),
getSmartReplies(key, i),
- canBubble(i)
+ canBubble(i),
+ visuallyInterruptive(i)
);
rankings[i] = ranking;
}
@@ -363,6 +366,10 @@ public class NotificationListenerServiceTest extends UiServiceTestCase {
return index % 4 == 0;
}
+ private boolean visuallyInterruptive(int index) {
+ return index % 4 == 0;
+ }
+
private void assertActionsEqual(
List<Notification.Action> expecteds, List<Notification.Action> actuals) {
assertEquals(expecteds.size(), actuals.size());
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
index b5542afed46d..389b68a7d4b7 100755
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
@@ -1017,20 +1017,6 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
}
@Test
- public void testCancelImmediatelyAfterEnqueueNotifiesListeners_ForegroundServiceFlag()
- throws Exception {
- final StatusBarNotification sbn = generateNotificationRecord(null).sbn;
- sbn.getNotification().flags =
- Notification.FLAG_ONGOING_EVENT | FLAG_FOREGROUND_SERVICE;
- mBinderService.enqueueNotificationWithTag(PKG, PKG, "tag",
- sbn.getId(), sbn.getNotification(), sbn.getUserId());
- mBinderService.cancelNotificationWithTag(PKG, "tag", sbn.getId(), sbn.getUserId());
- waitForIdle();
- verify(mListeners, times(1)).notifyPostedLocked(any(), any());
- verify(mListeners, times(1)).notifyRemovedLocked(any(), anyInt(), any());
- }
-
- @Test
public void testUserInitiatedClearAll_noLeak() throws Exception {
final NotificationRecord n = generateNotificationRecord(
mTestNotificationChannel, 1, "group", true);
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationShellCmdTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationShellCmdTest.java
index fa90b291eeee..0d44318e4aaa 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationShellCmdTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationShellCmdTest.java
@@ -116,8 +116,8 @@ public class NotificationShellCmdTest extends UiServiceTestCase {
ArgumentCaptor<Notification> notificationCaptor =
ArgumentCaptor.forClass(Notification.class);
verify(mMockBinderService).enqueueNotificationWithTag(
- eq(NotificationShellCmd.NOTIFICATION_PACKAGE),
- eq("android"),
+ eq(getContext().getPackageName()),
+ eq(getContext().getPackageName()),
eq(aTag),
eq(NotificationShellCmd.NOTIFICATION_ID),
notificationCaptor.capture(),
diff --git a/services/usage/java/com/android/server/usage/AppIdleHistory.java b/services/usage/java/com/android/server/usage/AppIdleHistory.java
index 1e4861a89694..82292cfeea09 100644
--- a/services/usage/java/com/android/server/usage/AppIdleHistory.java
+++ b/services/usage/java/com/android/server/usage/AppIdleHistory.java
@@ -578,7 +578,7 @@ public class AppIdleHistory {
}
}
} catch (IOException | XmlPullParserException e) {
- Slog.e(TAG, "Unable to read app idle file for user " + userId);
+ Slog.e(TAG, "Unable to read app idle file for user " + userId, e);
} finally {
IoUtils.closeQuietly(fis);
}
@@ -608,6 +608,11 @@ public class AppIdleHistory {
final int N = userHistory.size();
for (int i = 0; i < N; i++) {
String packageName = userHistory.keyAt(i);
+ // Skip any unexpected null package names
+ if (packageName == null) {
+ Slog.w(TAG, "Skipping App Idle write for unexpected null package");
+ continue;
+ }
AppUsageHistory history = userHistory.valueAt(i);
xml.startTag(null, TAG_PACKAGE);
xml.attribute(null, ATTR_NAME, packageName);
@@ -641,7 +646,7 @@ public class AppIdleHistory {
appIdleFile.finishWrite(fos);
} catch (Exception e) {
appIdleFile.failWrite(fos);
- Slog.e(TAG, "Error writing app idle file for user " + userId);
+ Slog.e(TAG, "Error writing app idle file for user " + userId, e);
}
}
diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java
index 92c6e938d727..2dbd1c0ce674 100644
--- a/telephony/java/android/telephony/CarrierConfigManager.java
+++ b/telephony/java/android/telephony/CarrierConfigManager.java
@@ -2919,6 +2919,54 @@ public class CarrierConfigManager {
"ping_test_before_data_switch_bool";
/**
+ * Controls whether to switch data to primary from opportunistic subscription
+ * if primary is out of service. This control only affects system or 1st party app
+ * initiated data switch, but will not override data switch initiated by privileged carrier apps
+ * This carrier config is used to disable this feature.
+ * @hide
+ */
+ public static final String KEY_SWITCH_DATA_TO_PRIMARY_IF_PRIMARY_IS_OOS_BOOL =
+ "switch_data_to_primary_if_primary_is_oos_bool";
+
+ /**
+ * Controls the ping pong determination of opportunistic network.
+ * If opportunistic network is determined as out of service or below
+ * #KEY_OPPORTUNISTIC_NETWORK_EXIT_THRESHOLD_RSRP_INT or
+ * #KEY_OPPORTUNISTIC_NETWORK_EXIT_THRESHOLD_RSSNR_INT within
+ * #KEY_OPPORTUNISTIC_NETWORK_PING_PONG_TIME_LONG of switching to opportunistic network,
+ * it will be determined as ping pong situation by system app or 1st party app.
+ * @hide
+ */
+ public static final String KEY_OPPORTUNISTIC_NETWORK_PING_PONG_TIME_LONG =
+ "opportunistic_network_ping_pong_time_long";
+ /**
+ * Controls back off time in milli seconds for switching back to
+ * opportunistic subscription. This time will be added to
+ * {@link CarrierConfigManager#KEY_OPPORTUNISTIC_NETWORK_DATA_SWITCH_HYSTERESIS_TIME_LONG} to
+ * determine hysteresis time if there is ping pong situation
+ * (determined by system app or 1st party app) between primary and opportunistic
+ * subscription. Ping ping situation is defined in
+ * #KEY_OPPORTUNISTIC_NETWORK_PING_PONG_TIME_LONG.
+ * If ping pong situation continuous #KEY_OPPORTUNISTIC_NETWORK_BACKOFF_TIME_LONG
+ * will be added to previously determined hysteresis time.
+ * @hide
+ */
+ public static final String KEY_OPPORTUNISTIC_NETWORK_BACKOFF_TIME_LONG =
+ "opportunistic_network_backoff_time_long";
+
+ /**
+ * Controls the max back off time in milli seconds for switching back to
+ * opportunistic subscription.
+ * This time will be the max hysteresis that can be determined irrespective of there is
+ * continuous ping pong situation or not as described in
+ * #KEY_OPPORTUNISTIC_NETWORK_PING_PONG_TIME_LONG and
+ * #KEY_OPPORTUNISTIC_NETWORK_BACKOFF_TIME_LONG.
+ * @hide
+ */
+ public static final String KEY_OPPORTUNISTIC_NETWORK_MAX_BACKOFF_TIME_LONG =
+ "opportunistic_network_max_backoff_time_long";
+
+ /**
* Indicates zero or more emergency number prefix(es), because some carrier requires
* if users dial an emergency number address with a specific prefix, the combination of the
* prefix and the address is also a valid emergency number to dial. For example, an emergency
@@ -3725,6 +3773,13 @@ public class CarrierConfigManager {
/* Default value is 3 seconds. */
sDefaults.putLong(KEY_OPPORTUNISTIC_NETWORK_DATA_SWITCH_EXIT_HYSTERESIS_TIME_LONG, 3000);
sDefaults.putBoolean(KEY_PING_TEST_BEFORE_DATA_SWITCH_BOOL, true);
+ sDefaults.putBoolean(KEY_SWITCH_DATA_TO_PRIMARY_IF_PRIMARY_IS_OOS_BOOL, true);
+ /* Default value is 60 seconds. */
+ sDefaults.putLong(KEY_OPPORTUNISTIC_NETWORK_PING_PONG_TIME_LONG, 60000);
+ /* Default value is 10 seconds. */
+ sDefaults.putLong(KEY_OPPORTUNISTIC_NETWORK_BACKOFF_TIME_LONG, 10000);
+ /* Default value is 60 seconds. */
+ sDefaults.putLong(KEY_OPPORTUNISTIC_NETWORK_MAX_BACKOFF_TIME_LONG, 60000);
sDefaults.putAll(Gps.getDefaults());
sDefaults.putAll(Wifi.getDefaults());
sDefaults.putIntArray(KEY_CDMA_ENHANCED_ROAMING_INDICATOR_FOR_HOME_NETWORK_INT_ARRAY,
diff --git a/telephony/java/android/telephony/SubscriptionInfo.java b/telephony/java/android/telephony/SubscriptionInfo.java
index 9eff809eaf5d..ebb517596b6c 100644
--- a/telephony/java/android/telephony/SubscriptionInfo.java
+++ b/telephony/java/android/telephony/SubscriptionInfo.java
@@ -89,8 +89,8 @@ public class SubscriptionInfo implements Parcelable {
private int mCarrierId;
/**
- * The source of the name, NAME_SOURCE_DEFAULT_SOURCE, NAME_SOURCE_SIM_SOURCE or
- * NAME_SOURCE_USER_INPUT.
+ * The source of the name, NAME_SOURCE_DEFAULT_SOURCE, NAME_SOURCE_SIM_SPN,
+ * NAME_SOURCE_SIM_PNN, or NAME_SOURCE_USER_INPUT.
*/
private int mNameSource;
@@ -334,7 +334,7 @@ public class SubscriptionInfo implements Parcelable {
}
/**
- * @return the source of the name, eg NAME_SOURCE_DEFAULT_SOURCE, NAME_SOURCE_SIM_SOURCE or
+ * @return the source of the name, eg NAME_SOURCE_DEFAULT_SOURCE, NAME_SOURCE_SIM_SPN or
* NAME_SOURCE_USER_INPUT.
* @hide
*/
diff --git a/telephony/java/android/telephony/SubscriptionManager.java b/telephony/java/android/telephony/SubscriptionManager.java
index fbbc9518065d..16415ead8b13 100644
--- a/telephony/java/android/telephony/SubscriptionManager.java
+++ b/telephony/java/android/telephony/SubscriptionManager.java
@@ -400,19 +400,19 @@ public class SubscriptionManager {
public static final String NAME_SOURCE = "name_source";
/**
- * The name_source is the default
+ * The name_source is the default, which is from the carrier id.
* @hide
*/
public static final int NAME_SOURCE_DEFAULT_SOURCE = 0;
/**
- * The name_source is from the SIM
+ * The name_source is from SIM EF_SPN.
* @hide
*/
- public static final int NAME_SOURCE_SIM_SOURCE = 1;
+ public static final int NAME_SOURCE_SIM_SPN = 1;
/**
- * The name_source is from the user
+ * The name_source is from user input
* @hide
*/
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
@@ -425,6 +425,24 @@ public class SubscriptionManager {
public static final int NAME_SOURCE_CARRIER = 3;
/**
+ * The name_source is from SIM EF_PNN.
+ * @hide
+ */
+ public static final int NAME_SOURCE_SIM_PNN = 4;
+
+ /** @hide */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef(prefix = {"NAME_SOURCE_"},
+ value = {
+ NAME_SOURCE_DEFAULT_SOURCE,
+ NAME_SOURCE_SIM_SPN,
+ NAME_SOURCE_USER_INPUT,
+ NAME_SOURCE_CARRIER,
+ NAME_SOURCE_SIM_PNN
+ })
+ public @interface SimDisplayNameSource {}
+
+ /**
* TelephonyProvider column name for the color of a SIM.
* <P>Type: INTEGER (int)</P>
*/
@@ -1660,13 +1678,12 @@ public class SubscriptionManager {
* Set display name by simInfo index with name source
* @param displayName the display name of SIM card
* @param subId the unique SubscriptionInfo index in database
- * @param nameSource 0: NAME_SOURCE_DEFAULT_SOURCE, 1: NAME_SOURCE_SIM_SOURCE,
- * 2: NAME_SOURCE_USER_INPUT
+ * @param nameSource SIM display name source
* @return the number of records updated or < 0 if invalid subId
* @hide
*/
@UnsupportedAppUsage
- public int setDisplayName(String displayName, int subId, int nameSource) {
+ public int setDisplayName(String displayName, int subId, @SimDisplayNameSource int nameSource) {
if (VDBG) {
logd("[setDisplayName]+ displayName:" + displayName + " subId:" + subId
+ " nameSource:" + nameSource);
diff --git a/telephony/java/android/telephony/ims/ImsReasonInfo.java b/telephony/java/android/telephony/ims/ImsReasonInfo.java
index 76b214b71075..f4b2cef73a44 100644
--- a/telephony/java/android/telephony/ims/ImsReasonInfo.java
+++ b/telephony/java/android/telephony/ims/ImsReasonInfo.java
@@ -889,6 +889,12 @@ public final class ImsReasonInfo implements Parcelable {
*/
public static final int CODE_WFC_SERVICE_NOT_AVAILABLE_IN_THIS_LOCATION = 1623;
+ /**
+ * The dialed RTT call should be retried without RTT
+ * @hide
+ */
+ public static final int CODE_RETRY_ON_IMS_WITHOUT_RTT = 3001;
+
/*
* OEM specific error codes. To be used by OEMs when they don't want to reveal error code which
* would be replaced by ERROR_UNSPECIFIED.
@@ -1069,6 +1075,7 @@ public final class ImsReasonInfo implements Parcelable {
CODE_REJECT_VT_AVPF_NOT_ALLOWED,
CODE_REJECT_ONGOING_ENCRYPTED_CALL,
CODE_REJECT_ONGOING_CS_CALL,
+ CODE_RETRY_ON_IMS_WITHOUT_RTT,
CODE_OEM_CAUSE_1,
CODE_OEM_CAUSE_2,
CODE_OEM_CAUSE_3,
diff --git a/telephony/java/com/android/internal/telephony/gsm/GsmSmsCbMessage.java b/telephony/java/com/android/internal/telephony/gsm/GsmSmsCbMessage.java
index bdd1fab34a3c..515687fdfdf0 100644
--- a/telephony/java/com/android/internal/telephony/gsm/GsmSmsCbMessage.java
+++ b/telephony/java/com/android/internal/telephony/gsm/GsmSmsCbMessage.java
@@ -225,7 +225,7 @@ public class GsmSmsCbMessage {
private static Pair<Integer, List<Geometry>> parseWarningAreaCoordinates(
byte[] pdu, int wacOffset) {
// little-endian
- int wacDataLength = (pdu[wacOffset + 1] << 8) | pdu[wacOffset];
+ int wacDataLength = ((pdu[wacOffset + 1] & 0xff) << 8) | (pdu[wacOffset] & 0xff);
int offset = wacOffset + 2;
if (offset + wacDataLength > pdu.length) {
diff --git a/tests/testables/src/android/testing/TestableSettingsProvider.java b/tests/testables/src/android/testing/TestableSettingsProvider.java
index b158476bd438..fd92c657cb2e 100644
--- a/tests/testables/src/android/testing/TestableSettingsProvider.java
+++ b/tests/testables/src/android/testing/TestableSettingsProvider.java
@@ -14,6 +14,8 @@
package android.testing;
+import static org.junit.Assert.assertEquals;
+
import android.content.ContentProviderClient;
import android.content.Context;
import android.os.Bundle;
@@ -25,8 +27,6 @@ import android.util.Log;
import java.util.HashMap;
-import static org.junit.Assert.*;
-
/**
* Allows calls to android.provider.Settings to be tested easier.
*
@@ -71,7 +71,7 @@ public class TestableSettingsProvider extends MockContentProvider {
public Bundle call(String method, String arg, Bundle extras) {
// Methods are "GET_system", "GET_global", "PUT_secure", etc.
- final int userId = extras.getInt(Settings.CALL_METHOD_USER_KEY, 0);
+ final int userId = extras.getInt(Settings.CALL_METHOD_USER_KEY, UserHandle.myUserId());
final String[] commands = method.split("_", 2);
final String op = commands[0];
final String table = commands[1];
diff --git a/wifi/java/android/net/wifi/WifiManager.java b/wifi/java/android/net/wifi/WifiManager.java
index 671b3c715012..7ce83f9445e8 100644
--- a/wifi/java/android/net/wifi/WifiManager.java
+++ b/wifi/java/android/net/wifi/WifiManager.java
@@ -1776,12 +1776,13 @@ public class WifiManager {
}
/**
- * Remove the Passpoint configuration identified by its FQDN (Fully Qualified Domain Name).
+ * Remove the Passpoint configuration identified by its FQDN (Fully Qualified Domain Name) added
+ * by the caller.
*
- * @param fqdn The FQDN of the Passpoint configuration to be removed
+ * @param fqdn The FQDN of the Passpoint configuration added by the caller to be removed
* @throws IllegalArgumentException if no configuration is associated with the given FQDN or
* Passpoint is not enabled on the device.
- * @deprecated This is no longer supported.
+ * @deprecated This will be non-functional in a future release.
*/
@Deprecated
@RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS)
@@ -1796,12 +1797,12 @@ public class WifiManager {
}
/**
- * Return the list of installed Passpoint configurations.
+ * Return the list of installed Passpoint configurations added by the caller.
*
* An empty list will be returned when no configurations are installed.
*
- * @return A list of {@link PasspointConfiguration}
- * @deprecated This is no longer supported.
+ * @return A list of {@link PasspointConfiguration} added by the caller
+ * @deprecated This will be non-functional in a future release.
*/
@Deprecated
@RequiresPermission(anyOf = {
@@ -2068,69 +2069,69 @@ public class WifiManager {
}
/** @hide */
- public static final int WIFI_FEATURE_INFRA = 0x0001; // Basic infrastructure mode
+ public static final long WIFI_FEATURE_INFRA = 0x0001L; // Basic infrastructure mode
/** @hide */
- public static final int WIFI_FEATURE_INFRA_5G = 0x0002; // Support for 5 GHz Band
+ public static final long WIFI_FEATURE_INFRA_5G = 0x0002L; // Support for 5 GHz Band
/** @hide */
- public static final int WIFI_FEATURE_PASSPOINT = 0x0004; // Support for GAS/ANQP
+ public static final long WIFI_FEATURE_PASSPOINT = 0x0004L; // Support for GAS/ANQP
/** @hide */
- public static final int WIFI_FEATURE_P2P = 0x0008; // Wifi-Direct
+ public static final long WIFI_FEATURE_P2P = 0x0008L; // Wifi-Direct
/** @hide */
- public static final int WIFI_FEATURE_MOBILE_HOTSPOT = 0x0010; // Soft AP
+ public static final long WIFI_FEATURE_MOBILE_HOTSPOT = 0x0010L; // Soft AP
/** @hide */
- public static final int WIFI_FEATURE_SCANNER = 0x0020; // WifiScanner APIs
+ public static final long WIFI_FEATURE_SCANNER = 0x0020L; // WifiScanner APIs
/** @hide */
- public static final int WIFI_FEATURE_AWARE = 0x0040; // Wi-Fi AWare networking
+ public static final long WIFI_FEATURE_AWARE = 0x0040L; // Wi-Fi AWare networking
/** @hide */
- public static final int WIFI_FEATURE_D2D_RTT = 0x0080; // Device-to-device RTT
+ public static final long WIFI_FEATURE_D2D_RTT = 0x0080L; // Device-to-device RTT
/** @hide */
- public static final int WIFI_FEATURE_D2AP_RTT = 0x0100; // Device-to-AP RTT
+ public static final long WIFI_FEATURE_D2AP_RTT = 0x0100L; // Device-to-AP RTT
/** @hide */
- public static final int WIFI_FEATURE_BATCH_SCAN = 0x0200; // Batched Scan (deprecated)
+ public static final long WIFI_FEATURE_BATCH_SCAN = 0x0200L; // Batched Scan (deprecated)
/** @hide */
- public static final int WIFI_FEATURE_PNO = 0x0400; // Preferred network offload
+ public static final long WIFI_FEATURE_PNO = 0x0400L; // Preferred network offload
/** @hide */
- public static final int WIFI_FEATURE_ADDITIONAL_STA = 0x0800; // Support for two STAs
+ public static final long WIFI_FEATURE_ADDITIONAL_STA = 0x0800L; // Support for two STAs
/** @hide */
- public static final int WIFI_FEATURE_TDLS = 0x1000; // Tunnel directed link setup
+ public static final long WIFI_FEATURE_TDLS = 0x1000L; // Tunnel directed link setup
/** @hide */
- public static final int WIFI_FEATURE_TDLS_OFFCHANNEL = 0x2000; // Support for TDLS off channel
+ public static final long WIFI_FEATURE_TDLS_OFFCHANNEL = 0x2000L; // TDLS off channel
/** @hide */
- public static final int WIFI_FEATURE_EPR = 0x4000; // Enhanced power reporting
+ public static final long WIFI_FEATURE_EPR = 0x4000L; // Enhanced power reporting
/** @hide */
- public static final int WIFI_FEATURE_AP_STA = 0x8000; // AP STA Concurrency
+ public static final long WIFI_FEATURE_AP_STA = 0x8000L; // AP STA Concurrency
/** @hide */
- public static final int WIFI_FEATURE_LINK_LAYER_STATS = 0x10000; // Link layer stats collection
+ public static final long WIFI_FEATURE_LINK_LAYER_STATS = 0x10000L; // Link layer stats
/** @hide */
- public static final int WIFI_FEATURE_LOGGER = 0x20000; // WiFi Logger
+ public static final long WIFI_FEATURE_LOGGER = 0x20000L; // WiFi Logger
/** @hide */
- public static final int WIFI_FEATURE_HAL_EPNO = 0x40000; // Enhanced PNO
+ public static final long WIFI_FEATURE_HAL_EPNO = 0x40000L; // Enhanced PNO
/** @hide */
- public static final int WIFI_FEATURE_RSSI_MONITOR = 0x80000; // RSSI Monitor
+ public static final long WIFI_FEATURE_RSSI_MONITOR = 0x80000L; // RSSI Monitor
/** @hide */
- public static final int WIFI_FEATURE_MKEEP_ALIVE = 0x100000; // mkeep_alive
+ public static final long WIFI_FEATURE_MKEEP_ALIVE = 0x100000L; // mkeep_alive
/** @hide */
- public static final int WIFI_FEATURE_CONFIG_NDO = 0x200000; // ND offload
+ public static final long WIFI_FEATURE_CONFIG_NDO = 0x200000L; // ND offload
/** @hide */
- public static final int WIFI_FEATURE_TRANSMIT_POWER = 0x400000; // Capture transmit power
+ public static final long WIFI_FEATURE_TRANSMIT_POWER = 0x400000L; // Capture transmit power
/** @hide */
- public static final int WIFI_FEATURE_CONTROL_ROAMING = 0x800000; // Control firmware roaming
+ public static final long WIFI_FEATURE_CONTROL_ROAMING = 0x800000L; // Control firmware roaming
/** @hide */
- public static final int WIFI_FEATURE_IE_WHITELIST = 0x1000000; // Probe IE white listing
+ public static final long WIFI_FEATURE_IE_WHITELIST = 0x1000000L; // Probe IE white listing
/** @hide */
- public static final int WIFI_FEATURE_SCAN_RAND = 0x2000000; // Random MAC & Probe seq
+ public static final long WIFI_FEATURE_SCAN_RAND = 0x2000000L; // Random MAC & Probe seq
/** @hide */
- public static final int WIFI_FEATURE_TX_POWER_LIMIT = 0x4000000; // Set Tx power limit
+ public static final long WIFI_FEATURE_TX_POWER_LIMIT = 0x4000000L; // Set Tx power limit
/** @hide */
- public static final int WIFI_FEATURE_WPA3_SAE = 0x8000000; // WPA3-Personal SAE
+ public static final long WIFI_FEATURE_WPA3_SAE = 0x8000000L; // WPA3-Personal SAE
/** @hide */
- public static final int WIFI_FEATURE_WPA3_SUITE_B = 0x10000000; // WPA3-Enterprise Suite-B
+ public static final long WIFI_FEATURE_WPA3_SUITE_B = 0x10000000L; // WPA3-Enterprise Suite-B
/** @hide */
- public static final int WIFI_FEATURE_OWE = 0x20000000; // Enhanced Open
+ public static final long WIFI_FEATURE_OWE = 0x20000000L; // Enhanced Open
/** @hide */
- public static final int WIFI_FEATURE_LOW_LATENCY = 0x40000000; // Low Latency modes
+ public static final long WIFI_FEATURE_LOW_LATENCY = 0x40000000L; // Low Latency modes
/** @hide */
- public static final int WIFI_FEATURE_DPP = 0x80000000; // DPP (Easy-Connect)
+ public static final long WIFI_FEATURE_DPP = 0x80000000L; // DPP (Easy-Connect)
/** @hide */
public static final long WIFI_FEATURE_P2P_RAND_MAC = 0x100000000L; // Random P2P MAC
diff --git a/wifi/java/android/net/wifi/aware/WifiAwareNetworkInfo.java b/wifi/java/android/net/wifi/aware/WifiAwareNetworkInfo.java
index fd26817bfd79..e9fb37ea0c29 100644
--- a/wifi/java/android/net/wifi/aware/WifiAwareNetworkInfo.java
+++ b/wifi/java/android/net/wifi/aware/WifiAwareNetworkInfo.java
@@ -120,10 +120,12 @@ public final class WifiAwareNetworkInfo implements TransportInfo, Parcelable {
new Creator<WifiAwareNetworkInfo>() {
@Override
public WifiAwareNetworkInfo createFromParcel(Parcel in) {
+ byte[] addr = in.createByteArray();
+ String interfaceName = in.readString();
+ int port = in.readInt();
+ int transportProtocol = in.readInt();
Inet6Address ipv6Addr;
try {
- byte[] addr = in.createByteArray();
- String interfaceName = in.readString();
NetworkInterface ni = null;
if (interfaceName != null) {
try {
@@ -137,9 +139,6 @@ public final class WifiAwareNetworkInfo implements TransportInfo, Parcelable {
e.printStackTrace();
return null;
}
- int port = in.readInt();
- int transportProtocol = in.readInt();
-
return new WifiAwareNetworkInfo(ipv6Addr, port, transportProtocol);
}
diff --git a/wifi/java/android/net/wifi/rtt/package.html b/wifi/java/android/net/wifi/rtt/package.html
index e6392821ff54..4a32f5206fde 100644
--- a/wifi/java/android/net/wifi/rtt/package.html
+++ b/wifi/java/android/net/wifi/rtt/package.html
@@ -37,5 +37,9 @@ location to be queried.</p>
<pre>
getPackageManager().hasSystemFeature(PackageManager.FEATURE_WIFI_RTT)
</pre>
+
+<p>For an example of this functionality, see
+<a href="{@docRoot}guide/topics/connectivity/wifi-rtt" class="external">Wi-Fi location: ranging
+with RTT</a>.</p>
</BODY>
</HTML>