summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.prebuilt_info/prebuilt_info_packages_CtsShim_apk__arm_CtsShimPriv_apk.asciipb2
-rw-r--r--.prebuilt_info/prebuilt_info_packages_CtsShim_apk__arm_CtsShim_apk.asciipb2
-rw-r--r--.prebuilt_info/prebuilt_info_packages_CtsShim_apk__x86_CtsShimPriv_apk.asciipb2
-rw-r--r--.prebuilt_info/prebuilt_info_packages_CtsShim_apk__x86_CtsShim_apk.asciipb2
-rw-r--r--apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java13
-rw-r--r--api/Android.bp40
-rw-r--r--cmds/app_process/Android.bp11
-rw-r--r--cmds/app_process/version-script.txt4
-rw-r--r--core/api/test-current.txt1
-rw-r--r--core/java/android/app/ActivityOptions.java13
-rw-r--r--core/java/android/app/AutomaticZenRule.java21
-rw-r--r--core/java/android/app/INotificationManager.aidl2
-rw-r--r--core/java/android/app/NotificationManager.java8
-rw-r--r--core/java/android/app/PendingIntent.java1
-rw-r--r--core/java/android/app/admin/DevicePolicyManagerInternal.java10
-rw-r--r--core/java/android/app/admin/DevicePolicyManagerLiteInternal.java41
-rw-r--r--core/java/android/content/pm/AppSearchShortcutInfo.java2
-rw-r--r--core/java/android/content/pm/ShortcutInfo.java33
-rw-r--r--core/java/android/content/pm/ShortcutServiceInternal.java2
-rw-r--r--core/java/android/hardware/biometrics/BiometricAuthenticator.java5
-rw-r--r--core/java/android/os/AggregateBatteryConsumer.java51
-rw-r--r--core/java/android/os/BatteryUsageStats.java222
-rw-r--r--core/java/android/os/BatteryUsageStatsQuery.java38
-rw-r--r--core/java/android/os/PackageTagsList.java156
-rw-r--r--core/java/android/os/PowerComponents.java183
-rw-r--r--core/java/android/os/UidBatteryConsumer.java101
-rw-r--r--core/java/android/os/UserBatteryConsumer.java42
-rw-r--r--core/java/android/os/UserManager.java19
-rw-r--r--core/java/android/service/notification/ZenModeConfig.java15
-rw-r--r--core/java/android/window/ITaskOrganizer.aidl5
-rw-r--r--core/java/android/window/SplashScreenView.java64
-rw-r--r--core/java/android/window/TaskOrganizer.java17
-rw-r--r--core/java/com/android/internal/display/BrightnessSynchronizer.java87
-rw-r--r--core/java/com/android/internal/jank/InteractionJankMonitor.java30
-rw-r--r--core/java/com/android/internal/os/BatteryStatsImpl.java25
-rw-r--r--core/java/com/android/internal/os/BatteryUsageStatsProvider.java40
-rw-r--r--core/java/com/android/internal/os/BatteryUsageStatsStore.java285
-rw-r--r--core/tests/coretests/src/android/os/PackageTagsListTest.java52
-rw-r--r--core/tests/coretests/src/com/android/internal/os/BatteryStatsTests.java1
-rw-r--r--core/tests/coretests/src/com/android/internal/os/BatteryUsageStatsProviderTest.java88
-rw-r--r--core/tests/coretests/src/com/android/internal/os/BatteryUsageStatsStoreTest.java196
-rw-r--r--core/tests/coretests/src/com/android/internal/os/BatteryUsageStatsTest.java392
-rw-r--r--data/etc/car/com.android.car.cluster.home.xml1
-rw-r--r--data/etc/services.core.protolog.json6
-rw-r--r--graphics/java/android/graphics/HardwareRenderer.java4
-rw-r--r--graphics/java/android/graphics/drawable/RippleDrawable.java12
-rw-r--r--libs/WindowManager/Shell/res/values/config.xml2
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/ShellTaskOrganizer.java7
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHanded.java5
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedController.java78
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedDisplayAreaOrganizer.java11
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedEventCallback.java28
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedSettingsUtil.java11
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipResizeGestureHandler.java13
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/StartingSurfaceDrawer.java54
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/StartingWindowController.java8
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedControllerTest.java45
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedDisplayAreaOrganizerTest.java14
-rw-r--r--libs/hwui/jni/android_graphics_HardwareRenderer.cpp34
-rw-r--r--location/java/android/location/LocationManagerInternal.java73
-rw-r--r--packages/CtsShim/apk/arm/CtsShim.apkbin5483 -> 5488 bytes
-rw-r--r--packages/CtsShim/apk/arm/CtsShimPriv.apkbin31624 -> 31627 bytes
-rw-r--r--packages/CtsShim/apk/x86/CtsShim.apkbin5483 -> 5488 bytes
-rw-r--r--packages/CtsShim/apk/x86/CtsShimPriv.apkbin24257 -> 24260 bytes
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/enterprise/ActionDisabledByAdminController.java11
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/enterprise/ActionDisabledByAdminControllerFactory.java31
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/enterprise/BiometricActionDisabledByAdminController.java71
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/enterprise/DeviceAdminStringProvider.java10
-rw-r--r--packages/SettingsLib/tests/robotests/src/com/android/settingslib/enterprise/BiometricActionDisabledByAdminControllerTest.java79
-rw-r--r--packages/SettingsLib/tests/robotests/src/com/android/settingslib/enterprise/FakeDeviceAdminStringProvider.java13
-rw-r--r--packages/SystemUI/animation/src/com/android/systemui/animation/ActivityLaunchAnimator.kt4
-rw-r--r--packages/SystemUI/animation/src/com/android/systemui/animation/GhostedViewLaunchAnimatorController.kt10
-rw-r--r--packages/SystemUI/plugin/src/com/android/systemui/plugins/ActivityStarter.java7
-rw-r--r--packages/SystemUI/res/layout/global_actions_change_panel.xml16
-rw-r--r--packages/SystemUI/res/layout/long_screenshot.xml1
-rw-r--r--packages/SystemUI/res/layout/quick_qs_status_icons.xml11
-rw-r--r--packages/SystemUI/res/values-television/config.xml1
-rw-r--r--packages/SystemUI/res/values/attrs.xml2
-rw-r--r--packages/SystemUI/res/values/config.xml4
-rw-r--r--packages/SystemUI/res/values/dimens.xml2
-rw-r--r--packages/SystemUI/res/values/strings.xml2
-rw-r--r--packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java17
-rw-r--r--packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitorCallback.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/ImageWallpaper.java32
-rw-r--r--packages/SystemUI/src/com/android/systemui/SwipeHelper.java13
-rw-r--r--packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/AccessibilityFloatingMenu.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/AccessibilityTargetAdapter.java6
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleController.kt46
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleView.kt95
-rw-r--r--packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java24
-rw-r--r--packages/SystemUI/src/com/android/systemui/doze/DozeUi.java23
-rw-r--r--packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java141
-rw-r--r--packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialogLite.java146
-rw-r--r--packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsImpl.java14
-rw-r--r--packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsInfoProvider.kt6
-rw-r--r--packages/SystemUI/src/com/android/systemui/glwallpaper/ImageWallpaperRenderer.java8
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/KeyguardIndicationRotateTextViewController.java8
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/KeyguardMediaController.kt8
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/MediaControlPanel.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/MediaDataManager.kt22
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/MediaHierarchyManager.kt24
-rw-r--r--packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java58
-rw-r--r--packages/SystemUI/src/com/android/systemui/people/NotificationHelper.java13
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/QSFooterViewController.java6
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/QSFragment.java5
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/QSTileHost.java13
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/external/CustomTile.java77
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/external/CustomTileStatePersister.kt122
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/external/TileServices.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSFactoryImpl.java3
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileImpl.java33
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/AlarmTile.kt6
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/DeviceControlsTile.kt4
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/QuickAccessWalletTile.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/screenshot/CropView.java46
-rw-r--r--packages/SystemUI/src/com/android/systemui/screenshot/LongScreenshotActivity.java38
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java20
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/LightRevealScrim.kt8
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/StatusBarStateControllerImpl.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/SysuiStatusBarStateController.java14
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/events/SystemStatusAnimationScheduler.kt16
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationClicker.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationWakeUpCoordinator.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationView.java24
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java82
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableOutlineView.java78
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableView.java9
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationBackgroundView.java36
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/AmbientState.java15
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java257
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java27
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSwipeHelper.java12
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackStateAnimator.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeParameters.java8
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardIndicationTextView.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java58
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowControllerImpl.java17
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java202
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java16
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java117
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/UnlockedScreenOffAnimationController.kt49
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallController.kt6
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardStateController.java5
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardStateControllerImpl.java10
-rw-r--r--packages/SystemUI/src/com/android/systemui/tuner/TunerServiceImpl.java3
-rw-r--r--packages/SystemUI/src/com/android/systemui/wmshell/WMShell.java11
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthRippleControllerTest.kt17
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/doze/DozeTriggersTest.java23
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/doze/DozeUiTest.java7
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsDialogLiteTest.java68
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsDialogTest.java10
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardIndicationRotateTextViewControllerTest.java5
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/media/MediaDataManagerTest.kt33
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/people/widget/LaunchConversationActivityTest.java2
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/qs/QSFragmentTest.java3
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/qs/QSTileHostTest.java19
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/qs/external/CustomTileStatePersisterTest.kt159
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/qs/external/CustomTileTest.kt101
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/qs/external/TileServicesTest.java3
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/QSTileImplTest.java3
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/qs/tiles/AlarmTileTest.kt2
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/qs/tiles/BatterySaverTileTest.kt3
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/qs/tiles/CastTileTest.java3
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/qs/tiles/DeviceControlsTileTest.kt5
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/qs/tiles/NfcTileTest.java3
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/qs/tiles/QuickAccessWalletTileTest.java3
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/qs/tiles/ReduceBrightColorsTileTest.java3
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/qs/tiles/ScreenRecordTileTest.java3
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowTest.java1
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/DozeParametersTest.java7
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewTest.java16
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationShadeWindowControllerImplTest.java4
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java12
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeKeyguardStateController.java5
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java6
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/wmshell/NewNotifPipelineBubblesTest.java6
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/wmshell/TestableBubblePositioner.java12
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/wmshell/WMShellTest.java2
-rw-r--r--services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java18
-rw-r--r--services/core/java/com/android/server/am/ActiveServices.java28
-rw-r--r--services/core/java/com/android/server/am/BatteryStatsService.java27
-rw-r--r--services/core/java/com/android/server/am/OomAdjuster.java6
-rw-r--r--services/core/java/com/android/server/am/ServiceRecord.java5
-rw-r--r--services/core/java/com/android/server/appop/AppOpsService.java15
-rw-r--r--services/core/java/com/android/server/appop/DiscreteRegistry.java90
-rw-r--r--services/core/java/com/android/server/appop/HistoricalRegistry.java11
-rw-r--r--services/core/java/com/android/server/display/WifiDisplayController.java5
-rw-r--r--services/core/java/com/android/server/location/LocationManagerService.java103
-rw-r--r--services/core/java/com/android/server/location/provider/AbstractLocationProvider.java20
-rw-r--r--services/core/java/com/android/server/location/provider/LocationProviderManager.java10
-rw-r--r--services/core/java/com/android/server/location/provider/MockableLocationProvider.java29
-rwxr-xr-xservices/core/java/com/android/server/notification/NotificationManagerService.java5
-rw-r--r--services/core/java/com/android/server/notification/ZenModeHelper.java19
-rw-r--r--services/core/java/com/android/server/pm/LauncherAppsService.java9
-rw-r--r--services/core/java/com/android/server/pm/ShortcutPackage.java12
-rw-r--r--services/core/java/com/android/server/pm/ShortcutParser.java11
-rw-r--r--services/core/java/com/android/server/pm/ShortcutService.java7
-rw-r--r--services/core/java/com/android/server/policy/AppOpsPolicy.java143
-rw-r--r--services/core/java/com/android/server/stats/pull/ProcfsMemoryUtil.java21
-rw-r--r--services/core/java/com/android/server/stats/pull/StatsPullAtomService.java24
-rw-r--r--services/core/java/com/android/server/wm/AccessibilityController.java33
-rw-r--r--services/core/java/com/android/server/wm/ActivityRecord.java45
-rw-r--r--services/core/java/com/android/server/wm/AppTransitionController.java6
-rw-r--r--services/core/java/com/android/server/wm/DisplayContent.java22
-rw-r--r--services/core/java/com/android/server/wm/KeyguardController.java22
-rw-r--r--services/core/java/com/android/server/wm/LetterboxConfiguration.java115
-rw-r--r--services/core/java/com/android/server/wm/Task.java34
-rw-r--r--services/core/java/com/android/server/wm/TaskOrganizerController.java29
-rw-r--r--services/core/java/com/android/server/wm/WindowManagerService.java19
-rw-r--r--services/core/java/com/android/server/wm/WindowManagerShellCommand.java361
-rw-r--r--services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java8
-rw-r--r--services/devicepolicy/java/com/android/server/devicepolicy/OneTimeSafetyChecker.java6
-rw-r--r--services/devicepolicy/java/com/android/server/devicepolicy/SecurityLogMonitor.java11
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/alarm/AlarmManagerServiceTest.java57
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/location/provider/MockableLocationProviderTest.java2
-rw-r--r--services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerServiceMigrationTest.java5
-rw-r--r--services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java6
-rw-r--r--services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest2.java14
-rw-r--r--services/tests/uiservicestests/src/com/android/server/notification/NotificationListenerServiceTest.java2
-rwxr-xr-xservices/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java6
-rw-r--r--services/tests/uiservicestests/src/com/android/server/notification/ZenModeConfigTest.java55
-rw-r--r--services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java6
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java24
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/WindowOrganizerTests.java3
226 files changed, 5286 insertions, 1968 deletions
diff --git a/.prebuilt_info/prebuilt_info_packages_CtsShim_apk__arm_CtsShimPriv_apk.asciipb b/.prebuilt_info/prebuilt_info_packages_CtsShim_apk__arm_CtsShimPriv_apk.asciipb
index 80317e4634f3..748fe14cde3e 100644
--- a/.prebuilt_info/prebuilt_info_packages_CtsShim_apk__arm_CtsShimPriv_apk.asciipb
+++ b/.prebuilt_info/prebuilt_info_packages_CtsShim_apk__arm_CtsShimPriv_apk.asciipb
@@ -1,6 +1,6 @@
drops {
android_build_drop {
- build_id: "7396576"
+ build_id: "7464984"
target: "CtsShim"
source_file: "aosp_arm64/CtsShimPriv.apk"
}
diff --git a/.prebuilt_info/prebuilt_info_packages_CtsShim_apk__arm_CtsShim_apk.asciipb b/.prebuilt_info/prebuilt_info_packages_CtsShim_apk__arm_CtsShim_apk.asciipb
index 3605b6d0433b..15103b1f175c 100644
--- a/.prebuilt_info/prebuilt_info_packages_CtsShim_apk__arm_CtsShim_apk.asciipb
+++ b/.prebuilt_info/prebuilt_info_packages_CtsShim_apk__arm_CtsShim_apk.asciipb
@@ -1,6 +1,6 @@
drops {
android_build_drop {
- build_id: "7396576"
+ build_id: "7464984"
target: "CtsShim"
source_file: "aosp_arm64/CtsShim.apk"
}
diff --git a/.prebuilt_info/prebuilt_info_packages_CtsShim_apk__x86_CtsShimPriv_apk.asciipb b/.prebuilt_info/prebuilt_info_packages_CtsShim_apk__x86_CtsShimPriv_apk.asciipb
index 025ec3a9fdaf..2b9ed2fba339 100644
--- a/.prebuilt_info/prebuilt_info_packages_CtsShim_apk__x86_CtsShimPriv_apk.asciipb
+++ b/.prebuilt_info/prebuilt_info_packages_CtsShim_apk__x86_CtsShimPriv_apk.asciipb
@@ -1,6 +1,6 @@
drops {
android_build_drop {
- build_id: "7396576"
+ build_id: "7464984"
target: "CtsShim"
source_file: "aosp_x86_64/CtsShimPriv.apk"
}
diff --git a/.prebuilt_info/prebuilt_info_packages_CtsShim_apk__x86_CtsShim_apk.asciipb b/.prebuilt_info/prebuilt_info_packages_CtsShim_apk__x86_CtsShim_apk.asciipb
index e19235a12f8f..d4ff3b7336b5 100644
--- a/.prebuilt_info/prebuilt_info_packages_CtsShim_apk__x86_CtsShim_apk.asciipb
+++ b/.prebuilt_info/prebuilt_info_packages_CtsShim_apk__x86_CtsShim_apk.asciipb
@@ -1,6 +1,6 @@
drops {
android_build_drop {
- build_id: "7396576"
+ build_id: "7464984"
target: "CtsShim"
source_file: "aosp_x86_64/CtsShim.apk"
}
diff --git a/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java b/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java
index fe0c7f718bb0..fb5129f18417 100644
--- a/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java
+++ b/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java
@@ -2006,16 +2006,12 @@ public class AlarmManagerService extends SystemService {
windowLength = INTERVAL_DAY;
} else if ((flags & FLAG_PRIORITIZE) == 0 && windowLength < minAllowedWindow) {
// Prioritized alarms are exempt from minimum window limits.
- if (CompatChanges.isChangeEnabled(
+ if (!isExemptFromMinWindowRestrictions(callingUid) && CompatChanges.isChangeEnabled(
AlarmManager.ENFORCE_MINIMUM_WINDOW_ON_INEXACT_ALARMS, callingPackage,
UserHandle.getUserHandleForUid(callingUid))) {
Slog.w(TAG, "Window length " + windowLength + "ms too short; expanding to "
+ minAllowedWindow + "ms.");
windowLength = minAllowedWindow;
- } else {
- // TODO (b/185199076): Remove temporary log to catch breaking apps.
- Slog.wtf(TAG, "Short window " + windowLength + "ms specified by "
- + callingPackage);
}
}
maxElapsed = triggerElapsed + windowLength;
@@ -2409,6 +2405,13 @@ public class AlarmManagerService extends SystemService {
}
/**
+ * Returns true if the given uid can set window to be as small as it wants.
+ */
+ boolean isExemptFromMinWindowRestrictions(int uid) {
+ return isExemptFromExactAlarmPermission(uid);
+ }
+
+ /**
* Returns true if the given uid does not require SCHEDULE_EXACT_ALARM to set exact,
* allow-while-idle alarms.
*/
diff --git a/api/Android.bp b/api/Android.bp
index db1f64c57e2c..a84e6a6cb031 100644
--- a/api/Android.bp
+++ b/api/Android.bp
@@ -69,7 +69,10 @@ genrule {
dest: "current.txt",
},
{
- targets: ["sdk", "win_sdk"],
+ targets: [
+ "sdk",
+ "win_sdk",
+ ],
dir: "apistubs/android/public/api",
dest: "android.txt",
},
@@ -151,7 +154,10 @@ genrule {
dest: "removed.txt",
},
{
- targets: ["sdk", "win_sdk"],
+ targets: [
+ "sdk",
+ "win_sdk",
+ ],
dir: "apistubs/android/public/api",
dest: "removed.txt",
},
@@ -187,7 +193,10 @@ genrule {
dest: "system-current.txt",
},
{
- targets: ["sdk", "win_sdk"],
+ targets: [
+ "sdk",
+ "win_sdk",
+ ],
dir: "apistubs/android/system/api",
dest: "android.txt",
},
@@ -242,7 +251,10 @@ genrule {
dest: "system-removed.txt",
},
{
- targets: ["sdk", "win_sdk"],
+ targets: [
+ "sdk",
+ "win_sdk",
+ ],
dir: "apistubs/android/system/api",
dest: "removed.txt",
},
@@ -279,7 +291,10 @@ genrule {
dest: "module-lib-current.txt",
},
{
- targets: ["sdk", "win_sdk"],
+ targets: [
+ "sdk",
+ "win_sdk",
+ ],
dir: "apistubs/android/module-lib/api",
dest: "android.txt",
},
@@ -336,7 +351,10 @@ genrule {
dest: "module-lib-removed.txt",
},
{
- targets: ["sdk", "win_sdk"],
+ targets: [
+ "sdk",
+ "win_sdk",
+ ],
dir: "apistubs/android/module-lib/api",
dest: "removed.txt",
},
@@ -377,7 +395,10 @@ genrule {
dest: "system-server-current.txt",
},
{
- targets: ["sdk", "win_sdk"],
+ targets: [
+ "sdk",
+ "win_sdk",
+ ],
dir: "apistubs/android/system-server/api",
dest: "android.txt",
},
@@ -401,7 +422,10 @@ genrule {
dest: "system-server-removed.txt",
},
{
- targets: ["sdk", "win_sdk"],
+ targets: [
+ "sdk",
+ "win_sdk",
+ ],
dir: "apistubs/android/system-server/api",
dest: "removed.txt",
},
diff --git a/cmds/app_process/Android.bp b/cmds/app_process/Android.bp
index 0eff83c99282..a1575173ded6 100644
--- a/cmds/app_process/Android.bp
+++ b/cmds/app_process/Android.bp
@@ -29,7 +29,16 @@ cc_binary {
},
},
- ldflags: ["-Wl,--export-dynamic"],
+ // Symbols exported from the executable in .dynsym interpose symbols in every
+ // linker namespace, including an app's classloader namespace. Provide this
+ // version script to prevent unwanted interposition.
+ //
+ // By default, the static linker doesn't export most of an executable's symbols,
+ // but it will export a symbol that appears to override a symbol in a needed DSO.
+ // This commonly happens with C++ vaguely-linked entities, such as template
+ // functions or type_info variables. Hence, a version script is needed even for
+ // an executable.
+ version_script: "version-script.txt",
shared_libs: [
"libandroid_runtime",
diff --git a/cmds/app_process/version-script.txt b/cmds/app_process/version-script.txt
new file mode 100644
index 000000000000..a98066a67675
--- /dev/null
+++ b/cmds/app_process/version-script.txt
@@ -0,0 +1,4 @@
+{
+ local:
+ *;
+};
diff --git a/core/api/test-current.txt b/core/api/test-current.txt
index 42708f9d2e15..80664ed0816f 100644
--- a/core/api/test-current.txt
+++ b/core/api/test-current.txt
@@ -3190,6 +3190,7 @@ package android.window {
method @Nullable @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_TASKS) public java.util.List<android.app.ActivityManager.RunningTaskInfo> getChildTasks(@NonNull android.window.WindowContainerToken, @NonNull int[]);
method @Nullable @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_TASKS) public android.window.WindowContainerToken getImeTarget(int);
method @Nullable @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_TASKS) public java.util.List<android.app.ActivityManager.RunningTaskInfo> getRootTasks(int, @NonNull int[]);
+ method @BinderThread public void onAppSplashScreenViewRemoved(int);
method @BinderThread public void onBackPressedOnTaskRoot(@NonNull android.app.ActivityManager.RunningTaskInfo);
method @BinderThread public void onTaskAppeared(@NonNull android.app.ActivityManager.RunningTaskInfo, @NonNull android.view.SurfaceControl);
method @BinderThread public void onTaskInfoChanged(@NonNull android.app.ActivityManager.RunningTaskInfo);
diff --git a/core/java/android/app/ActivityOptions.java b/core/java/android/app/ActivityOptions.java
index 1ce598b5fa18..8e1f263ebf03 100644
--- a/core/java/android/app/ActivityOptions.java
+++ b/core/java/android/app/ActivityOptions.java
@@ -419,7 +419,7 @@ public class ActivityOptions {
private IBinder mLaunchCookie;
private IRemoteTransition mRemoteTransition;
private boolean mOverrideTaskTransition;
- private int mSplashScreenThemeResId;
+ private String mSplashScreenThemeResName;
@SplashScreen.SplashScreenStyle
private int mSplashScreenStyle;
private boolean mRemoveWithTaskOrganizer;
@@ -1174,7 +1174,7 @@ public class ActivityOptions {
mRemoteTransition = IRemoteTransition.Stub.asInterface(opts.getBinder(
KEY_REMOTE_TRANSITION));
mOverrideTaskTransition = opts.getBoolean(KEY_OVERRIDE_TASK_TRANSITION);
- mSplashScreenThemeResId = opts.getInt(KEY_SPLASH_SCREEN_THEME);
+ mSplashScreenThemeResName = opts.getString(KEY_SPLASH_SCREEN_THEME);
mRemoveWithTaskOrganizer = opts.getBoolean(KEY_REMOVE_WITH_TASK_ORGANIZER);
mLaunchedFromBubble = opts.getBoolean(KEY_LAUNCHED_FROM_BUBBLE);
mTransientLaunch = opts.getBoolean(KEY_TRANSIENT_LAUNCH);
@@ -1368,8 +1368,9 @@ public class ActivityOptions {
* Gets whether the activity want to be launched as other theme for the splash screen.
* @hide
*/
- public int getSplashScreenThemeResId() {
- return mSplashScreenThemeResId;
+ @Nullable
+ public String getSplashScreenThemeResName() {
+ return mSplashScreenThemeResName;
}
/**
@@ -1945,8 +1946,8 @@ public class ActivityOptions {
if (mOverrideTaskTransition) {
b.putBoolean(KEY_OVERRIDE_TASK_TRANSITION, mOverrideTaskTransition);
}
- if (mSplashScreenThemeResId != 0) {
- b.putInt(KEY_SPLASH_SCREEN_THEME, mSplashScreenThemeResId);
+ if (mSplashScreenThemeResName != null && !mSplashScreenThemeResName.isEmpty()) {
+ b.putString(KEY_SPLASH_SCREEN_THEME, mSplashScreenThemeResName);
}
if (mRemoveWithTaskOrganizer) {
b.putBoolean(KEY_REMOVE_WITH_TASK_ORGANIZER, mRemoveWithTaskOrganizer);
diff --git a/core/java/android/app/AutomaticZenRule.java b/core/java/android/app/AutomaticZenRule.java
index c3272c1a770d..7a806bdf473d 100644
--- a/core/java/android/app/AutomaticZenRule.java
+++ b/core/java/android/app/AutomaticZenRule.java
@@ -45,6 +45,7 @@ public final class AutomaticZenRule implements Parcelable {
private long creationTime;
private ZenPolicy mZenPolicy;
private boolean mModified = false;
+ private String mPkg;
/**
* Creates an automatic zen rule.
@@ -123,6 +124,7 @@ public final class AutomaticZenRule implements Parcelable {
creationTime = source.readLong();
mZenPolicy = source.readParcelable(null);
mModified = source.readInt() == ENABLED;
+ mPkg = source.readString();
}
/**
@@ -244,6 +246,20 @@ public final class AutomaticZenRule implements Parcelable {
this.configurationActivity = componentName;
}
+ /**
+ * @hide
+ */
+ public void setPackageName(String pkgName) {
+ mPkg = pkgName;
+ }
+
+ /**
+ * @hide
+ */
+ public String getPackageName() {
+ return mPkg;
+ }
+
@Override
public int describeContents() {
return 0;
@@ -265,6 +281,7 @@ public final class AutomaticZenRule implements Parcelable {
dest.writeLong(creationTime);
dest.writeParcelable(mZenPolicy, 0);
dest.writeInt(mModified ? ENABLED : DISABLED);
+ dest.writeString(mPkg);
}
@Override
@@ -273,6 +290,7 @@ public final class AutomaticZenRule implements Parcelable {
.append("enabled=").append(enabled)
.append(",name=").append(name)
.append(",interruptionFilter=").append(interruptionFilter)
+ .append(",pkg=").append(mPkg)
.append(",conditionId=").append(conditionId)
.append(",owner=").append(owner)
.append(",configActivity=").append(configurationActivity)
@@ -294,13 +312,14 @@ public final class AutomaticZenRule implements Parcelable {
&& Objects.equals(other.owner, owner)
&& Objects.equals(other.mZenPolicy, mZenPolicy)
&& Objects.equals(other.configurationActivity, configurationActivity)
+ && Objects.equals(other.mPkg, mPkg)
&& other.creationTime == creationTime;
}
@Override
public int hashCode() {
return Objects.hash(enabled, name, interruptionFilter, conditionId, owner,
- configurationActivity, mZenPolicy, mModified, creationTime);
+ configurationActivity, mZenPolicy, mModified, creationTime, mPkg);
}
public static final @android.annotation.NonNull Parcelable.Creator<AutomaticZenRule> CREATOR
diff --git a/core/java/android/app/INotificationManager.aidl b/core/java/android/app/INotificationManager.aidl
index f33adb3c01d1..098492c8234b 100644
--- a/core/java/android/app/INotificationManager.aidl
+++ b/core/java/android/app/INotificationManager.aidl
@@ -207,7 +207,7 @@ interface INotificationManager
void setNotificationPolicyAccessGrantedForUser(String pkg, int userId, boolean granted);
AutomaticZenRule getAutomaticZenRule(String id);
List<ZenModeConfig.ZenRule> getZenRules();
- String addAutomaticZenRule(in AutomaticZenRule automaticZenRule);
+ String addAutomaticZenRule(in AutomaticZenRule automaticZenRule, String pkg);
boolean updateAutomaticZenRule(String id, in AutomaticZenRule automaticZenRule);
boolean removeAutomaticZenRule(String id);
boolean removeAutomaticZenRules(String packageName);
diff --git a/core/java/android/app/NotificationManager.java b/core/java/android/app/NotificationManager.java
index da03a3da820a..ccf1edb3fecc 100644
--- a/core/java/android/app/NotificationManager.java
+++ b/core/java/android/app/NotificationManager.java
@@ -1182,10 +1182,12 @@ public class NotificationManager {
List<ZenModeConfig.ZenRule> rules = service.getZenRules();
Map<String, AutomaticZenRule> ruleMap = new HashMap<>();
for (ZenModeConfig.ZenRule rule : rules) {
- ruleMap.put(rule.id, new AutomaticZenRule(rule.name, rule.component,
+ AutomaticZenRule azr = new AutomaticZenRule(rule.name, rule.component,
rule.configurationActivity, rule.conditionId, rule.zenPolicy,
zenModeToInterruptionFilter(rule.zenMode), rule.enabled,
- rule.creationTime));
+ rule.creationTime);
+ azr.setPackageName(rule.pkg);
+ ruleMap.put(rule.id, azr);
}
return ruleMap;
} catch (RemoteException e) {
@@ -1226,7 +1228,7 @@ public class NotificationManager {
public String addAutomaticZenRule(AutomaticZenRule automaticZenRule) {
INotificationManager service = getService();
try {
- return service.addAutomaticZenRule(automaticZenRule);
+ return service.addAutomaticZenRule(automaticZenRule, mContext.getPackageName());
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
diff --git a/core/java/android/app/PendingIntent.java b/core/java/android/app/PendingIntent.java
index e68eb7471e42..0136a35e3975 100644
--- a/core/java/android/app/PendingIntent.java
+++ b/core/java/android/app/PendingIntent.java
@@ -365,7 +365,6 @@ public final class PendingIntent implements Parcelable {
}
if (Compatibility.isChangeEnabled(PENDING_INTENT_EXPLICIT_MUTABILITY_REQUIRED)
- && !"com.google.android.apps.gcs".equals(packageName)
&& !flagImmutableSet && !flagMutableSet) {
String msg = packageName + ": Targeting S+ (version " + Build.VERSION_CODES.S
+ " and above) requires that one of FLAG_IMMUTABLE or FLAG_MUTABLE"
diff --git a/core/java/android/app/admin/DevicePolicyManagerInternal.java b/core/java/android/app/admin/DevicePolicyManagerInternal.java
index a9bec98ce405..a0d2977cf09a 100644
--- a/core/java/android/app/admin/DevicePolicyManagerInternal.java
+++ b/core/java/android/app/admin/DevicePolicyManagerInternal.java
@@ -18,7 +18,6 @@ package android.app.admin;
import android.annotation.Nullable;
import android.annotation.UserIdInt;
-import android.app.admin.DevicePolicyManager.OperationSafetyReason;
import android.content.ComponentName;
import android.content.Intent;
import android.os.UserHandle;
@@ -256,13 +255,4 @@ public abstract class DevicePolicyManagerInternal {
* {@link #supportsResetOp(int)} is true.
*/
public abstract void resetOp(int op, String packageName, @UserIdInt int userId);
-
- /**
- * Notifies the system that an unsafe operation reason has changed.
- *
- * @throws IllegalArgumentException if {@code checker} is not the same as set on
- * {@code DevicePolicyManagerService}.
- */
- public abstract void notifyUnsafeOperationStateChanged(DevicePolicySafetyChecker checker,
- @OperationSafetyReason int reason, boolean isSafe);
}
diff --git a/core/java/android/app/admin/DevicePolicyManagerLiteInternal.java b/core/java/android/app/admin/DevicePolicyManagerLiteInternal.java
new file mode 100644
index 000000000000..ccb99470d372
--- /dev/null
+++ b/core/java/android/app/admin/DevicePolicyManagerLiteInternal.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.admin;
+
+import android.app.admin.DevicePolicyManager.OperationSafetyReason;
+
+/**
+ * Device policy manager local system service interface for methods that don't require the
+ * {@code device_admin} feature.
+ *
+ * Maintenance note: if you need to expose information from DPMS to lower level services such as
+ * PM/UM/AM/etc, then exposing it from DevicePolicyManagerInternal is not safe because it may cause
+ * lock order inversion. Consider using {@link DevicePolicyCache} instead.
+ *
+ * @hide Only for use within the system server.
+ */
+public interface DevicePolicyManagerLiteInternal {
+
+ /**
+ * Notifies the system that an unsafe operation reason has changed.
+ *
+ * @throws IllegalArgumentException if {@code checker} is not the same as set on
+ * {@code DevicePolicyManagerService.setDevicePolicySafetyChecker()}.
+ */
+ void notifyUnsafeOperationStateChanged(DevicePolicySafetyChecker checker,
+ @OperationSafetyReason int reason, boolean isSafe);
+}
diff --git a/core/java/android/content/pm/AppSearchShortcutInfo.java b/core/java/android/content/pm/AppSearchShortcutInfo.java
index 63f93bfa24e5..806091e2158d 100644
--- a/core/java/android/content/pm/AppSearchShortcutInfo.java
+++ b/core/java/android/content/pm/AppSearchShortcutInfo.java
@@ -423,7 +423,7 @@ public class AppSearchShortcutInfo extends GenericDocument {
shortLabelResName, longLabel, longLabelResId, longLabelResName, disabledMessage,
disabledMessageResId, disabledMessageResName, categoriesSet, intents, rank, extras,
getCreationTimestampMillis(), flags, iconResId, iconResName, bitmapPath, iconUri,
- disabledReason, persons, locusId, 0);
+ disabledReason, persons, locusId, null);
si.setImplicitRank(implicitRank);
if ((implicitRank & ShortcutInfo.RANK_CHANGED_BIT) != 0) {
si.setRankChanged();
diff --git a/core/java/android/content/pm/ShortcutInfo.java b/core/java/android/content/pm/ShortcutInfo.java
index 76712b5ce2dc..a264bebb5d88 100644
--- a/core/java/android/content/pm/ShortcutInfo.java
+++ b/core/java/android/content/pm/ShortcutInfo.java
@@ -449,7 +449,7 @@ public final class ShortcutInfo implements Parcelable {
private int mDisabledReason;
- private int mStartingThemeResId;
+ @Nullable private String mStartingThemeResName;
private ShortcutInfo(Builder b) {
mUserId = b.mContext.getUserId();
@@ -478,8 +478,9 @@ public final class ShortcutInfo implements Parcelable {
mExtras = b.mExtras;
mLocusId = b.mLocusId;
+ mStartingThemeResName = b.mStartingThemeResId != 0
+ ? b.mContext.getResources().getResourceName(b.mStartingThemeResId) : null;
updateTimestamp();
- mStartingThemeResId = b.mStartingThemeResId;
}
/**
@@ -626,7 +627,7 @@ public final class ShortcutInfo implements Parcelable {
// Set this bit.
mFlags |= FLAG_KEY_FIELDS_ONLY;
}
- mStartingThemeResId = source.mStartingThemeResId;
+ mStartingThemeResName = source.mStartingThemeResName;
}
/**
@@ -950,8 +951,8 @@ public final class ShortcutInfo implements Parcelable {
if (source.mLocusId != null) {
mLocusId = source.mLocusId;
}
- if (source.mStartingThemeResId != 0) {
- mStartingThemeResId = source.mStartingThemeResId;
+ if (source.mStartingThemeResName != null && !source.mStartingThemeResName.isEmpty()) {
+ mStartingThemeResName = source.mStartingThemeResName;
}
}
@@ -1454,11 +1455,12 @@ public final class ShortcutInfo implements Parcelable {
}
/**
- * Returns the theme resource id used for the splash screen.
+ * Returns the theme resource name used for the splash screen.
* @hide
*/
- public int getStartingThemeResId() {
- return mStartingThemeResId;
+ @Nullable
+ public String getStartingThemeResName() {
+ return mStartingThemeResName;
}
/** @hide -- old signature, the internal code still uses it. */
@@ -2182,7 +2184,7 @@ public final class ShortcutInfo implements Parcelable {
mPersons = source.readParcelableArray(cl, Person.class);
mLocusId = source.readParcelable(cl);
mIconUri = source.readString8();
- mStartingThemeResId = source.readInt();
+ mStartingThemeResName = source.readString8();
}
@Override
@@ -2234,7 +2236,7 @@ public final class ShortcutInfo implements Parcelable {
dest.writeParcelableArray(mPersons, flags);
dest.writeParcelable(mLocusId, flags);
dest.writeString8(mIconUri);
- dest.writeInt(mStartingThemeResId);
+ dest.writeString8(mStartingThemeResName);
}
public static final @NonNull Creator<ShortcutInfo> CREATOR =
@@ -2391,10 +2393,10 @@ public final class ShortcutInfo implements Parcelable {
sb.append("disabledReason=");
sb.append(getDisabledReasonDebugString(mDisabledReason));
- if (mStartingThemeResId != 0) {
+ if (mStartingThemeResName != null && !mStartingThemeResName.isEmpty()) {
addIndentOrComma(sb, indent);
- sb.append("SplashScreenThemeResId=");
- sb.append(Integer.toHexString(mStartingThemeResId));
+ sb.append("SplashScreenThemeResName=");
+ sb.append(mStartingThemeResName);
}
addIndentOrComma(sb, indent);
@@ -2482,7 +2484,8 @@ public final class ShortcutInfo implements Parcelable {
Set<String> categories, Intent[] intentsWithExtras, int rank, PersistableBundle extras,
long lastChangedTimestamp,
int flags, int iconResId, String iconResName, String bitmapPath, String iconUri,
- int disabledReason, Person[] persons, LocusId locusId, int startingThemeResId) {
+ int disabledReason, Person[] persons, LocusId locusId,
+ @Nullable String startingThemeResName) {
mUserId = userId;
mId = id;
mPackageName = packageName;
@@ -2511,6 +2514,6 @@ public final class ShortcutInfo implements Parcelable {
mDisabledReason = disabledReason;
mPersons = persons;
mLocusId = locusId;
- mStartingThemeResId = startingThemeResId;
+ mStartingThemeResName = startingThemeResName;
}
}
diff --git a/core/java/android/content/pm/ShortcutServiceInternal.java b/core/java/android/content/pm/ShortcutServiceInternal.java
index 233abf36131b..3ed5c6457fa5 100644
--- a/core/java/android/content/pm/ShortcutServiceInternal.java
+++ b/core/java/android/content/pm/ShortcutServiceInternal.java
@@ -74,7 +74,7 @@ public abstract class ShortcutServiceInternal {
/**
* Get the theme res ID of the starting window, it can be 0 if not specified.
*/
- public abstract int getShortcutStartingThemeResId(int launcherUserId,
+ public abstract @Nullable String getShortcutStartingThemeResName(int launcherUserId,
@NonNull String callingPackage, @NonNull String packageName, @NonNull String shortcutId,
int userId);
diff --git a/core/java/android/hardware/biometrics/BiometricAuthenticator.java b/core/java/android/hardware/biometrics/BiometricAuthenticator.java
index a0027077e1a3..60a365835e2e 100644
--- a/core/java/android/hardware/biometrics/BiometricAuthenticator.java
+++ b/core/java/android/hardware/biometrics/BiometricAuthenticator.java
@@ -63,6 +63,11 @@ public interface BiometricAuthenticator {
*/
int TYPE_FACE = 1 << 3;
+ /**
+ * @hide
+ */
+ int TYPE_ANY_BIOMETRIC = TYPE_FINGERPRINT | TYPE_IRIS | TYPE_FACE;
+
@IntDef(flag = true, value = {
TYPE_NONE,
TYPE_CREDENTIAL,
diff --git a/core/java/android/os/AggregateBatteryConsumer.java b/core/java/android/os/AggregateBatteryConsumer.java
index ee86265e81ad..802387c675b5 100644
--- a/core/java/android/os/AggregateBatteryConsumer.java
+++ b/core/java/android/os/AggregateBatteryConsumer.java
@@ -17,7 +17,13 @@
package android.os;
import android.annotation.NonNull;
+import android.util.TypedXmlPullParser;
+import android.util.TypedXmlSerializer;
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+
+import java.io.IOException;
import java.io.PrintWriter;
/**
@@ -72,6 +78,43 @@ public final class AggregateBatteryConsumer extends BatteryConsumer implements P
return mConsumedPowerMah;
}
+ /** Serializes this object to XML */
+ void writeToXml(TypedXmlSerializer serializer,
+ @BatteryUsageStats.AggregateBatteryConsumerScope int scope) throws IOException {
+ serializer.startTag(null, BatteryUsageStats.XML_TAG_AGGREGATE);
+ serializer.attributeInt(null, BatteryUsageStats.XML_ATTR_SCOPE, scope);
+ serializer.attributeDouble(null, BatteryUsageStats.XML_ATTR_POWER, mConsumedPowerMah);
+ mPowerComponents.writeToXml(serializer);
+ serializer.endTag(null, BatteryUsageStats.XML_TAG_AGGREGATE);
+ }
+
+ /** Parses an XML representation and populates the BatteryUsageStats builder */
+ static void parseXml(TypedXmlPullParser parser, BatteryUsageStats.Builder builder)
+ throws XmlPullParserException, IOException {
+ final int scope = parser.getAttributeInt(null, BatteryUsageStats.XML_ATTR_SCOPE);
+ final Builder consumerBuilder = builder.getAggregateBatteryConsumerBuilder(scope);
+
+ int eventType = parser.getEventType();
+ if (eventType != XmlPullParser.START_TAG || !parser.getName().equals(
+ BatteryUsageStats.XML_TAG_AGGREGATE)) {
+ throw new XmlPullParserException("Invalid XML parser state");
+ }
+
+ consumerBuilder.setConsumedPower(
+ parser.getAttributeDouble(null, BatteryUsageStats.XML_ATTR_POWER));
+
+ while (!(eventType == XmlPullParser.END_TAG && parser.getName().equals(
+ BatteryUsageStats.XML_TAG_AGGREGATE))
+ && eventType != XmlPullParser.END_DOCUMENT) {
+ if (eventType == XmlPullParser.START_TAG) {
+ if (parser.getName().equals(BatteryUsageStats.XML_TAG_POWER_COMPONENTS)) {
+ PowerComponents.parseXml(parser, consumerBuilder.mPowerComponentsBuilder);
+ }
+ }
+ eventType = parser.next();
+ }
+ }
+
/**
* Builder for DeviceBatteryConsumer.
*/
@@ -91,6 +134,14 @@ public final class AggregateBatteryConsumer extends BatteryConsumer implements P
}
/**
+ * Adds power and usage duration from the supplied AggregateBatteryConsumer.
+ */
+ public void add(AggregateBatteryConsumer aggregateBatteryConsumer) {
+ mConsumedPowerMah += aggregateBatteryConsumer.mConsumedPowerMah;
+ mPowerComponentsBuilder.addPowerAndDuration(aggregateBatteryConsumer.mPowerComponents);
+ }
+
+ /**
* Creates a read-only object out of the Builder values.
*/
@NonNull
diff --git a/core/java/android/os/BatteryUsageStats.java b/core/java/android/os/BatteryUsageStats.java
index 6c9f0f677db9..77f8a87cdcd1 100644
--- a/core/java/android/os/BatteryUsageStats.java
+++ b/core/java/android/os/BatteryUsageStats.java
@@ -20,16 +20,23 @@ import android.annotation.IntDef;
import android.annotation.NonNull;
import android.util.Range;
import android.util.SparseArray;
+import android.util.TypedXmlPullParser;
+import android.util.TypedXmlSerializer;
import android.util.proto.ProtoOutputStream;
import com.android.internal.os.BatteryStatsHistory;
import com.android.internal.os.BatteryStatsHistoryIterator;
import com.android.internal.os.PowerCalculator;
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+
+import java.io.IOException;
import java.io.PrintWriter;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.Comparator;
import java.util.List;
@@ -77,6 +84,34 @@ public final class BatteryUsageStats implements Parcelable {
private static final int STATSD_PULL_ATOM_MAX_BYTES = 45000;
+ // XML tags and attributes for BatteryUsageStats persistence
+ static final String XML_TAG_BATTERY_USAGE_STATS = "battery_usage_stats";
+ static final String XML_TAG_AGGREGATE = "aggregate";
+ static final String XML_TAG_UID = "uid";
+ static final String XML_TAG_USER = "user";
+ static final String XML_TAG_POWER_COMPONENTS = "power_components";
+ static final String XML_TAG_COMPONENT = "component";
+ static final String XML_TAG_CUSTOM_COMPONENT = "custom_component";
+ static final String XML_ATTR_ID = "id";
+ static final String XML_ATTR_UID = "uid";
+ static final String XML_ATTR_USER_ID = "user_id";
+ static final String XML_ATTR_SCOPE = "scope";
+ static final String XML_ATTR_PREFIX_CUSTOM_COMPONENT = "custom_component_";
+ static final String XML_ATTR_START_TIMESTAMP = "start_timestamp";
+ static final String XML_ATTR_END_TIMESTAMP = "end_timestamp";
+ static final String XML_ATTR_POWER = "power";
+ static final String XML_ATTR_DURATION = "duration";
+ static final String XML_ATTR_MODEL = "model";
+ static final String XML_ATTR_BATTERY_CAPACITY = "battery_capacity";
+ static final String XML_ATTR_DISCHARGE_PERCENT = "discharge_pct";
+ static final String XML_ATTR_DISCHARGE_LOWER = "discharge_lower";
+ static final String XML_ATTR_DISCHARGE_UPPER = "discharge_upper";
+ static final String XML_ATTR_BATTERY_REMAINING = "battery_remaining";
+ static final String XML_ATTR_CHARGE_REMAINING = "charge_remaining";
+ static final String XML_ATTR_HIGHEST_DRAIN_PACKAGE = "highest_drain_package";
+ static final String XML_ATTR_TIME_IN_FOREGROUND = "time_in_foreground";
+ static final String XML_ATTR_TIME_IN_BACKGROUND = "time_in_background";
+
private final int mDischargePercentage;
private final double mBatteryCapacityMah;
private final long mStatsStartTimestampMs;
@@ -96,11 +131,7 @@ public final class BatteryUsageStats implements Parcelable {
private BatteryUsageStats(@NonNull Builder builder) {
mStatsStartTimestampMs = builder.mStatsStartTimestampMs;
mStatsEndTimestampMs = builder.mStatsEndTimestampMs;
- if (builder.mStatsDurationMs != -1) {
- mStatsDurationMs = builder.mStatsDurationMs;
- } else {
- mStatsDurationMs = mStatsEndTimestampMs - mStatsStartTimestampMs;
- }
+ mStatsDurationMs = builder.getStatsDuration();
mBatteryCapacityMah = builder.mBatteryCapacityMah;
mDischargePercentage = builder.mDischargePercentage;
mDischargedPowerLowerBound = builder.mDischargedPowerLowerBoundMah;
@@ -579,6 +610,109 @@ public final class BatteryUsageStats implements Parcelable {
}
}
+ /** Serializes this object to XML */
+ public void writeXml(TypedXmlSerializer serializer) throws IOException {
+ serializer.startTag(null, XML_TAG_BATTERY_USAGE_STATS);
+
+ for (int i = 0; i < mCustomPowerComponentNames.length; i++) {
+ serializer.attribute(null, XML_ATTR_PREFIX_CUSTOM_COMPONENT + i,
+ mCustomPowerComponentNames[i]);
+ }
+
+ serializer.attributeLong(null, XML_ATTR_START_TIMESTAMP, mStatsStartTimestampMs);
+ serializer.attributeLong(null, XML_ATTR_END_TIMESTAMP, mStatsEndTimestampMs);
+ serializer.attributeLong(null, XML_ATTR_DURATION, mStatsDurationMs);
+ serializer.attributeDouble(null, XML_ATTR_BATTERY_CAPACITY, mBatteryCapacityMah);
+ serializer.attributeInt(null, XML_ATTR_DISCHARGE_PERCENT, mDischargePercentage);
+ serializer.attributeDouble(null, XML_ATTR_DISCHARGE_LOWER, mDischargedPowerLowerBound);
+ serializer.attributeDouble(null, XML_ATTR_DISCHARGE_UPPER, mDischargedPowerUpperBound);
+ serializer.attributeLong(null, XML_ATTR_BATTERY_REMAINING, mBatteryTimeRemainingMs);
+ serializer.attributeLong(null, XML_ATTR_CHARGE_REMAINING, mChargeTimeRemainingMs);
+
+ for (int scope = 0; scope < BatteryUsageStats.AGGREGATE_BATTERY_CONSUMER_SCOPE_COUNT;
+ scope++) {
+ mAggregateBatteryConsumers[scope].writeToXml(serializer, scope);
+ }
+ for (UidBatteryConsumer consumer : mUidBatteryConsumers) {
+ consumer.writeToXml(serializer);
+ }
+ for (UserBatteryConsumer consumer : mUserBatteryConsumers) {
+ consumer.writeToXml(serializer);
+ }
+ serializer.endTag(null, XML_TAG_BATTERY_USAGE_STATS);
+ }
+
+ /** Parses an XML representation of BatteryUsageStats */
+ public static BatteryUsageStats createFromXml(TypedXmlPullParser parser)
+ throws XmlPullParserException, IOException {
+ Builder builder = null;
+ int eventType = parser.getEventType();
+ while (eventType != XmlPullParser.END_DOCUMENT) {
+ if (eventType == XmlPullParser.START_TAG
+ && parser.getName().equals(XML_TAG_BATTERY_USAGE_STATS)) {
+ List<String> customComponentNames = new ArrayList<>();
+ int i = 0;
+ while (true) {
+ int index = parser.getAttributeIndex(null,
+ XML_ATTR_PREFIX_CUSTOM_COMPONENT + i);
+ if (index == -1) {
+ break;
+ }
+ customComponentNames.add(parser.getAttributeValue(index));
+ i++;
+ }
+
+ builder = new Builder(
+ customComponentNames.toArray(new String[0]), true);
+
+ builder.setStatsStartTimestamp(
+ parser.getAttributeLong(null, XML_ATTR_START_TIMESTAMP));
+ builder.setStatsEndTimestamp(
+ parser.getAttributeLong(null, XML_ATTR_END_TIMESTAMP));
+ builder.setStatsDuration(
+ parser.getAttributeLong(null, XML_ATTR_DURATION));
+ builder.setBatteryCapacity(
+ parser.getAttributeDouble(null, XML_ATTR_BATTERY_CAPACITY));
+ builder.setDischargePercentage(
+ parser.getAttributeInt(null, XML_ATTR_DISCHARGE_PERCENT));
+ builder.setDischargedPowerRange(
+ parser.getAttributeDouble(null, XML_ATTR_DISCHARGE_LOWER),
+ parser.getAttributeDouble(null, XML_ATTR_DISCHARGE_UPPER));
+ builder.setBatteryTimeRemainingMs(
+ parser.getAttributeLong(null, XML_ATTR_BATTERY_REMAINING));
+ builder.setChargeTimeRemainingMs(
+ parser.getAttributeLong(null, XML_ATTR_CHARGE_REMAINING));
+
+ eventType = parser.next();
+ break;
+ }
+ eventType = parser.next();
+ }
+
+ if (builder == null) {
+ throw new XmlPullParserException("No root element");
+ }
+
+ while (eventType != XmlPullParser.END_DOCUMENT) {
+ if (eventType == XmlPullParser.START_TAG) {
+ switch (parser.getName()) {
+ case XML_TAG_AGGREGATE:
+ AggregateBatteryConsumer.parseXml(parser, builder);
+ break;
+ case XML_TAG_UID:
+ UidBatteryConsumer.createFromXml(parser, builder);
+ break;
+ case XML_TAG_USER:
+ UserBatteryConsumer.createFromXml(parser, builder);
+ break;
+ }
+ }
+ eventType = parser.next();
+ }
+
+ return builder.build();
+ }
+
/**
* Builder for BatteryUsageStats.
*/
@@ -658,6 +792,14 @@ public final class BatteryUsageStats implements Parcelable {
return this;
}
+ private long getStatsDuration() {
+ if (mStatsDurationMs != -1) {
+ return mStatsDurationMs;
+ } else {
+ return mStatsEndTimestampMs - mStatsStartTimestampMs;
+ }
+ }
+
/**
* Sets the battery discharge amount since BatteryStats reset as percentage of the full
* charge.
@@ -738,6 +880,22 @@ public final class BatteryUsageStats implements Parcelable {
}
/**
+ * Creates or returns a UidBatteryConsumer, which represents battery attribution
+ * data for an individual UID. This version of the method is not suitable for use
+ * with PowerCalculators.
+ */
+ @NonNull
+ public UidBatteryConsumer.Builder getOrCreateUidBatteryConsumerBuilder(int uid) {
+ UidBatteryConsumer.Builder builder = mUidBatteryConsumerBuilders.get(uid);
+ if (builder == null) {
+ builder = new UidBatteryConsumer.Builder(mCustomPowerComponentNames,
+ mIncludePowerModels, uid);
+ mUidBatteryConsumerBuilders.put(uid, builder);
+ }
+ return builder;
+ }
+
+ /**
* Creates or returns a UserBatteryConsumer, which represents battery attribution
* data for an individual {@link UserHandle}.
*/
@@ -756,5 +914,59 @@ public final class BatteryUsageStats implements Parcelable {
public SparseArray<UidBatteryConsumer.Builder> getUidBatteryConsumerBuilders() {
return mUidBatteryConsumerBuilders;
}
+
+ /**
+ * Adds battery usage stats from another snapshots. The two snapshots are assumed to be
+ * non-overlapping, meaning that the power consumption estimates and session durations
+ * can be simply summed across the two snapshots. This remains true even if the timestamps
+ * seem to indicate that the sessions are in fact overlapping: timestamps may be off as a
+ * result of realtime clock adjustments by the user or the system.
+ */
+ @NonNull
+ public Builder add(BatteryUsageStats stats) {
+ if (!Arrays.equals(mCustomPowerComponentNames, stats.mCustomPowerComponentNames)) {
+ throw new IllegalArgumentException(
+ "BatteryUsageStats have different custom power components");
+ }
+
+ if (mUserBatteryConsumerBuilders.size() != 0
+ || !stats.getUserBatteryConsumers().isEmpty()) {
+ throw new UnsupportedOperationException(
+ "Combining UserBatteryConsumers is not supported");
+ }
+
+ mDischargedPowerLowerBoundMah += stats.mDischargedPowerLowerBound;
+ mDischargedPowerUpperBoundMah += stats.mDischargedPowerUpperBound;
+ mDischargePercentage += stats.mDischargePercentage;
+
+ mStatsDurationMs = getStatsDuration() + stats.getStatsDuration();
+
+ if (mStatsStartTimestampMs == 0
+ || stats.mStatsStartTimestampMs < mStatsStartTimestampMs) {
+ mStatsStartTimestampMs = stats.mStatsStartTimestampMs;
+ }
+
+ final boolean addingLaterSnapshot = stats.mStatsEndTimestampMs > mStatsEndTimestampMs;
+ if (addingLaterSnapshot) {
+ mStatsEndTimestampMs = stats.mStatsEndTimestampMs;
+ }
+
+ for (int scope = 0; scope < AGGREGATE_BATTERY_CONSUMER_SCOPE_COUNT; scope++) {
+ getAggregateBatteryConsumerBuilder(scope)
+ .add(stats.mAggregateBatteryConsumers[scope]);
+ }
+
+ for (UidBatteryConsumer consumer : stats.getUidBatteryConsumers()) {
+ getOrCreateUidBatteryConsumerBuilder(consumer.getUid()).add(consumer);
+ }
+
+ if (addingLaterSnapshot) {
+ mBatteryCapacityMah = stats.mBatteryCapacityMah;
+ mBatteryTimeRemainingMs = stats.mBatteryTimeRemainingMs;
+ mChargeTimeRemainingMs = stats.mChargeTimeRemainingMs;
+ }
+
+ return this;
+ }
}
}
diff --git a/core/java/android/os/BatteryUsageStatsQuery.java b/core/java/android/os/BatteryUsageStatsQuery.java
index 50804422e92f..97f24ccecaee 100644
--- a/core/java/android/os/BatteryUsageStatsQuery.java
+++ b/core/java/android/os/BatteryUsageStatsQuery.java
@@ -72,12 +72,16 @@ public final class BatteryUsageStatsQuery implements Parcelable {
@NonNull
private final int[] mUserIds;
private final long mMaxStatsAgeMs;
+ private long mFromTimestamp;
+ private long mToTimestamp;
private BatteryUsageStatsQuery(@NonNull Builder builder) {
mFlags = builder.mFlags;
mUserIds = builder.mUserIds != null ? builder.mUserIds.toArray()
: new int[]{UserHandle.USER_ALL};
mMaxStatsAgeMs = builder.mMaxStatsAgeMs;
+ mFromTimestamp = builder.mFromTimestamp;
+ mToTimestamp = builder.mToTimestamp;
}
@BatteryUsageStatsFlags
@@ -112,11 +116,30 @@ public final class BatteryUsageStatsQuery implements Parcelable {
return mMaxStatsAgeMs;
}
+ /**
+ * Returns the exclusive lower bound of the stored snapshot timestamps that should be included
+ * in the aggregation. Ignored if {@link #getToTimestamp()} is zero.
+ */
+ public long getFromTimestamp() {
+ return mFromTimestamp;
+ }
+
+ /**
+ * Returns the inclusive upper bound of the stored snapshot timestamps that should
+ * be included in the aggregation. The default is to include only the current stats
+ * accumulated since the latest battery reset.
+ */
+ public long getToTimestamp() {
+ return mToTimestamp;
+ }
+
private BatteryUsageStatsQuery(Parcel in) {
mFlags = in.readInt();
mUserIds = new int[in.readInt()];
in.readIntArray(mUserIds);
mMaxStatsAgeMs = in.readLong();
+ mFromTimestamp = in.readLong();
+ mToTimestamp = in.readLong();
}
@Override
@@ -125,6 +148,8 @@ public final class BatteryUsageStatsQuery implements Parcelable {
dest.writeInt(mUserIds.length);
dest.writeIntArray(mUserIds);
dest.writeLong(mMaxStatsAgeMs);
+ dest.writeLong(mFromTimestamp);
+ dest.writeLong(mToTimestamp);
}
@Override
@@ -153,6 +178,8 @@ public final class BatteryUsageStatsQuery implements Parcelable {
private int mFlags;
private IntArray mUserIds;
private long mMaxStatsAgeMs = DEFAULT_MAX_STATS_AGE_MS;
+ private long mFromTimestamp;
+ private long mToTimestamp;
/**
* Builds a read-only BatteryUsageStatsQuery object.
@@ -204,6 +231,17 @@ public final class BatteryUsageStatsQuery implements Parcelable {
}
/**
+ * Requests to aggregate stored snapshots between the two supplied timestamps
+ * @param fromTimestamp Exclusive starting timestamp, as per System.currentTimeMillis()
+ * @param toTimestamp Inclusive ending timestamp, as per System.currentTimeMillis()
+ */
+ public Builder aggregateSnapshots(long fromTimestamp, long toTimestamp) {
+ mFromTimestamp = fromTimestamp;
+ mToTimestamp = toTimestamp;
+ return this;
+ }
+
+ /**
* Set the client's tolerance for stale battery stats. The data may be up to
* this many milliseconds out-of-date.
*/
diff --git a/core/java/android/os/PackageTagsList.java b/core/java/android/os/PackageTagsList.java
index c94d3de33b6f..4a81dc6f592e 100644
--- a/core/java/android/os/PackageTagsList.java
+++ b/core/java/android/os/PackageTagsList.java
@@ -23,20 +23,26 @@ import android.annotation.TestApi;
import android.util.ArrayMap;
import android.util.ArraySet;
+import com.android.internal.annotations.Immutable;
+
import java.io.PrintWriter;
+import java.util.Collection;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
/**
- * A list of packages and associated attribution tags that supports easy membership checks.
+ * A list of packages and associated attribution tags that supports easy membership checks. Supports
+ * "wildcard" attribution tags (ie, matching any attribution tag under a package) in additional to
+ * standard checks.
*
* @hide
*/
@TestApi
+@Immutable
public final class PackageTagsList implements Parcelable {
- // an empty set value matches any attribution tag
+ // an empty set value matches any attribution tag (ie, wildcard)
private final ArrayMap<String, ArraySet<String>> mPackageTags;
private PackageTagsList(@NonNull ArrayMap<String, ArraySet<String>> packageTags) {
@@ -51,15 +57,34 @@ public final class PackageTagsList implements Parcelable {
}
/**
- * Returns true if the given package is represented within this instance. If this returns true
- * this does not imply anything about whether any given attribution tag under the given package
- * name is present.
+ * Returns true if the given package is found within this instance. If this returns true this
+ * does not imply anything about whether any given attribution tag under the given package name
+ * is present.
*/
public boolean includes(@NonNull String packageName) {
return mPackageTags.containsKey(packageName);
}
/**
+ * Returns true if the given attribution tag is found within this instance under any package.
+ * Only returns true if the attribution tag literal is found, not if any package contains the
+ * set of all attribution tags.
+ *
+ * @hide
+ */
+ public boolean includesTag(@NonNull String attributionTag) {
+ final int size = mPackageTags.size();
+ for (int i = 0; i < size; i++) {
+ ArraySet<String> tags = mPackageTags.valueAt(i);
+ if (tags.contains(attributionTag)) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ /**
* Returns true if all attribution tags under the given package are contained within this
* instance.
*/
@@ -76,6 +101,7 @@ public final class PackageTagsList implements Parcelable {
if (tags == null) {
return false;
} else if (tags.isEmpty()) {
+ // our tags are the full set, so we contain any attribution tag
return true;
} else {
return tags.contains(attributionTag);
@@ -98,10 +124,12 @@ public final class PackageTagsList implements Parcelable {
return false;
}
if (tags.isEmpty()) {
+ // our tags are the full set, so we contain whatever the other tags are
continue;
}
ArraySet<String> otherTags = packageTagsList.mPackageTags.valueAt(i);
if (otherTags.isEmpty()) {
+ // other tags are the full set, so we can't contain them
return false;
}
if (!tags.containsAll(otherTags)) {
@@ -248,6 +276,31 @@ public final class PackageTagsList implements Parcelable {
}
/**
+ * Adds the specified package and set of attribution tags to the builder.
+ *
+ * @hide
+ */
+ @SuppressLint("MissingGetterMatchingBuilder")
+ public @NonNull Builder add(@NonNull String packageName,
+ @NonNull Collection<String> attributionTags) {
+ if (attributionTags.isEmpty()) {
+ // the input is not allowed to specify a full set by passing in an empty collection
+ return this;
+ }
+
+ ArraySet<String> tags = mPackageTags.get(packageName);
+ if (tags == null) {
+ tags = new ArraySet<>(attributionTags);
+ mPackageTags.put(packageName, tags);
+ } else if (!tags.isEmpty()) {
+ // if we contain the full set, already done, otherwise add all the tags
+ tags.addAll(attributionTags);
+ }
+
+ return this;
+ }
+
+ /**
* Adds the specified {@link PackageTagsList} to the builder.
*/
@SuppressLint("MissingGetterMatchingBuilder")
@@ -267,13 +320,92 @@ public final class PackageTagsList implements Parcelable {
if (newTags.isEmpty()) {
add(entry.getKey());
} else {
- ArraySet<String> tags = mPackageTags.get(entry.getKey());
- if (tags == null) {
- tags = new ArraySet<>(newTags);
- mPackageTags.put(entry.getKey(), tags);
- } else if (!tags.isEmpty()) {
- tags.addAll(newTags);
- }
+ add(entry.getKey(), newTags);
+ }
+ }
+
+ return this;
+ }
+
+ /**
+ * Removes all attribution tags under the specified package from the builder.
+ *
+ * @hide
+ */
+ @SuppressLint("MissingGetterMatchingBuilder")
+ public @NonNull Builder remove(@NonNull String packageName) {
+ mPackageTags.remove(packageName);
+ return this;
+ }
+
+ /**
+ * Removes the specified package and attribution tag from the builder if and only if the
+ * specified attribution tag is listed explicitly under the package. If the package contains
+ * all possible attribution tags, then nothing will be removed.
+ *
+ * @hide
+ */
+ @SuppressLint("MissingGetterMatchingBuilder")
+ public @NonNull Builder remove(@NonNull String packageName,
+ @Nullable String attributionTag) {
+ ArraySet<String> tags = mPackageTags.get(packageName);
+ if (tags != null && tags.remove(attributionTag) && tags.isEmpty()) {
+ mPackageTags.remove(packageName);
+ }
+ return this;
+ }
+
+ /**
+ * Removes the specified package and set of attribution tags from the builder if and only if
+ * the specified set of attribution tags are listed explicitly under the package. If the
+ * package contains all possible attribution tags, then nothing will be removed.
+ *
+ * @hide
+ */
+ @SuppressLint("MissingGetterMatchingBuilder")
+ public @NonNull Builder remove(@NonNull String packageName,
+ @NonNull Collection<String> attributionTags) {
+ if (attributionTags.isEmpty()) {
+ // the input is not allowed to specify a full set by passing in an empty collection
+ return this;
+ }
+
+ ArraySet<String> tags = mPackageTags.get(packageName);
+ if (tags != null && tags.removeAll(attributionTags) && tags.isEmpty()) {
+ mPackageTags.remove(packageName);
+ }
+ return this;
+ }
+
+ /**
+ * Removes the specified {@link PackageTagsList} from the builder. If a package contains all
+ * possible attribution tags, it will only be removed if the package in the removed
+ * {@link PackageTagsList} also contains all possible attribution tags.
+ *
+ * @hide
+ */
+ @SuppressLint("MissingGetterMatchingBuilder")
+ public @NonNull Builder remove(@NonNull PackageTagsList packageTagsList) {
+ return remove(packageTagsList.mPackageTags);
+ }
+
+ /**
+ * Removes the given map of package to attribution tags to the builder. An empty set of
+ * attribution tags is interpreted to imply all attribution tags under that package. If a
+ * package contains all possible attribution tags, it will only be removed if the package in
+ * the removed map also contains all possible attribution tags.
+ *
+ * @hide
+ */
+ @SuppressLint("MissingGetterMatchingBuilder")
+ public @NonNull Builder remove(@NonNull Map<String, ? extends Set<String>> packageTagsMap) {
+ for (Map.Entry<String, ? extends Set<String>> entry : packageTagsMap.entrySet()) {
+ Set<String> removedTags = entry.getValue();
+ if (removedTags.isEmpty()) {
+ // if removing the full set, drop the package completely
+ remove(entry.getKey());
+ } else {
+ remove(entry.getKey(), removedTags);
}
}
diff --git a/core/java/android/os/PowerComponents.java b/core/java/android/os/PowerComponents.java
index a90ed20d54fc..db3d13bdb07b 100644
--- a/core/java/android/os/PowerComponents.java
+++ b/core/java/android/os/PowerComponents.java
@@ -19,11 +19,18 @@ import static android.os.BatteryConsumer.convertMahToDeciCoulombs;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.util.TypedXmlPullParser;
+import android.util.TypedXmlSerializer;
import android.util.proto.ProtoOutputStream;
import com.android.internal.os.PowerCalculator;
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+
+import java.io.IOException;
import java.io.PrintWriter;
+import java.util.Arrays;
/**
* Contains details of battery attribution data broken down to individual power drain types
@@ -36,9 +43,12 @@ class PowerComponents {
- BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID;
private final double mConsumedPowerMah;
+ @NonNull
private final double[] mPowerComponentsMah;
+ @NonNull
private final long[] mUsageDurationsMs;
private final int mCustomPowerComponentCount;
+ @Nullable
private final byte[] mPowerModels;
// Not written to Parcel and must be explicitly restored during the parent object's unparceling
private String[] mCustomPowerComponentNames;
@@ -49,7 +59,7 @@ class PowerComponents {
mPowerComponentsMah = builder.mPowerComponentsMah;
mUsageDurationsMs = builder.mUsageDurationsMs;
mConsumedPowerMah = builder.getTotalPower();
- mPowerModels = builder.mPowerModels;
+ mPowerModels = builder.getPowerModels();
}
PowerComponents(@NonNull Parcel source) {
@@ -146,9 +156,13 @@ class PowerComponents {
}
}
+ public boolean hasPowerModels() {
+ return mPowerModels != null;
+ }
+
@BatteryConsumer.PowerModel
int getPowerModel(@BatteryConsumer.PowerComponent int component) {
- if (mPowerModels == null) {
+ if (!hasPowerModels()) {
throw new IllegalStateException(
"Power model IDs were not requested in the BatteryUsageStatsQuery");
}
@@ -294,10 +308,128 @@ class PowerComponents {
return interestingData;
}
+ void writeToXml(TypedXmlSerializer serializer) throws IOException {
+ serializer.startTag(null, BatteryUsageStats.XML_TAG_POWER_COMPONENTS);
+ for (int componentId = 0; componentId < BatteryConsumer.POWER_COMPONENT_COUNT;
+ componentId++) {
+ final double powerMah = getConsumedPower(componentId);
+ final long durationMs = getUsageDurationMillis(componentId);
+ if (powerMah == 0 && durationMs == 0) {
+ continue;
+ }
+
+ serializer.startTag(null, BatteryUsageStats.XML_TAG_COMPONENT);
+ serializer.attributeInt(null, BatteryUsageStats.XML_ATTR_ID, componentId);
+ if (powerMah != 0) {
+ serializer.attributeDouble(null, BatteryUsageStats.XML_ATTR_POWER, powerMah);
+ }
+ if (durationMs != 0) {
+ serializer.attributeLong(null, BatteryUsageStats.XML_ATTR_DURATION, durationMs);
+ }
+ if (mPowerModels != null) {
+ serializer.attributeInt(null, BatteryUsageStats.XML_ATTR_MODEL,
+ mPowerModels[componentId]);
+ }
+ serializer.endTag(null, BatteryUsageStats.XML_TAG_COMPONENT);
+ }
+
+ final int customComponentEnd =
+ BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID + mCustomPowerComponentCount;
+ for (int componentId = BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID;
+ componentId < customComponentEnd;
+ componentId++) {
+ final double powerMah = getConsumedPowerForCustomComponent(componentId);
+ final long durationMs = getUsageDurationForCustomComponentMillis(componentId);
+ if (powerMah == 0 && durationMs == 0) {
+ continue;
+ }
+
+ serializer.startTag(null, BatteryUsageStats.XML_TAG_CUSTOM_COMPONENT);
+ serializer.attributeInt(null, BatteryUsageStats.XML_ATTR_ID, componentId);
+ if (powerMah != 0) {
+ serializer.attributeDouble(null, BatteryUsageStats.XML_ATTR_POWER, powerMah);
+ }
+ if (durationMs != 0) {
+ serializer.attributeLong(null, BatteryUsageStats.XML_ATTR_DURATION, durationMs);
+ }
+ serializer.endTag(null, BatteryUsageStats.XML_TAG_CUSTOM_COMPONENT);
+ }
+
+ serializer.endTag(null, BatteryUsageStats.XML_TAG_POWER_COMPONENTS);
+ }
+
+
+ static void parseXml(TypedXmlPullParser parser, PowerComponents.Builder builder)
+ throws XmlPullParserException, IOException {
+ int eventType = parser.getEventType();
+ if (eventType != XmlPullParser.START_TAG || !parser.getName().equals(
+ BatteryUsageStats.XML_TAG_POWER_COMPONENTS)) {
+ throw new XmlPullParserException("Invalid XML parser state");
+ }
+
+ while (!(eventType == XmlPullParser.END_TAG && parser.getName().equals(
+ BatteryUsageStats.XML_TAG_POWER_COMPONENTS))
+ && eventType != XmlPullParser.END_DOCUMENT) {
+ if (eventType == XmlPullParser.START_TAG) {
+ switch (parser.getName()) {
+ case BatteryUsageStats.XML_TAG_COMPONENT: {
+ int componentId = -1;
+ double powerMah = 0;
+ long durationMs = 0;
+ int model = BatteryConsumer.POWER_MODEL_UNDEFINED;
+ for (int i = 0; i < parser.getAttributeCount(); i++) {
+ switch (parser.getAttributeName(i)) {
+ case BatteryUsageStats.XML_ATTR_ID:
+ componentId = parser.getAttributeInt(i);
+ break;
+ case BatteryUsageStats.XML_ATTR_POWER:
+ powerMah = parser.getAttributeDouble(i);
+ break;
+ case BatteryUsageStats.XML_ATTR_DURATION:
+ durationMs = parser.getAttributeLong(i);
+ break;
+ case BatteryUsageStats.XML_ATTR_MODEL:
+ model = parser.getAttributeInt(i);
+ break;
+ }
+ }
+ builder.setConsumedPower(componentId, powerMah, model);
+ builder.setUsageDurationMillis(componentId, durationMs);
+ break;
+ }
+ case BatteryUsageStats.XML_TAG_CUSTOM_COMPONENT: {
+ int componentId = -1;
+ double powerMah = 0;
+ long durationMs = 0;
+ for (int i = 0; i < parser.getAttributeCount(); i++) {
+ switch (parser.getAttributeName(i)) {
+ case BatteryUsageStats.XML_ATTR_ID:
+ componentId = parser.getAttributeInt(i);
+ break;
+ case BatteryUsageStats.XML_ATTR_POWER:
+ powerMah = parser.getAttributeDouble(i);
+ break;
+ case BatteryUsageStats.XML_ATTR_DURATION:
+ durationMs = parser.getAttributeLong(i);
+ break;
+ }
+ }
+ builder.setConsumedPowerForCustomComponent(componentId, powerMah);
+ builder.setUsageDurationForCustomComponentMillis(componentId, durationMs);
+ break;
+ }
+ }
+ }
+ eventType = parser.next();
+ }
+ }
+
/**
* Builder for PowerComponents.
*/
static final class Builder {
+ private static final byte POWER_MODEL_UNINITIALIZED = -1;
+
private final double[] mPowerComponentsMah;
private final String[] mCustomPowerComponentNames;
private final long[] mUsageDurationsMs;
@@ -311,6 +443,7 @@ class PowerComponents {
mUsageDurationsMs = new long[powerComponentCount];
if (includePowerModels) {
mPowerModels = new byte[BatteryConsumer.POWER_COMPONENT_COUNT];
+ Arrays.fill(mPowerModels, POWER_MODEL_UNINITIALIZED);
} else {
mPowerModels = null;
}
@@ -412,12 +545,39 @@ class PowerComponents {
return this;
}
- public void addPowerAndDuration(Builder other) {
+ public void addPowerAndDuration(PowerComponents.Builder other) {
+ addPowerAndDuration(other.mPowerComponentsMah, other.mUsageDurationsMs,
+ other.mPowerModels);
+ }
+
+ public void addPowerAndDuration(PowerComponents other) {
+ addPowerAndDuration(other.mPowerComponentsMah, other.mUsageDurationsMs,
+ other.mPowerModels);
+ }
+
+ private void addPowerAndDuration(double[] powerComponentsMah,
+ long[] usageDurationsMs, byte[] powerModels) {
+ if (mPowerComponentsMah.length != powerComponentsMah.length) {
+ throw new IllegalArgumentException(
+ "Number of power components does not match: " + powerComponentsMah.length
+ + ", expected: " + mPowerComponentsMah.length);
+ }
+
for (int i = mPowerComponentsMah.length - 1; i >= 0; i--) {
- mPowerComponentsMah[i] += other.mPowerComponentsMah[i];
+ mPowerComponentsMah[i] += powerComponentsMah[i];
}
for (int i = mUsageDurationsMs.length - 1; i >= 0; i--) {
- mUsageDurationsMs[i] += other.mUsageDurationsMs[i];
+ mUsageDurationsMs[i] += usageDurationsMs[i];
+ }
+ if (mPowerModels != null && powerModels != null) {
+ for (int i = mPowerModels.length - 1; i >= 0; i--) {
+ if (mPowerModels[i] == POWER_MODEL_UNINITIALIZED) {
+ mPowerModels[i] = powerModels[i];
+ } else if (mPowerModels[i] != powerModels[i]
+ && powerModels[i] != POWER_MODEL_UNINITIALIZED) {
+ mPowerModels[i] = BatteryConsumer.POWER_MODEL_UNDEFINED;
+ }
+ }
}
}
@@ -433,6 +593,19 @@ class PowerComponents {
return totalPowerMah;
}
+ private byte[] getPowerModels() {
+ if (mPowerModels == null) {
+ return null;
+ }
+
+ byte[] powerModels = new byte[mPowerModels.length];
+ for (int i = mPowerModels.length - 1; i >= 0; i--) {
+ powerModels[i] = mPowerModels[i] != POWER_MODEL_UNINITIALIZED ? mPowerModels[i]
+ : BatteryConsumer.POWER_MODEL_UNDEFINED;
+ }
+ return powerModels;
+ }
+
/**
* Creates a read-only object out of the Builder values.
*/
diff --git a/core/java/android/os/UidBatteryConsumer.java b/core/java/android/os/UidBatteryConsumer.java
index 16a6c767da38..bfc4f73835d9 100644
--- a/core/java/android/os/UidBatteryConsumer.java
+++ b/core/java/android/os/UidBatteryConsumer.java
@@ -19,9 +19,16 @@ package android.os;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.text.TextUtils;
+import android.util.TypedXmlPullParser;
+import android.util.TypedXmlSerializer;
import com.android.internal.os.PowerCalculator;
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+
+import java.io.IOException;
import java.io.PrintWriter;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@@ -143,13 +150,65 @@ public final class UidBatteryConsumer extends BatteryConsumer implements Parcela
return 0;
}
+ /** Serializes this object to XML */
+ void writeToXml(TypedXmlSerializer serializer) throws IOException {
+ if (getConsumedPower() == 0) {
+ return;
+ }
+
+ serializer.startTag(null, BatteryUsageStats.XML_TAG_UID);
+ serializer.attributeInt(null, BatteryUsageStats.XML_ATTR_UID, getUid());
+ if (!TextUtils.isEmpty(mPackageWithHighestDrain)) {
+ serializer.attribute(null, BatteryUsageStats.XML_ATTR_HIGHEST_DRAIN_PACKAGE,
+ mPackageWithHighestDrain);
+ }
+ serializer.attributeLong(null, BatteryUsageStats.XML_ATTR_TIME_IN_FOREGROUND,
+ mTimeInForegroundMs);
+ serializer.attributeLong(null, BatteryUsageStats.XML_ATTR_TIME_IN_BACKGROUND,
+ mTimeInBackgroundMs);
+ mPowerComponents.writeToXml(serializer);
+ serializer.endTag(null, BatteryUsageStats.XML_TAG_UID);
+ }
+
+ /** Parses an XML representation and populates the BatteryUsageStats builder */
+ static void createFromXml(TypedXmlPullParser parser, BatteryUsageStats.Builder builder)
+ throws XmlPullParserException, IOException {
+ final int uid = parser.getAttributeInt(null, BatteryUsageStats.XML_ATTR_UID);
+ final UidBatteryConsumer.Builder consumerBuilder =
+ builder.getOrCreateUidBatteryConsumerBuilder(uid);
+
+ int eventType = parser.getEventType();
+ if (eventType != XmlPullParser.START_TAG
+ || !parser.getName().equals(BatteryUsageStats.XML_TAG_UID)) {
+ throw new XmlPullParserException("Invalid XML parser state");
+ }
+
+ consumerBuilder.setPackageWithHighestDrain(
+ parser.getAttributeValue(null, BatteryUsageStats.XML_ATTR_HIGHEST_DRAIN_PACKAGE));
+ consumerBuilder.setTimeInStateMs(STATE_FOREGROUND,
+ parser.getAttributeLong(null, BatteryUsageStats.XML_ATTR_TIME_IN_FOREGROUND));
+ consumerBuilder.setTimeInStateMs(STATE_BACKGROUND,
+ parser.getAttributeLong(null, BatteryUsageStats.XML_ATTR_TIME_IN_BACKGROUND));
+ while (!(eventType == XmlPullParser.END_TAG
+ && parser.getName().equals(BatteryUsageStats.XML_TAG_UID))
+ && eventType != XmlPullParser.END_DOCUMENT) {
+ if (eventType == XmlPullParser.START_TAG) {
+ if (parser.getName().equals(BatteryUsageStats.XML_TAG_POWER_COMPONENTS)) {
+ PowerComponents.parseXml(parser, consumerBuilder.mPowerComponentsBuilder);
+ }
+ }
+ eventType = parser.next();
+ }
+ }
+
/**
* Builder for UidBatteryConsumer.
*/
public static final class Builder extends BaseBuilder<Builder> {
+ private static final String PACKAGE_NAME_UNINITIALIZED = "";
private final BatteryStats.Uid mBatteryStatsUid;
private final int mUid;
- private String mPackageWithHighestDrain;
+ private String mPackageWithHighestDrain = PACKAGE_NAME_UNINITIALIZED;
public long mTimeInForegroundMs;
public long mTimeInBackgroundMs;
private boolean mExcludeFromBatteryUsageStats;
@@ -161,8 +220,19 @@ public final class UidBatteryConsumer extends BatteryConsumer implements Parcela
mUid = batteryStatsUid.getUid();
}
+ public Builder(@NonNull String[] customPowerComponentNames, boolean includePowerModels,
+ int uid) {
+ super(customPowerComponentNames, includePowerModels);
+ mBatteryStatsUid = null;
+ mUid = uid;
+ }
+
@NonNull
public BatteryStats.Uid getBatteryStatsUid() {
+ if (mBatteryStatsUid == null) {
+ throw new IllegalStateException(
+ "UidBatteryConsumer.Builder was initialized without a BatteryStats.Uid");
+ }
return mBatteryStatsUid;
}
@@ -176,7 +246,7 @@ public final class UidBatteryConsumer extends BatteryConsumer implements Parcela
*/
@NonNull
public Builder setPackageWithHighestDrain(@Nullable String packageName) {
- mPackageWithHighestDrain = packageName;
+ mPackageWithHighestDrain = TextUtils.nullIfEmpty(packageName);
return this;
}
@@ -208,6 +278,30 @@ public final class UidBatteryConsumer extends BatteryConsumer implements Parcela
}
/**
+ * Adds power and usage duration from the supplied UidBatteryConsumer.
+ */
+ public Builder add(UidBatteryConsumer consumer) {
+ mPowerComponentsBuilder.addPowerAndDuration(consumer.mPowerComponents);
+ mTimeInBackgroundMs += consumer.mTimeInBackgroundMs;
+ mTimeInForegroundMs += consumer.mTimeInForegroundMs;
+
+ if (mPackageWithHighestDrain == PACKAGE_NAME_UNINITIALIZED) {
+ mPackageWithHighestDrain = consumer.mPackageWithHighestDrain;
+ } else if (!TextUtils.equals(mPackageWithHighestDrain,
+ consumer.mPackageWithHighestDrain)) {
+ // Consider combining two UidBatteryConsumers with this distribution
+ // of power drain between packages:
+ // (package1=100, package2=10) and (package1=100, package2=101).
+ // Since we don't know the actual power distribution between packages at this
+ // point, we have no way to correctly declare package1 as the winner.
+ // The naive logic of picking the consumer with the higher total consumed
+ // power would produce an incorrect result.
+ mPackageWithHighestDrain = null;
+ }
+ return this;
+ }
+
+ /**
* Returns true if this UidBatteryConsumer must be excluded from the
* BatteryUsageStats.
*/
@@ -220,6 +314,9 @@ public final class UidBatteryConsumer extends BatteryConsumer implements Parcela
*/
@NonNull
public UidBatteryConsumer build() {
+ if (mPackageWithHighestDrain == PACKAGE_NAME_UNINITIALIZED) {
+ mPackageWithHighestDrain = null;
+ }
return new UidBatteryConsumer(this);
}
}
diff --git a/core/java/android/os/UserBatteryConsumer.java b/core/java/android/os/UserBatteryConsumer.java
index 429d2c53a836..b508a8cd98ae 100644
--- a/core/java/android/os/UserBatteryConsumer.java
+++ b/core/java/android/os/UserBatteryConsumer.java
@@ -17,9 +17,15 @@
package android.os;
import android.annotation.NonNull;
+import android.util.TypedXmlPullParser;
+import android.util.TypedXmlSerializer;
import com.android.internal.os.PowerCalculator;
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+
+import java.io.IOException;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.List;
@@ -85,6 +91,42 @@ public class UserBatteryConsumer extends BatteryConsumer implements Parcelable {
return 0;
}
+ /** Serializes this object to XML */
+ void writeToXml(TypedXmlSerializer serializer) throws IOException {
+ if (getConsumedPower() == 0) {
+ return;
+ }
+
+ serializer.startTag(null, BatteryUsageStats.XML_TAG_USER);
+ serializer.attributeInt(null, BatteryUsageStats.XML_ATTR_USER_ID, getUserId());
+ mPowerComponents.writeToXml(serializer);
+ serializer.endTag(null, BatteryUsageStats.XML_TAG_USER);
+ }
+
+ /** Parses an XML representation and populates the BatteryUsageStats builder */
+ static void createFromXml(TypedXmlPullParser parser, BatteryUsageStats.Builder builder)
+ throws XmlPullParserException, IOException {
+ final int userId = parser.getAttributeInt(null, BatteryUsageStats.XML_ATTR_USER_ID);
+ final UserBatteryConsumer.Builder consumerBuilder =
+ builder.getOrCreateUserBatteryConsumerBuilder(userId);
+
+ int eventType = parser.getEventType();
+ if (eventType != XmlPullParser.START_TAG
+ || !parser.getName().equals(BatteryUsageStats.XML_TAG_USER)) {
+ throw new XmlPullParserException("Invalid XML parser state");
+ }
+ while (!(eventType == XmlPullParser.END_TAG
+ && parser.getName().equals(BatteryUsageStats.XML_TAG_USER))
+ && eventType != XmlPullParser.END_DOCUMENT) {
+ if (eventType == XmlPullParser.START_TAG) {
+ if (parser.getName().equals(BatteryUsageStats.XML_TAG_POWER_COMPONENTS)) {
+ PowerComponents.parseXml(parser, consumerBuilder.mPowerComponentsBuilder);
+ }
+ }
+ eventType = parser.next();
+ }
+ }
+
/**
* Builder for UserBatteryConsumer.
*/
diff --git a/core/java/android/os/UserManager.java b/core/java/android/os/UserManager.java
index c22224dd0da2..7edd6e6597b2 100644
--- a/core/java/android/os/UserManager.java
+++ b/core/java/android/os/UserManager.java
@@ -1322,6 +1322,24 @@ public class UserManager {
"disallow_camera_toggle";
/**
+ * This is really not a user restriction in the normal sense. This can't be set to a user,
+ * via UserManager nor via DevicePolicyManager. This is not even set in UserSettingsUtils.
+ * This is defined here purely for convenience within the settings app.
+ *
+ * TODO(b/191306258): Refactor the Settings app to remove the need for this field, and delete it
+ *
+ * Specifies whether biometrics are available to the user. This is used internally only,
+ * as a means of communications between biometric settings and
+ * {@link com.android.settingslib.enterprise.ActionDisabledByAdminControllerFactory}.
+ *
+ * @see {@link android.hardware.biometrics.ParentalControlsUtilsInternal}
+ * @see {@link com.android.settings.biometrics.ParentalControlsUtils}
+ *
+ * @hide
+ */
+ public static final String DISALLOW_BIOMETRIC = "disallow_biometric";
+
+ /**
* Application restriction key that is used to indicate the pending arrival
* of real restrictions for the app.
*
@@ -1415,6 +1433,7 @@ public class UserManager {
DISALLOW_MICROPHONE_TOGGLE,
DISALLOW_CAMERA_TOGGLE,
KEY_RESTRICTIONS_PENDING,
+ DISALLOW_BIOMETRIC,
})
@Retention(RetentionPolicy.SOURCE)
public @interface UserRestrictionKey {}
diff --git a/core/java/android/service/notification/ZenModeConfig.java b/core/java/android/service/notification/ZenModeConfig.java
index 12d905588e1e..ff692818863a 100644
--- a/core/java/android/service/notification/ZenModeConfig.java
+++ b/core/java/android/service/notification/ZenModeConfig.java
@@ -50,6 +50,7 @@ import android.util.TypedXmlSerializer;
import android.util.proto.ProtoOutputStream;
import com.android.internal.R;
+import com.android.internal.util.XmlUtils;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
@@ -162,6 +163,7 @@ public class ZenModeConfig implements Parcelable {
private static final String RULE_ATT_ENABLED = "enabled";
private static final String RULE_ATT_SNOOZING = "snoozing";
private static final String RULE_ATT_NAME = "name";
+ private static final String RULE_ATT_PKG = "pkg";
private static final String RULE_ATT_COMPONENT = "component";
private static final String RULE_ATT_CONFIG_ACTIVITY = "configActivity";
private static final String RULE_ATT_ZEN = "zen";
@@ -671,11 +673,11 @@ public class ZenModeConfig implements Parcelable {
rt.conditionId = safeUri(parser, RULE_ATT_CONDITION_ID);
rt.component = safeComponentName(parser, RULE_ATT_COMPONENT);
rt.configurationActivity = safeComponentName(parser, RULE_ATT_CONFIG_ACTIVITY);
- rt.pkg = (rt.component != null)
- ? rt.component.getPackageName()
- : (rt.configurationActivity != null)
- ? rt.configurationActivity.getPackageName()
- : null;
+ rt.pkg = XmlUtils.readStringAttribute(parser, RULE_ATT_PKG);
+ if (rt.pkg == null) {
+ // backfill from component, if present. configActivity is not safe to backfill from
+ rt.pkg = rt.component != null ? rt.component.getPackageName() : null;
+ }
rt.creationTime = safeLong(parser, RULE_ATT_CREATION_TIME, 0);
rt.enabler = parser.getAttributeValue(null, RULE_ATT_ENABLER);
rt.condition = readConditionXml(parser);
@@ -697,6 +699,9 @@ public class ZenModeConfig implements Parcelable {
out.attribute(null, RULE_ATT_NAME, rule.name);
}
out.attributeInt(null, RULE_ATT_ZEN, rule.zenMode);
+ if (rule.pkg != null) {
+ out.attribute(null, RULE_ATT_PKG, rule.pkg);
+ }
if (rule.component != null) {
out.attribute(null, RULE_ATT_COMPONENT, rule.component.flattenToString());
}
diff --git a/core/java/android/window/ITaskOrganizer.aidl b/core/java/android/window/ITaskOrganizer.aidl
index 3eb35c2c5e8a..8b8dba89ea67 100644
--- a/core/java/android/window/ITaskOrganizer.aidl
+++ b/core/java/android/window/ITaskOrganizer.aidl
@@ -52,6 +52,11 @@ oneway interface ITaskOrganizer {
void copySplashScreenView(int taskId);
/**
+ * Called when the Task removed the splash screen.
+ */
+ void onAppSplashScreenViewRemoved(int taskId);
+
+ /**
* A callback when the Task is available for the registered organizer. The client is responsible
* for releasing the SurfaceControl in the callback. For non-root tasks, the leash may initially
* be hidden so it is up to the organizer to show this task.
diff --git a/core/java/android/window/SplashScreenView.java b/core/java/android/window/SplashScreenView.java
index 6772afeb0270..4a3bf91645f2 100644
--- a/core/java/android/window/SplashScreenView.java
+++ b/core/java/android/window/SplashScreenView.java
@@ -34,8 +34,10 @@ import android.graphics.PixelFormat;
import android.graphics.Rect;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
+import android.os.Build;
import android.os.Parcel;
import android.os.Parcelable;
+import android.os.RemoteCallback;
import android.os.Trace;
import android.util.AttributeSet;
import android.util.Log;
@@ -76,7 +78,7 @@ import java.time.Instant;
*/
public final class SplashScreenView extends FrameLayout {
private static final String TAG = SplashScreenView.class.getSimpleName();
- private static final boolean DEBUG = false;
+ private static final boolean DEBUG = Build.IS_DEBUGGABLE;
private static final int LIGHT_BARS_MASK =
WindowInsetsController.APPEARANCE_LIGHT_NAVIGATION_BARS
@@ -85,6 +87,7 @@ public final class SplashScreenView extends FrameLayout {
| FLAG_TRANSLUCENT_NAVIGATION | FLAG_TRANSLUCENT_STATUS;
private boolean mNotCopyable;
+ private boolean mIsCopied;
private int mInitBackgroundColor;
private int mInitIconBackgroundColor;
private View mIconView;
@@ -103,6 +106,10 @@ public final class SplashScreenView extends FrameLayout {
private SurfaceControlViewHost.SurfacePackage mSurfacePackage;
@Nullable
private SurfaceView mSurfaceView;
+ @Nullable
+ private SurfaceControlViewHost mSurfaceHost;
+ @Nullable
+ private RemoteCallback mClientCallback;
// cache original window and status
private Window mWindow;
@@ -127,6 +134,7 @@ public final class SplashScreenView extends FrameLayout {
private Bitmap mParceledIconBitmap;
private Drawable mIconDrawable;
private SurfaceControlViewHost.SurfacePackage mSurfacePackage;
+ private RemoteCallback mClientCallback;
private int mBrandingImageWidth;
private int mBrandingImageHeight;
private Drawable mBrandingDrawable;
@@ -161,6 +169,7 @@ public final class SplashScreenView extends FrameLayout {
}
mIconAnimationStart = Instant.ofEpochMilli(parcelable.mIconAnimationStartMillis);
mIconAnimationDuration = Duration.ofMillis(parcelable.mIconAnimationDurationMillis);
+ mClientCallback = parcelable.mClientCallback;
if (DEBUG) {
Log.d(TAG, String.format("Building from parcel drawable: %s", mIconDrawable));
}
@@ -228,6 +237,7 @@ public final class SplashScreenView extends FrameLayout {
view.mInitBackgroundColor = mBackgroundColor;
view.mInitIconBackgroundColor = mIconBackground;
view.setBackgroundColor(mBackgroundColor);
+ view.mClientCallback = mClientCallback;
view.mBrandingImageView = view.findViewById(R.id.splashscreen_branding_view);
@@ -285,7 +295,8 @@ public final class SplashScreenView extends FrameLayout {
if (mSurfacePackage == null) {
if (DEBUG) {
Log.d(TAG,
- "Creating Original SurfacePackage. SurfaceView: " + surfaceView);
+ "SurfaceControlViewHost created on thread "
+ + Thread.currentThread().getId());
}
SurfaceControlViewHost viewHost = new SurfaceControlViewHost(mContext,
@@ -297,6 +308,7 @@ public final class SplashScreenView extends FrameLayout {
SurfaceControlViewHost.SurfacePackage surfacePackage = viewHost.getSurfacePackage();
surfaceView.setChildSurfacePackage(surfacePackage);
view.mSurfacePackage = surfacePackage;
+ view.mSurfaceHost = viewHost;
view.mSurfacePackageCopy = new SurfaceControlViewHost.SurfacePackage(
surfacePackage);
} else {
@@ -357,6 +369,7 @@ public final class SplashScreenView extends FrameLayout {
* @hide
*/
public void onCopied() {
+ mIsCopied = true;
if (mSurfaceView == null) {
return;
}
@@ -369,6 +382,12 @@ public final class SplashScreenView extends FrameLayout {
mSurfacePackage = null;
}
+ /** @hide **/
+ @Nullable
+ public SurfaceControlViewHost getSurfaceHost() {
+ return mSurfaceHost;
+ }
+
@Override
public void setAlpha(float alpha) {
super.setAlpha(alpha);
@@ -407,8 +426,7 @@ public final class SplashScreenView extends FrameLayout {
if (DEBUG) {
mSurfacePackage.getSurfaceControl().addOnReparentListener(
(transaction, parent) -> Log.e(TAG,
- String.format("SurfacePackage'surface reparented.\n Parent: %s",
- parent), new Throwable()));
+ String.format("SurfacePackage'surface reparented to %s", parent)));
Log.d(TAG, "Transferring surface " + mSurfaceView.toString());
}
mSurfaceView.setChildSurfacePackage(mSurfacePackage);
@@ -466,9 +484,36 @@ public final class SplashScreenView extends FrameLayout {
mHasRemoved = true;
}
+ /** @hide **/
+ @Override
+ protected void onDetachedFromWindow() {
+ super.onDetachedFromWindow();
+ releaseAnimationSurfaceHost();
+ }
+
+ private void releaseAnimationSurfaceHost() {
+ if (mSurfaceHost != null && !mIsCopied) {
+ final SurfaceControlViewHost finalSurfaceHost = mSurfaceHost;
+ mSurfaceHost = null;
+ finalSurfaceHost.getView().post(() -> {
+ if (DEBUG) {
+ Log.d(TAG,
+ "Shell removed splash screen."
+ + " Releasing SurfaceControlViewHost on thread #"
+ + Thread.currentThread().getId());
+ }
+ finalSurfaceHost.release();
+ });
+ } else if (mSurfacePackage != null && mSurfaceHost == null) {
+ mSurfacePackage = null;
+ mClientCallback.sendResult(null);
+ }
+ }
+
/**
* Called when this view is attached to an activity. This also makes SystemUI colors
* transparent so the content of splash screen view can draw fully.
+ *
* @hide
*/
public void attachHostActivityAndSetSystemUIColors(Activity activity, Window window) {
@@ -585,6 +630,7 @@ public final class SplashScreenView extends FrameLayout {
private long mIconAnimationDurationMillis;
private SurfaceControlViewHost.SurfacePackage mSurfacePackage;
+ private RemoteCallback mClientCallback;
public SplashScreenViewParcelable(SplashScreenView view) {
mIconSize = view.mIconView.getWidth();
@@ -641,6 +687,7 @@ public final class SplashScreenView extends FrameLayout {
mIconAnimationDurationMillis = source.readLong();
mIconBackground = source.readInt();
mSurfacePackage = source.readTypedObject(SurfaceControlViewHost.SurfacePackage.CREATOR);
+ mClientCallback = source.readTypedObject(RemoteCallback.CREATOR);
}
@Override
@@ -660,6 +707,7 @@ public final class SplashScreenView extends FrameLayout {
dest.writeLong(mIconAnimationDurationMillis);
dest.writeInt(mIconBackground);
dest.writeTypedObject(mSurfacePackage, flags);
+ dest.writeTypedObject(mClientCallback, flags);
}
public static final @NonNull Parcelable.Creator<SplashScreenViewParcelable> CREATOR =
@@ -697,5 +745,13 @@ public final class SplashScreenView extends FrameLayout {
int getIconBackground() {
return mIconBackground;
}
+
+ /**
+ * Sets the {@link RemoteCallback} that will be called by the client to notify the shell
+ * of the removal of the {@link SplashScreenView}.
+ */
+ public void setClientCallback(@NonNull RemoteCallback clientCallback) {
+ mClientCallback = clientCallback;
+ }
}
}
diff --git a/core/java/android/window/TaskOrganizer.java b/core/java/android/window/TaskOrganizer.java
index 3340cf4fb707..73995491668a 100644
--- a/core/java/android/window/TaskOrganizer.java
+++ b/core/java/android/window/TaskOrganizer.java
@@ -117,6 +117,16 @@ public class TaskOrganizer extends WindowOrganizer {
public void copySplashScreenView(int taskId) {}
/**
+ * Notify the shell ({@link com.android.wm.shell.ShellTaskOrganizer} that the client has
+ * removed the splash screen view.
+ * @see com.android.wm.shell.ShellTaskOrganizer#onAppSplashScreenViewRemoved(int)
+ * @see SplashScreenView#remove()
+ */
+ @BinderThread
+ public void onAppSplashScreenViewRemoved(int taskId) {
+ }
+
+ /**
* Called when a task with the registered windowing mode can be controlled by this task
* organizer. For non-root tasks, the leash may initially be hidden so it is up to the organizer
* to show this task.
@@ -236,11 +246,16 @@ public class TaskOrganizer extends WindowOrganizer {
}
@Override
- public void copySplashScreenView(int taskId) {
+ public void copySplashScreenView(int taskId) {
mExecutor.execute(() -> TaskOrganizer.this.copySplashScreenView(taskId));
}
@Override
+ public void onAppSplashScreenViewRemoved(int taskId) {
+ mExecutor.execute(() -> TaskOrganizer.this.onAppSplashScreenViewRemoved(taskId));
+ }
+
+ @Override
public void onTaskAppeared(ActivityManager.RunningTaskInfo taskInfo, SurfaceControl leash) {
mExecutor.execute(() -> TaskOrganizer.this.onTaskAppeared(taskInfo, leash));
}
diff --git a/core/java/com/android/internal/display/BrightnessSynchronizer.java b/core/java/com/android/internal/display/BrightnessSynchronizer.java
index bd908900fccc..19183b8eb9e7 100644
--- a/core/java/com/android/internal/display/BrightnessSynchronizer.java
+++ b/core/java/com/android/internal/display/BrightnessSynchronizer.java
@@ -31,9 +31,6 @@ import android.provider.Settings;
import android.util.MathUtils;
import android.view.Display;
-import java.util.LinkedList;
-import java.util.Queue;
-
/**
* BrightnessSynchronizer helps convert between the int (old) system and float
* (new) system for storing the brightness. It has methods to convert between the two and also
@@ -43,12 +40,11 @@ public class BrightnessSynchronizer {
private static final int MSG_UPDATE_FLOAT = 1;
private static final int MSG_UPDATE_INT = 2;
+ private static final int MSG_UPDATE_BOTH = 3;
private static final String TAG = "BrightnessSynchronizer";
private static final Uri BRIGHTNESS_URI =
Settings.System.getUriFor(Settings.System.SCREEN_BRIGHTNESS);
- private static final Uri BRIGHTNESS_FLOAT_URI =
- Settings.System.getUriFor(Settings.System.SCREEN_BRIGHTNESS_FLOAT);
// The tolerance within which we consider brightness values approximately equal to eachother.
// This value is approximately 1/3 of the smallest possible brightness value.
@@ -57,8 +53,6 @@ public class BrightnessSynchronizer {
private DisplayManager mDisplayManager;
private final Context mContext;
- private final Queue<Object> mWriteHistory = new LinkedList<>();
-
private final Handler mHandler = new Handler(Looper.getMainLooper()) {
@Override
public void handleMessage(Message msg) {
@@ -69,6 +63,9 @@ public class BrightnessSynchronizer {
case MSG_UPDATE_INT:
updateBrightnessIntFromFloat(Float.intBitsToFloat(msg.arg1));
break;
+ case MSG_UPDATE_BOTH:
+ updateBoth(Float.intBitsToFloat(msg.arg1));
+ break;
default:
super.handleMessage(msg);
}
@@ -139,7 +136,7 @@ public class BrightnessSynchronizer {
/**
* Translates specified value from the float brightness system to the int brightness system,
- * given the min/max of each range. Accounts for special values such as OFF and invalid values.
+ * given the min/max of each range. Accounts for special values such as OFF and invalid values.
* Value returned as a float primitive (to preserve precision), but is a value within the
* int-system range.
*/
@@ -168,49 +165,63 @@ public class BrightnessSynchronizer {
}
/**
- * Updates the float setting based on a passed in int value. This is called whenever the int
- * setting changes. mWriteHistory keeps a record of the values that been written to the settings
- * from either this method or updateBrightnessIntFromFloat. This is to ensure that the value
- * being set is due to an external value being set, rather than the updateBrightness* methods.
- * The intention of this is to avoid race conditions when the setting is being changed
- * frequently and to ensure we are not reacting to settings changes from this file.
+ * Updates the settings based on a passed in int value. This is called whenever the int
+ * setting changes. mPreferredSettingValue holds the most recently updated brightness value
+ * as a float that we would like the display to be set to.
+ *
+ * We then schedule an update to both the int and float settings, but, remove all the other
+ * messages to update all, to prevent us getting stuck in a loop.
+ *
* @param value Brightness value as int to store in the float setting.
*/
private void updateBrightnessFloatFromInt(int value) {
- Object topOfQueue = mWriteHistory.peek();
- if (topOfQueue != null && topOfQueue.equals(value)) {
- mWriteHistory.poll();
- } else {
- if (brightnessFloatToInt(mPreferredSettingValue) == value) {
- return;
- }
- float newBrightnessFloat = brightnessIntToFloat(value);
- mWriteHistory.offer(newBrightnessFloat);
- mPreferredSettingValue = newBrightnessFloat;
- mDisplayManager.setBrightness(Display.DEFAULT_DISPLAY, newBrightnessFloat);
+ if (brightnessFloatToInt(mPreferredSettingValue) == value) {
+ return;
}
+
+ mPreferredSettingValue = brightnessIntToFloat(value);
+ final int newBrightnessAsIntBits = Float.floatToIntBits(mPreferredSettingValue);
+ mHandler.removeMessages(MSG_UPDATE_BOTH);
+ mHandler.obtainMessage(MSG_UPDATE_BOTH, newBrightnessAsIntBits, 0).sendToTarget();
}
/**
- * Updates the int setting based on a passed in float value. This is called whenever the float
- * setting changes. mWriteHistory keeps a record of the values that been written to the settings
- * from either this method or updateBrightnessFloatFromInt. This is to ensure that the value
- * being set is due to an external value being set, rather than the updateBrightness* methods.
- * The intention of this is to avoid race conditions when the setting is being changed
- * frequently and to ensure we are not reacting to settings changes from this file.
+ * Updates the settings based on a passed in float value. This is called whenever the float
+ * setting changes. mPreferredSettingValue holds the most recently updated brightness value
+ * as a float that we would like the display to be set to.
+ *
+ * We then schedule an update to both the int and float settings, but, remove all the other
+ * messages to update all, to prevent us getting stuck in a loop.
+ *
* @param value Brightness setting as float to store in int setting.
*/
private void updateBrightnessIntFromFloat(float value) {
- int newBrightnessInt = brightnessFloatToInt(value);
- Object topOfQueue = mWriteHistory.peek();
- if (topOfQueue != null && topOfQueue.equals(value)) {
- mWriteHistory.poll();
- } else {
- mWriteHistory.offer(newBrightnessInt);
- mPreferredSettingValue = value;
+ if (floatEquals(mPreferredSettingValue, value)) {
+ return;
+ }
+
+ mPreferredSettingValue = value;
+ final int newBrightnessAsIntBits = Float.floatToIntBits(mPreferredSettingValue);
+ mHandler.removeMessages(MSG_UPDATE_BOTH);
+ mHandler.obtainMessage(MSG_UPDATE_BOTH, newBrightnessAsIntBits, 0).sendToTarget();
+ }
+
+
+ /**
+ * Updates both setting values if they have changed
+ * mDisplayManager.setBrightness automatically checks for changes
+ * Settings.System.putIntForUser needs to be checked, to prevent an extra callback to this class
+ *
+ * @param newBrightnessFloat Brightness setting as float to store in both settings
+ */
+ private void updateBoth(float newBrightnessFloat) {
+ int newBrightnessInt = brightnessFloatToInt(newBrightnessFloat);
+ mDisplayManager.setBrightness(Display.DEFAULT_DISPLAY, newBrightnessFloat);
+ if (getScreenBrightnessInt(mContext) != newBrightnessInt) {
Settings.System.putIntForUser(mContext.getContentResolver(),
Settings.System.SCREEN_BRIGHTNESS, newBrightnessInt, UserHandle.USER_CURRENT);
}
+
}
/**
diff --git a/core/java/com/android/internal/jank/InteractionJankMonitor.java b/core/java/com/android/internal/jank/InteractionJankMonitor.java
index 26d6a0c74b08..aabcd7f82ac7 100644
--- a/core/java/com/android/internal/jank/InteractionJankMonitor.java
+++ b/core/java/com/android/internal/jank/InteractionJankMonitor.java
@@ -43,6 +43,10 @@ import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_IN
import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__NOTIFICATION_SHADE_SWIPE;
import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SETTINGS_PAGE_SCROLL;
import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SHADE_APP_LAUNCH;
+import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SHADE_APP_LAUNCH_FROM_HISTORY_BUTTON;
+import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SHADE_APP_LAUNCH_FROM_MEDIA_PLAYER;
+import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SHADE_APP_LAUNCH_FROM_QS_TILE;
+import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SHADE_APP_LAUNCH_FROM_SETTINGS_BUTTON;
import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SHADE_EXPAND_COLLAPSE_LOCK;
import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SHADE_HEADS_UP_APPEAR;
import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SHADE_HEADS_UP_DISAPPEAR;
@@ -53,6 +57,7 @@ import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_IN
import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SHADE_ROW_EXPAND;
import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SHADE_ROW_SWIPE;
import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SHADE_SCROLL_FLING;
+import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__STATUS_BAR_APP_LAUNCH_FROM_CALL_CHIP;
import android.annotation.IntDef;
import android.annotation.NonNull;
@@ -153,6 +158,11 @@ public class InteractionJankMonitor {
public static final int CUJ_LAUNCHER_APP_LAUNCH_FROM_WIDGET = 27;
public static final int CUJ_SETTINGS_PAGE_SCROLL = 28;
public static final int CUJ_LOCKSCREEN_UNLOCK_ANIMATION = 29;
+ public static final int CUJ_SHADE_APP_LAUNCH_FROM_HISTORY_BUTTON = 30;
+ public static final int CUJ_SHADE_APP_LAUNCH_FROM_MEDIA_PLAYER = 31;
+ public static final int CUJ_SHADE_APP_LAUNCH_FROM_QS_TILE = 32;
+ public static final int CUJ_SHADE_APP_LAUNCH_FROM_SETTINGS_BUTTON = 33;
+ public static final int CUJ_STATUS_BAR_APP_LAUNCH_FROM_CALL_CHIP = 34;
private static final int NO_STATSD_LOGGING = -1;
@@ -191,6 +201,11 @@ public class InteractionJankMonitor {
UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LAUNCHER_APP_LAUNCH_FROM_WIDGET,
UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SETTINGS_PAGE_SCROLL,
UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LOCKSCREEN_UNLOCK_ANIMATION,
+ UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SHADE_APP_LAUNCH_FROM_HISTORY_BUTTON,
+ UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SHADE_APP_LAUNCH_FROM_MEDIA_PLAYER,
+ UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SHADE_APP_LAUNCH_FROM_QS_TILE,
+ UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SHADE_APP_LAUNCH_FROM_SETTINGS_BUTTON,
+ UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__STATUS_BAR_APP_LAUNCH_FROM_CALL_CHIP,
};
private static volatile InteractionJankMonitor sInstance;
@@ -240,6 +255,11 @@ public class InteractionJankMonitor {
CUJ_LAUNCHER_APP_LAUNCH_FROM_WIDGET,
CUJ_SETTINGS_PAGE_SCROLL,
CUJ_LOCKSCREEN_UNLOCK_ANIMATION,
+ CUJ_SHADE_APP_LAUNCH_FROM_HISTORY_BUTTON,
+ CUJ_SHADE_APP_LAUNCH_FROM_MEDIA_PLAYER,
+ CUJ_SHADE_APP_LAUNCH_FROM_QS_TILE,
+ CUJ_SHADE_APP_LAUNCH_FROM_SETTINGS_BUTTON,
+ CUJ_STATUS_BAR_APP_LAUNCH_FROM_CALL_CHIP,
})
@Retention(RetentionPolicy.SOURCE)
public @interface CujType {
@@ -578,6 +598,16 @@ public class InteractionJankMonitor {
return "SETTINGS_PAGE_SCROLL";
case CUJ_LOCKSCREEN_UNLOCK_ANIMATION:
return "LOCKSCREEN_UNLOCK_ANIMATION";
+ case CUJ_SHADE_APP_LAUNCH_FROM_HISTORY_BUTTON:
+ return "SHADE_APP_LAUNCH_FROM_HISTORY_BUTTON";
+ case CUJ_SHADE_APP_LAUNCH_FROM_MEDIA_PLAYER:
+ return "SHADE_APP_LAUNCH_FROM_MEDIA_PLAYER";
+ case CUJ_SHADE_APP_LAUNCH_FROM_QS_TILE:
+ return "SHADE_APP_LAUNCH_FROM_QS_TILE";
+ case CUJ_SHADE_APP_LAUNCH_FROM_SETTINGS_BUTTON:
+ return "SHADE_APP_LAUNCH_FROM_SETTINGS_BUTTON";
+ case CUJ_STATUS_BAR_APP_LAUNCH_FROM_CALL_CHIP:
+ return "STATUS_BAR_APP_LAUNCH_FROM_CALL_CHIP";
}
return "UNKNOWN";
}
diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java
index 5dfc5faead43..945a6ab11856 100644
--- a/core/java/com/android/internal/os/BatteryStatsImpl.java
+++ b/core/java/com/android/internal/os/BatteryStatsImpl.java
@@ -341,6 +341,19 @@ public class BatteryStatsImpl extends BatteryStats {
}
}
+ /**
+ * Listener for the battery stats reset.
+ */
+ public interface BatteryResetListener {
+
+ /**
+ * Callback invoked immediately prior to resetting battery stats.
+ */
+ void prepareForBatteryStatsReset();
+ }
+
+ private BatteryResetListener mBatteryResetListener;
+
public interface BatteryCallback {
public void batteryNeedsCpuUpdate();
public void batteryPowerChanged(boolean onBattery);
@@ -10736,6 +10749,10 @@ public class BatteryStatsImpl extends BatteryStats {
}
}
+ PowerProfile getPowerProfile() {
+ return mPowerProfile;
+ }
+
/**
* Starts tracking CPU time-in-state for threads of the system server process,
* keeping a separate account of threads receiving incoming binder calls.
@@ -11184,6 +11201,10 @@ public class BatteryStatsImpl extends BatteryStats {
mDischargeCounter.reset(false, elapsedRealtimeUs);
}
+ public void setBatteryResetListener(BatteryResetListener batteryResetListener) {
+ mBatteryResetListener = batteryResetListener;
+ }
+
public void resetAllStatsCmdLocked() {
final long mSecUptime = mClocks.uptimeMillis();
long uptimeUs = mSecUptime * 1000;
@@ -11219,6 +11240,10 @@ public class BatteryStatsImpl extends BatteryStats {
}
private void resetAllStatsLocked(long uptimeMillis, long elapsedRealtimeMillis) {
+ if (mBatteryResetListener != null) {
+ mBatteryResetListener.prepareForBatteryStatsReset();
+ }
+
final long uptimeUs = uptimeMillis * 1000;
final long elapsedRealtimeUs = elapsedRealtimeMillis * 1000;
mStartCount = 0;
diff --git a/core/java/com/android/internal/os/BatteryUsageStatsProvider.java b/core/java/com/android/internal/os/BatteryUsageStatsProvider.java
index 3aaccdd71844..8943db6c8a1e 100644
--- a/core/java/com/android/internal/os/BatteryUsageStatsProvider.java
+++ b/core/java/com/android/internal/os/BatteryUsageStatsProvider.java
@@ -38,14 +38,24 @@ import java.util.Map;
public class BatteryUsageStatsProvider {
private final Context mContext;
private final BatteryStats mStats;
+ private final BatteryUsageStatsStore mBatteryUsageStatsStore;
private final PowerProfile mPowerProfile;
private final Object mLock = new Object();
private List<PowerCalculator> mPowerCalculators;
public BatteryUsageStatsProvider(Context context, BatteryStats stats) {
+ this(context, stats, null);
+ }
+
+ @VisibleForTesting
+ public BatteryUsageStatsProvider(Context context, BatteryStats stats,
+ BatteryUsageStatsStore batteryUsageStatsStore) {
mContext = context;
mStats = stats;
- mPowerProfile = new PowerProfile(mContext);
+ mBatteryUsageStatsStore = batteryUsageStatsStore;
+ mPowerProfile = stats instanceof BatteryStatsImpl
+ ? ((BatteryStatsImpl) stats).getPowerProfile()
+ : new PowerProfile(context);
}
private List<PowerCalculator> getPowerCalculators() {
@@ -126,6 +136,15 @@ public class BatteryUsageStatsProvider {
private BatteryUsageStats getBatteryUsageStats(BatteryUsageStatsQuery query,
long currentTimeMs) {
+ if (query.getToTimestamp() == 0) {
+ return getCurrentBatteryUsageStats(query, currentTimeMs);
+ } else {
+ return getAggregatedBatteryUsageStats(query);
+ }
+ }
+
+ private BatteryUsageStats getCurrentBatteryUsageStats(BatteryUsageStatsQuery query,
+ long currentTimeMs) {
final long realtimeUs = elapsedRealtime() * 1000;
final long uptimeUs = uptimeMillis() * 1000;
@@ -209,6 +228,25 @@ public class BatteryUsageStatsProvider {
BatteryStats.STATS_SINCE_CHARGED) / 1000;
}
+ private BatteryUsageStats getAggregatedBatteryUsageStats(BatteryUsageStatsQuery query) {
+ final boolean includePowerModels = (query.getFlags()
+ & BatteryUsageStatsQuery.FLAG_BATTERY_USAGE_STATS_INCLUDE_POWER_MODELS) != 0;
+
+ final BatteryUsageStats.Builder builder = new BatteryUsageStats.Builder(
+ mStats.getCustomEnergyConsumerNames(), includePowerModels);
+ final long[] timestamps = mBatteryUsageStatsStore.listBatteryUsageStatsTimestamps();
+ for (long timestamp : timestamps) {
+ if (timestamp > query.getFromTimestamp() && timestamp <= query.getToTimestamp()) {
+ final BatteryUsageStats snapshot =
+ mBatteryUsageStatsStore.loadBatteryUsageStats(timestamp);
+ if (snapshot != null) {
+ builder.add(snapshot);
+ }
+ }
+ }
+ return builder.build();
+ }
+
private long elapsedRealtime() {
if (mStats instanceof BatteryStatsImpl) {
return ((BatteryStatsImpl) mStats).mClocks.elapsedRealtime();
diff --git a/core/java/com/android/internal/os/BatteryUsageStatsStore.java b/core/java/com/android/internal/os/BatteryUsageStatsStore.java
new file mode 100644
index 000000000000..5c976025d39d
--- /dev/null
+++ b/core/java/com/android/internal/os/BatteryUsageStatsStore.java
@@ -0,0 +1,285 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.os;
+
+import android.annotation.Nullable;
+import android.content.Context;
+import android.os.BatteryUsageStats;
+import android.os.BatteryUsageStatsQuery;
+import android.os.Handler;
+import android.util.AtomicFile;
+import android.util.LongArray;
+import android.util.Slog;
+import android.util.TypedXmlPullParser;
+import android.util.TypedXmlSerializer;
+import android.util.Xml;
+
+import com.android.internal.annotations.VisibleForTesting;
+
+import org.xmlpull.v1.XmlPullParserException;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.nio.channels.FileChannel;
+import java.nio.channels.FileLock;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.StandardOpenOption;
+import java.util.List;
+import java.util.Locale;
+import java.util.Map;
+import java.util.Properties;
+import java.util.TreeMap;
+
+/**
+ * A storage mechanism for BatteryUsageStats snapshots.
+ */
+public class BatteryUsageStatsStore {
+ private static final String TAG = "BatteryUsageStatsStore";
+
+ private static final List<BatteryUsageStatsQuery> BATTERY_USAGE_STATS_QUERY = List.of(
+ new BatteryUsageStatsQuery.Builder()
+ .setMaxStatsAgeMs(0)
+ .includePowerModels()
+ .build());
+ private static final String BATTERY_USAGE_STATS_DIR = "battery-usage-stats";
+ private static final String SNAPSHOT_FILE_EXTENSION = ".bus";
+ private static final String DIR_LOCK_FILENAME = ".lock";
+ private static final String CONFIG_FILENAME = "config";
+ private static final String BATTERY_USAGE_STATS_BEFORE_RESET_TIMESTAMP_PROPERTY =
+ "BATTERY_USAGE_STATS_BEFORE_RESET_TIMESTAMP";
+ private static final long MAX_BATTERY_STATS_SNAPSHOT_STORAGE_BYTES = 100 * 1024;
+
+ private final Context mContext;
+ private final BatteryStatsImpl mBatteryStats;
+ private final File mStoreDir;
+ private final File mLockFile;
+ private final AtomicFile mConfigFile;
+ private final long mMaxStorageBytes;
+ private final Handler mHandler;
+ private final BatteryUsageStatsProvider mBatteryUsageStatsProvider;
+
+ public BatteryUsageStatsStore(Context context, BatteryStatsImpl stats, File systemDir,
+ Handler handler) {
+ this(context, stats, systemDir, handler, MAX_BATTERY_STATS_SNAPSHOT_STORAGE_BYTES);
+ }
+
+ @VisibleForTesting
+ public BatteryUsageStatsStore(Context context, BatteryStatsImpl batteryStats, File systemDir,
+ Handler handler, long maxStorageBytes) {
+ mContext = context;
+ mBatteryStats = batteryStats;
+ mStoreDir = new File(systemDir, BATTERY_USAGE_STATS_DIR);
+ mLockFile = new File(mStoreDir, DIR_LOCK_FILENAME);
+ mConfigFile = new AtomicFile(new File(mStoreDir, CONFIG_FILENAME));
+ mHandler = handler;
+ mMaxStorageBytes = maxStorageBytes;
+ mBatteryStats.setBatteryResetListener(this::prepareForBatteryStatsReset);
+ mBatteryUsageStatsProvider = new BatteryUsageStatsProvider(mContext, mBatteryStats);
+ }
+
+ private void prepareForBatteryStatsReset() {
+ final List<BatteryUsageStats> stats =
+ mBatteryUsageStatsProvider.getBatteryUsageStats(BATTERY_USAGE_STATS_QUERY);
+ if (stats.isEmpty()) {
+ Slog.wtf(TAG, "No battery usage stats generated");
+ return;
+ }
+
+ mHandler.post(() -> storeBatteryUsageStats(stats.get(0)));
+ }
+
+ private void storeBatteryUsageStats(BatteryUsageStats stats) {
+ try (FileLock lock = lockSnapshotDirectory()) {
+ if (!mStoreDir.exists()) {
+ if (!mStoreDir.mkdirs()) {
+ Slog.e(TAG,
+ "Could not create a directory for battery usage stats snapshots");
+ return;
+ }
+ }
+ File file = makeSnapshotFilename(stats.getStatsEndTimestamp());
+ try {
+ writeXmlFileLocked(stats, file);
+ } catch (Exception e) {
+ Slog.e(TAG, "Cannot save battery usage stats", e);
+ }
+
+ removeOldSnapshotsLocked();
+ } catch (IOException e) {
+ Slog.e(TAG, "Cannot lock battery usage stats directory", e);
+ }
+ }
+
+ /**
+ * Returns the timestamps of the stored BatteryUsageStats snapshots. The timestamp corresponds
+ * to the time the snapshot was taken {@link BatteryUsageStats#getStatsEndTimestamp()}.
+ */
+ public long[] listBatteryUsageStatsTimestamps() {
+ LongArray timestamps = new LongArray(100);
+ try (FileLock lock = lockSnapshotDirectory()) {
+ for (File file : mStoreDir.listFiles()) {
+ String fileName = file.getName();
+ if (fileName.endsWith(SNAPSHOT_FILE_EXTENSION)) {
+ try {
+ String fileNameWithoutExtension = fileName.substring(0,
+ fileName.length() - SNAPSHOT_FILE_EXTENSION.length());
+ timestamps.add(Long.parseLong(fileNameWithoutExtension));
+ } catch (NumberFormatException e) {
+ Slog.wtf(TAG, "Invalid format of BatteryUsageStats snapshot file name: "
+ + fileName);
+ }
+ }
+ }
+ } catch (IOException e) {
+ Slog.e(TAG, "Cannot lock battery usage stats directory", e);
+ }
+ return timestamps.toArray();
+ }
+
+ /**
+ * Reads the specified snapshot of BatteryUsageStats. Returns null if the snapshot
+ * does not exist.
+ */
+ @Nullable
+ public BatteryUsageStats loadBatteryUsageStats(long timestamp) {
+ try (FileLock lock = lockSnapshotDirectory()) {
+ File file = makeSnapshotFilename(timestamp);
+ try {
+ return readXmlFileLocked(file);
+ } catch (Exception e) {
+ Slog.e(TAG, "Cannot read battery usage stats", e);
+ }
+ } catch (IOException e) {
+ Slog.e(TAG, "Cannot lock battery usage stats directory", e);
+ }
+ return null;
+ }
+
+ /**
+ * Saves the supplied timestamp of the BATTERY_USAGE_STATS_BEFORE_RESET statsd atom pull
+ * in persistent file.
+ */
+ public void setLastBatteryUsageStatsBeforeResetAtomPullTimestamp(long timestamp) {
+ Properties props = new Properties();
+ try (FileLock lock = lockSnapshotDirectory()) {
+ try (InputStream in = mConfigFile.openRead()) {
+ props.load(in);
+ } catch (IOException e) {
+ Slog.e(TAG, "Cannot load config file " + mConfigFile, e);
+ }
+ props.put(BATTERY_USAGE_STATS_BEFORE_RESET_TIMESTAMP_PROPERTY,
+ String.valueOf(timestamp));
+ FileOutputStream out = null;
+ try {
+ out = mConfigFile.startWrite();
+ props.store(out, "Statsd atom pull timestamps");
+ mConfigFile.finishWrite(out);
+ } catch (IOException e) {
+ mConfigFile.failWrite(out);
+ Slog.e(TAG, "Cannot save config file " + mConfigFile, e);
+ }
+ } catch (IOException e) {
+ Slog.e(TAG, "Cannot lock battery usage stats directory", e);
+ }
+ }
+
+ /**
+ * Retrieves the previously saved timestamp of the last BATTERY_USAGE_STATS_BEFORE_RESET
+ * statsd atom pull.
+ */
+ public long getLastBatteryUsageStatsBeforeResetAtomPullTimestamp() {
+ Properties props = new Properties();
+ try (FileLock lock = lockSnapshotDirectory()) {
+ try (InputStream in = mConfigFile.openRead()) {
+ props.load(in);
+ } catch (IOException e) {
+ Slog.e(TAG, "Cannot load config file " + mConfigFile, e);
+ }
+ } catch (IOException e) {
+ Slog.e(TAG, "Cannot lock battery usage stats directory", e);
+ }
+ return Long.parseLong(
+ props.getProperty(BATTERY_USAGE_STATS_BEFORE_RESET_TIMESTAMP_PROPERTY, "0"));
+ }
+
+ private FileLock lockSnapshotDirectory() throws IOException {
+ mLockFile.getParentFile().mkdirs();
+ mLockFile.createNewFile();
+ return FileChannel.open(mLockFile.toPath(), StandardOpenOption.WRITE).lock();
+ }
+
+ /**
+ * Creates a file name by formatting the timestamp as 19-digit zero-padded number.
+ * This ensures that sorted directory list follows the chronological order.
+ */
+ private File makeSnapshotFilename(long statsEndTimestamp) {
+ return new File(mStoreDir, String.format(Locale.ENGLISH, "%019d", statsEndTimestamp)
+ + SNAPSHOT_FILE_EXTENSION);
+ }
+
+ private void writeXmlFileLocked(BatteryUsageStats stats, File file) throws IOException {
+ try (OutputStream out = new FileOutputStream(file)) {
+ TypedXmlSerializer serializer = Xml.newBinarySerializer();
+ serializer.setOutput(out, StandardCharsets.UTF_8.name());
+ serializer.startDocument(null, true);
+ stats.writeXml(serializer);
+ serializer.endDocument();
+ }
+ }
+
+ private BatteryUsageStats readXmlFileLocked(File file)
+ throws IOException, XmlPullParserException {
+ try (InputStream in = new FileInputStream(file)) {
+ TypedXmlPullParser parser = Xml.newBinaryPullParser();
+ parser.setInput(in, StandardCharsets.UTF_8.name());
+ return BatteryUsageStats.createFromXml(parser);
+ }
+ }
+
+ private void removeOldSnapshotsLocked() {
+ // Read the directory list into a _sorted_ map. The alphanumeric ordering
+ // corresponds to the historical order of snapshots because the file names
+ // are timestamps zero-padded to the same length.
+ long totalSize = 0;
+ TreeMap<File, Long> mFileSizes = new TreeMap<>();
+ for (File file : mStoreDir.listFiles()) {
+ final long fileSize = file.length();
+ totalSize += fileSize;
+ if (file.getName().endsWith(SNAPSHOT_FILE_EXTENSION)) {
+ mFileSizes.put(file, fileSize);
+ }
+ }
+
+ while (totalSize > mMaxStorageBytes) {
+ final Map.Entry<File, Long> entry = mFileSizes.firstEntry();
+ if (entry == null) {
+ break;
+ }
+
+ File file = entry.getKey();
+ if (!file.delete()) {
+ Slog.e(TAG, "Cannot delete battery usage stats " + file);
+ }
+ totalSize -= entry.getValue();
+ mFileSizes.remove(file);
+ }
+ }
+}
diff --git a/core/tests/coretests/src/android/os/PackageTagsListTest.java b/core/tests/coretests/src/android/os/PackageTagsListTest.java
index 518e02e44b06..9034a5ccdd7f 100644
--- a/core/tests/coretests/src/android/os/PackageTagsListTest.java
+++ b/core/tests/coretests/src/android/os/PackageTagsListTest.java
@@ -30,6 +30,7 @@ import org.junit.Test;
import org.junit.runner.RunWith;
import java.util.Arrays;
+import java.util.Collections;
@Presubmit
@RunWith(AndroidJUnit4.class)
@@ -40,7 +41,8 @@ public class PackageTagsListTest {
PackageTagsList.Builder builder = new PackageTagsList.Builder()
.add("package1", "attr1")
.add("package1", "attr2")
- .add("package2");
+ .add("package2")
+ .add("package4", Arrays.asList("attr1", "attr2"));
PackageTagsList list = builder.build();
assertTrue(list.contains(builder.build()));
@@ -49,10 +51,13 @@ public class PackageTagsListTest {
assertTrue(list.contains("package2", "attr1"));
assertTrue(list.contains("package2", "attr2"));
assertTrue(list.contains("package2", "attr3"));
+ assertTrue(list.contains("package4", "attr1"));
+ assertTrue(list.contains("package4", "attr2"));
assertTrue(list.containsAll("package2"));
assertTrue(list.includes("package1"));
assertTrue(list.includes("package2"));
assertFalse(list.contains("package1", "attr3"));
+ assertFalse(list.contains("package4", "attr3"));
assertFalse(list.containsAll("package1"));
assertFalse(list.includes("package3"));
@@ -92,6 +97,51 @@ public class PackageTagsListTest {
}
@Test
+ public void testPackageTagsList_Remove() {
+ PackageTagsList.Builder builder = new PackageTagsList.Builder()
+ .add("package1", "attr1")
+ .add("package1", "attr2")
+ .add("package2")
+ .add("package4", Arrays.asList("attr1", "attr2", "attr3"))
+ .add("package3", "attr1")
+ .remove("package1", "attr1")
+ .remove("package1", "attr2")
+ .remove("package2", "attr1")
+ .remove("package4", Arrays.asList("attr1", "attr2"))
+ .remove("package3");
+ PackageTagsList list = builder.build();
+
+ assertTrue(list.contains(builder.build()));
+ assertFalse(list.contains("package1", "attr1"));
+ assertFalse(list.contains("package1", "attr2"));
+ assertTrue(list.contains("package2", "attr1"));
+ assertTrue(list.contains("package2", "attr2"));
+ assertTrue(list.contains("package2", "attr3"));
+ assertFalse(list.contains("package3", "attr1"));
+ assertFalse(list.contains("package4", "attr1"));
+ assertFalse(list.contains("package4", "attr2"));
+ assertTrue(list.contains("package4", "attr3"));
+ assertTrue(list.containsAll("package2"));
+ assertFalse(list.includes("package1"));
+ assertTrue(list.includes("package2"));
+ assertFalse(list.includes("package3"));
+ assertTrue(list.includes("package4"));
+ }
+
+ @Test
+ public void testPackageTagsList_EmptyCollections() {
+ PackageTagsList.Builder builder = new PackageTagsList.Builder()
+ .add("package1", Collections.emptyList())
+ .add("package2")
+ .remove("package2", Collections.emptyList());
+ PackageTagsList list = builder.build();
+
+ assertTrue(list.contains(builder.build()));
+ assertFalse(list.contains("package1", "attr1"));
+ assertTrue(list.contains("package2", "attr2"));
+ }
+
+ @Test
public void testWriteToParcel() {
PackageTagsList list = new PackageTagsList.Builder()
.add("package1", "attr1")
diff --git a/core/tests/coretests/src/com/android/internal/os/BatteryStatsTests.java b/core/tests/coretests/src/com/android/internal/os/BatteryStatsTests.java
index 46e2772b30ca..90a9572b5560 100644
--- a/core/tests/coretests/src/com/android/internal/os/BatteryStatsTests.java
+++ b/core/tests/coretests/src/com/android/internal/os/BatteryStatsTests.java
@@ -44,6 +44,7 @@ import org.junit.runners.Suite;
BatteryStatsUidTest.class,
BatteryUsageStatsProviderTest.class,
BatteryUsageStatsTest.class,
+ BatteryUsageStatsStoreTest.class,
BatteryStatsUserLifecycleTests.class,
BluetoothPowerCalculatorTest.class,
BstatsCpuTimesValidationTest.class,
diff --git a/core/tests/coretests/src/com/android/internal/os/BatteryUsageStatsProviderTest.java b/core/tests/coretests/src/com/android/internal/os/BatteryUsageStatsProviderTest.java
index d83645d6e0a5..cbd67c8324f4 100644
--- a/core/tests/coretests/src/com/android/internal/os/BatteryUsageStatsProviderTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/BatteryUsageStatsProviderTest.java
@@ -20,10 +20,14 @@ import static com.google.common.truth.Truth.assertThat;
import android.app.ActivityManager;
import android.content.Context;
+import android.os.BatteryConsumer;
import android.os.BatteryManager;
import android.os.BatteryStats;
import android.os.BatteryUsageStats;
import android.os.BatteryUsageStatsQuery;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.Message;
import android.os.Parcel;
import android.os.Process;
import android.os.UidBatteryConsumer;
@@ -36,6 +40,7 @@ import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
+import java.io.File;
import java.util.List;
@SmallTest
@@ -45,7 +50,8 @@ public class BatteryUsageStatsProviderTest {
private static final long MINUTE_IN_MS = 60 * 1000;
@Rule
- public final BatteryUsageStatsRule mStatsRule = new BatteryUsageStatsRule(12345);
+ public final BatteryUsageStatsRule mStatsRule = new BatteryUsageStatsRule(12345)
+ .setAveragePower(PowerProfile.POWER_FLASHLIGHT, 360.0);
@Test
public void test_getBatteryUsageStats() {
@@ -187,4 +193,84 @@ public class BatteryUsageStatsProviderTest {
mStatsRule.setTime(11500, 0);
assertThat(provider.shouldUpdateStats(queries, 10000)).isTrue();
}
+
+ @Test
+ public void testAggregateBatteryStats() {
+ Context context = InstrumentationRegistry.getContext();
+ BatteryStatsImpl batteryStats = mStatsRule.getBatteryStats();
+ mStatsRule.setCurrentTime(5 * MINUTE_IN_MS);
+ batteryStats.resetAllStatsCmdLocked();
+
+ BatteryUsageStatsStore batteryUsageStatsStore = new BatteryUsageStatsStore(context,
+ batteryStats, new File(context.getCacheDir(), "BatteryUsageStatsProviderTest"),
+ new TestHandler(), Integer.MAX_VALUE);
+
+ BatteryUsageStatsProvider provider = new BatteryUsageStatsProvider(context,
+ batteryStats, batteryUsageStatsStore);
+
+ batteryStats.noteFlashlightOnLocked(APP_UID,
+ 10 * MINUTE_IN_MS, 10 * MINUTE_IN_MS);
+ batteryStats.noteFlashlightOffLocked(APP_UID,
+ 20 * MINUTE_IN_MS, 20 * MINUTE_IN_MS);
+ mStatsRule.setCurrentTime(25 * MINUTE_IN_MS);
+ batteryStats.resetAllStatsCmdLocked();
+
+ batteryStats.noteFlashlightOnLocked(APP_UID,
+ 30 * MINUTE_IN_MS, 30 * MINUTE_IN_MS);
+ batteryStats.noteFlashlightOffLocked(APP_UID,
+ 50 * MINUTE_IN_MS, 50 * MINUTE_IN_MS);
+ mStatsRule.setCurrentTime(55 * MINUTE_IN_MS);
+ batteryStats.resetAllStatsCmdLocked();
+
+ // This section should be ignored because the timestamp is out or range
+ batteryStats.noteFlashlightOnLocked(APP_UID,
+ 60 * MINUTE_IN_MS, 60 * MINUTE_IN_MS);
+ batteryStats.noteFlashlightOffLocked(APP_UID,
+ 70 * MINUTE_IN_MS, 70 * MINUTE_IN_MS);
+ mStatsRule.setCurrentTime(75 * MINUTE_IN_MS);
+ batteryStats.resetAllStatsCmdLocked();
+
+ // This section should be ignored because it represents the current stats session
+ batteryStats.noteFlashlightOnLocked(APP_UID,
+ 80 * MINUTE_IN_MS, 80 * MINUTE_IN_MS);
+ batteryStats.noteFlashlightOffLocked(APP_UID,
+ 90 * MINUTE_IN_MS, 90 * MINUTE_IN_MS);
+ mStatsRule.setCurrentTime(95 * MINUTE_IN_MS);
+
+ // Include the first and the second snapshot, but not the third or current
+ BatteryUsageStatsQuery query = new BatteryUsageStatsQuery.Builder()
+ .aggregateSnapshots(20 * MINUTE_IN_MS, 60 * MINUTE_IN_MS)
+ .build();
+ final BatteryUsageStats stats = provider.getBatteryUsageStats(query);
+
+ assertThat(stats.getStatsStartTimestamp()).isEqualTo(5 * MINUTE_IN_MS);
+ assertThat(stats.getStatsEndTimestamp()).isEqualTo(55 * MINUTE_IN_MS);
+ assertThat(stats.getAggregateBatteryConsumer(
+ BatteryUsageStats.AGGREGATE_BATTERY_CONSUMER_SCOPE_DEVICE)
+ .getConsumedPower(BatteryConsumer.POWER_COMPONENT_FLASHLIGHT))
+ .isWithin(0.0001)
+ .of(180.0); // 360 mA * 0.5 hours
+ assertThat(stats.getAggregateBatteryConsumer(
+ BatteryUsageStats.AGGREGATE_BATTERY_CONSUMER_SCOPE_DEVICE)
+ .getUsageDurationMillis(BatteryConsumer.POWER_COMPONENT_FLASHLIGHT))
+ .isEqualTo((10 + 20) * MINUTE_IN_MS);
+ final UidBatteryConsumer uidBatteryConsumer = stats.getUidBatteryConsumers().stream()
+ .filter(uid -> uid.getUid() == APP_UID).findFirst().get();
+ assertThat(uidBatteryConsumer
+ .getConsumedPower(BatteryConsumer.POWER_COMPONENT_FLASHLIGHT))
+ .isWithin(0.1)
+ .of(180.0);
+ }
+
+ private static class TestHandler extends Handler {
+ TestHandler() {
+ super(Looper.getMainLooper());
+ }
+
+ @Override
+ public boolean sendMessageAtTime(Message msg, long uptimeMillis) {
+ msg.getCallback().run();
+ return true;
+ }
+ }
}
diff --git a/core/tests/coretests/src/com/android/internal/os/BatteryUsageStatsStoreTest.java b/core/tests/coretests/src/com/android/internal/os/BatteryUsageStatsStoreTest.java
new file mode 100644
index 000000000000..141a9fa30c85
--- /dev/null
+++ b/core/tests/coretests/src/com/android/internal/os/BatteryUsageStatsStoreTest.java
@@ -0,0 +1,196 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.os;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.Mockito.mock;
+
+import android.content.Context;
+import android.os.BatteryManager;
+import android.os.BatteryUsageStats;
+import android.os.BatteryUsageStatsQuery;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.Message;
+import android.util.TypedXmlSerializer;
+import android.util.Xml;
+
+import androidx.test.InstrumentationRegistry;
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.IOException;
+import java.nio.charset.StandardCharsets;
+import java.util.Arrays;
+
+@RunWith(AndroidJUnit4.class)
+public class BatteryUsageStatsStoreTest {
+ private static final long MAX_BATTERY_STATS_SNAPSHOT_STORAGE_BYTES = 2 * 1024;
+
+ private final MockClocks mMockClocks = new MockClocks();
+ private MockBatteryStatsImpl mBatteryStats;
+ private BatteryUsageStatsStore mBatteryUsageStatsStore;
+ private BatteryUsageStatsProvider mBatteryUsageStatsProvider;
+ private File mStoreDirectory;
+
+ @Before
+ public void setup() {
+ mMockClocks.currentTime = 123;
+ mBatteryStats = new MockBatteryStatsImpl(mMockClocks);
+ mBatteryStats.setNoAutoReset(true);
+ mBatteryStats.setPowerProfile(mock(PowerProfile.class));
+
+ Context context = InstrumentationRegistry.getContext();
+
+ mStoreDirectory = new File(context.getCacheDir(), "BatteryUsageStatsStoreTest");
+ clearDirectory(mStoreDirectory);
+
+ mBatteryUsageStatsStore = new BatteryUsageStatsStore(context, mBatteryStats,
+ mStoreDirectory, new TestHandler(), MAX_BATTERY_STATS_SNAPSHOT_STORAGE_BYTES);
+
+ mBatteryUsageStatsProvider = new BatteryUsageStatsProvider(context, mBatteryStats);
+ }
+
+ @Test
+ public void testStoreSnapshot() {
+ mMockClocks.currentTime = 1_600_000;
+
+ prepareBatteryStats();
+ mBatteryStats.resetAllStatsCmdLocked();
+
+ final long[] timestamps = mBatteryUsageStatsStore.listBatteryUsageStatsTimestamps();
+ assertThat(timestamps).hasLength(1);
+ assertThat(timestamps[0]).isEqualTo(1_600_000);
+
+ final BatteryUsageStats batteryUsageStats = mBatteryUsageStatsStore.loadBatteryUsageStats(
+ 1_600_000);
+ assertThat(batteryUsageStats.getStatsStartTimestamp()).isEqualTo(123);
+ assertThat(batteryUsageStats.getStatsEndTimestamp()).isEqualTo(1_600_000);
+ assertThat(batteryUsageStats.getBatteryCapacity()).isEqualTo(4000);
+ assertThat(batteryUsageStats.getDischargePercentage()).isEqualTo(5);
+ assertThat(batteryUsageStats.getAggregateBatteryConsumer(
+ BatteryUsageStats.AGGREGATE_BATTERY_CONSUMER_SCOPE_DEVICE).getConsumedPower())
+ .isEqualTo(600); // (3_600_000 - 3_000_000) / 1000
+ }
+
+ @Test
+ public void testGarbageCollectOldSnapshots() throws Exception {
+ prepareBatteryStats();
+
+ mMockClocks.realtime = 10_000_000;
+ mMockClocks.uptime = 10_000_000;
+ mMockClocks.currentTime = 10_000_000;
+
+ final int snapshotFileSize = getSnapshotFileSize();
+ final int numberOfSnapshots =
+ (int) (MAX_BATTERY_STATS_SNAPSHOT_STORAGE_BYTES / snapshotFileSize);
+ for (int i = 0; i < numberOfSnapshots + 2; i++) {
+ mBatteryStats.resetAllStatsCmdLocked();
+
+ mMockClocks.realtime += 10_000_000;
+ mMockClocks.uptime += 10_000_000;
+ mMockClocks.currentTime += 10_000_000;
+ prepareBatteryStats();
+ }
+
+ final long[] timestamps = mBatteryUsageStatsStore.listBatteryUsageStatsTimestamps();
+ Arrays.sort(timestamps);
+ assertThat(timestamps).hasLength(numberOfSnapshots);
+ // Two snapshots (10_000_000 and 20_000_000) should have been discarded
+ assertThat(timestamps[0]).isEqualTo(30_000_000);
+ assertThat(getDirectorySize(mStoreDirectory))
+ .isAtMost(MAX_BATTERY_STATS_SNAPSHOT_STORAGE_BYTES);
+ }
+
+ @Test
+ public void testSavingStatsdAtomPullTimestamp() {
+ mBatteryUsageStatsStore.setLastBatteryUsageStatsBeforeResetAtomPullTimestamp(1234);
+ assertThat(mBatteryUsageStatsStore.getLastBatteryUsageStatsBeforeResetAtomPullTimestamp())
+ .isEqualTo(1234);
+ mBatteryUsageStatsStore.setLastBatteryUsageStatsBeforeResetAtomPullTimestamp(5478);
+ assertThat(mBatteryUsageStatsStore.getLastBatteryUsageStatsBeforeResetAtomPullTimestamp())
+ .isEqualTo(5478);
+ }
+
+ private void prepareBatteryStats() {
+ mBatteryStats.setBatteryStateLocked(BatteryManager.BATTERY_STATUS_DISCHARGING, 100,
+ /* plugType */ 0, 90, 72, 3700, 3_600_000, 4_000_000, 0,
+ mMockClocks.realtime, mMockClocks.uptime, mMockClocks.currentTime);
+ mBatteryStats.setBatteryStateLocked(BatteryManager.BATTERY_STATUS_DISCHARGING, 100,
+ /* plugType */ 0, 85, 72, 3700, 3_000_000, 4_000_000, 0,
+ mMockClocks.realtime + 500_000, mMockClocks.uptime + 500_000,
+ mMockClocks.currentTime + 500_000);
+ }
+
+ private void clearDirectory(File dir) {
+ if (dir.exists()) {
+ for (File child : dir.listFiles()) {
+ if (child.isDirectory()) {
+ clearDirectory(child);
+ }
+ child.delete();
+ }
+ }
+ }
+
+ private long getDirectorySize(File dir) {
+ long size = 0;
+ if (dir.exists()) {
+ for (File child : dir.listFiles()) {
+ if (child.isDirectory()) {
+ size += getDirectorySize(child);
+ } else {
+ size += child.length();
+ }
+ }
+ }
+ return size;
+ }
+
+ private int getSnapshotFileSize() throws IOException {
+ BatteryUsageStats stats = mBatteryUsageStatsProvider.getBatteryUsageStats(
+ new BatteryUsageStatsQuery.Builder()
+ .setMaxStatsAgeMs(0)
+ .includePowerModels()
+ .build());
+ ByteArrayOutputStream out = new ByteArrayOutputStream();
+ TypedXmlSerializer serializer = Xml.newBinarySerializer();
+ serializer.setOutput(out, StandardCharsets.UTF_8.name());
+ serializer.startDocument(null, true);
+ stats.writeXml(serializer);
+ serializer.endDocument();
+ return out.toByteArray().length;
+ }
+
+ private static class TestHandler extends Handler {
+ TestHandler() {
+ super(Looper.getMainLooper());
+ }
+
+ @Override
+ public boolean sendMessageAtTime(Message msg, long uptimeMillis) {
+ msg.getCallback().run();
+ return true;
+ }
+ }
+}
diff --git a/core/tests/coretests/src/com/android/internal/os/BatteryUsageStatsTest.java b/core/tests/coretests/src/com/android/internal/os/BatteryUsageStatsTest.java
index 380b4ae7e748..3e620c2bbec6 100644
--- a/core/tests/coretests/src/com/android/internal/os/BatteryUsageStatsTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/BatteryUsageStatsTest.java
@@ -16,8 +16,13 @@
package com.android.internal.os;
+import static android.os.BatteryConsumer.POWER_MODEL_MEASURED_ENERGY;
+import static android.os.BatteryConsumer.POWER_MODEL_POWER_PROFILE;
+import static android.os.BatteryConsumer.POWER_MODEL_UNDEFINED;
+
import static com.google.common.truth.Truth.assertThat;
+import static org.junit.Assert.assertThrows;
import static org.junit.Assert.fail;
import android.os.BatteryConsumer;
@@ -25,6 +30,9 @@ import android.os.BatteryUsageStats;
import android.os.Parcel;
import android.os.UidBatteryConsumer;
import android.os.UserBatteryConsumer;
+import android.util.TypedXmlPullParser;
+import android.util.TypedXmlSerializer;
+import android.util.Xml;
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
@@ -32,8 +40,11 @@ import androidx.test.runner.AndroidJUnit4;
import org.junit.Test;
import org.junit.runner.RunWith;
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
import java.io.PrintWriter;
import java.io.StringWriter;
+import java.nio.charset.StandardCharsets;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
@@ -42,15 +53,19 @@ import java.util.Set;
@RunWith(AndroidJUnit4.class)
public class BatteryUsageStatsTest {
+ private static final int USER_ID = 42;
+ private static final int APP_UID1 = 271;
+ private static final int APP_UID2 = 314;
+
@Test
public void testBuilder() {
- BatteryUsageStats batteryUsageStats = buildBatteryUsageStats().build();
- validateBatteryUsageStats(batteryUsageStats);
+ BatteryUsageStats batteryUsageStats = buildBatteryUsageStats1(true).build();
+ assertBatteryUsageStats1(batteryUsageStats, true);
}
@Test
public void testParcelability() {
- final BatteryUsageStats outBatteryUsageStats = buildBatteryUsageStats().build();
+ final BatteryUsageStats outBatteryUsageStats = buildBatteryUsageStats1(true).build();
final Parcel outParcel = Parcel.obtain();
outParcel.writeParcelable(outBatteryUsageStats, 0);
final byte[] bytes = outParcel.marshall();
@@ -62,20 +77,20 @@ public class BatteryUsageStatsTest {
final BatteryUsageStats inBatteryUsageStats =
inParcel.readParcelable(getClass().getClassLoader());
assertThat(inBatteryUsageStats).isNotNull();
- validateBatteryUsageStats(inBatteryUsageStats);
+ assertBatteryUsageStats1(inBatteryUsageStats, true);
}
@Test
public void testDefaultSessionDuration() {
final BatteryUsageStats stats =
- buildBatteryUsageStats().setStatsDuration(10000).build();
+ buildBatteryUsageStats1(true).setStatsDuration(10000).build();
assertThat(stats.getStatsDuration()).isEqualTo(10000);
}
@Test
public void testDump() {
- final BatteryUsageStats stats = buildBatteryUsageStats().build();
+ final BatteryUsageStats stats = buildBatteryUsageStats1(true).build();
final StringWriter out = new StringWriter();
try (PrintWriter pw = new PrintWriter(out)) {
stats.dump(pw, " ");
@@ -87,7 +102,7 @@ public class BatteryUsageStatsTest {
assertThat(dump).contains("actual drain: 1000-2000");
assertThat(dump).contains("cpu: 20100 apps: 10100 duration: 20s 300ms");
assertThat(dump).contains("FOO: 20200 apps: 10200 duration: 20s 400ms");
- assertThat(dump).contains("UID 2000: 1200 ( screen=300 cpu=400 FOO=500 )");
+ assertThat(dump).contains("UID 271: 1200 ( screen=300 cpu=400 FOO=500 )");
assertThat(dump).contains("User 42: 30.0 ( cpu=10.0 FOO=20.0 )");
}
@@ -101,154 +116,297 @@ public class BatteryUsageStatsTest {
assertThat(allNames).hasSize(BatteryConsumer.POWER_COMPONENT_COUNT);
}
- private BatteryUsageStats.Builder buildBatteryUsageStats() {
+ @Test
+ public void testAdd() {
+ final BatteryUsageStats stats1 = buildBatteryUsageStats1(false).build();
+ final BatteryUsageStats stats2 = buildBatteryUsageStats2(new String[] {"FOO"}).build();
+
+ final BatteryUsageStats sum =
+ new BatteryUsageStats.Builder(new String[] {"FOO"}, true)
+ .add(stats1)
+ .add(stats2)
+ .build();
+
+ assertBatteryUsageStats(sum, 42345, 50, 2234, 4345, 1000, 5000, 5000);
+
+ final List<UidBatteryConsumer> uidBatteryConsumers =
+ sum.getUidBatteryConsumers();
+ for (UidBatteryConsumer uidBatteryConsumer : uidBatteryConsumers) {
+ if (uidBatteryConsumer.getUid() == APP_UID1) {
+ assertUidBatteryConsumer(uidBatteryConsumer, 2124, null,
+ 5321, 7432, 423, POWER_MODEL_POWER_PROFILE, 745, POWER_MODEL_UNDEFINED,
+ 956, 1167, 1478);
+ } else if (uidBatteryConsumer.getUid() == APP_UID2) {
+ assertUidBatteryConsumer(uidBatteryConsumer, 1332, "bar",
+ 1111, 2222, 333, POWER_MODEL_POWER_PROFILE, 444, POWER_MODEL_POWER_PROFILE,
+ 555, 666, 777);
+ } else {
+ fail("Unexpected UID " + uidBatteryConsumer.getUid());
+ }
+ }
+
+ assertAggregateBatteryConsumer(sum,
+ BatteryUsageStats.AGGREGATE_BATTERY_CONSUMER_SCOPE_ALL_APPS,
+ 20223, 20434, 20645, 20856);
+
+ assertAggregateBatteryConsumer(sum,
+ BatteryUsageStats.AGGREGATE_BATTERY_CONSUMER_SCOPE_DEVICE,
+ 40211, 40422, 40633, 40844);
+ }
+
+ @Test
+ public void testAdd_customComponentMismatch() {
+ final BatteryUsageStats.Builder builder =
+ new BatteryUsageStats.Builder(new String[] {"FOO"}, true);
+ final BatteryUsageStats stats = buildBatteryUsageStats2(new String[] {"BAR"}).build();
+
+ assertThrows(IllegalArgumentException.class, () -> builder.add(stats));
+ }
+
+ @Test
+ public void testXml() throws Exception {
+ ByteArrayOutputStream out = new ByteArrayOutputStream();
+ TypedXmlSerializer serializer = Xml.newBinarySerializer();
+ serializer.setOutput(out, StandardCharsets.UTF_8.name());
+ serializer.startDocument(null, true);
+ final BatteryUsageStats stats = buildBatteryUsageStats1(true).build();
+ stats.writeXml(serializer);
+ serializer.endDocument();
+
+ ByteArrayInputStream in = new ByteArrayInputStream(out.toByteArray());
+ TypedXmlPullParser parser = Xml.newBinaryPullParser();
+ parser.setInput(in, StandardCharsets.UTF_8.name());
+ final BatteryUsageStats fromXml = BatteryUsageStats.createFromXml(parser);
+
+ assertBatteryUsageStats1(fromXml, true);
+ }
+
+ private BatteryUsageStats.Builder buildBatteryUsageStats1(boolean includeUserBatteryConsumer) {
final MockClocks clocks = new MockClocks();
final MockBatteryStatsImpl batteryStats = new MockBatteryStatsImpl(clocks);
- final BatteryStatsImpl.Uid batteryStatsUid = batteryStats.getUidStatsLocked(2000);
final BatteryUsageStats.Builder builder =
- new BatteryUsageStats.Builder(new String[]{"FOO"}, true)
+ new BatteryUsageStats.Builder(new String[] {"FOO"}, true)
.setBatteryCapacity(4000)
.setDischargePercentage(20)
.setDischargedPowerRange(1000, 2000)
.setStatsStartTimestamp(1000)
.setStatsEndTimestamp(3000);
- builder.getOrCreateUidBatteryConsumerBuilder(batteryStatsUid)
- .setPackageWithHighestDrain("foo")
- .setTimeInStateMs(UidBatteryConsumer.STATE_FOREGROUND, 1000)
- .setTimeInStateMs(UidBatteryConsumer.STATE_BACKGROUND, 2000)
- .setConsumedPower(
- BatteryConsumer.POWER_COMPONENT_SCREEN, 300)
- .setConsumedPower(
- BatteryConsumer.POWER_COMPONENT_CPU, 400)
- .setConsumedPowerForCustomComponent(
- BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID, 500)
- .setUsageDurationMillis(
- BatteryConsumer.POWER_COMPONENT_CPU, 600)
- .setUsageDurationForCustomComponentMillis(
- BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID, 800);
- builder.getAggregateBatteryConsumerBuilder(
- BatteryUsageStats.AGGREGATE_BATTERY_CONSUMER_SCOPE_ALL_APPS)
- .setConsumedPower(
- BatteryConsumer.POWER_COMPONENT_CPU, 10100)
- .setConsumedPowerForCustomComponent(
- BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID, 10200)
- .setUsageDurationMillis(
- BatteryConsumer.POWER_COMPONENT_CPU, 10300)
- .setUsageDurationForCustomComponentMillis(
- BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID, 10400);
+ addUidBatteryConsumer(builder, batteryStats, APP_UID1, "foo",
+ 1000, 2000,
+ 300, POWER_MODEL_POWER_PROFILE, 400, POWER_MODEL_POWER_PROFILE, 500, 600, 800);
+
+ addAggregateBatteryConsumer(builder,
+ BatteryUsageStats.AGGREGATE_BATTERY_CONSUMER_SCOPE_ALL_APPS, 0,
+ 10100, 10200, 10300, 10400);
+
+ addAggregateBatteryConsumer(builder,
+ BatteryUsageStats.AGGREGATE_BATTERY_CONSUMER_SCOPE_DEVICE, 30000,
+ 20100, 20200, 20300, 20400);
+
+
+ if (includeUserBatteryConsumer) {
+ builder.getOrCreateUserBatteryConsumerBuilder(USER_ID)
+ .setConsumedPower(
+ BatteryConsumer.POWER_COMPONENT_CPU, 10)
+ .setConsumedPowerForCustomComponent(
+ BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID, 20)
+ .setUsageDurationMillis(
+ BatteryConsumer.POWER_COMPONENT_CPU, 30)
+ .setUsageDurationForCustomComponentMillis(
+ BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID, 40);
+ }
+ return builder;
+ }
+
+ private BatteryUsageStats.Builder buildBatteryUsageStats2(String[] customPowerComponentNames) {
+ final MockClocks clocks = new MockClocks();
+ final MockBatteryStatsImpl batteryStats = new MockBatteryStatsImpl(clocks);
+
+ final BatteryUsageStats.Builder builder =
+ new BatteryUsageStats.Builder(customPowerComponentNames, true)
+ .setDischargePercentage(30)
+ .setDischargedPowerRange(1234, 2345)
+ .setStatsStartTimestamp(2000)
+ .setStatsEndTimestamp(5000);
- builder.getAggregateBatteryConsumerBuilder(
- BatteryUsageStats.AGGREGATE_BATTERY_CONSUMER_SCOPE_DEVICE)
- .setConsumedPower(30000)
+ addUidBatteryConsumer(builder, batteryStats, APP_UID1, null,
+ 4321, 5432,
+ 123, POWER_MODEL_POWER_PROFILE, 345, POWER_MODEL_MEASURED_ENERGY, 456, 567, 678);
+
+ addUidBatteryConsumer(builder, batteryStats, APP_UID2, "bar",
+ 1111, 2222,
+ 333, POWER_MODEL_POWER_PROFILE, 444, POWER_MODEL_POWER_PROFILE, 555, 666, 777);
+
+ addAggregateBatteryConsumer(builder,
+ BatteryUsageStats.AGGREGATE_BATTERY_CONSUMER_SCOPE_ALL_APPS, 0,
+ 10123, 10234, 10345, 10456);
+
+ addAggregateBatteryConsumer(builder,
+ BatteryUsageStats.AGGREGATE_BATTERY_CONSUMER_SCOPE_DEVICE, 12345,
+ 20111, 20222, 20333, 20444);
+
+ return builder;
+ }
+
+ private void addUidBatteryConsumer(BatteryUsageStats.Builder builder,
+ MockBatteryStatsImpl batteryStats, int uid, String packageWithHighestDrain,
+ int timeInStateForeground, int timeInStateBackground, int screenPower,
+ int screenPowerModel, int cpuPower, int cpuPowerModel, int customComponentPower,
+ int cpuDuration, int customComponentDuration) {
+ final BatteryStatsImpl.Uid batteryStatsUid = batteryStats.getUidStatsLocked(uid);
+ builder.getOrCreateUidBatteryConsumerBuilder(batteryStatsUid)
+ .setPackageWithHighestDrain(packageWithHighestDrain)
+ .setTimeInStateMs(UidBatteryConsumer.STATE_FOREGROUND, timeInStateForeground)
+ .setTimeInStateMs(UidBatteryConsumer.STATE_BACKGROUND, timeInStateBackground)
.setConsumedPower(
- BatteryConsumer.POWER_COMPONENT_CPU, 20100)
+ BatteryConsumer.POWER_COMPONENT_SCREEN, screenPower, screenPowerModel)
+ .setConsumedPower(
+ BatteryConsumer.POWER_COMPONENT_CPU, cpuPower, cpuPowerModel)
.setConsumedPowerForCustomComponent(
- BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID, 20200)
+ BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID, customComponentPower)
.setUsageDurationMillis(
- BatteryConsumer.POWER_COMPONENT_CPU, 20300)
+ BatteryConsumer.POWER_COMPONENT_CPU, cpuDuration)
.setUsageDurationForCustomComponentMillis(
- BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID, 20400);
+ BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID, customComponentDuration);
+ }
- builder.getOrCreateUserBatteryConsumerBuilder(42)
+ private void addAggregateBatteryConsumer(BatteryUsageStats.Builder builder, int scope,
+ double consumedPower, int cpuPower, int customComponentPower, int cpuDuration,
+ int customComponentDuration) {
+ builder.getAggregateBatteryConsumerBuilder(scope)
+ .setConsumedPower(consumedPower)
.setConsumedPower(
- BatteryConsumer.POWER_COMPONENT_CPU, 10)
+ BatteryConsumer.POWER_COMPONENT_CPU, cpuPower)
.setConsumedPowerForCustomComponent(
- BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID, 20)
+ BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID, customComponentPower)
.setUsageDurationMillis(
- BatteryConsumer.POWER_COMPONENT_CPU, 30)
+ BatteryConsumer.POWER_COMPONENT_CPU, cpuDuration)
.setUsageDurationForCustomComponentMillis(
- BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID, 40);
-
- return builder;
+ BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID, customComponentDuration);
}
- public void validateBatteryUsageStats(BatteryUsageStats batteryUsageStats) {
- assertThat(batteryUsageStats.getConsumedPower()).isEqualTo(30000);
- assertThat(batteryUsageStats.getBatteryCapacity()).isEqualTo(4000);
- assertThat(batteryUsageStats.getDischargePercentage()).isEqualTo(20);
- assertThat(batteryUsageStats.getDischargedPowerRange().getLower()).isEqualTo(1000);
- assertThat(batteryUsageStats.getDischargedPowerRange().getUpper()).isEqualTo(2000);
- assertThat(batteryUsageStats.getStatsStartTimestamp()).isEqualTo(1000);
- assertThat(batteryUsageStats.getStatsEndTimestamp()).isEqualTo(3000);
- assertThat(batteryUsageStats.getStatsDuration()).isEqualTo(2000);
+ public void assertBatteryUsageStats1(BatteryUsageStats batteryUsageStats,
+ boolean includesUserBatteryConsumers) {
+ assertBatteryUsageStats(batteryUsageStats, 30000, 20, 1000, 2000, 1000, 3000, 2000);
final List<UidBatteryConsumer> uidBatteryConsumers =
batteryUsageStats.getUidBatteryConsumers();
+ assertThat(uidBatteryConsumers).hasSize(1);
for (UidBatteryConsumer uidBatteryConsumer : uidBatteryConsumers) {
- if (uidBatteryConsumer.getUid() == 2000) {
- assertThat(uidBatteryConsumer.getPackageWithHighestDrain()).isEqualTo("foo");
- assertThat(uidBatteryConsumer.getTimeInStateMs(
- UidBatteryConsumer.STATE_FOREGROUND)).isEqualTo(1000);
- assertThat(uidBatteryConsumer.getTimeInStateMs(
- UidBatteryConsumer.STATE_BACKGROUND)).isEqualTo(2000);
- assertThat(uidBatteryConsumer.getConsumedPower(
- BatteryConsumer.POWER_COMPONENT_SCREEN)).isEqualTo(300);
- assertThat(uidBatteryConsumer.getConsumedPower(
- BatteryConsumer.POWER_COMPONENT_CPU)).isEqualTo(400);
- assertThat(uidBatteryConsumer.getConsumedPowerForCustomComponent(
- BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID)).isEqualTo(500);
- assertThat(uidBatteryConsumer.getUsageDurationMillis(
- BatteryConsumer.POWER_COMPONENT_CPU)).isEqualTo(600);
- assertThat(uidBatteryConsumer.getUsageDurationForCustomComponentMillis(
- BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID)).isEqualTo(800);
- assertThat(uidBatteryConsumer.getConsumedPower()).isEqualTo(1200);
- assertThat(uidBatteryConsumer.getCustomPowerComponentCount()).isEqualTo(1);
- assertThat(uidBatteryConsumer.getCustomPowerComponentName(
- BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID)).isEqualTo("FOO");
+ if (uidBatteryConsumer.getUid() == APP_UID1) {
+ assertUidBatteryConsumer(uidBatteryConsumer, 1200, "foo",
+ 1000, 2000, 300, POWER_MODEL_POWER_PROFILE, 400, POWER_MODEL_POWER_PROFILE,
+ 500, 600, 800);
} else {
fail("Unexpected UID " + uidBatteryConsumer.getUid());
}
}
+ final List<UserBatteryConsumer> userBatteryConsumers =
+ batteryUsageStats.getUserBatteryConsumers();
+ if (includesUserBatteryConsumers) {
+ assertThat(userBatteryConsumers).hasSize(1);
+ for (UserBatteryConsumer userBatteryConsumer : userBatteryConsumers) {
+ if (userBatteryConsumer.getUserId() == USER_ID) {
+ assertUserBatteryConsumer(userBatteryConsumer, 42, 10, 20, 30, 40);
+ } else {
+ fail("Unexpected User ID " + userBatteryConsumer.getUserId());
+ }
+ }
+ } else {
+ assertThat(userBatteryConsumers).isEmpty();
+ }
+
+ assertAggregateBatteryConsumer(batteryUsageStats,
+ BatteryUsageStats.AGGREGATE_BATTERY_CONSUMER_SCOPE_ALL_APPS,
+ 10100, 10200, 10300, 10400);
+
+ assertAggregateBatteryConsumer(batteryUsageStats,
+ BatteryUsageStats.AGGREGATE_BATTERY_CONSUMER_SCOPE_DEVICE,
+ 20100, 20200, 20300, 20400);
+ }
+
+ private void assertBatteryUsageStats(BatteryUsageStats batteryUsageStats, int consumedPower,
+ int dischargePercentage, int dischagePowerLower, int dischargePowerUpper,
+ int statsStartTimestamp, int statsEndTimestamp, int statsDuration) {
+ assertThat(batteryUsageStats.getConsumedPower()).isEqualTo(consumedPower);
+ assertThat(batteryUsageStats.getDischargePercentage()).isEqualTo(dischargePercentage);
+ assertThat(batteryUsageStats.getDischargedPowerRange().getLower()).isEqualTo(
+ dischagePowerLower);
+ assertThat(batteryUsageStats.getDischargedPowerRange().getUpper()).isEqualTo(
+ dischargePowerUpper);
+ assertThat(batteryUsageStats.getStatsStartTimestamp()).isEqualTo(statsStartTimestamp);
+ assertThat(batteryUsageStats.getStatsEndTimestamp()).isEqualTo(statsEndTimestamp);
+ assertThat(batteryUsageStats.getStatsDuration()).isEqualTo(statsDuration);
+ }
+
+ private void assertUidBatteryConsumer(UidBatteryConsumer uidBatteryConsumer,
+ int consumedPower, String packageWithHighestDrain, int timeInStateForeground,
+ int timeInStateBackground, int screenPower, int screenPowerModel, int cpuPower,
+ int cpuPowerModel, int customComponentPower, int cpuDuration,
+ int customComponentDuration) {
+ assertThat(uidBatteryConsumer.getConsumedPower()).isEqualTo(consumedPower);
+ assertThat(uidBatteryConsumer.getPackageWithHighestDrain()).isEqualTo(
+ packageWithHighestDrain);
+ assertThat(uidBatteryConsumer.getTimeInStateMs(
+ UidBatteryConsumer.STATE_FOREGROUND)).isEqualTo(timeInStateForeground);
+ assertThat(uidBatteryConsumer.getTimeInStateMs(
+ UidBatteryConsumer.STATE_BACKGROUND)).isEqualTo(timeInStateBackground);
+ assertThat(uidBatteryConsumer.getConsumedPower(
+ BatteryConsumer.POWER_COMPONENT_SCREEN)).isEqualTo(screenPower);
+ assertThat(uidBatteryConsumer.getPowerModel(
+ BatteryConsumer.POWER_COMPONENT_SCREEN)).isEqualTo(screenPowerModel);
+ assertThat(uidBatteryConsumer.getConsumedPower(
+ BatteryConsumer.POWER_COMPONENT_CPU)).isEqualTo(cpuPower);
+ assertThat(uidBatteryConsumer.getPowerModel(
+ BatteryConsumer.POWER_COMPONENT_CPU)).isEqualTo(cpuPowerModel);
+ assertThat(uidBatteryConsumer.getConsumedPowerForCustomComponent(
+ BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID)).isEqualTo(customComponentPower);
+ assertThat(uidBatteryConsumer.getUsageDurationMillis(
+ BatteryConsumer.POWER_COMPONENT_CPU)).isEqualTo(cpuDuration);
+ assertThat(uidBatteryConsumer.getUsageDurationForCustomComponentMillis(
+ BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID)).isEqualTo(
+ customComponentDuration);
+ assertThat(uidBatteryConsumer.getCustomPowerComponentCount()).isEqualTo(1);
+ assertThat(uidBatteryConsumer.getCustomPowerComponentName(
+ BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID)).isEqualTo("FOO");
+ }
+
+ private void assertUserBatteryConsumer(UserBatteryConsumer userBatteryConsumer,
+ int userId, int cpuPower, int customComponentPower,
+ int cpuDuration, int customComponentDuration) {
+ assertThat(userBatteryConsumer.getConsumedPower(
+ BatteryConsumer.POWER_COMPONENT_CPU)).isEqualTo(cpuPower);
+ assertThat(userBatteryConsumer.getConsumedPowerForCustomComponent(
+ BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID)).isEqualTo(customComponentPower);
+ assertThat(userBatteryConsumer.getUsageDurationMillis(
+ BatteryConsumer.POWER_COMPONENT_CPU)).isEqualTo(cpuDuration);
+ assertThat(userBatteryConsumer.getUsageDurationForCustomComponentMillis(
+ BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID)).isEqualTo(
+ customComponentDuration);
+ assertThat(userBatteryConsumer.getCustomPowerComponentCount()).isEqualTo(1);
+ assertThat(userBatteryConsumer.getCustomPowerComponentName(
+ BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID)).isEqualTo("FOO");
+ }
+
+ private void assertAggregateBatteryConsumer(BatteryUsageStats batteryUsageStats,
+ int aggregateBatteryConsumerScopeAllApps, int cpuPower, int customComponentPower,
+ int cpuDuration, int customComponentDuration) {
final BatteryConsumer appsBatteryConsumer = batteryUsageStats.getAggregateBatteryConsumer(
- BatteryUsageStats.AGGREGATE_BATTERY_CONSUMER_SCOPE_ALL_APPS);
+ aggregateBatteryConsumerScopeAllApps);
assertThat(appsBatteryConsumer.getConsumedPower(
- BatteryConsumer.POWER_COMPONENT_CPU)).isEqualTo(10100);
+ BatteryConsumer.POWER_COMPONENT_CPU)).isEqualTo(cpuPower);
assertThat(appsBatteryConsumer.getConsumedPowerForCustomComponent(
- BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID)).isEqualTo(10200);
+ BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID)).isEqualTo(customComponentPower);
assertThat(appsBatteryConsumer.getUsageDurationMillis(
- BatteryConsumer.POWER_COMPONENT_CPU)).isEqualTo(10300);
+ BatteryConsumer.POWER_COMPONENT_CPU)).isEqualTo(cpuDuration);
assertThat(appsBatteryConsumer.getUsageDurationForCustomComponentMillis(
- BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID)).isEqualTo(10400);
+ BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID)).isEqualTo(
+ customComponentDuration);
assertThat(appsBatteryConsumer.getCustomPowerComponentCount()).isEqualTo(1);
assertThat(appsBatteryConsumer.getCustomPowerComponentName(
BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID)).isEqualTo("FOO");
-
- final BatteryConsumer deviceBatteryConsumer = batteryUsageStats.getAggregateBatteryConsumer(
- BatteryUsageStats.AGGREGATE_BATTERY_CONSUMER_SCOPE_DEVICE);
- assertThat(deviceBatteryConsumer.getConsumedPower(
- BatteryConsumer.POWER_COMPONENT_CPU)).isEqualTo(20100);
- assertThat(deviceBatteryConsumer.getConsumedPowerForCustomComponent(
- BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID)).isEqualTo(20200);
- assertThat(deviceBatteryConsumer.getUsageDurationMillis(
- BatteryConsumer.POWER_COMPONENT_CPU)).isEqualTo(20300);
- assertThat(deviceBatteryConsumer.getUsageDurationForCustomComponentMillis(
- BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID)).isEqualTo(20400);
- assertThat(deviceBatteryConsumer.getCustomPowerComponentCount()).isEqualTo(1);
- assertThat(deviceBatteryConsumer.getCustomPowerComponentName(
- BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID)).isEqualTo("FOO");
-
- final List<UserBatteryConsumer> userBatteryConsumers =
- batteryUsageStats.getUserBatteryConsumers();
- for (UserBatteryConsumer userBatteryConsumer : userBatteryConsumers) {
- if (userBatteryConsumer.getUserId() == 42) {
- assertThat(userBatteryConsumer.getConsumedPower(
- BatteryConsumer.POWER_COMPONENT_CPU)).isEqualTo(10);
- assertThat(userBatteryConsumer.getConsumedPowerForCustomComponent(
- BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID)).isEqualTo(20);
- assertThat(userBatteryConsumer.getUsageDurationMillis(
- BatteryConsumer.POWER_COMPONENT_CPU)).isEqualTo(30);
- assertThat(userBatteryConsumer.getUsageDurationForCustomComponentMillis(
- BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID)).isEqualTo(40);
- assertThat(userBatteryConsumer.getConsumedPower()).isEqualTo(30);
- assertThat(userBatteryConsumer.getCustomPowerComponentCount()).isEqualTo(1);
- assertThat(userBatteryConsumer.getCustomPowerComponentName(
- BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID)).isEqualTo("FOO");
- } else {
- fail("Unexpected user ID " + userBatteryConsumer.getUserId());
- }
- }
}
}
diff --git a/data/etc/car/com.android.car.cluster.home.xml b/data/etc/car/com.android.car.cluster.home.xml
index e1d2b18d7167..a3d0fcffc813 100644
--- a/data/etc/car/com.android.car.cluster.home.xml
+++ b/data/etc/car/com.android.car.cluster.home.xml
@@ -18,5 +18,6 @@
<privapp-permissions package="com.android.car.cluster.home">
<permission name="android.permission.INTERACT_ACROSS_USERS"/>
<permission name="android.car.permission.CAR_INSTRUMENT_CLUSTER_CONTROL"/>
+ <permission name="android.car.permission.CAR_MONITOR_INPUT"/>
</privapp-permissions>
</permissions>
diff --git a/data/etc/services.core.protolog.json b/data/etc/services.core.protolog.json
index 3c9086dde021..ac5e2d0fcacb 100644
--- a/data/etc/services.core.protolog.json
+++ b/data/etc/services.core.protolog.json
@@ -961,6 +961,12 @@
"group": "WM_DEBUG_APP_TRANSITIONS",
"at": "com\/android\/server\/wm\/AppTransitionController.java"
},
+ "-1003678883": {
+ "message": "Cleaning splash screen token=%s",
+ "level": "VERBOSE",
+ "group": "WM_DEBUG_STARTING_WINDOW",
+ "at": "com\/android\/server\/wm\/ActivityRecord.java"
+ },
"-1003060523": {
"message": "Finish needs to pause: %s",
"level": "VERBOSE",
diff --git a/graphics/java/android/graphics/HardwareRenderer.java b/graphics/java/android/graphics/HardwareRenderer.java
index 954d062b55e9..6aa74cb415f9 100644
--- a/graphics/java/android/graphics/HardwareRenderer.java
+++ b/graphics/java/android/graphics/HardwareRenderer.java
@@ -753,8 +753,12 @@ public class HardwareRenderer {
nCancelLayerUpdate(mNativeProxy, layer.getDeferredLayerUpdater());
}
+ private ASurfaceTransactionCallback mASurfaceTransactionCallback;
+
/** @hide */
public void setASurfaceTransactionCallback(ASurfaceTransactionCallback callback) {
+ // ensure callback is kept alive on the java side since weak ref is used in native code
+ mASurfaceTransactionCallback = callback;
nSetASurfaceTransactionCallback(mNativeProxy, callback);
}
diff --git a/graphics/java/android/graphics/drawable/RippleDrawable.java b/graphics/java/android/graphics/drawable/RippleDrawable.java
index fe80b5845bf5..1651a8cdcad5 100644
--- a/graphics/java/android/graphics/drawable/RippleDrawable.java
+++ b/graphics/java/android/graphics/drawable/RippleDrawable.java
@@ -221,6 +221,7 @@ public class RippleDrawable extends LayerDrawable {
private boolean mForceSoftware;
// Patterned
+ private boolean mAddRipple = false;
private float mTargetBackgroundOpacity;
private ValueAnimator mBackgroundAnimation;
private float mBackgroundOpacity;
@@ -716,6 +717,7 @@ public class RippleDrawable extends LayerDrawable {
}
cancelExitingRipples();
+ exitPatternedAnimation();
}
@Override
@@ -807,7 +809,7 @@ public class RippleDrawable extends LayerDrawable {
}
private void startPatternedAnimation() {
- mRippleActive = true;
+ mAddRipple = true;
invalidateSelf(false);
}
@@ -862,17 +864,17 @@ public class RippleDrawable extends LayerDrawable {
h = bounds.height();
w = bounds.width();
}
- boolean shouldAnimate = mRippleActive;
+ boolean addRipple = mAddRipple;
boolean shouldExit = mExitingAnimation;
- mRippleActive = false;
mExitingAnimation = false;
- if (mRunningAnimations.size() > 0 && !shouldAnimate) {
+ mAddRipple = false;
+ if (mRunningAnimations.size() > 0 && !addRipple) {
// update paint when view is invalidated
getRipplePaint();
}
drawContent(canvas);
drawPatternedBackground(canvas, cx, cy);
- if (shouldAnimate && mRunningAnimations.size() <= MAX_RIPPLES) {
+ if (addRipple && mRunningAnimations.size() <= MAX_RIPPLES) {
RippleAnimationSession.AnimationProperties<Float, Paint> properties =
createAnimationProperties(x, y, cx, cy, w, h);
mRunningAnimations.add(new RippleAnimationSession(properties, !useCanvasProps)
diff --git a/libs/WindowManager/Shell/res/values/config.xml b/libs/WindowManager/Shell/res/values/config.xml
index 4c2863e4f594..8cea869aea34 100644
--- a/libs/WindowManager/Shell/res/values/config.xml
+++ b/libs/WindowManager/Shell/res/values/config.xml
@@ -40,7 +40,7 @@
<integer name="long_press_dock_anim_duration">250</integer>
<!-- Animation duration for translating of one handed when trigger / dismiss. -->
- <integer name="config_one_handed_translate_animation_duration">800</integer>
+ <integer name="config_one_handed_translate_animation_duration">600</integer>
<!-- One handed mode default offset % of display size -->
<fraction name="config_one_handed_offset">40%</fraction>
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/ShellTaskOrganizer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/ShellTaskOrganizer.java
index f7fb63d9ab98..4b1955e56a6c 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/ShellTaskOrganizer.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/ShellTaskOrganizer.java
@@ -325,6 +325,13 @@ public class ShellTaskOrganizer extends TaskOrganizer {
}
@Override
+ public void onAppSplashScreenViewRemoved(int taskId) {
+ if (mStartingWindow != null) {
+ mStartingWindow.onAppSplashScreenViewRemoved(taskId);
+ }
+ }
+
+ @Override
public void onTaskAppeared(RunningTaskInfo taskInfo, SurfaceControl leash) {
synchronized (mLock) {
onTaskAppeared(new TaskAppearedInfo(taskInfo, leash));
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHanded.java b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHanded.java
index 8dc05de9bb8f..a525c2c0219c 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHanded.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHanded.java
@@ -68,6 +68,11 @@ public interface OneHanded {
void setLockedDisabled(boolean locked, boolean enabled);
/**
+ * Registers callback to notify WMShell when user tap shortcut to expand notification.
+ */
+ void registerEventCallback(OneHandedEventCallback callback);
+
+ /**
* Registers callback to be notified after {@link OneHandedDisplayAreaOrganizer}
* transition start or finish
*/
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedController.java
index c275d50a5d56..b43daa0da2c0 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedController.java
@@ -43,6 +43,7 @@ import android.util.Slog;
import android.view.Surface;
import android.view.WindowManager;
import android.view.accessibility.AccessibilityManager;
+import android.widget.Toast;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
@@ -72,6 +73,7 @@ public class OneHandedController implements RemoteCallable<OneHandedController>
private static final String ONE_HANDED_MODE_GESTURAL_OVERLAY =
"com.android.internal.systemui.onehanded.gestural";
private static final int OVERLAY_ENABLED_DELAY_MS = 250;
+ private static final int DISPLAY_AREA_READY_RETRY_MS = 10;
static final String SUPPORT_ONE_HANDED_MODE = "ro.support_one_handed_mode";
@@ -99,6 +101,7 @@ public class OneHandedController implements RemoteCallable<OneHandedController>
private final Handler mMainHandler;
private final OneHandedImpl mImpl = new OneHandedImpl();
+ private OneHandedEventCallback mEventCallback;
private OneHandedDisplayAreaOrganizer mDisplayAreaOrganizer;
private OneHandedBackgroundPanelOrganizer mBackgroundPanelOrganizer;
@@ -288,7 +291,7 @@ public class OneHandedController implements RemoteCallable<OneHandedController>
mTimeoutObserver = getObserver(this::onTimeoutSettingChanged);
mTaskChangeExitObserver = getObserver(this::onTaskChangeExitSettingChanged);
mSwipeToNotificationEnabledObserver =
- getObserver(this::onSwipeToNotificationEnabledSettingChanged);
+ getObserver(this::onSwipeToNotificationEnabledChanged);
mDisplayController.addDisplayChangingController(mRotationController);
setupCallback();
@@ -358,14 +361,23 @@ public class OneHandedController implements RemoteCallable<OneHandedController>
Slog.d(TAG, "Temporary lock disabled");
return;
}
+
+ if (!mDisplayAreaOrganizer.isReady()) {
+ // Must wait until DisplayAreaOrganizer is ready for transitioning.
+ mMainExecutor.executeDelayed(this::startOneHanded, DISPLAY_AREA_READY_RETRY_MS);
+ return;
+ }
+
if (mState.isTransitioning() || mState.isInOneHanded()) {
return;
}
+
final int currentRotation = mDisplayAreaOrganizer.getDisplayLayout().rotation();
if (currentRotation != Surface.ROTATION_0 && currentRotation != Surface.ROTATION_180) {
Slog.w(TAG, "One handed mode only support portrait mode");
return;
}
+
mState.setState(STATE_ENTERING);
final int yOffSet = Math.round(
mDisplayAreaOrganizer.getDisplayLayout().height() * mOffSetFraction);
@@ -394,6 +406,10 @@ public class OneHandedController implements RemoteCallable<OneHandedController>
mOneHandedUiEventLogger.writeEvent(uiEvent);
}
+ void registerEventCallback(OneHandedEventCallback callback) {
+ mEventCallback = callback;
+ }
+
@VisibleForTesting
void registerTransitionCallback(OneHandedTransitionCallback callback) {
mDisplayAreaOrganizer.registerTransitionCallback(callback);
@@ -464,8 +480,29 @@ public class OneHandedController implements RemoteCallable<OneHandedController>
}
@VisibleForTesting
+ void notifyExpandNotification() {
+ mMainExecutor.execute(() -> mEventCallback.notifyExpandNotification());
+ }
+
+ @VisibleForTesting
+ void notifyUserConfigChanged(boolean success) {
+ if (!success) {
+ return;
+ }
+ // TODO Check UX if popup Toast to notify user when auto-enabled one-handed is good option.
+ Toast.makeText(mContext, R.string.one_handed_tutorial_title, Toast.LENGTH_LONG).show();
+ }
+
+ @VisibleForTesting
void onActivatedActionChanged() {
- if (mState.isTransitioning() || !isOneHandedEnabled()) {
+ if (!isOneHandedEnabled()) {
+ final boolean success = mOneHandedSettingsUtil.setOneHandedModeEnabled(
+ mContext.getContentResolver(), 1 /* Enabled for shortcut */, mUserId);
+ notifyUserConfigChanged(success);
+ }
+
+ if (isSwipeToNotificationEnabled()) {
+ notifyExpandNotification();
return;
}
@@ -494,11 +531,9 @@ public class OneHandedController implements RemoteCallable<OneHandedController>
setOneHandedEnabled(enabled);
// Also checks swipe to notification settings since they all need gesture overlay.
- // Enabled overlay package may affect the current animation(e.g:Settings switch),
- // so we delay 250ms to enabled overlay after switch animation finish
- mMainExecutor.executeDelayed(() -> setEnabledGesturalOverlay(
+ setEnabledGesturalOverlay(
enabled || mOneHandedSettingsUtil.getSettingsSwipeToNotificationEnabled(
- mContext.getContentResolver(), mUserId)), OVERLAY_ENABLED_DELAY_MS);
+ mContext.getContentResolver(), mUserId), true /* DelayExecute */);
}
@VisibleForTesting
@@ -542,7 +577,7 @@ public class OneHandedController implements RemoteCallable<OneHandedController>
}
@VisibleForTesting
- void onSwipeToNotificationEnabledSettingChanged() {
+ void onSwipeToNotificationEnabledChanged() {
final boolean enabled =
mOneHandedSettingsUtil.getSettingsSwipeToNotificationEnabled(
mContext.getContentResolver(), mUserId);
@@ -551,7 +586,7 @@ public class OneHandedController implements RemoteCallable<OneHandedController>
// Also checks one handed mode settings since they all need gesture overlay.
setEnabledGesturalOverlay(
enabled || mOneHandedSettingsUtil.getSettingsOneHandedModeEnabled(
- mContext.getContentResolver(), mUserId));
+ mContext.getContentResolver(), mUserId), true /* DelayExecute */);
}
private void setupTimeoutListener() {
@@ -569,11 +604,19 @@ public class OneHandedController implements RemoteCallable<OneHandedController>
return mIsOneHandedEnabled;
}
+ @VisibleForTesting
+ boolean isSwipeToNotificationEnabled() {
+ return mIsSwipeToNotificationEnabled;
+ }
+
private void updateOneHandedEnabled() {
if (mState.getState() == STATE_ENTERING || mState.getState() == STATE_ACTIVE) {
mMainExecutor.execute(() -> stopOneHanded());
}
+ // Reset and align shortcut one_handed_mode_activated status with current mState
+ notifyShortcutState(mState.getState());
+
mTouchHandler.onOneHandedEnabled(mIsOneHandedEnabled);
if (!mIsOneHandedEnabled) {
@@ -608,12 +651,19 @@ public class OneHandedController implements RemoteCallable<OneHandedController>
if (info != null && !info.isEnabled()) {
// Enable the default gestural one handed overlay.
- setEnabledGesturalOverlay(true);
+ setEnabledGesturalOverlay(true /* enabled */, false /* delayExecute */);
}
}
@VisibleForTesting
- private void setEnabledGesturalOverlay(boolean enabled) {
+ private void setEnabledGesturalOverlay(boolean enabled, boolean delayExecute) {
+ if (mState.isTransitioning() || delayExecute) {
+ // Enabled overlay package may affect the current animation(e.g:Settings switch),
+ // so we delay 250ms to enabled overlay after switch animation finish, only delay once.
+ mMainExecutor.executeDelayed(() -> setEnabledGesturalOverlay(enabled, false),
+ OVERLAY_ENABLED_DELAY_MS);
+ return;
+ }
try {
mOverlayManager.setEnabled(ONE_HANDED_MODE_GESTURAL_OVERLAY, enabled, USER_CURRENT);
} catch (RemoteException e) {
@@ -628,6 +678,7 @@ public class OneHandedController implements RemoteCallable<OneHandedController>
if (enabled == isFeatureEnabled) {
return;
}
+
mLockedDisabled = locked && !enabled;
}
@@ -761,6 +812,13 @@ public class OneHandedController implements RemoteCallable<OneHandedController>
}
@Override
+ public void registerEventCallback(OneHandedEventCallback callback) {
+ mMainExecutor.execute(() -> {
+ OneHandedController.this.registerEventCallback(callback);
+ });
+ }
+
+ @Override
public void registerTransitionCallback(OneHandedTransitionCallback callback) {
mMainExecutor.execute(() -> {
OneHandedController.this.registerTransitionCallback(callback);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedDisplayAreaOrganizer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedDisplayAreaOrganizer.java
index b8da37fd0c25..d749c320bf94 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedDisplayAreaOrganizer.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedDisplayAreaOrganizer.java
@@ -61,11 +61,12 @@ public class OneHandedDisplayAreaOrganizer extends DisplayAreaOrganizer {
private DisplayLayout mDisplayLayout = new DisplayLayout();
- private float mLastVisualOffset = 0;
private final Rect mLastVisualDisplayBounds = new Rect();
private final Rect mDefaultDisplayBounds = new Rect();
private final OneHandedSettingsUtil mOneHandedSettingsUtil;
+ private boolean mIsReady;
+ private float mLastVisualOffset = 0;
private int mEnterExitAnimationDurationMs;
private ArrayMap<WindowContainerToken, SurfaceControl> mDisplayAreaTokenMap = new ArrayMap();
@@ -157,6 +158,7 @@ public class OneHandedDisplayAreaOrganizer extends DisplayAreaOrganizer {
final DisplayAreaAppearedInfo info = displayAreaInfos.get(i);
onDisplayAreaAppeared(info.getDisplayAreaInfo(), info.getLeash());
}
+ mIsReady = true;
updateDisplayBounds();
return displayAreaInfos;
}
@@ -164,9 +166,14 @@ public class OneHandedDisplayAreaOrganizer extends DisplayAreaOrganizer {
@Override
public void unregisterOrganizer() {
super.unregisterOrganizer();
+ mIsReady = false;
resetWindowsOffset();
}
+ boolean isReady() {
+ return mIsReady;
+ }
+
/**
* Handler for display rotation changes by {@link DisplayLayout}
*
@@ -312,6 +319,8 @@ public class OneHandedDisplayAreaOrganizer extends DisplayAreaOrganizer {
pw.println(mDisplayAreaTokenMap);
pw.print(innerPrefix + "mDefaultDisplayBounds=");
pw.println(mDefaultDisplayBounds);
+ pw.print(innerPrefix + "mIsReady=");
+ pw.println(mIsReady);
pw.print(innerPrefix + "mLastVisualDisplayBounds=");
pw.println(mLastVisualDisplayBounds);
pw.print(innerPrefix + "mLastVisualOffset=");
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedEventCallback.java b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedEventCallback.java
new file mode 100644
index 000000000000..d07eea271eac
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedEventCallback.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.wm.shell.onehanded;
+
+/**
+ * Additional callback interface for OneHanded events.
+ */
+public interface OneHandedEventCallback {
+ /**
+ * Called to notify expand notification shade.
+ */
+ default void notifyExpandNotification() {
+ }
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedSettingsUtil.java b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedSettingsUtil.java
index 90fc823fb574..da53b359a304 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedSettingsUtil.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedSettingsUtil.java
@@ -105,6 +105,17 @@ public final class OneHandedSettingsUtil {
}
/**
+ * Sets one handed enable or disable flag from Settings provider.
+ *
+ * @return true if the value was set, false on database errors
+ */
+ public boolean setOneHandedModeEnabled(ContentResolver resolver, int enabled, int userId) {
+ return Settings.Secure.putIntForUser(resolver,
+ Settings.Secure.ONE_HANDED_MODE_ENABLED, enabled, userId);
+ }
+
+
+ /**
* Queries taps app to exit config from Settings provider.
*
* @return enable or disable taps app exit.
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipResizeGestureHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipResizeGestureHandler.java
index 841edef9172f..f0bd8a2846ed 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipResizeGestureHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipResizeGestureHandler.java
@@ -235,15 +235,20 @@ public class PipResizeGestureHandler {
@VisibleForTesting
void onInputEvent(InputEvent ev) {
+ if (!mEnableDragCornerResize && !mEnablePinchResize) {
+ // No need to handle anything if neither form of resizing is enabled.
+ return;
+ }
+
// Don't allow resize when PiP is stashed.
if (mPipBoundsState.isStashed()) {
return;
}
if (ev instanceof MotionEvent) {
- if (mOngoingPinchToResize) {
+ if (mEnablePinchResize && mOngoingPinchToResize) {
onPinchResize((MotionEvent) ev);
- } else {
+ } else if (mEnableDragCornerResize) {
onDragCornerResize((MotionEvent) ev);
}
}
@@ -318,8 +323,8 @@ public class PipResizeGestureHandler {
case MotionEvent.ACTION_POINTER_DOWN:
if (mEnablePinchResize && ev.getPointerCount() == 2) {
onPinchResize(ev);
- mOngoingPinchToResize = true;
- return true;
+ mOngoingPinchToResize = mAllowGesture;
+ return mAllowGesture;
}
break;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/StartingSurfaceDrawer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/StartingSurfaceDrawer.java
index 4d33cb0452dc..46db35a6e29f 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/StartingSurfaceDrawer.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/StartingSurfaceDrawer.java
@@ -35,6 +35,7 @@ import android.graphics.Rect;
import android.graphics.drawable.ColorDrawable;
import android.hardware.display.DisplayManager;
import android.os.IBinder;
+import android.os.RemoteCallback;
import android.os.Trace;
import android.os.UserHandle;
import android.util.Slog;
@@ -42,6 +43,7 @@ import android.util.SparseArray;
import android.view.Choreographer;
import android.view.Display;
import android.view.SurfaceControl;
+import android.view.SurfaceControlViewHost;
import android.view.View;
import android.view.WindowManager;
import android.widget.FrameLayout;
@@ -121,6 +123,13 @@ public class StartingSurfaceDrawer {
private final SparseArray<StartingWindowRecord> mStartingWindowRecords = new SparseArray<>();
+ /**
+ * Records of {@link SurfaceControlViewHost} where the splash screen icon animation is
+ * rendered and that have not yet been removed by their client.
+ */
+ private final SparseArray<SurfaceControlViewHost> mAnimatedSplashScreenSurfaceHosts =
+ new SparseArray<>(1);
+
/** Obtain proper context for showing splash screen on the provided display. */
private Context getDisplayContext(Context context, int displayId) {
if (displayId == DEFAULT_DISPLAY) {
@@ -386,25 +395,58 @@ public class StartingSurfaceDrawer {
/**
* Called when the Task wants to copy the splash screen.
- * @param taskId
*/
public void copySplashScreenView(int taskId) {
final StartingWindowRecord preView = mStartingWindowRecords.get(taskId);
SplashScreenViewParcelable parcelable;
- if (preView != null && preView.mContentView != null
- && preView.mContentView.isCopyable()) {
- parcelable = new SplashScreenViewParcelable(preView.mContentView);
- preView.mContentView.onCopied();
+ SplashScreenView splashScreenView = preView != null ? preView.mContentView : null;
+ if (splashScreenView != null && splashScreenView.isCopyable()) {
+ parcelable = new SplashScreenViewParcelable(splashScreenView);
+ parcelable.setClientCallback(
+ new RemoteCallback((bundle) -> mSplashScreenExecutor.execute(
+ () -> onAppSplashScreenViewRemoved(taskId, false))));
+ splashScreenView.onCopied();
+ mAnimatedSplashScreenSurfaceHosts.append(taskId, splashScreenView.getSurfaceHost());
} else {
parcelable = null;
}
if (DEBUG_SPLASH_SCREEN) {
Slog.v(TAG, "Copying splash screen window view for task: " + taskId
- + " parcelable? " + parcelable);
+ + " parcelable: " + parcelable);
}
ActivityTaskManager.getInstance().onSplashScreenViewCopyFinished(taskId, parcelable);
}
+ /**
+ * Called when the {@link SplashScreenView} is removed from the client Activity view's hierarchy
+ * or when the Activity is clean up.
+ *
+ * @param taskId The Task id on which the splash screen was attached
+ */
+ public void onAppSplashScreenViewRemoved(int taskId) {
+ onAppSplashScreenViewRemoved(taskId, true /* fromServer */);
+ }
+
+ /**
+ * @param fromServer If true, this means the removal was notified by the server. This is only
+ * used for debugging purposes.
+ * @see #onAppSplashScreenViewRemoved(int)
+ */
+ private void onAppSplashScreenViewRemoved(int taskId, boolean fromServer) {
+ SurfaceControlViewHost viewHost =
+ mAnimatedSplashScreenSurfaceHosts.get(taskId);
+ if (viewHost == null) {
+ return;
+ }
+ mAnimatedSplashScreenSurfaceHosts.remove(taskId);
+ if (DEBUG_SPLASH_SCREEN) {
+ String reason = fromServer ? "Server cleaned up" : "App removed";
+ Slog.v(TAG, reason + "the splash screen. Releasing SurfaceControlViewHost for task:"
+ + taskId);
+ }
+ viewHost.getView().post(viewHost::release);
+ }
+
protected boolean addWindow(int taskId, IBinder appToken, View view, WindowManager wm,
WindowManager.LayoutParams params) {
boolean shouldSaveView = true;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/StartingWindowController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/StartingWindowController.java
index cffc789106cb..9c1dde925762 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/StartingWindowController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/StartingWindowController.java
@@ -150,6 +150,14 @@ public class StartingWindowController implements RemoteCallable<StartingWindowCo
}
/**
+ * @see StartingSurfaceDrawer#onAppSplashScreenViewRemoved(int)
+ */
+ public void onAppSplashScreenViewRemoved(int taskId) {
+ mSplashScreenExecutor.execute(
+ () -> mStartingSurfaceDrawer.onAppSplashScreenViewRemoved(taskId));
+ }
+
+ /**
* Called when the content of a task is ready to show, starting window can be removed.
*/
public void removeStartingWindow(int taskId, SurfaceControl leash, Rect frame,
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedControllerTest.java
index 1852279ee96c..47789b7490ee 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedControllerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedControllerTest.java
@@ -74,6 +74,8 @@ public class OneHandedControllerTest extends OneHandedTestCase {
@Mock
OneHandedDisplayAreaOrganizer mMockDisplayAreaOrganizer;
@Mock
+ OneHandedEventCallback mMockEventCallback;
+ @Mock
OneHandedTouchHandler mMockTouchHandler;
@Mock
OneHandedTutorialHandler mMockTutorialHandler;
@@ -106,6 +108,7 @@ public class OneHandedControllerTest extends OneHandedTestCase {
when(mMockDisplayController.getDisplay(anyInt())).thenReturn(mDisplay);
when(mMockDisplayAreaOrganizer.getDisplayAreaTokenMap()).thenReturn(new ArrayMap<>());
+ when(mMockDisplayAreaOrganizer.isReady()).thenReturn(true);
when(mMockBackgroundOrganizer.getBackgroundSurface()).thenReturn(mMockLeash);
when(mMockSettingsUitl.getSettingsOneHandedModeEnabled(any(), anyInt())).thenReturn(
mDefaultEnabled);
@@ -241,7 +244,7 @@ public class OneHandedControllerTest extends OneHandedTestCase {
@Test
public void testSettingsObserverUpdateSwipeToNotification() {
- mSpiedOneHandedController.onSwipeToNotificationEnabledSettingChanged();
+ mSpiedOneHandedController.onSwipeToNotificationEnabledChanged();
verify(mSpiedOneHandedController).setSwipeToNotificationEnabled(anyBoolean());
}
@@ -311,6 +314,7 @@ public class OneHandedControllerTest extends OneHandedTestCase {
final DisplayLayout testDisplayLayout = new DisplayLayout(mDisplayLayout);
testDisplayLayout.rotateTo(mContext.getResources(), Surface.ROTATION_180);
mSpiedTransitionState.setState(STATE_NONE);
+ when(mMockDisplayAreaOrganizer.isReady()).thenReturn(true);
when(mMockDisplayAreaOrganizer.getDisplayLayout()).thenReturn(testDisplayLayout);
mSpiedOneHandedController.setOneHandedEnabled(true);
mSpiedOneHandedController.setLockedDisabled(false /* locked */, false /* enabled */);
@@ -372,8 +376,7 @@ public class OneHandedControllerTest extends OneHandedTestCase {
when(mMockSettingsUitl.getOneHandedModeActivated(any(), anyInt())).thenReturn(true);
mSpiedOneHandedController.onActivatedActionChanged();
- verify(mSpiedOneHandedController, never()).startOneHanded();
- verify(mSpiedOneHandedController, never()).stopOneHanded();
+ verify(mSpiedTransitionState, never()).setState(STATE_EXITING);
}
@Test
@@ -383,20 +386,20 @@ public class OneHandedControllerTest extends OneHandedTestCase {
when(mMockSettingsUitl.getOneHandedModeActivated(any(), anyInt())).thenReturn(true);
mSpiedOneHandedController.onActivatedActionChanged();
- verify(mSpiedOneHandedController, never()).startOneHanded();
- verify(mSpiedOneHandedController, never()).stopOneHanded();
+ verify(mSpiedTransitionState, never()).setState(STATE_ENTERING);
}
@Test
- public void testOneHandedDisabled_shortcutEnabled_skipActions() {
+ public void testOneHandedDisabled_shortcutTrigger_thenAutoEnabled() {
when(mSpiedOneHandedController.isOneHandedEnabled()).thenReturn(false);
when(mSpiedTransitionState.getState()).thenReturn(STATE_NONE);
when(mSpiedTransitionState.isTransitioning()).thenReturn(false);
- when(mMockSettingsUitl.getOneHandedModeActivated(any(), anyInt())).thenReturn(true);
+ when(mMockSettingsUitl.getOneHandedModeActivated(any(), anyInt())).thenReturn(false);
+ when(mMockSettingsUitl.setOneHandedModeEnabled(any(), anyInt(), anyInt())).thenReturn(
+ false);
mSpiedOneHandedController.onActivatedActionChanged();
- verify(mSpiedOneHandedController, never()).startOneHanded();
- verify(mSpiedOneHandedController, never()).stopOneHanded();
+ verify(mSpiedOneHandedController).notifyUserConfigChanged(anyBoolean());
}
@Test
@@ -408,4 +411,28 @@ public class OneHandedControllerTest extends OneHandedTestCase {
verify(mSpiedTransitionState).addSListeners(mMockTutorialHandler);
}
+
+ @Test
+ public void testNotifyEventCallbackWithMainExecutor() {
+ when(mSpiedOneHandedController.isOneHandedEnabled()).thenReturn(true);
+ when(mSpiedTransitionState.getState()).thenReturn(STATE_NONE);
+ when(mSpiedTransitionState.isTransitioning()).thenReturn(false);
+ when(mSpiedOneHandedController.isSwipeToNotificationEnabled()).thenReturn(true);
+ mSpiedOneHandedController.registerEventCallback(mMockEventCallback);
+ mSpiedOneHandedController.onActivatedActionChanged();
+
+ verify(mMockShellMainExecutor).execute(any());
+ }
+
+ @Test
+ public void testNotifyShortcutState_whenUpdateOneHandedEnabled() {
+ when(mSpiedOneHandedController.isOneHandedEnabled()).thenReturn(false);
+ when(mSpiedTransitionState.getState()).thenReturn(STATE_NONE);
+ when(mSpiedTransitionState.isTransitioning()).thenReturn(false);
+ when(mSpiedOneHandedController.isSwipeToNotificationEnabled()).thenReturn(true);
+ mSpiedOneHandedController.registerEventCallback(mMockEventCallback);
+ mSpiedOneHandedController.setOneHandedEnabled(true);
+
+ verify(mSpiedOneHandedController).notifyShortcutState(anyInt());
+ }
}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedDisplayAreaOrganizerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedDisplayAreaOrganizerTest.java
index a27ed114de70..ef16fd391235 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedDisplayAreaOrganizerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedDisplayAreaOrganizerTest.java
@@ -418,4 +418,18 @@ public class OneHandedDisplayAreaOrganizerTest extends OneHandedTestCase {
verify(mSpiedDisplayAreaOrganizer, never()).resetWindowsOffset();
}
+
+ @Test
+ public void testDisplayArea_notReadyForTransition() {
+ OneHandedDisplayAreaOrganizer testSpiedDisplayAreaOrganizer = spy(
+ new OneHandedDisplayAreaOrganizer(mContext,
+ mDisplayLayout,
+ mMockSettingsUitl,
+ mMockAnimationController,
+ mTutorialHandler,
+ mMockBackgroundOrganizer,
+ mMockShellMainExecutor));
+
+ assertThat(testSpiedDisplayAreaOrganizer.isReady()).isFalse();
+ }
}
diff --git a/libs/hwui/jni/android_graphics_HardwareRenderer.cpp b/libs/hwui/jni/android_graphics_HardwareRenderer.cpp
index 602c32a966d3..819a34b21a05 100644
--- a/libs/hwui/jni/android_graphics_HardwareRenderer.cpp
+++ b/libs/hwui/jni/android_graphics_HardwareRenderer.cpp
@@ -500,6 +500,28 @@ private:
jobject mObject;
};
+class JWeakGlobalRefHolder {
+public:
+ JWeakGlobalRefHolder(JavaVM* vm, jobject object) : mVm(vm) {
+ mWeakRef = getenv(vm)->NewWeakGlobalRef(object);
+ }
+
+ virtual ~JWeakGlobalRefHolder() {
+ if (mWeakRef != nullptr) getenv(mVm)->DeleteWeakGlobalRef(mWeakRef);
+ mWeakRef = nullptr;
+ }
+
+ jobject ref() { return mWeakRef; }
+ JavaVM* vm() { return mVm; }
+
+private:
+ JWeakGlobalRefHolder(const JWeakGlobalRefHolder&) = delete;
+ void operator=(const JWeakGlobalRefHolder&) = delete;
+
+ JavaVM* mVm;
+ jobject mWeakRef;
+};
+
using TextureMap = std::unordered_map<uint32_t, sk_sp<SkImage>>;
struct PictureCaptureState {
@@ -633,15 +655,19 @@ static void android_view_ThreadedRenderer_setASurfaceTransactionCallback(
} else {
JavaVM* vm = nullptr;
LOG_ALWAYS_FATAL_IF(env->GetJavaVM(&vm) != JNI_OK, "Unable to get Java VM");
- auto globalCallbackRef = std::make_shared<JGlobalRefHolder>(
- vm, env->NewGlobalRef(aSurfaceTransactionCallback));
+ auto globalCallbackRef =
+ std::make_shared<JWeakGlobalRefHolder>(vm, aSurfaceTransactionCallback);
proxy->setASurfaceTransactionCallback(
[globalCallbackRef](int64_t transObj, int64_t scObj, int64_t frameNr) {
JNIEnv* env = getenv(globalCallbackRef->vm());
- env->CallVoidMethod(globalCallbackRef->object(),
- gASurfaceTransactionCallback.onMergeTransaction,
+ jobject localref = env->NewLocalRef(globalCallbackRef->ref());
+ if (CC_UNLIKELY(!localref)) {
+ return;
+ }
+ env->CallVoidMethod(localref, gASurfaceTransactionCallback.onMergeTransaction,
static_cast<jlong>(transObj), static_cast<jlong>(scObj),
static_cast<jlong>(frameNr));
+ env->DeleteLocalRef(localref);
});
}
}
diff --git a/location/java/android/location/LocationManagerInternal.java b/location/java/android/location/LocationManagerInternal.java
index 763835c9cbe2..d59756d02348 100644
--- a/location/java/android/location/LocationManagerInternal.java
+++ b/location/java/android/location/LocationManagerInternal.java
@@ -16,14 +16,10 @@
package android.location;
-
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.location.util.identity.CallerIdentity;
-
-import com.android.internal.annotations.Immutable;
-
-import java.util.Set;
+import android.os.PackageTagsList;
/**
* Location manager local system service interface.
@@ -43,18 +39,14 @@ public abstract class LocationManagerInternal {
}
/**
- * Interface for getting callbacks when a location provider's location tags change.
- *
- * @see LocationTagInfo
+ * Interface for getting callbacks when an app id's location provider package tags change.
*/
- public interface OnProviderLocationTagsChangeListener {
+ public interface LocationPackageTagsListener {
/**
- * Called when the location tags for a provider change.
- *
- * @param providerLocationTagInfo The tag info for a provider.
+ * Called when the package tags for a location provider change for a uid.
*/
- void onLocationTagsChanged(@NonNull LocationTagInfo providerLocationTagInfo);
+ void onLocationPackageTagsChanged(int uid, @NonNull PackageTagsList packageTagsList);
}
/**
@@ -109,58 +101,9 @@ public abstract class LocationManagerInternal {
public abstract @Nullable LocationTime getGnssTimeMillis();
/**
- * Sets a listener for changes in the location providers' tags. Passing
+ * Sets a listener for changes in an app id's location provider package tags. Passing
* {@code null} clears the current listener.
- *
- * @param listener The listener.
*/
- public abstract void setOnProviderLocationTagsChangeListener(
- @Nullable OnProviderLocationTagsChangeListener listener);
-
- /**
- * This class represents the location permission tags used by the location provider
- * packages in a given UID. These tags are strictly used for accessing state guarded
- * by the location permission(s) by a location provider which are required for the
- * provider to fulfill its function as being a location provider.
- */
- @Immutable
- public static class LocationTagInfo {
- private final int mUid;
-
- @NonNull
- private final String mPackageName;
-
- @Nullable
- private final Set<String> mLocationTags;
-
- public LocationTagInfo(int uid, @NonNull String packageName,
- @Nullable Set<String> locationTags) {
- mUid = uid;
- mPackageName = packageName;
- mLocationTags = locationTags;
- }
-
- /**
- * @return The UID for which tags are related.
- */
- public int getUid() {
- return mUid;
- }
-
- /**
- * @return The package for which tags are related.
- */
- @NonNull
- public String getPackageName() {
- return mPackageName;
- }
-
- /**
- * @return The tags for the package used for location related accesses.
- */
- @Nullable
- public Set<String> getTags() {
- return mLocationTags;
- }
- }
+ public abstract void setLocationPackageTagsListener(
+ @Nullable LocationPackageTagsListener listener);
}
diff --git a/packages/CtsShim/apk/arm/CtsShim.apk b/packages/CtsShim/apk/arm/CtsShim.apk
index bb6dfa3a4ede..18915039b6a1 100644
--- a/packages/CtsShim/apk/arm/CtsShim.apk
+++ b/packages/CtsShim/apk/arm/CtsShim.apk
Binary files differ
diff --git a/packages/CtsShim/apk/arm/CtsShimPriv.apk b/packages/CtsShim/apk/arm/CtsShimPriv.apk
index 2835d57474d9..eb2202586753 100644
--- a/packages/CtsShim/apk/arm/CtsShimPriv.apk
+++ b/packages/CtsShim/apk/arm/CtsShimPriv.apk
Binary files differ
diff --git a/packages/CtsShim/apk/x86/CtsShim.apk b/packages/CtsShim/apk/x86/CtsShim.apk
index bb6dfa3a4ede..18915039b6a1 100644
--- a/packages/CtsShim/apk/x86/CtsShim.apk
+++ b/packages/CtsShim/apk/x86/CtsShim.apk
Binary files differ
diff --git a/packages/CtsShim/apk/x86/CtsShimPriv.apk b/packages/CtsShim/apk/x86/CtsShimPriv.apk
index 2e1a78989b70..b3f923c4176f 100644
--- a/packages/CtsShim/apk/x86/CtsShimPriv.apk
+++ b/packages/CtsShim/apk/x86/CtsShimPriv.apk
Binary files differ
diff --git a/packages/SettingsLib/src/com/android/settingslib/enterprise/ActionDisabledByAdminController.java b/packages/SettingsLib/src/com/android/settingslib/enterprise/ActionDisabledByAdminController.java
index a5373944474c..0b3a519cd919 100644
--- a/packages/SettingsLib/src/com/android/settingslib/enterprise/ActionDisabledByAdminController.java
+++ b/packages/SettingsLib/src/com/android/settingslib/enterprise/ActionDisabledByAdminController.java
@@ -16,8 +16,10 @@
package com.android.settingslib.enterprise;
+import android.annotation.NonNull;
import android.annotation.UserIdInt;
import android.content.Context;
+import android.content.DialogInterface;
import androidx.annotation.Nullable;
@@ -54,4 +56,13 @@ public interface ActionDisabledByAdminController {
* Updates the enforced admin
*/
void updateEnforcedAdmin(RestrictedLockUtils.EnforcedAdmin admin, @UserIdInt int adminUserId);
+
+ /**
+ * Returns a listener for handling positive button clicks
+ */
+ @Nullable
+ default DialogInterface.OnClickListener getPositiveButtonListener(@NonNull Context context,
+ @NonNull RestrictedLockUtils.EnforcedAdmin enforcedAdmin) {
+ return null;
+ }
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/enterprise/ActionDisabledByAdminControllerFactory.java b/packages/SettingsLib/src/com/android/settingslib/enterprise/ActionDisabledByAdminControllerFactory.java
index da42e330b8b4..44cafb17e1d8 100644
--- a/packages/SettingsLib/src/com/android/settingslib/enterprise/ActionDisabledByAdminControllerFactory.java
+++ b/packages/SettingsLib/src/com/android/settingslib/enterprise/ActionDisabledByAdminControllerFactory.java
@@ -20,6 +20,11 @@ import static android.app.admin.DevicePolicyManager.DEVICE_OWNER_TYPE_FINANCED;
import android.app.admin.DevicePolicyManager;
import android.content.Context;
+import android.hardware.biometrics.BiometricAuthenticator;
+import android.hardware.biometrics.ParentalControlsUtilsInternal;
+import android.os.UserHandle;
+import android.os.UserManager;
+import android.text.TextUtils;
/**
* A factory that returns the relevant instance of {@link ActionDisabledByAdminController}.
@@ -30,10 +35,28 @@ public final class ActionDisabledByAdminControllerFactory {
* Returns the relevant instance of {@link ActionDisabledByAdminController}.
*/
public static ActionDisabledByAdminController createInstance(Context context,
- DeviceAdminStringProvider stringProvider) {
- return isFinancedDevice(context)
- ? new FinancedDeviceActionDisabledByAdminController(stringProvider)
- : new ManagedDeviceActionDisabledByAdminController(stringProvider);
+ String restriction, DeviceAdminStringProvider stringProvider) {
+ if (doesBiometricRequireParentalConsent(context, restriction)) {
+ return new BiometricActionDisabledByAdminController(stringProvider);
+ } else if (isFinancedDevice(context)) {
+ return new FinancedDeviceActionDisabledByAdminController(stringProvider);
+ } else {
+ return new ManagedDeviceActionDisabledByAdminController(stringProvider);
+ }
+ }
+
+ /**
+ * @return true if the restriction == UserManager.DISALLOW_BIOMETRIC and parental consent
+ * is required.
+ */
+ private static boolean doesBiometricRequireParentalConsent(Context context,
+ String restriction) {
+ if (!TextUtils.equals(UserManager.DISALLOW_BIOMETRIC, restriction)) {
+ return false;
+ }
+ DevicePolicyManager dpm = context.getSystemService(DevicePolicyManager.class);
+ return ParentalControlsUtilsInternal.parentConsentRequired(context, dpm,
+ BiometricAuthenticator.TYPE_ANY_BIOMETRIC, new UserHandle(UserHandle.myUserId()));
}
private static boolean isFinancedDevice(Context context) {
diff --git a/packages/SettingsLib/src/com/android/settingslib/enterprise/BiometricActionDisabledByAdminController.java b/packages/SettingsLib/src/com/android/settingslib/enterprise/BiometricActionDisabledByAdminController.java
new file mode 100644
index 000000000000..814d5d23f458
--- /dev/null
+++ b/packages/SettingsLib/src/com/android/settingslib/enterprise/BiometricActionDisabledByAdminController.java
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.settingslib.enterprise;
+
+import android.annotation.NonNull;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.content.Intent;
+import android.util.Log;
+
+import androidx.annotation.Nullable;
+
+import com.android.settingslib.RestrictedLockUtils;
+
+public class BiometricActionDisabledByAdminController extends BaseActionDisabledByAdminController {
+
+ private static final String TAG = "BiometricActionDisabledByAdminController";
+
+ // These MUST not change, as they are the stable API between here and device admin specified
+ // by the component below.
+ private static final String ACTION_LEARN_MORE = "android.settings.LEARN_MORE";
+ private static final String EXTRA_FROM_BIOMETRIC_SETUP = "from_biometric_setup";
+
+ BiometricActionDisabledByAdminController(
+ DeviceAdminStringProvider stringProvider) {
+ super(stringProvider);
+ }
+
+ @Override
+ public void setupLearnMoreButton(Context context) {
+
+ }
+
+ @Override
+ public String getAdminSupportTitle(@Nullable String restriction) {
+ return mStringProvider.getDisabledBiometricsParentConsentTitle();
+ }
+
+ @Override
+ public CharSequence getAdminSupportContentString(Context context,
+ @Nullable CharSequence supportMessage) {
+ return mStringProvider.getDisabledBiometricsParentConsentContent();
+ }
+
+ @Override
+ public DialogInterface.OnClickListener getPositiveButtonListener(@NonNull Context context,
+ @NonNull RestrictedLockUtils.EnforcedAdmin enforcedAdmin) {
+ return (dialog, which) -> {
+ Log.d(TAG, "Positive button clicked, component: " + enforcedAdmin.component);
+ final Intent intent = new Intent(ACTION_LEARN_MORE)
+ .setComponent(enforcedAdmin.component)
+ .putExtra(EXTRA_FROM_BIOMETRIC_SETUP, true)
+ .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ context.startActivity(intent);
+ };
+ }
+}
diff --git a/packages/SettingsLib/src/com/android/settingslib/enterprise/DeviceAdminStringProvider.java b/packages/SettingsLib/src/com/android/settingslib/enterprise/DeviceAdminStringProvider.java
index c47d789a514d..b83837e6caf6 100644
--- a/packages/SettingsLib/src/com/android/settingslib/enterprise/DeviceAdminStringProvider.java
+++ b/packages/SettingsLib/src/com/android/settingslib/enterprise/DeviceAdminStringProvider.java
@@ -72,4 +72,14 @@ public interface DeviceAdminStringProvider {
* a financed device.
*/
String getDisabledByPolicyTitleForFinancedDevice();
+
+ /**
+ * Returns the dialog title for when biometrics require parental consent.
+ */
+ String getDisabledBiometricsParentConsentTitle();
+
+ /**
+ * Returns the dialog contents for when biometrics require parental consent.
+ */
+ String getDisabledBiometricsParentConsentContent();
}
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/enterprise/BiometricActionDisabledByAdminControllerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/enterprise/BiometricActionDisabledByAdminControllerTest.java
new file mode 100644
index 000000000000..766c2f5f9872
--- /dev/null
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/enterprise/BiometricActionDisabledByAdminControllerTest.java
@@ -0,0 +1,79 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.settingslib.enterprise;
+
+import static com.android.settingslib.enterprise.ActionDisabledByAdminControllerTestUtils.ENFORCED_ADMIN;
+import static com.android.settingslib.enterprise.ActionDisabledByAdminControllerTestUtils.ENFORCEMENT_ADMIN_USER_ID;
+import static com.android.settingslib.enterprise.FakeDeviceAdminStringProvider.DEFAULT_DEVICE_ADMIN_STRING_PROVIDER;
+
+import static junit.framework.Assert.assertNotNull;
+import static junit.framework.TestCase.assertEquals;
+import static junit.framework.TestCase.assertTrue;
+
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.content.Intent;
+import android.os.UserHandle;
+
+import com.android.settingslib.RestrictedLockUtils;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.robolectric.RobolectricTestRunner;
+
+@RunWith(RobolectricTestRunner.class)
+public class BiometricActionDisabledByAdminControllerTest {
+
+ private final ActionDisabledByAdminControllerTestUtils mTestUtils =
+ new ActionDisabledByAdminControllerTestUtils();
+ private final BiometricActionDisabledByAdminController mController =
+ new BiometricActionDisabledByAdminController(DEFAULT_DEVICE_ADMIN_STRING_PROVIDER);
+
+ @Before
+ public void setUp() {
+ mController.initialize(mTestUtils.createLearnMoreButtonLauncher());
+ mController.updateEnforcedAdmin(ENFORCED_ADMIN, ENFORCEMENT_ADMIN_USER_ID);
+ }
+
+ @Test
+ public void buttonClicked() {
+ Context context = mock(Context.class);
+ ComponentName componentName = mock(ComponentName.class);
+ RestrictedLockUtils.EnforcedAdmin enforcedAdmin = new RestrictedLockUtils.EnforcedAdmin(
+ componentName, new UserHandle(UserHandle.myUserId()));
+
+ DialogInterface.OnClickListener listener =
+ mController.getPositiveButtonListener(context, enforcedAdmin);
+ assertNotNull("Biometric Controller must supply a non-null listener", listener);
+ listener.onClick(mock(DialogInterface.class), 0 /* which */);
+
+ ArgumentCaptor<Intent> intentCaptor = ArgumentCaptor.forClass(Intent.class);
+ verify(context).startActivity(intentCaptor.capture());
+ assertEquals("android.settings.LEARN_MORE",
+ intentCaptor.getValue().getAction());
+ assertTrue("from_biometric_setup", intentCaptor.getValue()
+ .getBooleanExtra("from_biometric_setup", false));
+ assertEquals(componentName, intentCaptor.getValue().getComponent());
+ }
+
+}
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/enterprise/FakeDeviceAdminStringProvider.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/enterprise/FakeDeviceAdminStringProvider.java
index be3e9fcf45ea..99e13c325472 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/enterprise/FakeDeviceAdminStringProvider.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/enterprise/FakeDeviceAdminStringProvider.java
@@ -30,6 +30,8 @@ class FakeDeviceAdminStringProvider implements DeviceAdminStringProvider {
static final String DEFAULT_DISABLED_BY_POLICY_CONTENT = "default_disabled_by_policy_content";
static final String DEFAULT_DISABLED_BY_POLICY_TITLE_FINANCED_DEVICE =
"default_disabled_by_policy_title_financed_device";
+ static final String DEFAULT_BIOMETRIC_TITLE = "biometric_title";
+ static final String DEFAULT_BIOMETRIC_CONTENTS = "biometric_contents";
static final DeviceAdminStringProvider DEFAULT_DEVICE_ADMIN_STRING_PROVIDER =
new FakeDeviceAdminStringProvider(/* url = */ null);
@@ -88,4 +90,15 @@ class FakeDeviceAdminStringProvider implements DeviceAdminStringProvider {
public String getDisabledByPolicyTitleForFinancedDevice() {
return DEFAULT_DISABLED_BY_POLICY_TITLE_FINANCED_DEVICE;
}
+
+ @Override
+ public String getDisabledBiometricsParentConsentTitle() {
+ return DEFAULT_BIOMETRIC_TITLE;
+ }
+
+ @Override
+ public String getDisabledBiometricsParentConsentContent() {
+ return DEFAULT_BIOMETRIC_CONTENTS;
+ }
+
}
diff --git a/packages/SystemUI/animation/src/com/android/systemui/animation/ActivityLaunchAnimator.kt b/packages/SystemUI/animation/src/com/android/systemui/animation/ActivityLaunchAnimator.kt
index 8bc3d228f7a8..2579e7084e08 100644
--- a/packages/SystemUI/animation/src/com/android/systemui/animation/ActivityLaunchAnimator.kt
+++ b/packages/SystemUI/animation/src/com/android/systemui/animation/ActivityLaunchAnimator.kt
@@ -238,7 +238,9 @@ class ActivityLaunchAnimator(
* during the animation.
*/
@JvmStatic
- fun fromView(view: View): Controller = GhostedViewLaunchAnimatorController(view)
+ fun fromView(view: View, cujType: Int? = null): Controller {
+ return GhostedViewLaunchAnimatorController(view, cujType)
+ }
}
/**
diff --git a/packages/SystemUI/animation/src/com/android/systemui/animation/GhostedViewLaunchAnimatorController.kt b/packages/SystemUI/animation/src/com/android/systemui/animation/GhostedViewLaunchAnimatorController.kt
index 4b655a1a1b02..ffb7ab4eff7c 100644
--- a/packages/SystemUI/animation/src/com/android/systemui/animation/GhostedViewLaunchAnimatorController.kt
+++ b/packages/SystemUI/animation/src/com/android/systemui/animation/GhostedViewLaunchAnimatorController.kt
@@ -14,6 +14,7 @@ import android.view.View
import android.view.ViewGroup
import android.view.ViewGroupOverlay
import android.widget.FrameLayout
+import com.android.internal.jank.InteractionJankMonitor
import kotlin.math.min
/**
@@ -29,7 +30,10 @@ import kotlin.math.min
*/
open class GhostedViewLaunchAnimatorController(
/** The view that will be ghosted and from which the background will be extracted. */
- private val ghostedView: View
+ private val ghostedView: View,
+
+ /** The [InteractionJankMonitor.CujType] associated to this animation. */
+ private val cujType: Int? = null
) : ActivityLaunchAnimator.Controller {
/** The container to which we will add the ghost view and expanding background. */
override var launchContainer = ghostedView.rootView as ViewGroup
@@ -125,6 +129,8 @@ open class GhostedViewLaunchAnimatorController(
val matrix = ghostView?.animationMatrix ?: Matrix.IDENTITY_MATRIX
matrix.getValues(initialGhostViewMatrixValues)
+
+ cujType?.let { InteractionJankMonitor.getInstance().begin(ghostedView, it) }
}
override fun onLaunchAnimationProgress(
@@ -167,6 +173,8 @@ open class GhostedViewLaunchAnimatorController(
}
override fun onLaunchAnimationEnd(isExpandingFullyAbove: Boolean) {
+ cujType?.let { InteractionJankMonitor.getInstance().end(it) }
+
backgroundDrawable?.wrapped?.alpha = startBackgroundAlpha
GhostView.removeGhost(ghostedView)
diff --git a/packages/SystemUI/plugin/src/com/android/systemui/plugins/ActivityStarter.java b/packages/SystemUI/plugin/src/com/android/systemui/plugins/ActivityStarter.java
index 298b7c38066b..7c81325d685f 100644
--- a/packages/SystemUI/plugin/src/com/android/systemui/plugins/ActivityStarter.java
+++ b/packages/SystemUI/plugin/src/com/android/systemui/plugins/ActivityStarter.java
@@ -92,5 +92,12 @@ public interface ActivityStarter {
* *after* returning to start hiding the keyguard.
*/
boolean onDismiss();
+
+ /**
+ * Whether running this action when we are locked will start an animation on the keyguard.
+ */
+ default boolean willRunAnimationOnKeyguard() {
+ return false;
+ }
}
}
diff --git a/packages/SystemUI/res/layout/global_actions_change_panel.xml b/packages/SystemUI/res/layout/global_actions_change_panel.xml
index dffb0f011bb5..bc9c203b299a 100644
--- a/packages/SystemUI/res/layout/global_actions_change_panel.xml
+++ b/packages/SystemUI/res/layout/global_actions_change_panel.xml
@@ -14,8 +14,18 @@
~ See the License for the specific language governing permissions and
~ limitations under the License.
-->
-<ImageView
+<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@+id/global_actions_change_button"
android:layout_width="wrap_content"
- android:layout_height="wrap_content"/>
+ android:layout_height="wrap_content">
+ <TextView
+ android:id="@+id/global_actions_change_message"
+ android:layout_width="wrap_content"
+ android:visibility="gone"
+ android:layout_height="wrap_content"
+ android:text="@string/global_actions_change_description" />
+ <ImageView
+ android:id="@+id/global_actions_change_button"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"/>
+</LinearLayout> \ No newline at end of file
diff --git a/packages/SystemUI/res/layout/long_screenshot.xml b/packages/SystemUI/res/layout/long_screenshot.xml
index 4765b44d78c8..3f4baaf27b84 100644
--- a/packages/SystemUI/res/layout/long_screenshot.xml
+++ b/packages/SystemUI/res/layout/long_screenshot.xml
@@ -99,6 +99,7 @@
app:handleThickness="@dimen/screenshot_crop_handle_thickness"
app:handleColor="?androidprv:attr/colorAccentPrimary"
app:scrimColor="@color/screenshot_crop_scrim"
+ app:containerBackgroundColor="?android:colorBackgroundFloating"
tools:background="?android:colorBackground"
tools:minHeight="100dp"
tools:minWidth="100dp" />
diff --git a/packages/SystemUI/res/layout/quick_qs_status_icons.xml b/packages/SystemUI/res/layout/quick_qs_status_icons.xml
index c88703dabeea..5b9ca1b26158 100644
--- a/packages/SystemUI/res/layout/quick_qs_status_icons.xml
+++ b/packages/SystemUI/res/layout/quick_qs_status_icons.xml
@@ -59,11 +59,16 @@
android:visibility="gone"
/>
- <LinearLayout
+ <FrameLayout
android:id="@+id/rightLayout"
android:layout_width="wrap_content"
android:layout_height="match_parent"
- android:gravity="center_vertical|end"
+ android:gravity="end"
+ >
+ <LinearLayout
+ android:layout_width="wrap_content"
+ android:layout_height="match_parent"
+ android:layout_gravity="center_vertical|end"
>
<com.android.systemui.statusbar.phone.StatusIconContainer
android:id="@+id/statusIcons"
@@ -80,4 +85,6 @@
android:paddingEnd="2dp" />
</LinearLayout>
+ </FrameLayout>
+
</LinearLayout>
diff --git a/packages/SystemUI/res/values-television/config.xml b/packages/SystemUI/res/values-television/config.xml
index 70bd85036198..2f0957caaaae 100644
--- a/packages/SystemUI/res/values-television/config.xml
+++ b/packages/SystemUI/res/values-television/config.xml
@@ -33,6 +33,7 @@
<item>com.android.systemui.statusbar.tv.notifications.TvNotificationPanel</item>
<item>com.android.systemui.statusbar.tv.notifications.TvNotificationHandler</item>
<item>com.android.systemui.statusbar.tv.VpnStatusObserver</item>
+ <item>com.android.systemui.globalactions.GlobalActionsComponent</item>
<item>com.android.systemui.usb.StorageNotification</item>
<item>com.android.systemui.power.PowerUI</item>
<item>com.android.systemui.media.RingtonePlayer</item>
diff --git a/packages/SystemUI/res/values/attrs.xml b/packages/SystemUI/res/values/attrs.xml
index 067d56f3d157..d2ed6017b205 100644
--- a/packages/SystemUI/res/values/attrs.xml
+++ b/packages/SystemUI/res/values/attrs.xml
@@ -143,6 +143,7 @@
<attr name="handleThickness" format="dimension" />
<attr name="handleColor" format="color" />
<attr name="scrimColor" format="color" />
+ <attr name="containerBackgroundColor" format="color" />
<attr name="isVertical" format="boolean" />
@@ -178,6 +179,7 @@
<attr name="handleThickness" />
<attr name="handleColor" />
<attr name="scrimColor" />
+ <attr name="containerBackgroundColor" />
</declare-styleable>
<declare-styleable name="MagnifierView">
diff --git a/packages/SystemUI/res/values/config.xml b/packages/SystemUI/res/values/config.xml
index b4deaa0af543..82ce881b2fbc 100644
--- a/packages/SystemUI/res/values/config.xml
+++ b/packages/SystemUI/res/values/config.xml
@@ -396,10 +396,6 @@
<!-- Whether or not the notifications should always fade as they are dismissed. -->
<bool name="config_fadeNotificationsOnDismiss">false</bool>
- <!-- Whether or not the parent of the notification row itself is being translated when swiped or
- its children views. If true, then the contents are translated and vice versa. -->
- <bool name="config_translateNotificationContentsOnSwipe">true</bool>
-
<!-- Whether or not the fade on the notification is based on the amount that it has been swiped
off-screen. -->
<bool name="config_fadeDependingOnAmountSwiped">false</bool>
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index 0ccde60f9bfd..9b860c75f476 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -347,7 +347,7 @@
<!-- Padding to make tappable chip height 48dp (18+11+11+4+4) -->
<dimen name="screenshot_action_chip_margin_vertical">4dp</dimen>
<dimen name="screenshot_action_chip_padding_vertical">11dp</dimen>
- <dimen name="screenshot_action_chip_icon_size">18dp</dimen>
+ <dimen name="screenshot_action_chip_icon_size">18sp</dimen>
<!-- Padding on each side of the icon for icon-only chips -->
<dimen name="screenshot_action_chip_icon_only_padding_horizontal">14dp</dimen>
<!-- Padding at the edges of the chip for icon-and-text chips -->
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index 91301dfda5e8..93ce8f3c2a96 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -2979,6 +2979,8 @@
<!-- Content description for a chip in the status bar showing that the user is currently on a phone call. [CHAR LIMIT=NONE] -->
<string name="ongoing_phone_call_content_description">Ongoing phone call</string>
+ <!-- Placeholder for string describing changes in global actions -->
+ <string name="global_actions_change_description" translatable="false"><xliff:g>%1$s</xliff:g></string>
<!-- URL for more information about changes in global actions -->
<string name="global_actions_change_url" translatable="false"></string>
</resources>
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
index f3a6d6377c6d..38f8f7ac321f 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
@@ -249,6 +249,16 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
public void onStateChanged(int newState) {
mStatusBarState = newState;
}
+
+ @Override
+ public void onExpandedChanged(boolean isExpanded) {
+ for (int i = 0; i < mCallbacks.size(); i++) {
+ KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get();
+ if (cb != null) {
+ cb.onShadeExpandedChanged(isExpanded);
+ }
+ }
+ }
};
HashMap<Integer, SimData> mSimDatas = new HashMap<>();
@@ -775,6 +785,9 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
if (msgId == FingerprintManager.FINGERPRINT_ERROR_LOCKOUT
|| msgId == FingerprintManager.FINGERPRINT_ERROR_LOCKOUT_PERMANENT) {
mFingerprintLockedOut = true;
+ if (isUdfpsEnrolled()) {
+ updateFingerprintListeningState();
+ }
}
for (int i = 0; i < mCallbacks.size(); i++) {
@@ -2115,7 +2128,8 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
|| (!getUserCanSkipBouncer(getCurrentUser())
&& !isEncryptedOrLockdown(getCurrentUser())
&& !userNeedsStrongAuth()
- && userDoesNotHaveTrust);
+ && userDoesNotHaveTrust
+ && !mFingerprintLockedOut);
return shouldListenKeyguardState && shouldListenUserState && shouldListenBouncerState
&& shouldListenUdfpsState;
}
@@ -3244,6 +3258,7 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
pw.println(" strongAuthFlags=" + Integer.toHexString(strongAuthFlags));
pw.println(" trustManaged=" + getUserTrustIsManaged(userId));
pw.println(" udfpsEnrolled=" + isUdfpsEnrolled());
+ pw.println(" mFingerprintLockedOut=" + mFingerprintLockedOut);
pw.println(" enabledByUser=" + mBiometricEnabledForUser.get(userId));
if (isUdfpsEnrolled()) {
pw.println(" shouldListenForUdfps=" + shouldListenForFingerprint(true));
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitorCallback.java b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitorCallback.java
index e561a5a84f24..9849a7efe837 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitorCallback.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitorCallback.java
@@ -333,4 +333,8 @@ public class KeyguardUpdateMonitorCallback {
*/
public void onRequireUnlockForNfc() { }
+ /**
+ * Called when the notification shade is expanded or collapsed.
+ */
+ public void onShadeExpandedChanged(boolean expanded) { }
}
diff --git a/packages/SystemUI/src/com/android/systemui/ImageWallpaper.java b/packages/SystemUI/src/com/android/systemui/ImageWallpaper.java
index 64a683e78953..a68f79604b25 100644
--- a/packages/SystemUI/src/com/android/systemui/ImageWallpaper.java
+++ b/packages/SystemUI/src/com/android/systemui/ImageWallpaper.java
@@ -130,10 +130,7 @@ public class ImageWallpaper extends WallpaperService {
.getBounds();
mHeight = window.height();
mWidth = window.width();
- mMiniBitmap = null;
- if (mWorker != null && mWorker.getThreadHandler() != null) {
- mWorker.getThreadHandler().post(this::updateMiniBitmap);
- }
+ mRenderer.setOnBitmapChanged(this::updateMiniBitmap);
}
EglHelper getEglHelperInstance() {
@@ -177,20 +174,19 @@ public class ImageWallpaper extends WallpaperService {
mPageOffset = (1 - imgWidth) / (float) (mPages - 1);
}
- private void updateMiniBitmap() {
- mRenderer.useBitmap(b -> {
- int size = Math.min(b.getWidth(), b.getHeight());
- float scale = 1.0f;
- if (size > MIN_SURFACE_WIDTH) {
- scale = (float) MIN_SURFACE_WIDTH / (float) size;
- }
- mImgHeight = b.getHeight();
- mImgWidth = b.getWidth();
- mMiniBitmap = Bitmap.createScaledBitmap(b, (int) Math.max(scale * b.getWidth(), 1),
- (int) Math.max(scale * b.getHeight(), 1), false);
- computeAndNotifyLocalColors(mLocalColorsToAdd, mMiniBitmap);
- mLocalColorsToAdd.clear();
- });
+ private void updateMiniBitmap(Bitmap b) {
+ if (b == null) return;
+ int size = Math.min(b.getWidth(), b.getHeight());
+ float scale = 1.0f;
+ if (size > MIN_SURFACE_WIDTH) {
+ scale = (float) MIN_SURFACE_WIDTH / (float) size;
+ }
+ mImgHeight = b.getHeight();
+ mImgWidth = b.getWidth();
+ mMiniBitmap = Bitmap.createScaledBitmap(b, (int) Math.max(scale * b.getWidth(), 1),
+ (int) Math.max(scale * b.getHeight(), 1), false);
+ computeAndNotifyLocalColors(mLocalColorsToAdd, mMiniBitmap);
+ mLocalColorsToAdd.clear();
}
private void updateSurfaceSize() {
diff --git a/packages/SystemUI/src/com/android/systemui/SwipeHelper.java b/packages/SystemUI/src/com/android/systemui/SwipeHelper.java
index b0f4da251208..affad7a57d86 100644
--- a/packages/SystemUI/src/com/android/systemui/SwipeHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/SwipeHelper.java
@@ -391,9 +391,9 @@ public class SwipeHelper implements Gefingerpoken {
boolean animateLeft = (Math.abs(velocity) > getEscapeVelocity() && velocity < 0) ||
(getTranslation(animView) < 0 && !isDismissAll);
if (animateLeft || animateLeftForRtl || animateUpForMenu) {
- newPos = -getSize(animView);
+ newPos = -getTotalTranslationLength(animView);
} else {
- newPos = getSize(animView);
+ newPos = getTotalTranslationLength(animView);
}
long duration;
if (fixedDuration == 0) {
@@ -470,6 +470,15 @@ public class SwipeHelper implements Gefingerpoken {
}
/**
+ * Get the total translation length where we want to swipe to when dismissing the view. By
+ * default this is the size of the view, but can also be larger.
+ * @param animView the view to ask about
+ */
+ protected float getTotalTranslationLength(View animView) {
+ return getSize(animView);
+ }
+
+ /**
* Called to update the dismiss animation.
*/
protected void prepareDismissAnimation(View view, Animator anim) {
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/AccessibilityFloatingMenu.java b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/AccessibilityFloatingMenu.java
index 47f373920b90..05256e646948 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/AccessibilityFloatingMenu.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/AccessibilityFloatingMenu.java
@@ -53,7 +53,7 @@ public class AccessibilityFloatingMenu implements IAccessibilityFloatingMenu {
@FloatRange(from = 0.0, to = 1.0)
private static final float DEFAULT_POSITION_X_PERCENT = 1.0f;
@FloatRange(from = 0.0, to = 1.0)
- private static final float DEFAULT_POSITION_Y_PERCENT = 0.8f;
+ private static final float DEFAULT_POSITION_Y_PERCENT = 0.9f;
private final Context mContext;
private final AccessibilityFloatingMenuView mMenuView;
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/AccessibilityTargetAdapter.java b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/AccessibilityTargetAdapter.java
index 63cfd5123c96..ee09c620ec1d 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/AccessibilityTargetAdapter.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/AccessibilityTargetAdapter.java
@@ -143,7 +143,7 @@ public class AccessibilityTargetAdapter extends Adapter<ViewHolder> {
}
void updateItemPadding(int padding, int size) {
- itemView.setPaddingRelative(padding, padding, padding, padding);
+ itemView.setPaddingRelative(padding, padding, padding, 0);
}
}
@@ -154,7 +154,7 @@ public class AccessibilityTargetAdapter extends Adapter<ViewHolder> {
@Override
void updateItemPadding(int padding, int size) {
- final int paddingBottom = size <= 2 ? padding : 0;
+ final int paddingBottom = size <= 1 ? padding : 0;
itemView.setPaddingRelative(padding, padding, padding, paddingBottom);
}
}
@@ -166,7 +166,7 @@ public class AccessibilityTargetAdapter extends Adapter<ViewHolder> {
@Override
void updateItemPadding(int padding, int size) {
- itemView.setPaddingRelative(padding, 0, padding, padding);
+ itemView.setPaddingRelative(padding, padding, padding, padding);
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleController.kt b/packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleController.kt
index cf577a37d625..77cca2e3089c 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleController.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleController.kt
@@ -24,9 +24,13 @@ import androidx.annotation.VisibleForTesting
import com.android.keyguard.KeyguardUpdateMonitor
import com.android.keyguard.KeyguardUpdateMonitorCallback
import com.android.settingslib.Utils
+import com.android.systemui.statusbar.CircleReveal
+import com.android.systemui.statusbar.LiftReveal
+import com.android.systemui.statusbar.LightRevealEffect
import com.android.systemui.statusbar.NotificationShadeWindowController
import com.android.systemui.statusbar.commandline.Command
import com.android.systemui.statusbar.commandline.CommandRegistry
+import com.android.systemui.statusbar.phone.BiometricUnlockController
import com.android.systemui.statusbar.phone.KeyguardBypassController
import com.android.systemui.statusbar.phone.StatusBar
import com.android.systemui.statusbar.phone.dagger.StatusBarComponent.StatusBarScope
@@ -49,10 +53,12 @@ class AuthRippleController @Inject constructor(
private val commandRegistry: CommandRegistry,
private val notificationShadeWindowController: NotificationShadeWindowController,
private val bypassController: KeyguardBypassController,
+ private val biometricUnlockController: BiometricUnlockController,
rippleView: AuthRippleView?
) : ViewController<AuthRippleView>(rippleView) {
var fingerprintSensorLocation: PointF? = null
private var faceSensorLocation: PointF? = null
+ private var circleReveal: LightRevealEffect? = null
@VisibleForTesting
public override fun onViewAttached() {
@@ -96,15 +102,47 @@ class AuthRippleController @Inject constructor(
private fun showRipple() {
notificationShadeWindowController.setForcePluginOpen(true, this)
- mView.startRipple(Runnable {
- notificationShadeWindowController.setForcePluginOpen(false, this)
- })
+ val biometricUnlockMode = biometricUnlockController.mode
+ val useCircleReveal = circleReveal != null &&
+ (biometricUnlockMode == BiometricUnlockController.MODE_WAKE_AND_UNLOCK ||
+ biometricUnlockMode == BiometricUnlockController.MODE_WAKE_AND_UNLOCK_PULSING ||
+ biometricUnlockMode == BiometricUnlockController.MODE_WAKE_AND_UNLOCK_FROM_DREAM)
+ val lightRevealScrim = statusBar.lightRevealScrim
+ if (useCircleReveal) {
+ lightRevealScrim?.revealEffect = circleReveal!!
+ }
+
+ mView.startRipple(
+ /* end runnable */
+ Runnable {
+ notificationShadeWindowController.setForcePluginOpen(false, this)
+ if (useCircleReveal) {
+ lightRevealScrim?.revealEffect = LiftReveal
+ }
+ },
+ /* circleReveal */
+ if (useCircleReveal) {
+ lightRevealScrim
+ } else {
+ null
+ }
+ )
}
fun updateSensorLocation() {
fingerprintSensorLocation = authController.fingerprintSensorLocation
faceSensorLocation = authController.faceAuthSensorLocation
- statusBar.updateCircleReveal()
+ fingerprintSensorLocation?.let {
+ circleReveal = CircleReveal(
+ it.x,
+ it.y,
+ 0f,
+ Math.max(
+ Math.max(it.x, statusBar.displayWidth - it.x),
+ Math.max(it.y, statusBar.displayHeight - it.y)
+ )
+ )
+ }
}
private fun updateRippleColor() {
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleView.kt b/packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleView.kt
index 75373abc5124..dd73c4f8d071 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleView.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleView.kt
@@ -31,6 +31,7 @@ import android.util.MathUtils
import android.view.View
import android.view.animation.PathInterpolator
import com.android.internal.graphics.ColorUtils
+import com.android.systemui.statusbar.LightRevealScrim
import com.android.systemui.statusbar.charging.RippleShader
private const val RIPPLE_ANIMATION_DURATION: Long = 1533
@@ -70,51 +71,79 @@ class AuthRippleView(context: Context?, attrs: AttributeSet?) : View(context, at
.toFloat()
}
- fun startRipple(onAnimationEnd: Runnable?) {
+ fun startRipple(onAnimationEnd: Runnable?, lightReveal: LightRevealScrim?) {
if (rippleInProgress) {
return // Ignore if ripple effect is already playing
}
- val animator = ValueAnimator.ofFloat(0f, 1f)
- animator.interpolator = PathInterpolator(0.4f, 0f, 0f, 1f)
- animator.duration = RIPPLE_ANIMATION_DURATION
- animator.addUpdateListener { animator ->
- val now = animator.currentPlayTime
- rippleShader.progress = animator.animatedValue as Float
- rippleShader.time = now.toFloat()
- rippleShader.distortionStrength = 1 - rippleShader.progress
- invalidate()
+ val rippleAnimator = ValueAnimator.ofFloat(0f, 1f).apply {
+ interpolator = PathInterpolator(0.4f, 0f, 0f, 1f)
+ duration = RIPPLE_ANIMATION_DURATION
+ addUpdateListener { animator ->
+ val now = animator.currentPlayTime
+ rippleShader.progress = animator.animatedValue as Float
+ rippleShader.time = now.toFloat()
+
+ lightReveal?.revealAmount = animator.animatedValue as Float
+ invalidate()
+ }
}
- val alphaInAnimator = ValueAnimator.ofInt(0, 127)
- alphaInAnimator.duration = 167
- alphaInAnimator.addUpdateListener { alphaInAnimator ->
- rippleShader.color = ColorUtils.setAlphaComponent(rippleShader.color,
- alphaInAnimator.animatedValue as Int)
- invalidate()
+
+ val revealAnimator = ValueAnimator.ofFloat(0f, 1f).apply {
+ interpolator = rippleAnimator.interpolator
+ startDelay = 10
+ duration = rippleAnimator.duration
+ addUpdateListener { animator ->
+ lightReveal?.revealAmount = animator.animatedValue as Float
+ }
}
- val alphaOutAnimator = ValueAnimator.ofInt(127, 0)
- alphaOutAnimator.startDelay = 417
- alphaOutAnimator.duration = 1116
- alphaOutAnimator.addUpdateListener { alphaOutAnimator ->
- rippleShader.color = ColorUtils.setAlphaComponent(rippleShader.color,
- alphaOutAnimator.animatedValue as Int)
- invalidate()
+
+ val alphaInAnimator = ValueAnimator.ofInt(0, 127).apply {
+ duration = 167
+ addUpdateListener { animator ->
+ rippleShader.color = ColorUtils.setAlphaComponent(
+ rippleShader.color,
+ animator.animatedValue as Int
+ )
+ invalidate()
+ }
}
- val animatorSet = AnimatorSet()
- animatorSet.playTogether(animator, alphaInAnimator, alphaOutAnimator)
- animatorSet.addListener(object : AnimatorListenerAdapter() {
- override fun onAnimationEnd(animation: Animator?) {
- onAnimationEnd?.run()
- rippleInProgress = false
- visibility = GONE
+ val alphaOutAnimator = ValueAnimator.ofInt(127, 0).apply {
+ startDelay = 417
+ duration = 1116
+ addUpdateListener { animator ->
+ rippleShader.color = ColorUtils.setAlphaComponent(
+ rippleShader.color,
+ animator.animatedValue as Int
+ )
+ invalidate()
}
- })
+ }
+
+ val animatorSet = AnimatorSet().apply {
+ playTogether(
+ rippleAnimator,
+ revealAnimator,
+ alphaInAnimator,
+ alphaOutAnimator
+ )
+ addListener(object : AnimatorListenerAdapter() {
+ override fun onAnimationStart(animation: Animator?) {
+ rippleInProgress = true
+ visibility = VISIBLE
+ }
+
+ override fun onAnimationEnd(animation: Animator?) {
+ onAnimationEnd?.run()
+ rippleInProgress = false
+ visibility = GONE
+ }
+ })
+ }
// TODO (b/185124905): custom haptic TBD
// vibrate()
animatorSet.start()
- visibility = VISIBLE
- rippleInProgress = true
}
fun setColor(color: Int) {
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java b/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java
index ff95604088ed..7b34e52c16e8 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java
@@ -38,6 +38,7 @@ import com.android.systemui.biometrics.AuthController;
import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.dock.DockManager;
+import com.android.systemui.doze.DozeMachine.State;
import com.android.systemui.doze.dagger.DozeScope;
import com.android.systemui.statusbar.phone.DozeParameters;
import com.android.systemui.util.Assert;
@@ -96,6 +97,7 @@ public class DozeTriggers implements DozeMachine.Part {
private long mNotificationPulseTime;
private boolean mPulsePending;
+ private Runnable mAodInterruptRunnable;
/** see {@link #onProximityFar} prox for callback */
private boolean mWantProxSensor;
@@ -303,11 +305,16 @@ public class DozeTriggers implements DozeMachine.Part {
} else if (isPickup) {
gentleWakeUp(pulseReason);
} else if (isUdfpsLongPress) {
+ final State state = mMachine.getState();
+ if (state == State.DOZE_AOD || state == State.DOZE) {
+ // Since the gesture won't be received by the UDFPS view, we need to
+ // manually inject an event once the display is ON
+ mAodInterruptRunnable = () ->
+ mAuthController.onAodInterrupt((int) screenX, (int) screenY,
+ rawValues[3] /* major */, rawValues[4] /* minor */);
+ }
+
requestPulse(DozeLog.REASON_SENSOR_UDFPS_LONG_PRESS, true, null);
- // Since the gesture won't be received by the UDFPS view, manually inject an
- // event.
- mAuthController.onAodInterrupt((int) screenX, (int) screenY,
- rawValues[3] /* major */, rawValues[4] /* minor */);
} else {
mDozeHost.extendPulse(pulseReason);
}
@@ -439,6 +446,7 @@ public class DozeTriggers implements DozeMachine.Part {
public void transitionTo(DozeMachine.State oldState, DozeMachine.State newState) {
switch (newState) {
case INITIALIZED:
+ mAodInterruptRunnable = null;
sWakeDisplaySensorState = true;
mBroadcastReceiver.register(mBroadcastDispatcher);
mDozeHost.addCallback(mHostCallback);
@@ -448,6 +456,7 @@ public class DozeTriggers implements DozeMachine.Part {
break;
case DOZE:
case DOZE_AOD:
+ mAodInterruptRunnable = null;
mWantProxSensor = newState != DozeMachine.State.DOZE;
mWantSensors = true;
mWantTouchScreenSensors = true;
@@ -494,6 +503,11 @@ public class DozeTriggers implements DozeMachine.Part {
|| state == Display.STATE_DOZE_SUSPEND || state == Display.STATE_OFF;
mDozeSensors.setProxListening(mWantProxSensor && lowPowerStateOrOff);
mDozeSensors.setListening(mWantSensors, mWantTouchScreenSensors, lowPowerStateOrOff);
+
+ if (mAodInterruptRunnable != null && state == Display.STATE_ON) {
+ mAodInterruptRunnable.run();
+ mAodInterruptRunnable = null;
+ }
}
/**
@@ -576,6 +590,8 @@ public class DozeTriggers implements DozeMachine.Part {
@Override
public void dump(PrintWriter pw) {
+ pw.println(" mAodInterruptRunnable=" + mAodInterruptRunnable);
+
pw.print(" notificationPulseTime=");
pw.println(Formatter.formatShortElapsedTime(mContext, mNotificationPulseTime));
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeUi.java b/packages/SystemUI/src/com/android/systemui/doze/DozeUi.java
index ff3cb2102d60..fbe06b02d955 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeUi.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeUi.java
@@ -21,6 +21,7 @@ import static com.android.systemui.doze.DozeMachine.State.DOZE_AOD_PAUSED;
import android.app.AlarmManager;
import android.content.Context;
+import android.content.res.Configuration;
import android.os.Handler;
import android.os.SystemClock;
import android.provider.Settings;
@@ -34,6 +35,7 @@ import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.doze.dagger.DozeScope;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.statusbar.phone.DozeParameters;
+import com.android.systemui.statusbar.policy.ConfigurationController;
import com.android.systemui.tuner.TunerService;
import com.android.systemui.util.AlarmTimeout;
import com.android.systemui.util.wakelock.WakeLock;
@@ -48,7 +50,8 @@ import dagger.Lazy;
* The policy controlling doze.
*/
@DozeScope
-public class DozeUi implements DozeMachine.Part, TunerService.Tunable {
+public class DozeUi implements DozeMachine.Part, TunerService.Tunable,
+ ConfigurationController.ConfigurationListener {
// if enabled, calls dozeTimeTick() whenever the time changes:
private static final boolean BURN_IN_TESTING_ENABLED = false;
private static final long TIME_TICK_DEADLINE_MILLIS = 90 * 1000; // 1.5min
@@ -63,6 +66,7 @@ public class DozeUi implements DozeMachine.Part, TunerService.Tunable {
private final DozeLog mDozeLog;
private final Lazy<StatusBarStateController> mStatusBarStateController;
private final TunerService mTunerService;
+ private final ConfigurationController mConfigurationController;
private boolean mKeyguardShowing;
private final KeyguardUpdateMonitorCallback mKeyguardVisibilityCallback =
@@ -84,6 +88,11 @@ public class DozeUi implements DozeMachine.Part, TunerService.Tunable {
mHandler.post(mWakeLock.wrap(() -> {}));
}
}
+
+ @Override
+ public void onShadeExpandedChanged(boolean expanded) {
+ updateAnimateScreenOff();
+ }
};
private long mLastTimeTickElapsed = 0;
@@ -93,7 +102,8 @@ public class DozeUi implements DozeMachine.Part, TunerService.Tunable {
WakeLock wakeLock, DozeHost host, @Main Handler handler,
DozeParameters params, KeyguardUpdateMonitor keyguardUpdateMonitor,
DozeLog dozeLog, TunerService tunerService,
- Lazy<StatusBarStateController> statusBarStateController) {
+ Lazy<StatusBarStateController> statusBarStateController,
+ ConfigurationController configurationController) {
mContext = context;
mWakeLock = wakeLock;
mHost = host;
@@ -107,11 +117,15 @@ public class DozeUi implements DozeMachine.Part, TunerService.Tunable {
mStatusBarStateController = statusBarStateController;
mTunerService.addTunable(this, Settings.Secure.DOZE_ALWAYS_ON);
+
+ mConfigurationController = configurationController;
+ mConfigurationController.addCallback(this);
}
@Override
public void destroy() {
mTunerService.removeTunable(this);
+ mConfigurationController.removeCallback(this);
}
@Override
@@ -274,4 +288,9 @@ public class DozeUi implements DozeMachine.Part, TunerService.Tunable {
updateAnimateScreenOff();
}
}
+
+ @Override
+ public void onConfigChanged(Configuration newConfig) {
+ updateAnimateScreenOff();
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java
index dfd85fe4dc90..bb44b09f1bce 100644
--- a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java
+++ b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java
@@ -30,6 +30,7 @@ import android.app.admin.DevicePolicyManager;
import android.app.trust.TrustManager;
import android.content.Context;
import android.content.DialogInterface;
+import android.content.pm.PackageManager;
import android.content.res.Resources;
import android.database.ContentObserver;
import android.graphics.drawable.Drawable;
@@ -72,6 +73,7 @@ import com.android.systemui.plugins.GlobalActions.GlobalActionsManager;
import com.android.systemui.plugins.GlobalActionsPanelPlugin;
import com.android.systemui.statusbar.NotificationShadeDepthController;
import com.android.systemui.statusbar.NotificationShadeWindowController;
+import com.android.systemui.statusbar.phone.StatusBar;
import com.android.systemui.statusbar.policy.ConfigurationController;
import com.android.systemui.statusbar.policy.KeyguardStateController;
import com.android.systemui.telephony.TelephonyListenerManager;
@@ -112,47 +114,101 @@ public class GlobalActionsDialog extends GlobalActionsDialogLite
@VisibleForTesting
boolean mShowLockScreenCards = false;
+ private final KeyguardStateController.Callback mKeyguardStateControllerListener =
+ new KeyguardStateController.Callback() {
+ @Override
+ public void onUnlockedChanged() {
+ if (mDialog != null) {
+ ActionsDialog dialog = (ActionsDialog) mDialog;
+ boolean unlocked = mKeyguardStateController.isUnlocked();
+ if (dialog.mWalletViewController != null) {
+ dialog.mWalletViewController.onDeviceLockStateChanged(!unlocked);
+ }
+
+ if (unlocked) {
+ dialog.hideLockMessage();
+ }
+ }
+ }
+ };
+
+ private final ContentObserver mSettingsObserver = new ContentObserver(mMainHandler) {
+ @Override
+ public void onChange(boolean selfChange) {
+ onPowerMenuLockScreenSettingsChanged();
+ }
+ };
+
/**
* @param context everything needs a context :(
*/
@Inject
- public GlobalActionsDialog(Context context, GlobalActionsManager windowManagerFuncs,
- AudioManager audioManager, IDreamManager iDreamManager,
- DevicePolicyManager devicePolicyManager, LockPatternUtils lockPatternUtils,
+ public GlobalActionsDialog(
+ Context context,
+ GlobalActionsManager windowManagerFuncs,
+ AudioManager audioManager,
+ IDreamManager iDreamManager,
+ DevicePolicyManager devicePolicyManager,
+ LockPatternUtils lockPatternUtils,
BroadcastDispatcher broadcastDispatcher,
TelephonyListenerManager telephonyListenerManager,
- GlobalSettings globalSettings, SecureSettings secureSettings,
- @Nullable Vibrator vibrator, @Main Resources resources,
- ConfigurationController configurationController, ActivityStarter activityStarter,
- KeyguardStateController keyguardStateController, UserManager userManager,
- TrustManager trustManager, IActivityManager iActivityManager,
- @Nullable TelecomManager telecomManager, MetricsLogger metricsLogger,
- NotificationShadeDepthController depthController, SysuiColorExtractor colorExtractor,
+ GlobalSettings globalSettings,
+ SecureSettings secureSettings,
+ @Nullable Vibrator vibrator,
+ @Main Resources resources,
+ ConfigurationController configurationController,
+ ActivityStarter activityStarter,
+ KeyguardStateController keyguardStateController,
+ UserManager userManager,
+ TrustManager trustManager,
+ IActivityManager iActivityManager,
+ @Nullable TelecomManager telecomManager,
+ MetricsLogger metricsLogger,
+ NotificationShadeDepthController depthController,
+ SysuiColorExtractor colorExtractor,
IStatusBarService statusBarService,
NotificationShadeWindowController notificationShadeWindowController,
IWindowManager iWindowManager,
@Background Executor backgroundExecutor,
UiEventLogger uiEventLogger,
- RingerModeTracker ringerModeTracker, SysUiState sysUiState, @Main Handler handler) {
-
- super(context, windowManagerFuncs,
- audioManager, iDreamManager,
- devicePolicyManager, lockPatternUtils,
- broadcastDispatcher, telephonyListenerManager,
- globalSettings, secureSettings,
- vibrator, resources,
+ RingerModeTracker ringerModeTracker,
+ SysUiState sysUiState,
+ @Main Handler handler,
+ PackageManager packageManager,
+ StatusBar statusBar) {
+
+ super(context,
+ windowManagerFuncs,
+ audioManager,
+ iDreamManager,
+ devicePolicyManager,
+ lockPatternUtils,
+ broadcastDispatcher,
+ telephonyListenerManager,
+ globalSettings,
+ secureSettings,
+ vibrator,
+ resources,
configurationController,
- keyguardStateController, userManager,
- trustManager, iActivityManager,
- telecomManager, metricsLogger,
- depthController, colorExtractor,
+ keyguardStateController,
+ userManager,
+ trustManager,
+ iActivityManager,
+ telecomManager,
+ metricsLogger,
+ depthController,
+ colorExtractor,
statusBarService,
notificationShadeWindowController,
iWindowManager,
backgroundExecutor,
uiEventLogger,
null,
- ringerModeTracker, sysUiState, handler);
+ ringerModeTracker,
+ sysUiState,
+ handler,
+ packageManager,
+ statusBar);
mLockPatternUtils = lockPatternUtils;
mKeyguardStateController = keyguardStateController;
@@ -162,34 +218,22 @@ public class GlobalActionsDialog extends GlobalActionsDialogLite
mNotificationShadeWindowController = notificationShadeWindowController;
mSysUiState = sysUiState;
mActivityStarter = activityStarter;
- keyguardStateController.addCallback(new KeyguardStateController.Callback() {
- @Override
- public void onUnlockedChanged() {
- if (mDialog != null) {
- ActionsDialog dialog = (ActionsDialog) mDialog;
- boolean unlocked = mKeyguardStateController.isUnlocked();
- if (dialog.mWalletViewController != null) {
- dialog.mWalletViewController.onDeviceLockStateChanged(!unlocked);
- }
- if (unlocked) {
- dialog.hideLockMessage();
- }
- }
- }
- });
+ mKeyguardStateController.addCallback(mKeyguardStateControllerListener);
// Listen for changes to show pay on the power menu while locked
onPowerMenuLockScreenSettingsChanged();
mGlobalSettings.registerContentObserver(
Settings.Secure.getUriFor(Settings.Secure.POWER_MENU_LOCKED_SHOW_CONTENT),
false /* notifyForDescendants */,
- new ContentObserver(handler) {
- @Override
- public void onChange(boolean selfChange) {
- onPowerMenuLockScreenSettingsChanged();
- }
- });
+ mSettingsObserver);
+ }
+
+ @Override
+ public void destroy() {
+ super.destroy();
+ mKeyguardStateController.removeCallback(mKeyguardStateControllerListener);
+ mGlobalSettings.unregisterContentObserver(mSettingsObserver);
}
/**
@@ -227,7 +271,8 @@ public class GlobalActionsDialog extends GlobalActionsDialogLite
ActionsDialog dialog = new ActionsDialog(getContext(), mAdapter, mOverflowAdapter,
this::getWalletViewController, mDepthController, mSysuiColorExtractor,
mStatusBarService, mNotificationShadeWindowController,
- mSysUiState, this::onRotate, isKeyguardShowing(), mPowerAdapter, getEventLogger());
+ mSysUiState, this::onRotate, isKeyguardShowing(), mPowerAdapter, getEventLogger(),
+ getStatusBar());
if (shouldShowLockMessage(dialog)) {
dialog.showLockMessage();
@@ -295,11 +340,13 @@ public class GlobalActionsDialog extends GlobalActionsDialogLite
SysuiColorExtractor sysuiColorExtractor, IStatusBarService statusBarService,
NotificationShadeWindowController notificationShadeWindowController,
SysUiState sysuiState, Runnable onRotateCallback, boolean keyguardShowing,
- MyPowerOptionsAdapter powerAdapter, UiEventLogger uiEventLogger) {
+ MyPowerOptionsAdapter powerAdapter, UiEventLogger uiEventLogger,
+ StatusBar statusBar) {
super(context, com.android.systemui.R.style.Theme_SystemUI_Dialog_GlobalActions,
adapter, overflowAdapter, depthController, sysuiColorExtractor,
statusBarService, notificationShadeWindowController, sysuiState,
- onRotateCallback, keyguardShowing, powerAdapter, uiEventLogger, null);
+ onRotateCallback, keyguardShowing, powerAdapter, uiEventLogger, null,
+ statusBar);
mWalletFactory = walletFactory;
// Update window attributes
diff --git a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialogLite.java b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialogLite.java
index 8e152830e208..42cd4f7da3a8 100644
--- a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialogLite.java
+++ b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialogLite.java
@@ -74,8 +74,10 @@ import android.telephony.TelephonyManager;
import android.util.ArraySet;
import android.util.Log;
import android.view.ContextThemeWrapper;
+import android.view.GestureDetector;
import android.view.IWindowManager;
import android.view.LayoutInflater;
+import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.view.Window;
@@ -119,6 +121,7 @@ import com.android.systemui.plugins.GlobalActionsPanelPlugin;
import com.android.systemui.scrim.ScrimDrawable;
import com.android.systemui.statusbar.NotificationShadeDepthController;
import com.android.systemui.statusbar.NotificationShadeWindowController;
+import com.android.systemui.statusbar.phone.StatusBar;
import com.android.systemui.statusbar.policy.ConfigurationController;
import com.android.systemui.statusbar.policy.KeyguardStateController;
import com.android.systemui.telephony.TelephonyListenerManager;
@@ -174,6 +177,7 @@ public class GlobalActionsDialogLite implements DialogInterface.OnDismissListene
private final IDreamManager mDreamManager;
private final DevicePolicyManager mDevicePolicyManager;
private final LockPatternUtils mLockPatternUtils;
+ private final TelephonyListenerManager mTelephonyListenerManager;
private final KeyguardStateController mKeyguardStateController;
private final BroadcastDispatcher mBroadcastDispatcher;
protected final GlobalSettings mGlobalSettings;
@@ -228,6 +232,7 @@ public class GlobalActionsDialogLite implements DialogInterface.OnDismissListene
private int mDialogPressDelay = DIALOG_PRESS_DELAY; // ms
protected Handler mMainHandler;
private int mSmallestScreenWidthDp;
+ private final StatusBar mStatusBar;
@VisibleForTesting
public enum GlobalActionsEvent implements UiEventLogger.UiEventEnum {
@@ -304,31 +309,46 @@ public class GlobalActionsDialogLite implements DialogInterface.OnDismissListene
* @param context everything needs a context :(
*/
@Inject
- public GlobalActionsDialogLite(Context context, GlobalActionsManager windowManagerFuncs,
- AudioManager audioManager, IDreamManager iDreamManager,
- DevicePolicyManager devicePolicyManager, LockPatternUtils lockPatternUtils,
+ public GlobalActionsDialogLite(
+ Context context,
+ GlobalActionsManager windowManagerFuncs,
+ AudioManager audioManager,
+ IDreamManager iDreamManager,
+ DevicePolicyManager devicePolicyManager,
+ LockPatternUtils lockPatternUtils,
BroadcastDispatcher broadcastDispatcher,
TelephonyListenerManager telephonyListenerManager,
- GlobalSettings globalSettings, SecureSettings secureSettings,
- @Nullable Vibrator vibrator, @Main Resources resources,
+ GlobalSettings globalSettings,
+ SecureSettings secureSettings,
+ @Nullable Vibrator vibrator,
+ @Main Resources resources,
ConfigurationController configurationController,
- KeyguardStateController keyguardStateController, UserManager userManager,
- TrustManager trustManager, IActivityManager iActivityManager,
- @Nullable TelecomManager telecomManager, MetricsLogger metricsLogger,
- NotificationShadeDepthController depthController, SysuiColorExtractor colorExtractor,
+ KeyguardStateController keyguardStateController,
+ UserManager userManager,
+ TrustManager trustManager,
+ IActivityManager iActivityManager,
+ @Nullable TelecomManager telecomManager,
+ MetricsLogger metricsLogger,
+ NotificationShadeDepthController depthController,
+ SysuiColorExtractor colorExtractor,
IStatusBarService statusBarService,
NotificationShadeWindowController notificationShadeWindowController,
IWindowManager iWindowManager,
@Background Executor backgroundExecutor,
UiEventLogger uiEventLogger,
GlobalActionsInfoProvider infoProvider,
- RingerModeTracker ringerModeTracker, SysUiState sysUiState, @Main Handler handler) {
+ RingerModeTracker ringerModeTracker,
+ SysUiState sysUiState,
+ @Main Handler handler,
+ PackageManager packageManager,
+ StatusBar statusBar) {
mContext = context;
mWindowManagerFuncs = windowManagerFuncs;
mAudioManager = audioManager;
mDreamManager = iDreamManager;
mDevicePolicyManager = devicePolicyManager;
mLockPatternUtils = lockPatternUtils;
+ mTelephonyListenerManager = telephonyListenerManager;
mKeyguardStateController = keyguardStateController;
mBroadcastDispatcher = broadcastDispatcher;
mGlobalSettings = globalSettings;
@@ -351,7 +371,8 @@ public class GlobalActionsDialogLite implements DialogInterface.OnDismissListene
mRingerModeTracker = ringerModeTracker;
mSysUiState = sysUiState;
mMainHandler = handler;
- mSmallestScreenWidthDp = mContext.getResources().getConfiguration().smallestScreenWidthDp;
+ mSmallestScreenWidthDp = resources.getConfiguration().smallestScreenWidthDp;
+ mStatusBar = statusBar;
// receive broadcasts
IntentFilter filter = new IntentFilter();
@@ -360,11 +381,10 @@ public class GlobalActionsDialogLite implements DialogInterface.OnDismissListene
filter.addAction(TelephonyManager.ACTION_EMERGENCY_CALLBACK_MODE_CHANGED);
mBroadcastDispatcher.registerReceiver(mBroadcastReceiver, filter);
- mHasTelephony =
- context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_TELEPHONY);
+ mHasTelephony = packageManager.hasSystemFeature(PackageManager.FEATURE_TELEPHONY);
// get notified of phone state changes
- telephonyListenerManager.addServiceStateListener(mPhoneStateListener);
+ mTelephonyListenerManager.addServiceStateListener(mPhoneStateListener);
mGlobalSettings.registerContentObserver(
Settings.Global.getUriFor(Settings.Global.AIRPLANE_MODE_ON), true,
mAirplaneModeObserver);
@@ -384,6 +404,16 @@ public class GlobalActionsDialogLite implements DialogInterface.OnDismissListene
mConfigurationController.addCallback(this);
}
+ /**
+ * Clean up callbacks
+ */
+ public void destroy() {
+ mBroadcastDispatcher.unregisterReceiver(mBroadcastReceiver);
+ mTelephonyListenerManager.removeServiceStateListener(mPhoneStateListener);
+ mGlobalSettings.unregisterContentObserver(mAirplaneModeObserver);
+ mConfigurationController.removeCallback(this);
+ }
+
protected Context getContext() {
return mContext;
}
@@ -392,6 +422,10 @@ public class GlobalActionsDialogLite implements DialogInterface.OnDismissListene
return mUiEventLogger;
}
+ protected StatusBar getStatusBar() {
+ return mStatusBar;
+ }
+
/**
* Show the global actions dialog (creating if necessary)
*
@@ -625,7 +659,7 @@ public class GlobalActionsDialogLite implements DialogInterface.OnDismissListene
mDepthController, mSysuiColorExtractor,
mStatusBarService, mNotificationShadeWindowController,
mSysUiState, this::onRotate, mKeyguardShowing, mPowerAdapter, mUiEventLogger,
- mInfoProvider);
+ mInfoProvider, mStatusBar);
dialog.setOnDismissListener(this);
dialog.setOnShowListener(this);
@@ -679,14 +713,6 @@ public class GlobalActionsDialogLite implements DialogInterface.OnDismissListene
mDialog.refreshDialog();
}
}
-
- /**
- * Clean up callbacks
- */
- public void destroy() {
- mConfigurationController.removeCallback(this);
- }
-
/**
* Implements {@link GlobalActionsPanelPlugin.Callbacks#dismissGlobalActionsMenu()}, which is
* called when the quick access wallet requests dismissal.
@@ -2008,7 +2034,7 @@ public class GlobalActionsDialogLite implements DialogInterface.OnDismissListene
}
};
- private ContentObserver mAirplaneModeObserver = new ContentObserver(mMainHandler) {
+ private final ContentObserver mAirplaneModeObserver = new ContentObserver(mMainHandler) {
@Override
public void onChange(boolean selfChange) {
onAirplaneModeChanged();
@@ -2100,9 +2126,53 @@ public class GlobalActionsDialogLite implements DialogInterface.OnDismissListene
protected final Runnable mOnRotateCallback;
private UiEventLogger mUiEventLogger;
private GlobalActionsInfoProvider mInfoProvider;
+ private GestureDetector mGestureDetector;
+ private StatusBar mStatusBar;
protected ViewGroup mContainer;
+ @VisibleForTesting
+ protected GestureDetector.SimpleOnGestureListener mGestureListener =
+ new GestureDetector.SimpleOnGestureListener() {
+ @Override
+ public boolean onDown(MotionEvent e) {
+ // All gestures begin with this message, so continue listening
+ return true;
+ }
+
+ @Override
+ public boolean onSingleTapConfirmed(MotionEvent e) {
+ // Close without opening shade
+ mUiEventLogger.log(GlobalActionsEvent.GA_CLOSE_TAP_OUTSIDE);
+ cancel();
+ return false;
+ }
+
+ @Override
+ public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX,
+ float distanceY) {
+ if (distanceY < 0 && distanceY > distanceX
+ && e1.getY() <= mStatusBar.getStatusBarHeight()) {
+ // Downwards scroll from top
+ openShadeAndDismiss();
+ return true;
+ }
+ return false;
+ }
+
+ @Override
+ public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX,
+ float velocityY) {
+ if (velocityY > 0 && Math.abs(velocityY) > Math.abs(velocityX)
+ && e1.getY() <= mStatusBar.getStatusBarHeight()) {
+ // Downwards fling from top
+ openShadeAndDismiss();
+ return true;
+ }
+ return false;
+ }
+ };
+
ActionsDialogLite(Context context, int themeRes, MyAdapter adapter,
MyOverflowAdapter overflowAdapter,
NotificationShadeDepthController depthController,
@@ -2110,7 +2180,7 @@ public class GlobalActionsDialogLite implements DialogInterface.OnDismissListene
NotificationShadeWindowController notificationShadeWindowController,
SysUiState sysuiState, Runnable onRotateCallback, boolean keyguardShowing,
MyPowerOptionsAdapter powerAdapter, UiEventLogger uiEventLogger,
- @Nullable GlobalActionsInfoProvider infoProvider) {
+ @Nullable GlobalActionsInfoProvider infoProvider, StatusBar statusBar) {
super(context, themeRes);
mContext = context;
mAdapter = adapter;
@@ -2125,6 +2195,9 @@ public class GlobalActionsDialogLite implements DialogInterface.OnDismissListene
mKeyguardShowing = keyguardShowing;
mUiEventLogger = uiEventLogger;
mInfoProvider = infoProvider;
+ mStatusBar = statusBar;
+
+ mGestureDetector = new GestureDetector(mContext, mGestureListener);
// Window initialization
Window window = getWindow();
@@ -2146,6 +2219,23 @@ public class GlobalActionsDialogLite implements DialogInterface.OnDismissListene
initializeLayout();
}
+ @Override
+ public boolean onTouchEvent(MotionEvent event) {
+ return mGestureDetector.onTouchEvent(event) || super.onTouchEvent(event);
+ }
+
+ private void openShadeAndDismiss() {
+ mUiEventLogger.log(GlobalActionsEvent.GA_CLOSE_TAP_OUTSIDE);
+ if (mStatusBar.isKeyguardShowing()) {
+ // match existing lockscreen behavior to open QS when swiping from status bar
+ mStatusBar.animateExpandSettingsPanel(null);
+ } else {
+ // otherwise, swiping down should expand notification shade
+ mStatusBar.animateExpandNotificationsPanel();
+ }
+ dismiss();
+ }
+
private ListPopupWindow createPowerOverflowPopup() {
GlobalActionsPopupMenu popup = new GlobalActionsPopupMenu(
new ContextThemeWrapper(
@@ -2194,9 +2284,9 @@ public class GlobalActionsDialogLite implements DialogInterface.OnDismissListene
mGlobalActionsLayout.setRotationListener(this::onRotate);
mGlobalActionsLayout.setAdapter(mAdapter);
mContainer = findViewById(com.android.systemui.R.id.global_actions_container);
- mContainer.setOnClickListener(v -> {
- mUiEventLogger.log(GlobalActionsEvent.GA_CLOSE_TAP_OUTSIDE);
- cancel();
+ mContainer.setOnTouchListener((v, event) -> {
+ mGestureDetector.onTouchEvent(event);
+ return v.onTouchEvent(event);
});
View overflowButton = findViewById(
diff --git a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsImpl.java b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsImpl.java
index 178a74cecc2e..e37d3d586ccc 100644
--- a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsImpl.java
@@ -32,7 +32,6 @@ import android.widget.TextView;
import com.android.internal.R;
import com.android.keyguard.KeyguardUpdateMonitor;
import com.android.settingslib.Utils;
-import com.android.systemui.Dependency;
import com.android.systemui.plugins.GlobalActions;
import com.android.systemui.scrim.ScrimDrawable;
import com.android.systemui.statusbar.BlurUtils;
@@ -52,19 +51,24 @@ public class GlobalActionsImpl implements GlobalActions, CommandQueue.Callbacks
private final KeyguardStateController mKeyguardStateController;
private final DeviceProvisionedController mDeviceProvisionedController;
private final BlurUtils mBlurUtils;
+ private final KeyguardUpdateMonitor mKeyguardUpdateMonitor;
private final CommandQueue mCommandQueue;
private GlobalActionsDialogLite mGlobalActionsDialog;
private boolean mDisabled;
@Inject
public GlobalActionsImpl(Context context, CommandQueue commandQueue,
- Lazy<GlobalActionsDialogLite> globalActionsDialogLazy, BlurUtils blurUtils) {
+ Lazy<GlobalActionsDialogLite> globalActionsDialogLazy, BlurUtils blurUtils,
+ KeyguardStateController keyguardStateController,
+ DeviceProvisionedController deviceProvisionedController,
+ KeyguardUpdateMonitor keyguardUpdateMonitor) {
mContext = context;
mGlobalActionsDialogLazy = globalActionsDialogLazy;
- mKeyguardStateController = Dependency.get(KeyguardStateController.class);
- mDeviceProvisionedController = Dependency.get(DeviceProvisionedController.class);
+ mKeyguardStateController = keyguardStateController;
+ mDeviceProvisionedController = deviceProvisionedController;
mCommandQueue = commandQueue;
mBlurUtils = blurUtils;
+ mKeyguardUpdateMonitor = keyguardUpdateMonitor;
mCommandQueue.addCallback(this);
}
@@ -83,7 +87,7 @@ public class GlobalActionsImpl implements GlobalActions, CommandQueue.Callbacks
mGlobalActionsDialog = mGlobalActionsDialogLazy.get();
mGlobalActionsDialog.showOrHideDialog(mKeyguardStateController.isShowing(),
mDeviceProvisionedController.isDeviceProvisioned());
- Dependency.get(KeyguardUpdateMonitor.class).requestFaceAuth();
+ mKeyguardUpdateMonitor.requestFaceAuth();
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsInfoProvider.kt b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsInfoProvider.kt
index 39008eecd6a2..17b532a643cd 100644
--- a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsInfoProvider.kt
+++ b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsInfoProvider.kt
@@ -25,6 +25,7 @@ import android.util.Log
import android.view.LayoutInflater
import android.view.ViewGroup
import android.widget.ImageView
+import android.widget.TextView
import com.android.systemui.R
import com.android.systemui.controls.controller.ControlsController
import com.android.systemui.plugins.ActivityStarter
@@ -70,6 +71,11 @@ class GlobalActionsInfoProvider @Inject constructor(
val view = LayoutInflater.from(context).inflate(R.layout.global_actions_change_panel,
parent, false)
+
+ val walletTitle = walletClient.serviceLabel ?: context.getString(R.string.wallet_title)
+ val message = view.findViewById<TextView>(R.id.global_actions_change_message)
+ message?.setText(context.getString(R.string.global_actions_change_description, walletTitle))
+
val button = view.findViewById<ImageView>(R.id.global_actions_change_button)
button.setOnClickListener { _ ->
dismissParent.run()
diff --git a/packages/SystemUI/src/com/android/systemui/glwallpaper/ImageWallpaperRenderer.java b/packages/SystemUI/src/com/android/systemui/glwallpaper/ImageWallpaperRenderer.java
index 01a353ce8f1f..d30783c29f92 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 final ImageGLWallpaper mWallpaper;
private final Rect mSurfaceSize = new Rect();
private final WallpaperTexture mTexture;
+ private Consumer<Bitmap> mOnBitmapUpdated;
public ImageWallpaperRenderer(Context context) {
final WallpaperManager wpm = context.getSystemService(WallpaperManager.class);
@@ -60,10 +61,9 @@ public class ImageWallpaperRenderer implements GLWallpaperRenderer {
/**
* @hide
- * @return
*/
- public void useBitmap(Consumer<Bitmap> c) {
- mTexture.use(c);
+ public void setOnBitmapChanged(Consumer<Bitmap> c) {
+ mOnBitmapUpdated = c;
}
@Override
@@ -80,6 +80,8 @@ public class ImageWallpaperRenderer implements GLWallpaperRenderer {
mTexture.use(bitmap -> {
if (bitmap == null) {
Log.w(TAG, "reload texture failed!");
+ } else if (mOnBitmapUpdated != null) {
+ mOnBitmapUpdated.accept(bitmap);
}
mWallpaper.setup(bitmap);
});
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardIndicationRotateTextViewController.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardIndicationRotateTextViewController.java
index bec4ce6ba658..fc5f3b8ae994 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardIndicationRotateTextViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardIndicationRotateTextViewController.java
@@ -20,7 +20,6 @@ import android.annotation.Nullable;
import android.content.res.ColorStateList;
import android.graphics.Color;
import android.text.TextUtils;
-import android.view.View;
import androidx.annotation.IntDef;
@@ -202,10 +201,7 @@ public class KeyguardIndicationRotateTextViewController extends
mCurrIndicationType = type;
mIndicationQueue.removeIf(x -> x == type);
- if (mCurrIndicationType == INDICATION_TYPE_NONE) {
- mView.setVisibility(View.GONE);
- } else {
- mView.setVisibility(View.VISIBLE);
+ if (mCurrIndicationType != INDICATION_TYPE_NONE) {
mIndicationQueue.add(type); // re-add to show later
}
@@ -299,7 +295,7 @@ public class KeyguardIndicationRotateTextViewController extends
}
}
- private static final int INDICATION_TYPE_NONE = -1;
+ static final int INDICATION_TYPE_NONE = -1;
public static final int INDICATION_TYPE_OWNER_INFO = 0;
public static final int INDICATION_TYPE_DISCLOSURE = 1;
public static final int INDICATION_TYPE_LOGOUT = 2;
diff --git a/packages/SystemUI/src/com/android/systemui/media/KeyguardMediaController.kt b/packages/SystemUI/src/com/android/systemui/media/KeyguardMediaController.kt
index 77d789292e5e..2bf102f724f4 100644
--- a/packages/SystemUI/src/com/android/systemui/media/KeyguardMediaController.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/KeyguardMediaController.kt
@@ -186,11 +186,9 @@ class KeyguardMediaController @Inject constructor(
}
private fun hideMediaPlayer() {
- if (useSplitShade) {
- setVisibility(splitShadeContainer, View.GONE)
- } else {
- setVisibility(singlePaneContainer, View.GONE)
- }
+ // always hide splitShadeContainer as it's initially visible and may influence layout
+ setVisibility(splitShadeContainer, View.GONE)
+ setVisibility(singlePaneContainer, View.GONE)
}
private fun setVisibility(view: ViewGroup?, newVisibility: Int) {
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaControlPanel.java b/packages/SystemUI/src/com/android/systemui/media/MediaControlPanel.java
index c2b580773424..19190cd4a17d 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaControlPanel.java
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaControlPanel.java
@@ -46,6 +46,7 @@ import androidx.annotation.Nullable;
import androidx.annotation.UiThread;
import androidx.constraintlayout.widget.ConstraintSet;
+import com.android.internal.jank.InteractionJankMonitor;
import com.android.settingslib.widget.AdaptiveIcon;
import com.android.systemui.R;
import com.android.systemui.animation.ActivityLaunchAnimator;
@@ -468,7 +469,8 @@ public class MediaControlPanel {
TransitionLayout player) {
// TODO(b/174236650): Make sure that the carousel indicator also fades out.
// TODO(b/174236650): Instrument the animation to measure jank.
- return new GhostedViewLaunchAnimatorController(player) {
+ return new GhostedViewLaunchAnimatorController(player,
+ InteractionJankMonitor.CUJ_SHADE_APP_LAUNCH_FROM_MEDIA_PLAYER) {
@Override
protected float getCurrentTopCornerRadius() {
return ((IlluminationDrawable) player.getBackground()).getCornerRadius();
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaDataManager.kt b/packages/SystemUI/src/com/android/systemui/media/MediaDataManager.kt
index 5b1e039ad0f8..28d336ea18d5 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaDataManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaDataManager.kt
@@ -39,6 +39,7 @@ import android.media.session.MediaSession
import android.net.Uri
import android.os.Parcelable
import android.os.UserHandle
+import android.provider.Settings
import android.service.notification.StatusBarNotification
import android.text.TextUtils
import android.util.Log
@@ -54,6 +55,7 @@ import com.android.systemui.plugins.ActivityStarter
import com.android.systemui.plugins.BcSmartspaceDataPlugin
import com.android.systemui.statusbar.NotificationMediaManager.isPlayingState
import com.android.systemui.statusbar.notification.row.HybridGroupManager
+import com.android.systemui.tuner.TunerService
import com.android.systemui.util.Assert
import com.android.systemui.util.Utils
import com.android.systemui.util.concurrency.DelayableExecutor
@@ -114,7 +116,8 @@ class MediaDataManager(
private val smartspaceMediaDataProvider: SmartspaceMediaDataProvider,
private var useMediaResumption: Boolean,
private val useQsMediaPlayer: Boolean,
- private val systemClock: SystemClock
+ private val systemClock: SystemClock,
+ private val tunerService: TunerService
) : Dumpable, BcSmartspaceDataPlugin.SmartspaceTargetListener {
companion object {
@@ -147,6 +150,7 @@ class MediaDataManager(
// There should ONLY be at most one Smartspace media recommendation.
private var smartspaceMediaData: SmartspaceMediaData = EMPTY_SMARTSPACE_MEDIA_DATA
private var smartspaceSession: SmartspaceSession? = null
+ private var allowMediaRecommendations = Utils.allowMediaRecommendations(context)
@Inject
constructor(
@@ -164,12 +168,13 @@ class MediaDataManager(
mediaDataFilter: MediaDataFilter,
activityStarter: ActivityStarter,
smartspaceMediaDataProvider: SmartspaceMediaDataProvider,
- clock: SystemClock
+ clock: SystemClock,
+ tunerService: TunerService
) : this(context, backgroundExecutor, foregroundExecutor, mediaControllerFactory,
broadcastDispatcher, dumpManager, mediaTimeoutListener, mediaResumeListener,
mediaSessionBasedFilter, mediaDeviceManager, mediaDataCombineLatest, mediaDataFilter,
activityStarter, smartspaceMediaDataProvider, Utils.useMediaResumption(context),
- Utils.useQsMediaPlayer(context), clock)
+ Utils.useQsMediaPlayer(context), clock, tunerService)
private val appChangeReceiver = object : BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent) {
@@ -243,6 +248,14 @@ class MediaDataManager(
})
}
smartspaceSession?.let { it.requestSmartspaceUpdate() }
+ tunerService.addTunable(object : TunerService.Tunable {
+ override fun onTuningChanged(key: String?, newValue: String?) {
+ allowMediaRecommendations = Utils.allowMediaRecommendations(context)
+ if (!allowMediaRecommendations) {
+ dismissSmartspaceRecommendation(key = smartspaceMediaData.targetId, delay = 0L)
+ }
+ }
+ }, Settings.Secure.MEDIA_CONTROLS_RECOMMENDATION)
}
fun destroy() {
@@ -695,8 +708,7 @@ class MediaDataManager(
}
override fun onSmartspaceTargetsUpdated(targets: List<Parcelable>) {
- if (!Utils.allowMediaRecommendations(context)) {
- Log.d(TAG, "Smartspace recommendation is disabled in Settings.")
+ if (!allowMediaRecommendations) {
return
}
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaHierarchyManager.kt b/packages/SystemUI/src/com/android/systemui/media/MediaHierarchyManager.kt
index 075bc700cfa0..edbf18789e28 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaHierarchyManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaHierarchyManager.kt
@@ -187,6 +187,12 @@ class MediaHierarchyManager @Inject constructor(
private var currentAttachmentLocation = -1
/**
+ * Is there any active media in the carousel?
+ */
+ private var hasActiveMedia: Boolean = false
+ get() = mediaHosts.get(LOCATION_QQS)?.visible == true
+
+ /**
* Are we currently waiting on an animation to start?
*/
private var animationPending: Boolean = false
@@ -476,8 +482,12 @@ class MediaHierarchyManager @Inject constructor(
val viewHost = createUniqueObjectHost()
mediaObject.hostView = viewHost
mediaObject.addVisibilityChangeListener {
+ // If QQS changes visibility, we need to force an update to ensure the transition
+ // goes into the correct state
+ val stateUpdate = mediaObject.location == LOCATION_QQS
+
// Never animate because of a visibility change, only state changes should do that
- updateDesiredLocation(forceNoAnimation = true)
+ updateDesiredLocation(forceNoAnimation = true, forceStateUpdate = stateUpdate)
}
mediaHosts[mediaObject.location] = mediaObject
if (mediaObject.location == desiredLocation) {
@@ -521,10 +531,15 @@ class MediaHierarchyManager @Inject constructor(
* going from the old desired location to the new one.
*
* @param forceNoAnimation optional parameter telling the system not to animate
+ * @param forceStateUpdate optional parameter telling the system to update transition state
+ * even if location did not change
*/
- private fun updateDesiredLocation(forceNoAnimation: Boolean = false) {
+ private fun updateDesiredLocation(
+ forceNoAnimation: Boolean = false,
+ forceStateUpdate: Boolean = false
+ ) {
val desiredLocation = calculateLocation()
- if (desiredLocation != this.desiredLocation) {
+ if (desiredLocation != this.desiredLocation || forceStateUpdate) {
if (this.desiredLocation >= 0) {
previousLocation = this.desiredLocation
}
@@ -784,7 +799,7 @@ class MediaHierarchyManager @Inject constructor(
private fun getQSTransformationProgress(): Float {
val currentHost = getHost(desiredLocation)
val previousHost = getHost(previousLocation)
- if (currentHost?.location == LOCATION_QS) {
+ if (hasActiveMedia && currentHost?.location == LOCATION_QS) {
if (previousHost?.location == LOCATION_QQS) {
if (previousHost.visible || statusbarState != StatusBarState.KEYGUARD) {
return qsExpansion
@@ -917,6 +932,7 @@ class MediaHierarchyManager @Inject constructor(
val location = when {
qsExpansion > 0.0f && !onLockscreen -> LOCATION_QS
qsExpansion > 0.4f && onLockscreen -> LOCATION_QS
+ !hasActiveMedia -> LOCATION_QS
onLockscreen && isTransformingToFullShadeAndInQQS() -> LOCATION_QQS
onLockscreen && allowedOnLockscreen -> LOCATION_LOCKSCREEN
else -> LOCATION_QQS
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java
index 0d9749e05262..ff5d0b157c80 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java
@@ -251,8 +251,9 @@ public class EdgeBackGestureHandler extends CurrentUserTracker
private float mMLResults;
// For debugging
- private ArrayDeque<String> mPredictionLog = new ArrayDeque<>();
- private ArrayDeque<String> mGestureLog = new ArrayDeque<>();
+ private LogArray mPredictionLog = new LogArray(MAX_NUM_LOGGED_PREDICTIONS);
+ private LogArray mGestureLogInsideInsets = new LogArray(MAX_NUM_LOGGED_GESTURES);
+ private LogArray mGestureLogOutsideInsets = new LogArray(MAX_NUM_LOGGED_GESTURES);
private final GestureNavigationSettingsObserver mGestureNavigationSettingsObserver;
@@ -631,7 +632,7 @@ public class EdgeBackGestureHandler extends CurrentUserTracker
return mMLResults >= mMLModelThreshold ? 1 : 0;
}
- private boolean isWithinTouchRegion(int x, int y) {
+ private boolean isWithinInsets(int x, int y) {
// Disallow if we are in the bottom gesture area
if (y >= (mDisplaySize.y - mBottomGestureHeight)) {
return false;
@@ -644,7 +645,10 @@ public class EdgeBackGestureHandler extends CurrentUserTracker
&& x < (mDisplaySize.x - 2 * (mEdgeWidthRight + mRightInset))) {
return false;
}
+ return true;
+ }
+ private boolean isWithinTouchRegion(int x, int y) {
// If the point is inside the PiP or Nav bar overlay excluded bounds, then ignore the back
// gesture
final boolean isInsidePip = mIsInPipMode && mPipExcludedBounds.contains(x, y);
@@ -675,14 +679,8 @@ public class EdgeBackGestureHandler extends CurrentUserTracker
}
// For debugging purposes
- if (mPredictionLog.size() >= MAX_NUM_LOGGED_PREDICTIONS) {
- mPredictionLog.removeFirst();
- }
- mPredictionLog.addLast(String.format("Prediction [%d,%d,%d,%d,%f,%d]",
+ mPredictionLog.log(String.format("Prediction [%d,%d,%d,%d,%f,%d]",
System.currentTimeMillis(), x, y, app, mMLResults, withinRange ? 1 : 0));
- if (DEBUG_MISSING_GESTURE) {
- Log.d(DEBUG_MISSING_GESTURE_TAG, mPredictionLog.peekLast());
- }
// Always allow if the user is in a transient sticky immersive state
if (mIsNavBarShownTransiently) {
@@ -755,7 +753,8 @@ public class EdgeBackGestureHandler extends CurrentUserTracker
mMLResults = 0;
mLogGesture = false;
mInRejectedExclusion = false;
- mAllowGesture = !mDisabledForQuickstep && mIsBackGestureAllowed
+ boolean isWithinInsets = isWithinInsets((int) ev.getX(), (int) ev.getY());
+ mAllowGesture = !mDisabledForQuickstep && mIsBackGestureAllowed && isWithinInsets
&& !mGestureBlockingActivityRunning
&& !QuickStepContract.isBackGestureDisabled(mSysUiFlags)
&& isWithinTouchRegion((int) ev.getX(), (int) ev.getY());
@@ -769,18 +768,13 @@ public class EdgeBackGestureHandler extends CurrentUserTracker
mThresholdCrossed = false;
}
- // For debugging purposes
- if (mGestureLog.size() >= MAX_NUM_LOGGED_GESTURES) {
- mGestureLog.removeFirst();
- }
- mGestureLog.addLast(String.format(
+ // For debugging purposes, only log edge points
+ (isWithinInsets ? mGestureLogInsideInsets : mGestureLogOutsideInsets).log(String.format(
"Gesture [%d,alw=%B,%B,%B,%B,disp=%s,wl=%d,il=%d,wr=%d,ir=%d,excl=%s]",
- System.currentTimeMillis(), mAllowGesture, mIsOnLeftEdge, mIsBackGestureAllowed,
+ System.currentTimeMillis(), mAllowGesture, mIsOnLeftEdge,
+ mIsBackGestureAllowed,
QuickStepContract.isBackGestureDisabled(mSysUiFlags), mDisplaySize,
mEdgeWidthLeft, mLeftInset, mEdgeWidthRight, mRightInset, mExcludeRegion));
- if (DEBUG_MISSING_GESTURE) {
- Log.d(DEBUG_MISSING_GESTURE_TAG, mGestureLog.peekLast());
- }
} else if (mAllowGesture || mLogGesture) {
if (!mThresholdCrossed) {
mEndPoint.x = (int) ev.getX();
@@ -907,7 +901,7 @@ public class EdgeBackGestureHandler extends CurrentUserTracker
pw.println(" mUseMLModel=" + mUseMLModel);
pw.println(" mDisabledForQuickstep=" + mDisabledForQuickstep);
pw.println(" mStartingQuickstepRotation=" + mStartingQuickstepRotation);
- pw.println(" mInRejectedExclusion" + mInRejectedExclusion);
+ pw.println(" mInRejectedExclusion=" + mInRejectedExclusion);
pw.println(" mExcludeRegion=" + mExcludeRegion);
pw.println(" mUnrestrictedExcludeRegion=" + mUnrestrictedExcludeRegion);
pw.println(" mIsInPipMode=" + mIsInPipMode);
@@ -922,7 +916,8 @@ public class EdgeBackGestureHandler extends CurrentUserTracker
pw.println(" mTouchSlop=" + mTouchSlop);
pw.println(" mBottomGestureHeight=" + mBottomGestureHeight);
pw.println(" mPredictionLog=" + String.join("\n", mPredictionLog));
- pw.println(" mGestureLog=" + String.join("\n", mGestureLog));
+ pw.println(" mGestureLogInsideInsets=" + String.join("\n", mGestureLogInsideInsets));
+ pw.println(" mGestureLogOutsideInsets=" + String.join("\n", mGestureLogOutsideInsets));
pw.println(" mEdgeBackPlugin=" + mEdgeBackPlugin);
}
@@ -945,4 +940,23 @@ public class EdgeBackGestureHandler extends CurrentUserTracker
}
proto.edgeBackGestureHandler.allowGesture = mAllowGesture;
}
+
+
+ private static class LogArray extends ArrayDeque<String> {
+ private final int mLength;
+
+ LogArray(int length) {
+ mLength = length;
+ }
+
+ void log(String message) {
+ if (size() >= mLength) {
+ removeFirst();
+ }
+ addLast(message);
+ if (DEBUG_MISSING_GESTURE) {
+ Log.d(DEBUG_MISSING_GESTURE_TAG, message);
+ }
+ }
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/people/NotificationHelper.java b/packages/SystemUI/src/com/android/systemui/people/NotificationHelper.java
index b5ac90828fce..d863dcce4fc7 100644
--- a/packages/SystemUI/src/com/android/systemui/people/NotificationHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/people/NotificationHelper.java
@@ -240,9 +240,16 @@ public class NotificationHelper {
/** Returns whether {@code entry} is suppressed from shade, meaning we should not show it. */
public static boolean shouldFilterOut(
Optional<Bubbles> bubblesOptional, NotificationEntry entry) {
- return bubblesOptional.isPresent()
- && bubblesOptional.get().isBubbleNotificationSuppressedFromShade(
- entry.getKey(), entry.getSbn().getGroupKey());
+ boolean isSuppressed = false;
+ //TODO(b/190822282): Investigate what is causing the NullPointerException
+ try {
+ isSuppressed = bubblesOptional.isPresent()
+ && bubblesOptional.get().isBubbleNotificationSuppressedFromShade(
+ entry.getKey(), entry.getSbn().getGroupKey());
+ } catch (Exception e) {
+ Log.e(TAG, "Exception checking if notification is suppressed: " + e);
+ }
+ return isSuppressed;
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSFooterViewController.java b/packages/SystemUI/src/com/android/systemui/qs/QSFooterViewController.java
index f6d93895ce05..929aedae6706 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSFooterViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSFooterViewController.java
@@ -28,6 +28,7 @@ import android.view.View;
import android.widget.TextView;
import android.widget.Toast;
+import com.android.internal.jank.InteractionJankMonitor;
import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.UiEventLogger;
import com.android.internal.logging.nano.MetricsProto;
@@ -73,7 +74,7 @@ public class QSFooterViewController extends ViewController<QSFooterView> impleme
private final PageIndicator mPageIndicator;
private final View mPowerMenuLite;
private final boolean mShowPMLiteButton;
- private GlobalActionsDialogLite mGlobalActionsDialog;
+ private final GlobalActionsDialogLite mGlobalActionsDialog;
private final UiEventLogger mUiEventLogger;
private final UserInfoController.OnUserInfoChangedListener mOnUserInfoChangedListener =
@@ -272,7 +273,8 @@ public class QSFooterViewController extends ViewController<QSFooterView> impleme
private void startSettingsActivity() {
ActivityLaunchAnimator.Controller animationController =
mSettingsButtonContainer != null ? ActivityLaunchAnimator.Controller.fromView(
- mSettingsButtonContainer) : null;
+ mSettingsButtonContainer,
+ InteractionJankMonitor.CUJ_SHADE_APP_LAUNCH_FROM_SETTINGS_BUTTON) : null;
mActivityStarter.startActivity(new Intent(android.provider.Settings.ACTION_SETTINGS),
true /* dismissShade */, animationController);
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java b/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java
index e1a66b2c07ee..7b8a6a0a8d0e 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java
@@ -539,8 +539,9 @@ public class QSFragment extends LifecycleFragment implements QS, CommandQueue.Ca
private void pinToBottom(float absoluteBottomPosition, MediaHost mediaHost, boolean expanded) {
View hostView = mediaHost.getHostView();
- // on keyguard we cross-fade to expanded, so no need to pin it.
- if (mLastQSExpansion > 0 && !isKeyguardState()) {
+ // On keyguard we cross-fade to expanded, so no need to pin it.
+ // If the collapsed qs isn't visible, we also just keep it at the laid out position.
+ if (mLastQSExpansion > 0 && !isKeyguardState() && mQqsMediaHost.getVisible()) {
float targetPosition = absoluteBottomPosition - getTotalBottomMargin(hostView)
- hostView.getHeight();
float currentPosition = mediaHost.getCurrentBounds().top
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSTileHost.java b/packages/SystemUI/src/com/android/systemui/qs/QSTileHost.java
index 525bad8a0e25..6ddf2a75f491 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSTileHost.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSTileHost.java
@@ -45,7 +45,9 @@ import com.android.systemui.plugins.qs.QSFactory;
import com.android.systemui.plugins.qs.QSTile;
import com.android.systemui.plugins.qs.QSTileView;
import com.android.systemui.qs.external.CustomTile;
+import com.android.systemui.qs.external.CustomTileStatePersister;
import com.android.systemui.qs.external.TileLifecycleManager;
+import com.android.systemui.qs.external.TileServiceKey;
import com.android.systemui.qs.external.TileServices;
import com.android.systemui.qs.logging.QSLogger;
import com.android.systemui.settings.UserTracker;
@@ -93,6 +95,7 @@ public class QSTileHost implements QSHost, Tunable, PluginListener<QSFactory>, D
private final QSLogger mQSLogger;
private final UiEventLogger mUiEventLogger;
private final InstanceIdSequence mInstanceIdSequence;
+ private final CustomTileStatePersister mCustomTileStatePersister;
private final List<Callback> mCallbacks = new ArrayList<>();
private AutoTileManager mAutoTiles;
@@ -119,7 +122,8 @@ public class QSTileHost implements QSHost, Tunable, PluginListener<QSFactory>, D
QSLogger qsLogger,
UiEventLogger uiEventLogger,
UserTracker userTracker,
- SecureSettings secureSettings) {
+ SecureSettings secureSettings,
+ CustomTileStatePersister customTileStatePersister) {
mIconController = iconController;
mContext = context;
mUserContext = context;
@@ -139,6 +143,7 @@ public class QSTileHost implements QSHost, Tunable, PluginListener<QSFactory>, D
mDumpManager.registerDumpable(TAG, this);
mUserTracker = userTracker;
mSecureSettings = secureSettings;
+ mCustomTileStatePersister = customTileStatePersister;
mainHandler.post(() -> {
// This is technically a hack to avoid circular dependency of
@@ -418,6 +423,11 @@ public class QSTileHost implements QSHost, Tunable, PluginListener<QSFactory>, D
changeTiles(mTileSpecs, newSpecs);
}
+ /**
+ * Change the tiles triggered by the user editing.
+ * <p>
+ * This is not called on device start, or on user change.
+ */
public void changeTiles(List<String> previousTiles, List<String> newTiles) {
final List<String> copy = new ArrayList<>(previousTiles);
final int NP = copy.size();
@@ -433,6 +443,7 @@ public class QSTileHost implements QSHost, Tunable, PluginListener<QSFactory>, D
mBroadcastDispatcher);
lifecycleManager.onStopListening();
lifecycleManager.onTileRemoved();
+ mCustomTileStatePersister.removeState(new TileServiceKey(component, mCurrentUser));
TileLifecycleManager.setTileAdded(mContext, component, false);
lifecycleManager.flushMessagesAndUnbind();
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java b/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java
index 7cc6ecd8cf62..997b96626747 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java
@@ -399,12 +399,14 @@ public class QuickStatusBarHeader extends FrameLayout {
mClockIconsSeparatorLayoutParams.width = 0;
setSeparatorVisibility(false);
mShowClockIconsSeparator = false;
+ mBatteryRemainingIcon.setPercentShowMode(BatteryMeterView.MODE_ESTIMATE);
} else {
datePrivacySeparatorLayoutParams.width = topCutout.width();
mDatePrivacySeparator.setVisibility(View.VISIBLE);
mClockIconsSeparatorLayoutParams.width = topCutout.width();
mShowClockIconsSeparator = true;
setSeparatorVisibility(mKeyguardExpansionFraction == 0f);
+ mBatteryRemainingIcon.setPercentShowMode(BatteryMeterView.MODE_ON);
}
}
mDatePrivacySeparator.setLayoutParams(datePrivacySeparatorLayoutParams);
diff --git a/packages/SystemUI/src/com/android/systemui/qs/external/CustomTile.java b/packages/SystemUI/src/com/android/systemui/qs/external/CustomTile.java
index 10eea828bcb4..396eca5c1bee 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/external/CustomTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/external/CustomTile.java
@@ -46,6 +46,7 @@ import android.widget.Switch;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
+import androidx.annotation.WorkerThread;
import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
@@ -85,6 +86,7 @@ public class CustomTile extends QSTileImpl<State> implements TileChangeListener
private final IQSTileService mService;
private final TileServiceManager mServiceManager;
private final int mUser;
+ private final CustomTileStatePersister mCustomTileStatePersister;
private android.graphics.drawable.Icon mDefaultIcon;
private CharSequence mDefaultLabel;
@@ -94,6 +96,8 @@ public class CustomTile extends QSTileImpl<State> implements TileChangeListener
private boolean mIsTokenGranted;
private boolean mIsShowingDialog;
+ private final TileServiceKey mKey;
+
private CustomTile(
QSHost host,
Looper backgroundLooper,
@@ -104,7 +108,8 @@ public class CustomTile extends QSTileImpl<State> implements TileChangeListener
ActivityStarter activityStarter,
QSLogger qsLogger,
String action,
- Context userContext
+ Context userContext,
+ CustomTileStatePersister customTileStatePersister
) {
super(host, backgroundLooper, mainHandler, falsingManager, metricsLogger,
statusBarStateController, activityStarter, qsLogger);
@@ -113,15 +118,29 @@ public class CustomTile extends QSTileImpl<State> implements TileChangeListener
mTile = new Tile();
mUserContext = userContext;
mUser = mUserContext.getUserId();
- updateDefaultTileAndIcon();
+ mKey = new TileServiceKey(mComponent, mUser);
+
mServiceManager = host.getTileServices().getTileWrapper(this);
+ mService = mServiceManager.getTileService();
+ mCustomTileStatePersister = customTileStatePersister;
+ }
+
+ @Override
+ protected void handleInitialize() {
+ updateDefaultTileAndIcon();
if (mServiceManager.isToggleableTile()) {
// Replace states with BooleanState
resetStates();
}
-
- mService = mServiceManager.getTileService();
mServiceManager.setTileChangeListener(this);
+ if (mServiceManager.isActiveTile()) {
+ Tile t = mCustomTileStatePersister.readState(mKey);
+ if (t != null) {
+ applyTileState(t, /* overwriteNulls */ false);
+ mServiceManager.clearPendingBind();
+ refreshState();
+ }
+ }
}
@Override
@@ -191,7 +210,7 @@ public class CustomTile extends QSTileImpl<State> implements TileChangeListener
@Override
public void onTileChanged(ComponentName tile) {
- updateDefaultTileAndIcon();
+ mHandler.post(this::updateDefaultTileAndIcon);
}
@Override
@@ -213,16 +232,44 @@ public class CustomTile extends QSTileImpl<State> implements TileChangeListener
}
public Tile getQsTile() {
+ // TODO(b/191145007) Move to background thread safely
updateDefaultTileAndIcon();
return mTile;
}
- public void updateState(Tile tile) {
- mTile.setIcon(tile.getIcon());
- mTile.setLabel(tile.getLabel());
- mTile.setSubtitle(tile.getSubtitle());
- mTile.setContentDescription(tile.getContentDescription());
- mTile.setStateDescription(tile.getStateDescription());
+ /**
+ * Update state of {@link this#mTile} from a remote {@link TileService}.
+ * @param tile tile populated with state to apply
+ */
+ public void updateTileState(Tile tile) {
+ // This comes from a binder call IQSService.updateQsTile
+ mHandler.post(() -> handleUpdateTileState(tile));
+ }
+
+ private void handleUpdateTileState(Tile tile) {
+ applyTileState(tile, /* overwriteNulls */ true);
+ if (mServiceManager.isActiveTile()) {
+ mCustomTileStatePersister.persistState(mKey, tile);
+ }
+ }
+
+ @WorkerThread
+ private void applyTileState(Tile tile, boolean overwriteNulls) {
+ if (tile.getIcon() != null || overwriteNulls) {
+ mTile.setIcon(tile.getIcon());
+ }
+ if (tile.getLabel() != null || overwriteNulls) {
+ mTile.setLabel(tile.getLabel());
+ }
+ if (tile.getSubtitle() != null || overwriteNulls) {
+ mTile.setSubtitle(tile.getSubtitle());
+ }
+ if (tile.getContentDescription() != null || overwriteNulls) {
+ mTile.setContentDescription(tile.getContentDescription());
+ }
+ if (tile.getStateDescription() != null || overwriteNulls) {
+ mTile.setStateDescription(tile.getStateDescription());
+ }
mTile.setState(tile.getState());
}
@@ -459,6 +506,7 @@ public class CustomTile extends QSTileImpl<State> implements TileChangeListener
final StatusBarStateController mStatusBarStateController;
final ActivityStarter mActivityStarter;
final QSLogger mQSLogger;
+ final CustomTileStatePersister mCustomTileStatePersister;
Context mUserContext;
String mSpec = "";
@@ -472,7 +520,8 @@ public class CustomTile extends QSTileImpl<State> implements TileChangeListener
MetricsLogger metricsLogger,
StatusBarStateController statusBarStateController,
ActivityStarter activityStarter,
- QSLogger qsLogger
+ QSLogger qsLogger,
+ CustomTileStatePersister customTileStatePersister
) {
mQSHostLazy = hostLazy;
mBackgroundLooper = backgroundLooper;
@@ -482,6 +531,7 @@ public class CustomTile extends QSTileImpl<State> implements TileChangeListener
mStatusBarStateController = statusBarStateController;
mActivityStarter = activityStarter;
mQSLogger = qsLogger;
+ mCustomTileStatePersister = customTileStatePersister;
}
Builder setSpec(@NonNull String spec) {
@@ -509,7 +559,8 @@ public class CustomTile extends QSTileImpl<State> implements TileChangeListener
mActivityStarter,
mQSLogger,
action,
- mUserContext
+ mUserContext,
+ mCustomTileStatePersister
);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/external/CustomTileStatePersister.kt b/packages/SystemUI/src/com/android/systemui/qs/external/CustomTileStatePersister.kt
new file mode 100644
index 000000000000..021e632810f9
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/external/CustomTileStatePersister.kt
@@ -0,0 +1,122 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.qs.external
+
+import android.content.ComponentName
+import android.content.Context
+import android.service.quicksettings.Tile
+import android.util.Log
+import com.android.internal.annotations.VisibleForTesting
+import org.json.JSONException
+import org.json.JSONObject
+import javax.inject.Inject
+
+data class TileServiceKey(val componentName: ComponentName, val user: Int) {
+ private val string = "${componentName.flattenToString()}:$user"
+ override fun toString() = string
+}
+private const val STATE = "state"
+private const val LABEL = "label"
+private const val SUBTITLE = "subtitle"
+private const val CONTENT_DESCRIPTION = "content_description"
+private const val STATE_DESCRIPTION = "state_description"
+
+/**
+ * Persists and retrieves state for [CustomTile].
+ *
+ * This class will persists to a fixed [SharedPreference] file a state for a pair of [ComponentName]
+ * and user id ([TileServiceKey]).
+ *
+ * It persists the state from a [Tile] necessary to present the view in the same state when
+ * retrieved, with the exception of the icon.
+ */
+class CustomTileStatePersister @Inject constructor(context: Context) {
+ companion object {
+ private const val FILE_NAME = "custom_tiles_state"
+ }
+
+ private val sharedPreferences = context.getSharedPreferences(FILE_NAME, 0)
+
+ /**
+ * Read the state from [SharedPreferences].
+ *
+ * Returns `null` if the tile has no saved state.
+ *
+ * Any fields that have not been saved will be set to `null`
+ */
+ fun readState(key: TileServiceKey): Tile? {
+ val state = sharedPreferences.getString(key.toString(), null) ?: return null
+ return try {
+ readTileFromString(state)
+ } catch (e: JSONException) {
+ Log.e("TileServicePersistence", "Bad saved state: $state", e)
+ null
+ }
+ }
+
+ /**
+ * Persists the state into [SharedPreferences].
+ *
+ * The implementation does not store fields that are `null` or icons.
+ */
+ fun persistState(key: TileServiceKey, tile: Tile) {
+ val state = writeToString(tile)
+
+ sharedPreferences.edit().putString(key.toString(), state).apply()
+ }
+
+ /**
+ * Removes the state for a given tile, user pair.
+ *
+ * Used when the tile is removed by the user.
+ */
+ fun removeState(key: TileServiceKey) {
+ sharedPreferences.edit().remove(key.toString()).apply()
+ }
+}
+
+@VisibleForTesting
+internal fun readTileFromString(stateString: String): Tile {
+ val json = JSONObject(stateString)
+ return Tile().apply {
+ state = json.getInt(STATE)
+ label = json.getStringOrNull(LABEL)
+ subtitle = json.getStringOrNull(SUBTITLE)
+ contentDescription = json.getStringOrNull(CONTENT_DESCRIPTION)
+ stateDescription = json.getStringOrNull(STATE_DESCRIPTION)
+ }
+}
+
+// Properties with null values will not be saved to the Json string in any way. This makes sure
+// to properly retrieve a null in that case.
+private fun JSONObject.getStringOrNull(name: String): String? {
+ return if (has(name)) getString(name) else null
+}
+
+@VisibleForTesting
+internal fun writeToString(tile: Tile): String {
+ // Not storing the icon
+ return with(tile) {
+ JSONObject()
+ .put(STATE, state)
+ .put(LABEL, label)
+ .put(SUBTITLE, subtitle)
+ .put(CONTENT_DESCRIPTION, contentDescription)
+ .put(STATE_DESCRIPTION, stateDescription)
+ .toString()
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/external/TileServices.java b/packages/SystemUI/src/com/android/systemui/qs/external/TileServices.java
index 35cf2a12745e..a7cd11314d7e 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/external/TileServices.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/external/TileServices.java
@@ -204,7 +204,7 @@ public class TileServices extends IQSService.Stub {
tileServiceManager.clearPendingBind();
tileServiceManager.setLastUpdate(System.currentTimeMillis());
}
- customTile.updateState(tile);
+ customTile.updateTileState(tile);
customTile.refreshState();
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSFactoryImpl.java b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSFactoryImpl.java
index 8f7c493417ec..842fd6c62d06 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSFactoryImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSFactoryImpl.java
@@ -160,7 +160,8 @@ public class QSFactoryImpl implements QSFactory {
public QSTile createTile(String tileSpec) {
QSTileImpl tile = createTileInternal(tileSpec);
if (tile != null) {
- tile.handleStale(); // Tile was just created, must be stale.
+ tile.initialize();
+ tile.postStale(); // Tile was just created, must be stale.
}
return tile;
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileImpl.java b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileImpl.java
index a938821a343f..4616be8f7937 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileImpl.java
@@ -50,6 +50,7 @@ import androidx.lifecycle.LifecycleOwner;
import androidx.lifecycle.LifecycleRegistry;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.jank.InteractionJankMonitor;
import com.android.internal.logging.InstanceId;
import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.UiEventLogger;
@@ -158,6 +159,15 @@ public abstract class QSTileImpl<TState extends State> implements QSTile, Lifecy
*/
abstract public int getMetricsCategory();
+ /**
+ * Performs initialization of the tile
+ *
+ * Use this to perform initialization of the tile. Empty by default.
+ */
+ protected void handleInitialize() {
+
+ }
+
protected QSTileImpl(
QSHost host,
Looper backgroundLooper,
@@ -346,6 +356,15 @@ public abstract class QSTileImpl<TState extends State> implements QSTile, Lifecy
mHandler.sendEmptyMessage(H.DESTROY);
}
+ /**
+ * Schedules initialization of the tile.
+ *
+ * Should be called upon creation of the tile, before performing other operations
+ */
+ public void initialize() {
+ mHandler.sendEmptyMessage(H.INITIALIZE);
+ }
+
public TState getState() {
return mState;
}
@@ -370,6 +389,13 @@ public abstract class QSTileImpl<TState extends State> implements QSTile, Lifecy
}
/**
+ * Posts a stale message to the background thread.
+ */
+ public void postStale() {
+ mHandler.sendEmptyMessage(H.STALE);
+ }
+
+ /**
* Handles secondary click on the tile.
*
* Defaults to {@link QSTileImpl#handleClick}
@@ -389,7 +415,8 @@ public abstract class QSTileImpl<TState extends State> implements QSTile, Lifecy
*/
protected void handleLongClick(@Nullable View view) {
ActivityLaunchAnimator.Controller animationController =
- view != null ? ActivityLaunchAnimator.Controller.fromView(view) : null;
+ view != null ? ActivityLaunchAnimator.Controller.fromView(view,
+ InteractionJankMonitor.CUJ_SHADE_APP_LAUNCH_FROM_QS_TILE) : null;
mActivityStarter.postStartActivityDismissingKeyguard(getLongClickIntent(), 0,
animationController);
}
@@ -580,6 +607,7 @@ public abstract class QSTileImpl<TState extends State> implements QSTile, Lifecy
private static final int SET_LISTENING = 13;
@VisibleForTesting
protected static final int STALE = 14;
+ private static final int INITIALIZE = 15;
@VisibleForTesting
protected H(Looper looper) {
@@ -638,6 +666,9 @@ public abstract class QSTileImpl<TState extends State> implements QSTile, Lifecy
} else if (msg.what == STALE) {
name = "handleStale";
handleStale();
+ } else if (msg.what == INITIALIZE) {
+ name = "initialize";
+ handleInitialize();
} else {
throw new IllegalArgumentException("Unknown msg: " + msg.what);
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/AlarmTile.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/AlarmTile.kt
index 69d49d44f822..73d13700d61b 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/AlarmTile.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/AlarmTile.kt
@@ -11,6 +11,7 @@ import android.text.TextUtils
import android.text.format.DateFormat
import android.view.View
import androidx.annotation.VisibleForTesting
+import com.android.internal.jank.InteractionJankMonitor
import com.android.internal.logging.MetricsLogger
import com.android.systemui.R
import com.android.systemui.animation.ActivityLaunchAnimator
@@ -70,7 +71,10 @@ class AlarmTile @Inject constructor(
}
override fun handleClick(view: View?) {
- val animationController = view?.let { ActivityLaunchAnimator.Controller.fromView(it) }
+ val animationController = view?.let {
+ ActivityLaunchAnimator.Controller.fromView(
+ it, InteractionJankMonitor.CUJ_SHADE_APP_LAUNCH_FROM_QS_TILE)
+ }
val pendingIntent = lastAlarmInfo?.showIntent
if (pendingIntent != null) {
mActivityStarter.postStartActivityDismissingKeyguard(pendingIntent, animationController)
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/DeviceControlsTile.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/DeviceControlsTile.kt
index 6d3190ffa725..f66b7226fbae 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/DeviceControlsTile.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/DeviceControlsTile.kt
@@ -22,6 +22,7 @@ import android.os.Handler
import android.os.Looper
import android.service.quicksettings.Tile
import android.view.View
+import com.android.internal.jank.InteractionJankMonitor
import com.android.internal.logging.MetricsLogger
import com.android.systemui.R
import com.android.systemui.animation.ActivityLaunchAnimator
@@ -106,7 +107,8 @@ class DeviceControlsTile @Inject constructor(
putExtra(ControlsUiController.EXTRA_ANIMATE, true)
}
val animationController = view?.let {
- ActivityLaunchAnimator.Controller.fromView(it)
+ ActivityLaunchAnimator.Controller.fromView(
+ it, InteractionJankMonitor.CUJ_SHADE_APP_LAUNCH_FROM_QS_TILE)
}
mUiHandler.post {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/QuickAccessWalletTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/QuickAccessWalletTile.java
index 0e4434baa0e8..98cd88af232f 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/QuickAccessWalletTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/QuickAccessWalletTile.java
@@ -37,6 +37,7 @@ import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.jank.InteractionJankMonitor;
import com.android.internal.logging.MetricsLogger;
import com.android.systemui.R;
import com.android.systemui.animation.ActivityLaunchAnimator;
@@ -120,7 +121,8 @@ public class QuickAccessWalletTile extends QSTileImpl<QSTile.State> {
@Override
protected void handleClick(@Nullable View view) {
ActivityLaunchAnimator.Controller animationController =
- view == null ? null : ActivityLaunchAnimator.Controller.fromView(view);
+ view == null ? null : ActivityLaunchAnimator.Controller.fromView(view,
+ InteractionJankMonitor.CUJ_SHADE_APP_LAUNCH_FROM_QS_TILE);
mUiHandler.post(() -> {
if (mSelectedCard != null) {
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/CropView.java b/packages/SystemUI/src/com/android/systemui/screenshot/CropView.java
index 9e11451afa06..0a60f6da159e 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/CropView.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/CropView.java
@@ -62,6 +62,7 @@ public class CropView extends View {
private final float mCropTouchMargin;
private final Paint mShadePaint;
private final Paint mHandlePaint;
+ private final Paint mContainerBackgroundPaint;
// Crop rect with each element represented as [0,1] along its proper axis.
private RectF mCrop = new RectF(0, 0, 1, 1);
@@ -79,6 +80,9 @@ public class CropView extends View {
// The allowable values for the current boundary being dragged
private Range<Float> mMotionRange;
+ // Value [0,1] indicating progress in animateEntrance()
+ private float mEntranceInterpolation = 1f;
+
private CropInteractionListener mCropInteractionListener;
private final ExploreByTouchHelper mExploreByTouchHelper;
@@ -92,6 +96,9 @@ public class CropView extends View {
attrs, R.styleable.CropView, 0, 0);
mShadePaint = new Paint();
mShadePaint.setColor(t.getColor(R.styleable.CropView_scrimColor, Color.TRANSPARENT));
+ mContainerBackgroundPaint = new Paint();
+ mContainerBackgroundPaint.setColor(t.getColor(R.styleable.CropView_containerBackgroundColor,
+ Color.TRANSPARENT));
mHandlePaint = new Paint();
mHandlePaint.setColor(t.getColor(R.styleable.CropView_handleColor, Color.BLACK));
mHandlePaint.setStrokeCap(Paint.Cap.ROUND);
@@ -125,10 +132,22 @@ public class CropView extends View {
@Override
public void onDraw(Canvas canvas) {
super.onDraw(canvas);
- drawShade(canvas, 0, 0, 1, mCrop.top);
- drawShade(canvas, 0, mCrop.bottom, 1, 1);
+ // Top and bottom borders reflect the boundary between the (scrimmed) image and the
+ // opaque container background. This is only meaningful during an entrance transition.
+ float topBorder = MathUtils.lerp(mCrop.top, 0, mEntranceInterpolation);
+ float bottomBorder = MathUtils.lerp(mCrop.bottom, 1, mEntranceInterpolation);
+ drawShade(canvas, 0, topBorder, 1, mCrop.top);
+ drawShade(canvas, 0, mCrop.bottom, 1, bottomBorder);
drawShade(canvas, 0, mCrop.top, mCrop.left, mCrop.bottom);
drawShade(canvas, mCrop.right, mCrop.top, 1, mCrop.bottom);
+
+ // Entrance transition expects the crop bounds to be full width, so we only draw container
+ // background on the top and bottom.
+ drawContainerBackground(canvas, 0, 0, 1, topBorder);
+ drawContainerBackground(canvas, 0, bottomBorder, 1, 1);
+
+ mHandlePaint.setAlpha((int) (mEntranceInterpolation * 255));
+
drawHorizontalHandle(canvas, mCrop.top, /* draw the handle tab up */ true);
drawHorizontalHandle(canvas, mCrop.bottom, /* draw the handle tab down */ false);
drawVerticalHandle(canvas, mCrop.left, /* left */ true);
@@ -282,6 +301,22 @@ public class CropView extends View {
}
/**
+ * Fade in crop bounds, animate reveal of cropped-out area from current crop bounds.
+ */
+ public void animateEntrance() {
+ mEntranceInterpolation = 0;
+ ValueAnimator animator = new ValueAnimator();
+ animator.addUpdateListener(animation -> {
+ mEntranceInterpolation = animation.getAnimatedFraction();
+ invalidate();
+ });
+ animator.setFloatValues(0f, 1f);
+ animator.setDuration(750);
+ animator.setInterpolator(new FastOutSlowInInterpolator());
+ animator.start();
+ }
+
+ /**
* Set additional top and bottom padding for the image being cropped (used when the
* corresponding ImageView doesn't take the full height).
*/
@@ -369,6 +404,13 @@ public class CropView extends View {
fractionToVerticalPixels(bottom), mShadePaint);
}
+ private void drawContainerBackground(Canvas canvas, float left, float top, float right,
+ float bottom) {
+ canvas.drawRect(fractionToHorizontalPixels(left), fractionToVerticalPixels(top),
+ fractionToHorizontalPixels(right),
+ fractionToVerticalPixels(bottom), mContainerBackgroundPaint);
+ }
+
private void drawHorizontalHandle(Canvas canvas, float frac, boolean handleTabUp) {
int y = fractionToVerticalPixels(frac);
canvas.drawLine(fractionToHorizontalPixels(mCrop.left), y,
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/LongScreenshotActivity.java b/packages/SystemUI/src/com/android/systemui/screenshot/LongScreenshotActivity.java
index d5b4032b1c0f..25ec1d74008e 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/LongScreenshotActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/LongScreenshotActivity.java
@@ -192,7 +192,6 @@ public class LongScreenshotActivity extends Activity {
mLongScreenshot = longScreenshot;
Drawable drawable = mLongScreenshot.getDrawable();
mPreview.setImageDrawable(drawable);
- mCropView.setVisibility(View.VISIBLE);
mMagnifierView.setDrawable(mLongScreenshot.getDrawable(),
mLongScreenshot.getWidth(), mLongScreenshot.getHeight());
// Original boundaries go from the image tile set's y=0 to y=pageSize, so
@@ -219,10 +218,12 @@ public class LongScreenshotActivity extends Activity {
public void onTransitionEnd(Transition transition) {
super.onTransitionEnd(transition);
mPreview.animate().alpha(1f);
- mCropView.animateBoundaryTo(
+ mCropView.setBoundaryPosition(
CropView.CropBoundary.TOP, topFraction);
- mCropView.animateBoundaryTo(
+ mCropView.setBoundaryPosition(
CropView.CropBoundary.BOTTOM, bottomFraction);
+ mCropView.animateEntrance();
+ mCropView.setVisibility(View.VISIBLE);
setButtonsEnabled(true);
mEnterTransitionView.setVisibility(View.GONE);
}
@@ -250,6 +251,7 @@ public class LongScreenshotActivity extends Activity {
Log.d(TAG, "onCachedImageLoaded(imageResult=" + imageResult + ")");
BitmapDrawable drawable = new BitmapDrawable(getResources(), imageResult.bitmap);
mPreview.setImageDrawable(drawable);
+ mPreview.setAlpha(1f);
mMagnifierView.setDrawable(drawable, imageResult.bitmap.getWidth(),
imageResult.bitmap.getHeight());
mCropView.setVisibility(View.VISIBLE);
@@ -476,19 +478,21 @@ public class LongScreenshotActivity extends Activity {
params.height = boundaries.height();
mTransitionView.setLayoutParams(params);
- ConstraintLayout.LayoutParams enterTransitionParams =
- (ConstraintLayout.LayoutParams) mEnterTransitionView.getLayoutParams();
- float topFraction = Math.max(0,
- -mLongScreenshot.getTop() / (float) mLongScreenshot.getHeight());
- enterTransitionParams.width = (int) (scale * drawable.getIntrinsicWidth());
- enterTransitionParams.height = (int) (scale * mLongScreenshot.getPageHeight());
- mEnterTransitionView.setLayoutParams(enterTransitionParams);
-
- Matrix matrix = new Matrix();
- matrix.setScale(scale, scale);
- matrix.postTranslate(0, -scale * drawable.getIntrinsicHeight() * topFraction);
- mEnterTransitionView.setImageMatrix(matrix);
- mEnterTransitionView.setTranslationY(
- topFraction * previewHeight + mPreview.getPaddingTop() + extraPadding);
+ if (mLongScreenshot != null) {
+ ConstraintLayout.LayoutParams enterTransitionParams =
+ (ConstraintLayout.LayoutParams) mEnterTransitionView.getLayoutParams();
+ float topFraction = Math.max(0,
+ -mLongScreenshot.getTop() / (float) mLongScreenshot.getHeight());
+ enterTransitionParams.width = (int) (scale * drawable.getIntrinsicWidth());
+ enterTransitionParams.height = (int) (scale * mLongScreenshot.getPageHeight());
+ mEnterTransitionView.setLayoutParams(enterTransitionParams);
+
+ Matrix matrix = new Matrix();
+ matrix.setScale(scale, scale);
+ matrix.postTranslate(0, -scale * drawable.getIntrinsicHeight() * topFraction);
+ mEnterTransitionView.setImageMatrix(matrix);
+ mEnterTransitionView.setTranslationY(
+ topFraction * previewHeight + mPreview.getPaddingTop() + extraPadding);
+ }
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java
index 91a0e6fedef8..0bb702f6c9e4 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java
@@ -112,6 +112,7 @@ public class KeyguardIndicationController implements KeyguardStateController.Cal
private final KeyguardUpdateMonitor mKeyguardUpdateMonitor;
private ViewGroup mIndicationArea;
private KeyguardIndicationTextView mTopIndicationView;
+ private KeyguardIndicationTextView mLockScreenIndicationView;
private final IBatteryStats mBatteryInfo;
private final SettableWakeLock mWakeLock;
private final DockManager mDockManager;
@@ -208,17 +209,21 @@ public class KeyguardIndicationController implements KeyguardStateController.Cal
mKeyguardUpdateMonitor.registerCallback(mTickReceiver);
mStatusBarStateController.addCallback(mStatusBarStateListener);
mKeyguardStateController.addCallback(this);
+
+ mStatusBarStateListener.onDozingChanged(mStatusBarStateController.isDozing());
}
public void setIndicationArea(ViewGroup indicationArea) {
mIndicationArea = indicationArea;
mTopIndicationView = indicationArea.findViewById(R.id.keyguard_indication_text);
+ mLockScreenIndicationView = indicationArea.findViewById(
+ R.id.keyguard_indication_text_bottom);
mInitialTextColorState = mTopIndicationView != null
? mTopIndicationView.getTextColors() : ColorStateList.valueOf(Color.WHITE);
mRotateTextViewController = new KeyguardIndicationRotateTextViewController(
- indicationArea.findViewById(R.id.keyguard_indication_text_bottom),
- mExecutor,
- mStatusBarStateController);
+ mLockScreenIndicationView,
+ mExecutor,
+ mStatusBarStateController);
updateIndication(false /* animate */);
updateDisclosure();
if (mBroadcastReceiver == null) {
@@ -630,6 +635,7 @@ public class KeyguardIndicationController implements KeyguardStateController.Cal
// should be shown based on user or device state
// AoD
if (mDozing) {
+ mLockScreenIndicationView.setVisibility(View.GONE);
mTopIndicationView.setVisibility(VISIBLE);
// When dozing we ignore any text color and use white instead, because
// colors can be hard to read in low brightness.
@@ -659,6 +665,8 @@ public class KeyguardIndicationController implements KeyguardStateController.Cal
// LOCK SCREEN
mTopIndicationView.setVisibility(GONE);
+ mTopIndicationView.setText(null);
+ mLockScreenIndicationView.setVisibility(View.VISIBLE);
updateIndications(animate, KeyguardUpdateMonitor.getCurrentUser());
}
@@ -914,7 +922,8 @@ public class KeyguardIndicationController implements KeyguardStateController.Cal
} else if (mStatusBarKeyguardViewManager.isBouncerShowing()) {
mStatusBarKeyguardViewManager.showBouncerMessage(errString, mInitialTextColorState);
} else if (mKeyguardUpdateMonitor.isScreenOn()) {
- showTransientIndication(errString);
+ showTransientIndication(errString, /* isError */ true,
+ /* hideOnScreenOff */ true);
// We want to keep this message around in case the screen was off
hideTransientIndicationDelayed(HIDE_DELAY_MS);
} else {
@@ -1032,9 +1041,8 @@ public class KeyguardIndicationController implements KeyguardStateController.Cal
if (mHideTransientMessageOnScreenOff && mDozing) {
hideTransientIndication();
- } else {
- updateIndication(false);
}
+ updateIndication(false);
}
};
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/LightRevealScrim.kt b/packages/SystemUI/src/com/android/systemui/statusbar/LightRevealScrim.kt
index 6a5f001ac2ee..ec648ad519a3 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/LightRevealScrim.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/LightRevealScrim.kt
@@ -93,10 +93,10 @@ class CircleReveal(
val endRadius: Float
) : LightRevealEffect {
override fun setRevealAmountOnScrim(amount: Float, scrim: LightRevealScrim) {
- val interpolatedAmount = Interpolators.FAST_OUT_SLOW_IN.getInterpolation(amount)
- val fadeAmount =
- LightRevealEffect.getPercentPastThreshold(interpolatedAmount, 0.75f)
- val radius = startRadius + ((endRadius - startRadius) * interpolatedAmount)
+ // reveal amount updates already have an interpolator, so we intentionally use the
+ // non-interpolated amount
+ val fadeAmount = LightRevealEffect.getPercentPastThreshold(amount, 0.5f)
+ val radius = startRadius + ((endRadius - startRadius) * amount)
scrim.revealGradientEndColorAlpha = 1f - fadeAmount
scrim.setRevealGradientBounds(
centerX - radius /* left */,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarStateControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarStateControllerImpl.java
index 9f59023f1890..f8a1ff879e72 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarStateControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarStateControllerImpl.java
@@ -145,11 +145,11 @@ public class StatusBarStateControllerImpl implements SysuiStatusBarStateControll
}
@Override
- public boolean setState(int state) {
+ public boolean setState(int state, boolean force) {
if (state > MAX_STATE || state < MIN_STATE) {
throw new IllegalArgumentException("Invalid state " + state);
}
- if (state == mState) {
+ if (!force && state == mState) {
return false;
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/SysuiStatusBarStateController.java b/packages/SystemUI/src/com/android/systemui/statusbar/SysuiStatusBarStateController.java
index b6d6ed53b681..73f3d90bd4f8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/SysuiStatusBarStateController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/SysuiStatusBarStateController.java
@@ -59,7 +59,19 @@ public interface SysuiStatusBarStateController extends StatusBarStateController
* @param state see {@link StatusBarState} for valid options
* @return {@code true} if the state changed, else {@code false}
*/
- boolean setState(int state);
+ default boolean setState(int state) {
+ return setState(state, false /* force */);
+ }
+
+ /**
+ * Update the status bar state
+ * @param state see {@link StatusBarState} for valid options
+ * @param force whether to set the state even if it's the same as the current state. This will
+ * dispatch the state to all StatusBarStateListeners, ensuring that all listening
+ * components are reset to this state.
+ * @return {@code true} if the state was changed or set forcefully
+ */
+ boolean setState(int state, boolean force);
/**
* Update the dozing state from {@link StatusBar}'s perspective
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/events/SystemStatusAnimationScheduler.kt b/packages/SystemUI/src/com/android/systemui/statusbar/events/SystemStatusAnimationScheduler.kt
index 8479b30c3a75..f30010cf4d1c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/events/SystemStatusAnimationScheduler.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/events/SystemStatusAnimationScheduler.kt
@@ -383,10 +383,18 @@ const val ANIMATING_OUT = 3
const val SHOWING_PERSISTENT_DOT = 4
private const val TAG = "SystemStatusAnimationScheduler"
-private const val DELAY: Long = 100
-private const val DISPLAY_LENGTH = 5000L
-private const val ENTRANCE_ANIM_LENGTH = 500L
-private const val CHIP_ANIM_LENGTH = 500L
+private const val DELAY = 0L
+
+/**
+ * The total time spent animation should be 1500ms. The entrance animation is how much time
+ * we give to the system to animate system elements out of the way. Total chip animation length
+ * will be equivalent to 2*chip_anim_length + display_length
+ */
+private const val ENTRANCE_ANIM_LENGTH = 250L
+private const val CHIP_ANIM_LENGTH = 250L
+// 1s + entrance time + chip anim_length
+private const val DISPLAY_LENGTH = 1500L
+
private const val MIN_UPTIME: Long = 5 * 1000
private const val DEBUG = false
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationClicker.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationClicker.java
index aef01e9bd811..0fb1c54bb150 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationClicker.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationClicker.java
@@ -71,11 +71,11 @@ public final class NotificationClicker implements View.OnClickListener {
// Check if the notification is displaying the menu, if so slide notification back
if (isMenuVisible(row)) {
mLogger.logMenuVisible(entry);
- row.animateTranslateNotification(0);
+ row.animateResetTranslation();
return;
} else if (row.isChildInGroup() && isMenuVisible(row.getNotificationParent())) {
mLogger.logParentMenuVisible(entry);
- row.getNotificationParent().animateTranslateNotification(0);
+ row.getNotificationParent().animateResetTranslation();
return;
} else if (row.isSummaryWithChildren() && row.areChildrenExpanded()) {
// We never want to open the app directly if the user clicks in between
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationWakeUpCoordinator.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationWakeUpCoordinator.kt
index 760bee21b0d1..b0a7767accfc 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationWakeUpCoordinator.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationWakeUpCoordinator.kt
@@ -264,7 +264,7 @@ class NotificationWakeUpCoordinator @Inject constructor(
}
override fun onStateChanged(newState: Int) {
- if (unlockedScreenOffAnimationController.shouldPlayScreenOffAnimation()) {
+ if (dozeParameters.shouldControlUnlockedScreenOff()) {
if (unlockedScreenOffAnimationController.isScreenOffAnimationPlaying() &&
state == StatusBarState.KEYGUARD &&
newState == StatusBarState.SHADE) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationView.java
index 413662447028..c24c2be3faa3 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationView.java
@@ -330,30 +330,6 @@ public abstract class ActivatableNotificationView extends ExpandableOutlineView
}
}
- @Override
- public void setDistanceToTopRoundness(float distanceToTopRoundness) {
- super.setDistanceToTopRoundness(distanceToTopRoundness);
- mBackgroundNormal.setDistanceToTopRoundness(distanceToTopRoundness);
- }
-
- /** Sets whether this view is the last notification in a section. */
- @Override
- public void setLastInSection(boolean lastInSection) {
- if (lastInSection != mLastInSection) {
- super.setLastInSection(lastInSection);
- mBackgroundNormal.setLastInSection(lastInSection);
- }
- }
-
- /** Sets whether this view is the first notification in a section. */
- @Override
- public void setFirstInSection(boolean firstInSection) {
- if (firstInSection != mFirstInSection) {
- super.setFirstInSection(firstInSection);
- mBackgroundNormal.setFirstInSection(firstInSection);
- }
- }
-
/**
* Set an override tint color that is used for the background.
*
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
index 6fd556763943..ba28dc59def4 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
@@ -846,8 +846,6 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
updateClickAndFocus();
if (mNotificationParent != null) {
setOverrideTintColor(NO_COLOR, 0.0f);
- // Let's reset the distance to top roundness, as this isn't applied to group children
- setDistanceToTopRoundness(NO_ROUNDNESS);
mNotificationParent.updateBackgroundForGroupState();
}
updateBackgroundClipping();
@@ -876,7 +874,7 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
@Override
protected boolean handleSlideBack() {
if (mMenuRow != null && mMenuRow.isMenuVisible()) {
- animateTranslateNotification(0 /* targetLeft */);
+ animateResetTranslation();
return true;
}
return false;
@@ -1713,21 +1711,17 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
mChildrenContainer.setContainingNotification(ExpandableNotificationRow.this);
mChildrenContainer.onNotificationUpdated();
- if (mShouldTranslateContents) {
- mTranslateableViews.add(mChildrenContainer);
- }
+ mTranslateableViews.add(mChildrenContainer);
});
- if (mShouldTranslateContents) {
- // Add the views that we translate to reveal the menu
- mTranslateableViews = new ArrayList<>();
- for (int i = 0; i < getChildCount(); i++) {
- mTranslateableViews.add(getChildAt(i));
- }
- // Remove views that don't translate
- mTranslateableViews.remove(mChildrenContainerStub);
- mTranslateableViews.remove(mGutsStub);
+ // Add the views that we translate to reveal the menu
+ mTranslateableViews = new ArrayList<>();
+ for (int i = 0; i < getChildCount(); i++) {
+ mTranslateableViews.add(getChildAt(i));
}
+ // Remove views that don't translate
+ mTranslateableViews.remove(mChildrenContainerStub);
+ mTranslateableViews.remove(mGutsStub);
}
private void doLongClickCallback() {
@@ -1805,7 +1799,7 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
mTranslateAnim.cancel();
}
- if (!mShouldTranslateContents) {
+ if (mDismissUsingRowTranslationX) {
setTranslationX(0);
} else if (mTranslateableViews != null) {
for (int i = 0; i < mTranslateableViews.size(); i++) {
@@ -1867,23 +1861,47 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
return mPrivateLayout.getActiveRemoteInputText();
}
- public void animateTranslateNotification(final float leftTarget) {
+ /**
+ * Reset the translation with an animation.
+ */
+ public void animateResetTranslation() {
if (mTranslateAnim != null) {
mTranslateAnim.cancel();
}
- mTranslateAnim = getTranslateViewAnimator(leftTarget, null /* updateListener */);
+ mTranslateAnim = getTranslateViewAnimator(0, null /* updateListener */);
if (mTranslateAnim != null) {
mTranslateAnim.start();
}
}
+ /**
+ * Set the dismiss behavior of the view.
+ * @param usingRowTranslationX {@code true} if the view should translate using regular
+ * translationX, otherwise the contents will be
+ * translated.
+ */
+ @Override
+ public void setDismissUsingRowTranslationX(boolean usingRowTranslationX) {
+ if (usingRowTranslationX != mDismissUsingRowTranslationX) {
+ // In case we were already transitioning, let's switch over!
+ float previousTranslation = getTranslation();
+ if (previousTranslation != 0) {
+ setTranslation(0);
+ }
+ super.setDismissUsingRowTranslationX(usingRowTranslationX);
+ if (previousTranslation != 0) {
+ setTranslation(previousTranslation);
+ }
+ }
+ }
+
@Override
public void setTranslation(float translationX) {
invalidate();
if (isBlockingHelperShowingAndTranslationFinished()) {
mGuts.setTranslationX(translationX);
return;
- } else if (!mShouldTranslateContents) {
+ } else if (mDismissUsingRowTranslationX) {
setTranslationX(translationX);
} else if (mTranslateableViews != null) {
// Translate the group of views
@@ -1907,7 +1925,7 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
@Override
public float getTranslation() {
- if (!mShouldTranslateContents) {
+ if (mDismissUsingRowTranslationX) {
return getTranslationX();
}
@@ -2898,7 +2916,11 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
float y = event.getY();
NotificationViewWrapper wrapper = getVisibleNotificationViewWrapper();
NotificationHeaderView header = wrapper == null ? null : wrapper.getNotificationHeader();
- if (header != null && header.isInTouchRect(x - getTranslation(), y)) {
+ // the extra translation only needs to be added, if we're translating the notification
+ // contents, otherwise the motionEvent is already at the right place due to the
+ // touch event system.
+ float translation = !mDismissUsingRowTranslationX ? getTranslation() : 0;
+ if (header != null && header.isInTouchRect(x - translation, y)) {
return true;
}
if ((!mIsSummaryWithChildren || shouldShowPublic())
@@ -3037,24 +3059,6 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
}
@Override
- public boolean topAmountNeedsClipping() {
- if (isGroupExpanded()) {
- return true;
- }
- if (isGroupExpansionChanging()) {
- return true;
- }
- if (getShowingLayout().shouldClipToRounding(true /* topRounded */,
- false /* bottomRounded */)) {
- return true;
- }
- if (mGuts != null && mGuts.getAlpha() != 0.0f) {
- return true;
- }
- return false;
- }
-
- @Override
protected boolean childNeedsClipping(View child) {
if (child instanceof NotificationContentView) {
NotificationContentView contentView = (NotificationContentView) child;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableOutlineView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableOutlineView.java
index 5134c62dc182..d58fe3b3c4a3 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableOutlineView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableOutlineView.java
@@ -71,21 +71,19 @@ public abstract class ExpandableOutlineView extends ExpandableView {
private int mBackgroundTop;
/**
- * {@code true} if the children views of the {@link ExpandableOutlineView} are translated when
+ * {@code false} if the children views of the {@link ExpandableOutlineView} are translated when
* it is moved. Otherwise, the translation is set on the {@code ExpandableOutlineView} itself.
*/
- protected boolean mShouldTranslateContents;
- private boolean mTopAmountRounded;
- private float mDistanceToTopRoundness = -1;
+ protected boolean mDismissUsingRowTranslationX = true;
private float[] mTmpCornerRadii = new float[8];
private final ViewOutlineProvider mProvider = new ViewOutlineProvider() {
@Override
public void getOutline(View view, Outline outline) {
if (!mCustomOutline && getCurrentTopRoundness() == 0.0f
- && getCurrentBottomRoundness() == 0.0f && !mAlwaysRoundBothCorners
- && !mTopAmountRounded) {
- int translation = mShouldTranslateContents ? (int) getTranslation() : 0;
+ && getCurrentBottomRoundness() == 0.0f && !mAlwaysRoundBothCorners) {
+ // Only when translating just the contents, does the outline need to be shifted.
+ int translation = !mDismissUsingRowTranslationX ? (int) getTranslation() : 0;
int left = Math.max(translation, 0);
int top = mClipTopAmount + mBackgroundTop;
int right = getWidth() + Math.min(translation, 0);
@@ -110,7 +108,9 @@ public abstract class ExpandableOutlineView extends ExpandableView {
float topRoundness = mAlwaysRoundBothCorners
? mOutlineRadius : getCurrentBackgroundRadiusTop();
if (!mCustomOutline) {
- int translation = mShouldTranslateContents && !ignoreTranslation
+ // The outline just needs to be shifted if we're translating the contents. Otherwise
+ // it's already in the right place.
+ int translation = !mDismissUsingRowTranslationX && !ignoreTranslation
? (int) getTranslation() : 0;
int halfExtraWidth = (int) (mExtraWidthForClipping / 2.0f);
left = Math.max(translation, 0) - halfExtraWidth;
@@ -168,33 +168,15 @@ public abstract class ExpandableOutlineView extends ExpandableView {
@Override
protected boolean drawChild(Canvas canvas, View child, long drawingTime) {
canvas.save();
- Path intersectPath = null;
- if (mTopAmountRounded && topAmountNeedsClipping()) {
- int left = (int) (- mExtraWidthForClipping / 2.0f);
- int top = (int) (mClipTopAmount - mDistanceToTopRoundness);
- int right = getWidth() + (int) (mExtraWidthForClipping + left);
- int bottom = (int) Math.max(mMinimumHeightForClipping,
- Math.max(getActualHeight() - mClipBottomAmount, top + mOutlineRadius));
- getRoundedRectPath(left, top, right, bottom, mOutlineRadius, 0.0f, mClipPath);
- intersectPath = mClipPath;
- }
- boolean clipped = false;
if (childNeedsClipping(child)) {
Path clipPath = getCustomClipPath(child);
if (clipPath == null) {
clipPath = getClipPath(false /* ignoreTranslation */);
}
if (clipPath != null) {
- if (intersectPath != null) {
- clipPath.op(intersectPath, Path.Op.INTERSECT);
- }
canvas.clipPath(clipPath);
- clipped = true;
}
}
- if (!clipped && intersectPath != null) {
- canvas.clipPath(intersectPath);
- }
boolean result = super.drawChild(canvas, child, drawingTime);
canvas.restore();
return result;
@@ -212,32 +194,19 @@ public abstract class ExpandableOutlineView extends ExpandableView {
invalidate();
}
- @Override
- public void setDistanceToTopRoundness(float distanceToTopRoundness) {
- super.setDistanceToTopRoundness(distanceToTopRoundness);
- if (distanceToTopRoundness != mDistanceToTopRoundness) {
- mTopAmountRounded = distanceToTopRoundness >= 0;
- mDistanceToTopRoundness = distanceToTopRoundness;
- applyRoundness();
- }
- }
-
protected boolean childNeedsClipping(View child) {
return false;
}
- public boolean topAmountNeedsClipping() {
- return true;
- }
-
protected boolean isClippingNeeded() {
- return mAlwaysRoundBothCorners || mCustomOutline || getTranslation() != 0 ;
+ // When translating the contents instead of the overall view, we need to make sure we clip
+ // rounded to the contents.
+ boolean forTranslation = getTranslation() != 0 && !mDismissUsingRowTranslationX;
+ return mAlwaysRoundBothCorners || mCustomOutline || forTranslation;
}
private void initDimens() {
Resources res = getResources();
- mShouldTranslateContents =
- res.getBoolean(R.bool.config_translateNotificationContentsOnSwipe);
mOutlineRadius = res.getDimension(R.dimen.notification_shadow_radius);
mAlwaysRoundBothCorners = res.getBoolean(R.bool.config_clipNotificationsToOutline);
if (!mAlwaysRoundBothCorners) {
@@ -272,11 +241,6 @@ public abstract class ExpandableOutlineView extends ExpandableView {
}
public float getCurrentBackgroundRadiusTop() {
- // If this view is top amount notification view, it should always has round corners on top.
- // It will be applied with applyRoundness()
- if (mTopAmountRounded) {
- return mOutlineRadius;
- }
return getCurrentTopRoundness() * mOutlineRadius;
}
@@ -382,9 +346,25 @@ public abstract class ExpandableOutlineView extends ExpandableView {
}
}
+ /**
+ * Set the dismiss behavior of the view.
+ * @param usingRowTranslationX {@code true} if the view should translate using regular
+ * translationX, otherwise the contents will be
+ * translated.
+ */
+ public void setDismissUsingRowTranslationX(boolean usingRowTranslationX) {
+ mDismissUsingRowTranslationX = usingRowTranslationX;
+ }
+
@Override
public int getOutlineTranslation() {
- return mCustomOutline ? mOutlineRect.left : (int) getTranslation();
+ if (mCustomOutline) {
+ return mOutlineRect.left;
+ }
+ if (mDismissUsingRowTranslationX) {
+ return 0;
+ }
+ return (int) getTranslation();
}
public void updateOutline() {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableView.java
index 763d197847c3..8b0764b1c313 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableView.java
@@ -46,7 +46,6 @@ import java.util.List;
public abstract class ExpandableView extends FrameLayout implements Dumpable {
private static final String TAG = "ExpandableView";
- public static final float NO_ROUNDNESS = -1;
protected OnHeightChangedListener mOnHeightChangedListener;
private int mActualHeight;
protected int mClipTopAmount;
@@ -192,14 +191,6 @@ public abstract class ExpandableView extends FrameLayout implements Dumpable {
}
}
- /**
- * Set the distance to the top roundness, from where we should start clipping a value above
- * or equal to 0 is the effective distance, and if a value below 0 is received, there should
- * be no clipping.
- */
- public void setDistanceToTopRoundness(float distanceToTopRoundness) {
- }
-
public void setActualHeight(int actualHeight) {
setActualHeight(actualHeight, true /* notifyListeners */);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationBackgroundView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationBackgroundView.java
index 4b1f679b8851..754de580cd61 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationBackgroundView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationBackgroundView.java
@@ -42,10 +42,8 @@ public class NotificationBackgroundView extends View {
private int mActualHeight;
private int mClipBottomAmount;
private int mTintColor;
- private float[] mCornerRadii = new float[8];
+ private final float[] mCornerRadii = new float[8];
private boolean mBottomIsRounded;
- private boolean mLastInSection;
- private boolean mFirstInSection;
private int mBackgroundTop;
private boolean mBottomAmountClips = true;
private boolean mExpandAnimationRunning;
@@ -53,9 +51,6 @@ public class NotificationBackgroundView extends View {
private int mDrawableAlpha = 255;
private boolean mIsPressedAllowed;
- private boolean mTopAmountRounded;
- private float mDistanceToTopRoundness;
-
public NotificationBackgroundView(Context context, AttributeSet attrs) {
super(context, attrs);
mDontModifyCorners = getResources().getBoolean(
@@ -90,15 +85,6 @@ public class NotificationBackgroundView extends View {
left = (int) ((getWidth() - mActualWidth) / 2.0f);
right = (int) (left + mActualWidth);
}
- if (mTopAmountRounded) {
- int clipTop = (int) (mClipTopAmount - mDistanceToTopRoundness);
- if (clipTop >= 0 || !mFirstInSection) {
- top += clipTop;
- }
- if (clipTop >= 0 && !mLastInSection) {
- bottom += clipTop;
- }
- }
drawable.setBounds(left, top, right, bottom);
drawable.draw(canvas);
}
@@ -180,14 +166,6 @@ public class NotificationBackgroundView extends View {
invalidate();
}
- public void setDistanceToTopRoundness(float distanceToTopRoundness) {
- if (distanceToTopRoundness != mDistanceToTopRoundness) {
- mTopAmountRounded = distanceToTopRoundness >= 0;
- mDistanceToTopRoundness = distanceToTopRoundness;
- invalidate();
- }
- }
-
@Override
public boolean hasOverlappingRendering() {
@@ -246,18 +224,6 @@ public class NotificationBackgroundView extends View {
}
}
- /** Sets whether this background belongs to the last notification in a section. */
- public void setLastInSection(boolean lastInSection) {
- mLastInSection = lastInSection;
- invalidate();
- }
-
- /** Sets whether this background belongs to the first notification in a section. */
- public void setFirstInSection(boolean firstInSection) {
- mFirstInSection = firstInSection;
- invalidate();
- }
-
private void updateBackgroundRadii() {
if (mDontModifyCorners) {
return;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/AmbientState.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/AmbientState.java
index 0c86262d9037..6822d24947c3 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/AmbientState.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/AmbientState.java
@@ -75,7 +75,6 @@ public class AmbientState {
private int mExpandAnimationTopChange;
private ExpandableNotificationRow mExpandingNotification;
private float mHideAmount;
- private float mNotificationScrimTop;
private boolean mAppearing;
private float mPulseHeight = MAX_PULSE_HEIGHT;
private float mDozeAmount = 0.0f;
@@ -256,20 +255,6 @@ public class AmbientState {
return mHideAmount;
}
- /**
- * Set y position of top of notifications background scrim, relative to top of screen.
- */
- public void setNotificationScrimTop(float notificationScrimTop) {
- mNotificationScrimTop = notificationScrimTop;
- }
-
- /**
- * @return Y position of top of notifications background scrim, relative to top of screen.
- */
- public float getNotificationScrimTop() {
- return mNotificationScrimTop;
- }
-
public void setHideSensitive(boolean hideSensitive) {
mHideSensitive = hideSensitive;
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
index f90b4c079c50..d79c57565dcd 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
@@ -40,6 +40,7 @@ import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Outline;
import android.graphics.Paint;
+import android.graphics.Path;
import android.graphics.PointF;
import android.graphics.Rect;
import android.os.Bundle;
@@ -424,13 +425,19 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
private final Rect mBackgroundAnimationRect = new Rect();
private ArrayList<BiConsumer<Float, Float>> mExpandedHeightListeners = new ArrayList<>();
private int mHeadsUpInset;
+
+ /**
+ * The position of the scroll boundary relative to this view. This is where the notifications
+ * stop scrolling and will start to clip instead.
+ */
+ private int mQsScrollBoundaryPosition;
private HeadsUpAppearanceController mHeadsUpAppearanceController;
private final Rect mTmpRect = new Rect();
private DismissListener mDismissListener;
private DismissAllAnimationListener mDismissAllAnimationListener;
private NotificationRemoteInputManager mRemoteInputManager;
private ShadeController mShadeController;
- private Runnable mOnStackYChanged;
+ private Consumer<Boolean> mOnStackYChanged;
protected boolean mClearAllEnabled;
@@ -453,6 +460,38 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
private NotificationStackScrollLayoutController mController;
private boolean mKeyguardMediaControllorVisible;
+
+ /**
+ * The clip path used to clip the view in a rounded way.
+ */
+ private final Path mRoundedClipPath = new Path();
+
+ /**
+ * Should we use rounded rect clipping right now
+ */
+ private boolean mShouldUseRoundedRectClipping = false;
+
+ private int mRoundedRectClippingLeft;
+ private int mRoundedRectClippingTop;
+ private int mRoundedRectClippingBottom;
+ private int mRoundedRectClippingRight;
+ private float[] mBgCornerRadii = new float[8];
+
+ /**
+ * Whether stackY should be animated in case the view is getting shorter than the scroll
+ * position and this scrolling will lead to the top scroll inset getting smaller.
+ */
+ private boolean mAnimateStackYForContentHeightChange = false;
+
+ /**
+ * Are we launching a notification right now
+ */
+ private boolean mLaunchingNotification;
+
+ /**
+ * Do notifications dismiss with normal transitioning
+ */
+ private boolean mDismissUsingRowTranslationX = true;
private NotificationEntry mTopHeadsUpEntry;
private long mNumHeadsUp;
private NotificationStackScrollLayoutController.TouchHandler mTouchHandler;
@@ -506,7 +545,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
mSectionsManager = notificationSectionsManager;
mFeatureFlags = featureFlags;
mUnlockedScreenOffAnimationController = unlockedScreenOffAnimationController;
- mShouldUseSplitNotificationShade = shouldUseSplitNotificationShade(mFeatureFlags, res);
+ updateSplitNotificationShade();
mSectionsManager.initialize(this, LayoutInflater.from(context));
mSections = mSectionsManager.createSectionsForBuckets();
@@ -862,6 +901,8 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
mCornerRadius = res.getDimensionPixelSize(R.dimen.notification_corner_radius);
mHeadsUpInset = mStatusBarHeight + res.getDimensionPixelSize(
R.dimen.heads_up_status_bar_padding);
+ mQsScrollBoundaryPosition = res.getDimensionPixelSize(
+ com.android.internal.R.dimen.quick_qs_offset_height);
}
void updateCornerRadius() {
@@ -961,6 +1002,9 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
updateFirstAndLastBackgroundViews();
updateAlgorithmLayoutMinHeight();
updateOwnTranslationZ();
+
+ // Once the layout has finished, we don't need to animate any scrolling clampings anymore.
+ mAnimateStackYForContentHeightChange = false;
}
@ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
@@ -1017,33 +1061,11 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
@ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
private void onPreDrawDuringAnimation() {
mShelf.updateAppearance();
- updateClippingToTopRoundedCorner();
if (!mNeedsAnimation && !mChildrenUpdateRequested) {
updateBackground();
}
}
- @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
- private void updateClippingToTopRoundedCorner() {
- Float clipStart = mAmbientState.getNotificationScrimTop();
- Float clipEnd = clipStart + mCornerRadius;
- boolean first = true;
- for (int i = 0; i < getChildCount(); i++) {
- ExpandableView child = (ExpandableView) getChildAt(i);
- if (child.getVisibility() == GONE) {
- continue;
- }
- float start = child.getTranslationY();
- float end = start + child.getActualHeight();
- boolean clip = clipStart > start && clipStart < end
- || clipEnd >= start && clipEnd <= end;
- clip &= !(first && mScrollAdapter.isScrolledToTop());
- child.setDistanceToTopRoundness(clip ? Math.max(start - clipStart, 0)
- : ExpandableView.NO_ROUNDNESS);
- first = false;
- }
- }
-
@ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
private void updateScrollStateForAddedChildren() {
if (mChildrenToAddAnimated.isEmpty()) {
@@ -1117,7 +1139,13 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
private void clampScrollPosition() {
int scrollRange = getScrollRange();
if (scrollRange < mOwnScrollY) {
- setOwnScrollY(scrollRange);
+ boolean animateStackY = false;
+ if (scrollRange < getScrollAmountToScrollBoundary()
+ && mAnimateStackYForContentHeightChange) {
+ // if the scroll boundary updates the position of the stack,
+ animateStackY = true;
+ }
+ setOwnScrollY(scrollRange, animateStackY);
}
}
@@ -1146,6 +1174,14 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
* Apply expansion fraction to the y position and height of the notifications panel.
*/
private void updateStackPosition() {
+ updateStackPosition(false /* listenerNeedsAnimation */);
+ }
+
+ /**
+ * Apply expansion fraction to the y position and height of the notifications panel.
+ * @param listenerNeedsAnimation does the listener need to animate?
+ */
+ private void updateStackPosition(boolean listenerNeedsAnimation) {
// Consider interpolating from an mExpansionStartY for use on lockscreen and AOD
float endTopPosition = mTopPadding + mExtraTopInsetForFullShadeTransition
+ mAmbientState.getOverExpansion();
@@ -1153,7 +1189,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
final float stackY = MathUtils.lerp(0, endTopPosition, fraction);
mAmbientState.setStackY(stackY);
if (mOnStackYChanged != null) {
- mOnStackYChanged.run();
+ mOnStackYChanged.accept(listenerNeedsAnimation);
}
if (mQsExpansionFraction <= 0) {
final float stackEndHeight = Math.max(0f,
@@ -1165,7 +1201,11 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
}
}
- void setOnStackYChanged(Runnable onStackYChanged) {
+ /**
+ * Add a listener when the StackY changes. The argument signifies whether an animation is
+ * needed.
+ */
+ void setOnStackYChanged(Consumer<Boolean> onStackYChanged) {
mOnStackYChanged = onStackYChanged;
}
@@ -1600,7 +1640,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
protected void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
Resources res = getResources();
- mShouldUseSplitNotificationShade = shouldUseSplitNotificationShade(mFeatureFlags, res);
+ updateSplitNotificationShade();
mStatusBarHeight = res.getDimensionPixelOffset(R.dimen.status_bar_height);
float densityScale = res.getDisplayMetrics().density;
mSwipeHelper.setDensityScale(densityScale);
@@ -2527,8 +2567,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
updateScrollStateForRemovedChild(child);
boolean animationGenerated = generateRemoveAnimation(child);
if (animationGenerated) {
- if (!mSwipedOutViews.contains(child)
- || Math.abs(child.getTranslation()) != child.getWidth()) {
+ if (!mSwipedOutViews.contains(child) || !isFullySwipedOut(child)) {
container.addTransientView(child, 0);
child.setTransientContainer(container);
}
@@ -2540,6 +2579,13 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
focusNextViewIfFocused(child);
}
+ /**
+ * Has this view been fully swiped out such that it's not visible anymore.
+ */
+ public boolean isFullySwipedOut(ExpandableView child) {
+ return Math.abs(child.getTranslation()) >= Math.abs(getTotalTranslationLength(child));
+ }
+
@ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
private void focusNextViewIfFocused(View view) {
if (view instanceof ExpandableNotificationRow) {
@@ -2659,17 +2705,27 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
final int startingPosition = getPositionInLinearLayout(removedChild);
final int childHeight = getIntrinsicHeight(removedChild) + mPaddingBetweenElements;
final int endPosition = startingPosition + childHeight;
- if (endPosition <= mOwnScrollY) {
+ final int scrollBoundaryStart = getScrollAmountToScrollBoundary();
+ mAnimateStackYForContentHeightChange = true;
+ // This is reset onLayout
+ if (endPosition <= mOwnScrollY - scrollBoundaryStart) {
// This child is fully scrolled of the top, so we have to deduct its height from the
// scrollPosition
setOwnScrollY(mOwnScrollY - childHeight);
- } else if (startingPosition < mOwnScrollY) {
+ } else if (startingPosition < mOwnScrollY - scrollBoundaryStart) {
// This child is currently being scrolled into, set the scroll position to the
// start of this child
- setOwnScrollY(startingPosition);
+ setOwnScrollY(startingPosition + scrollBoundaryStart);
}
}
+ /**
+ * @return the amount of scrolling needed to start clipping notifications.
+ */
+ private int getScrollAmountToScrollBoundary() {
+ return mTopPadding - mQsScrollBoundaryPosition;
+ }
+
@ShadeViewRefactor(RefactorComponent.COORDINATOR)
private int getIntrinsicHeight(View view) {
if (view instanceof ExpandableView) {
@@ -2758,7 +2814,10 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
updateAnimationState(child);
updateChronometerForChild(child);
if (child instanceof ExpandableNotificationRow) {
- ((ExpandableNotificationRow) child).setDismissRtl(mDismissRtl);
+ ExpandableNotificationRow row = (ExpandableNotificationRow) child;
+ row.setDismissRtl(mDismissRtl);
+ row.setDismissUsingRowTranslationX(mDismissUsingRowTranslationX);
+
}
}
@@ -2818,6 +2877,9 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
@ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
public void applyExpandAnimationParams(ExpandAnimationParameters params) {
mAmbientState.setExpandAnimationTopChange(params == null ? 0 : params.getTopChange());
+
+ // Disable clipping for launches
+ setLaunchingNotification(params != null);
requestChildrenUpdate();
}
@@ -2901,7 +2963,6 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
mAnimationEvents.clear();
updateBackground();
updateViewShadows();
- updateClippingToTopRoundedCorner();
} else {
applyCurrentState();
}
@@ -3030,7 +3091,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
removedTranslation = row.getTranslationWhenRemoved();
ignoreChildren = false;
}
- childWasSwipedOut |= Math.abs(row.getTranslation()) == row.getWidth();
+ childWasSwipedOut |= isFullySwipedOut(row);
} else if (child instanceof MediaHeaderView) {
childWasSwipedOut = true;
}
@@ -3038,11 +3099,11 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
Rect clipBounds = child.getClipBounds();
childWasSwipedOut = clipBounds != null && clipBounds.height() == 0;
- if (childWasSwipedOut && child instanceof ExpandableView) {
+ if (childWasSwipedOut) {
// Clean up any potential transient views if the child has already been swiped
// out, as we won't be animating it further (due to its height already being
// clipped to 0.
- ViewGroup transientContainer = ((ExpandableView) child).getTransientContainer();
+ ViewGroup transientContainer = child.getTransientContainer();
if (transientContainer != null) {
transientContainer.removeTransientView(child);
}
@@ -3795,6 +3856,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
updateNotificationAnimationStates();
updateChronometers();
requestChildrenUpdate();
+ updateUseRoundedRectClipping();
}
}
@@ -3815,6 +3877,10 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
}
void onChildHeightChanged(ExpandableView view, boolean needsAnimation) {
+ boolean previouslyNeededAnimation = mAnimateStackYForContentHeightChange;
+ if (needsAnimation) {
+ mAnimateStackYForContentHeightChange = true;
+ }
updateContentHeight();
updateScrollPositionOnExpandInBottom(view);
clampScrollPosition();
@@ -3835,6 +3901,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
requestAnimationOnViewResize(row);
}
requestChildrenUpdate();
+ mAnimateStackYForContentHeightChange = previouslyNeededAnimation;
}
void onChildHeightReset(ExpandableView view) {
@@ -4015,7 +4082,6 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
setAnimationRunning(false);
updateBackground();
updateViewShadows();
- updateClippingToTopRoundedCorner();
}
@ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
@@ -4048,7 +4114,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
expandableView.setFakeShadowIntensity(
diff / FakeShadowView.SHADOW_SIBLING_TRESHOLD,
previous.getOutlineAlpha(), (int) yLocation,
- previous.getOutlineTranslation());
+ (int) (previous.getOutlineTranslation() + previous.getTranslation()));
}
previous = expandableView;
}
@@ -4550,6 +4616,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
@ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
public void setQsExpansionFraction(float qsExpansionFraction) {
mQsExpansionFraction = qsExpansionFraction;
+ updateUseRoundedRectClipping();
// If notifications are scrolled,
// clear out scrollY by the time we push notifications offscreen
@@ -4560,13 +4627,18 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
@ShadeViewRefactor(RefactorComponent.COORDINATOR)
private void setOwnScrollY(int ownScrollY) {
+ setOwnScrollY(ownScrollY, false /* animateScrollChangeListener */);
+ }
+
+ @ShadeViewRefactor(RefactorComponent.COORDINATOR)
+ private void setOwnScrollY(int ownScrollY, boolean animateStackYChangeListener) {
if (ownScrollY != mOwnScrollY) {
// We still want to call the normal scrolled changed for accessibility reasons
onScrollChanged(mScrollX, ownScrollY, mScrollX, mOwnScrollY);
mOwnScrollY = ownScrollY;
mAmbientState.setScrollY(mOwnScrollY);
updateOnScrollChange();
- updateStackPosition();
+ updateStackPosition(animateStackYChangeListener);
}
}
@@ -4632,6 +4704,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
mStatusBarState = statusBarState;
mAmbientState.setStatusBarState(statusBarState);
updateSpeedBumpIndex();
+ updateDismissBehavior();
}
void onStatePostChange(boolean fromShadeLocked) {
@@ -5192,6 +5265,108 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
}
/**
+ * Set rounded rect clipping bounds on this view.
+ */
+ public void setRoundedClippingBounds(int left, int top, int right, int bottom, int topRadius,
+ int bottomRadius) {
+ if (mRoundedRectClippingLeft == left && mRoundedRectClippingRight == right
+ && mRoundedRectClippingBottom == bottom && mRoundedRectClippingTop == top
+ && mBgCornerRadii[0] == topRadius && mBgCornerRadii[5] == bottomRadius) {
+ return;
+ }
+ mRoundedRectClippingLeft = left;
+ mRoundedRectClippingTop = top;
+ mRoundedRectClippingBottom = bottom;
+ mRoundedRectClippingRight = right;
+ mBgCornerRadii[0] = topRadius;
+ mBgCornerRadii[1] = topRadius;
+ mBgCornerRadii[2] = topRadius;
+ mBgCornerRadii[3] = topRadius;
+ mBgCornerRadii[4] = bottomRadius;
+ mBgCornerRadii[5] = bottomRadius;
+ mBgCornerRadii[6] = bottomRadius;
+ mBgCornerRadii[7] = bottomRadius;
+ mRoundedClipPath.reset();
+ mRoundedClipPath.addRoundRect(left, top, right, bottom, mBgCornerRadii, Path.Direction.CW);
+ if (mShouldUseRoundedRectClipping) {
+ invalidate();
+ }
+ }
+
+ private void updateSplitNotificationShade() {
+ boolean split = shouldUseSplitNotificationShade(mFeatureFlags, getResources());
+ if (split != mShouldUseSplitNotificationShade) {
+ mShouldUseSplitNotificationShade = split;
+ updateDismissBehavior();
+ updateUseRoundedRectClipping();
+ }
+ }
+
+ private void updateDismissBehavior() {
+ // On the split keyguard, dismissing with clipping without a visual boundary looks odd,
+ // so let's use the content dismiss behavior instead.
+ boolean dismissUsingRowTranslationX = !mShouldUseSplitNotificationShade
+ || mStatusBarState != StatusBarState.KEYGUARD;
+ if (mDismissUsingRowTranslationX != dismissUsingRowTranslationX) {
+ mDismissUsingRowTranslationX = dismissUsingRowTranslationX;
+ for (int i = 0; i < getChildCount(); i++) {
+ View child = getChildAt(i);
+ if (child instanceof ExpandableNotificationRow) {
+ ((ExpandableNotificationRow) child).setDismissUsingRowTranslationX(
+ dismissUsingRowTranslationX);
+ }
+ }
+ }
+ }
+
+ /**
+ * Set if we're launching a notification right now.
+ */
+ private void setLaunchingNotification(boolean launching) {
+ if (launching == mLaunchingNotification) {
+ return;
+ }
+ mLaunchingNotification = launching;
+ updateUseRoundedRectClipping();
+ }
+
+ /**
+ * Should we use rounded rect clipping
+ */
+ private void updateUseRoundedRectClipping() {
+ // We don't want to clip notifications when QS is expanded, because incoming heads up on
+ // the bottom would be clipped otherwise
+ boolean qsAllowsClipping = mQsExpansionFraction < 0.5f || mShouldUseSplitNotificationShade;
+ boolean clip = !mLaunchingNotification && mIsExpanded && qsAllowsClipping;
+ if (clip != mShouldUseRoundedRectClipping) {
+ mShouldUseRoundedRectClipping = clip;
+ invalidate();
+ }
+ }
+
+ @Override
+ protected void dispatchDraw(Canvas canvas) {
+ if (mShouldUseRoundedRectClipping) {
+ // Let's clip rounded.
+ canvas.clipPath(mRoundedClipPath);
+ }
+ super.dispatchDraw(canvas);
+ }
+
+ /**
+ * Calculate the total translation needed when dismissing.
+ */
+ public float getTotalTranslationLength(View animView) {
+ if (!mDismissUsingRowTranslationX) {
+ return animView.getMeasuredWidth();
+ }
+ float notificationWidth = animView.getMeasuredWidth();
+ int containerWidth = getMeasuredWidth();
+ float padding = (containerWidth - notificationWidth) / 2.0f;
+ return containerWidth - padding;
+ }
+
+ /**
* A listener that is notified when the empty space below the notifications is clicked on
*/
@ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java
index dec98887577e..fb4f5592e97f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java
@@ -384,6 +384,11 @@ public class NotificationStackScrollLayoutController {
}
@Override
+ public float getTotalTranslationLength(View animView) {
+ return mView.getTotalTranslationLength(animView);
+ }
+
+ @Override
public void onSnooze(StatusBarNotification sbn,
NotificationSwipeActionHelper.SnoozeOption snoozeOption) {
mStatusBar.setNotificationSnoozed(sbn, snoozeOption);
@@ -822,8 +827,18 @@ public class NotificationStackScrollLayoutController {
return mView.isLayoutRtl();
}
+ /**
+ * @return the left of the view.
+ */
public int getLeft() {
- return mView.getLeft();
+ return mView.getLeft();
+ }
+
+ /**
+ * @return the top of the view.
+ */
+ public int getTop() {
+ return mView.getTop();
}
public float getTranslationX() {
@@ -1008,7 +1023,7 @@ public class NotificationStackScrollLayoutController {
mView.setQsExpansionFraction(expansionFraction);
}
- public void setOnStackYChanged(Runnable onStackYChanged) {
+ public void setOnStackYChanged(Consumer<Boolean> onStackYChanged) {
mView.setOnStackYChanged(onStackYChanged);
}
@@ -1440,6 +1455,14 @@ public class NotificationStackScrollLayoutController {
}
/**
+ * Set rounded rect clipping bounds on this view.
+ */
+ public void setRoundedClippingBounds(int left, int top, int right, int bottom, int topRadius,
+ int bottomRadius) {
+ mView.setRoundedClippingBounds(left, top, right, bottom, topRadius, bottomRadius);
+ }
+
+ /**
* Enum for UiEvent logged from this class
*/
enum NotificationPanelEvent implements UiEventLogger.UiEventEnum {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSwipeHelper.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSwipeHelper.java
index f4c4d440b063..664776975b24 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSwipeHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSwipeHelper.java
@@ -325,6 +325,11 @@ class NotificationSwipeHelper extends SwipeHelper implements NotificationSwipeAc
}
@Override
+ protected float getTotalTranslationLength(View animView) {
+ return mCallback.getTotalTranslationLength(animView);
+ }
+
+ @Override
public void setTranslation(View v, float translate) {
if (v instanceof SwipeableView) {
((SwipeableView) v).setTranslation(translate);
@@ -466,6 +471,13 @@ class NotificationSwipeHelper extends SwipeHelper implements NotificationSwipeAc
void onSnooze(StatusBarNotification sbn, SnoozeOption snoozeOption);
void onDismiss();
+
+ /**
+ * Get the total translation length where we want to swipe to when dismissing the view. By
+ * default this is the size of the view, but can also be larger.
+ * @param animView the view to ask about
+ */
+ float getTotalTranslationLength(View animView);
}
static class Builder {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java
index e5fd103c239f..74e8de4c9d11 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java
@@ -158,7 +158,7 @@ public class StackScrollAlgorithm {
AmbientState ambientState) {
float drawStart = ambientState.isOnKeyguard() ? 0
: ambientState.getStackY() - ambientState.getScrollY();
- float clipStart = ambientState.getNotificationScrimTop();
+ float clipStart = 0;
int childCount = algorithmState.visibleChildren.size();
boolean firstHeadsUp = true;
for (int i = 0; i < childCount; i++) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackStateAnimator.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackStateAnimator.java
index 4fd2064b394d..ee12b4b2d728 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackStateAnimator.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackStateAnimator.java
@@ -392,7 +392,7 @@ public class StackStateAnimator {
0, () -> removeTransientView(changingView), null);
} else if (event.animationType ==
NotificationStackScrollLayout.AnimationEvent.ANIMATION_TYPE_REMOVE_SWIPED_OUT) {
- if (Math.abs(changingView.getTranslation()) == changingView.getWidth()
+ if (mHostLayout.isFullySwipedOut(changingView)
&& changingView.getTransientContainer() != null) {
changingView.getTransientContainer().removeTransientView(changingView);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeParameters.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeParameters.java
index 01d489f91de2..c4d1abc1b74c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeParameters.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeParameters.java
@@ -63,6 +63,7 @@ public class DozeParameters implements TunerService.Tunable,
private final Resources mResources;
private final BatteryController mBatteryController;
private final FeatureFlags mFeatureFlags;
+ private final UnlockedScreenOffAnimationController mUnlockedScreenOffAnimationController;
private final Set<Callback> mCallbacks = new HashSet<>();
@@ -78,7 +79,8 @@ public class DozeParameters implements TunerService.Tunable,
BatteryController batteryController,
TunerService tunerService,
DumpManager dumpManager,
- FeatureFlags featureFlags) {
+ FeatureFlags featureFlags,
+ UnlockedScreenOffAnimationController unlockedScreenOffAnimationController) {
mResources = resources;
mAmbientDisplayConfiguration = ambientDisplayConfiguration;
mAlwaysOnPolicy = alwaysOnDisplayPolicy;
@@ -89,6 +91,7 @@ public class DozeParameters implements TunerService.Tunable,
mPowerManager = powerManager;
mPowerManager.setDozeAfterScreenOff(!mControlScreenOffAnimation);
mFeatureFlags = featureFlags;
+ mUnlockedScreenOffAnimationController = unlockedScreenOffAnimationController;
tunerService.addTunable(
this,
@@ -220,7 +223,8 @@ public class DozeParameters implements TunerService.Tunable,
* then abruptly showing AOD.
*/
public boolean shouldControlUnlockedScreenOff() {
- return getAlwaysOn() && mFeatureFlags.useNewLockscreenAnimations();
+ return getAlwaysOn() && mFeatureFlags.useNewLockscreenAnimations()
+ && mUnlockedScreenOffAnimationController.shouldPlayUnlockedScreenOffAnimation();
}
private boolean getBoolean(String propName, int resId) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardIndicationTextView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardIndicationTextView.java
index 68e20705fbeb..96276f46d23d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardIndicationTextView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardIndicationTextView.java
@@ -38,7 +38,7 @@ import java.util.LinkedList;
* A view to show hints on Keyguard ("Swipe up to unlock", "Tap again to open").
*/
public class KeyguardIndicationTextView extends TextView {
- private static final long MSG_DURATION_MILLIS = 600;
+ private static final long MSG_DURATION_MILLIS = 1500;
private long mNextAnimationTime = 0;
private boolean mAnimationsEnabled = true;
private LinkedList<CharSequence> mMessages = new LinkedList<>();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java
index 528827f639dd..aaddfca4b685 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java
@@ -830,6 +830,7 @@ public class NotificationPanelViewController extends PanelViewController {
mNotificationStackScrollLayoutController.setOverscrollTopChangedListener(
mOnOverscrollTopChangedListener);
mNotificationStackScrollLayoutController.setOnScrollListener(this::onNotificationScrolled);
+ mNotificationStackScrollLayoutController.setOnStackYChanged(this::onStackYChanged);
mNotificationStackScrollLayoutController.setOnEmptySpaceClickListener(
mOnEmptySpaceClickListener);
addTrackingHeadsUpListener(mNotificationStackScrollLayoutController::setTrackingHeadsUp);
@@ -2198,15 +2199,17 @@ public class NotificationPanelViewController extends PanelViewController {
mDepthController.setQsPanelExpansion(qsExpansionFraction);
}
- private Runnable mOnStackYChanged = () -> {
+ private void onStackYChanged(boolean shouldAnimate) {
if (mQs != null) {
+ if (shouldAnimate) {
+ mAnimateNextNotificationBounds = true;
+ mNotificationBoundsAnimationDelay = 0;
+ }
setQSClippingBounds();
}
};
private void onNotificationScrolled(int newScrollPosition) {
- // Since this is an overscroller, sometimes the scrollY can be temporarily negative
- // (when overscrollng on the top and flinging). Let's
updateQSExpansionEnabledAmbient();
}
@@ -2228,14 +2231,13 @@ public class NotificationPanelViewController extends PanelViewController {
* and QS state.
*/
private void setQSClippingBounds() {
- int top = 0;
- int bottom = 0;
- int left = 0;
- int right = 0;
+ int top;
+ int bottom;
+ int left;
+ int right;
final int qsPanelBottomY = calculateQsBottomPosition(computeQsExpansionFraction());
- final boolean visible = (computeQsExpansionFraction() > 0 || qsPanelBottomY > 0)
- && !mShouldUseSplitNotificationShade;
+ final boolean qsVisible = (computeQsExpansionFraction() > 0 || qsPanelBottomY > 0);
if (!mShouldUseSplitNotificationShade) {
if (mTransitioningToFullShadeProgress > 0.0f) {
@@ -2244,7 +2246,6 @@ public class NotificationPanelViewController extends PanelViewController {
top = mTransitionToFullShadeQSPosition;
} else {
final float notificationTop = getQSEdgePosition();
- mAmbientState.setNotificationScrimTop(notificationTop);
top = (int) (isOnKeyguard() ? Math.min(qsPanelBottomY, notificationTop)
: notificationTop);
}
@@ -2252,8 +2253,7 @@ public class NotificationPanelViewController extends PanelViewController {
// notification bounds should take full screen width regardless of insets
left = 0;
right = getView().getRight() + mDisplayRightInset;
- } else if (qsPanelBottomY > 0) { // so bounds are empty on lockscreen
- mAmbientState.setNotificationScrimTop(mSplitShadeNotificationsTopPadding);
+ } else {
top = Math.min(qsPanelBottomY, mSplitShadeNotificationsTopPadding);
bottom = mNotificationStackScrollLayoutController.getHeight();
left = mNotificationStackScrollLayoutController.getLeft();
@@ -2261,17 +2261,17 @@ public class NotificationPanelViewController extends PanelViewController {
}
// top should never be lower than bottom, otherwise it will be invisible.
top = Math.min(top, bottom);
- applyQSClippingBounds(left, top, right, bottom, visible);
+ applyQSClippingBounds(left, top, right, bottom, qsVisible);
}
private void applyQSClippingBounds(int left, int top, int right, int bottom,
- boolean visible) {
+ boolean qsVisible) {
if (!mAnimateNextNotificationBounds || mKeyguardStatusAreaClipBounds.isEmpty()) {
if (mQsClippingAnimation != null) {
// update the end position of the animator
mQsClippingAnimationEndBounds.set(left, top, right, bottom);
} else {
- applyQSClippingImmediately(left, top, right, bottom, visible);
+ applyQSClippingImmediately(left, top, right, bottom, qsVisible);
}
} else {
mQsClippingAnimationEndBounds.set(left, top, right, bottom);
@@ -2295,7 +2295,7 @@ public class NotificationPanelViewController extends PanelViewController {
int animBottom = (int) MathUtils.lerp(startBottom,
mQsClippingAnimationEndBounds.bottom, fraction);
applyQSClippingImmediately(animLeft, animTop, animRight, animBottom,
- visible /* visible */);
+ qsVisible /* qsVisible */);
});
mQsClippingAnimation.addListener(new AnimatorListenerAdapter() {
@Override
@@ -2310,7 +2310,7 @@ public class NotificationPanelViewController extends PanelViewController {
}
private void applyQSClippingImmediately(int left, int top, int right, int bottom,
- boolean visible) {
+ boolean qsVisible) {
// Fancy clipping for quick settings
int radius = mScrimCornerRadius;
int statusBarClipTop = 0;
@@ -2318,19 +2318,34 @@ public class NotificationPanelViewController extends PanelViewController {
if (!mShouldUseSplitNotificationShade) {
// The padding on this area is large enough that we can use a cheaper clipping strategy
mKeyguardStatusAreaClipBounds.set(left, top, right, bottom);
- clipStatusView = visible;
+ clipStatusView = qsVisible;
radius = (int) MathUtils.lerp(mScreenCornerRadius, mScrimCornerRadius,
Math.min(top / (float) mScrimCornerRadius, 1f));
statusBarClipTop = top - mKeyguardStatusBar.getTop();
}
if (mQs != null) {
- mQs.setFancyClipping(top, bottom, radius, visible);
+ mQs.setFancyClipping(top, bottom, radius, qsVisible
+ && !mShouldUseSplitNotificationShade);
}
mKeyguardStatusViewController.setClipBounds(
clipStatusView ? mKeyguardStatusAreaClipBounds : null);
- mScrimController.setNotificationsBounds(left, top, right, bottom);
+ if (!qsVisible && mShouldUseSplitNotificationShade) {
+ // On the lockscreen when qs isn't visible, we don't want the bounds of the shade to
+ // be visible, otherwise you can see the bounds once swiping up to see bouncer
+ mScrimController.setNotificationsBounds(0, 0, 0, 0);
+ } else {
+ mScrimController.setNotificationsBounds(left, top, right, bottom);
+ }
+
mScrimController.setScrimCornerRadius(radius);
mKeyguardStatusBar.setTopClipping(statusBarClipTop);
+ int nsslLeft = left - mNotificationStackScrollLayoutController.getLeft();
+ int nsslRight = right - mNotificationStackScrollLayoutController.getLeft();
+ int nsslTop = top - mNotificationStackScrollLayoutController.getTop();
+ int nsslBottom = bottom - mNotificationStackScrollLayoutController.getTop();
+ int bottomRadius = mShouldUseSplitNotificationShade ? radius : 0;
+ mNotificationStackScrollLayoutController.setRoundedClippingBounds(
+ nsslLeft, nsslTop, nsslRight, nsslBottom, radius, bottomRadius);
}
private float getQSEdgePosition() {
@@ -3323,7 +3338,6 @@ public class NotificationPanelViewController extends PanelViewController {
// The expandedHeight is always the full panel Height when bypassing
expandedHeight = getMaxPanelHeightNonBypass();
}
- mNotificationStackScrollLayoutController.setOnStackYChanged(mOnStackYChanged);
mNotificationStackScrollLayoutController.setExpandedHeight(expandedHeight);
updateKeyguardBottomAreaAlpha();
updateBigClockAlpha();
@@ -4222,7 +4236,7 @@ public class NotificationPanelViewController extends PanelViewController {
int oldState = mBarState;
boolean keyguardShowing = statusBarState == KEYGUARD;
- if (mUnlockedScreenOffAnimationController.shouldPlayScreenOffAnimation()
+ if (mDozeParameters.shouldControlUnlockedScreenOff()
&& oldState == StatusBarState.SHADE
&& statusBarState == KEYGUARD) {
// This means we're doing the screen off animation - position the keyguard status
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowControllerImpl.java
index 52f9aca82783..c95879650049 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowControllerImpl.java
@@ -26,11 +26,9 @@ import static com.android.systemui.statusbar.NotificationRemoteInputManager.ENAB
import android.app.IActivityManager;
import android.content.Context;
import android.content.pm.ActivityInfo;
-import android.content.res.Resources;
import android.graphics.PixelFormat;
import android.os.Binder;
import android.os.RemoteException;
-import android.os.SystemProperties;
import android.os.Trace;
import android.util.Log;
import android.view.Display;
@@ -53,6 +51,7 @@ import com.android.systemui.statusbar.StatusBarState;
import com.android.systemui.statusbar.SysuiStatusBarStateController;
import com.android.systemui.statusbar.policy.ConfigurationController;
import com.android.systemui.statusbar.policy.ConfigurationController.ConfigurationListener;
+import com.android.systemui.statusbar.policy.KeyguardStateController;
import com.google.android.collect.Lists;
@@ -108,12 +107,14 @@ public class NotificationShadeWindowControllerImpl implements NotificationShadeW
StatusBarStateController statusBarStateController,
ConfigurationController configurationController,
KeyguardViewMediator keyguardViewMediator,
- KeyguardBypassController keyguardBypassController, SysuiColorExtractor colorExtractor,
- DumpManager dumpManager) {
+ KeyguardBypassController keyguardBypassController,
+ SysuiColorExtractor colorExtractor,
+ DumpManager dumpManager,
+ KeyguardStateController keyguardStateController) {
mContext = context;
mWindowManager = windowManager;
mActivityManager = activityManager;
- mKeyguardScreenRotation = shouldEnableKeyguardScreenRotation();
+ mKeyguardScreenRotation = keyguardStateController.isKeyguardScreenRotationAllowed();
mDozeParameters = dozeParameters;
mScreenBrightnessDoze = mDozeParameters.getScreenBrightnessDoze();
mLpChanged = new LayoutParams();
@@ -173,12 +174,6 @@ public class NotificationShadeWindowControllerImpl implements NotificationShadeW
}
}
- private boolean shouldEnableKeyguardScreenRotation() {
- Resources res = mContext.getResources();
- return SystemProperties.getBoolean("lockscreen.rot_override", false)
- || res.getBoolean(R.bool.config_enableLockScreenRotation);
- }
-
/**
* Adds the notification shade view to the window manager.
*/
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 5ee5e489479d..5d2fe523c803 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
@@ -21,7 +21,6 @@ import static android.app.StatusBarManager.WINDOW_STATE_SHOWING;
import static android.app.StatusBarManager.WindowType;
import static android.app.StatusBarManager.WindowVisibleState;
import static android.app.StatusBarManager.windowStateToString;
-import static android.hardware.biometrics.BiometricSourceType.FINGERPRINT;
import static android.view.InsetsState.ITYPE_STATUS_BAR;
import static android.view.InsetsState.containsType;
import static android.view.WindowInsetsController.APPEARANCE_LOW_PROFILE_BARS;
@@ -46,8 +45,6 @@ import static com.android.systemui.statusbar.phone.BarTransitions.MODE_WARNING;
import static com.android.systemui.statusbar.phone.BarTransitions.TransitionMode;
import static com.android.wm.shell.bubbles.BubbleController.TASKBAR_CHANGED_BROADCAST;
-import android.animation.ValueAnimator;
-import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.ActivityManager;
import android.app.ActivityOptions;
@@ -121,6 +118,7 @@ import android.view.WindowManagerGlobal;
import android.view.accessibility.AccessibilityManager;
import android.widget.DateTimeView;
+import androidx.annotation.NonNull;
import androidx.lifecycle.Lifecycle;
import androidx.lifecycle.LifecycleOwner;
import androidx.lifecycle.LifecycleRegistry;
@@ -402,8 +400,6 @@ public class StatusBar extends SystemUI implements DemoMode,
private LightRevealScrim mLightRevealScrim;
private WiredChargingRippleController mChargingRippleAnimationController;
private PowerButtonReveal mPowerButtonReveal;
- private CircleReveal mCircleReveal;
- private ValueAnimator mCircleRevealAnimator = ValueAnimator.ofFloat(0f, 1f);
private final Object mQueueLock = new Object();
@@ -2778,9 +2774,14 @@ public class StatusBar extends SystemUI implements DemoMode,
+ String.valueOf(CameraIntents.getOverrideCameraPackage(mContext)));
}
- public static void dumpBarTransitions(PrintWriter pw, String var, BarTransitions transitions) {
+ public static void dumpBarTransitions(
+ PrintWriter pw, String var, @Nullable BarTransitions transitions) {
pw.print(" "); pw.print(var); pw.print(".BarTransitions.mMode=");
- pw.println(BarTransitions.modeToString(transitions.getMode()));
+ if (transitions != null) {
+ pw.println(BarTransitions.modeToString(transitions.getMode()));
+ } else {
+ pw.println("Unknown");
+ }
}
public void createAndAddWindows(@Nullable RegisterStatusBarResult result) {
@@ -2803,11 +2804,11 @@ public class StatusBar extends SystemUI implements DemoMode,
return mDisplayMetrics.density;
}
- float getDisplayWidth() {
+ public float getDisplayWidth() {
return mDisplayMetrics.widthPixels;
}
- float getDisplayHeight() {
+ public float getDisplayHeight() {
return mDisplayMetrics.heightPixels;
}
@@ -2841,9 +2842,11 @@ public class StatusBar extends SystemUI implements DemoMode,
mActivityIntentHelper.wouldLaunchResolverActivity(intent,
mLockscreenUserManager.getCurrentUserId());
+ boolean animate =
+ animationController != null && !willLaunchResolverActivity && shouldAnimateLaunch(
+ true /* isActivityIntent */);
ActivityLaunchAnimator.Controller animController =
- !willLaunchResolverActivity && shouldAnimateLaunch(true /* isActivityIntent */)
- ? wrapAnimationController(animationController, dismissShade) : null;
+ animate ? wrapAnimationController(animationController, dismissShade) : null;
// If we animate, we will dismiss the shade only once the animation is done. This is taken
// care of by the StatusBarLaunchAnimationController.
@@ -2857,7 +2860,7 @@ public class StatusBar extends SystemUI implements DemoMode,
int[] result = new int[]{ActivityManager.START_CANCELED};
mActivityLaunchAnimator.startIntentWithAnimation(animController,
- true /* animate */, intent.getPackage(), (adapter) -> {
+ animate, intent.getPackage(), (adapter) -> {
ActivityOptions options = new ActivityOptions(
getActivityOptions(mDisplayId, adapter));
options.setDisallowEnterPictureInPictureWhileLaunching(
@@ -2907,16 +2910,12 @@ public class StatusBar extends SystemUI implements DemoMode,
}
};
executeRunnableDismissingKeyguard(runnable, cancelRunnable, dismissShadeDirectly,
- willLaunchResolverActivity, true /* deferred */);
+ willLaunchResolverActivity, true /* deferred */, animate);
}
@Nullable
private ActivityLaunchAnimator.Controller wrapAnimationController(
- @Nullable ActivityLaunchAnimator.Controller animationController, boolean dismissShade) {
- if (animationController == null) {
- return null;
- }
-
+ ActivityLaunchAnimator.Controller animationController, boolean dismissShade) {
View rootView = animationController.getLaunchContainer().getRootView();
if (rootView == mSuperStatusBarViewFactory.getStatusBarWindowView()) {
// We are animating a view in the status bar. We have to make sure that the status bar
@@ -2959,34 +2958,56 @@ public class StatusBar extends SystemUI implements DemoMode,
final boolean dismissShade,
final boolean afterKeyguardGone,
final boolean deferred) {
- dismissKeyguardThenExecute(() -> {
- if (runnable != null) {
- if (mStatusBarKeyguardViewManager.isShowing()
- && mStatusBarKeyguardViewManager.isOccluded()) {
- mStatusBarKeyguardViewManager.addAfterKeyguardGoneRunnable(runnable);
- } else {
- AsyncTask.execute(runnable);
- }
- }
- if (dismissShade) {
- if (mExpandedVisible && !mBouncerShowing) {
- mShadeController.animateCollapsePanels(CommandQueue.FLAG_EXCLUDE_RECENTS_PANEL,
- true /* force */, true /* delayed*/);
- } else {
+ executeRunnableDismissingKeyguard(runnable, cancelAction, dismissShade, afterKeyguardGone,
+ deferred, false /* willAnimateOnKeyguard */);
+ }
- // Do it after DismissAction has been processed to conserve the needed ordering.
- mHandler.post(mShadeController::runPostCollapseRunnables);
+ public void executeRunnableDismissingKeyguard(final Runnable runnable,
+ final Runnable cancelAction,
+ final boolean dismissShade,
+ final boolean afterKeyguardGone,
+ final boolean deferred,
+ final boolean willAnimateOnKeyguard) {
+ OnDismissAction onDismissAction = new OnDismissAction() {
+ @Override
+ public boolean onDismiss() {
+ if (runnable != null) {
+ if (mStatusBarKeyguardViewManager.isShowing()
+ && mStatusBarKeyguardViewManager.isOccluded()) {
+ mStatusBarKeyguardViewManager.addAfterKeyguardGoneRunnable(runnable);
+ } else {
+ AsyncTask.execute(runnable);
+ }
}
- } else if (isInLaunchTransition()
- && mNotificationPanelViewController.isLaunchTransitionFinished()) {
+ if (dismissShade) {
+ if (mExpandedVisible && !mBouncerShowing) {
+ mShadeController.animateCollapsePanels(
+ CommandQueue.FLAG_EXCLUDE_RECENTS_PANEL,
+ true /* force */, true /* delayed*/);
+ } else {
+
+ // Do it after DismissAction has been processed to conserve the needed
+ // ordering.
+ mHandler.post(mShadeController::runPostCollapseRunnables);
+ }
+ } else if (StatusBar.this.isInLaunchTransition()
+ && mNotificationPanelViewController.isLaunchTransitionFinished()) {
+
+ // We are not dismissing the shade, but the launch transition is already
+ // finished,
+ // so nobody will call readyForKeyguardDone anymore. Post it such that
+ // keyguardDonePending gets called first.
+ mHandler.post(mStatusBarKeyguardViewManager::readyForKeyguardDone);
+ }
+ return deferred;
+ }
- // We are not dismissing the shade, but the launch transition is already finished,
- // so nobody will call readyForKeyguardDone anymore. Post it such that
- // keyguardDonePending gets called first.
- mHandler.post(mStatusBarKeyguardViewManager::readyForKeyguardDone);
+ @Override
+ public boolean willRunAnimationOnKeyguard() {
+ return willAnimateOnKeyguard;
}
- return deferred;
- }, cancelAction, afterKeyguardGone);
+ };
+ dismissKeyguardThenExecute(onDismissAction, cancelAction, afterKeyguardGone);
}
private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
@@ -3408,6 +3429,10 @@ public class StatusBar extends SystemUI implements DemoMode,
}
boolean updateIsKeyguard() {
+ return updateIsKeyguard(false /* force */);
+ }
+
+ boolean updateIsKeyguard(boolean force) {
boolean wakeAndUnlocking = mBiometricUnlockController.getMode()
== BiometricUnlockController.MODE_WAKE_AND_UNLOCK;
@@ -3431,7 +3456,7 @@ public class StatusBar extends SystemUI implements DemoMode,
showKeyguardImpl();
}
} else {
- return hideKeyguardImpl();
+ return hideKeyguardImpl(force);
}
return false;
}
@@ -3513,9 +3538,6 @@ public class StatusBar extends SystemUI implements DemoMode,
public void fadeKeyguardWhilePulsing() {
mNotificationPanelViewController.fadeOut(0, FADE_KEYGUARD_DURATION_PULSING,
()-> {
- if (shouldShowCircleReveal()) {
- startCircleReveal();
- }
hideKeyguard();
mStatusBarKeyguardViewManager.onKeyguardFadedAway();
}).start();
@@ -3560,11 +3582,11 @@ public class StatusBar extends SystemUI implements DemoMode,
/**
* @return true if we would like to stay in the shade, false if it should go away entirely
*/
- public boolean hideKeyguardImpl() {
+ public boolean hideKeyguardImpl(boolean force) {
mIsKeyguard = false;
Trace.beginSection("StatusBar#hideKeyguard");
boolean staying = mStatusBarStateController.leaveOpenOnKeyguardHide();
- if (!(mStatusBarStateController.setState(StatusBarState.SHADE))) {
+ if (!(mStatusBarStateController.setState(StatusBarState.SHADE, force))) {
//TODO: StatusBarStateController should probably know about hiding the keyguard and
// notify listeners.
@@ -3856,7 +3878,7 @@ public class StatusBar extends SystemUI implements DemoMode,
@Override
public void onDozeAmountChanged(float linear, float eased) {
if (mFeatureFlags.useNewLockscreenAnimations()
- && !mCircleRevealAnimator.isRunning()) {
+ && !(mLightRevealScrim.getRevealEffect() instanceof CircleReveal)) {
mLightRevealScrim.setRevealAmount(1f - linear);
}
}
@@ -3879,7 +3901,7 @@ public class StatusBar extends SystemUI implements DemoMode,
|| (!isDozing && mWakefulnessLifecycle.getLastWakeReason()
== PowerManager.WAKE_REASON_POWER_BUTTON)) {
mLightRevealScrim.setRevealEffect(mPowerButtonReveal);
- } else if (!mCircleRevealAnimator.isRunning()) {
+ } else if (!(mLightRevealScrim.getRevealEffect() instanceof CircleReveal)) {
mLightRevealScrim.setRevealEffect(LiftReveal.INSTANCE);
}
@@ -3891,36 +3913,8 @@ public class StatusBar extends SystemUI implements DemoMode,
Trace.endSection();
}
- /**
- * Update the parameters for the dozing circle reveal that animates when the user authenticates
- * from AOD using the fingerprint sensor.
- */
- public void updateCircleReveal() {
- final PointF fpLocation = mAuthRippleController.getFingerprintSensorLocation();
- if (fpLocation != null) {
- mCircleReveal =
- new CircleReveal(
- fpLocation.x,
- fpLocation.y,
- 0,
- Math.max(Math.max(fpLocation.x, getDisplayWidth() - fpLocation.x),
- Math.max(fpLocation.y, getDisplayHeight() - fpLocation.y)));
- }
- }
-
- private void startCircleReveal() {
- mLightRevealScrim.setRevealEffect(mCircleReveal);
- mCircleRevealAnimator.cancel();
- mCircleRevealAnimator.addUpdateListener(animation ->
- mLightRevealScrim.setRevealAmount(
- (float) mCircleRevealAnimator.getAnimatedValue()));
- mCircleRevealAnimator.setDuration(900);
- mCircleRevealAnimator.start();
- }
-
- private boolean shouldShowCircleReveal() {
- return mCircleReveal != null && !mCircleRevealAnimator.isRunning()
- && mBiometricUnlockController.getBiometricType() == FINGERPRINT;
+ public LightRevealScrim getLightRevealScrim() {
+ return mLightRevealScrim;
}
private void updateKeyguardState() {
@@ -4060,7 +4054,7 @@ public class StatusBar extends SystemUI implements DemoMode,
// The screen off animation uses our LightRevealScrim - we need to be expanded for it to
// be visible.
- if (mUnlockedScreenOffAnimationController.shouldPlayScreenOffAnimation()) {
+ if (mDozeParameters.shouldControlUnlockedScreenOff()) {
makeExpandedVisible(true);
}
@@ -4609,28 +4603,37 @@ public class StatusBar extends SystemUI implements DemoMode,
*
* @param action The action to execute after dismissing the keyguard.
* @param collapsePanel Whether we should collapse the panel after dismissing the keyguard.
- * @param deferKeyguardDismiss Whether we should defer the keyguard actual dismissal, for
- * instance to run animations on the keyguard before hiding it.
+ * @param willAnimateOnKeyguard Whether {@param action} will run an animation on the keyguard if
+ * we are locked.
*/
private void executeActionDismissingKeyguard(Runnable action, boolean afterKeyguardGone,
- boolean collapsePanel, boolean deferKeyguardDismiss) {
+ boolean collapsePanel, boolean willAnimateOnKeyguard) {
if (!mDeviceProvisionedController.isDeviceProvisioned()) return;
- dismissKeyguardThenExecute(() -> {
- new Thread(() -> {
- try {
- // The intent we are sending is for the application, which
- // won't have permission to immediately start an activity after
- // the user switches to home. We know it is safe to do at this
- // point, so make sure new activity switches are now allowed.
- ActivityManager.getService().resumeAppSwitches();
- } catch (RemoteException e) {
- }
- action.run();
- }).start();
+ OnDismissAction onDismissAction = new OnDismissAction() {
+ @Override
+ public boolean onDismiss() {
+ new Thread(() -> {
+ try {
+ // The intent we are sending is for the application, which
+ // won't have permission to immediately start an activity after
+ // the user switches to home. We know it is safe to do at this
+ // point, so make sure new activity switches are now allowed.
+ ActivityManager.getService().resumeAppSwitches();
+ } catch (RemoteException e) {
+ }
+ action.run();
+ }).start();
- return collapsePanel ? mShadeController.collapsePanel() : deferKeyguardDismiss;
- }, afterKeyguardGone);
+ return collapsePanel ? mShadeController.collapsePanel() : willAnimateOnKeyguard;
+ }
+
+ @Override
+ public boolean willRunAnimationOnKeyguard() {
+ return willAnimateOnKeyguard;
+ }
+ };
+ dismissKeyguardThenExecute(onDismissAction, afterKeyguardGone);
}
@Override
@@ -4674,7 +4677,6 @@ public class StatusBar extends SystemUI implements DemoMode,
// the animation on the keyguard). The animation will take care of (instantly) collapsing
// the shade and hiding the keyguard once it is done.
boolean collapse = !animate;
- boolean deferKeyguardDismiss = animate;
executeActionDismissingKeyguard(() -> {
try {
// We wrap animationCallback with a StatusBarLaunchAnimatorController so that the
@@ -4703,7 +4705,7 @@ public class StatusBar extends SystemUI implements DemoMode,
if (intentSentUiThreadCallback != null) {
postOnUiThread(intentSentUiThreadCallback);
}
- }, willLaunchResolverActivity, collapse, deferKeyguardDismiss);
+ }, willLaunchResolverActivity, collapse, animate);
}
private void postOnUiThread(Runnable runnable) {
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 c7efcb2923e7..e8463992ed13 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
@@ -192,6 +192,7 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb
private OnDismissAction mAfterKeyguardGoneAction;
private Runnable mKeyguardGoneCancelAction;
+ private boolean mDismissActionWillAnimateOnKeyguard;
private final ArrayList<Runnable> mAfterKeyguardGoneRunnables = new ArrayList<>();
// Dismiss action to be launched when we stop dozing or the keyguard is gone.
@@ -447,6 +448,7 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb
mAfterKeyguardGoneAction = r;
mKeyguardGoneCancelAction = cancelAction;
+ mDismissActionWillAnimateOnKeyguard = r != null && r.willRunAnimationOnKeyguard();
// If there is an an alternate auth interceptor (like the UDFPS), show that one instead
// of the bouncer.
@@ -625,9 +627,12 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb
mBouncer.startPreHideAnimation(finishRunnable);
mStatusBar.onBouncerPreHideAnimation();
- // startPreHideAnimation() will change the visibility of the bouncer, so we have to
- // make sure to update its state.
- updateStates();
+ // We update the state (which will show the keyguard) only if an animation will run on
+ // the keyguard. If there is no animation, we wait before updating the state so that we
+ // go directly from bouncer to launcher/app.
+ if (mDismissActionWillAnimateOnKeyguard) {
+ updateStates();
+ }
} else if (finishRunnable != null) {
finishRunnable.run();
}
@@ -798,6 +803,7 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb
mAfterKeyguardGoneAction = null;
}
mKeyguardGoneCancelAction = null;
+ mDismissActionWillAnimateOnKeyguard = false;
for (int i = 0; i < mAfterKeyguardGoneRunnables.size(); i++) {
mAfterKeyguardGoneRunnables.get(i).run();
}
@@ -866,6 +872,7 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb
return; // allow bouncer to trigger saved actions
}
mAfterKeyguardGoneAction = null;
+ mDismissActionWillAnimateOnKeyguard = false;
if (mKeyguardGoneCancelAction != null) {
mKeyguardGoneCancelAction.run();
mKeyguardGoneCancelAction = null;
@@ -892,6 +899,9 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb
};
protected void updateStates() {
+ if (mContainer == null ) {
+ return;
+ }
int vis = mContainer.getSystemUiVisibility();
boolean showing = mShowing;
boolean occluded = mOccluded;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java
index d93b76646d58..98b9cc9bc716 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java
@@ -41,6 +41,7 @@ import android.text.TextUtils;
import android.util.EventLog;
import android.view.View;
+import com.android.internal.jank.InteractionJankMonitor;
import com.android.internal.logging.MetricsLogger;
import com.android.internal.statusbar.NotificationVisibility;
import com.android.internal.widget.LockPatternUtils;
@@ -260,10 +261,19 @@ public class StatusBarNotificationActivityStarter implements NotificationActivit
boolean showOverLockscreen = mKeyguardStateController.isShowing() && intent != null
&& mActivityIntentHelper.wouldShowOverLockscreen(intent.getIntent(),
mLockscreenUserManager.getCurrentUserId());
- ActivityStarter.OnDismissAction postKeyguardAction =
- () -> handleNotificationClickAfterKeyguardDismissed(
+ ActivityStarter.OnDismissAction postKeyguardAction = new ActivityStarter.OnDismissAction() {
+ @Override
+ public boolean onDismiss() {
+ return handleNotificationClickAfterKeyguardDismissed(
entry, row, controller, intent,
isActivityIntent, animate, showOverLockscreen);
+ }
+
+ @Override
+ public boolean willRunAnimationOnKeyguard() {
+ return animate;
+ }
+ };
if (showOverLockscreen) {
mIsCollapsingToShowActivityOverLockscreen = true;
postKeyguardAction.onDismiss();
@@ -453,53 +463,76 @@ public class StatusBarNotificationActivityStarter implements NotificationActivit
public void startNotificationGutsIntent(final Intent intent, final int appUid,
ExpandableNotificationRow row) {
boolean animate = mStatusBar.shouldAnimateLaunch(true /* isActivityIntent */);
- mActivityStarter.dismissKeyguardThenExecute(() -> {
- AsyncTask.execute(() -> {
- ActivityLaunchAnimator.Controller animationController =
- new StatusBarLaunchAnimatorController(
- mNotificationAnimationProvider.getAnimatorController(row),
- mStatusBar, true /* isActivityIntent */);
-
- mActivityLaunchAnimator.startIntentWithAnimation(
- animationController, animate, intent.getPackage(),
- (adapter) -> TaskStackBuilder.create(mContext)
- .addNextIntentWithParentStack(intent)
- .startActivities(getActivityOptions(
- mStatusBar.getDisplayId(),
- adapter),
- new UserHandle(UserHandle.getUserId(appUid))));
- });
- return true;
- }, null, false /* afterKeyguardGone */);
+ ActivityStarter.OnDismissAction onDismissAction = new ActivityStarter.OnDismissAction() {
+ @Override
+ public boolean onDismiss() {
+ AsyncTask.execute(() -> {
+ ActivityLaunchAnimator.Controller animationController =
+ new StatusBarLaunchAnimatorController(
+ mNotificationAnimationProvider.getAnimatorController(row),
+ mStatusBar, true /* isActivityIntent */);
+
+ mActivityLaunchAnimator.startIntentWithAnimation(
+ animationController, animate, intent.getPackage(),
+ (adapter) -> TaskStackBuilder.create(mContext)
+ .addNextIntentWithParentStack(intent)
+ .startActivities(getActivityOptions(
+ mStatusBar.getDisplayId(),
+ adapter),
+ new UserHandle(UserHandle.getUserId(appUid))));
+ });
+ return true;
+ }
+
+ @Override
+ public boolean willRunAnimationOnKeyguard() {
+ return animate;
+ }
+ };
+ mActivityStarter.dismissKeyguardThenExecute(onDismissAction, null,
+ false /* afterKeyguardGone */);
}
@Override
public void startHistoryIntent(View view, boolean showHistory) {
boolean animate = mStatusBar.shouldAnimateLaunch(true /* isActivityIntent */);
- mActivityStarter.dismissKeyguardThenExecute(() -> {
- AsyncTask.execute(() -> {
- Intent intent = showHistory ? new Intent(
- Settings.ACTION_NOTIFICATION_HISTORY) : new Intent(
- Settings.ACTION_NOTIFICATION_SETTINGS);
- TaskStackBuilder tsb = TaskStackBuilder.create(mContext)
- .addNextIntent(new Intent(Settings.ACTION_NOTIFICATION_SETTINGS));
- if (showHistory) {
- tsb.addNextIntent(intent);
- }
+ ActivityStarter.OnDismissAction onDismissAction = new ActivityStarter.OnDismissAction() {
+ @Override
+ public boolean onDismiss() {
+ AsyncTask.execute(() -> {
+ Intent intent = showHistory ? new Intent(
+ Settings.ACTION_NOTIFICATION_HISTORY) : new Intent(
+ Settings.ACTION_NOTIFICATION_SETTINGS);
+ TaskStackBuilder tsb = TaskStackBuilder.create(mContext)
+ .addNextIntent(new Intent(Settings.ACTION_NOTIFICATION_SETTINGS));
+ if (showHistory) {
+ tsb.addNextIntent(intent);
+ }
- ActivityLaunchAnimator.Controller animationController =
- new StatusBarLaunchAnimatorController(
- ActivityLaunchAnimator.Controller.fromView(view), mStatusBar,
- true /* isActivityIntent */);
+ ActivityLaunchAnimator.Controller viewController =
+ ActivityLaunchAnimator.Controller.fromView(view,
+ InteractionJankMonitor.CUJ_SHADE_APP_LAUNCH_FROM_HISTORY_BUTTON
+ );
+ ActivityLaunchAnimator.Controller animationController =
+ new StatusBarLaunchAnimatorController(viewController, mStatusBar,
+ true /* isActivityIntent */);
+
+ mActivityLaunchAnimator.startIntentWithAnimation(animationController, animate,
+ intent.getPackage(),
+ (adapter) -> tsb.startActivities(
+ getActivityOptions(mStatusBar.getDisplayId(), adapter),
+ UserHandle.CURRENT));
+ });
+ return true;
+ }
- mActivityLaunchAnimator.startIntentWithAnimation(animationController, animate,
- intent.getPackage(),
- (adapter) -> tsb.startActivities(
- getActivityOptions(mStatusBar.getDisplayId(), adapter),
- UserHandle.CURRENT));
- });
- return true;
- }, null, false /* afterKeyguardGone */);
+ @Override
+ public boolean willRunAnimationOnKeyguard() {
+ return animate;
+ }
+ };
+ mActivityStarter.dismissKeyguardThenExecute(onDismissAction, null,
+ false /* afterKeyguardGone */);
}
private void removeHunAfterClick(ExpandableNotificationRow row) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/UnlockedScreenOffAnimationController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/UnlockedScreenOffAnimationController.kt
index e135cc51a7bc..52bf2d577776 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/UnlockedScreenOffAnimationController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/UnlockedScreenOffAnimationController.kt
@@ -3,6 +3,8 @@ package com.android.systemui.statusbar.phone
import android.animation.Animator
import android.animation.AnimatorListenerAdapter
import android.animation.ValueAnimator
+import android.content.Context
+import android.content.res.Configuration
import android.os.Handler
import android.view.View
import com.android.systemui.animation.Interpolators
@@ -16,6 +18,7 @@ import com.android.systemui.statusbar.notification.AnimatableProperty
import com.android.systemui.statusbar.notification.PropertyAnimator
import com.android.systemui.statusbar.notification.stack.AnimationProperties
import com.android.systemui.statusbar.notification.stack.StackStateAnimator
+import com.android.systemui.statusbar.policy.KeyguardStateController
import javax.inject.Inject
/**
@@ -38,10 +41,11 @@ private const val LIGHT_REVEAL_ANIMATION_DURATION = 750L
*/
@SysUISingleton
class UnlockedScreenOffAnimationController @Inject constructor(
+ private val context: Context,
private val wakefulnessLifecycle: WakefulnessLifecycle,
private val statusBarStateControllerImpl: StatusBarStateControllerImpl,
private val keyguardViewMediatorLazy: dagger.Lazy<KeyguardViewMediator>,
- private val dozeParameters: DozeParameters
+ private val keyguardStateController: KeyguardStateController
) : WakefulnessLifecycle.Observer {
private val handler = Handler()
@@ -131,13 +135,18 @@ class UnlockedScreenOffAnimationController @Inject constructor(
lightRevealAnimationPlaying = false
aodUiAnimationPlaying = false
- // Make sure the status bar is in the correct keyguard state, since we might have left it in
- // the KEYGUARD state if this wakeup cancelled the screen off animation.
- statusBar.updateIsKeyguard()
+ // Make sure the status bar is in the correct keyguard state, forcing it if necessary. This
+ // is required if the screen off animation is cancelled, since it might be incorrectly left
+ // in the KEYGUARD or SHADE states depending on when it was cancelled and whether 'lock
+ // instantly' is enabled. We need to force it so that the state is set even if we're going
+ // from SHADE to SHADE or KEYGUARD to KEYGUARD, since we might have changed parts of the UI
+ // (such as showing AOD in the shade) without actually changing the StatusBarState. This
+ // ensures that the UI definitely reflects the desired state.
+ statusBar.updateIsKeyguard(true /* force */)
}
override fun onStartedGoingToSleep() {
- if (shouldPlayScreenOffAnimation()) {
+ if (shouldPlayUnlockedScreenOffAnimation()) {
lightRevealAnimationPlaying = true
lightRevealAnimator.start()
@@ -151,13 +160,31 @@ class UnlockedScreenOffAnimationController @Inject constructor(
}
/**
- * Whether we should play the screen off animation when the phone starts going to sleep. We can
- * do that if dozeParameters says we can control the unlocked screen off animation and we are in
- * the SHADE state. If we're in KEYGUARD or SHADE_LOCKED, the regular
+ * Whether we want to play the screen off animation when the phone starts going to sleep, based
+ * on the current state of the device.
*/
- fun shouldPlayScreenOffAnimation(): Boolean {
- return dozeParameters.shouldControlUnlockedScreenOff() &&
- statusBarStateControllerImpl.state == StatusBarState.SHADE
+ fun shouldPlayUnlockedScreenOffAnimation(): Boolean {
+ // We only play the unlocked screen off animation if we are... unlocked.
+ if (statusBarStateControllerImpl.state != StatusBarState.SHADE) {
+ return false
+ }
+
+ // We currently draw both the light reveal scrim, and the AOD UI, in the shade. If it's
+ // already expanded and showing notifications/QS, the animation looks really messy. For now,
+ // disable it if the notification panel is expanded.
+ if (statusBar.notificationPanelViewController.isFullyExpanded) {
+ return false
+ }
+
+ // If we're not allowed to rotate the keyguard, then only do the screen off animation if
+ // we're in portrait. Otherwise, AOD will animate in sideways, which looks weird.
+ if (!keyguardStateController.isKeyguardScreenRotationAllowed &&
+ context.resources.configuration.orientation != Configuration.ORIENTATION_PORTRAIT) {
+ return false
+ }
+
+ // Otherwise, good to go.
+ return true
}
/**
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallController.kt
index ef7fac311799..b295f6659f81 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallController.kt
@@ -25,6 +25,7 @@ import android.content.Intent
import android.util.Log
import android.view.View
import android.widget.Chronometer
+import com.android.internal.jank.InteractionJankMonitor
import com.android.systemui.R
import com.android.systemui.animation.ActivityLaunchAnimator
import com.android.systemui.dagger.SysUISingleton
@@ -179,7 +180,10 @@ class OngoingCallController @Inject constructor(
logger.logChipClicked()
activityStarter.postStartActivityDismissingKeyguard(
currentCallNotificationInfo.intent, 0,
- ActivityLaunchAnimator.Controller.fromView(backgroundView))
+ ActivityLaunchAnimator.Controller.fromView(
+ backgroundView,
+ InteractionJankMonitor.CUJ_STATUS_BAR_APP_LAUNCH_FROM_CALL_CHIP)
+ )
}
setUpUidObserver(currentCallNotificationInfo)
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardStateController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardStateController.java
index af7bf9500bf3..fcfc9670b8b0 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardStateController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardStateController.java
@@ -57,6 +57,11 @@ public interface KeyguardStateController extends CallbackController<Callback> {
boolean canPerformSmartSpaceTransition();
/**
+ * Whether the keyguard is allowed to rotate, or needs to be locked to the default orientation.
+ */
+ boolean isKeyguardScreenRotationAllowed();
+
+ /**
* If the device has PIN/pattern/password or a lock screen at all.
*/
boolean isMethodSecure();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardStateControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardStateControllerImpl.java
index 0945a3f884d6..64750bd803d5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardStateControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardStateControllerImpl.java
@@ -23,6 +23,7 @@ import android.content.Intent;
import android.content.IntentFilter;
import android.hardware.biometrics.BiometricSourceType;
import android.os.Build;
+import android.os.SystemProperties;
import android.os.Trace;
import androidx.annotation.VisibleForTesting;
@@ -31,6 +32,7 @@ import com.android.internal.widget.LockPatternUtils;
import com.android.keyguard.KeyguardUpdateMonitor;
import com.android.keyguard.KeyguardUpdateMonitorCallback;
import com.android.systemui.Dumpable;
+import com.android.systemui.R;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.shared.system.smartspace.SmartspaceTransitionController;
@@ -50,6 +52,7 @@ public class KeyguardStateControllerImpl implements KeyguardStateController, Dum
private static final String AUTH_BROADCAST_KEY = "debug_trigger_auth";
private final ArrayList<Callback> mCallbacks = new ArrayList<>();
+ private final Context mContext;
private final KeyguardUpdateMonitor mKeyguardUpdateMonitor;
private final LockPatternUtils mLockPatternUtils;
private final KeyguardUpdateMonitorCallback mKeyguardUpdateMonitorCallback =
@@ -100,6 +103,7 @@ public class KeyguardStateControllerImpl implements KeyguardStateController, Dum
public KeyguardStateControllerImpl(Context context,
KeyguardUpdateMonitor keyguardUpdateMonitor, LockPatternUtils lockPatternUtils,
SmartspaceTransitionController smartspaceTransitionController) {
+ mContext = context;
mKeyguardUpdateMonitor = keyguardUpdateMonitor;
mLockPatternUtils = lockPatternUtils;
mKeyguardUpdateMonitor.registerCallback(mKeyguardUpdateMonitorCallback);
@@ -243,6 +247,12 @@ public class KeyguardStateControllerImpl implements KeyguardStateController, Dum
}
@Override
+ public boolean isKeyguardScreenRotationAllowed() {
+ return SystemProperties.getBoolean("lockscreen.rot_override", false)
+ || mContext.getResources().getBoolean(R.bool.config_enableLockScreenRotation);
+ }
+
+ @Override
public boolean isFaceAuthEnabled() {
return mFaceAuthEnabled;
}
diff --git a/packages/SystemUI/src/com/android/systemui/tuner/TunerServiceImpl.java b/packages/SystemUI/src/com/android/systemui/tuner/TunerServiceImpl.java
index 26f4a2b9d2b2..d97815f92964 100644
--- a/packages/SystemUI/src/com/android/systemui/tuner/TunerServiceImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/tuner/TunerServiceImpl.java
@@ -68,7 +68,8 @@ public class TunerServiceImpl extends TunerService {
private static final String[] RESET_EXCEPTION_LIST = new String[] {
QSTileHost.TILES_SETTING,
Settings.Secure.DOZE_ALWAYS_ON,
- Settings.Secure.MEDIA_CONTROLS_RESUME
+ Settings.Secure.MEDIA_CONTROLS_RESUME,
+ Settings.Secure.MEDIA_CONTROLS_RECOMMENDATION
};
private final Observer mObserver = new Observer();
diff --git a/packages/SystemUI/src/com/android/systemui/wmshell/WMShell.java b/packages/SystemUI/src/com/android/systemui/wmshell/WMShell.java
index 92ef8504d123..3af82f91af1c 100644
--- a/packages/SystemUI/src/com/android/systemui/wmshell/WMShell.java
+++ b/packages/SystemUI/src/com/android/systemui/wmshell/WMShell.java
@@ -34,6 +34,7 @@ import android.graphics.drawable.Drawable;
import android.inputmethodservice.InputMethodService;
import android.os.IBinder;
import android.os.ParcelFileDescriptor;
+import android.view.KeyEvent;
import com.android.internal.annotations.VisibleForTesting;
import com.android.keyguard.KeyguardUpdateMonitor;
@@ -57,6 +58,7 @@ import com.android.wm.shell.hidedisplaycutout.HideDisplayCutout;
import com.android.wm.shell.legacysplitscreen.LegacySplitScreen;
import com.android.wm.shell.nano.WmShellTraceProto;
import com.android.wm.shell.onehanded.OneHanded;
+import com.android.wm.shell.onehanded.OneHandedEventCallback;
import com.android.wm.shell.onehanded.OneHandedTransitionCallback;
import com.android.wm.shell.onehanded.OneHandedUiEventLogger;
import com.android.wm.shell.pip.Pip;
@@ -253,6 +255,15 @@ public final class WMShell extends SystemUI
}
});
+ oneHanded.registerEventCallback(new OneHandedEventCallback() {
+ @Override
+ public void notifyExpandNotification() {
+ mSysUiMainExecutor.execute(
+ () -> mCommandQueue.handleSystemKey(
+ KeyEvent.KEYCODE_SYSTEM_NAVIGATION_DOWN));
+ }
+ });
+
mOneHandedKeyguardCallback = new KeyguardUpdateMonitorCallback() {
@Override
public void onKeyguardBouncerChanged(boolean bouncer) {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthRippleControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthRippleControllerTest.kt
index 240fdf3a4e17..d87a26b096fd 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthRippleControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthRippleControllerTest.kt
@@ -25,6 +25,7 @@ import com.android.keyguard.KeyguardUpdateMonitorCallback
import com.android.systemui.SysuiTestCase
import com.android.systemui.statusbar.NotificationShadeWindowController
import com.android.systemui.statusbar.commandline.CommandRegistry
+import com.android.systemui.statusbar.phone.BiometricUnlockController
import com.android.systemui.statusbar.phone.KeyguardBypassController
import com.android.systemui.statusbar.phone.StatusBar
import com.android.systemui.statusbar.policy.ConfigurationController
@@ -53,6 +54,7 @@ class AuthRippleControllerTest : SysuiTestCase() {
@Mock private lateinit var authController: AuthController
@Mock private lateinit var notificationShadeWindowController: NotificationShadeWindowController
@Mock private lateinit var bypassController: KeyguardBypassController
+ @Mock private lateinit var biometricUnlockController: BiometricUnlockController
@Before
fun setUp() {
@@ -66,6 +68,7 @@ class AuthRippleControllerTest : SysuiTestCase() {
commandRegistry,
notificationShadeWindowController,
bypassController,
+ biometricUnlockController,
rippleView
)
controller.init()
@@ -90,7 +93,7 @@ class AuthRippleControllerTest : SysuiTestCase() {
// THEN update sensor location and show ripple
verify(rippleView).setSensorLocation(fpsLocation)
- verify(rippleView).startRipple(any())
+ verify(rippleView).startRipple(any(), any())
}
@Test
@@ -111,7 +114,7 @@ class AuthRippleControllerTest : SysuiTestCase() {
false /* isStrongBiometric */)
// THEN no ripple
- verify(rippleView, never()).startRipple(any())
+ verify(rippleView, never()).startRipple(any(), any())
}
@Test
@@ -132,7 +135,7 @@ class AuthRippleControllerTest : SysuiTestCase() {
false /* isStrongBiometric */)
// THEN no ripple
- verify(rippleView, never()).startRipple(any())
+ verify(rippleView, never()).startRipple(any(), any())
}
@Test
@@ -156,7 +159,7 @@ class AuthRippleControllerTest : SysuiTestCase() {
// THEN show ripple
verify(rippleView).setSensorLocation(faceLocation)
- verify(rippleView).startRipple(any())
+ verify(rippleView).startRipple(any(), any())
}
@Test
@@ -176,7 +179,7 @@ class AuthRippleControllerTest : SysuiTestCase() {
false /* isStrongBiometric */)
// THEN no ripple
- verify(rippleView, never()).startRipple(any())
+ verify(rippleView, never()).startRipple(any(), any())
}
@Test
@@ -191,7 +194,7 @@ class AuthRippleControllerTest : SysuiTestCase() {
0 /* userId */,
BiometricSourceType.FACE /* type */,
false /* isStrongBiometric */)
- verify(rippleView, never()).startRipple(any())
+ verify(rippleView, never()).startRipple(any(), any())
}
@Test
@@ -206,7 +209,7 @@ class AuthRippleControllerTest : SysuiTestCase() {
0 /* userId */,
BiometricSourceType.FINGERPRINT /* type */,
false /* isStrongBiometric */)
- verify(rippleView, never()).startRipple(any())
+ verify(rippleView, never()).startRipple(any(), any())
}
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeTriggersTest.java b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeTriggersTest.java
index c0b45c6d5c96..a11b9cf357a8 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeTriggersTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeTriggersTest.java
@@ -16,7 +16,10 @@
package com.android.systemui.doze;
+import static com.android.systemui.doze.DozeMachine.State.DOZE_AOD;
+
import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyFloat;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.clearInvocations;
@@ -161,7 +164,7 @@ public class DozeTriggersTest extends SysuiTestCase {
clearInvocations(mSensors);
mTriggers.transitionTo(DozeMachine.State.DOZE_PULSING, DozeMachine.State.DOZE_PULSE_DONE);
- mTriggers.transitionTo(DozeMachine.State.DOZE_PULSE_DONE, DozeMachine.State.DOZE_AOD);
+ mTriggers.transitionTo(DozeMachine.State.DOZE_PULSE_DONE, DOZE_AOD);
waitForSensorManager();
verify(mSensors).requestTriggerSensor(any(), eq(mTapSensor));
}
@@ -207,7 +210,7 @@ public class DozeTriggersTest extends SysuiTestCase {
mTriggers.onSensor(DozeLog.REASON_SENSOR_QUICK_PICKUP, 100, 100, null);
// THEN device goes into aod (shows clock with black background)
- verify(mMachine).requestState(DozeMachine.State.DOZE_AOD);
+ verify(mMachine).requestState(DOZE_AOD);
// THEN a log is taken that quick pick up was triggered
verify(mUiEventLogger).log(DozingUpdateUiEvent.DOZING_UPDATE_QUICK_PICKUP);
@@ -218,7 +221,7 @@ public class DozeTriggersTest extends SysuiTestCase {
// GIVEN quick pickup is triggered when device is in DOZE
when(mMachine.getState()).thenReturn(DozeMachine.State.DOZE);
mTriggers.onSensor(DozeLog.REASON_SENSOR_QUICK_PICKUP, 100, 100, null);
- verify(mMachine).requestState(DozeMachine.State.DOZE_AOD);
+ verify(mMachine).requestState(DOZE_AOD);
verify(mMachine, never()).requestState(DozeMachine.State.DOZE);
// WHEN next executable is run
@@ -234,6 +237,8 @@ public class DozeTriggersTest extends SysuiTestCase {
@Test
public void testOnSensor_Fingerprint() {
+ // GIVEN dozing state
+ when(mMachine.getState()).thenReturn(DOZE_AOD);
final int screenX = 100;
final int screenY = 100;
final float misc = -1;
@@ -241,8 +246,20 @@ public class DozeTriggersTest extends SysuiTestCase {
final float major = 3f;
final int reason = DozeLog.REASON_SENSOR_UDFPS_LONG_PRESS;
float[] rawValues = new float[]{screenX, screenY, misc, major, minor};
+
+ // WHEN longpress gesture is triggered
mTriggers.onSensor(reason, screenX, screenY, rawValues);
+
+ // THEN
+ // * don't immediately send interrupt
+ // * immediately extend pulse
+ verify(mAuthController, never()).onAodInterrupt(anyInt(), anyInt(), anyFloat(), anyFloat());
verify(mHost).extendPulse(reason);
+
+ // WHEN display state changes to ON
+ mTriggers.onScreenState(Display.STATE_ON);
+
+ // THEN send interrupt
verify(mAuthController).onAodInterrupt(eq(screenX), eq(screenY), eq(major), eq(minor));
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeUiTest.java b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeUiTest.java
index afe5c0b2edbd..1d34aac3b1cb 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeUiTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeUiTest.java
@@ -43,6 +43,7 @@ import com.android.keyguard.KeyguardUpdateMonitor;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.statusbar.phone.DozeParameters;
+import com.android.systemui.statusbar.policy.ConfigurationController;
import com.android.systemui.tuner.TunerService;
import com.android.systemui.util.wakelock.WakeLockFake;
@@ -77,6 +78,8 @@ public class DozeUiTest extends SysuiTestCase {
private DozeUi mDozeUi;
@Mock
private StatusBarStateController mStatusBarStateController;
+ @Mock
+ private ConfigurationController mConfigurationController;
@Before
public void setUp() throws Exception {
@@ -89,7 +92,7 @@ public class DozeUiTest extends SysuiTestCase {
mDozeUi = new DozeUi(mContext, mAlarmManager, mWakeLock, mHost, mHandler,
mDozeParameters, mKeyguardUpdateMonitor, mDozeLog, mTunerService,
- () -> mStatusBarStateController);
+ () -> mStatusBarStateController, mConfigurationController);
mDozeUi.setDozeMachine(mMachine);
}
@@ -146,7 +149,7 @@ public class DozeUiTest extends SysuiTestCase {
when(mDozeParameters.getDisplayNeedsBlanking()).thenReturn(true);
mDozeUi = new DozeUi(mContext, mAlarmManager, mWakeLock, mHost, mHandler,
mDozeParameters, mKeyguardUpdateMonitor, mDozeLog, mTunerService,
- () -> mStatusBarStateController);
+ () -> mStatusBarStateController, mConfigurationController);
mDozeUi.setDozeMachine(mMachine);
// Never animate if display doesn't support it.
diff --git a/packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsDialogLiteTest.java b/packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsDialogLiteTest.java
index 83e7b17eb746..adc8ffc1d633 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsDialogLiteTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsDialogLiteTest.java
@@ -30,6 +30,7 @@ import static org.mockito.Mockito.when;
import android.app.IActivityManager;
import android.app.admin.DevicePolicyManager;
import android.app.trust.TrustManager;
+import android.content.pm.PackageManager;
import android.content.res.Resources;
import android.graphics.Color;
import android.media.AudioManager;
@@ -38,8 +39,9 @@ import android.os.UserManager;
import android.service.dreams.IDreamManager;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
+import android.view.GestureDetector;
import android.view.IWindowManager;
-import android.view.View;
+import android.view.MotionEvent;
import android.view.WindowManagerPolicyConstants;
import androidx.test.filters.SmallTest;
@@ -57,6 +59,7 @@ import com.android.systemui.plugins.GlobalActions;
import com.android.systemui.settings.UserContextProvider;
import com.android.systemui.statusbar.NotificationShadeDepthController;
import com.android.systemui.statusbar.NotificationShadeWindowController;
+import com.android.systemui.statusbar.phone.StatusBar;
import com.android.systemui.statusbar.policy.ConfigurationController;
import com.android.systemui.statusbar.policy.KeyguardStateController;
import com.android.systemui.telephony.TelephonyListenerManager;
@@ -107,8 +110,10 @@ public class GlobalActionsDialogLiteTest extends SysuiTestCase {
@Mock private RingerModeTracker mRingerModeTracker;
@Mock private RingerModeLiveData mRingerModeLiveData;
@Mock private SysUiState mSysUiState;
+ @Mock private PackageManager mPackageManager;
@Mock private Handler mHandler;
@Mock private UserContextProvider mUserContextProvider;
+ @Mock private StatusBar mStatusBar;
private TestableLooper mTestableLooper;
@@ -120,6 +125,8 @@ public class GlobalActionsDialogLiteTest extends SysuiTestCase {
when(mRingerModeTracker.getRingerMode()).thenReturn(mRingerModeLiveData);
when(mUserContextProvider.getUserContext()).thenReturn(mContext);
+ when(mResources.getConfiguration()).thenReturn(
+ getContext().getResources().getConfiguration());
mGlobalActionsDialogLite = new GlobalActionsDialogLite(mContext,
mWindowManagerFuncs,
@@ -150,7 +157,9 @@ public class GlobalActionsDialogLiteTest extends SysuiTestCase {
mInfoProvider,
mRingerModeTracker,
mSysUiState,
- mHandler
+ mHandler,
+ mPackageManager,
+ mStatusBar
);
mGlobalActionsDialogLite.setZeroDialogPressDelayForTesting();
@@ -194,7 +203,7 @@ public class GlobalActionsDialogLiteTest extends SysuiTestCase {
}
@Test
- public void testShouldLogOnTapOutside() {
+ public void testSingleTap_logAndDismiss() {
mGlobalActionsDialogLite = spy(mGlobalActionsDialogLite);
doReturn(4).when(mGlobalActionsDialogLite).getMaxShownPowerItems();
doReturn(true).when(mGlobalActionsDialogLite).shouldDisplayLockdown(any());
@@ -207,9 +216,58 @@ public class GlobalActionsDialogLiteTest extends SysuiTestCase {
};
doReturn(actions).when(mGlobalActionsDialogLite).getDefaultActions();
GlobalActionsDialogLite.ActionsDialogLite dialog = mGlobalActionsDialogLite.createDialog();
- View container = dialog.findViewById(com.android.systemui.R.id.global_actions_container);
- container.callOnClick();
+
+ GestureDetector.SimpleOnGestureListener gestureListener = spy(dialog.mGestureListener);
+ gestureListener.onSingleTapConfirmed(null);
+ verifyLogPosted(GlobalActionsDialog.GlobalActionsEvent.GA_CLOSE_TAP_OUTSIDE);
+ }
+
+ @Test
+ public void testSwipeDownLockscreen_logAndOpenQS() {
+ mGlobalActionsDialogLite = spy(mGlobalActionsDialogLite);
+ doReturn(4).when(mGlobalActionsDialogLite).getMaxShownPowerItems();
+ doReturn(true).when(mGlobalActionsDialogLite).shouldDisplayLockdown(any());
+ doReturn(true).when(mGlobalActionsDialogLite).shouldShowAction(any());
+ doReturn(true).when(mStatusBar).isKeyguardShowing();
+ String[] actions = {
+ GlobalActionsDialog.GLOBAL_ACTION_KEY_EMERGENCY,
+ GlobalActionsDialog.GLOBAL_ACTION_KEY_LOCKDOWN,
+ GlobalActionsDialog.GLOBAL_ACTION_KEY_POWER,
+ GlobalActionsDialog.GLOBAL_ACTION_KEY_RESTART,
+ };
+ doReturn(actions).when(mGlobalActionsDialogLite).getDefaultActions();
+ GlobalActionsDialogLite.ActionsDialogLite dialog = mGlobalActionsDialogLite.createDialog();
+
+ GestureDetector.SimpleOnGestureListener gestureListener = spy(dialog.mGestureListener);
+ MotionEvent start = MotionEvent.obtain(0, 0, MotionEvent.ACTION_DOWN, 0, 0, 0);
+ MotionEvent end = MotionEvent.obtain(0, 0, MotionEvent.ACTION_DOWN, 0, 500, 0);
+ gestureListener.onFling(start, end, 0, 1000);
+ verifyLogPosted(GlobalActionsDialog.GlobalActionsEvent.GA_CLOSE_TAP_OUTSIDE);
+ verify(mStatusBar).animateExpandSettingsPanel(null);
+ }
+
+ @Test
+ public void testSwipeDown_logAndOpenNotificationShade() {
+ mGlobalActionsDialogLite = spy(mGlobalActionsDialogLite);
+ doReturn(4).when(mGlobalActionsDialogLite).getMaxShownPowerItems();
+ doReturn(true).when(mGlobalActionsDialogLite).shouldDisplayLockdown(any());
+ doReturn(true).when(mGlobalActionsDialogLite).shouldShowAction(any());
+ doReturn(false).when(mStatusBar).isKeyguardShowing();
+ String[] actions = {
+ GlobalActionsDialog.GLOBAL_ACTION_KEY_EMERGENCY,
+ GlobalActionsDialog.GLOBAL_ACTION_KEY_LOCKDOWN,
+ GlobalActionsDialog.GLOBAL_ACTION_KEY_POWER,
+ GlobalActionsDialog.GLOBAL_ACTION_KEY_RESTART,
+ };
+ doReturn(actions).when(mGlobalActionsDialogLite).getDefaultActions();
+ GlobalActionsDialogLite.ActionsDialogLite dialog = mGlobalActionsDialogLite.createDialog();
+
+ GestureDetector.SimpleOnGestureListener gestureListener = spy(dialog.mGestureListener);
+ MotionEvent start = MotionEvent.obtain(0, 0, MotionEvent.ACTION_DOWN, 0, 0, 0);
+ MotionEvent end = MotionEvent.obtain(0, 0, MotionEvent.ACTION_DOWN, 0, 500, 0);
+ gestureListener.onFling(start, end, 0, 1000);
verifyLogPosted(GlobalActionsDialog.GlobalActionsEvent.GA_CLOSE_TAP_OUTSIDE);
+ verify(mStatusBar).animateExpandNotificationsPanel();
}
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsDialogTest.java b/packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsDialogTest.java
index 3130e977dc83..e5c104e7d377 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsDialogTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsDialogTest.java
@@ -33,6 +33,7 @@ import static org.mockito.Mockito.when;
import android.app.IActivityManager;
import android.app.admin.DevicePolicyManager;
import android.app.trust.TrustManager;
+import android.content.pm.PackageManager;
import android.content.pm.UserInfo;
import android.content.res.Resources;
import android.graphics.Color;
@@ -65,6 +66,7 @@ import com.android.systemui.plugins.GlobalActionsPanelPlugin;
import com.android.systemui.settings.UserTracker;
import com.android.systemui.statusbar.NotificationShadeDepthController;
import com.android.systemui.statusbar.NotificationShadeWindowController;
+import com.android.systemui.statusbar.phone.StatusBar;
import com.android.systemui.statusbar.policy.ConfigurationController;
import com.android.systemui.statusbar.policy.KeyguardStateController;
import com.android.systemui.telephony.TelephonyListenerManager;
@@ -123,7 +125,9 @@ public class GlobalActionsDialogTest extends SysuiTestCase {
@Mock GlobalActionsPanelPlugin.PanelViewController mWalletController;
@Mock private Handler mHandler;
@Mock private UserTracker mUserTracker;
+ @Mock private PackageManager mPackageManager;
@Mock private SecureSettings mSecureSettings;
+ @Mock private StatusBar mStatusBar;
private TestableLooper mTestableLooper;
@@ -134,6 +138,8 @@ public class GlobalActionsDialogTest extends SysuiTestCase {
allowTestableLooperAsMainThread();
when(mRingerModeTracker.getRingerMode()).thenReturn(mRingerModeLiveData);
+ when(mResources.getConfiguration()).thenReturn(
+ getContext().getResources().getConfiguration());
mGlobalActionsDialog = new GlobalActionsDialog(mContext,
mWindowManagerFuncs,
@@ -164,7 +170,9 @@ public class GlobalActionsDialogTest extends SysuiTestCase {
mUiEventLogger,
mRingerModeTracker,
mSysUiState,
- mHandler
+ mHandler,
+ mPackageManager,
+ mStatusBar
);
mGlobalActionsDialog.setZeroDialogPressDelayForTesting();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardIndicationRotateTextViewControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardIndicationRotateTextViewControllerTest.java
index 2f78532b9e71..51576687880c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardIndicationRotateTextViewControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardIndicationRotateTextViewControllerTest.java
@@ -34,7 +34,6 @@ import android.content.res.ColorStateList;
import android.graphics.Color;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper.RunWithLooper;
-import android.view.View;
import androidx.test.filters.SmallTest;
@@ -258,8 +257,8 @@ public class KeyguardIndicationRotateTextViewControllerTest extends SysuiTestCas
// WHEN the device is dozing
mStatusBarStateListener.onDozingChanged(true);
- // THEN the view is GONE
- verify(mView).setVisibility(View.GONE);
+ // THEN switch to INDICATION_TYPE_NONE
+ verify(mView).switchIndication(null);
}
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/MediaDataManagerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/MediaDataManagerTest.kt
index 15cfee828293..3128db423a24 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/MediaDataManagerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/MediaDataManagerTest.kt
@@ -9,8 +9,8 @@ import android.media.MediaDescription
import android.media.MediaMetadata
import android.media.session.MediaController
import android.media.session.MediaSession
-import android.provider.Settings
import android.os.Bundle
+import android.provider.Settings
import android.service.notification.StatusBarNotification
import android.testing.AndroidTestingRunner
import android.testing.TestableLooper.RunWithLooper
@@ -20,6 +20,7 @@ import com.android.systemui.broadcast.BroadcastDispatcher
import com.android.systemui.dump.DumpManager
import com.android.systemui.plugins.ActivityStarter
import com.android.systemui.statusbar.SbnBuilder
+import com.android.systemui.tuner.TunerService
import com.android.systemui.util.concurrency.FakeExecutor
import com.android.systemui.util.mockito.capture
import com.android.systemui.util.mockito.eq
@@ -86,6 +87,8 @@ class MediaDataManagerTest : SysuiTestCase() {
lateinit var mediaNotification: StatusBarNotification
@Captor lateinit var mediaDataCaptor: ArgumentCaptor<MediaData>
private val clock = FakeSystemClock()
+ @Mock private lateinit var tunerService: TunerService
+ @Captor lateinit var tunableCaptor: ArgumentCaptor<TunerService.Tunable>
private val originalSmartspaceSetting = Settings.Secure.getInt(context.contentResolver,
Settings.Secure.MEDIA_CONTROLS_RECOMMENDATION, 1)
@@ -114,8 +117,11 @@ class MediaDataManagerTest : SysuiTestCase() {
smartspaceMediaDataProvider = smartspaceMediaDataProvider,
useMediaResumption = true,
useQsMediaPlayer = true,
- systemClock = clock
+ systemClock = clock,
+ tunerService = tunerService
)
+ verify(tunerService).addTunable(capture(tunableCaptor),
+ eq(Settings.Secure.MEDIA_CONTROLS_RECOMMENDATION))
session = MediaSession(context, "MediaDataManagerTestSession")
mediaNotification = SbnBuilder().run {
setPkg(PACKAGE_NAME)
@@ -364,6 +370,9 @@ class MediaDataManagerTest : SysuiTestCase() {
fun testOnSmartspaceMediaDataLoaded_hasNoneMediaTarget_callsRemoveListener() {
smartspaceMediaDataProvider.onTargetsAvailable(listOf(mediaSmartspaceTarget))
smartspaceMediaDataProvider.onTargetsAvailable(listOf())
+ foregroundExecutor.advanceClockToLast()
+ foregroundExecutor.runAllReady()
+
verify(listener).onSmartspaceMediaDataRemoved(eq(KEY_MEDIA_SMARTSPACE), eq(false))
}
@@ -372,6 +381,8 @@ class MediaDataManagerTest : SysuiTestCase() {
// WHEN media recommendation setting is off
Settings.Secure.putInt(context.contentResolver,
Settings.Secure.MEDIA_CONTROLS_RECOMMENDATION, 0)
+ tunableCaptor.value.onTuningChanged(Settings.Secure.MEDIA_CONTROLS_RECOMMENDATION, "0")
+
smartspaceMediaDataProvider.onTargetsAvailable(listOf(mediaSmartspaceTarget))
// THEN smartspace signal is ignored
@@ -380,6 +391,24 @@ class MediaDataManagerTest : SysuiTestCase() {
}
@Test
+ fun testMediaRecommendationDisabled_removesSmartspaceData() {
+ // GIVEN a media recommendation card is present
+ smartspaceMediaDataProvider.onTargetsAvailable(listOf(mediaSmartspaceTarget))
+ verify(listener).onSmartspaceMediaDataLoaded(eq(KEY_MEDIA_SMARTSPACE), anyObject(),
+ anyBoolean())
+
+ // WHEN the media recommendation setting is turned off
+ Settings.Secure.putInt(context.contentResolver,
+ Settings.Secure.MEDIA_CONTROLS_RECOMMENDATION, 0)
+ tunableCaptor.value.onTuningChanged(Settings.Secure.MEDIA_CONTROLS_RECOMMENDATION, "0")
+
+ // THEN listeners are notified
+ foregroundExecutor.advanceClockToLast()
+ foregroundExecutor.runAllReady()
+ verify(listener).onSmartspaceMediaDataRemoved(eq(KEY_MEDIA_SMARTSPACE), eq(true))
+ }
+
+ @Test
fun testOnMediaDataChanged_updatesLastActiveTime() {
val currentTime = clock.elapsedRealtime()
mediaDataManager.onNotificationAdded(KEY, mediaNotification)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/people/widget/LaunchConversationActivityTest.java b/packages/SystemUI/tests/src/com/android/systemui/people/widget/LaunchConversationActivityTest.java
index 5f4d90b3666f..f6264ffc6a70 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/people/widget/LaunchConversationActivityTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/people/widget/LaunchConversationActivityTest.java
@@ -49,6 +49,7 @@ import com.android.systemui.wmshell.BubblesManager;
import com.android.wm.shell.bubbles.Bubble;
import org.junit.Before;
+import org.junit.Ignore;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
@@ -213,6 +214,7 @@ public class LaunchConversationActivityTest extends SysuiTestCase {
verify(mBubblesManager, never()).expandStackAndSelectBubble(any(NotificationEntry.class));
}
+ @Ignore
@Test
public void testBubbleWithNoNotifOpensBubble() throws Exception {
Bubble bubble = mock(Bubble.class);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QSFragmentTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/QSFragmentTest.java
index b0e3e3e936a9..2ae4cbe17ac6 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/QSFragmentTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QSFragmentTest.java
@@ -46,6 +46,7 @@ import com.android.systemui.dump.DumpManager;
import com.android.systemui.media.MediaHost;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.qs.dagger.QSFragmentComponent;
+import com.android.systemui.qs.external.CustomTileStatePersister;
import com.android.systemui.qs.logging.QSLogger;
import com.android.systemui.qs.tileimpl.QSFactoryImpl;
import com.android.systemui.settings.UserTracker;
@@ -132,7 +133,7 @@ public class QSFragmentTest extends SysuiBaseFragmentTest {
() -> mock(AutoTileManager.class), mock(DumpManager.class),
mock(BroadcastDispatcher.class), Optional.of(mock(StatusBar.class)),
mock(QSLogger.class), mock(UiEventLogger.class), mock(UserTracker.class),
- mock(SecureSettings.class));
+ mock(SecureSettings.class), mock(CustomTileStatePersister.class));
qs.setHost(host);
qs.setListening(true);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QSTileHostTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/QSTileHostTest.java
index 7c73b4c44e90..69bdcbcff270 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/QSTileHostTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QSTileHostTest.java
@@ -59,6 +59,8 @@ import com.android.systemui.plugins.qs.QSFactory;
import com.android.systemui.plugins.qs.QSTile;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.qs.external.CustomTile;
+import com.android.systemui.qs.external.CustomTileStatePersister;
+import com.android.systemui.qs.external.TileServiceKey;
import com.android.systemui.qs.logging.QSLogger;
import com.android.systemui.qs.tileimpl.QSTileImpl;
import com.android.systemui.settings.UserTracker;
@@ -125,6 +127,8 @@ public class QSTileHostTest extends SysuiTestCase {
private UserTracker mUserTracker;
@Mock
private SecureSettings mSecureSettings;
+ @Mock
+ private CustomTileStatePersister mCustomTileStatePersister;
private Handler mHandler;
private TestableLooper mLooper;
@@ -145,7 +149,7 @@ public class QSTileHostTest extends SysuiTestCase {
mQSTileHost = new TestQSTileHost(mContext, mIconController, mDefaultFactory, mHandler,
mLooper.getLooper(), mPluginManager, mTunerService, mAutoTiles, mDumpManager,
mBroadcastDispatcher, mStatusBar, mQSLogger, mUiEventLogger, mUserTracker,
- mSecureSettings);
+ mSecureSettings, mCustomTileStatePersister);
setUpTileFactory();
when(mSecureSettings.getStringForUser(eq(QSTileHost.TILES_SETTING), anyInt()))
@@ -371,6 +375,14 @@ public class QSTileHostTest extends SysuiTestCase {
verify(mQSLogger, never()).logTileDestroyed(isNull(), anyString());
}
+ @Test
+ public void testCustomTileRemoved_stateDeleted() {
+ mQSTileHost.changeTiles(List.of(CUSTOM_TILE_SPEC), List.of());
+
+ verify(mCustomTileStatePersister)
+ .removeState(new TileServiceKey(CUSTOM_TILE, mQSTileHost.getUserId()));
+ }
+
private class TestQSTileHost extends QSTileHost {
TestQSTileHost(Context context, StatusBarIconController iconController,
QSFactory defaultFactory, Handler mainHandler, Looper bgLooper,
@@ -378,10 +390,11 @@ public class QSTileHostTest extends SysuiTestCase {
Provider<AutoTileManager> autoTiles, DumpManager dumpManager,
BroadcastDispatcher broadcastDispatcher, StatusBar statusBar, QSLogger qsLogger,
UiEventLogger uiEventLogger, UserTracker userTracker,
- SecureSettings secureSettings) {
+ SecureSettings secureSettings, CustomTileStatePersister customTileStatePersister) {
super(context, iconController, defaultFactory, mainHandler, bgLooper, pluginManager,
tunerService, autoTiles, dumpManager, broadcastDispatcher,
- Optional.of(statusBar), qsLogger, uiEventLogger, userTracker, secureSettings);
+ Optional.of(statusBar), qsLogger, uiEventLogger, userTracker, secureSettings,
+ customTileStatePersister);
}
@Override
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/external/CustomTileStatePersisterTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/external/CustomTileStatePersisterTest.kt
new file mode 100644
index 000000000000..6c96576bcbc1
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/external/CustomTileStatePersisterTest.kt
@@ -0,0 +1,159 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.qs.external
+
+import android.content.ComponentName
+import android.content.Context
+import android.content.SharedPreferences
+import android.service.quicksettings.Tile
+import android.testing.AndroidTestingRunner
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.util.mockito.capture
+import com.android.systemui.util.mockito.eq
+import com.google.common.truth.Truth.assertThat
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.Answers
+import org.mockito.ArgumentCaptor
+import org.mockito.ArgumentMatchers.any
+import org.mockito.ArgumentMatchers.anyInt
+import org.mockito.ArgumentMatchers.anyString
+import org.mockito.Captor
+import org.mockito.Mock
+import org.mockito.Mockito.`when`
+import org.mockito.Mockito.verify
+import org.mockito.MockitoAnnotations
+
+@SmallTest
+@RunWith(AndroidTestingRunner::class)
+class CustomTileStatePersisterTest : SysuiTestCase() {
+
+ companion object {
+ private val TEST_COMPONENT = ComponentName("pkg", "cls")
+ private const val TEST_USER = 0
+ private val KEY = TileServiceKey(TEST_COMPONENT, TEST_USER)
+
+ private const val TEST_STATE = Tile.STATE_INACTIVE
+ private const val TEST_LABEL = "test_label"
+ private const val TEST_SUBTITLE = "test_subtitle"
+ private const val TEST_CONTENT_DESCRIPTION = "test_content_description"
+ private const val TEST_STATE_DESCRIPTION = "test_state_description"
+
+ private fun Tile.isEqualTo(other: Tile): Boolean {
+ return state == other.state &&
+ label == other.label &&
+ subtitle == other.subtitle &&
+ contentDescription == other.contentDescription &&
+ stateDescription == other.stateDescription
+ }
+ }
+
+ @Mock
+ private lateinit var mockContext: Context
+ @Mock
+ private lateinit var sharedPreferences: SharedPreferences
+ @Mock(answer = Answers.RETURNS_SELF)
+ private lateinit var editor: SharedPreferences.Editor
+ private lateinit var tile: Tile
+ private lateinit var customTileStatePersister: CustomTileStatePersister
+
+ @Captor
+ private lateinit var stringCaptor: ArgumentCaptor<String>
+
+ @Before
+ fun setUp() {
+ MockitoAnnotations.initMocks(this)
+ `when`(mockContext.getSharedPreferences(anyString(), anyInt()))
+ .thenReturn(sharedPreferences)
+ `when`(sharedPreferences.edit()).thenReturn(editor)
+
+ tile = Tile()
+ customTileStatePersister = CustomTileStatePersister(mockContext)
+ }
+
+ @Test
+ fun testWriteState() {
+ tile.apply {
+ state = TEST_STATE
+ label = TEST_LABEL
+ subtitle = TEST_SUBTITLE
+ contentDescription = TEST_CONTENT_DESCRIPTION
+ stateDescription = TEST_STATE_DESCRIPTION
+ }
+
+ customTileStatePersister.persistState(KEY, tile)
+
+ verify(editor).putString(eq(KEY.toString()), capture(stringCaptor))
+
+ assertThat(tile.isEqualTo(readTileFromString(stringCaptor.value))).isTrue()
+ }
+
+ @Test
+ fun testReadState() {
+ tile.apply {
+ state = TEST_STATE
+ label = TEST_LABEL
+ subtitle = TEST_SUBTITLE
+ contentDescription = TEST_CONTENT_DESCRIPTION
+ stateDescription = TEST_STATE_DESCRIPTION
+ }
+
+ `when`(sharedPreferences.getString(eq(KEY.toString()), any()))
+ .thenReturn(writeToString(tile))
+
+ assertThat(tile.isEqualTo(customTileStatePersister.readState(KEY)!!)).isTrue()
+ }
+
+ @Test
+ fun testReadStateDefault() {
+ `when`(sharedPreferences.getString(any(), any())).thenAnswer {
+ it.getArgument(1)
+ }
+
+ assertThat(customTileStatePersister.readState(KEY)).isNull()
+ }
+
+ @Test
+ fun testStoreNulls() {
+ assertThat(tile.label).isNull()
+
+ customTileStatePersister.persistState(KEY, tile)
+
+ verify(editor).putString(eq(KEY.toString()), capture(stringCaptor))
+
+ assertThat(readTileFromString(stringCaptor.value).label).isNull()
+ }
+
+ @Test
+ fun testReadNulls() {
+ assertThat(tile.label).isNull()
+
+ `when`(sharedPreferences.getString(eq(KEY.toString()), any()))
+ .thenReturn(writeToString(tile))
+
+ assertThat(customTileStatePersister.readState(KEY)!!.label).isNull()
+ }
+
+ @Test
+ fun testRemoveState() {
+ customTileStatePersister.removeState(KEY)
+
+ verify(editor).remove(KEY.toString())
+ }
+} \ No newline at end of file
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/external/CustomTileTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/external/CustomTileTest.kt
index b1c3d1da8fea..9b5c1619ef31 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/external/CustomTileTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/external/CustomTileTest.kt
@@ -38,6 +38,7 @@ import com.android.systemui.plugins.qs.QSTile
import com.android.systemui.plugins.statusbar.StatusBarStateController
import com.android.systemui.qs.QSHost
import com.android.systemui.qs.logging.QSLogger
+import com.android.systemui.util.mockito.any
import org.junit.Assert.assertEquals
import org.junit.Assert.assertFalse
import org.junit.Assert.assertTrue
@@ -48,8 +49,9 @@ import org.mockito.ArgumentMatchers.anyInt
import org.mockito.ArgumentMatchers.anyString
import org.mockito.Mock
import org.mockito.Mockito.`when`
-import org.mockito.Mockito.any
import org.mockito.Mockito.mock
+import org.mockito.Mockito.never
+import org.mockito.Mockito.verify
import org.mockito.MockitoAnnotations
@SmallTest
@@ -76,6 +78,7 @@ class CustomTileTest : SysuiTestCase() {
@Mock private lateinit var packageManager: PackageManager
@Mock private lateinit var applicationInfo: ApplicationInfo
@Mock private lateinit var serviceInfo: ServiceInfo
+ @Mock private lateinit var customTileStatePersister: CustomTileStatePersister
private lateinit var customTile: CustomTile
private lateinit var testableLooper: TestableLooper
@@ -108,10 +111,13 @@ class CustomTileTest : SysuiTestCase() {
metricsLogger,
statusBarStateController,
activityStarter,
- qsLogger
+ qsLogger,
+ customTileStatePersister
)
customTile = CustomTile.create(customTileBuilder, TILE_SPEC, mContext)
+ customTile.initialize()
+ testableLooper.processAllMessages()
}
@Test
@@ -123,6 +129,8 @@ class CustomTileTest : SysuiTestCase() {
`when`(userContext.userId).thenReturn(10)
val tile = CustomTile.create(customTileBuilder, TILE_SPEC, userContext)
+ tile.initialize()
+ testableLooper.processAllMessages()
assertEquals(10, tile.user)
}
@@ -131,6 +139,8 @@ class CustomTileTest : SysuiTestCase() {
fun testToggleableTileHasBooleanState() {
`when`(tileServiceManager.isToggleableTile).thenReturn(true)
customTile = CustomTile.create(customTileBuilder, TILE_SPEC, mContext)
+ customTile.initialize()
+ testableLooper.processAllMessages()
assertTrue(customTile.state is QSTile.BooleanState)
assertTrue(customTile.newTileState() is QSTile.BooleanState)
@@ -146,6 +156,9 @@ class CustomTileTest : SysuiTestCase() {
fun testValueUpdatedInBooleanTile() {
`when`(tileServiceManager.isToggleableTile).thenReturn(true)
customTile = CustomTile.create(customTileBuilder, TILE_SPEC, mContext)
+ customTile.initialize()
+ testableLooper.processAllMessages()
+
customTile.qsTile.icon = mock(Icon::class.java)
`when`(customTile.qsTile.icon.loadDrawable(any(Context::class.java)))
.thenReturn(mock(Drawable::class.java))
@@ -173,4 +186,88 @@ class CustomTileTest : SysuiTestCase() {
.thenReturn(null)
customTile.handleUpdateState(customTile.newTileState(), null)
}
+
+ @Test
+ fun testNoLoadStateTileNotActive() {
+ // Not active by default
+ testableLooper.processAllMessages()
+
+ verify(customTileStatePersister, never()).readState(any())
+ }
+
+ @Test
+ fun testNoPersistedStateTileNotActive() {
+ // Not active by default
+ val t = Tile().apply {
+ state = Tile.STATE_INACTIVE
+ }
+ customTile.updateTileState(t)
+ testableLooper.processAllMessages()
+
+ verify(customTileStatePersister, never()).persistState(any(), any())
+ }
+
+ @Test
+ fun testPersistedStateRetrieved() {
+ val state = Tile.STATE_INACTIVE
+ val label = "test_label"
+ val subtitle = "test_subtitle"
+ val contentDescription = "test_content_description"
+ val stateDescription = "test_state_description"
+
+ val t = Tile().apply {
+ this.state = state
+ this.label = label
+ this.subtitle = subtitle
+ this.contentDescription = contentDescription
+ this.stateDescription = stateDescription
+ }
+ `when`(tileServiceManager.isActiveTile).thenReturn(true)
+ `when`(customTileStatePersister
+ .readState(TileServiceKey(componentName, customTile.user))).thenReturn(t)
+ val tile = CustomTile.create(customTileBuilder, TILE_SPEC, mContext)
+ tile.initialize()
+ testableLooper.processAllMessages()
+
+ // Make sure we have an icon in the tile because we don't have a default icon
+ // This should not be overridden by the retrieved tile that has null icon.
+ tile.qsTile.icon = mock(Icon::class.java)
+ `when`(tile.qsTile.icon.loadDrawable(any(Context::class.java)))
+ .thenReturn(mock(Drawable::class.java))
+
+ tile.refreshState()
+
+ testableLooper.processAllMessages()
+
+ val tileState = tile.state
+
+ assertEquals(state, tileState.state)
+ assertEquals(label, tileState.label)
+ assertEquals(subtitle, tileState.secondaryLabel)
+ assertEquals(contentDescription, tileState.contentDescription)
+ assertEquals(stateDescription, tileState.stateDescription)
+ }
+
+ @Test
+ fun testStoreStateOnChange() {
+ val t = Tile().apply {
+ state = Tile.STATE_INACTIVE
+ label = "test_label"
+ subtitle = "test_subtitle"
+ contentDescription = "test_content_description"
+ stateDescription = "test_state_description"
+ }
+ `when`(tileServiceManager.isActiveTile).thenReturn(true)
+
+ val tile = CustomTile.create(customTileBuilder, TILE_SPEC, mContext)
+ tile.initialize()
+ testableLooper.processAllMessages()
+
+ tile.updateTileState(t)
+
+ testableLooper.processAllMessages()
+
+ verify(customTileStatePersister)
+ .persistState(TileServiceKey(componentName, customTile.user), t)
+ }
} \ No newline at end of file
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/external/TileServicesTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/external/TileServicesTest.java
index 641f917bcfbe..2b1840462291 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/external/TileServicesTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/external/TileServicesTest.java
@@ -118,7 +118,8 @@ public class TileServicesTest extends SysuiTestCase {
mQSLogger,
mUiEventLogger,
mUserTracker,
- mSecureSettings);
+ mSecureSettings,
+ mock(CustomTileStatePersister.class));
mTileService = new TestTileServices(host, Looper.getMainLooper(), mBroadcastDispatcher,
mUserTracker);
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/QSTileImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/QSTileImplTest.java
index 80231a49bb44..ea4d7cc2529c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/QSTileImplTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/QSTileImplTest.java
@@ -121,6 +121,9 @@ public class QSTileImplTest extends SysuiTestCase {
mTile = new TileImpl(mHost, mTestableLooper.getLooper(), mainHandler, mFalsingManager,
mMetricsLogger, mStatusBarStateController, mActivityStarter, mQsLogger);
+ mTile.initialize();
+ mTestableLooper.processAllMessages();
+
mTile.setTileSpec(SPEC);
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/AlarmTileTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/AlarmTileTest.kt
index 32b1f433dfcf..5e2d8fde84da 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/AlarmTileTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/AlarmTileTest.kt
@@ -80,6 +80,8 @@ class AlarmTileTest : SysuiTestCase() {
nextAlarmController
)
+ tile.initialize()
+
verify(nextAlarmController).observe(eq(tile), capture(callbackCaptor))
tile.refreshState()
testableLooper.processAllMessages()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/BatterySaverTileTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/BatterySaverTileTest.kt
index f17bd56d0052..1bf83513d472 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/BatterySaverTileTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/BatterySaverTileTest.kt
@@ -87,6 +87,9 @@ class BatterySaverTileTest : SysuiTestCase() {
qsLogger,
batteryController,
secureSettings)
+
+ tile.initialize()
+ testableLooper.processAllMessages()
}
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/CastTileTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/CastTileTest.java
index 7c1a5f5ebf30..d44a52607707 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/CastTileTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/CastTileTest.java
@@ -112,11 +112,14 @@ public class CastTileTest extends SysuiTestCase {
mNetworkController,
mHotspotController
);
+ mCastTile.initialize();
// We are not setting the mocks to listening, so we trigger a first refresh state to
// set the initial state
mCastTile.refreshState();
+ mTestableLooper.processAllMessages();
+
mCastTile.handleSetListening(true);
ArgumentCaptor<NetworkController.SignalCallback> signalCallbackArgumentCaptor =
ArgumentCaptor.forClass(NetworkController.SignalCallback.class);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/DeviceControlsTileTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/DeviceControlsTileTest.kt
index 6d1bbd9708ea..94af10a485fd 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/DeviceControlsTileTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/DeviceControlsTileTest.kt
@@ -378,7 +378,10 @@ class DeviceControlsTileTest : SysuiTestCase() {
qsLogger,
controlsComponent,
keyguardStateController
- )
+ ).also {
+ it.initialize()
+ testableLooper.processAllMessages()
+ }
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/NfcTileTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/NfcTileTest.java
index 99d028cd8c5c..cfd37358dcff 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/NfcTileTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/NfcTileTest.java
@@ -92,6 +92,9 @@ public class NfcTileTest extends SysuiTestCase {
mQSLogger,
mBroadcastDispatcher
);
+
+ mNfcTile.initialize();
+ mTestableLooper.processAllMessages();
}
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/QuickAccessWalletTileTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/QuickAccessWalletTileTest.java
index e4a9aacb57ab..a50cbe5adc48 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/QuickAccessWalletTileTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/QuickAccessWalletTileTest.java
@@ -155,6 +155,9 @@ public class QuickAccessWalletTileTest extends SysuiTestCase {
mPackageManager,
mSecureSettings,
mController);
+
+ mTile.initialize();
+ mTestableLooper.processAllMessages();
}
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/ReduceBrightColorsTileTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/ReduceBrightColorsTileTest.java
index df4908ddc4ef..9eb688de3511 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/ReduceBrightColorsTileTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/ReduceBrightColorsTileTest.java
@@ -89,6 +89,9 @@ public class ReduceBrightColorsTileTest extends SysuiTestCase {
mActivityStarter,
mQSLogger
);
+
+ mTile.initialize();
+ mTestableLooper.processAllMessages();
}
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/ScreenRecordTileTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/ScreenRecordTileTest.java
index 3b4e863ed8bd..964ce01312bf 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/ScreenRecordTileTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/ScreenRecordTileTest.java
@@ -91,6 +91,9 @@ public class ScreenRecordTileTest extends SysuiTestCase {
mController,
mKeyguardDismissUtil
);
+
+ mTile.initialize();
+ mTestableLooper.processAllMessages();
}
// Test that the tile is inactive and labeled correctly when the controller is neither starting
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowTest.java
index 2e0827f24bf8..fa25c3f1e005 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowTest.java
@@ -289,6 +289,7 @@ public class ExpandableNotificationRowTest extends SysuiTestCase {
@Test
public void testIconScrollXAfterTranslationAndReset() throws Exception {
+ mGroupRow.setDismissUsingRowTranslationX(false);
mGroupRow.setTranslation(50);
assertEquals(50, -mGroupRow.getEntry().getIcons().getShelfIcon().getScrollX());
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/DozeParametersTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/DozeParametersTest.java
index 9ac600afe990..5bf1bb3c573f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/DozeParametersTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/DozeParametersTest.java
@@ -63,6 +63,7 @@ public class DozeParametersTest extends SysuiTestCase {
@Mock private BatteryController mBatteryController;
@Mock private FeatureFlags mFeatureFlags;
@Mock private DumpManager mDumpManager;
+ @Mock private UnlockedScreenOffAnimationController mUnlockedScreenOffAnimationController;
@Before
public void setup() {
@@ -75,7 +76,8 @@ public class DozeParametersTest extends SysuiTestCase {
mBatteryController,
mTunerService,
mDumpManager,
- mFeatureFlags
+ mFeatureFlags,
+ mUnlockedScreenOffAnimationController
);
}
@Test
@@ -125,7 +127,8 @@ public class DozeParametersTest extends SysuiTestCase {
when(mAmbientDisplayConfiguration.alwaysOnEnabled(anyInt())).thenReturn(true);
mDozeParameters.onTuningChanged(Settings.Secure.DOZE_ALWAYS_ON, "1");
when(mFeatureFlags.useNewLockscreenAnimations()).thenReturn(true);
-
+ when(mUnlockedScreenOffAnimationController.shouldPlayUnlockedScreenOffAnimation())
+ .thenReturn(true);
assertTrue(mDozeParameters.shouldControlUnlockedScreenOff());
// Trigger the setter for the current value.
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewTest.java
index 9e939eefa705..2d51683c8ae5 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewTest.java
@@ -84,7 +84,6 @@ import com.android.systemui.media.MediaDataManager;
import com.android.systemui.media.MediaHierarchyManager;
import com.android.systemui.navigationbar.NavigationModeController;
import com.android.systemui.plugins.FalsingManager;
-import com.android.systemui.plugins.qs.QS;
import com.android.systemui.qs.QSDetailDisplayer;
import com.android.systemui.statusbar.CommandQueue;
import com.android.systemui.statusbar.FeatureFlags;
@@ -675,21 +674,6 @@ public class NotificationPanelViewTest extends SysuiTestCase {
verify(mTapAgainViewController).show();
}
- @Test
- public void testNotificationClipping_isAlignedWithNotificationScrimInSplitShade() {
- mStatusBarStateController.setState(SHADE);
- QS qs = mock(QS.class);
- when(qs.getHeader()).thenReturn(mock(View.class));
- mNotificationPanelViewController.mQs = qs;
- enableSplitShade();
-
- // hacky way to refresh notification scrim top with non-zero qsPanelBottom value
- mNotificationPanelViewController.setTransitionToFullShadeAmount(200, false, 0);
-
- verify(mAmbientState)
- .setNotificationScrimTop(NOTIFICATION_SCRIM_TOP_PADDING_IN_SPLIT_SHADE);
- }
-
private FalsingManager.FalsingTapListener getFalsingTapListener() {
for (View.OnAttachStateChangeListener listener : mOnAttachStateChangeListeners) {
listener.onViewAttachedToWindow(mView);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationShadeWindowControllerImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationShadeWindowControllerImplTest.java
index 323843098a1a..9fe47eceff1b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationShadeWindowControllerImplTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationShadeWindowControllerImplTest.java
@@ -45,6 +45,7 @@ import com.android.systemui.dump.DumpManager;
import com.android.systemui.keyguard.KeyguardViewMediator;
import com.android.systemui.statusbar.SysuiStatusBarStateController;
import com.android.systemui.statusbar.policy.ConfigurationController;
+import com.android.systemui.statusbar.policy.KeyguardStateController;
import org.junit.Before;
import org.junit.Test;
@@ -70,6 +71,7 @@ public class NotificationShadeWindowControllerImplTest extends SysuiTestCase {
@Mock private SysuiColorExtractor mColorExtractor;
@Mock ColorExtractor.GradientColors mGradientColors;
@Mock private DumpManager mDumpManager;
+ @Mock private KeyguardStateController mKeyguardStateController;
@Captor private ArgumentCaptor<WindowManager.LayoutParams> mLayoutParameters;
private NotificationShadeWindowControllerImpl mNotificationShadeWindowController;
@@ -83,7 +85,7 @@ public class NotificationShadeWindowControllerImplTest extends SysuiTestCase {
mNotificationShadeWindowController = new NotificationShadeWindowControllerImpl(mContext,
mWindowManager, mActivityManager, mDozeParameters, mStatusBarStateController,
mConfigurationController, mKeyguardViewMediator, mKeyguardBypassController,
- mColorExtractor, mDumpManager);
+ mColorExtractor, mDumpManager, mKeyguardStateController);
mNotificationShadeWindowController.setNotificationShadeView(mNotificationShadeWindowView);
mNotificationShadeWindowController.attach();
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 b2efd682bc62..cbc7c6dd0447 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
@@ -777,6 +777,12 @@ public class StatusBarTest extends SysuiTestCase {
}
@Test
+ public void testDumpBarTransitions_DoesNotCrash() {
+ StatusBar.dumpBarTransitions(
+ new PrintWriter(new ByteArrayOutputStream()), "var", /* transitions= */ null);
+ }
+
+ @Test
@RunWithLooper(setAsMainLooper = true)
public void testUpdateKeyguardState_DoesNotCrash() {
mStatusBar.setBarStateForTest(StatusBarState.KEYGUARD);
@@ -843,12 +849,14 @@ public class StatusBarTest extends SysuiTestCase {
// By default, showKeyguardImpl sets state to KEYGUARD.
mStatusBar.showKeyguardImpl();
- verify(mStatusBarStateController).setState(eq(StatusBarState.KEYGUARD));
+ verify(mStatusBarStateController).setState(
+ eq(StatusBarState.KEYGUARD), eq(false) /* force */);
// If useFullscreenUserSwitcher is true, state is set to FULLSCREEN_USER_SWITCHER.
when(mUserSwitcherController.useFullscreenUserSwitcher()).thenReturn(true);
mStatusBar.showKeyguardImpl();
- verify(mStatusBarStateController).setState(eq(StatusBarState.FULLSCREEN_USER_SWITCHER));
+ verify(mStatusBarStateController).setState(
+ eq(StatusBarState.FULLSCREEN_USER_SWITCHER), eq(false) /* force */);
}
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeKeyguardStateController.java b/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeKeyguardStateController.java
index 1aebf1c1c80d..e136d00b86f8 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeKeyguardStateController.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeKeyguardStateController.java
@@ -132,4 +132,9 @@ public class FakeKeyguardStateController implements KeyguardStateController {
public boolean canPerformSmartSpaceTransition() {
return false;
}
+
+ @Override
+ public boolean isKeyguardScreenRotationAllowed() {
+ return false;
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java b/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java
index 6e2e4cb9ecfa..496976e8f55c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java
@@ -98,6 +98,7 @@ import com.android.systemui.statusbar.phone.ShadeController;
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.KeyguardStateController;
import com.android.systemui.statusbar.policy.ZenModeController;
import com.android.wm.shell.R;
import com.android.wm.shell.ShellTaskOrganizer;
@@ -227,6 +228,8 @@ public class BubblesTest extends SysuiTestCase {
private TaskStackListenerImpl mTaskStackListener;
@Mock
private ShellTaskOrganizer mShellTaskOrganizer;
+ @Mock
+ private KeyguardStateController mKeyguardStateController;
private TestableBubblePositioner mPositioner;
@@ -249,7 +252,7 @@ public class BubblesTest extends SysuiTestCase {
mNotificationShadeWindowController = new NotificationShadeWindowControllerImpl(mContext,
mWindowManager, mActivityManager, mDozeParameters, mStatusBarStateController,
mConfigurationController, mKeyguardViewMediator, mKeyguardBypassController,
- mColorExtractor, mDumpManager);
+ mColorExtractor, mDumpManager, mKeyguardStateController);
mNotificationShadeWindowController.setNotificationShadeView(mNotificationShadeWindowView);
mNotificationShadeWindowController.attach();
@@ -287,6 +290,7 @@ public class BubblesTest extends SysuiTestCase {
// TODO: Fix
mPositioner = new TestableBubblePositioner(mContext, mWindowManager);
+ mPositioner.setMaxBubbles(5);
mBubbleData = new BubbleData(mContext, mBubbleLogger, mPositioner, syncExecutor);
TestableNotificationInterruptStateProviderImpl interruptionStateProvider =
diff --git a/packages/SystemUI/tests/src/com/android/systemui/wmshell/NewNotifPipelineBubblesTest.java b/packages/SystemUI/tests/src/com/android/systemui/wmshell/NewNotifPipelineBubblesTest.java
index 9339f81940d9..9114b7a35fd2 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/wmshell/NewNotifPipelineBubblesTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/wmshell/NewNotifPipelineBubblesTest.java
@@ -82,6 +82,7 @@ import com.android.systemui.statusbar.phone.ShadeController;
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.KeyguardStateController;
import com.android.systemui.statusbar.policy.ZenModeController;
import com.android.wm.shell.ShellTaskOrganizer;
import com.android.wm.shell.WindowManagerShellWrapper;
@@ -192,6 +193,8 @@ public class NewNotifPipelineBubblesTest extends SysuiTestCase {
private TaskStackListenerImpl mTaskStackListener;
@Mock
private ShellTaskOrganizer mShellTaskOrganizer;
+ @Mock
+ private KeyguardStateController mKeyguardStateController;
private TestableBubblePositioner mPositioner;
@@ -213,7 +216,7 @@ public class NewNotifPipelineBubblesTest extends SysuiTestCase {
mNotificationShadeWindowController = new NotificationShadeWindowControllerImpl(mContext,
mWindowManager, mActivityManager, mDozeParameters, mStatusBarStateController,
mConfigurationController, mKeyguardViewMediator, mKeyguardBypassController,
- mColorExtractor, mDumpManager);
+ mColorExtractor, mDumpManager, mKeyguardStateController);
mNotificationShadeWindowController.setNotificationShadeView(mNotificationShadeWindowView);
mNotificationShadeWindowController.attach();
@@ -232,6 +235,7 @@ public class NewNotifPipelineBubblesTest extends SysuiTestCase {
when(mZenModeController.getConfig()).thenReturn(mZenModeConfig);
mPositioner = new TestableBubblePositioner(mContext, mWindowManager);
+ mPositioner.setMaxBubbles(5);
mBubbleData = new BubbleData(mContext, mBubbleLogger, mPositioner, syncExecutor);
TestableNotificationInterruptStateProviderImpl interruptionStateProvider =
diff --git a/packages/SystemUI/tests/src/com/android/systemui/wmshell/TestableBubblePositioner.java b/packages/SystemUI/tests/src/com/android/systemui/wmshell/TestableBubblePositioner.java
index 24a7cd5c89ac..6edc373d2926 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/wmshell/TestableBubblePositioner.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/wmshell/TestableBubblePositioner.java
@@ -22,9 +22,11 @@ import android.graphics.Insets;
import android.graphics.Rect;
import android.view.WindowManager;
+import com.android.wm.shell.R;
import com.android.wm.shell.bubbles.BubblePositioner;
public class TestableBubblePositioner extends BubblePositioner {
+ private int mMaxBubbles;
public TestableBubblePositioner(Context context,
WindowManager windowManager) {
@@ -33,5 +35,15 @@ public class TestableBubblePositioner extends BubblePositioner {
updateInternal(Configuration.ORIENTATION_PORTRAIT,
Insets.of(0, 0, 0, 0),
new Rect(0, 0, 500, 1000));
+ mMaxBubbles = context.getResources().getInteger(R.integer.bubbles_max_rendered);
+ }
+
+ public void setMaxBubbles(int max) {
+ mMaxBubbles = max;
+ }
+
+ @Override
+ public int getMaxBubbles() {
+ return mMaxBubbles;
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/wmshell/WMShellTest.java b/packages/SystemUI/tests/src/com/android/systemui/wmshell/WMShellTest.java
index 1dd0b21bda30..ff15d0151ea4 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/wmshell/WMShellTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/wmshell/WMShellTest.java
@@ -37,6 +37,7 @@ import com.android.wm.shell.common.ShellExecutor;
import com.android.wm.shell.hidedisplaycutout.HideDisplayCutout;
import com.android.wm.shell.legacysplitscreen.LegacySplitScreen;
import com.android.wm.shell.onehanded.OneHanded;
+import com.android.wm.shell.onehanded.OneHandedEventCallback;
import com.android.wm.shell.onehanded.OneHandedTransitionCallback;
import com.android.wm.shell.pip.Pip;
@@ -106,6 +107,7 @@ public class WMShellTest extends SysuiTestCase {
verify(mCommandQueue).addCallback(any(CommandQueue.Callbacks.class));
verify(mScreenLifecycle).addObserver(any(ScreenLifecycle.Observer.class));
verify(mOneHanded).registerTransitionCallback(any(OneHandedTransitionCallback.class));
+ verify(mOneHanded).registerEventCallback(any(OneHandedEventCallback.class));
}
@Test
diff --git a/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java b/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java
index 83dfe8ed2576..05131d44b01e 100644
--- a/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java
+++ b/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java
@@ -454,19 +454,13 @@ public class CompanionDeviceManagerService extends SystemService implements Bind
}).cancelTimeout();
}, FgThread.getExecutor()).whenComplete(uncheckExceptions((association, err) -> {
-
- final long callingIdentity = Binder.clearCallingIdentity();
- try {
- if (err == null) {
- addAssociation(association);
- } else {
- Slog.e(LOG_TAG, "Failed to discover device(s)", err);
- callback.onFailure("No devices found: " + err.getMessage());
- }
- cleanup();
- } finally {
- Binder.restoreCallingIdentity(callingIdentity);
+ if (err == null) {
+ addAssociation(association);
+ } else {
+ Slog.e(LOG_TAG, "Failed to discover device(s)", err);
+ callback.onFailure("No devices found: " + err.getMessage());
}
+ cleanup();
}));
}
diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java
index 0802123b39da..5e388d94869d 100644
--- a/services/core/java/com/android/server/am/ActiveServices.java
+++ b/services/core/java/com/android/server/am/ActiveServices.java
@@ -1877,7 +1877,6 @@ public final class ActiveServices {
active.mNumActive++;
}
r.isForeground = true;
- r.mLogEntering = true;
// The logging of FOREGROUND_SERVICE_STATE_CHANGED__STATE__ENTER event could
// be deferred, make a copy of mAllowStartForeground and
// mAllowWhileInUsePermissionInFgs.
@@ -1903,6 +1902,9 @@ public final class ActiveServices {
AppOpsManager.ATTRIBUTION_CHAIN_ID_NONE);
registerAppOpCallbackLocked(r);
mAm.updateForegroundServiceUsageStats(r.name, r.userId, true);
+ logFGSStateChangeLocked(r,
+ FrameworkStatsLog.FOREGROUND_SERVICE_STATE_CHANGED__STATE__ENTER,
+ 0);
}
// Even if the service is already a FGS, we need to update the notification,
// so we need to call it again.
@@ -1958,6 +1960,7 @@ public final class ActiveServices {
FrameworkStatsLog.FOREGROUND_SERVICE_STATE_CHANGED__STATE__EXIT,
r.mFgsExitTime > r.mFgsEnterTime
? (int)(r.mFgsExitTime - r.mFgsEnterTime) : 0);
+ r.mFgsNotificationWasDeferred = false;
resetFgsRestrictionLocked(r);
mAm.updateForegroundServiceUsageStats(r.name, r.userId, false);
if (r.app != null) {
@@ -2162,6 +2165,7 @@ public final class ActiveServices {
}
r.fgDisplayTime = when;
r.mFgsNotificationDeferred = true;
+ r.mFgsNotificationWasDeferred = true;
r.mFgsNotificationShown = false;
mPendingFgsNotifications.add(r);
if (DEBUG_FOREGROUND_SERVICE) {
@@ -2205,11 +2209,6 @@ public final class ActiveServices {
Slog.d(TAG_SERVICE, " - service no longer running/fg, ignoring");
}
}
- // Regardless of whether we needed to post the notification or the
- // service is no longer running, we may not have logged its FGS
- // transition yet depending on the timing and API sequence that led
- // to this point - so make sure to do so.
- maybeLogFGSStateEnteredLocked(r);
}
}
if (DEBUG_FOREGROUND_SERVICE) {
@@ -2252,16 +2251,6 @@ public final class ActiveServices {
}
}
- private void maybeLogFGSStateEnteredLocked(ServiceRecord r) {
- if (r.mLogEntering) {
- logFGSStateChangeLocked(r,
- FrameworkStatsLog
- .FOREGROUND_SERVICE_STATE_CHANGED__STATE__ENTER,
- 0);
- r.mLogEntering = false;
- }
- }
-
/**
* Callback from NotificationManagerService whenever it posts a notification
* associated with a foreground service. This is the unified handling point
@@ -2280,9 +2269,7 @@ public final class ActiveServices {
&& id == sr.foregroundId
&& sr.appInfo.packageName.equals(pkg)) {
// Found it. If 'shown' is false, it means that the notification
- // subsystem will not be displaying it yet, so all we do is log
- // the "fgs entered" transition noting deferral, then we're done.
- maybeLogFGSStateEnteredLocked(sr);
+ // subsystem will not be displaying it yet.
if (shown) {
if (DEBUG_FOREGROUND_SERVICE) {
Slog.d(TAG_SERVICE, "Notification shown; canceling deferral of "
@@ -4240,6 +4227,7 @@ public final class ActiveServices {
r.isForeground = false;
r.foregroundId = 0;
r.foregroundNoti = null;
+ r.mFgsNotificationWasDeferred = false;
resetFgsRestrictionLocked(r);
// Clear start entries.
@@ -6263,7 +6251,7 @@ public final class ActiveServices {
? r.mRecentCallerApplicationInfo.targetSdkVersion : 0,
r.mInfoTempFgsAllowListReason != null
? r.mInfoTempFgsAllowListReason.mCallingUid : INVALID_UID,
- r.mFgsNotificationDeferred,
+ r.mFgsNotificationWasDeferred,
r.mFgsNotificationShown,
durationMs,
r.mStartForegroundCount,
diff --git a/services/core/java/com/android/server/am/BatteryStatsService.java b/services/core/java/com/android/server/am/BatteryStatsService.java
index 4e6e91ac7b5d..7f6e6687625c 100644
--- a/services/core/java/com/android/server/am/BatteryStatsService.java
+++ b/services/core/java/com/android/server/am/BatteryStatsService.java
@@ -78,6 +78,7 @@ import com.android.internal.os.BackgroundThread;
import com.android.internal.os.BatteryStatsHelper;
import com.android.internal.os.BatteryStatsImpl;
import com.android.internal.os.BatteryUsageStatsProvider;
+import com.android.internal.os.BatteryUsageStatsStore;
import com.android.internal.os.BinderCallsStats;
import com.android.internal.os.PowerProfile;
import com.android.internal.os.RailStats;
@@ -124,10 +125,12 @@ public final class BatteryStatsService extends IBatteryStats.Stub
Watchdog.Monitor {
static final String TAG = "BatteryStatsService";
static final boolean DBG = false;
+ private static final boolean BATTERY_USAGE_STORE_ENABLED = true;
private static IBatteryStats sService;
final BatteryStatsImpl mStats;
+ private final BatteryUsageStatsStore mBatteryUsageStatsStore;
private final BatteryStatsImpl.UserInfoProvider mUserManagerUserInfoProvider;
private final Context mContext;
private final BatteryExternalStatsWorker mWorker;
@@ -348,7 +351,14 @@ public final class BatteryStatsService extends IBatteryStats.Stub
mStats.setPowerProfileLocked(new PowerProfile(context));
mStats.startTrackingSystemServerCpuTime();
- mBatteryUsageStatsProvider = new BatteryUsageStatsProvider(context, mStats);
+ if (BATTERY_USAGE_STORE_ENABLED) {
+ mBatteryUsageStatsStore =
+ new BatteryUsageStatsStore(context, mStats, systemDir, mHandler);
+ } else {
+ mBatteryUsageStatsStore = null;
+ }
+ mBatteryUsageStatsProvider = new BatteryUsageStatsProvider(context, mStats,
+ mBatteryUsageStatsStore);
}
public void publish() {
@@ -752,6 +762,10 @@ public final class BatteryStatsService extends IBatteryStats.Stub
FrameworkStatsLog.BATTERY_USAGE_STATS_SINCE_RESET_USING_POWER_PROFILE_MODEL,
null, // use default PullAtomMetadata values
BackgroundThread.getExecutor(), pullAtomCallback);
+ statsManager.setPullAtomCallback(
+ FrameworkStatsLog.BATTERY_USAGE_STATS_BEFORE_RESET,
+ null, // use default PullAtomMetadata values
+ BackgroundThread.getExecutor(), pullAtomCallback);
}
/** StatsPullAtomCallback for pulling BatteryUsageStats data. */
@@ -768,6 +782,17 @@ public final class BatteryStatsService extends IBatteryStats.Stub
new BatteryUsageStatsQuery.Builder().powerProfileModeledOnly().build();
bus = getBatteryUsageStats(List.of(powerProfileQuery)).get(0);
break;
+ case FrameworkStatsLog.BATTERY_USAGE_STATS_BEFORE_RESET:
+ final long sessionStart = mBatteryUsageStatsStore
+ .getLastBatteryUsageStatsBeforeResetAtomPullTimestamp();
+ final long sessionEnd = mStats.getStartClockTime();
+ final BatteryUsageStatsQuery query = new BatteryUsageStatsQuery.Builder()
+ .aggregateSnapshots(sessionStart, sessionEnd)
+ .build();
+ bus = getBatteryUsageStats(List.of(query)).get(0);
+ mBatteryUsageStatsStore
+ .setLastBatteryUsageStatsBeforeResetAtomPullTimestamp(sessionEnd);
+ break;
default:
throw new UnsupportedOperationException("Unknown tagId=" + atomTag);
}
diff --git a/services/core/java/com/android/server/am/OomAdjuster.java b/services/core/java/com/android/server/am/OomAdjuster.java
index 62286379ba33..aef402ac3213 100644
--- a/services/core/java/com/android/server/am/OomAdjuster.java
+++ b/services/core/java/com/android/server/am/OomAdjuster.java
@@ -328,7 +328,8 @@ public class OomAdjuster {
final int index = mCache.indexOfKey(app.packageName);
Pair<Boolean, WeakReference<ApplicationInfo>> p;
if (index < 0) {
- p = new Pair<>(mPlatformCompat.isChangeEnabled(mChangeId, app),
+ p = new Pair<>(mPlatformCompat.isChangeEnabledInternalNoLogging(mChangeId,
+ app),
new WeakReference<>(app));
mCache.put(app.packageName, p);
return p.first;
@@ -338,7 +339,8 @@ public class OomAdjuster {
return p.first;
}
// Cache is invalid, regenerate it
- p = new Pair<>(mPlatformCompat.isChangeEnabled(mChangeId, app),
+ p = new Pair<>(mPlatformCompat.isChangeEnabledInternalNoLogging(mChangeId,
+ app),
new WeakReference<>(app));
mCache.setValueAt(index, p);
return p.first;
diff --git a/services/core/java/com/android/server/am/ServiceRecord.java b/services/core/java/com/android/server/am/ServiceRecord.java
index 3ba07af710c2..141f081ab9a7 100644
--- a/services/core/java/com/android/server/am/ServiceRecord.java
+++ b/services/core/java/com/android/server/am/ServiceRecord.java
@@ -109,7 +109,6 @@ final class ServiceRecord extends Binder implements ComponentName.WithComponentN
boolean fgWaiting; // is a timeout for going foreground already scheduled?
boolean isNotAppComponentUsage; // is service binding not considered component/package usage?
boolean isForeground; // is service currently in foreground mode?
- boolean mLogEntering; // need to report fgs transition once deferral policy is known
int foregroundId; // Notification ID of last foreground req.
Notification foregroundNoti; // Notification record of foreground state.
long fgDisplayTime; // time at which the FGS notification should become visible
@@ -167,8 +166,10 @@ final class ServiceRecord extends Binder implements ComponentName.WithComponentN
long mFgsEnterTime = 0;
// The uptime when the service exits FGS state.
long mFgsExitTime = 0;
- // FGS notification was deferred.
+ // FGS notification is deferred.
boolean mFgsNotificationDeferred;
+ // FGS notification was deferred.
+ boolean mFgsNotificationWasDeferred;
// FGS notification was shown before the FGS finishes, or it wasn't deferred in the first place.
boolean mFgsNotificationShown;
diff --git a/services/core/java/com/android/server/appop/AppOpsService.java b/services/core/java/com/android/server/appop/AppOpsService.java
index 104bc9b2c8d1..99a33e4462e2 100644
--- a/services/core/java/com/android/server/appop/AppOpsService.java
+++ b/services/core/java/com/android/server/appop/AppOpsService.java
@@ -848,7 +848,7 @@ public class AppOpsService extends IAppOpsService.Stub {
return mAttributionFlags;
}
- /** @return attributoin chiang id for the access */
+ /** @return attribution chain id for the access */
public int getAttributionChainId() {
return mAttributionChainId;
}
@@ -912,7 +912,8 @@ public class AppOpsService extends IAppOpsService.Stub {
proxyAttributionTag, uidState, flags);
mHistoricalRegistry.incrementOpAccessedCount(parent.op, parent.uid, parent.packageName,
- tag, uidState, flags, accessTime);
+ tag, uidState, flags, accessTime, AppOpsManager.ATTRIBUTION_FLAGS_NONE,
+ AppOpsManager.ATTRIBUTION_CHAIN_ID_NONE);
}
/**
@@ -1053,9 +1054,9 @@ public class AppOpsService extends IAppOpsService.Stub {
event.numUnfinishedStarts++;
if (isStarted) {
- // TODO: Consider storing the attribution chain flags and id
mHistoricalRegistry.incrementOpAccessedCount(parent.op, parent.uid,
- parent.packageName, tag, uidState, flags, startTime);
+ parent.packageName, tag, uidState, flags, startTime, attributionFlags,
+ attributionChainId);
}
}
@@ -1112,7 +1113,8 @@ public class AppOpsService extends IAppOpsService.Stub {
mHistoricalRegistry.increaseOpAccessDuration(parent.op, parent.uid,
parent.packageName, tag, event.getUidState(),
- event.getFlags(), finishedEvent.getNoteTime(), finishedEvent.getDuration());
+ event.getFlags(), finishedEvent.getNoteTime(), finishedEvent.getDuration(),
+ event.getAttributionFlags(), event.getAttributionChainId());
if (!isPausing) {
mInProgressStartOpEventPool.release(event);
@@ -1215,7 +1217,8 @@ public class AppOpsService extends IAppOpsService.Stub {
event.mStartElapsedTime = SystemClock.elapsedRealtime();
event.mStartTime = startTime;
mHistoricalRegistry.incrementOpAccessedCount(parent.op, parent.uid,
- parent.packageName, tag, event.mUidState, event.mFlags, startTime);
+ parent.packageName, tag, event.mUidState, event.mFlags, startTime,
+ event.getAttributionFlags(), event.getAttributionChainId());
if (shouldSendActive) {
scheduleOpActiveChangedIfNeededLocked(parent.op, parent.uid, parent.packageName,
tag, true, event.getAttributionFlags(), event.getAttributionChainId());
diff --git a/services/core/java/com/android/server/appop/DiscreteRegistry.java b/services/core/java/com/android/server/appop/DiscreteRegistry.java
index 10cfddfb3ae4..49469cc8a597 100644
--- a/services/core/java/com/android/server/appop/DiscreteRegistry.java
+++ b/services/core/java/com/android/server/appop/DiscreteRegistry.java
@@ -26,6 +26,7 @@ import static android.app.AppOpsManager.OP_FINE_LOCATION;
import static android.app.AppOpsManager.OP_FLAGS_ALL;
import static android.app.AppOpsManager.OP_FLAG_SELF;
import static android.app.AppOpsManager.OP_FLAG_TRUSTED_PROXIED;
+import static android.app.AppOpsManager.OP_FLAG_TRUSTED_PROXY;
import static android.app.AppOpsManager.OP_NONE;
import static android.app.AppOpsManager.OP_PHONE_CALL_CAMERA;
import static android.app.AppOpsManager.OP_PHONE_CALL_MICROPHONE;
@@ -156,8 +157,11 @@ final class DiscreteRegistry {
private static final String ATTR_NOTE_DURATION = "nd";
private static final String ATTR_UID_STATE = "us";
private static final String ATTR_FLAGS = "f";
+ private static final String ATTR_ATTRIBUTION_FLAGS = "af";
+ private static final String ATTR_CHAIN_ID = "ci";
- private static final int OP_FLAGS_DISCRETE = OP_FLAG_SELF | OP_FLAG_TRUSTED_PROXIED;
+ private static final int OP_FLAGS_DISCRETE = OP_FLAG_SELF | OP_FLAG_TRUSTED_PROXIED
+ | OP_FLAG_TRUSTED_PROXY;
// Lock for read/write access to on disk state
private final Object mOnDiskLock = new Object();
@@ -227,13 +231,14 @@ final class DiscreteRegistry {
void recordDiscreteAccess(int uid, String packageName, int op, @Nullable String attributionTag,
@AppOpsManager.OpFlags int flags, @AppOpsManager.UidState int uidState, long accessTime,
- long accessDuration) {
+ long accessDuration, @AppOpsManager.AttributionFlags int attributionFlags,
+ int attributionChainId) {
if (!isDiscreteOp(op, flags)) {
return;
}
synchronized (mInMemoryLock) {
mDiscreteOps.addDiscreteAccess(op, uid, packageName, attributionTag, flags, uidState,
- accessTime, accessDuration);
+ accessTime, accessDuration, attributionFlags, attributionChainId);
}
}
@@ -383,9 +388,10 @@ final class DiscreteRegistry {
void addDiscreteAccess(int op, int uid, @NonNull String packageName,
@Nullable String attributionTag, @AppOpsManager.OpFlags int flags,
- @AppOpsManager.UidState int uidState, long accessTime, long accessDuration) {
+ @AppOpsManager.UidState int uidState, long accessTime, long accessDuration,
+ @AppOpsManager.AttributionFlags int attributionFlags, int attributionChainId) {
getOrCreateDiscreteUidOps(uid).addDiscreteAccess(op, packageName, attributionTag, flags,
- uidState, accessTime, accessDuration);
+ uidState, accessTime, accessDuration, attributionFlags, attributionChainId);
}
private void filter(long beginTimeMillis, long endTimeMillis,
@@ -613,9 +619,10 @@ final class DiscreteRegistry {
void addDiscreteAccess(int op, @NonNull String packageName, @Nullable String attributionTag,
@AppOpsManager.OpFlags int flags, @AppOpsManager.UidState int uidState,
- long accessTime, long accessDuration) {
+ long accessTime, long accessDuration,
+ @AppOpsManager.AttributionFlags int attributionFlags, int attributionChainId) {
getOrCreateDiscretePackageOps(packageName).addDiscreteAccess(op, attributionTag, flags,
- uidState, accessTime, accessDuration);
+ uidState, accessTime, accessDuration, attributionFlags, attributionChainId);
}
private DiscretePackageOps getOrCreateDiscretePackageOps(String packageName) {
@@ -680,9 +687,10 @@ final class DiscreteRegistry {
void addDiscreteAccess(int op, @Nullable String attributionTag,
@AppOpsManager.OpFlags int flags, @AppOpsManager.UidState int uidState,
- long accessTime, long accessDuration) {
+ long accessTime, long accessDuration,
+ @AppOpsManager.AttributionFlags int attributionFlags, int attributionChainId) {
getOrCreateDiscreteOp(op).addDiscreteAccess(attributionTag, flags, uidState, accessTime,
- accessDuration);
+ accessDuration, attributionFlags, attributionChainId);
}
void merge(DiscretePackageOps other) {
@@ -823,37 +831,39 @@ final class DiscreteRegistry {
for (int j = 0; j < n; j++) {
DiscreteOpEvent event = list.get(j);
list.set(j, new DiscreteOpEvent(event.mNoteTime - offset, event.mNoteDuration,
- event.mUidState, event.mOpFlag));
+ event.mUidState, event.mOpFlag, event.mAttributionFlags,
+ event.mAttributionChainId));
}
}
}
void addDiscreteAccess(@Nullable String attributionTag,
@AppOpsManager.OpFlags int flags, @AppOpsManager.UidState int uidState,
- long accessTime, long accessDuration) {
+ long accessTime, long accessDuration,
+ @AppOpsManager.AttributionFlags int attributionFlags, int attributionChainId) {
List<DiscreteOpEvent> attributedOps = getOrCreateDiscreteOpEventsList(
attributionTag);
- accessTime = accessTime / sDiscreteHistoryQuantization * sDiscreteHistoryQuantization;
- accessDuration = accessDuration == -1 ? -1
- : (accessDuration + sDiscreteHistoryQuantization - 1)
- / sDiscreteHistoryQuantization * sDiscreteHistoryQuantization;
int nAttributedOps = attributedOps.size();
int i = nAttributedOps;
for (; i > 0; i--) {
DiscreteOpEvent previousOp = attributedOps.get(i - 1);
- if (previousOp.mNoteTime < accessTime) {
+ if (discretizeTimeStamp(previousOp.mNoteTime) < discretizeTimeStamp(accessTime)) {
break;
}
- if (previousOp.mOpFlag == flags && previousOp.mUidState == uidState) {
- if (accessDuration != previousOp.mNoteDuration) {
+ if (previousOp.mOpFlag == flags && previousOp.mUidState == uidState
+ && previousOp.mAttributionFlags == attributionFlags
+ && previousOp.mAttributionChainId == attributionChainId) {
+ if (discretizeDuration(accessDuration) != discretizeDuration(
+ previousOp.mNoteDuration)) {
break;
} else {
return;
}
}
}
- attributedOps.add(i, new DiscreteOpEvent(accessTime, accessDuration, uidState, flags));
+ attributedOps.add(i, new DiscreteOpEvent(accessTime, accessDuration, uidState, flags,
+ attributionFlags, attributionChainId));
}
private List<DiscreteOpEvent> getOrCreateDiscreteOpEventsList(String attributionTag) {
@@ -875,7 +885,8 @@ final class DiscreteRegistry {
for (int j = 0; j < nEvents; j++) {
DiscreteOpEvent event = events.get(j);
result.addDiscreteAccess(op, uid, packageName, tag, event.mUidState,
- event.mOpFlag, event.mNoteTime, event.mNoteDuration);
+ event.mOpFlag, discretizeTimeStamp(event.mNoteTime),
+ discretizeDuration(event.mNoteDuration));
}
}
}
@@ -932,11 +943,15 @@ final class DiscreteRegistry {
-1);
int uidState = parser.getAttributeInt(null, ATTR_UID_STATE);
int opFlags = parser.getAttributeInt(null, ATTR_FLAGS);
+ int attributionFlags = parser.getAttributeInt(null,
+ ATTR_ATTRIBUTION_FLAGS, AppOpsManager.ATTRIBUTION_FLAGS_NONE);
+ int attributionChainId = parser.getAttributeInt(null, ATTR_CHAIN_ID,
+ AppOpsManager.ATTRIBUTION_CHAIN_ID_NONE);
if (noteTime + noteDuration < beginTimeMillis) {
continue;
}
DiscreteOpEvent event = new DiscreteOpEvent(noteTime, noteDuration,
- uidState, opFlags);
+ uidState, opFlags, attributionFlags, attributionChainId);
events.add(event);
}
}
@@ -952,13 +967,18 @@ final class DiscreteRegistry {
final long mNoteDuration;
final @AppOpsManager.UidState int mUidState;
final @AppOpsManager.OpFlags int mOpFlag;
+ final @AppOpsManager.AttributionFlags int mAttributionFlags;
+ final int mAttributionChainId;
DiscreteOpEvent(long noteTime, long noteDuration, @AppOpsManager.UidState int uidState,
- @AppOpsManager.OpFlags int opFlag) {
+ @AppOpsManager.OpFlags int opFlag,
+ @AppOpsManager.AttributionFlags int attributionFlags, int attributionChainId) {
mNoteTime = noteTime;
mNoteDuration = noteDuration;
mUidState = uidState;
mOpFlag = opFlag;
+ mAttributionFlags = attributionFlags;
+ mAttributionChainId = attributionChainId;
}
private void dump(@NonNull PrintWriter pw, @NonNull SimpleDateFormat sdf,
@@ -969,13 +989,19 @@ final class DiscreteRegistry {
pw.print("-");
pw.print(flagsToString(mOpFlag));
pw.print("] at ");
- date.setTime(mNoteTime);
+ date.setTime(discretizeTimeStamp(mNoteTime));
pw.print(sdf.format(date));
if (mNoteDuration != -1) {
pw.print(" for ");
- pw.print(mNoteDuration);
+ pw.print(discretizeDuration(mNoteDuration));
pw.print(" milliseconds ");
}
+ if (mAttributionFlags != AppOpsManager.ATTRIBUTION_FLAGS_NONE) {
+ pw.print(" attribution flags=");
+ pw.print(mAttributionFlags);
+ pw.print(" with chainId=");
+ pw.print(mAttributionChainId);
+ }
pw.println();
}
@@ -984,6 +1010,12 @@ final class DiscreteRegistry {
if (mNoteDuration != -1) {
out.attributeLong(null, ATTR_NOTE_DURATION, mNoteDuration);
}
+ if (mAttributionFlags != AppOpsManager.ATTRIBUTION_FLAGS_NONE) {
+ out.attributeInt(null, ATTR_ATTRIBUTION_FLAGS, mAttributionFlags);
+ }
+ if (mAttributionChainId != AppOpsManager.ATTRIBUTION_CHAIN_ID_NONE) {
+ out.attributeInt(null, ATTR_CHAIN_ID, mAttributionChainId);
+ }
out.attributeInt(null, ATTR_UID_STATE, mUidState);
out.attributeInt(null, ATTR_FLAGS, mOpFlag);
}
@@ -1055,6 +1087,16 @@ final class DiscreteRegistry {
return true;
}
+ private static long discretizeTimeStamp(long timeStamp) {
+ return timeStamp / sDiscreteHistoryQuantization * sDiscreteHistoryQuantization;
+
+ }
+
+ private static long discretizeDuration(long duration) {
+ return duration == -1 ? -1 : (duration + sDiscreteHistoryQuantization - 1)
+ / sDiscreteHistoryQuantization * sDiscreteHistoryQuantization;
+ }
+
void setDebugMode(boolean debugMode) {
this.mDebugMode = debugMode;
}
diff --git a/services/core/java/com/android/server/appop/HistoricalRegistry.java b/services/core/java/com/android/server/appop/HistoricalRegistry.java
index 8b72be78a7f6..dd5df503d936 100644
--- a/services/core/java/com/android/server/appop/HistoricalRegistry.java
+++ b/services/core/java/com/android/server/appop/HistoricalRegistry.java
@@ -487,7 +487,8 @@ final class HistoricalRegistry {
void incrementOpAccessedCount(int op, int uid, @NonNull String packageName,
@Nullable String attributionTag, @UidState int uidState, @OpFlags int flags,
- long accessTime) {
+ long accessTime, @AppOpsManager.AttributionFlags int attributionFlags,
+ int attributionChainId) {
synchronized (mInMemoryLock) {
if (mMode == AppOpsManager.HISTORICAL_MODE_ENABLED_ACTIVE) {
if (!isPersistenceInitializedMLocked()) {
@@ -499,7 +500,7 @@ final class HistoricalRegistry {
attributionTag, uidState, flags, 1);
mDiscreteRegistry.recordDiscreteAccess(uid, packageName, op, attributionTag,
- flags, uidState, accessTime, -1);
+ flags, uidState, accessTime, -1, attributionFlags, attributionChainId);
}
}
}
@@ -521,7 +522,8 @@ final class HistoricalRegistry {
void increaseOpAccessDuration(int op, int uid, @NonNull String packageName,
@Nullable String attributionTag, @UidState int uidState, @OpFlags int flags,
- long eventStartTime, long increment) {
+ long eventStartTime, long increment,
+ @AppOpsManager.AttributionFlags int attributionFlags, int attributionChainId) {
synchronized (mInMemoryLock) {
if (mMode == AppOpsManager.HISTORICAL_MODE_ENABLED_ACTIVE) {
if (!isPersistenceInitializedMLocked()) {
@@ -532,7 +534,8 @@ final class HistoricalRegistry {
System.currentTimeMillis()).increaseAccessDuration(op, uid, packageName,
attributionTag, uidState, flags, increment);
mDiscreteRegistry.recordDiscreteAccess(uid, packageName, op, attributionTag,
- flags, uidState, eventStartTime, increment);
+ flags, uidState, eventStartTime, increment, attributionFlags,
+ attributionChainId);
}
}
}
diff --git a/services/core/java/com/android/server/display/WifiDisplayController.java b/services/core/java/com/android/server/display/WifiDisplayController.java
index 6db75eb80aea..a7e1a2876f81 100644
--- a/services/core/java/com/android/server/display/WifiDisplayController.java
+++ b/services/core/java/com/android/server/display/WifiDisplayController.java
@@ -550,11 +550,6 @@ final class WifiDisplayController implements DumpUtils.Dump {
private void disconnect() {
mDesiredDevice = null;
- mWifiP2pManager = null;
- if (null != mWifiP2pChannel) {
- mWifiP2pChannel.close();
- mWifiP2pChannel = null;
- }
updateConnection();
}
diff --git a/services/core/java/com/android/server/location/LocationManagerService.java b/services/core/java/com/android/server/location/LocationManagerService.java
index b9e97e89051b..aeb1893c78b6 100644
--- a/services/core/java/com/android/server/location/LocationManagerService.java
+++ b/services/core/java/com/android/server/location/LocationManagerService.java
@@ -65,7 +65,7 @@ import android.location.LastLocationRequest;
import android.location.Location;
import android.location.LocationManager;
import android.location.LocationManagerInternal;
-import android.location.LocationManagerInternal.OnProviderLocationTagsChangeListener;
+import android.location.LocationManagerInternal.LocationPackageTagsListener;
import android.location.LocationProvider;
import android.location.LocationRequest;
import android.location.LocationTime;
@@ -93,6 +93,7 @@ import android.util.Log;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.util.DumpUtils;
import com.android.internal.util.Preconditions;
+import com.android.server.FgThread;
import com.android.server.LocalServices;
import com.android.server.SystemService;
import com.android.server.location.eventlog.LocationEventLog;
@@ -259,7 +260,7 @@ public class LocationManagerService extends ILocationManager.Stub implements
new CopyOnWriteArrayList<>();
@GuardedBy("mLock")
- @Nullable OnProviderLocationTagsChangeListener mOnProviderLocationTagsChangeListener;
+ @Nullable LocationPackageTagsListener mLocationTagsChangedListener;
LocationManagerService(Context context, Injector injector) {
mContext = context.createAttributionContext(ATTRIBUTION_TAG);
@@ -1363,32 +1364,28 @@ public class LocationManagerService extends ILocationManager.Stub implements
refreshAppOpsRestrictions(UserHandle.USER_ALL);
}
- OnProviderLocationTagsChangeListener listener;
- synchronized (mLock) {
- listener = mOnProviderLocationTagsChangeListener;
- }
-
- if (listener != null) {
- if (!oldState.extraAttributionTags.equals(newState.extraAttributionTags)
- || !Objects.equals(oldState.identity, newState.identity)) {
- if (oldState.identity != null) {
- listener.onLocationTagsChanged(
- new LocationManagerInternal.LocationTagInfo(
- oldState.identity.getUid(),
- oldState.identity.getPackageName(),
- Collections.emptySet()));
- }
- if (newState.identity != null) {
- ArraySet<String> attributionTags = new ArraySet<>(
- newState.extraAttributionTags.size() + 1);
- attributionTags.addAll(newState.extraAttributionTags);
- attributionTags.add(newState.identity.getAttributionTag());
-
- listener.onLocationTagsChanged(
- new LocationManagerInternal.LocationTagInfo(
- newState.identity.getUid(),
- newState.identity.getPackageName(),
- attributionTags));
+ if (!oldState.extraAttributionTags.equals(newState.extraAttributionTags)
+ || !Objects.equals(oldState.identity, newState.identity)) {
+ // since we're potentially affecting the tag lists for two different uids, acquire the
+ // lock to ensure providers cannot change while we're looping over the providers
+ // multiple times, which could lead to inconsistent results.
+ synchronized (mLock) {
+ LocationPackageTagsListener listener = mLocationTagsChangedListener;
+ if (listener != null) {
+ int oldUid = oldState.identity != null ? oldState.identity.getUid() : -1;
+ int newUid = newState.identity != null ? newState.identity.getUid() : -1;
+ if (oldUid != -1) {
+ PackageTagsList tags = calculateAppOpsLocationSourceTags(oldUid);
+ FgThread.getHandler().post(
+ () -> listener.onLocationPackageTagsChanged(oldUid, tags));
+ }
+ // if the new app id is the same as the old app id, no need to invoke the
+ // listener twice, it's already been taken care of
+ if (newUid != -1 && newUid != oldUid) {
+ PackageTagsList tags = calculateAppOpsLocationSourceTags(newUid);
+ FgThread.getHandler().post(
+ () -> listener.onLocationPackageTagsChanged(newUid, tags));
+ }
}
}
}
@@ -1436,6 +1433,31 @@ public class LocationManagerService extends ILocationManager.Stub implements
userId);
}
+ PackageTagsList calculateAppOpsLocationSourceTags(int uid) {
+ PackageTagsList.Builder builder = new PackageTagsList.Builder();
+ for (LocationProviderManager manager : mProviderManagers) {
+ AbstractLocationProvider.State managerState = manager.getState();
+ if (managerState.identity == null) {
+ continue;
+ }
+ if (managerState.identity.getUid() != uid) {
+ continue;
+ }
+
+ builder.add(managerState.identity.getPackageName(), managerState.extraAttributionTags);
+ if (managerState.extraAttributionTags.isEmpty()
+ || managerState.identity.getAttributionTag() != null) {
+ builder.add(managerState.identity.getPackageName(),
+ managerState.identity.getAttributionTag());
+ } else {
+ Log.e(TAG, manager.getName() + " provider has specified a null attribution tag and "
+ + "a non-empty set of extra attribution tags - dropping the null "
+ + "attribution tag");
+ }
+ }
+ return builder.build();
+ }
+
private class LocalService extends LocationManagerInternal {
LocalService() {}
@@ -1506,10 +1528,29 @@ public class LocationManagerService extends ILocationManager.Stub implements
}
@Override
- public void setOnProviderLocationTagsChangeListener(
- @Nullable OnProviderLocationTagsChangeListener listener) {
+ public void setLocationPackageTagsListener(
+ @Nullable LocationPackageTagsListener listener) {
synchronized (mLock) {
- mOnProviderLocationTagsChangeListener = listener;
+ mLocationTagsChangedListener = listener;
+
+ // calculate initial tag list and send to listener
+ if (listener != null) {
+ ArraySet<Integer> uids = new ArraySet<>(mProviderManagers.size());
+ for (LocationProviderManager manager : mProviderManagers) {
+ CallerIdentity identity = manager.getIdentity();
+ if (identity != null) {
+ uids.add(identity.getUid());
+ }
+ }
+
+ for (int uid : uids) {
+ PackageTagsList tags = calculateAppOpsLocationSourceTags(uid);
+ if (!tags.isEmpty()) {
+ FgThread.getHandler().post(
+ () -> listener.onLocationPackageTagsChanged(uid, tags));
+ }
+ }
+ }
}
}
}
diff --git a/services/core/java/com/android/server/location/provider/AbstractLocationProvider.java b/services/core/java/com/android/server/location/provider/AbstractLocationProvider.java
index 1da45bd49767..eb7b77a2234f 100644
--- a/services/core/java/com/android/server/location/provider/AbstractLocationProvider.java
+++ b/services/core/java/com/android/server/location/provider/AbstractLocationProvider.java
@@ -263,10 +263,10 @@ public abstract class AbstractLocationProvider {
}
/**
- * The current allowed state of this provider.
+ * The current state of the provider.
*/
- public final boolean isAllowed() {
- return mInternalState.get().state.allowed;
+ public final State getState() {
+ return mInternalState.get().state;
}
/**
@@ -277,13 +277,6 @@ public abstract class AbstractLocationProvider {
}
/**
- * The current provider properties of this provider.
- */
- public final @Nullable ProviderProperties getProperties() {
- return mInternalState.get().state.properties;
- }
-
- /**
* Call this method to report a change in provider properties.
*/
protected void setProperties(@Nullable ProviderProperties properties) {
@@ -291,13 +284,6 @@ public abstract class AbstractLocationProvider {
}
/**
- * The current identity of this provider.
- */
- public final @Nullable CallerIdentity getIdentity() {
- return mInternalState.get().state.identity;
- }
-
- /**
* Call this method to report a change in the provider's identity.
*/
protected void setIdentity(@Nullable CallerIdentity identity) {
diff --git a/services/core/java/com/android/server/location/provider/LocationProviderManager.java b/services/core/java/com/android/server/location/provider/LocationProviderManager.java
index 4f8b87b51294..8d335b83d99c 100644
--- a/services/core/java/com/android/server/location/provider/LocationProviderManager.java
+++ b/services/core/java/com/android/server/location/provider/LocationProviderManager.java
@@ -1407,12 +1407,16 @@ public class LocationProviderManager extends
return mName;
}
+ public AbstractLocationProvider.State getState() {
+ return mProvider.getState();
+ }
+
public @Nullable CallerIdentity getIdentity() {
- return mProvider.getIdentity();
+ return mProvider.getState().identity;
}
public @Nullable ProviderProperties getProperties() {
- return mProvider.getProperties();
+ return mProvider.getState().properties;
}
public boolean hasProvider() {
@@ -2403,7 +2407,7 @@ public class LocationProviderManager extends
Preconditions.checkArgument(userId >= 0);
boolean enabled = mState == STATE_STARTED
- && mProvider.isAllowed()
+ && mProvider.getState().allowed
&& mSettingsHelper.isLocationEnabled(userId);
int index = mEnabled.indexOfKey(userId);
diff --git a/services/core/java/com/android/server/location/provider/MockableLocationProvider.java b/services/core/java/com/android/server/location/provider/MockableLocationProvider.java
index 021e8dbdb44e..22295fe7ba28 100644
--- a/services/core/java/com/android/server/location/provider/MockableLocationProvider.java
+++ b/services/core/java/com/android/server/location/provider/MockableLocationProvider.java
@@ -21,9 +21,7 @@ import static com.android.internal.util.ConcurrentUtils.DIRECT_EXECUTOR;
import android.annotation.Nullable;
import android.location.Location;
import android.location.LocationResult;
-import android.location.provider.ProviderProperties;
import android.location.provider.ProviderRequest;
-import android.location.util.identity.CallerIdentity;
import android.os.Bundle;
import com.android.internal.annotations.GuardedBy;
@@ -32,7 +30,6 @@ import com.android.internal.util.Preconditions;
import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.util.Collections;
-import java.util.Set;
/**
* Represents a location provider that may switch between a mock implementation and a real
@@ -290,21 +287,21 @@ public class MockableLocationProvider extends AbstractLocationProvider {
Preconditions.checkState(!Thread.holdsLock(mOwnerLock));
AbstractLocationProvider provider;
+ State providerState;
synchronized (mOwnerLock) {
provider = mProvider;
- pw.println("allowed=" + isAllowed());
- CallerIdentity identity = getIdentity();
- if (identity != null) {
- pw.println("identity=" + identity);
- }
- Set<String> extraAttributionTags = getExtraAttributionTags();
- if (!extraAttributionTags.isEmpty()) {
- pw.println("extra attribution tags=" + extraAttributionTags);
- }
- ProviderProperties properties = getProperties();
- if (properties != null) {
- pw.println("properties=" + properties);
- }
+ providerState = getState();
+ }
+
+ pw.println("allowed=" + providerState.allowed);
+ if (providerState.identity != null) {
+ pw.println("identity=" + providerState.identity);
+ }
+ if (!providerState.extraAttributionTags.isEmpty()) {
+ pw.println("extra attribution tags=" + providerState.extraAttributionTags);
+ }
+ if (providerState.properties != null) {
+ pw.println("properties=" + providerState.properties);
}
if (provider != null) {
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index b26485b5aad9..a3f3a3a503c8 100755
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -4785,7 +4785,7 @@ public class NotificationManagerService extends SystemService {
}
@Override
- public String addAutomaticZenRule(AutomaticZenRule automaticZenRule) {
+ public String addAutomaticZenRule(AutomaticZenRule automaticZenRule, String pkg) {
Objects.requireNonNull(automaticZenRule, "automaticZenRule is null");
Objects.requireNonNull(automaticZenRule.getName(), "Name is null");
if (automaticZenRule.getOwner() == null
@@ -4794,6 +4794,7 @@ public class NotificationManagerService extends SystemService {
"Rule must have a conditionproviderservice and/or configuration activity");
}
Objects.requireNonNull(automaticZenRule.getConditionId(), "ConditionId is null");
+ checkCallerIsSameApp(pkg);
if (automaticZenRule.getZenPolicy() != null
&& automaticZenRule.getInterruptionFilter() != INTERRUPTION_FILTER_PRIORITY) {
throw new IllegalArgumentException("ZenPolicy is only applicable to "
@@ -4801,7 +4802,7 @@ public class NotificationManagerService extends SystemService {
}
enforcePolicyAccess(Binder.getCallingUid(), "addAutomaticZenRule");
- return mZenModeHelper.addAutomaticZenRule(automaticZenRule,
+ return mZenModeHelper.addAutomaticZenRule(pkg, automaticZenRule,
"addAutomaticZenRule");
}
diff --git a/services/core/java/com/android/server/notification/ZenModeHelper.java b/services/core/java/com/android/server/notification/ZenModeHelper.java
index 4cb6c3ba20d8..a98f113a9d57 100644
--- a/services/core/java/com/android/server/notification/ZenModeHelper.java
+++ b/services/core/java/com/android/server/notification/ZenModeHelper.java
@@ -307,7 +307,8 @@ public class ZenModeHelper {
return null;
}
- public String addAutomaticZenRule(AutomaticZenRule automaticZenRule, String reason) {
+ public String addAutomaticZenRule(String pkg, AutomaticZenRule automaticZenRule,
+ String reason) {
if (!isSystemRule(automaticZenRule)) {
PackageItemInfo component = getServiceInfo(automaticZenRule.getOwner());
if (component == null) {
@@ -340,7 +341,7 @@ public class ZenModeHelper {
}
newConfig = mConfig.copy();
ZenRule rule = new ZenRule();
- populateZenRule(automaticZenRule, rule, true);
+ populateZenRule(pkg, automaticZenRule, rule, true);
newConfig.automaticRules.put(rule.id, rule);
if (setConfigLocked(newConfig, reason, rule.component, true)) {
return rule.id;
@@ -376,7 +377,7 @@ public class ZenModeHelper {
? AUTOMATIC_RULE_STATUS_ENABLED : AUTOMATIC_RULE_STATUS_DISABLED);
}
- populateZenRule(automaticZenRule, rule, false);
+ populateZenRule(rule.pkg, automaticZenRule, rule, false);
return setConfigLocked(newConfig, reason, rule.component, true);
}
}
@@ -585,7 +586,8 @@ public class ZenModeHelper {
return null;
}
- private void populateZenRule(AutomaticZenRule automaticZenRule, ZenRule rule, boolean isNew) {
+ private void populateZenRule(String pkg, AutomaticZenRule automaticZenRule, ZenRule rule,
+ boolean isNew) {
rule.name = automaticZenRule.getName();
rule.condition = null;
rule.conditionId = automaticZenRule.getConditionId();
@@ -600,9 +602,7 @@ public class ZenModeHelper {
rule.id = ZenModeConfig.newRuleId();
rule.creationTime = System.currentTimeMillis();
rule.component = automaticZenRule.getOwner();
- rule.pkg = (rule.component != null)
- ? rule.component.getPackageName()
- : rule.configurationActivity.getPackageName();
+ rule.pkg = pkg;
}
if (rule.enabled != automaticZenRule.isEnabled()) {
@@ -611,10 +611,13 @@ public class ZenModeHelper {
}
protected AutomaticZenRule createAutomaticZenRule(ZenRule rule) {
- return new AutomaticZenRule(rule.name, rule.component, rule.configurationActivity,
+ AutomaticZenRule azr = new AutomaticZenRule(rule.name, rule.component,
+ rule.configurationActivity,
rule.conditionId, rule.zenPolicy,
NotificationManager.zenModeToInterruptionFilter(rule.zenMode),
rule.enabled, rule.creationTime);
+ azr.setPackageName(rule.pkg);
+ return azr;
}
public void setManualZenMode(int zenMode, Uri conditionId, String caller, String reason) {
diff --git a/services/core/java/com/android/server/pm/LauncherAppsService.java b/services/core/java/com/android/server/pm/LauncherAppsService.java
index 9370b14341dc..5b2c80903ce5 100644
--- a/services/core/java/com/android/server/pm/LauncherAppsService.java
+++ b/services/core/java/com/android/server/pm/LauncherAppsService.java
@@ -1000,16 +1000,15 @@ public class LauncherAppsService extends SystemService {
intents[0].setSourceBounds(sourceBounds);
// Replace theme for splash screen
- final int splashScreenThemeResId =
- mShortcutServiceInternal.getShortcutStartingThemeResId(getCallingUserId(),
+ final String splashScreenThemeResName =
+ mShortcutServiceInternal.getShortcutStartingThemeResName(getCallingUserId(),
callingPackage, packageName, shortcutId, targetUserId);
- if (splashScreenThemeResId != 0) {
+ if (splashScreenThemeResName != null && !splashScreenThemeResName.isEmpty()) {
if (startActivityOptions == null) {
startActivityOptions = new Bundle();
}
- startActivityOptions.putInt(KEY_SPLASH_SCREEN_THEME, splashScreenThemeResId);
+ startActivityOptions.putString(KEY_SPLASH_SCREEN_THEME, splashScreenThemeResName);
}
-
return startShortcutIntentsAsPublisher(
intents, packageName, featureId, startActivityOptions, targetUserId);
}
diff --git a/services/core/java/com/android/server/pm/ShortcutPackage.java b/services/core/java/com/android/server/pm/ShortcutPackage.java
index 1bd9e5eedb84..b4bd086af272 100644
--- a/services/core/java/com/android/server/pm/ShortcutPackage.java
+++ b/services/core/java/com/android/server/pm/ShortcutPackage.java
@@ -139,7 +139,7 @@ class ShortcutPackage extends ShortcutPackageItem {
private static final String ATTR_BITMAP_PATH = "bitmap-path";
private static final String ATTR_ICON_URI = "icon-uri";
private static final String ATTR_LOCUS_ID = "locus-id";
- private static final String ATTR_SPLASH_SCREEN_THEME_ID = "splash-screen-theme-id";
+ private static final String ATTR_SPLASH_SCREEN_THEME_NAME = "splash-screen-theme-name";
private static final String ATTR_PERSON_NAME = "name";
private static final String ATTR_PERSON_URI = "uri";
@@ -1799,7 +1799,7 @@ class ShortcutPackage extends ShortcutPackageItem {
ShortcutService.writeAttr(out, ATTR_TITLE, si.getTitle());
ShortcutService.writeAttr(out, ATTR_TITLE_RES_ID, si.getTitleResId());
ShortcutService.writeAttr(out, ATTR_TITLE_RES_NAME, si.getTitleResName());
- ShortcutService.writeAttr(out, ATTR_SPLASH_SCREEN_THEME_ID, si.getStartingThemeResId());
+ ShortcutService.writeAttr(out, ATTR_SPLASH_SCREEN_THEME_NAME, si.getStartingThemeResName());
ShortcutService.writeAttr(out, ATTR_TEXT, si.getText());
ShortcutService.writeAttr(out, ATTR_TEXT_RES_ID, si.getTextResId());
ShortcutService.writeAttr(out, ATTR_TEXT_RES_NAME, si.getTextResName());
@@ -2010,7 +2010,7 @@ class ShortcutPackage extends ShortcutPackageItem {
String bitmapPath;
String iconUri;
final String locusIdString;
- int splashScreenThemeResId;
+ String splashScreenThemeResName;
int backupVersionCode;
ArraySet<String> categories = null;
ArrayList<Person> persons = new ArrayList<>();
@@ -2021,8 +2021,8 @@ class ShortcutPackage extends ShortcutPackageItem {
title = ShortcutService.parseStringAttribute(parser, ATTR_TITLE);
titleResId = ShortcutService.parseIntAttribute(parser, ATTR_TITLE_RES_ID);
titleResName = ShortcutService.parseStringAttribute(parser, ATTR_TITLE_RES_NAME);
- splashScreenThemeResId = ShortcutService.parseIntAttribute(parser,
- ATTR_SPLASH_SCREEN_THEME_ID);
+ splashScreenThemeResName = ShortcutService.parseStringAttribute(parser,
+ ATTR_SPLASH_SCREEN_THEME_NAME);
text = ShortcutService.parseStringAttribute(parser, ATTR_TEXT);
textResId = ShortcutService.parseIntAttribute(parser, ATTR_TEXT_RES_ID);
textResName = ShortcutService.parseStringAttribute(parser, ATTR_TEXT_RES_NAME);
@@ -2117,7 +2117,7 @@ class ShortcutPackage extends ShortcutPackageItem {
rank, extras, lastChangedTimestamp, flags,
iconResId, iconResName, bitmapPath, iconUri,
disabledReason, persons.toArray(new Person[persons.size()]), locusId,
- splashScreenThemeResId);
+ splashScreenThemeResName);
}
private static Intent parseIntent(TypedXmlPullParser parser)
diff --git a/services/core/java/com/android/server/pm/ShortcutParser.java b/services/core/java/com/android/server/pm/ShortcutParser.java
index c06f01a463ad..b86c50b23687 100644
--- a/services/core/java/com/android/server/pm/ShortcutParser.java
+++ b/services/core/java/com/android/server/pm/ShortcutParser.java
@@ -383,8 +383,11 @@ public class ShortcutParser {
final int textResId = sa.getResourceId(R.styleable.Shortcut_shortcutLongLabel, 0);
final int disabledMessageResId = sa.getResourceId(
R.styleable.Shortcut_shortcutDisabledMessage, 0);
- final int splashScreenTheme = sa.getResourceId(
+ final int splashScreenThemeResId = sa.getResourceId(
R.styleable.Shortcut_splashScreenTheme, 0);
+ final String splashScreenThemeResName = splashScreenThemeResId != 0
+ ? service.mContext.getResources().getResourceName(splashScreenThemeResId)
+ : null;
if (TextUtils.isEmpty(id)) {
Log.w(TAG, "android:shortcutId must be provided. activity=" + activity);
@@ -407,7 +410,7 @@ public class ShortcutParser {
rank,
iconResId,
enabled,
- splashScreenTheme);
+ splashScreenThemeResName);
} finally {
sa.recycle();
}
@@ -416,7 +419,7 @@ public class ShortcutParser {
private static ShortcutInfo createShortcutFromManifest(ShortcutService service,
@UserIdInt int userId, String id, String packageName, ComponentName activityComponent,
int titleResId, int textResId, int disabledMessageResId,
- int rank, int iconResId, boolean enabled, int splashScreenTheme) {
+ int rank, int iconResId, boolean enabled, @Nullable String splashScreenThemeResName) {
final int flags =
(enabled ? ShortcutInfo.FLAG_MANIFEST : ShortcutInfo.FLAG_DISABLED)
@@ -456,7 +459,7 @@ public class ShortcutParser {
disabledReason,
null /* persons */,
null /* locusId */,
- splashScreenTheme);
+ splashScreenThemeResName);
}
private static String parseCategory(ShortcutService service, AttributeSet attrs) {
diff --git a/services/core/java/com/android/server/pm/ShortcutService.java b/services/core/java/com/android/server/pm/ShortcutService.java
index 5f1027797292..fcbf40e29933 100644
--- a/services/core/java/com/android/server/pm/ShortcutService.java
+++ b/services/core/java/com/android/server/pm/ShortcutService.java
@@ -3536,7 +3536,8 @@ public class ShortcutService extends IShortcutService.Stub {
}
@Override
- public int getShortcutStartingThemeResId(int launcherUserId,
+ @Nullable
+ public String getShortcutStartingThemeResName(int launcherUserId,
@NonNull String callingPackage, @NonNull String packageName,
@NonNull String shortcutId, int userId) {
Objects.requireNonNull(callingPackage, "callingPackage");
@@ -3553,11 +3554,11 @@ public class ShortcutService extends IShortcutService.Stub {
final ShortcutPackage p = getUserShortcutsLocked(userId)
.getPackageShortcutsIfExists(packageName);
if (p == null) {
- return 0;
+ return null;
}
final ShortcutInfo shortcutInfo = p.findShortcutById(shortcutId);
- return shortcutInfo != null ? shortcutInfo.getStartingThemeResId() : 0;
+ return shortcutInfo != null ? shortcutInfo.getStartingThemeResName() : null;
}
}
diff --git a/services/core/java/com/android/server/policy/AppOpsPolicy.java b/services/core/java/com/android/server/policy/AppOpsPolicy.java
index 94005b2c8361..22c370ef4dbe 100644
--- a/services/core/java/com/android/server/policy/AppOpsPolicy.java
+++ b/services/core/java/com/android/server/policy/AppOpsPolicy.java
@@ -33,14 +33,15 @@ import android.content.pm.ResolveInfo;
import android.location.LocationManagerInternal;
import android.net.Uri;
import android.os.IBinder;
+import android.os.PackageTagsList;
import android.os.Process;
import android.os.UserHandle;
import android.service.voice.VoiceInteractionManagerInternal;
import android.service.voice.VoiceInteractionManagerInternal.HotwordDetectionServiceIdentity;
import android.text.TextUtils;
-import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.Log;
+import android.util.SparseArray;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.util.function.DecFunction;
@@ -52,9 +53,9 @@ import com.android.internal.util.function.TriFunction;
import com.android.internal.util.function.UndecFunction;
import com.android.server.LocalServices;
+import java.util.Arrays;
import java.util.List;
import java.util.Map;
-import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
/**
@@ -67,11 +68,10 @@ public final class AppOpsPolicy implements AppOpsManagerInternal.CheckOpsDelegat
"android:activity_recognition_allow_listed_tags";
private static final String ACTIVITY_RECOGNITION_TAGS_SEPARATOR = ";";
- private static ArraySet<String> sExpectedTags = new ArraySet<>(new String[] {
+ private static final ArraySet<String> sExpectedTags = new ArraySet<>(new String[] {
"awareness_provider", "activity_recognition_provider", "network_location_provider",
"network_location_calibration", "fused_location_provider", "geofencer_provider"});
-
@NonNull
private final Object mLock = new Object();
@@ -96,13 +96,22 @@ public final class AppOpsPolicy implements AppOpsManagerInternal.CheckOpsDelegat
*/
@GuardedBy("mLock - writes only - see above")
@NonNull
- private final ConcurrentHashMap<Integer, ArrayMap<String, ArraySet<String>>> mLocationTags =
+ private final ConcurrentHashMap<Integer, PackageTagsList> mLocationTags =
new ConcurrentHashMap<>();
+ // location tags can vary per uid - but we merge all tags under an app id into the final data
+ // structure above
+ @GuardedBy("mLock")
+ private final SparseArray<PackageTagsList> mPerUidLocationTags = new SparseArray<>();
+
+ // activity recognition currently only grabs tags from the APK manifest. we know that the
+ // manifest is the same for all users, so there's no need to track variations in tags across
+ // different users. if that logic ever changes, this might need to behave more like location
+ // tags above.
@GuardedBy("mLock - writes only - see above")
@NonNull
- private final ConcurrentHashMap<Integer, ArrayMap<String, ArraySet<String>>>
- mActivityRecognitionTags = new ConcurrentHashMap<>();
+ private final ConcurrentHashMap<Integer, PackageTagsList> mActivityRecognitionTags =
+ new ConcurrentHashMap<>();
public AppOpsPolicy(@NonNull Context context) {
mContext = context;
@@ -112,13 +121,28 @@ public final class AppOpsPolicy implements AppOpsManagerInternal.CheckOpsDelegat
final LocationManagerInternal locationManagerInternal = LocalServices.getService(
LocationManagerInternal.class);
- locationManagerInternal.setOnProviderLocationTagsChangeListener((providerTagInfo) -> {
- synchronized (mLock) {
- updateAllowListedTagsForPackageLocked(providerTagInfo.getUid(),
- providerTagInfo.getPackageName(), providerTagInfo.getTags(),
- mLocationTags);
- }
- });
+ locationManagerInternal.setLocationPackageTagsListener(
+ (uid, packageTagsList) -> {
+ synchronized (mLock) {
+ if (packageTagsList.isEmpty()) {
+ mPerUidLocationTags.remove(uid);
+ } else {
+ mPerUidLocationTags.set(uid, packageTagsList);
+ }
+
+ int appId = UserHandle.getAppId(uid);
+ PackageTagsList.Builder appIdTags = new PackageTagsList.Builder(1);
+ int size = mPerUidLocationTags.size();
+ for (int i = 0; i < size; i++) {
+ if (UserHandle.getAppId(mPerUidLocationTags.keyAt(i)) == appId) {
+ appIdTags.add(mPerUidLocationTags.valueAt(i));
+ }
+ }
+
+ updateAllowListedTagsForPackageLocked(appId, appIdTags.build(),
+ mLocationTags);
+ }
+ });
final IntentFilter intentFilter = new IntentFilter();
intentFilter.addAction(Intent.ACTION_PACKAGE_ADDED);
@@ -306,95 +330,30 @@ public final class AppOpsPolicy implements AppOpsManagerInternal.CheckOpsDelegat
}
final String tagsList = resolvedService.serviceInfo.metaData.getString(
ACTIVITY_RECOGNITION_TAGS);
- if (tagsList != null) {
- final String[] tags = tagsList.split(ACTIVITY_RECOGNITION_TAGS_SEPARATOR);
+ if (!TextUtils.isEmpty(tagsList)) {
+ PackageTagsList packageTagsList = new PackageTagsList.Builder(1).add(
+ resolvedService.serviceInfo.packageName,
+ Arrays.asList(tagsList.split(ACTIVITY_RECOGNITION_TAGS_SEPARATOR))).build();
synchronized (mLock) {
updateAllowListedTagsForPackageLocked(
- resolvedService.serviceInfo.applicationInfo.uid,
- resolvedService.serviceInfo.packageName, new ArraySet<>(tags),
+ UserHandle.getAppId(resolvedService.serviceInfo.applicationInfo.uid),
+ packageTagsList,
mActivityRecognitionTags);
}
}
}
- private static void updateAllowListedTagsForPackageLocked(int uid, String packageName,
- Set<String> allowListedTags, ConcurrentHashMap<Integer, ArrayMap<String,
- ArraySet<String>>> datastore) {
- final int appId = UserHandle.getAppId(uid);
- // We make a copy of the per UID state to limit our mutation to one
- // operation in the underlying concurrent data structure.
- ArrayMap<String, ArraySet<String>> appIdTags = datastore.get(appId);
- if (appIdTags != null) {
- appIdTags = new ArrayMap<>(appIdTags);
- }
-
- ArraySet<String> packageTags = (appIdTags != null) ? appIdTags.get(packageName) : null;
- if (packageTags != null) {
- packageTags = new ArraySet<>(packageTags);
- }
-
- if (allowListedTags != null && !allowListedTags.isEmpty()) {
- if (packageTags != null) {
- packageTags.clear();
- packageTags.addAll(allowListedTags);
- } else {
- packageTags = new ArraySet<>(allowListedTags);
- }
- if (appIdTags == null) {
- appIdTags = new ArrayMap<>();
- }
-
- // Remove any invalid tags
- boolean nullRemoved = packageTags.remove(null);
- boolean nullStrRemoved = packageTags.remove("null");
- boolean emptyRemoved = packageTags.remove("");
- if (nullRemoved || nullStrRemoved || emptyRemoved) {
- Log.e(LOG_TAG, "Attempted to add invalid source attribution tag, removed "
- + "null: " + nullRemoved + " removed \"null\": " + nullStrRemoved
- + " removed empty string: " + emptyRemoved);
- }
-
- appIdTags.put(packageName, packageTags);
- datastore.put(appId, appIdTags);
- } else if (appIdTags != null) {
- appIdTags.remove(packageName);
- if (!appIdTags.isEmpty()) {
- datastore.put(appId, appIdTags);
- } else {
- datastore.remove(appId);
- }
- }
+ private static void updateAllowListedTagsForPackageLocked(int appId,
+ PackageTagsList packageTagsList,
+ ConcurrentHashMap<Integer, PackageTagsList> datastore) {
+ datastore.put(appId, packageTagsList);
}
private static boolean isDatasourceAttributionTag(int uid, @NonNull String packageName,
- @NonNull String attributionTag, @NonNull Map<Integer, ArrayMap<String,
- ArraySet<String>>> mappedOps) {
+ @NonNull String attributionTag, @NonNull Map<Integer, PackageTagsList> mappedOps) {
// Only a single lookup from the underlying concurrent data structure
- final int appId = UserHandle.getAppId(uid);
- final ArrayMap<String, ArraySet<String>> appIdTags = mappedOps.get(appId);
- if (appIdTags != null) {
- final ArraySet<String> packageTags = appIdTags.get(packageName);
- if (packageTags != null && packageTags.contains(attributionTag)) {
- if (packageName.equals("com.google.android.gms")
- && !sExpectedTags.contains(attributionTag)) {
- Log.i("AppOpsDebugRemapping", packageName + " tag "
- + attributionTag + " in " + packageTags);
- }
- return true;
- }
- if (packageName.equals("com.google.android.gms")
- && sExpectedTags.contains(attributionTag)) {
- Log.i("AppOpsDebugRemapping", packageName + " tag " + attributionTag
- + " NOT in " + packageTags);
- }
- } else {
- if (packageName.equals("com.google.android.gms")) {
- Log.i("AppOpsDebugRemapping", "no package tags for uid " + uid
- + " package " + packageName);
- }
-
- }
- return false;
+ final PackageTagsList appIdTags = mappedOps.get(UserHandle.getAppId(uid));
+ return appIdTags != null && appIdTags.contains(packageName, attributionTag);
}
private static int resolveLocationOp(int code) {
diff --git a/services/core/java/com/android/server/stats/pull/ProcfsMemoryUtil.java b/services/core/java/com/android/server/stats/pull/ProcfsMemoryUtil.java
index e1e6195ad260..f653e4b26438 100644
--- a/services/core/java/com/android/server/stats/pull/ProcfsMemoryUtil.java
+++ b/services/core/java/com/android/server/stats/pull/ProcfsMemoryUtil.java
@@ -30,6 +30,9 @@ public final class ProcfsMemoryUtil {
"RssAnon:",
"VmSwap:"
};
+ private static final String[] VMSTAT_KEYS = new String[] {
+ "oom_kill"
+ };
private ProcfsMemoryUtil() {}
@@ -99,4 +102,22 @@ public final class ProcfsMemoryUtil {
public int anonRssInKilobytes;
public int swapInKilobytes;
}
+
+ /** Reads and parses selected entries of /proc/vmstat. */
+ @Nullable
+ static VmStat readVmStat() {
+ long[] vmstat = new long[VMSTAT_KEYS.length];
+ vmstat[0] = -1;
+ Process.readProcLines("/proc/vmstat", VMSTAT_KEYS, vmstat);
+ if (vmstat[0] == -1) {
+ return null;
+ }
+ VmStat result = new VmStat();
+ result.oomKillCount = (int) vmstat[0];
+ return result;
+ }
+
+ static final class VmStat {
+ public int oomKillCount;
+ }
}
diff --git a/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java b/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java
index dc868b325900..cd0ce2bd46a7 100644
--- a/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java
+++ b/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java
@@ -544,6 +544,8 @@ public class StatsPullAtomService extends SystemService {
return pullProcessDmabufMemory(atomTag, data);
case FrameworkStatsLog.SYSTEM_MEMORY:
return pullSystemMemory(atomTag, data);
+ case FrameworkStatsLog.VMSTAT:
+ return pullVmStat(atomTag, data);
case FrameworkStatsLog.TEMPERATURE:
synchronized (mTemperatureLock) {
return pullTemperatureLocked(atomTag, data);
@@ -842,6 +844,7 @@ public class StatsPullAtomService extends SystemService {
registerProcessSystemIonHeapSize();
registerSystemMemory();
registerProcessDmabufMemory();
+ registerVmStat();
registerTemperature();
registerCoolingDevice();
registerBinderCallsStats();
@@ -2273,6 +2276,27 @@ public class StatsPullAtomService extends SystemService {
return StatsManager.PULL_SUCCESS;
}
+ private void registerVmStat() {
+ int tagId = FrameworkStatsLog.VMSTAT;
+ mStatsManager.setPullAtomCallback(
+ tagId,
+ null, // use default PullAtomMetadata values
+ DIRECT_EXECUTOR,
+ mStatsCallbackImpl
+ );
+ }
+
+ int pullVmStat(int atomTag, List<StatsEvent> pulledData) {
+ ProcfsMemoryUtil.VmStat vmStat = ProcfsMemoryUtil.readVmStat();
+ if (vmStat != null) {
+ pulledData.add(
+ FrameworkStatsLog.buildStatsEvent(
+ atomTag,
+ vmStat.oomKillCount));
+ }
+ return StatsManager.PULL_SUCCESS;
+ }
+
private void registerTemperature() {
int tagId = FrameworkStatsLog.TEMPERATURE;
mStatsManager.setPullAtomCallback(
diff --git a/services/core/java/com/android/server/wm/AccessibilityController.java b/services/core/java/com/android/server/wm/AccessibilityController.java
index 71e31c3a10df..ac0665a37c0b 100644
--- a/services/core/java/com/android/server/wm/AccessibilityController.java
+++ b/services/core/java/com/android/server/wm/AccessibilityController.java
@@ -703,7 +703,7 @@ final class AccessibilityController {
Slog.i(LOG_TAG, "Rotation: " + Surface.rotationToString(rotation)
+ " displayId: " + displayContent.getDisplayId());
}
- mMagnifedViewport.onRotationChanged(displayContent.getPendingTransaction());
+ mMagnifedViewport.onRotationChanged();
mHandler.sendEmptyMessage(MyHandler.MESSAGE_NOTIFY_ROTATION_CHANGED);
}
@@ -858,7 +858,7 @@ final class AccessibilityController {
private final RectF mTempRectF = new RectF();
- private final Point mTempPoint = new Point();
+ private final Point mScreenSize = new Point();
private final Matrix mTempMatrix = new Matrix();
@@ -887,8 +887,8 @@ final class AccessibilityController {
if (mDisplayContext.getResources().getConfiguration().isScreenRound()) {
mCircularPath = new Path();
- mDisplay.getRealSize(mTempPoint);
- final int centerXY = mTempPoint.x / 2;
+ mDisplay.getRealSize(mScreenSize);
+ final int centerXY = mScreenSize.x / 2;
mCircularPath.addCircle(centerXY, centerXY, centerXY, Path.Direction.CW);
} else {
mCircularPath = null;
@@ -917,9 +917,9 @@ final class AccessibilityController {
}
void recomputeBounds() {
- mDisplay.getRealSize(mTempPoint);
- final int screenWidth = mTempPoint.x;
- final int screenHeight = mTempPoint.y;
+ mDisplay.getRealSize(mScreenSize);
+ final int screenWidth = mScreenSize.x;
+ final int screenHeight = mScreenSize.y;
mMagnificationRegion.set(0, 0, 0, 0);
final Region availableBounds = mTempRegion1;
@@ -1052,7 +1052,7 @@ final class AccessibilityController {
|| windowType == TYPE_ACCESSIBILITY_MAGNIFICATION_OVERLAY;
}
- void onRotationChanged(SurfaceControl.Transaction t) {
+ void onRotationChanged() {
// If we are showing the magnification border, hide it immediately so
// the user does not see strange artifacts during rotation. The screenshot
// used for rotation already has the border. After the rotation is complete
@@ -1066,7 +1066,7 @@ final class AccessibilityController {
mHandler.sendMessageDelayed(message, delay);
}
recomputeBounds();
- mWindow.updateSize(t);
+ mWindow.updateSize();
}
void setMagnifiedRegionBorderShown(boolean shown, boolean animate) {
@@ -1148,9 +1148,9 @@ final class AccessibilityController {
/* ignore */
}
mSurfaceControl = surfaceControl;
- mDisplay.getRealSize(mTempPoint);
+ mDisplay.getRealSize(mScreenSize);
mBlastBufferQueue = new BLASTBufferQueue(SURFACE_TITLE, mSurfaceControl,
- mTempPoint.x, mTempPoint.y, PixelFormat.RGBA_8888);
+ mScreenSize.x, mScreenSize.y, PixelFormat.RGBA_8888);
final SurfaceControl.Transaction t = mService.mTransactionFactory.get();
final int layer =
@@ -1224,10 +1224,11 @@ final class AccessibilityController {
}
}
- void updateSize(SurfaceControl.Transaction t) {
+ void updateSize() {
synchronized (mService.mGlobalLock) {
- mDisplay.getRealSize(mTempPoint);
- t.setBufferSize(mSurfaceControl, mTempPoint.x, mTempPoint.y);
+ mDisplay.getRealSize(mScreenSize);
+ mBlastBufferQueue.update(mSurfaceControl, mScreenSize.x, mScreenSize.y,
+ PixelFormat.RGBA_8888);
invalidate(mDirtyRect);
}
}
@@ -1296,8 +1297,8 @@ final class AccessibilityController {
pw.println(prefix
+ " mBounds= " + mBounds
+ " mDirtyRect= " + mDirtyRect
- + " mWidth= " + mSurfaceControl.getWidth()
- + " mHeight= " + mSurfaceControl.getHeight());
+ + " mWidth= " + mScreenSize.x
+ + " mHeight= " + mScreenSize.y);
}
private final class AnimationController extends Handler {
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index 45da45aafaba..8514f3599832 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -309,6 +309,7 @@ import android.view.animation.Animation;
import android.window.IRemoteTransition;
import android.window.SizeConfigurationBuckets;
import android.window.SplashScreen;
+import android.window.SplashScreenView;
import android.window.SplashScreenView.SplashScreenViewParcelable;
import android.window.TaskSnapshot;
import android.window.WindowContainerToken;
@@ -716,25 +717,29 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
boolean startingMoved;
boolean mHandleExitSplashScreen;
- @TransferSplashScreenState int mTransferringSplashScreenState =
- TRANSFER_SPLASH_SCREEN_IDLE;
+ @TransferSplashScreenState
+ int mTransferringSplashScreenState = TRANSFER_SPLASH_SCREEN_IDLE;
- // Idle, can be triggered to do transfer if needed.
+ /** Idle, can be triggered to do transfer if needed. */
static final int TRANSFER_SPLASH_SCREEN_IDLE = 0;
- // requesting a copy from shell.
+
+ /** Requesting a copy from shell. */
static final int TRANSFER_SPLASH_SCREEN_COPYING = 1;
- // attach the splash screen view to activity.
+
+ /** Attach the splash screen view to activity. */
static final int TRANSFER_SPLASH_SCREEN_ATTACH_TO_CLIENT = 2;
- // client has taken over splash screen view.
+
+ /** Client has taken over splash screen view. */
static final int TRANSFER_SPLASH_SCREEN_FINISH = 3;
- @IntDef(prefix = { "TRANSFER_SPLASH_SCREEN_" }, value = {
+ @IntDef(prefix = {"TRANSFER_SPLASH_SCREEN_"}, value = {
TRANSFER_SPLASH_SCREEN_IDLE,
TRANSFER_SPLASH_SCREEN_COPYING,
TRANSFER_SPLASH_SCREEN_ATTACH_TO_CLIENT,
TRANSFER_SPLASH_SCREEN_FINISH,
})
- @interface TransferSplashScreenState {}
+ @interface TransferSplashScreenState {
+ }
// How long we wait until giving up transfer splash screen.
private static final int TRANSFER_SPLASH_SCREEN_TIMEOUT = 2000;
@@ -2196,6 +2201,23 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
removeStartingWindowAnimation(false /* prepareAnimation */);
}
+ /**
+ * Notify the shell ({@link com.android.wm.shell.ShellTaskOrganizer} it should clean up any
+ * remaining reference to this {@link ActivityRecord}'s splash screen.
+ * @see com.android.wm.shell.ShellTaskOrganizer#onAppSplashScreenViewRemoved(int)
+ * @see SplashScreenView#remove()
+ */
+ void cleanUpSplashScreen() {
+ // We only clean up the splash screen if we were supposed to handle it. If it was
+ // transferred to another activity, the next one will handle the clean up.
+ if (mHandleExitSplashScreen && !startingMoved
+ && (mTransferringSplashScreenState == TRANSFER_SPLASH_SCREEN_FINISH
+ || mTransferringSplashScreenState == TRANSFER_SPLASH_SCREEN_IDLE)) {
+ ProtoLog.v(WM_DEBUG_STARTING_WINDOW, "Cleaning splash screen token=%s", this);
+ mAtmService.mTaskOrganizerController.onAppSplashScreenViewRemoved(getTask());
+ }
+ }
+
void removeStartingWindow() {
removeStartingWindowAnimation(true /* prepareAnimation */);
}
@@ -3343,6 +3365,9 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
task.cleanUpActivityReferences(this);
clearLastParentBeforePip();
+ // Clean up the splash screen if it was still displayed.
+ cleanUpSplashScreen();
+
deferRelaunchUntilPaused = false;
frozenBeforeDestroy = false;
@@ -7504,10 +7529,6 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
if (getUid() == SYSTEM_UID) {
return false;
}
- // Do not sandbox to activity window bounds if the feature is disabled.
- if (mDisplayContent != null && !mDisplayContent.sandboxDisplayApis()) {
- return false;
- }
// Never apply sandboxing to an app that should be explicitly excluded from the config.
if (info != null && info.neverSandboxDisplayApis()) {
return false;
diff --git a/services/core/java/com/android/server/wm/AppTransitionController.java b/services/core/java/com/android/server/wm/AppTransitionController.java
index eaebb6f1ce74..4acadb21b5e3 100644
--- a/services/core/java/com/android/server/wm/AppTransitionController.java
+++ b/services/core/java/com/android/server/wm/AppTransitionController.java
@@ -623,7 +623,11 @@ public class AppTransitionController {
siblings.add(current);
boolean canPromote = true;
- if (parent == null || !parent.canCreateRemoteAnimationTarget()) {
+ if (parent == null || !parent.canCreateRemoteAnimationTarget()
+ // We cannot promote the animation on Task's parent when the task is in
+ // clearing task in case the animating get stuck when performing the opening
+ // task that behind it.
+ || (current.asTask() != null && current.asTask().mInRemoveTask)) {
canPromote = false;
} else {
// In case a descendant of the parent belongs to the other group, we cannot promote
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index 81992d8934ed..e0bae9d8de32 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -358,13 +358,6 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
boolean mIsSizeForced = false;
/**
- * Overridden display size and metrics to activity window bounds. Set via
- * "adb shell wm set-sandbox-display-apis". Default to true, since only disable for debugging.
- * @see WindowManagerService#setSandboxDisplayApis(int, boolean)
- */
- private boolean mSandboxDisplayApis = true;
-
- /**
* Overridden display density for current user. Initialized with {@link #mInitialDisplayDensity}
* but can be set from Settings or via shell command "adb shell wm density".
* @see WindowManagerService#setForcedDisplayDensityForUser(int, int, int)
@@ -5810,21 +5803,6 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
return true;
}
- /**
- * Sets if Display APIs should be sandboxed to the activity window bounds.
- */
- void setSandboxDisplayApis(boolean sandboxDisplayApis) {
- mSandboxDisplayApis = sandboxDisplayApis;
- }
-
- /**
- * Returns {@code true} is Display APIs should be sandboxed to the activity window bounds,
- * {@code false} otherwise. Default to true, unless set for debugging purposes.
- */
- boolean sandboxDisplayApis() {
- return mSandboxDisplayApis;
- }
-
/** The entry for proceeding to handle {@link #mFixedRotationLaunchingApp}. */
class FixedRotationTransitionListener extends WindowManagerInternal.AppTransitionListener {
diff --git a/services/core/java/com/android/server/wm/KeyguardController.java b/services/core/java/com/android/server/wm/KeyguardController.java
index 0112f797d937..f8238c1d154a 100644
--- a/services/core/java/com/android/server/wm/KeyguardController.java
+++ b/services/core/java/com/android/server/wm/KeyguardController.java
@@ -48,6 +48,7 @@ import android.os.Trace;
import android.util.Slog;
import android.util.SparseArray;
import android.util.proto.ProtoOutputStream;
+import android.view.Display;
import com.android.internal.policy.IKeyguardDismissCallback;
import com.android.server.inputmethod.InputMethodManagerInternal;
@@ -163,16 +164,27 @@ class KeyguardController {
aodShowing ? 1 : 0,
mKeyguardGoingAway ? 1 : 0,
"setKeyguardShown");
+
+ // Update the task snapshot if the screen will not be turned off. To make sure that the
+ // unlocking animation can animate consistent content. The conditions are:
+ // - Either AOD or keyguard changes to be showing. So if the states change individually,
+ // the later one can be skipped to avoid taking snapshot again. While it still accepts
+ // if both of them change to show at the same time.
+ // - Keyguard was not going away. Because if it was, the closing transition is able to
+ // handle the snapshot.
+ // - The display state is ON. Because if AOD is not on or pulsing, the display state will
+ // be OFF or DOZE (the path of screen off may have handled it).
+ if (((aodShowing ^ keyguardShowing) || (aodShowing && aodChanged && keyguardChanged))
+ && !mKeyguardGoingAway && Display.isOnState(
+ mRootWindowContainer.getDefaultDisplay().getDisplayInfo().state)) {
+ mWindowManager.mTaskSnapshotController.snapshotForSleeping(DEFAULT_DISPLAY);
+ }
+
mKeyguardShowing = keyguardShowing;
mAodShowing = aodShowing;
if (aodChanged) {
// Ensure the new state takes effect.
mWindowManager.mWindowPlacerLocked.performSurfacePlacement();
- // If the device can enter AOD and keyguard at the same time, the screen will not be
- // turned off, so the snapshot needs to be refreshed when these states are changed.
- if (aodShowing && keyguardShowing && keyguardChanged) {
- mWindowManager.mTaskSnapshotController.snapshotForSleeping(DEFAULT_DISPLAY);
- }
}
if (keyguardChanged) {
diff --git a/services/core/java/com/android/server/wm/LetterboxConfiguration.java b/services/core/java/com/android/server/wm/LetterboxConfiguration.java
index eb7087cbc722..7174e68b06f4 100644
--- a/services/core/java/com/android/server/wm/LetterboxConfiguration.java
+++ b/services/core/java/com/android/server/wm/LetterboxConfiguration.java
@@ -21,6 +21,7 @@ import android.content.Context;
import android.graphics.Color;
import com.android.internal.R;
+import com.android.internal.annotations.VisibleForTesting;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@@ -104,20 +105,12 @@ final class LetterboxConfiguration {
* com.android.internal.R.dimen.config_fixedOrientationLetterboxAspectRatio} will be ignored and
* the framework implementation will be used to determine the aspect ratio.
*/
+ @VisibleForTesting
void setFixedOrientationLetterboxAspectRatio(float aspectRatio) {
mFixedOrientationLetterboxAspectRatio = aspectRatio;
}
/**
- * Resets the aspect ratio of letterbox for fixed orientation to {@link
- * com.android.internal.R.dimen.config_fixedOrientationLetterboxAspectRatio}.
- */
- void resetFixedOrientationLetterboxAspectRatio() {
- mFixedOrientationLetterboxAspectRatio = mContext.getResources().getFloat(
- com.android.internal.R.dimen.config_fixedOrientationLetterboxAspectRatio);
- }
-
- /**
* Gets the aspect ratio of letterbox for fixed orientation.
*/
float getFixedOrientationLetterboxAspectRatio() {
@@ -125,25 +118,6 @@ final class LetterboxConfiguration {
}
/**
- * Overrides corners raidus for activities presented in the letterbox mode. If given value < 0,
- * both it and a value of {@link
- * com.android.internal.R.integer.config_letterboxActivityCornersRadius} will be ignored and
- * and corners of the activity won't be rounded.
- */
- void setLetterboxActivityCornersRadius(int cornersRadius) {
- mLetterboxActivityCornersRadius = cornersRadius;
- }
-
- /**
- * Resets corners raidus for activities presented in the letterbox mode to {@link
- * com.android.internal.R.integer.config_letterboxActivityCornersRadius}.
- */
- void resetLetterboxActivityCornersRadius() {
- mLetterboxActivityCornersRadius = mContext.getResources().getInteger(
- com.android.internal.R.integer.config_letterboxActivityCornersRadius);
- }
-
- /**
* Whether corners of letterboxed activities are rounded.
*/
boolean isLetterboxActivityCornersRounded() {
@@ -166,25 +140,6 @@ final class LetterboxConfiguration {
return mLetterboxBackgroundColor;
}
-
- /**
- * Sets color of letterbox background which is used when {@link
- * #getLetterboxBackgroundType()} is {@link #LETTERBOX_BACKGROUND_SOLID_COLOR} or as
- * fallback for other backfround types.
- */
- void setLetterboxBackgroundColor(Color color) {
- mLetterboxBackgroundColor = color;
- }
-
- /**
- * Resets color of letterbox background to {@link
- * com.android.internal.R.color.config_letterboxBackgroundColor}.
- */
- void resetLetterboxBackgroundColor() {
- mLetterboxBackgroundColor = Color.valueOf(mContext.getResources().getColor(
- com.android.internal.R.color.config_letterboxBackgroundColor));
- }
-
/**
* Gets {@link LetterboxBackgroundType} specified in {@link
* com.android.internal.R.integer.config_letterboxBackgroundType} or over via ADB command.
@@ -194,19 +149,6 @@ final class LetterboxConfiguration {
return mLetterboxBackgroundType;
}
- /** Sets letterbox background type. */
- void setLetterboxBackgroundType(@LetterboxBackgroundType int backgroundType) {
- mLetterboxBackgroundType = backgroundType;
- }
-
- /**
- * Resets cletterbox background type to {@link
- * com.android.internal.R.integer.config_letterboxBackgroundType}.
- */
- void resetLetterboxBackgroundType() {
- mLetterboxBackgroundType = readLetterboxBackgroundTypeFromConfig(mContext);
- }
-
/** Returns a string representing the given {@link LetterboxBackgroundType}. */
static String letterboxBackgroundTypeToString(
@LetterboxBackgroundType int backgroundType) {
@@ -236,27 +178,6 @@ final class LetterboxConfiguration {
}
/**
- * Overrides alpha of a black scrim shown over wallpaper for {@link
- * #LETTERBOX_BACKGROUND_WALLPAPER} option in {@link mLetterboxBackgroundType}.
- *
- * <p>If given value is < 0 or >= 1, both it and a value of {@link
- * com.android.internal.R.dimen.config_letterboxBackgroundWallaperDarkScrimAlpha} are ignored
- * and 0.0 (transparent) is instead.
- */
- void setLetterboxBackgroundWallpaperDarkScrimAlpha(float alpha) {
- mLetterboxBackgroundWallpaperDarkScrimAlpha = alpha;
- }
-
- /**
- * Resets alpha of a black scrim shown over wallpaper letterbox background to {@link
- * com.android.internal.R.dimen.config_letterboxBackgroundWallaperDarkScrimAlpha}.
- */
- void resetLetterboxBackgroundWallpaperDarkScrimAlpha() {
- mLetterboxBackgroundWallpaperDarkScrimAlpha = mContext.getResources().getFloat(
- com.android.internal.R.dimen.config_letterboxBackgroundWallaperDarkScrimAlpha);
- }
-
- /**
* Gets alpha of a black scrim shown over wallpaper letterbox background.
*/
float getLetterboxBackgroundWallpaperDarkScrimAlpha() {
@@ -264,28 +185,6 @@ final class LetterboxConfiguration {
}
/**
- * Overrides blur radius for {@link #LETTERBOX_BACKGROUND_WALLPAPER} option in
- * {@link mLetterboxBackgroundType}.
- *
- * <p> If given value <= 0, both it and a value of {@link
- * com.android.internal.R.dimen.config_letterboxBackgroundWallpaperBlurRadius} are ignored
- * and 0 is used instead.
- */
- void setLetterboxBackgroundWallpaperBlurRadius(int radius) {
- mLetterboxBackgroundWallpaperBlurRadius = radius;
- }
-
- /**
- * Resets blur raidus for {@link #LETTERBOX_BACKGROUND_WALLPAPER} option in {@link
- * mLetterboxBackgroundType} to {@link
- * com.android.internal.R.dimen.config_letterboxBackgroundWallpaperBlurRadius}.
- */
- void resetLetterboxBackgroundWallpaperBlurRadius() {
- mLetterboxBackgroundWallpaperBlurRadius = mContext.getResources().getDimensionPixelSize(
- com.android.internal.R.dimen.config_letterboxBackgroundWallpaperBlurRadius);
- }
-
- /**
* Gets blur raidus for {@link #LETTERBOX_BACKGROUND_WALLPAPER} option in {@link
* mLetterboxBackgroundType}.
*/
@@ -312,17 +211,9 @@ final class LetterboxConfiguration {
* com.android.internal.R.dimen.config_letterboxHorizontalPositionMultiplier} are ignored and
* central position (0.5) is used.
*/
+ @VisibleForTesting
void setLetterboxHorizontalPositionMultiplier(float multiplier) {
mLetterboxHorizontalPositionMultiplier = multiplier;
}
- /**
- * Resets horizontal position of a center of the letterboxed app window to {@link
- * com.android.internal.R.dimen.config_letterboxHorizontalPositionMultiplier}.
- */
- void resetLetterboxHorizontalPositionMultiplier() {
- mLetterboxHorizontalPositionMultiplier = mContext.getResources().getFloat(
- com.android.internal.R.dimen.config_letterboxHorizontalPositionMultiplier);
- }
-
}
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index 7af8d8bb5200..565f99f80890 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -6689,24 +6689,26 @@ class Task extends WindowContainer<WindowContainer> {
}
}
- // TODO(185200798): Persist theme name instead of theme if
- int splashScreenThemeResId = options != null
- ? options.getSplashScreenThemeResId() : 0;
-
- // User can override the splashscreen theme. The theme name is used to persist
- // the setting, so if no theme is set in the ActivityOptions, we check if has
- // been persisted here.
- if (splashScreenThemeResId == 0) {
+ // Find the splash screen theme. User can override the persisted theme by
+ // ActivityOptions.
+ String splashScreenThemeResName = options != null
+ ? options.getSplashScreenThemeResName() : null;
+ if (splashScreenThemeResName == null || splashScreenThemeResName.isEmpty()) {
try {
- String themeName = mAtmService.getPackageManager()
+ splashScreenThemeResName = mAtmService.getPackageManager()
.getSplashScreenTheme(r.packageName, r.mUserId);
- if (themeName != null) {
- Context packageContext = mAtmService.mContext
- .createPackageContext(r.packageName, 0);
- splashScreenThemeResId = packageContext.getResources()
- .getIdentifier(themeName, null, null);
- }
- } catch (RemoteException | PackageManager.NameNotFoundException
+ } catch (RemoteException ignore) {
+ // Just use the default theme
+ }
+ }
+ int splashScreenThemeResId = 0;
+ if (splashScreenThemeResName != null && !splashScreenThemeResName.isEmpty()) {
+ try {
+ final Context packageContext = mAtmService.mContext
+ .createPackageContext(r.packageName, 0);
+ splashScreenThemeResId = packageContext.getResources()
+ .getIdentifier(splashScreenThemeResName, null, null);
+ } catch (PackageManager.NameNotFoundException
| Resources.NotFoundException ignore) {
// Just use the default theme
}
diff --git a/services/core/java/com/android/server/wm/TaskOrganizerController.java b/services/core/java/com/android/server/wm/TaskOrganizerController.java
index f23028f6f67a..3a2ca80f2e12 100644
--- a/services/core/java/com/android/server/wm/TaskOrganizerController.java
+++ b/services/core/java/com/android/server/wm/TaskOrganizerController.java
@@ -43,6 +43,7 @@ import android.util.proto.ProtoOutputStream;
import android.view.SurfaceControl;
import android.window.ITaskOrganizer;
import android.window.ITaskOrganizerController;
+import android.window.SplashScreenView;
import android.window.StartingWindowInfo;
import android.window.TaskAppearedInfo;
import android.window.TaskSnapshot;
@@ -215,6 +216,14 @@ class TaskOrganizerController extends ITaskOrganizerController.Stub {
}
}
+ void onAppSplashScreenViewRemoved(Task task) {
+ try {
+ mTaskOrganizer.onAppSplashScreenViewRemoved(task.mTaskId);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Exception sending onAppSplashScreenViewRemoved callback", e);
+ }
+ }
+
SurfaceControl prepareLeash(Task task, String reason) {
return new SurfaceControl(task.getSurfaceControl(), reason);
}
@@ -314,6 +323,10 @@ class TaskOrganizerController extends ITaskOrganizerController.Stub {
mOrganizer.copySplashScreenView(t);
}
+ public void onAppSplashScreenViewRemoved(Task t) {
+ mOrganizer.onAppSplashScreenViewRemoved(t);
+ }
+
/**
* Register this task with this state, but doesn't trigger the task appeared callback to
* the organizer.
@@ -566,6 +579,22 @@ class TaskOrganizerController extends ITaskOrganizerController.Stub {
return true;
}
+ /**
+ * Notify the shell ({@link com.android.wm.shell.ShellTaskOrganizer} that the client has
+ * removed the splash screen view.
+ * @see com.android.wm.shell.ShellTaskOrganizer#onAppSplashScreenViewRemoved(int)
+ * @see SplashScreenView#remove()
+ */
+ public void onAppSplashScreenViewRemoved(Task task) {
+ final Task rootTask = task.getRootTask();
+ if (rootTask == null || rootTask.mTaskOrganizer == null) {
+ return;
+ }
+ final TaskOrganizerState state =
+ mTaskOrganizerStates.get(rootTask.mTaskOrganizer.asBinder());
+ state.onAppSplashScreenViewRemoved(task);
+ }
+
void onTaskAppeared(ITaskOrganizer organizer, Task task) {
final TaskOrganizerState state = mTaskOrganizerStates.get(organizer.asBinder());
if (state != null && state.addTask(task)) {
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 6d51849ea513..e9dd521670a9 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -5391,25 +5391,6 @@ public class WindowManagerService extends IWindowManager.Stub
}
}
- void setSandboxDisplayApis(int displayId, boolean sandboxDisplayApis) {
- if (mContext.checkCallingOrSelfPermission(WRITE_SECURE_SETTINGS)
- != PackageManager.PERMISSION_GRANTED) {
- throw new SecurityException("Must hold permission " + WRITE_SECURE_SETTINGS);
- }
-
- final long ident = Binder.clearCallingIdentity();
- try {
- synchronized (mGlobalLock) {
- final DisplayContent displayContent = mRoot.getDisplayContent(displayId);
- if (displayContent != null) {
- displayContent.setSandboxDisplayApis(sandboxDisplayApis);
- }
- }
- } finally {
- Binder.restoreCallingIdentity(ident);
- }
- }
-
/** The global settings only apply to default display. */
private boolean applyForcedPropertiesForDefaultDisplay() {
boolean changed = false;
diff --git a/services/core/java/com/android/server/wm/WindowManagerShellCommand.java b/services/core/java/com/android/server/wm/WindowManagerShellCommand.java
index d59654949a27..a94fd074ff2e 100644
--- a/services/core/java/com/android/server/wm/WindowManagerShellCommand.java
+++ b/services/core/java/com/android/server/wm/WindowManagerShellCommand.java
@@ -19,12 +19,6 @@ package com.android.server.wm;
import static android.os.Build.IS_USER;
import static android.view.CrossWindowBlurListeners.CROSS_WINDOW_BLUR_SUPPORTED;
-import static com.android.server.wm.LetterboxConfiguration.LETTERBOX_BACKGROUND_APP_COLOR_BACKGROUND;
-import static com.android.server.wm.LetterboxConfiguration.LETTERBOX_BACKGROUND_APP_COLOR_BACKGROUND_FLOATING;
-import static com.android.server.wm.LetterboxConfiguration.LETTERBOX_BACKGROUND_SOLID_COLOR;
-import static com.android.server.wm.LetterboxConfiguration.LETTERBOX_BACKGROUND_WALLPAPER;
-
-import android.graphics.Color;
import android.graphics.Point;
import android.graphics.Rect;
import android.os.ParcelFileDescriptor;
@@ -42,7 +36,6 @@ import com.android.internal.os.ByteTransferPipe;
import com.android.internal.protolog.ProtoLogImpl;
import com.android.server.LocalServices;
import com.android.server.statusbar.StatusBarManagerInternal;
-import com.android.server.wm.LetterboxConfiguration.LetterboxBackgroundType;
import java.io.IOException;
import java.io.PrintWriter;
@@ -65,12 +58,10 @@ public class WindowManagerShellCommand extends ShellCommand {
// Internal service impl -- must perform security checks before touching.
private final WindowManagerService mInternal;
- private final LetterboxConfiguration mLetterboxConfiguration;
public WindowManagerShellCommand(WindowManagerService service) {
mInterface = service;
mInternal = service;
- mLetterboxConfiguration = service.mLetterboxConfiguration;
}
@Override
@@ -122,14 +113,6 @@ public class WindowManagerShellCommand extends ShellCommand {
return runGetIgnoreOrientationRequest(pw);
case "dump-visible-window-views":
return runDumpVisibleWindowViews(pw);
- case "set-letterbox-style":
- return runSetLetterboxStyle(pw);
- case "get-letterbox-style":
- return runGetLetterboxStyle(pw);
- case "reset-letterbox-style":
- return runResetLetterboxStyle(pw);
- case "set-sandbox-display-apis":
- return runSandboxDisplayApis(pw);
case "set-multi-window-config":
return runSetMultiWindowConfig();
case "get-multi-window-config":
@@ -348,37 +331,6 @@ public class WindowManagerShellCommand extends ShellCommand {
return 0;
}
- /**
- * Override display size and metrics to reflect the DisplayArea of the calling activity.
- */
- private int runSandboxDisplayApis(PrintWriter pw) throws RemoteException {
- int displayId = Display.DEFAULT_DISPLAY;
- String arg = getNextArgRequired();
- if ("-d".equals(arg)) {
- displayId = Integer.parseInt(getNextArgRequired());
- arg = getNextArgRequired();
- }
-
- final boolean sandboxDisplayApis;
- switch (arg) {
- case "true":
- case "1":
- sandboxDisplayApis = true;
- break;
- case "false":
- case "0":
- sandboxDisplayApis = false;
- break;
- default:
- getErrPrintWriter().println("Error: expecting true, 1, false, 0, but we "
- + "get " + arg);
- return -1;
- }
-
- mInternal.setSandboxDisplayApis(displayId, sandboxDisplayApis);
- return 0;
- }
-
private int runDismissKeyguard(PrintWriter pw) throws RemoteException {
mInterface.dismissKeyguard(null /* callback */, null /* message */);
return 0;
@@ -596,231 +548,6 @@ public class WindowManagerShellCommand extends ShellCommand {
return 0;
}
- private int runSetFixedOrientationLetterboxAspectRatio(PrintWriter pw) throws RemoteException {
- final float aspectRatio;
- try {
- String arg = getNextArgRequired();
- aspectRatio = Float.parseFloat(arg);
- } catch (NumberFormatException e) {
- getErrPrintWriter().println("Error: bad aspect ratio format " + e);
- return -1;
- } catch (IllegalArgumentException e) {
- getErrPrintWriter().println(
- "Error: 'reset' or aspect ratio should be provided as an argument " + e);
- return -1;
- }
- synchronized (mInternal.mGlobalLock) {
- mLetterboxConfiguration.setFixedOrientationLetterboxAspectRatio(aspectRatio);
- }
- return 0;
- }
-
- private int runSetLetterboxActivityCornersRadius(PrintWriter pw) throws RemoteException {
- final int cornersRadius;
- try {
- String arg = getNextArgRequired();
- cornersRadius = Integer.parseInt(arg);
- } catch (NumberFormatException e) {
- getErrPrintWriter().println("Error: bad corners radius format " + e);
- return -1;
- } catch (IllegalArgumentException e) {
- getErrPrintWriter().println(
- "Error: 'reset' or corners radius should be provided as an argument " + e);
- return -1;
- }
- synchronized (mInternal.mGlobalLock) {
- mLetterboxConfiguration.setLetterboxActivityCornersRadius(cornersRadius);
- }
- return 0;
- }
-
- private int runSetLetterboxBackgroundType(PrintWriter pw) throws RemoteException {
- @LetterboxBackgroundType final int backgroundType;
- try {
- String arg = getNextArgRequired();
- switch (arg) {
- case "solid_color":
- backgroundType = LETTERBOX_BACKGROUND_SOLID_COLOR;
- break;
- case "app_color_background":
- backgroundType = LETTERBOX_BACKGROUND_APP_COLOR_BACKGROUND;
- break;
- case "app_color_background_floating":
- backgroundType = LETTERBOX_BACKGROUND_APP_COLOR_BACKGROUND_FLOATING;
- break;
- case "wallpaper":
- backgroundType = LETTERBOX_BACKGROUND_WALLPAPER;
- break;
- default:
- getErrPrintWriter().println(
- "Error: 'reset', 'solid_color', 'app_color_background' or "
- + "'wallpaper' should be provided as an argument");
- return -1;
- }
- } catch (IllegalArgumentException e) {
- getErrPrintWriter().println(
- "Error: 'reset', 'solid_color', 'app_color_background' or "
- + "'wallpaper' should be provided as an argument" + e);
- return -1;
- }
- synchronized (mInternal.mGlobalLock) {
- mLetterboxConfiguration.setLetterboxBackgroundType(backgroundType);
- }
- return 0;
- }
-
- private int runSetLetterboxBackgroundColor(PrintWriter pw) throws RemoteException {
- final Color color;
- try {
- String arg = getNextArgRequired();
- color = Color.valueOf(Color.parseColor(arg));
- } catch (IllegalArgumentException e) {
- getErrPrintWriter().println(
- "Error: 'reset' or color in #RRGGBB format should be provided as "
- + "an argument " + e);
- return -1;
- }
- synchronized (mInternal.mGlobalLock) {
- mLetterboxConfiguration.setLetterboxBackgroundColor(color);
- }
- return 0;
- }
-
- private int runSetLetterboxBackgroundWallpaperBlurRadius(PrintWriter pw)
- throws RemoteException {
- final int radius;
- try {
- String arg = getNextArgRequired();
- radius = Integer.parseInt(arg);
- } catch (NumberFormatException e) {
- getErrPrintWriter().println("Error: blur radius format " + e);
- return -1;
- } catch (IllegalArgumentException e) {
- getErrPrintWriter().println(
- "Error: 'reset' or blur radius should be provided as an argument " + e);
- return -1;
- }
- synchronized (mInternal.mGlobalLock) {
- mLetterboxConfiguration.setLetterboxBackgroundWallpaperBlurRadius(radius);
- }
- return 0;
- }
-
- private int runSetLetterboxBackgroundWallpaperDarkScrimAlpha(PrintWriter pw)
- throws RemoteException {
- final float alpha;
- try {
- String arg = getNextArgRequired();
- alpha = Float.parseFloat(arg);
- } catch (NumberFormatException e) {
- getErrPrintWriter().println("Error: bad alpha format " + e);
- return -1;
- } catch (IllegalArgumentException e) {
- getErrPrintWriter().println(
- "Error: 'reset' or alpha should be provided as an argument " + e);
- return -1;
- }
- synchronized (mInternal.mGlobalLock) {
- mLetterboxConfiguration.setLetterboxBackgroundWallpaperDarkScrimAlpha(alpha);
- }
- return 0;
- }
-
- private int runSeLetterboxHorizontalPositionMultiplier(PrintWriter pw) throws RemoteException {
- final float multiplier;
- try {
- String arg = getNextArgRequired();
- multiplier = Float.parseFloat(arg);
- } catch (NumberFormatException e) {
- getErrPrintWriter().println("Error: bad multiplier format " + e);
- return -1;
- } catch (IllegalArgumentException e) {
- getErrPrintWriter().println(
- "Error: 'reset' or multiplier should be provided as an argument " + e);
- return -1;
- }
- synchronized (mInternal.mGlobalLock) {
- mLetterboxConfiguration.setLetterboxHorizontalPositionMultiplier(multiplier);
- }
- return 0;
- }
-
- private int runSetLetterboxStyle(PrintWriter pw) throws RemoteException {
- if (peekNextArg() == null) {
- getErrPrintWriter().println("Error: No arguments provided.");
- }
- while (peekNextArg() != null) {
- String arg = getNextArg();
- switch (arg) {
- case "--aspectRatio":
- runSetFixedOrientationLetterboxAspectRatio(pw);
- break;
- case "--cornerRadius":
- runSetLetterboxActivityCornersRadius(pw);
- break;
- case "--backgroundType":
- runSetLetterboxBackgroundType(pw);
- break;
- case "--backgroundColor":
- runSetLetterboxBackgroundColor(pw);
- break;
- case "--wallpaperBlurRadius":
- runSetLetterboxBackgroundWallpaperBlurRadius(pw);
- break;
- case "--wallpaperDarkScrimAlpha":
- runSetLetterboxBackgroundWallpaperDarkScrimAlpha(pw);
- break;
- case "--horizontalPositionMultiplier":
- runSeLetterboxHorizontalPositionMultiplier(pw);
- break;
- default:
- getErrPrintWriter().println(
- "Error: Unrecognized letterbox style option: " + arg);
- return -1;
- }
- }
- return 0;
- }
-
- private int runResetLetterboxStyle(PrintWriter pw) throws RemoteException {
- if (peekNextArg() == null) {
- resetLetterboxStyle();
- }
- synchronized (mInternal.mGlobalLock) {
- while (peekNextArg() != null) {
- String arg = getNextArg();
- switch (arg) {
- case "aspectRatio":
- mLetterboxConfiguration.resetFixedOrientationLetterboxAspectRatio();
- break;
- case "cornerRadius":
- mLetterboxConfiguration.resetLetterboxActivityCornersRadius();
- break;
- case "backgroundType":
- mLetterboxConfiguration.resetLetterboxBackgroundType();
- break;
- case "backgroundColor":
- mLetterboxConfiguration.resetLetterboxBackgroundColor();
- break;
- case "wallpaperBlurRadius":
- mLetterboxConfiguration.resetLetterboxBackgroundWallpaperBlurRadius();
- break;
- case "wallpaperDarkScrimAlpha":
- mLetterboxConfiguration.resetLetterboxBackgroundWallpaperDarkScrimAlpha();
- break;
- case "horizontalPositionMultiplier":
- mLetterboxConfiguration.resetLetterboxHorizontalPositionMultiplier();
- break;
- default:
- getErrPrintWriter().println(
- "Error: Unrecognized letterbox style option: " + arg);
- return -1;
- }
- }
- }
- return 0;
- }
-
private int runSetMultiWindowConfig() {
if (peekNextArg() == null) {
getErrPrintWriter().println("Error: No arguments provided.");
@@ -895,40 +622,6 @@ public class WindowManagerShellCommand extends ShellCommand {
return 0;
}
- private void resetLetterboxStyle() {
- synchronized (mInternal.mGlobalLock) {
- mLetterboxConfiguration.resetFixedOrientationLetterboxAspectRatio();
- mLetterboxConfiguration.resetLetterboxActivityCornersRadius();
- mLetterboxConfiguration.resetLetterboxBackgroundType();
- mLetterboxConfiguration.resetLetterboxBackgroundColor();
- mLetterboxConfiguration.resetLetterboxBackgroundWallpaperBlurRadius();
- mLetterboxConfiguration.resetLetterboxBackgroundWallpaperDarkScrimAlpha();
- mLetterboxConfiguration.resetLetterboxHorizontalPositionMultiplier();
- }
- }
-
- private int runGetLetterboxStyle(PrintWriter pw) throws RemoteException {
- synchronized (mInternal.mGlobalLock) {
- pw.println("Corner radius: "
- + mLetterboxConfiguration.getLetterboxActivityCornersRadius());
- pw.println("Horizontal position multiplier: "
- + mLetterboxConfiguration.getLetterboxHorizontalPositionMultiplier());
- pw.println("Aspect ratio: "
- + mLetterboxConfiguration.getFixedOrientationLetterboxAspectRatio());
-
- pw.println("Background type: "
- + LetterboxConfiguration.letterboxBackgroundTypeToString(
- mLetterboxConfiguration.getLetterboxBackgroundType()));
- pw.println(" Background color: " + Integer.toHexString(
- mLetterboxConfiguration.getLetterboxBackgroundColor().toArgb()));
- pw.println(" Wallpaper blur radius: "
- + mLetterboxConfiguration.getLetterboxBackgroundWallpaperBlurRadius());
- pw.println(" Wallpaper dark scrim alpha: "
- + mLetterboxConfiguration.getLetterboxBackgroundWallpaperDarkScrimAlpha());
- }
- return 0;
- }
-
private int runReset(PrintWriter pw) throws RemoteException {
int displayId = getDisplayId(getNextArg());
@@ -953,12 +646,6 @@ public class WindowManagerShellCommand extends ShellCommand {
// set-ignore-orientation-request
mInterface.setIgnoreOrientationRequest(displayId, false /* ignoreOrientationRequest */);
- // set-letterbox-style
- resetLetterboxStyle();
-
- // set-sandbox-display-apis
- mInternal.setSandboxDisplayApis(displayId, /* sandboxDisplayApis= */ true);
-
// set-multi-window-config
runResetMultiWindowConfig();
@@ -993,12 +680,7 @@ public class WindowManagerShellCommand extends ShellCommand {
pw.println(" set-ignore-orientation-request [-d DISPLAY_ID] [true|1|false|0]");
pw.println(" get-ignore-orientation-request [-d DISPLAY_ID] ");
pw.println(" If app requested orientation should be ignored.");
- pw.println(" set-sandbox-display-apis [true|1|false|0]");
- pw.println(" Sets override of Display APIs getRealSize / getRealMetrics to reflect ");
- pw.println(" DisplayArea of the activity, or the window bounds if in letterbox or");
- pw.println(" Size Compat Mode.");
- printLetterboxHelp(pw);
printMultiWindowConfigHelp(pw);
pw.println(" reset [-d DISPLAY_ID]");
@@ -1011,49 +693,6 @@ public class WindowManagerShellCommand extends ShellCommand {
}
}
- private void printLetterboxHelp(PrintWriter pw) {
- pw.println(" set-letterbox-style");
- pw.println(" Sets letterbox style using the following options:");
- pw.println(" --aspectRatio aspectRatio");
- pw.println(" Aspect ratio of letterbox for fixed orientation. If aspectRatio <= "
- + LetterboxConfiguration.MIN_FIXED_ORIENTATION_LETTERBOX_ASPECT_RATIO);
- pw.println(" both it and R.dimen.config_fixedOrientationLetterboxAspectRatio will");
- pw.println(" be ignored and framework implementation will determine aspect ratio.");
- pw.println(" --cornerRadius radius");
- pw.println(" Corners radius for activities in the letterbox mode. If radius < 0,");
- pw.println(" both it and R.integer.config_letterboxActivityCornersRadius will be");
- pw.println(" ignored and corners of the activity won't be rounded.");
- pw.println(" --backgroundType [reset|solid_color|app_color_background");
- pw.println(" |app_color_background_floating|wallpaper]");
- pw.println(" Type of background used in the letterbox mode.");
- pw.println(" --backgroundColor color");
- pw.println(" Color of letterbox which is be used when letterbox background type");
- pw.println(" is 'solid-color'. Use (set)get-letterbox-style to check and control");
- pw.println(" letterbox background type. See Color#parseColor for allowed color");
- pw.println(" formats (#RRGGBB and some colors by name, e.g. magenta or olive).");
- pw.println(" --wallpaperBlurRadius radius");
- pw.println(" Blur radius for 'wallpaper' letterbox background. If radius <= 0");
- pw.println(" both it and R.dimen.config_letterboxBackgroundWallpaperBlurRadius");
- pw.println(" are ignored and 0 is used.");
- pw.println(" --wallpaperDarkScrimAlpha alpha");
- pw.println(" Alpha of a black translucent scrim shown over 'wallpaper'");
- pw.println(" letterbox background. If alpha < 0 or >= 1 both it and");
- pw.println(" R.dimen.config_letterboxBackgroundWallaperDarkScrimAlpha are ignored");
- pw.println(" and 0.0 (transparent) is used instead.");
- pw.println(" --horizontalPositionMultiplier multiplier");
- pw.println(" Horizontal position of app window center. If multiplier < 0 or > 1,");
- pw.println(" both it and R.dimen.config_letterboxHorizontalPositionMultiplier");
- pw.println(" are ignored and central position (0.5) is used.");
- pw.println(" reset-letterbox-style [aspectRatio|cornerRadius|backgroundType");
- pw.println(" |backgroundColor|wallpaperBlurRadius|wallpaperDarkScrimAlpha");
- pw.println(" |horizontalPositionMultiplier]");
- pw.println(" Resets overrides to default values for specified properties separated");
- pw.println(" by space, e.g. 'reset-letterbox-style aspectRatio cornerRadius'.");
- pw.println(" If no arguments provided, all values will be reset.");
- pw.println(" get-letterbox-style");
- pw.println(" Prints letterbox style configuration.");
- }
-
private void printMultiWindowConfigHelp(PrintWriter pw) {
pw.println(" set-multi-window-config");
pw.println(" Sets options to determine if activity should be shown in multi window:");
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index fa24e5238820..2439760e3f67 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -169,6 +169,7 @@ import android.app.admin.DevicePolicyManager.OperationSafetyReason;
import android.app.admin.DevicePolicyManager.PasswordComplexity;
import android.app.admin.DevicePolicyManager.PersonalAppsSuspensionReason;
import android.app.admin.DevicePolicyManagerInternal;
+import android.app.admin.DevicePolicyManagerLiteInternal;
import android.app.admin.DevicePolicySafetyChecker;
import android.app.admin.DeviceStateCache;
import android.app.admin.FactoryResetProtectionPolicy;
@@ -1748,6 +1749,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
mTransferOwnershipMetadataManager = mInjector.newTransferOwnershipMetadataManager();
mBugreportCollectionManager = new RemoteBugreportManager(this, mInjector);
+ // "Lite" interface is available even when the device doesn't have the feature
+ LocalServices.addService(DevicePolicyManagerLiteInternal.class, mLocalService);
if (!mHasFeature) {
// Skip the rest of the initialization
mSetupContentObserver = null;
@@ -8027,6 +8030,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
inForeground = true;
receiverComponent = resolveDelegateReceiver(
DELEGATION_SECURITY_LOGGING, action, userId);
+ // STOPSHIP(b/185004808): remove excessive log.
+ Slogf.d(LOG_TAG, "Delegate for security logs broadcast: " + receiverComponent);
}
if (receiverComponent == null) {
receiverComponent = getOwnerComponent(userId);
@@ -12612,7 +12617,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
}
@VisibleForTesting
- final class LocalService extends DevicePolicyManagerInternal {
+ final class LocalService extends DevicePolicyManagerInternal
+ implements DevicePolicyManagerLiteInternal {
private List<OnCrossProfileWidgetProvidersChangeListener> mWidgetProviderListeners;
@Override
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/OneTimeSafetyChecker.java b/services/devicepolicy/java/com/android/server/devicepolicy/OneTimeSafetyChecker.java
index 86437a27a64d..1cbc634b7152 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/OneTimeSafetyChecker.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/OneTimeSafetyChecker.java
@@ -21,7 +21,7 @@ import static android.app.admin.DevicePolicyManager.operationToString;
import android.app.admin.DevicePolicyManager.DevicePolicyOperation;
import android.app.admin.DevicePolicyManager.OperationSafetyReason;
-import android.app.admin.DevicePolicyManagerInternal;
+import android.app.admin.DevicePolicyManagerLiteInternal;
import android.app.admin.DevicePolicySafetyChecker;
import android.os.Handler;
import android.os.Looper;
@@ -80,8 +80,8 @@ final class OneTimeSafetyChecker implements DevicePolicySafetyChecker {
+ ", should be " + operationToString(mOperation));
}
String reasonName = operationSafetyReasonToString(reason);
- DevicePolicyManagerInternal dpmi = LocalServices
- .getService(DevicePolicyManagerInternal.class);
+ DevicePolicyManagerLiteInternal dpmi = LocalServices
+ .getService(DevicePolicyManagerLiteInternal.class);
Slog.i(TAG, "notifying " + reasonName + " is UNSAFE");
dpmi.notifyUnsafeOperationStateChanged(this, reason, /* isSafe= */ false);
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/SecurityLogMonitor.java b/services/devicepolicy/java/com/android/server/devicepolicy/SecurityLogMonitor.java
index c29de905d370..0741c81bb528 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/SecurityLogMonitor.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/SecurityLogMonitor.java
@@ -64,7 +64,7 @@ class SecurityLogMonitor implements Runnable {
mLastForceNanos = System.nanoTime();
}
- private static final boolean DEBUG = false; // STOPSHIP if true.
+ private static final boolean DEBUG = true; // STOPSHIP if true.
private static final String TAG = "SecurityLogMonitor";
/**
* Each log entry can hold up to 4K bytes (but as of {@link android.os.Build.VERSION_CODES#N}
@@ -427,7 +427,7 @@ class SecurityLogMonitor implements Runnable {
while (!Thread.currentThread().isInterrupted()) {
try {
final boolean force = mForceSemaphore.tryAcquire(POLLING_INTERVAL_MS, MILLISECONDS);
-
+ if (DEBUG) Slog.d(TAG, "Retrieving next batch, force=" + force);
getNextBatch(newLogs);
mLock.lockInterruptibly();
@@ -469,6 +469,11 @@ class SecurityLogMonitor implements Runnable {
return;
}
final int logSize = mPendingLogs.size();
+ if (DEBUG) {
+ Slog.d(TAG, String.format(
+ "notifyDeviceOwnerOrProfileOwnerIfNeeded, size: %d now: %d next: %d",
+ logSize, SystemClock.elapsedRealtime(), mNextAllowedRetrievalTimeMillis));
+ }
if (logSize >= BUFFER_ENTRIES_NOTIFICATION_LEVEL || (force && logSize > 0)) {
// Allow DO to retrieve logs if too many pending logs or if forced.
if (!mAllowedToRetrieve) {
@@ -507,6 +512,7 @@ class SecurityLogMonitor implements Runnable {
synchronized (mForceSemaphore) {
final long toWaitNanos = mLastForceNanos + FORCE_FETCH_THROTTLE_NS - nowNanos;
if (toWaitNanos > 0) {
+ if (DEBUG) Slog.d(TAG, "Forcing security logs throttled");
return NANOSECONDS.toMillis(toWaitNanos) + 1; // Round up.
}
mLastForceNanos = nowNanos;
@@ -515,6 +521,7 @@ class SecurityLogMonitor implements Runnable {
if (mForceSemaphore.availablePermits() == 0) {
mForceSemaphore.release();
}
+ if (DEBUG) Slog.d(TAG, "Forcing security logs semaphore released");
return 0;
}
}
diff --git a/services/tests/mockingservicestests/src/com/android/server/alarm/AlarmManagerServiceTest.java b/services/tests/mockingservicestests/src/com/android/server/alarm/AlarmManagerServiceTest.java
index 21de7916e23d..280204dfd481 100644
--- a/services/tests/mockingservicestests/src/com/android/server/alarm/AlarmManagerServiceTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/alarm/AlarmManagerServiceTest.java
@@ -2309,12 +2309,14 @@ public class AlarmManagerServiceTest {
public void minWindowChangeDisabled() {
mockChangeEnabled(AlarmManager.ENFORCE_MINIMUM_WINDOW_ON_INEXACT_ALARMS, false);
final long minWindow = 73;
+ final long futurity = 10_000;
setDeviceConfigLong(KEY_MIN_WINDOW, minWindow);
// 0 is WINDOW_EXACT and < 0 is WINDOW_HEURISTIC.
for (int window = 1; window <= minWindow; window++) {
final PendingIntent pi = getNewMockPendingIntent();
- setTestAlarm(ELAPSED_REALTIME, 0, window, pi, 0, 0, TEST_CALLING_UID, null);
+ setTestAlarm(ELAPSED_REALTIME, mNowElapsedTest + futurity, window, pi, 0, 0,
+ TEST_CALLING_UID, null);
assertEquals(1, mService.mAlarmStore.size());
final Alarm a = mService.mAlarmStore.remove(unused -> true).get(0);
@@ -2323,18 +2325,61 @@ public class AlarmManagerServiceTest {
}
@Test
+ public void minWindowExempted() {
+ mockChangeEnabled(AlarmManager.ENFORCE_MINIMUM_WINDOW_ON_INEXACT_ALARMS, true);
+ final long minWindow = 73;
+ final long futurity = 10_000;
+
+ setDeviceConfigLong(KEY_MIN_WINDOW, minWindow);
+
+ final int coreUid = 2312;
+ doReturn(true).when(() -> UserHandle.isCore(coreUid));
+
+ final int allowlisted = 54239;
+ when(mDeviceIdleInternal.isAppOnWhitelist(UserHandle.getAppId(allowlisted))).thenReturn(
+ true);
+
+ for (final int callingUid : new int[]{SYSTEM_UI_UID, coreUid, coreUid}) {
+ // 0 is WINDOW_EXACT and < 0 is WINDOW_HEURISTIC.
+ for (int window = 1; window <= minWindow; window++) {
+ final PendingIntent pi = getNewMockPendingIntent();
+ setTestAlarm(ELAPSED_REALTIME, mNowElapsedTest + futurity, window, pi, 0, 0,
+ callingUid, null);
+
+ assertEquals(1, mService.mAlarmStore.size());
+ final Alarm a = mService.mAlarmStore.remove(unused -> true).get(0);
+ assertEquals(window, a.windowLength);
+ }
+ }
+
+ // 0 is WINDOW_EXACT and < 0 is WINDOW_HEURISTIC.
+ for (int window = 1; window <= minWindow; window++) {
+ final PendingIntent pi = getNewMockPendingIntent();
+ setTestAlarm(ELAPSED_REALTIME, mNowElapsedTest + futurity, window, pi, 0, 0,
+ TEST_CALLING_UID, null);
+
+ assertEquals(1, mService.mAlarmStore.size());
+ final Alarm a = mService.mAlarmStore.remove(unused -> true).get(0);
+ assertEquals(minWindow, a.windowLength);
+ }
+ }
+
+ @Test
public void minWindowPriorityAlarm() {
mockChangeEnabled(AlarmManager.ENFORCE_MINIMUM_WINDOW_ON_INEXACT_ALARMS, true);
final long minWindow = 73;
+ final long futurity = 10_000;
setDeviceConfigLong(KEY_MIN_WINDOW, minWindow);
// 0 is WINDOW_EXACT and < 0 is WINDOW_HEURISTIC.
for (int window = 1; window <= minWindow; window++) {
- setPrioritizedAlarm(ELAPSED_REALTIME, 0, window, new IAlarmListener.Stub() {
- @Override
- public void doAlarm(IAlarmCompleteListener callback) throws RemoteException {
- }
- });
+ setPrioritizedAlarm(ELAPSED_REALTIME, mNowElapsedTest + futurity, window,
+ new IAlarmListener.Stub() {
+ @Override
+ public void doAlarm(IAlarmCompleteListener callback)
+ throws RemoteException {
+ }
+ });
assertEquals(1, mService.mAlarmStore.size());
final Alarm a = mService.mAlarmStore.remove(unused -> true).get(0);
assertEquals(window, a.windowLength);
diff --git a/services/tests/mockingservicestests/src/com/android/server/location/provider/MockableLocationProviderTest.java b/services/tests/mockingservicestests/src/com/android/server/location/provider/MockableLocationProviderTest.java
index cf5db2e0db98..8532dbb7f38a 100644
--- a/services/tests/mockingservicestests/src/com/android/server/location/provider/MockableLocationProviderTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/location/provider/MockableLocationProviderTest.java
@@ -158,7 +158,7 @@ public class MockableLocationProviderTest {
@Test
public void testSetState() {
- assertThat(mProvider.isAllowed()).isFalse();
+ assertThat(mProvider.getState().allowed).isFalse();
AbstractLocationProvider.State newState;
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerServiceMigrationTest.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerServiceMigrationTest.java
index fa3f45c08202..a2b1c1cb1a49 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerServiceMigrationTest.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerServiceMigrationTest.java
@@ -34,6 +34,7 @@ import static org.mockito.Mockito.when;
import android.app.admin.DevicePolicyManager;
import android.app.admin.DevicePolicyManagerInternal;
+import android.app.admin.DevicePolicyManagerLiteInternal;
import android.content.ComponentName;
import android.content.Intent;
import android.content.pm.PackageManager;
@@ -158,6 +159,7 @@ public class DevicePolicyManagerServiceMigrationTest extends DpmTestBase {
final long ident = mContext.binder.clearCallingIdentity();
try {
+ LocalServices.removeServiceForTest(DevicePolicyManagerLiteInternal.class);
LocalServices.removeServiceForTest(DevicePolicyManagerInternal.class);
dpms = new DevicePolicyManagerServiceTestable(getServices(), mContext);
@@ -271,6 +273,7 @@ public class DevicePolicyManagerServiceMigrationTest extends DpmTestBase {
final long ident = mContext.binder.clearCallingIdentity();
try {
+ LocalServices.removeServiceForTest(DevicePolicyManagerLiteInternal.class);
LocalServices.removeServiceForTest(DevicePolicyManagerInternal.class);
dpms = new DevicePolicyManagerServiceTestable(getServices(), mContext);
@@ -339,6 +342,7 @@ public class DevicePolicyManagerServiceMigrationTest extends DpmTestBase {
// (Need clearCallingIdentity() to pass permission checks.)
final long ident = mContext.binder.clearCallingIdentity();
try {
+ LocalServices.removeServiceForTest(DevicePolicyManagerLiteInternal.class);
LocalServices.removeServiceForTest(DevicePolicyManagerInternal.class);
dpms = new DevicePolicyManagerServiceTestable(getServices(), mContext);
@@ -499,6 +503,7 @@ public class DevicePolicyManagerServiceMigrationTest extends DpmTestBase {
DevicePolicyManagerServiceTestable dpms;
final long ident = mContext.binder.clearCallingIdentity();
try {
+ LocalServices.removeServiceForTest(DevicePolicyManagerLiteInternal.class);
LocalServices.removeServiceForTest(DevicePolicyManagerInternal.class);
dpms = new DevicePolicyManagerServiceTestable(getServices(), mContext);
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
index 5447a58a1643..cedf6361e33b 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
@@ -84,6 +84,7 @@ import android.app.PendingIntent;
import android.app.admin.DeviceAdminReceiver;
import android.app.admin.DevicePolicyManager;
import android.app.admin.DevicePolicyManagerInternal;
+import android.app.admin.DevicePolicyManagerLiteInternal;
import android.app.admin.FactoryResetProtectionPolicy;
import android.app.admin.PasswordMetrics;
import android.app.admin.SystemUpdatePolicy;
@@ -280,6 +281,7 @@ public class DevicePolicyManagerTest extends DpmTestBase {
private void initializeDpms() {
// Need clearCallingIdentity() to pass permission checks.
final long ident = mContext.binder.clearCallingIdentity();
+ LocalServices.removeServiceForTest(DevicePolicyManagerLiteInternal.class);
LocalServices.removeServiceForTest(DevicePolicyManagerInternal.class);
dpms = new DevicePolicyManagerServiceTestable(getServices(), mContext);
@@ -369,10 +371,14 @@ public class DevicePolicyManagerTest extends DpmTestBase {
.thenReturn(false);
LocalServices.removeServiceForTest(DevicePolicyManagerInternal.class);
+ LocalServices.removeServiceForTest(DevicePolicyManagerLiteInternal.class);
new DevicePolicyManagerServiceTestable(getServices(), mContext);
// If the device has no DPMS feature, it shouldn't register the local service.
assertThat(LocalServices.getService(DevicePolicyManagerInternal.class)).isNull();
+
+ // But should still register the lite one
+ assertThat(LocalServices.getService(DevicePolicyManagerLiteInternal.class)).isNotNull();
}
@Test
diff --git a/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest2.java b/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest2.java
index 7241fa00ecf7..90a127701505 100644
--- a/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest2.java
+++ b/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest2.java
@@ -253,6 +253,7 @@ public class ShortcutManagerTest2 extends BaseShortcutManagerTest {
.setPerson(makePerson("person", "personKey", "personUri"))
.setLongLived(true)
.setExtras(pb)
+ .setStartingTheme(android.R.style.Theme_Black_NoTitleBar_Fullscreen)
.build();
si.addFlags(ShortcutInfo.FLAG_PINNED);
si.setBitmapPath("abc");
@@ -288,6 +289,8 @@ public class ShortcutManagerTest2 extends BaseShortcutManagerTest {
assertEquals(null, si.getTextResName());
assertEquals(0, si.getDisabledMessageResourceId());
assertEquals(null, si.getDisabledMessageResName());
+ assertEquals("android:style/Theme.Black.NoTitleBar.Fullscreen",
+ si.getStartingThemeResName());
}
public void testShortcutInfoParcel_resId() {
@@ -308,6 +311,7 @@ public class ShortcutManagerTest2 extends BaseShortcutManagerTest {
.setCategories(set(ShortcutInfo.SHORTCUT_CATEGORY_CONVERSATION, "xyz"))
.setRank(123)
.setExtras(pb)
+ .setStartingTheme(android.R.style.Theme_Black_NoTitleBar_Fullscreen)
.build();
si.addFlags(ShortcutInfo.FLAG_PINNED);
si.setBitmapPath("abc");
@@ -339,6 +343,8 @@ public class ShortcutManagerTest2 extends BaseShortcutManagerTest {
assertEquals(456, si.getIconResourceId());
assertEquals("string/r456", si.getIconResName());
assertEquals("test_uri", si.getIconUri());
+ assertEquals("android:style/Theme.Black.NoTitleBar.Fullscreen",
+ si.getStartingThemeResName());
}
public void testShortcutInfoClone() {
@@ -2210,6 +2216,10 @@ public class ShortcutManagerTest2 extends BaseShortcutManagerTest {
android.R.drawable.alert_dark_frame, true, getTestContext().getPackageName()));
assertEquals("" + android.R.string.cancel, ShortcutInfo.lookUpResourceName(res,
android.R.string.cancel, false, getTestContext().getPackageName()));
+ assertEquals("" + android.R.style.Theme_Black_NoTitleBar_Fullscreen,
+ ShortcutInfo.lookUpResourceName(
+ res, android.R.style.Theme_Black_NoTitleBar_Fullscreen, true,
+ getTestContext().getPackageName()));
}
public void testLookUpResourceName_appResources() {
@@ -2236,6 +2246,10 @@ public class ShortcutManagerTest2 extends BaseShortcutManagerTest {
assertEquals(android.R.drawable.alert_dark_frame, ShortcutInfo.lookUpResourceId(res,
"" + android.R.drawable.alert_dark_frame, null,
getTestContext().getPackageName()));
+ assertEquals(android.R.style.Theme_Black_NoTitleBar_Fullscreen,
+ ShortcutInfo.lookUpResourceId(
+ res, "" + android.R.style.Theme_Black_NoTitleBar_Fullscreen,
+ null, getTestContext().getPackageName()));
}
// Test for a ShortcutInfo method.
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 5614aa2a165d..577e36c7d5db 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationListenerServiceTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationListenerServiceTest.java
@@ -394,7 +394,7 @@ public class NotificationListenerServiceTest extends UiServiceTestCase {
"disabledMessage", 0, "disabledMessageResName",
null, null, 0, null, 0, 0,
0, "iconResName", "bitmapPath", null, 0,
- null, null, 0);
+ null, null, null);
return si;
}
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 70ba2cf17880..a522b5c6161e 100755
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
@@ -6472,7 +6472,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
zenPolicy, NotificationManager.INTERRUPTION_FILTER_NONE, isEnabled);
try {
- mBinderService.addAutomaticZenRule(rule);
+ mBinderService.addAutomaticZenRule(rule, mContext.getPackageName());
fail("Zen policy only applies to priority only mode");
} catch (IllegalArgumentException e) {
// yay
@@ -6480,11 +6480,11 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
rule = new AutomaticZenRule("test", owner, owner, mock(Uri.class),
zenPolicy, NotificationManager.INTERRUPTION_FILTER_PRIORITY, isEnabled);
- mBinderService.addAutomaticZenRule(rule);
+ mBinderService.addAutomaticZenRule(rule, mContext.getPackageName());
rule = new AutomaticZenRule("test", owner, owner, mock(Uri.class),
null, NotificationManager.INTERRUPTION_FILTER_NONE, isEnabled);
- mBinderService.addAutomaticZenRule(rule);
+ mBinderService.addAutomaticZenRule(rule, mContext.getPackageName());
}
@Test
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/ZenModeConfigTest.java b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeConfigTest.java
index 5262465a399c..d0bf63a1680f 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/ZenModeConfigTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeConfigTest.java
@@ -190,7 +190,7 @@ public class ZenModeConfigTest extends UiServiceTestCase {
ZenModeConfig.ZenRule rule = new ZenModeConfig.ZenRule();
rule.configurationActivity = new ComponentName("a", "a");
- rule.component = new ComponentName("a", "b");
+ rule.component = new ComponentName("b", "b");
rule.conditionId = new Uri.Builder().scheme("hello").build();
rule.condition = new Condition(rule.conditionId, "", Condition.STATE_TRUE);
rule.enabled = true;
@@ -200,6 +200,7 @@ public class ZenModeConfigTest extends UiServiceTestCase {
rule.modified = true;
rule.name = "name";
rule.snoozing = true;
+ rule.pkg = "b";
TypedXmlSerializer out = Xml.newFastSerializer();
ByteArrayOutputStream baos = new ByteArrayOutputStream();
@@ -215,8 +216,7 @@ public class ZenModeConfigTest extends UiServiceTestCase {
new ByteArrayInputStream(baos.toByteArray())), null);
parser.nextTag();
ZenModeConfig.ZenRule fromXml = ZenModeConfig.readRuleXml(parser);
- // read from backing component
- assertEquals("a", fromXml.pkg);
+ assertEquals("b", fromXml.pkg);
// always resets on reboot
assertFalse(fromXml.snoozing);
//should all match original
@@ -232,6 +232,55 @@ public class ZenModeConfigTest extends UiServiceTestCase {
assertEquals(rule.zenMode, fromXml.zenMode);
}
+ @Test
+ public void testRuleXml_pkg_component() throws Exception {
+ String tag = "tag";
+
+ ZenModeConfig.ZenRule rule = new ZenModeConfig.ZenRule();
+ rule.configurationActivity = new ComponentName("a", "a");
+ rule.component = new ComponentName("b", "b");
+
+ TypedXmlSerializer out = Xml.newFastSerializer();
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ out.setOutput(new BufferedOutputStream(baos), "utf-8");
+ out.startDocument(null, true);
+ out.startTag(null, tag);
+ ZenModeConfig.writeRuleXml(rule, out);
+ out.endTag(null, tag);
+ out.endDocument();
+
+ TypedXmlPullParser parser = Xml.newFastPullParser();
+ parser.setInput(new BufferedInputStream(
+ new ByteArrayInputStream(baos.toByteArray())), null);
+ parser.nextTag();
+ ZenModeConfig.ZenRule fromXml = ZenModeConfig.readRuleXml(parser);
+ assertEquals("b", fromXml.pkg);
+ }
+
+ @Test
+ public void testRuleXml_pkg_configActivity() throws Exception {
+ String tag = "tag";
+
+ ZenModeConfig.ZenRule rule = new ZenModeConfig.ZenRule();
+ rule.configurationActivity = new ComponentName("a", "a");
+
+ TypedXmlSerializer out = Xml.newFastSerializer();
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ out.setOutput(new BufferedOutputStream(baos), "utf-8");
+ out.startDocument(null, true);
+ out.startTag(null, tag);
+ ZenModeConfig.writeRuleXml(rule, out);
+ out.endTag(null, tag);
+ out.endDocument();
+
+ TypedXmlPullParser parser = Xml.newFastPullParser();
+ parser.setInput(new BufferedInputStream(
+ new ByteArrayInputStream(baos.toByteArray())), null);
+ parser.nextTag();
+ ZenModeConfig.ZenRule fromXml = ZenModeConfig.readRuleXml(parser);
+ assertNull(fromXml.pkg);
+ }
+
private ZenModeConfig getMutedRingerConfig() {
ZenModeConfig config = new ZenModeConfig();
// Allow alarms, media
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java
index 72c6028e5ad3..00dbaf649ca2 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java
@@ -1597,7 +1597,7 @@ public class ZenModeHelperTest extends UiServiceTestCase {
new ComponentName("android", "ScheduleConditionProvider"),
ZenModeConfig.toScheduleConditionId(new ScheduleInfo()),
NotificationManager.INTERRUPTION_FILTER_PRIORITY, true);
- String id = mZenModeHelperSpy.addAutomaticZenRule(zenRule, "test");
+ String id = mZenModeHelperSpy.addAutomaticZenRule("android", zenRule, "test");
assertTrue(id != null);
ZenModeConfig.ZenRule ruleInConfig = mZenModeHelperSpy.mConfig.automaticRules.get(id);
@@ -1617,12 +1617,12 @@ public class ZenModeHelperTest extends UiServiceTestCase {
new ComponentName("android", "ScheduleConditionProvider"),
sharedUri,
NotificationManager.INTERRUPTION_FILTER_PRIORITY, true);
- String id = mZenModeHelperSpy.addAutomaticZenRule(zenRule, "test");
+ String id = mZenModeHelperSpy.addAutomaticZenRule("android", zenRule, "test");
AutomaticZenRule zenRule2 = new AutomaticZenRule("name2",
new ComponentName("android", "ScheduleConditionProvider"),
sharedUri,
NotificationManager.INTERRUPTION_FILTER_PRIORITY, true);
- String id2 = mZenModeHelperSpy.addAutomaticZenRule(zenRule2, "test");
+ String id2 = mZenModeHelperSpy.addAutomaticZenRule("android", zenRule2, "test");
Condition condition = new Condition(sharedUri, "", Condition.STATE_TRUE);
mZenModeHelperSpy.setAutomaticZenRuleState(sharedUri, condition);
diff --git a/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java b/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java
index d2270b55b954..4e261deb67f6 100644
--- a/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java
@@ -1534,30 +1534,6 @@ public class SizeCompatTests extends WindowTestsBase {
}
@Test
- public void testSandboxDisplayApis_unresizableAppNotSandboxed() {
- // Set up a display in landscape with an unresizable app.
- setUpDisplaySizeWithApp(2500, 1000);
- mActivity.mDisplayContent.setSandboxDisplayApis(false /* sandboxDisplayApis */);
- prepareUnresizable(mActivity, 1.5f, SCREEN_ORIENTATION_LANDSCAPE);
- assertFitted();
-
- // Activity max bounds not be sandboxed since sandboxing is disabled.
- assertMaxBoundsInheritDisplayAreaBounds();
- }
-
- @Test
- public void testSandboxDisplayApis_unresizableAppSandboxed() {
- // Set up a display in landscape with an unresizable app.
- setUpDisplaySizeWithApp(2500, 1000);
- mActivity.mDisplayContent.setSandboxDisplayApis(true /* sandboxDisplayApis */);
- prepareUnresizable(mActivity, 1.5f, SCREEN_ORIENTATION_LANDSCAPE);
- assertFitted();
-
- // Activity max bounds should be sandboxed since sandboxing is enabled.
- assertActivityMaxBoundsSandboxed();
- }
-
- @Test
public void testResizableApp_notSandboxed() {
// Set up a display in landscape with a fully resizable app.
setUpDisplaySizeWithApp(2500, 1000);
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowOrganizerTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowOrganizerTests.java
index 3f1248a5fff7..a1b3159825fb 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowOrganizerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowOrganizerTests.java
@@ -796,6 +796,9 @@ public class WindowOrganizerTests extends WindowTestsBase {
@Override
public void onBackPressedOnTaskRoot(RunningTaskInfo taskInfo) {
}
+ @Override
+ public void onAppSplashScreenViewRemoved(int taskId) {
+ }
};
private ActivityRecord makePipableActivity() {