summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--core/api/current.txt1
-rw-r--r--core/api/system-current.txt46
-rw-r--r--core/java/android/app/ActivityOptions.java3
-rw-r--r--core/java/android/app/Notification.java6
-rw-r--r--core/java/android/app/wallpaper.aconfig7
-rw-r--r--core/java/android/companion/virtual/VirtualDeviceInternal.java15
-rw-r--r--core/java/android/companion/virtual/VirtualDeviceManager.java10
-rw-r--r--core/java/android/companion/virtual/VirtualDeviceParams.java60
-rw-r--r--core/java/android/companion/virtual/flags/flags.aconfig8
-rw-r--r--core/java/android/content/Intent.java13
-rw-r--r--core/java/android/content/pm/LauncherApps.java6
-rw-r--r--core/java/android/hardware/devicestate/feature/flags.aconfig14
-rw-r--r--core/java/android/hardware/display/VirtualDisplayConfig.java4
-rw-r--r--core/java/android/hardware/input/VirtualStylus.java3
-rw-r--r--core/java/android/hardware/input/VirtualStylusButtonEvent.java4
-rw-r--r--core/java/android/hardware/input/VirtualStylusConfig.java4
-rw-r--r--core/java/android/hardware/input/VirtualStylusMotionEvent.java4
-rw-r--r--core/java/android/hardware/input/input_framework.aconfig7
-rw-r--r--core/java/android/os/CombinedMessageQueue/MessageQueue.java7
-rw-r--r--core/java/android/os/IPowerStatsService.aidl2
-rw-r--r--core/java/android/os/LegacyMessageQueue/MessageQueue.java7
-rw-r--r--core/java/android/os/Looper.java42
-rw-r--r--core/java/android/os/PowerMonitorReadings.java52
-rw-r--r--core/java/android/os/health/SystemHealthManager.java15
-rw-r--r--core/java/android/provider/Settings.java15
-rw-r--r--core/java/android/service/notification/StatusBarNotification.java63
-rw-r--r--core/java/android/view/InsetsAnimationControlImpl.java19
-rw-r--r--core/java/android/view/SurfaceControl.java4
-rw-r--r--core/java/android/view/SurfaceView.java2
-rw-r--r--core/java/android/window/flags/lse_desktop_experience.aconfig10
-rw-r--r--core/java/com/android/internal/widget/NotificationProgressBar.java19
-rw-r--r--core/res/res/values/attrs.xml2
-rw-r--r--core/tests/coretests/src/android/os/SystemHealthManagerUnitTest.java153
-rw-r--r--core/tests/coretests/src/android/service/notification/StatusBarNotificationTest.java33
-rw-r--r--core/tests/coretests/src/com/android/internal/widget/NotificationProgressBarTest.java179
-rw-r--r--data/etc/preinstalled-packages-platform.xml5
-rw-r--r--data/etc/privapp-permissions-platform.xml1
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/common/ComponentUtils.kt40
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayImeController.java15
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitScreenUtils.java29
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java5
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopRepository.kt2
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTaskChangeListener.kt10
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksTransitionObserver.kt2
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopUserRepositories.kt30
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/persistence/DesktopPersistentRepository.kt16
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/freeform/FreeformTaskListener.java2
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java4
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTransition.java4
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/recents/IRecentsAnimationRunner.aidl6
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentTasksController.java10
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentsShellCommandHandler.kt51
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentsTransitionHandler.java5
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java15
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java8
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/taskview/TaskViewTaskController.java5
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultMixedHandler.java6
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java51
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecoration.java31
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/WindowDecoration.java2
-rw-r--r--libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/functional/EnterDesktopWithDragExistingWindowsTest.kt27
-rw-r--r--libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/EnterDesktopWithAppHandleMenuExistingWindows.kt70
-rw-r--r--libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/EnterDesktopWithDragExistingWindows.kt74
-rw-r--r--libs/WindowManager/Shell/tests/e2e/splitscreen/scenarios/src/com/android/wm/shell/scenarios/CopyContentInSplit.kt4
-rw-r--r--libs/WindowManager/Shell/tests/e2e/splitscreen/scenarios/src/com/android/wm/shell/scenarios/DismissSplitScreenByDivider.kt4
-rw-r--r--libs/WindowManager/Shell/tests/e2e/splitscreen/scenarios/src/com/android/wm/shell/scenarios/DismissSplitScreenByGoHome.kt4
-rw-r--r--libs/WindowManager/Shell/tests/e2e/splitscreen/scenarios/src/com/android/wm/shell/scenarios/DragDividerToResize.kt4
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/utils/RecentTasksUtils.kt27
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopRepositoryTest.kt44
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTaskChangeListenerTest.kt13
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksTransitionObserverTest.kt4
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/persistence/DesktopPersistentRepositoryTest.kt56
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/freeform/FreeformTaskListenerTests.java6
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/recents/RecentsTransitionHandlerTest.java4
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModelTests.kt54
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModelTestsBase.kt15
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorationTests.java56
-rw-r--r--media/java/android/media/quality/MediaQualityManager.java53
-rw-r--r--media/tests/MediaRouter/Android.bp2
-rw-r--r--native/android/libandroid.map.txt1
-rw-r--r--native/android/system_health.cpp60
-rw-r--r--native/android/tests/system_health/Android.bp66
-rw-r--r--native/android/tests/system_health/NativeSystemHealthUnitTest.cpp231
-rw-r--r--packages/CrashRecovery/framework/Android.bp9
-rw-r--r--packages/CrashRecovery/services/module/java/com/android/server/RescueParty.java132
-rw-r--r--packages/CrashRecovery/services/module/java/com/android/util/ArrayUtils.java73
-rw-r--r--packages/CrashRecovery/services/module/java/com/android/util/FileUtils.java11
-rw-r--r--packages/CrashRecovery/services/module/java/com/android/util/XmlUtils.java53
-rw-r--r--packages/PrintSpooler/Android.bp13
-rw-r--r--packages/PrintSpooler/flags/flags.aconfig11
-rw-r--r--packages/PrintSpooler/res/layout/select_printer_activity.xml1
-rw-r--r--packages/PrintSpooler/res/values-night/themes.xml6
-rw-r--r--packages/PrintSpooler/res/values/themes.xml6
-rw-r--r--packages/PrintSpooler/src/com/android/printspooler/ui/SelectPrinterActivity.java4
-rw-r--r--packages/PrintSpooler/src/com/android/printspooler/widget/PrintContentView.java47
-rw-r--r--packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/framework/common/BytesFormatter.kt17
-rw-r--r--packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/model/app/AppStorageRepository.kt92
-rw-r--r--packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/AppStorageSize.kt40
-rw-r--r--packages/SettingsLib/SpaPrivileged/tests/src/com/android/settingslib/spaprivileged/model/app/AppStorageRepositoryTest.kt94
-rw-r--r--packages/SettingsLib/SpaPrivileged/tests/src/com/android/settingslib/spaprivileged/template/app/AppStorageSizeTest.kt85
-rw-r--r--packages/Shell/AndroidManifest.xml1
-rw-r--r--packages/SystemUI/Android.bp2
-rw-r--r--packages/SystemUI/aconfig/accessibility.aconfig10
-rw-r--r--packages/SystemUI/aconfig/systemui.aconfig35
-rw-r--r--packages/SystemUI/animation/src/com/android/systemui/animation/ActivityTransitionAnimator.kt10
-rw-r--r--packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/DraggableHandler.kt379
-rw-r--r--packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/MultiPointerDraggable.kt678
-rw-r--r--packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayout.kt34
-rw-r--r--packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayoutImpl.kt70
-rw-r--r--packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SwipeAnimation.kt58
-rw-r--r--packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SwipeToScene.kt140
-rw-r--r--packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/content/state/TransitionState.kt9
-rw-r--r--packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/DraggableHandlerTest.kt113
-rw-r--r--packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/MultiPointerDraggableTest.kt866
-rw-r--r--packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/SwipeToSceneTest.kt2
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/floatingmenu/MenuViewTest.java134
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/hearingaid/HearingDevicesDialogDelegateTest.java4
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/clipboardoverlay/ClipboardImageLoaderTest.kt65
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/HearingDevicesTileTest.java15
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/hearingdevices/domain/interactor/HearingDevicesTileDataInteractorTest.kt15
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/call/ui/viewmodel/CallChipViewModelTest.kt16
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/coordinator/RankingCoordinatorTest.java48
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicyTest.kt110
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallControllerTest.kt96
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/FakeHomeStatusBarViewModel.kt2
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/HomeStatusBarViewModelImplTest.kt92
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/StatusBarOperatorNameViewModelTest.kt17
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/volume/dialog/domain/interactor/VolumeDialogCallbacksInteractorTest.kt47
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/volume/dialog/sliders/domain/interactor/VolumeDialogSliderInteractorTest.kt122
-rw-r--r--packages/SystemUI/res/layout/window_magnification_settings_view.xml3
-rw-r--r--packages/SystemUI/res/values/strings.xml2
-rw-r--r--packages/SystemUI/shared/src/com/android/systemui/shared/system/RecentsAnimationListener.java3
-rw-r--r--packages/SystemUI/src/com/android/keyguard/KeyguardPINView.java3
-rw-r--r--packages/SystemUI/src/com/android/systemui/KairosActivatable.kt212
-rw-r--r--packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuInfoRepository.java6
-rw-r--r--packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuView.java25
-rw-r--r--packages/SystemUI/src/com/android/systemui/accessibility/hearingaid/HearingDevicesDialogDelegate.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/accessibility/hearingaid/HearingDevicesDialogReceiver.java6
-rw-r--r--packages/SystemUI/src/com/android/systemui/accessibility/qs/QSAccessibilityModule.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/bouncer/ui/binder/KeyguardBouncerViewBinder.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardImageLoader.kt19
-rw-r--r--packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/inputdevice/tutorial/ui/composable/ActionTutorialContent.kt60
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/controls/ui/animation/ColorSchemeTransition.kt92
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/HearingDevicesTile.java6
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/impl/hearingdevices/domain/interactor/HearingDevicesTileDataInteractor.kt4
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/core/StatusBarRootModernization.kt3
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/RankingCoordinator.java17
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java12
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java136
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragment.java20
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallController.kt79
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/CarrierConfigRepository.kt7
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/CarrierConfigRepositoryImpl.kt8
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/MobileConnectionsRepository.kt4
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/MobileRepositorySwitcher.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/demo/DemoMobileConnectionsRepository.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionsRepositoryImpl.kt27
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconsInteractor.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconsViewModel.kt3
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/binder/HomeStatusBarViewBinder.kt13
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/composable/StatusBarRoot.kt15
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/HomeStatusBarViewModel.kt13
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/StatusBarOperatorNameViewModel.kt7
-rw-r--r--packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/composable/TutorialSelectionScreen.kt4
-rw-r--r--packages/SystemUI/src/com/android/systemui/volume/dialog/domain/interactor/VolumeDialogCallbacksInteractor.kt4
-rw-r--r--packages/SystemUI/src/com/android/systemui/volume/dialog/domain/interactor/VolumeDialogStateInteractor.kt5
-rw-r--r--packages/SystemUI/src/com/android/systemui/volume/dialog/domain/model/VolumeDialogEventModel.kt7
-rw-r--r--packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/domain/interactor/VolumeDialogSliderInteractor.kt7
-rw-r--r--packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/viewmodel/VolumeDialogSliderIconProvider.kt18
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/animation/ActivityTransitionAnimatorTest.kt22
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragmentTest.java181
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionsRepositoryTest.kt27
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/plugins/FakeVolumeDialogController.kt2
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/FakeCarrierConfigRepository.kt9
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/FakeMobileIconsInteractor.kt2
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/volume/dialog/sliders/dagger/VolumeDialogSliderComponentKosmos.kt2
-rw-r--r--packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/BuildScope.kt28
-rw-r--r--packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/Events.kt10
-rw-r--r--packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/Filter.kt8
-rw-r--r--packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/GroupBy.kt6
-rw-r--r--packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/Incremental.kt4
-rw-r--r--packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/Merge.kt18
-rw-r--r--packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/State.kt12
-rw-r--r--packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/StateScope.kt60
-rw-r--r--packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/Transactional.kt4
-rw-r--r--packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/internal/store/MapK.kt8
-rw-r--r--packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/util/MapPatch.kt2
-rw-r--r--packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/util/Maybe.kt2
-rw-r--r--services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java8
-rw-r--r--services/backup/java/com/android/server/backup/BackupAgentConnectionManager.java4
-rw-r--r--services/backup/java/com/android/server/backup/BackupManagerConstants.java64
-rw-r--r--services/backup/java/com/android/server/backup/BackupManagerService.java8
-rw-r--r--services/backup/java/com/android/server/backup/BackupWakeLock.java95
-rw-r--r--services/backup/java/com/android/server/backup/KeyValueAdbBackupEngine.java16
-rw-r--r--services/backup/java/com/android/server/backup/KeyValueAdbRestoreEngine.java4
-rw-r--r--services/backup/java/com/android/server/backup/KeyValueBackupJob.java5
-rw-r--r--services/backup/java/com/android/server/backup/PackageManagerBackupAgent.java4
-rw-r--r--services/backup/java/com/android/server/backup/ProcessedPackagesJournal.java5
-rw-r--r--services/backup/java/com/android/server/backup/TransportManager.java12
-rw-r--r--services/backup/java/com/android/server/backup/UserBackupManagerService.java460
-rw-r--r--services/backup/java/com/android/server/backup/fullbackup/AppMetadataBackupWriter.java4
-rw-r--r--services/backup/java/com/android/server/backup/fullbackup/FullBackupEngine.java15
-rw-r--r--services/backup/java/com/android/server/backup/fullbackup/FullBackupObbConnection.java12
-rw-r--r--services/backup/java/com/android/server/backup/fullbackup/PerformAdbBackupTask.java35
-rw-r--r--services/backup/java/com/android/server/backup/fullbackup/PerformFullTransportBackupTask.java78
-rw-r--r--services/backup/java/com/android/server/backup/internal/BackupHandler.java37
-rw-r--r--services/backup/java/com/android/server/backup/internal/LifecycleOperationStorage.java25
-rw-r--r--services/backup/java/com/android/server/backup/internal/PerformClearTask.java2
-rw-r--r--services/backup/java/com/android/server/backup/internal/RunInitializeReceiver.java5
-rw-r--r--services/backup/java/com/android/server/backup/internal/SetupObserver.java6
-rw-r--r--services/backup/java/com/android/server/backup/keyvalue/KeyValueBackupReporter.java41
-rw-r--r--services/backup/java/com/android/server/backup/keyvalue/KeyValueBackupTask.java7
-rw-r--r--services/backup/java/com/android/server/backup/restore/ActiveRestoreSession.java88
-rw-r--r--services/backup/java/com/android/server/backup/restore/AdbRestoreFinishedLatch.java7
-rw-r--r--services/backup/java/com/android/server/backup/restore/FullRestoreEngine.java57
-rw-r--r--services/backup/java/com/android/server/backup/restore/PerformAdbRestoreTask.java9
-rw-r--r--services/backup/java/com/android/server/backup/restore/PerformUnifiedRestoreTask.java75
-rw-r--r--services/backup/java/com/android/server/backup/utils/BackupEligibilityRules.java10
-rw-r--r--services/backup/java/com/android/server/backup/utils/BackupManagerMonitorEventSender.java9
-rw-r--r--services/backup/java/com/android/server/backup/utils/BackupObserverUtils.java13
-rw-r--r--services/backup/java/com/android/server/backup/utils/RestoreUtils.java5
-rw-r--r--services/backup/java/com/android/server/backup/utils/TarBackupReader.java47
-rw-r--r--services/companion/java/com/android/server/companion/virtual/VirtualDeviceImpl.java59
-rw-r--r--services/companion/java/com/android/server/companion/virtual/VirtualDeviceManagerService.java55
-rw-r--r--services/core/java/com/android/server/am/ActivityManagerConstants.java4
-rw-r--r--services/core/java/com/android/server/am/flags.aconfig10
-rw-r--r--services/core/java/com/android/server/appop/DiscreteOpsDbHelper.java7
-rw-r--r--services/core/java/com/android/server/appop/DiscreteOpsSqlRegistry.java4
-rw-r--r--services/core/java/com/android/server/display/feature/DisplayManagerFlags.java14
-rw-r--r--services/core/java/com/android/server/display/feature/display_flags.aconfig8
-rw-r--r--services/core/java/com/android/server/hdmi/PowerStatusMonitorActionFromPlayback.java43
-rw-r--r--services/core/java/com/android/server/inputmethod/ImeVisibilityStateComputer.java24
-rw-r--r--services/core/java/com/android/server/inputmethod/InputMethodManagerService.java31
-rw-r--r--services/core/java/com/android/server/location/fudger/LocationFudger.java28
-rw-r--r--services/core/java/com/android/server/locksettings/LockSettingsService.java5
-rw-r--r--services/core/java/com/android/server/media/MediaSessionDeviceConfig.java29
-rw-r--r--services/core/java/com/android/server/media/MediaSessionRecord.java15
-rw-r--r--services/core/java/com/android/server/media/quality/MediaQualityService.java176
-rw-r--r--services/core/java/com/android/server/notification/NotificationManagerService.java86
-rw-r--r--services/core/java/com/android/server/notification/NotificationRecordLogger.java15
-rw-r--r--services/core/java/com/android/server/notification/PreferencesHelper.java58
-rw-r--r--services/core/java/com/android/server/pm/InstallPackageHelper.java9
-rw-r--r--services/core/java/com/android/server/pm/PackageHandler.java18
-rw-r--r--services/core/java/com/android/server/pm/PackageManagerService.java30
-rw-r--r--services/core/java/com/android/server/pm/PackageSetting.java6
-rw-r--r--services/core/java/com/android/server/pm/UserManagerService.java9
-rw-r--r--services/core/java/com/android/server/policy/DeviceStateProviderImpl.java60
-rw-r--r--services/core/java/com/android/server/powerstats/PowerStatsService.java43
-rw-r--r--services/core/java/com/android/server/selinux/QuotaExceededException.java23
-rw-r--r--services/core/java/com/android/server/selinux/SelinuxAuditLogsCollector.java147
-rw-r--r--services/core/java/com/android/server/selinux/SelinuxAuditLogsJob.java8
-rw-r--r--services/core/java/com/android/server/timezonedetector/Environment.java80
-rw-r--r--services/core/java/com/android/server/timezonedetector/EnvironmentImpl.java10
-rw-r--r--services/core/java/com/android/server/timezonedetector/NotifyingTimeZoneChangeListener.java20
-rw-r--r--services/core/java/com/android/server/timezonedetector/TimeZoneDetectorStrategyImpl.java55
-rw-r--r--services/core/java/com/android/server/wm/ActivityStarter.java1
-rw-r--r--services/core/java/com/android/server/wm/BackNavigationController.java24
-rw-r--r--services/core/java/com/android/server/wm/LaunchParamsController.java3
-rw-r--r--services/core/java/com/android/server/wm/RootWindowContainer.java27
-rw-r--r--services/core/java/com/android/server/wm/TaskLaunchParamsModifier.java6
-rw-r--r--services/core/java/com/android/server/wm/WindowManagerService.java48
-rw-r--r--services/core/java/com/android/server/wm/WindowStateAnimator.java12
-rw-r--r--services/core/xsd/device-state-config/device-state-config.xsd9
-rw-r--r--services/core/xsd/device-state-config/schema/current.txt7
-rw-r--r--services/incremental/IncrementalService.cpp2
-rw-r--r--services/java/com/android/server/SystemServer.java9
-rw-r--r--services/robotests/backup/src/com/android/server/backup/BackupManagerServiceRoboTest.java9
-rw-r--r--services/robotests/backup/src/com/android/server/backup/keyvalue/KeyValueBackupReporterTest.java4
-rw-r--r--services/robotests/backup/src/com/android/server/backup/keyvalue/KeyValueBackupTaskTest.java26
-rw-r--r--services/robotests/backup/src/com/android/server/backup/restore/ActiveRestoreSessionTest.java3
-rw-r--r--services/robotests/backup/src/com/android/server/backup/testing/BackupManagerServiceTestUtils.java13
-rw-r--r--services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/ImeVisibilityStateComputerTest.java6
-rw-r--r--services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/UserDataRepositoryTest.java10
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/backup/fullbackup/PerformFullTransportBackupTaskTest.java5
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/location/fudger/LocationFudgerTest.java27
-rw-r--r--services/tests/powerstatstests/src/com/android/server/powerstats/PowerStatsServiceTest.java26
-rw-r--r--services/tests/selinux/src/com/android/server/selinux/SelinuxAuditLogsCollectorTest.java38
-rw-r--r--services/tests/selinux/src/com/android/server/selinux/SelinuxAuditLogsJobTest.java8
-rw-r--r--services/tests/servicestests/src/com/android/server/accessibility/AccessibilityManagerServiceTest.java20
-rw-r--r--services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDevicePlaybackTest.java3
-rw-r--r--services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest1.java30
-rw-r--r--services/tests/servicestests/src/com/android/server/policy/DeviceStateProviderImplTest.java40
-rw-r--r--services/tests/timetests/src/com/android/server/timezonedetector/FakeEnvironment.java141
-rw-r--r--services/tests/timetests/src/com/android/server/timezonedetector/NotifyingTimeZoneChangeListenerTest.java114
-rw-r--r--services/tests/timetests/src/com/android/server/timezonedetector/TimeZoneDetectorStrategyImplTest.java110
-rw-r--r--services/tests/uiservicestests/src/com/android/server/notification/NotificationAssistantsTest.java120
-rw-r--r--services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java82
-rw-r--r--services/tests/wmtests/Android.bp66
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/SystemServicesTestRule.java40
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceTestSupport.kt112
291 files changed, 5456 insertions, 5436 deletions
diff --git a/core/api/current.txt b/core/api/current.txt
index b7f7a7f9e779..19f68eb0c787 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -38278,7 +38278,6 @@ package android.provider {
field public static final String ACTION_NOTIFICATION_LISTENER_DETAIL_SETTINGS = "android.settings.NOTIFICATION_LISTENER_DETAIL_SETTINGS";
field public static final String ACTION_NOTIFICATION_LISTENER_SETTINGS = "android.settings.ACTION_NOTIFICATION_LISTENER_SETTINGS";
field public static final String ACTION_NOTIFICATION_POLICY_ACCESS_SETTINGS = "android.settings.NOTIFICATION_POLICY_ACCESS_SETTINGS";
- field @FlaggedApi("android.provider.system_regional_preferences_api_enabled") public static final String ACTION_NUMBERING_SYSTEM_SETTINGS = "android.settings.NUMBERING_SYSTEM_SETTINGS";
field public static final String ACTION_PRINT_SETTINGS = "android.settings.ACTION_PRINT_SETTINGS";
field public static final String ACTION_PRIVACY_SETTINGS = "android.settings.PRIVACY_SETTINGS";
field public static final String ACTION_PROCESS_WIFI_EASY_CONNECT_URI = "android.settings.PROCESS_WIFI_EASY_CONNECT_URI";
diff --git a/core/api/system-current.txt b/core/api/system-current.txt
index 93f311969c1e..ae5542be7548 100644
--- a/core/api/system-current.txt
+++ b/core/api/system-current.txt
@@ -3405,12 +3405,12 @@ package android.companion.virtual {
public final class VirtualDeviceManager {
method @NonNull @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE) public android.companion.virtual.VirtualDeviceManager.VirtualDevice createVirtualDevice(int, @NonNull android.companion.virtual.VirtualDeviceParams);
- method @FlaggedApi("android.companion.virtual.flags.persistent_device_id_api") @NonNull public java.util.Set<java.lang.String> getAllPersistentDeviceIds();
- method @FlaggedApi("android.companion.virtual.flags.persistent_device_id_api") @Nullable public CharSequence getDisplayNameForPersistentDeviceId(@NonNull String);
+ method @NonNull public java.util.Set<java.lang.String> getAllPersistentDeviceIds();
+ method @Nullable public CharSequence getDisplayNameForPersistentDeviceId(@NonNull String);
field public static final int LAUNCH_FAILURE_NO_ACTIVITY = 2; // 0x2
field public static final int LAUNCH_FAILURE_PENDING_INTENT_CANCELED = 1; // 0x1
field public static final int LAUNCH_SUCCESS = 0; // 0x0
- field @FlaggedApi("android.companion.virtual.flags.persistent_device_id_api") public static final String PERSISTENT_DEVICE_ID_DEFAULT = "default:0";
+ field public static final String PERSISTENT_DEVICE_ID_DEFAULT = "default:0";
}
public static interface VirtualDeviceManager.ActivityListener {
@@ -3432,7 +3432,7 @@ package android.companion.virtual {
public static class VirtualDeviceManager.VirtualDevice implements java.lang.AutoCloseable {
method public void addActivityListener(@NonNull java.util.concurrent.Executor, @NonNull android.companion.virtual.VirtualDeviceManager.ActivityListener);
- method @FlaggedApi("android.companion.virtual.flags.dynamic_policy") public void addActivityPolicyExemption(@NonNull android.content.ComponentName);
+ method public void addActivityPolicyExemption(@NonNull android.content.ComponentName);
method @FlaggedApi("android.companion.virtualdevice.flags.activity_control_api") public void addActivityPolicyExemption(@NonNull android.companion.virtual.ActivityPolicyExemption);
method public void addSoundEffectListener(@NonNull java.util.concurrent.Executor, @NonNull android.companion.virtual.VirtualDeviceManager.SoundEffectListener);
method public void close();
@@ -3448,7 +3448,7 @@ package android.companion.virtual {
method @Deprecated @NonNull public android.hardware.input.VirtualMouse createVirtualMouse(@NonNull android.hardware.display.VirtualDisplay, @NonNull String, int, int);
method @NonNull public android.hardware.input.VirtualNavigationTouchpad createVirtualNavigationTouchpad(@NonNull android.hardware.input.VirtualNavigationTouchpadConfig);
method @FlaggedApi("android.companion.virtualdevice.flags.virtual_rotary") @NonNull public android.hardware.input.VirtualRotaryEncoder createVirtualRotaryEncoder(@NonNull android.hardware.input.VirtualRotaryEncoderConfig);
- method @FlaggedApi("android.companion.virtual.flags.virtual_stylus") @NonNull public android.hardware.input.VirtualStylus createVirtualStylus(@NonNull android.hardware.input.VirtualStylusConfig);
+ method @NonNull public android.hardware.input.VirtualStylus createVirtualStylus(@NonNull android.hardware.input.VirtualStylusConfig);
method @NonNull public android.hardware.input.VirtualTouchscreen createVirtualTouchscreen(@NonNull android.hardware.input.VirtualTouchscreenConfig);
method @Deprecated @NonNull public android.hardware.input.VirtualTouchscreen createVirtualTouchscreen(@NonNull android.hardware.display.VirtualDisplay, @NonNull String, int, int);
method public int getDeviceId();
@@ -3458,10 +3458,10 @@ package android.companion.virtual {
method public void launchPendingIntent(int, @NonNull android.app.PendingIntent, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.IntConsumer);
method public void registerIntentInterceptor(@NonNull android.content.IntentFilter, @NonNull java.util.concurrent.Executor, @NonNull android.companion.virtual.VirtualDeviceManager.IntentInterceptorCallback);
method public void removeActivityListener(@NonNull android.companion.virtual.VirtualDeviceManager.ActivityListener);
- method @FlaggedApi("android.companion.virtual.flags.dynamic_policy") public void removeActivityPolicyExemption(@NonNull android.content.ComponentName);
+ method public void removeActivityPolicyExemption(@NonNull android.content.ComponentName);
method @FlaggedApi("android.companion.virtualdevice.flags.activity_control_api") public void removeActivityPolicyExemption(@NonNull android.companion.virtual.ActivityPolicyExemption);
method public void removeSoundEffectListener(@NonNull android.companion.virtual.VirtualDeviceManager.SoundEffectListener);
- method @FlaggedApi("android.companion.virtual.flags.dynamic_policy") public void setDevicePolicy(int, int);
+ method public void setDevicePolicy(int, int);
method @FlaggedApi("android.companion.virtualdevice.flags.activity_control_api") public void setDevicePolicy(int, int, int);
method @FlaggedApi("android.companion.virtual.flags.vdm_custom_ime") public void setDisplayImePolicy(int, int);
method public void setShowPointerIcon(boolean);
@@ -3481,7 +3481,7 @@ package android.companion.virtual {
method @Deprecated public int getDefaultNavigationPolicy();
method public int getDevicePolicy(int);
method @FlaggedApi("android.companion.virtualdevice.flags.device_aware_display_power") @NonNull public java.time.Duration getDimDuration();
- method @FlaggedApi("android.companion.virtual.flags.vdm_custom_home") @Nullable public android.content.ComponentName getHomeComponent();
+ method @Nullable public android.content.ComponentName getHomeComponent();
method @FlaggedApi("android.companion.virtual.flags.vdm_custom_ime") @Nullable public android.content.ComponentName getInputMethodComponent();
method public int getLockState();
method @Nullable public String getName();
@@ -3498,11 +3498,11 @@ package android.companion.virtual {
field public static final int LOCK_STATE_DEFAULT = 0; // 0x0
field @Deprecated public static final int NAVIGATION_POLICY_DEFAULT_ALLOWED = 0; // 0x0
field @Deprecated public static final int NAVIGATION_POLICY_DEFAULT_BLOCKED = 1; // 0x1
- field @FlaggedApi("android.companion.virtual.flags.dynamic_policy") public static final int POLICY_TYPE_ACTIVITY = 3; // 0x3
+ field public static final int POLICY_TYPE_ACTIVITY = 3; // 0x3
field public static final int POLICY_TYPE_AUDIO = 1; // 0x1
field @FlaggedApi("android.companion.virtualdevice.flags.activity_control_api") public static final int POLICY_TYPE_BLOCKED_ACTIVITY = 6; // 0x6
field @FlaggedApi("android.companion.virtual.flags.virtual_camera") public static final int POLICY_TYPE_CAMERA = 5; // 0x5
- field @FlaggedApi("android.companion.virtual.flags.cross_device_clipboard") public static final int POLICY_TYPE_CLIPBOARD = 4; // 0x4
+ field public static final int POLICY_TYPE_CLIPBOARD = 4; // 0x4
field @FlaggedApi("android.companion.virtualdevice.flags.default_device_camera_access_policy") public static final int POLICY_TYPE_DEFAULT_DEVICE_CAMERA_ACCESS = 7; // 0x7
field public static final int POLICY_TYPE_RECENTS = 2; // 0x2
field public static final int POLICY_TYPE_SENSORS = 0; // 0x0
@@ -3520,7 +3520,7 @@ package android.companion.virtual {
method @Deprecated @NonNull public android.companion.virtual.VirtualDeviceParams.Builder setBlockedCrossTaskNavigations(@NonNull java.util.Set<android.content.ComponentName>);
method @NonNull public android.companion.virtual.VirtualDeviceParams.Builder setDevicePolicy(int, int);
method @FlaggedApi("android.companion.virtualdevice.flags.device_aware_display_power") @NonNull public android.companion.virtual.VirtualDeviceParams.Builder setDimDuration(@NonNull java.time.Duration);
- method @FlaggedApi("android.companion.virtual.flags.vdm_custom_home") @NonNull public android.companion.virtual.VirtualDeviceParams.Builder setHomeComponent(@Nullable android.content.ComponentName);
+ method @NonNull public android.companion.virtual.VirtualDeviceParams.Builder setHomeComponent(@Nullable android.content.ComponentName);
method @FlaggedApi("android.companion.virtual.flags.vdm_custom_ime") @NonNull public android.companion.virtual.VirtualDeviceParams.Builder setInputMethodComponent(@Nullable android.content.ComponentName);
method @NonNull @RequiresPermission(value=android.Manifest.permission.ADD_ALWAYS_UNLOCKED_DISPLAY, conditional=true) public android.companion.virtual.VirtualDeviceParams.Builder setLockState(int);
method @NonNull public android.companion.virtual.VirtualDeviceParams.Builder setName(@NonNull String);
@@ -5332,13 +5332,13 @@ package android.hardware.display {
public final class VirtualDisplayConfig implements android.os.Parcelable {
method @FlaggedApi("android.companion.virtualdevice.flags.virtual_display_insets") @Nullable public android.view.DisplayCutout getDisplayCutout();
- method @FlaggedApi("android.companion.virtual.flags.vdm_custom_home") public boolean isHomeSupported();
+ method public boolean isHomeSupported();
method @FlaggedApi("com.android.window.flags.vdm_force_app_universal_resizable_api") public boolean isIgnoreActivitySizeRestrictions();
}
public static final class VirtualDisplayConfig.Builder {
method @FlaggedApi("android.companion.virtualdevice.flags.virtual_display_insets") @NonNull public android.hardware.display.VirtualDisplayConfig.Builder setDisplayCutout(@Nullable android.view.DisplayCutout);
- method @FlaggedApi("android.companion.virtual.flags.vdm_custom_home") @NonNull public android.hardware.display.VirtualDisplayConfig.Builder setHomeSupported(boolean);
+ method @NonNull public android.hardware.display.VirtualDisplayConfig.Builder setHomeSupported(boolean);
method @FlaggedApi("com.android.window.flags.vdm_force_app_universal_resizable_api") @NonNull public android.hardware.display.VirtualDisplayConfig.Builder setIgnoreActivitySizeRestrictions(boolean);
}
@@ -5970,13 +5970,13 @@ package android.hardware.input {
method @NonNull public android.hardware.input.VirtualRotaryEncoderScrollEvent.Builder setScrollAmount(@FloatRange(from=-1.0F, to=1.0f) float);
}
- @FlaggedApi("android.companion.virtual.flags.virtual_stylus") public class VirtualStylus implements java.io.Closeable {
+ public class VirtualStylus implements java.io.Closeable {
method public void close();
method public void sendButtonEvent(@NonNull android.hardware.input.VirtualStylusButtonEvent);
method public void sendMotionEvent(@NonNull android.hardware.input.VirtualStylusMotionEvent);
}
- @FlaggedApi("android.companion.virtual.flags.virtual_stylus") public final class VirtualStylusButtonEvent implements android.os.Parcelable {
+ public final class VirtualStylusButtonEvent implements android.os.Parcelable {
method public int describeContents();
method public int getAction();
method public int getButtonCode();
@@ -5989,7 +5989,7 @@ package android.hardware.input {
field @NonNull public static final android.os.Parcelable.Creator<android.hardware.input.VirtualStylusButtonEvent> CREATOR;
}
- @FlaggedApi("android.companion.virtual.flags.virtual_stylus") public static final class VirtualStylusButtonEvent.Builder {
+ public static final class VirtualStylusButtonEvent.Builder {
ctor public VirtualStylusButtonEvent.Builder();
method @NonNull public android.hardware.input.VirtualStylusButtonEvent build();
method @NonNull public android.hardware.input.VirtualStylusButtonEvent.Builder setAction(int);
@@ -5997,7 +5997,7 @@ package android.hardware.input {
method @NonNull public android.hardware.input.VirtualStylusButtonEvent.Builder setEventTimeNanos(long);
}
- @FlaggedApi("android.companion.virtual.flags.virtual_stylus") public final class VirtualStylusConfig extends android.hardware.input.VirtualInputDeviceConfig implements android.os.Parcelable {
+ public final class VirtualStylusConfig extends android.hardware.input.VirtualInputDeviceConfig implements android.os.Parcelable {
method public int describeContents();
method public int getHeight();
method public int getWidth();
@@ -6005,12 +6005,12 @@ package android.hardware.input {
field @NonNull public static final android.os.Parcelable.Creator<android.hardware.input.VirtualStylusConfig> CREATOR;
}
- @FlaggedApi("android.companion.virtual.flags.virtual_stylus") public static final class VirtualStylusConfig.Builder extends android.hardware.input.VirtualInputDeviceConfig.Builder<android.hardware.input.VirtualStylusConfig.Builder> {
+ public static final class VirtualStylusConfig.Builder extends android.hardware.input.VirtualInputDeviceConfig.Builder<android.hardware.input.VirtualStylusConfig.Builder> {
ctor public VirtualStylusConfig.Builder(@IntRange(from=1) int, @IntRange(from=1) int);
method @NonNull public android.hardware.input.VirtualStylusConfig build();
}
- @FlaggedApi("android.companion.virtual.flags.virtual_stylus") public final class VirtualStylusMotionEvent implements android.os.Parcelable {
+ public final class VirtualStylusMotionEvent implements android.os.Parcelable {
method public int describeContents();
method public int getAction();
method public long getEventTimeNanos();
@@ -6029,7 +6029,7 @@ package android.hardware.input {
field public static final int TOOL_TYPE_STYLUS = 2; // 0x2
}
- @FlaggedApi("android.companion.virtual.flags.virtual_stylus") public static final class VirtualStylusMotionEvent.Builder {
+ public static final class VirtualStylusMotionEvent.Builder {
ctor public VirtualStylusMotionEvent.Builder();
method @NonNull public android.hardware.input.VirtualStylusMotionEvent build();
method @NonNull public android.hardware.input.VirtualStylusMotionEvent.Builder setAction(int);
@@ -11462,6 +11462,12 @@ package android.os {
method @RequiresPermission(android.Manifest.permission.SET_LOW_POWER_STANDBY_PORTS) public void release();
}
+ @FlaggedApi("com.android.server.power.optimization.power_monitor_api") public final class PowerMonitorReadings {
+ method @FlaggedApi("android.permission.flags.fine_power_monitor_permission") public int getGranularity();
+ field @FlaggedApi("android.permission.flags.fine_power_monitor_permission") public static final int GRANULARITY_FINE = 1; // 0x1
+ field @FlaggedApi("android.permission.flags.fine_power_monitor_permission") public static final int GRANULARITY_UNSPECIFIED = 0; // 0x0
+ }
+
@Deprecated public class PowerWhitelistManager {
method @Deprecated @RequiresPermission(android.Manifest.permission.DEVICE_POWER) public void addToWhitelist(@NonNull String);
method @Deprecated @RequiresPermission(android.Manifest.permission.DEVICE_POWER) public void addToWhitelist(@NonNull java.util.List<java.lang.String>);
diff --git a/core/java/android/app/ActivityOptions.java b/core/java/android/app/ActivityOptions.java
index af6978a6b70c..82c746a8ad4c 100644
--- a/core/java/android/app/ActivityOptions.java
+++ b/core/java/android/app/ActivityOptions.java
@@ -1846,6 +1846,7 @@ public class ActivityOptions extends ComponentOptions {
}
/** @hide */
+ @WindowConfiguration.WindowingMode
public int getLaunchWindowingMode() {
return mLaunchWindowingMode;
}
@@ -1855,7 +1856,7 @@ public class ActivityOptions extends ComponentOptions {
* @hide
*/
@TestApi
- public void setLaunchWindowingMode(int windowingMode) {
+ public void setLaunchWindowingMode(@WindowConfiguration.WindowingMode int windowingMode) {
mLaunchWindowingMode = windowingMode;
}
diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java
index c2ce7d511681..3c37b449c3a8 100644
--- a/core/java/android/app/Notification.java
+++ b/core/java/android/app/Notification.java
@@ -6942,6 +6942,12 @@ public class Notification implements Parcelable
public RemoteViews makePublicContentView(boolean isLowPriority) {
if (mN.publicVersion != null) {
final Builder builder = recoverBuilder(mContext, mN.publicVersion);
+ // copy non-sensitive style fields to the public style
+ if (mStyle instanceof Notification.MessagingStyle privateStyle) {
+ if (builder.mStyle instanceof Notification.MessagingStyle publicStyle) {
+ publicStyle.mConversationType = privateStyle.mConversationType;
+ }
+ }
return builder.createContentView();
}
Bundle savedBundle = mN.extras;
diff --git a/core/java/android/app/wallpaper.aconfig b/core/java/android/app/wallpaper.aconfig
index f750a844f4ff..be9e286f5eb7 100644
--- a/core/java/android/app/wallpaper.aconfig
+++ b/core/java/android/app/wallpaper.aconfig
@@ -40,3 +40,10 @@ flag {
purpose: PURPOSE_BUGFIX
}
}
+
+flag {
+ name: "enable_connected_displays_wallpaper"
+ namespace: "lse_desktop_experience"
+ description: "Enable wallpaper support in connected displays"
+ bug: "366461618"
+}
diff --git a/core/java/android/companion/virtual/VirtualDeviceInternal.java b/core/java/android/companion/virtual/VirtualDeviceInternal.java
index 311e24ba6254..3ef78affb7a5 100644
--- a/core/java/android/companion/virtual/VirtualDeviceInternal.java
+++ b/core/java/android/companion/virtual/VirtualDeviceInternal.java
@@ -32,7 +32,6 @@ import android.companion.virtual.audio.VirtualAudioDevice;
import android.companion.virtual.camera.VirtualCamera;
import android.companion.virtual.camera.VirtualCameraConfig;
import android.companion.virtual.sensor.VirtualSensor;
-import android.companion.virtualdevice.flags.Flags;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
@@ -473,14 +472,12 @@ public class VirtualDeviceInternal {
@Nullable VirtualAudioDevice.AudioConfigurationChangeCallback callback) {
if (mVirtualAudioDevice == null) {
try {
- Context context = mContext;
- if (Flags.deviceAwareRecordAudioPermission()) {
- // When using a default policy for audio device-aware RECORD_AUDIO permission
- // should not take effect, thus register policies with the default context.
- if (mVirtualDevice.getDevicePolicy(POLICY_TYPE_AUDIO) == DEVICE_POLICY_CUSTOM) {
- context = mContext.createDeviceContext(getDeviceId());
- }
- }
+ // When using a default policy for audio, the device-aware RECORD_AUDIO permission
+ // should not take effect, thus register policies with the default context.
+ final Context context =
+ mVirtualDevice.getDevicePolicy(POLICY_TYPE_AUDIO) == DEVICE_POLICY_CUSTOM
+ ? mContext.createDeviceContext(getDeviceId())
+ : mContext;
mVirtualAudioDevice = new VirtualAudioDevice(context, mVirtualDevice, display,
executor, callback, () -> mVirtualAudioDevice = null);
} catch (RemoteException e) {
diff --git a/core/java/android/companion/virtual/VirtualDeviceManager.java b/core/java/android/companion/virtual/VirtualDeviceManager.java
index 73ea9f0462d5..91ea673ab6f9 100644
--- a/core/java/android/companion/virtual/VirtualDeviceManager.java
+++ b/core/java/android/companion/virtual/VirtualDeviceManager.java
@@ -170,7 +170,6 @@ public final class VirtualDeviceManager {
* @hide
*/
@SystemApi
- @FlaggedApi(Flags.FLAG_PERSISTENT_DEVICE_ID_API)
public static final String PERSISTENT_DEVICE_ID_DEFAULT =
"default:" + Context.DEVICE_ID_DEFAULT;
@@ -393,7 +392,6 @@ public final class VirtualDeviceManager {
* @hide
*/
// TODO(b/315481938): Link @see VirtualDevice#getPersistentDeviceId()
- @FlaggedApi(Flags.FLAG_PERSISTENT_DEVICE_ID_API)
@SystemApi
@Nullable
public CharSequence getDisplayNameForPersistentDeviceId(@NonNull String persistentDeviceId) {
@@ -416,7 +414,6 @@ public final class VirtualDeviceManager {
* @hide
*/
// TODO(b/315481938): Link @see VirtualDevice#getPersistentDeviceId()
- @FlaggedApi(Flags.FLAG_PERSISTENT_DEVICE_ID_API)
@SystemApi
@NonNull
public Set<String> getAllPersistentDeviceIds() {
@@ -780,7 +777,6 @@ public final class VirtualDeviceManager {
* @see VirtualDeviceParams#POLICY_TYPE_RECENTS
* @see VirtualDeviceParams#POLICY_TYPE_ACTIVITY
*/
- @FlaggedApi(Flags.FLAG_DYNAMIC_POLICY)
public void setDevicePolicy(@VirtualDeviceParams.DynamicPolicyType int policyType,
@VirtualDeviceParams.DevicePolicy int devicePolicy) {
mVirtualDeviceInternal.setDevicePolicy(policyType, devicePolicy);
@@ -802,7 +798,6 @@ public final class VirtualDeviceManager {
* @see #removeActivityPolicyExemption(ComponentName)
* @see #setDevicePolicy
*/
- @FlaggedApi(Flags.FLAG_DYNAMIC_POLICY)
public void addActivityPolicyExemption(@NonNull ComponentName componentName) {
addActivityPolicyExemption(new ActivityPolicyExemption.Builder()
.setComponentName(componentName)
@@ -825,7 +820,6 @@ public final class VirtualDeviceManager {
* @see #addActivityPolicyExemption(ComponentName)
* @see #setDevicePolicy
*/
- @FlaggedApi(Flags.FLAG_DYNAMIC_POLICY)
public void removeActivityPolicyExemption(@NonNull ComponentName componentName) {
removeActivityPolicyExemption(new ActivityPolicyExemption.Builder()
.setComponentName(componentName)
@@ -1037,9 +1031,7 @@ public final class VirtualDeviceManager {
* @param config the touchscreen configurations for the virtual stylus.
*/
@NonNull
- @FlaggedApi(Flags.FLAG_VIRTUAL_STYLUS)
- public VirtualStylus createVirtualStylus(
- @NonNull VirtualStylusConfig config) {
+ public VirtualStylus createVirtualStylus(@NonNull VirtualStylusConfig config) {
return mVirtualDeviceInternal.createVirtualStylus(config);
}
diff --git a/core/java/android/companion/virtual/VirtualDeviceParams.java b/core/java/android/companion/virtual/VirtualDeviceParams.java
index 2be27dabcf90..761e75bd9076 100644
--- a/core/java/android/companion/virtual/VirtualDeviceParams.java
+++ b/core/java/android/companion/virtual/VirtualDeviceParams.java
@@ -248,7 +248,6 @@ public final class VirtualDeviceParams implements Parcelable {
*/
// TODO(b/333443509): Update the documentation of custom policy and link to the new policy
// POLICY_TYPE_BLOCKED_ACTIVITY
- @FlaggedApi(Flags.FLAG_DYNAMIC_POLICY)
public static final int POLICY_TYPE_ACTIVITY = 3;
/**
@@ -264,7 +263,6 @@ public final class VirtualDeviceParams implements Parcelable {
*
* @see android.hardware.display.DisplayManager#VIRTUAL_DISPLAY_FLAG_TRUSTED
*/
- @FlaggedApi(Flags.FLAG_CROSS_DEVICE_CLIPBOARD)
public static final int POLICY_TYPE_CLIPBOARD = 4;
/**
@@ -431,7 +429,6 @@ public final class VirtualDeviceParams implements Parcelable {
* @see Builder#setHomeComponent
* @see VirtualDisplayConfig#isHomeSupported()
*/
- @FlaggedApi(Flags.FLAG_VDM_CUSTOM_HOME)
@Nullable
public ComponentName getHomeComponent() {
return mHomeComponent;
@@ -926,7 +923,6 @@ public final class VirtualDeviceParams implements Parcelable {
*
* @see VirtualDisplayConfig#isHomeSupported()
*/
- @FlaggedApi(Flags.FLAG_VDM_CUSTOM_HOME)
@NonNull
public Builder setHomeComponent(@Nullable ComponentName homeComponent) {
mHomeComponent = homeComponent;
@@ -1282,33 +1278,31 @@ public final class VirtualDeviceParams implements Parcelable {
mVirtualSensorDirectChannelCallback);
}
- if (Flags.dynamicPolicy()) {
- switch (mDevicePolicies.get(POLICY_TYPE_ACTIVITY, -1)) {
- case DEVICE_POLICY_DEFAULT:
- if (mDefaultActivityPolicyConfigured
- && mDefaultActivityPolicy == ACTIVITY_POLICY_DEFAULT_BLOCKED) {
- throw new IllegalArgumentException(
- "DEVICE_POLICY_DEFAULT is explicitly configured for "
- + "POLICY_TYPE_ACTIVITY, which is exclusive with "
- + "setAllowedActivities.");
- }
- break;
- case DEVICE_POLICY_CUSTOM:
- if (mDefaultActivityPolicyConfigured
- && mDefaultActivityPolicy == ACTIVITY_POLICY_DEFAULT_ALLOWED) {
- throw new IllegalArgumentException(
- "DEVICE_POLICY_CUSTOM is explicitly configured for "
- + "POLICY_TYPE_ACTIVITY, which is exclusive with "
- + "setBlockedActivities.");
- }
- break;
- default:
- if (mDefaultActivityPolicyConfigured
- && mDefaultActivityPolicy == ACTIVITY_POLICY_DEFAULT_BLOCKED) {
- mDevicePolicies.put(POLICY_TYPE_ACTIVITY, DEVICE_POLICY_CUSTOM);
- }
- break;
- }
+ switch (mDevicePolicies.get(POLICY_TYPE_ACTIVITY, -1)) {
+ case DEVICE_POLICY_DEFAULT:
+ if (mDefaultActivityPolicyConfigured
+ && mDefaultActivityPolicy == ACTIVITY_POLICY_DEFAULT_BLOCKED) {
+ throw new IllegalArgumentException(
+ "DEVICE_POLICY_DEFAULT is explicitly configured for "
+ + "POLICY_TYPE_ACTIVITY, which is exclusive with "
+ + "setAllowedActivities.");
+ }
+ break;
+ case DEVICE_POLICY_CUSTOM:
+ if (mDefaultActivityPolicyConfigured
+ && mDefaultActivityPolicy == ACTIVITY_POLICY_DEFAULT_ALLOWED) {
+ throw new IllegalArgumentException(
+ "DEVICE_POLICY_CUSTOM is explicitly configured for "
+ + "POLICY_TYPE_ACTIVITY, which is exclusive with "
+ + "setBlockedActivities.");
+ }
+ break;
+ default:
+ if (mDefaultActivityPolicyConfigured
+ && mDefaultActivityPolicy == ACTIVITY_POLICY_DEFAULT_BLOCKED) {
+ mDevicePolicies.put(POLICY_TYPE_ACTIVITY, DEVICE_POLICY_CUSTOM);
+ }
+ break;
}
if (mDimDuration.compareTo(mScreenOffTimeout) > 0) {
@@ -1319,10 +1313,6 @@ public final class VirtualDeviceParams implements Parcelable {
mScreenOffTimeout = INFINITE_TIMEOUT;
}
- if (!Flags.crossDeviceClipboard()) {
- mDevicePolicies.delete(POLICY_TYPE_CLIPBOARD);
- }
-
if (!Flags.virtualCamera()) {
mDevicePolicies.delete(POLICY_TYPE_CAMERA);
}
diff --git a/core/java/android/companion/virtual/flags/flags.aconfig b/core/java/android/companion/virtual/flags/flags.aconfig
index 84af84072f1b..6da2a073ec19 100644
--- a/core/java/android/companion/virtual/flags/flags.aconfig
+++ b/core/java/android/companion/virtual/flags/flags.aconfig
@@ -19,14 +19,6 @@ flag {
flag {
namespace: "virtual_devices"
- name: "device_aware_record_audio_permission"
- description: "Enable device-aware RECORD_AUDIO permission"
- bug: "291737188"
- is_fixed_read_only: true
-}
-
-flag {
- namespace: "virtual_devices"
name: "media_projection_keyguard_restrictions"
description: "Auto-stop MP when the device locks"
bug: "348335290"
diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java
index 01e24d81a7cd..885a2dbc471e 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -4215,6 +4215,17 @@ public class Intent implements Parcelable, Cloneable {
public static final String ACTION_USER_INFO_CHANGED =
"android.intent.action.USER_INFO_CHANGED";
+
+ /**
+ * Broadcast sent to the system when a user's information changes. Carries an extra
+ * {@link #EXTRA_USER_HANDLE} to indicate which user's information changed.
+ * This is only sent to permission protected manifest receivers. It is sent to all users.
+ * @hide
+ */
+ @BroadcastBehavior(includeBackground = true)
+ public static final String ACTION_USER_INFO_CHANGED_BACKGROUND =
+ "android.intent.action.USER_INFO_CHANGED_BACKGROUND";
+
/**
* Broadcast sent to the primary user when an associated managed profile is added (the profile
* was created and is ready to be used). Carries an extra {@link #EXTRA_USER} that specifies
@@ -5460,7 +5471,7 @@ public class Intent implements Parcelable, Cloneable {
/**
* Activities that can be safely invoked from a browser must support this
* category. For example, if the user is viewing a web page or an e-mail
- * and clicks on a link in the text, the Intent generated execute that
+ * and clicks on a link in the text, the Intent generated to execute that
* link will require the BROWSABLE category, so that only activities
* supporting this category will be considered as possible actions. By
* supporting this category, you are promising that there is nothing
diff --git a/core/java/android/content/pm/LauncherApps.java b/core/java/android/content/pm/LauncherApps.java
index a0c0f122497f..1724d9ff0deb 100644
--- a/core/java/android/content/pm/LauncherApps.java
+++ b/core/java/android/content/pm/LauncherApps.java
@@ -1932,6 +1932,9 @@ public class LauncherApps {
* caller should have normal {@link android.Manifest.permission#ACCESS_HIDDEN_PROFILES}
* permission and the {@link android.app.role.RoleManager#ROLE_HOME} role.
*
+ * <p>This callback will also receive changes to the {@link LauncherUserInfo#getUserConfig()},
+ * allowing clients to monitor updates to the user-specific configuration.
+ *
* @param callback The callback to register.
*/
// Alternatively, a system app can access this api for private profile if they've been granted
@@ -1950,6 +1953,9 @@ public class LauncherApps {
* caller should have normal {@link android.Manifest.permission#ACCESS_HIDDEN_PROFILES}
* permission and the {@link android.app.role.RoleManager#ROLE_HOME} role.
*
+ * <p>This callback will also receive changes to the {@link LauncherUserInfo#getUserConfig()},
+ * allowing clients to monitor updates to the user-specific configuration.
+ *
* @param callback The callback to register.
* @param handler that should be used to post callbacks on, may be null.
*/
diff --git a/core/java/android/hardware/devicestate/feature/flags.aconfig b/core/java/android/hardware/devicestate/feature/flags.aconfig
index 6230f4dbf6f4..44d662f2a4fc 100644
--- a/core/java/android/hardware/devicestate/feature/flags.aconfig
+++ b/core/java/android/hardware/devicestate/feature/flags.aconfig
@@ -38,4 +38,16 @@ flag {
description: "Enables Rear Display Mode V2, where the inner display shows the user a UI affordance for exiting the state"
bug: "372486634"
is_fixed_read_only: true
-} \ No newline at end of file
+}
+
+flag {
+ name: "device_state_configuration_flag"
+ is_exported: true
+ namespace: "windowing_sdk"
+ description: "Re-add flag parsing for device_state_configuration.xml configuration for devices that didn't update vendor images."
+ bug: "388366842"
+ is_fixed_read_only: true
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
diff --git a/core/java/android/hardware/display/VirtualDisplayConfig.java b/core/java/android/hardware/display/VirtualDisplayConfig.java
index 72570553f78a..2a9ee7f07934 100644
--- a/core/java/android/hardware/display/VirtualDisplayConfig.java
+++ b/core/java/android/hardware/display/VirtualDisplayConfig.java
@@ -237,10 +237,9 @@ public final class VirtualDisplayConfig implements Parcelable {
* @see Builder#setHomeSupported
* @hide
*/
- @FlaggedApi(android.companion.virtual.flags.Flags.FLAG_VDM_CUSTOM_HOME)
@SystemApi
public boolean isHomeSupported() {
- return android.companion.virtual.flags.Flags.vdmCustomHome() && mIsHomeSupported;
+ return mIsHomeSupported;
}
/**
@@ -605,7 +604,6 @@ public final class VirtualDisplayConfig implements Parcelable {
* @see DisplayManager#VIRTUAL_DISPLAY_FLAG_OWN_CONTENT_ONLY
* @hide
*/
- @FlaggedApi(android.companion.virtual.flags.Flags.FLAG_VDM_CUSTOM_HOME)
@SystemApi
@NonNull
public Builder setHomeSupported(boolean isHomeSupported) {
diff --git a/core/java/android/hardware/input/VirtualStylus.java b/core/java/android/hardware/input/VirtualStylus.java
index 4b79bc482c7b..32aac2efb3c1 100644
--- a/core/java/android/hardware/input/VirtualStylus.java
+++ b/core/java/android/hardware/input/VirtualStylus.java
@@ -16,11 +16,9 @@
package android.hardware.input;
-import android.annotation.FlaggedApi;
import android.annotation.NonNull;
import android.annotation.SystemApi;
import android.companion.virtual.IVirtualDevice;
-import android.companion.virtual.flags.Flags;
import android.os.IBinder;
import android.os.RemoteException;
import android.util.Log;
@@ -34,7 +32,6 @@ import android.util.Log;
*
* @hide
*/
-@FlaggedApi(Flags.FLAG_VIRTUAL_STYLUS)
@SystemApi
public class VirtualStylus extends VirtualInputDevice {
/** @hide */
diff --git a/core/java/android/hardware/input/VirtualStylusButtonEvent.java b/core/java/android/hardware/input/VirtualStylusButtonEvent.java
index 8fcf561bedcd..9fe725a627b4 100644
--- a/core/java/android/hardware/input/VirtualStylusButtonEvent.java
+++ b/core/java/android/hardware/input/VirtualStylusButtonEvent.java
@@ -16,11 +16,9 @@
package android.hardware.input;
-import android.annotation.FlaggedApi;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.SystemApi;
-import android.companion.virtual.flags.Flags;
import android.os.Parcel;
import android.os.Parcelable;
import android.os.SystemClock;
@@ -35,7 +33,6 @@ import java.lang.annotation.RetentionPolicy;
*
* @hide
*/
-@FlaggedApi(Flags.FLAG_VIRTUAL_STYLUS)
@SystemApi
public final class VirtualStylusButtonEvent implements Parcelable {
/** @hide */
@@ -128,7 +125,6 @@ public final class VirtualStylusButtonEvent implements Parcelable {
/**
* Builder for {@link VirtualStylusButtonEvent}.
*/
- @FlaggedApi(Flags.FLAG_VIRTUAL_STYLUS)
public static final class Builder {
@Action
diff --git a/core/java/android/hardware/input/VirtualStylusConfig.java b/core/java/android/hardware/input/VirtualStylusConfig.java
index 64cf1f56d8bc..3c56023fa6d3 100644
--- a/core/java/android/hardware/input/VirtualStylusConfig.java
+++ b/core/java/android/hardware/input/VirtualStylusConfig.java
@@ -16,11 +16,9 @@
package android.hardware.input;
-import android.annotation.FlaggedApi;
import android.annotation.IntRange;
import android.annotation.NonNull;
import android.annotation.SystemApi;
-import android.companion.virtual.flags.Flags;
import android.os.Parcel;
import android.os.Parcelable;
@@ -29,7 +27,6 @@ import android.os.Parcelable;
*
* @hide
*/
-@FlaggedApi(Flags.FLAG_VIRTUAL_STYLUS)
@SystemApi
public final class VirtualStylusConfig extends VirtualTouchDeviceConfig implements Parcelable {
@@ -68,7 +65,6 @@ public final class VirtualStylusConfig extends VirtualTouchDeviceConfig implemen
/**
* Builder for creating a {@link VirtualStylusConfig}.
*/
- @FlaggedApi(Flags.FLAG_VIRTUAL_STYLUS)
public static final class Builder extends VirtualTouchDeviceConfig.Builder<Builder> {
/**
diff --git a/core/java/android/hardware/input/VirtualStylusMotionEvent.java b/core/java/android/hardware/input/VirtualStylusMotionEvent.java
index 0ac6f3aa3e15..fa0ff4f7eeab 100644
--- a/core/java/android/hardware/input/VirtualStylusMotionEvent.java
+++ b/core/java/android/hardware/input/VirtualStylusMotionEvent.java
@@ -16,12 +16,10 @@
package android.hardware.input;
-import android.annotation.FlaggedApi;
import android.annotation.IntDef;
import android.annotation.IntRange;
import android.annotation.NonNull;
import android.annotation.SystemApi;
-import android.companion.virtual.flags.Flags;
import android.os.Parcel;
import android.os.Parcelable;
import android.os.SystemClock;
@@ -38,7 +36,6 @@ import java.lang.annotation.RetentionPolicy;
*
* @hide
*/
-@FlaggedApi(Flags.FLAG_VIRTUAL_STYLUS)
@SystemApi
public final class VirtualStylusMotionEvent implements Parcelable {
private static final int TILT_MIN = -90;
@@ -209,7 +206,6 @@ public final class VirtualStylusMotionEvent implements Parcelable {
/**
* Builder for {@link VirtualStylusMotionEvent}.
*/
- @FlaggedApi(Flags.FLAG_VIRTUAL_STYLUS)
public static final class Builder {
@ToolType
diff --git a/core/java/android/hardware/input/input_framework.aconfig b/core/java/android/hardware/input/input_framework.aconfig
index 7887c15a72ff..62126963cba4 100644
--- a/core/java/android/hardware/input/input_framework.aconfig
+++ b/core/java/android/hardware/input/input_framework.aconfig
@@ -178,6 +178,13 @@ flag {
}
flag {
+ name: "enable_display_color_inversion_key_gestures"
+ namespace: "input"
+ description: "Adds key gestures for display color inversion for accessibility needs"
+ bug: "383730505"
+}
+
+flag {
name: "enable_talkback_and_magnifier_key_gestures"
namespace: "input"
description: "Adds key gestures for talkback and magnifier"
diff --git a/core/java/android/os/CombinedMessageQueue/MessageQueue.java b/core/java/android/os/CombinedMessageQueue/MessageQueue.java
index d9969d8b9596..3026609ed5cb 100644
--- a/core/java/android/os/CombinedMessageQueue/MessageQueue.java
+++ b/core/java/android/os/CombinedMessageQueue/MessageQueue.java
@@ -36,7 +36,6 @@ import android.util.Printer;
import android.util.SparseArray;
import android.util.proto.ProtoOutputStream;
-import com.android.internal.annotations.GuardedBy;
import com.android.internal.ravenwood.RavenwoodEnvironment;
import dalvik.annotation.optimization.NeverCompile;
@@ -1392,12 +1391,12 @@ public final class MessageQueue {
} while (msg != null && !msg.isAsynchronous());
}
if (msg != null) {
+ if (peek) {
+ return msg;
+ }
if (now >= msg.when) {
// Got a message.
mBlocked = false;
- if (peek) {
- return msg;
- }
if (prevMsg != null) {
prevMsg.next = msg.next;
if (prevMsg.next == null) {
diff --git a/core/java/android/os/IPowerStatsService.aidl b/core/java/android/os/IPowerStatsService.aidl
index a0c226205460..e0e9497cfb5c 100644
--- a/core/java/android/os/IPowerStatsService.aidl
+++ b/core/java/android/os/IPowerStatsService.aidl
@@ -25,6 +25,8 @@ interface IPowerStatsService {
const String KEY_ENERGY = "energy";
/** @hide */
const String KEY_TIMESTAMPS = "timestamps";
+ /** @hide */
+ const String KEY_GRANULARITY = "granularity";
/** @hide */
const int RESULT_SUCCESS = 0;
diff --git a/core/java/android/os/LegacyMessageQueue/MessageQueue.java b/core/java/android/os/LegacyMessageQueue/MessageQueue.java
index c0333e914b4d..d12d99a71251 100644
--- a/core/java/android/os/LegacyMessageQueue/MessageQueue.java
+++ b/core/java/android/os/LegacyMessageQueue/MessageQueue.java
@@ -16,7 +16,6 @@
package android.os;
-import android.annotation.FlaggedApi;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -754,12 +753,12 @@ public final class MessageQueue {
} while (msg != null && !msg.isAsynchronous());
}
if (msg != null) {
+ if (peek) {
+ return msg;
+ }
if (now >= msg.when) {
// Got a message.
mBlocked = false;
- if (peek) {
- return msg;
- }
if (prevMsg != null) {
prevMsg.next = msg.next;
if (prevMsg.next == null) {
diff --git a/core/java/android/os/Looper.java b/core/java/android/os/Looper.java
index 012590510714..2fe4871e08dd 100644
--- a/core/java/android/os/Looper.java
+++ b/core/java/android/os/Looper.java
@@ -70,6 +70,13 @@ public final class Looper {
private static final String TAG = "Looper";
+ private static class NoImagePreloadHolder {
+ // Enable/Disable verbose logging with a system prop. e.g.
+ // adb shell 'setprop log.looper.slow.verbose false && stop && start'
+ private static final boolean sVerboseLogging =
+ SystemProperties.getBoolean("log.looper.slow.verbose", false);
+ }
+
// sThreadLocal.get() will return null unless you've called prepare().
@UnsupportedAppUsage
static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
@@ -246,17 +253,21 @@ public final class Looper {
}
}
if (logSlowDelivery) {
+ boolean slow = false;
+
+ if (!me.mSlowDeliveryDetected || NoImagePreloadHolder.sVerboseLogging) {
+ slow = showSlowLog(slowDeliveryThresholdMs, msg.when, dispatchStart,
+ "delivery", msg);
+ }
if (me.mSlowDeliveryDetected) {
- if ((dispatchStart - msg.when) <= 10) {
+ if (!slow && (dispatchStart - msg.when) <= 10) {
Slog.w(TAG, "Drained");
me.mSlowDeliveryDetected = false;
}
- } else {
- if (showSlowLog(slowDeliveryThresholdMs, msg.when, dispatchStart, "delivery",
- msg)) {
- // Once we write a slow delivery log, suppress until the queue drains.
- me.mSlowDeliveryDetected = true;
- }
+ } else if (slow) {
+ // A slow delivery is detected, suppressing further logs unless verbose logging
+ // is enabled.
+ me.mSlowDeliveryDetected = true;
}
}
if (logSlowDispatch) {
@@ -322,6 +333,23 @@ public final class Looper {
@android.ravenwood.annotation.RavenwoodReplace
private static int getThresholdOverride() {
+ // Allow overriding the threshold for all processes' main looper with a system prop.
+ // e.g. adb shell 'setprop log.looper.any.main.slow 1 && stop && start'
+ if (myLooper() == getMainLooper()) {
+ final int globalOverride = SystemProperties.getInt("log.looper.any.main.slow", -1);
+ if (globalOverride >= 0) {
+ return globalOverride;
+ }
+ }
+
+ // Allow overriding the threshold for all threads within a process with a system prop.
+ // e.g. adb shell 'setprop log.looper.1000.any.slow 1 && stop && start'
+ final int processOverride = SystemProperties.getInt("log.looper."
+ + Process.myUid() + ".any.slow", -1);
+ if (processOverride >= 0) {
+ return processOverride;
+ }
+
return SystemProperties.getInt("log.looper."
+ Process.myUid() + "."
+ Thread.currentThread().getName()
diff --git a/core/java/android/os/PowerMonitorReadings.java b/core/java/android/os/PowerMonitorReadings.java
index a0ab066ffb75..85ffc4661df3 100644
--- a/core/java/android/os/PowerMonitorReadings.java
+++ b/core/java/android/os/PowerMonitorReadings.java
@@ -18,8 +18,12 @@ package android.os;
import android.annotation.ElapsedRealtimeLong;
import android.annotation.FlaggedApi;
+import android.annotation.IntDef;
import android.annotation.NonNull;
+import android.annotation.SystemApi;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
import java.util.Arrays;
import java.util.Comparator;
@@ -38,6 +42,37 @@ public final class PowerMonitorReadings {
@NonNull
private final long[] mTimestampsMs;
+ /**
+ * PowerMonitorReadings have the default level of granularity, which may be coarse or fine
+ * as determined by the implementation.
+ * @hide
+ */
+ @FlaggedApi(android.permission.flags.Flags.FLAG_FINE_POWER_MONITOR_PERMISSION)
+ @SystemApi
+ public static final int GRANULARITY_UNSPECIFIED = 0;
+
+ /**
+ * PowerMonitorReadings have a high level of granularity. This level of granularity is
+ * provided to applications that have the
+ * {@link android.Manifest.permission#ACCESS_FINE_POWER_MONITORS} permission.
+ *
+ * @hide
+ */
+ @FlaggedApi(android.permission.flags.Flags.FLAG_FINE_POWER_MONITOR_PERMISSION)
+ @SystemApi
+ public static final int GRANULARITY_FINE = 1;
+
+ /** @hide */
+ @IntDef(prefix = {"GRANULARITY_"}, value = {
+ GRANULARITY_UNSPECIFIED,
+ GRANULARITY_FINE
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface PowerMonitorGranularity {}
+
+ @PowerMonitorGranularity
+ private final int mGranularity;
+
private static final Comparator<PowerMonitor> POWER_MONITOR_COMPARATOR =
Comparator.comparingInt(pm -> pm.index);
@@ -46,10 +81,12 @@ public final class PowerMonitorReadings {
* @hide
*/
public PowerMonitorReadings(@NonNull PowerMonitor[] powerMonitors,
- @NonNull long[] energyUws, @NonNull long[] timestampsMs) {
+ @NonNull long[] energyUws, @NonNull long[] timestampsMs,
+ @PowerMonitorGranularity int granularity) {
mPowerMonitors = powerMonitors;
mEnergyUws = energyUws;
mTimestampsMs = timestampsMs;
+ mGranularity = granularity;
}
/**
@@ -79,6 +116,19 @@ public final class PowerMonitorReadings {
return 0;
}
+ /**
+ * Returns the granularity level of the results, which refers to the maximum age of the
+ * power monitor readings, {@link #GRANULARITY_FINE} indicating the highest level
+ * of freshness supported by the service implementation.
+ * @hide
+ */
+ @FlaggedApi(android.permission.flags.Flags.FLAG_FINE_POWER_MONITOR_PERMISSION)
+ @SystemApi
+ @PowerMonitorGranularity
+ public int getGranularity() {
+ return mGranularity;
+ }
+
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
diff --git a/core/java/android/os/health/SystemHealthManager.java b/core/java/android/os/health/SystemHealthManager.java
index a1e9cf25e3e1..9d0e221bd9e7 100644
--- a/core/java/android/os/health/SystemHealthManager.java
+++ b/core/java/android/os/health/SystemHealthManager.java
@@ -344,11 +344,7 @@ public class SystemHealthManager {
|| !mHintManagerClientData.supportInfo.headroom.isCpuSupported) {
throw new UnsupportedOperationException();
}
- try {
- return mHintManager.getCpuHeadroomMinIntervalMillis();
- } catch (RemoteException re) {
- throw re.rethrowFromSystemServer();
- }
+ return mHintManagerClientData.supportInfo.headroom.cpuMinIntervalMillis;
}
/**
@@ -366,11 +362,7 @@ public class SystemHealthManager {
|| !mHintManagerClientData.supportInfo.headroom.isGpuSupported) {
throw new UnsupportedOperationException();
}
- try {
- return mHintManager.getGpuHeadroomMinIntervalMillis();
- } catch (RemoteException re) {
- throw re.rethrowFromSystemServer();
- }
+ return mHintManagerClientData.supportInfo.headroom.gpuMinIntervalMillis;
}
/**
@@ -588,7 +580,8 @@ public class SystemHealthManager {
if (resultCode == IPowerStatsService.RESULT_SUCCESS) {
PowerMonitorReadings result = new PowerMonitorReadings(powerMonitorsArray,
resultData.getLongArray(IPowerStatsService.KEY_ENERGY),
- resultData.getLongArray(IPowerStatsService.KEY_TIMESTAMPS));
+ resultData.getLongArray(IPowerStatsService.KEY_TIMESTAMPS),
+ resultData.getInt(IPowerStatsService.KEY_GRANULARITY));
if (executor != null) {
executor.execute(() -> onResult.onResult(result));
} else {
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index baaaa464a4cf..0cfec2cc7314 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -1254,21 +1254,6 @@ public final class Settings {
"android.settings.TEMPERATURE_UNIT_SETTINGS";
/**
- * Activity Action: Show numbering system configuration settings.
- * <p>
- * Input: Nothing.
- * <p>
- * Output: After calling {@link android.app.Activity#startActivityForResult}, the callback
- * {@code onActivityResult} will have resultCode {@link android.app.Activity#RESULT_OK} if
- * the numbering system settings page is suitable to show on the UI. Otherwise, the result is
- * set to {@link android.app.Activity#RESULT_CANCELED}.
- */
- @FlaggedApi(Flags.FLAG_SYSTEM_REGIONAL_PREFERENCES_API_ENABLED)
- @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
- public static final String ACTION_NUMBERING_SYSTEM_SETTINGS =
- "android.settings.NUMBERING_SYSTEM_SETTINGS";
-
- /**
* Activity Action: Show measurement system configuration settings.
* <p>
* Input: Nothing.
diff --git a/core/java/android/service/notification/StatusBarNotification.java b/core/java/android/service/notification/StatusBarNotification.java
index 146c2b6fa46e..105fa3ffd4cd 100644
--- a/core/java/android/service/notification/StatusBarNotification.java
+++ b/core/java/android/service/notification/StatusBarNotification.java
@@ -30,13 +30,17 @@ import android.metrics.LogMaker;
import android.os.Build;
import android.os.Parcel;
import android.os.Parcelable;
+import android.os.Trace;
import android.os.UserHandle;
+import android.util.ArrayMap;
import com.android.internal.logging.InstanceId;
import com.android.internal.logging.nano.MetricsProto;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
+import static com.android.window.flags.Flags.enablePerDisplayPackageContextCacheInStatusbarNotif;
import java.util.ArrayList;
+import java.util.Map;
/**
* Class encapsulating a Notification. Sent by the NotificationManagerService to clients including
@@ -69,7 +73,15 @@ public class StatusBarNotification implements Parcelable {
// A small per-notification ID, used for statsd logging.
private InstanceId mInstanceId; // Not final, see setInstanceId()
+ /**
+ * @deprecated This field is only used when
+ * {@link enablePerDisplayPackageContextCacheInStatusbarNotif}
+ * is disabled.
+ */
+ @Deprecated
private Context mContext; // used for inflation & icon expansion
+ // Maps display id to context used for remote view content inflation and status bar icon.
+ private final Map<Integer, Context> mContextForDisplayId = new ArrayMap<>();
/** @hide */
public StatusBarNotification(String pkg, String opPkg, int id,
@@ -453,7 +465,11 @@ public class StatusBarNotification implements Parcelable {
* @hide
*/
public void clearPackageContext() {
- mContext = null;
+ if (enablePerDisplayPackageContextCacheInStatusbarNotif()) {
+ mContextForDisplayId.clear();
+ } else {
+ mContext = null;
+ }
}
/**
@@ -475,21 +491,42 @@ public class StatusBarNotification implements Parcelable {
*/
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
public Context getPackageContext(Context context) {
- if (mContext == null) {
- try {
- ApplicationInfo ai = context.getPackageManager()
- .getApplicationInfoAsUser(pkg, PackageManager.MATCH_UNINSTALLED_PACKAGES,
- getNormalizedUserId());
- mContext = context.createApplicationContext(ai,
- Context.CONTEXT_RESTRICTED);
- } catch (PackageManager.NameNotFoundException e) {
- mContext = null;
+ if (enablePerDisplayPackageContextCacheInStatusbarNotif()) {
+ if (context == null) return null;
+ return mContextForDisplayId.computeIfAbsent(context.getDisplayId(),
+ (displayId) -> createPackageContext(context));
+ } else {
+ if (mContext == null) {
+ try {
+ ApplicationInfo ai = context.getPackageManager()
+ .getApplicationInfoAsUser(pkg,
+ PackageManager.MATCH_UNINSTALLED_PACKAGES,
+ getNormalizedUserId());
+ mContext = context.createApplicationContext(ai,
+ Context.CONTEXT_RESTRICTED);
+ } catch (PackageManager.NameNotFoundException e) {
+ mContext = null;
+ }
+ }
+ if (mContext == null) {
+ mContext = context;
}
+ return mContext;
}
- if (mContext == null) {
- mContext = context;
+ }
+
+ private Context createPackageContext(Context context) {
+ try {
+ Trace.beginSection("StatusBarNotification#createPackageContext");
+ ApplicationInfo ai = context.getPackageManager()
+ .getApplicationInfoAsUser(pkg, PackageManager.MATCH_UNINSTALLED_PACKAGES,
+ getNormalizedUserId());
+ return context.createApplicationContext(ai, Context.CONTEXT_RESTRICTED);
+ } catch (PackageManager.NameNotFoundException e) {
+ return context;
+ } finally {
+ Trace.endSection();
}
- return mContext;
}
/**
diff --git a/core/java/android/view/InsetsAnimationControlImpl.java b/core/java/android/view/InsetsAnimationControlImpl.java
index 4fead2ad5246..6decd6d3a603 100644
--- a/core/java/android/view/InsetsAnimationControlImpl.java
+++ b/core/java/android/view/InsetsAnimationControlImpl.java
@@ -112,6 +112,7 @@ public class InsetsAnimationControlImpl implements InternalInsetsAnimationContro
private Insets mPendingInsets;
private float mPendingFraction;
private boolean mFinished;
+ private boolean mCancelling;
private boolean mCancelled;
private boolean mShownOnFinish;
private float mCurrentAlpha = 1.0f;
@@ -371,7 +372,7 @@ public class InsetsAnimationControlImpl implements InternalInsetsAnimationContro
mPendingInsets = mLayoutInsetsDuringAnimation == LAYOUT_INSETS_DURING_ANIMATION_SHOWN
? mShownInsets : mHiddenInsets;
mPendingAlpha = 1f;
- mPendingFraction = 1f;
+ mCancelling = true;
applyChangeInsets(null);
mCancelled = true;
mListener.onCancelled(mReadyDispatched ? this : null);
@@ -488,15 +489,15 @@ public class InsetsAnimationControlImpl implements InternalInsetsAnimationContro
return;
}
- final boolean visible = mPendingFraction == 0
- // The first frame of ANIMATION_TYPE_SHOW should be invisible since it is
- // animated from the hidden state.
- ? mAnimationType != ANIMATION_TYPE_SHOW
- : mPendingFraction < 1f || (mFinished
- ? mShownOnFinish
- // If the animation is cancelled, mFinished and mShownOnFinish are not set.
+ final boolean visible = mFinished
+ ? mShownOnFinish
+ : (mCancelling
+ // If the animation is being cancelled, mShownOnFinish is not valid.
// Here uses mLayoutInsetsDuringAnimation to decide if it should be visible.
- : mLayoutInsetsDuringAnimation == LAYOUT_INSETS_DURING_ANIMATION_SHOWN);
+ ? mLayoutInsetsDuringAnimation == LAYOUT_INSETS_DURING_ANIMATION_SHOWN
+ // The first frame of ANIMATION_TYPE_SHOW should be invisible since it is
+ // animated from the hidden state.
+ : (mAnimationType != ANIMATION_TYPE_SHOW || mPendingFraction != 0));
// TODO: Implement behavior when inset spans over multiple types
for (int i = controls.size() - 1; i >= 0; i--) {
diff --git a/core/java/android/view/SurfaceControl.java b/core/java/android/view/SurfaceControl.java
index e665c08c63e4..d7cf3e827695 100644
--- a/core/java/android/view/SurfaceControl.java
+++ b/core/java/android/view/SurfaceControl.java
@@ -4865,7 +4865,7 @@ public final class SurfaceControl implements Parcelable {
/**
* @hide
*/
- public Transaction setDesintationFrame(SurfaceControl sc, @NonNull Rect destinationFrame) {
+ public Transaction setDestinationFrame(SurfaceControl sc, @NonNull Rect destinationFrame) {
checkPreconditions(sc);
nativeSetDestinationFrame(mNativeObject, sc.mNativeObject,
destinationFrame.left, destinationFrame.top, destinationFrame.right,
@@ -4876,7 +4876,7 @@ public final class SurfaceControl implements Parcelable {
/**
* @hide
*/
- public Transaction setDesintationFrame(SurfaceControl sc, int width, int height) {
+ public Transaction setDestinationFrame(SurfaceControl sc, int width, int height) {
checkPreconditions(sc);
nativeSetDestinationFrame(mNativeObject, sc.mNativeObject, 0, 0, width, height);
return this;
diff --git a/core/java/android/view/SurfaceView.java b/core/java/android/view/SurfaceView.java
index b0051cefb21b..780e76122e8a 100644
--- a/core/java/android/view/SurfaceView.java
+++ b/core/java/android/view/SurfaceView.java
@@ -1125,7 +1125,7 @@ public class SurfaceView extends View implements ViewRootImpl.SurfaceChangedCall
}
}
- surfaceUpdateTransaction.setDesintationFrame(mBlastSurfaceControl, mSurfaceWidth,
+ surfaceUpdateTransaction.setDestinationFrame(mBlastSurfaceControl, mSurfaceWidth,
mSurfaceHeight);
if (isHardwareAccelerated()) {
diff --git a/core/java/android/window/flags/lse_desktop_experience.aconfig b/core/java/android/window/flags/lse_desktop_experience.aconfig
index be0b4fea459c..91d9a16faf4d 100644
--- a/core/java/android/window/flags/lse_desktop_experience.aconfig
+++ b/core/java/android/window/flags/lse_desktop_experience.aconfig
@@ -531,6 +531,16 @@ flag {
}
flag {
+ name: "enable_per_display_package_context_cache_in_statusbar_notif"
+ namespace: "lse_desktop_experience"
+ description: "Enables per-display package context caching in StatusBarNotification"
+ bug: "388886443"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
+
+flag {
name: "enable_desktop_wallpaper_activity_for_system_user"
namespace: "lse_desktop_experience"
description: "Enables starting DesktopWallpaperActivity on system user."
diff --git a/core/java/com/android/internal/widget/NotificationProgressBar.java b/core/java/com/android/internal/widget/NotificationProgressBar.java
index 904b73f41e70..1b770207f2cb 100644
--- a/core/java/com/android/internal/widget/NotificationProgressBar.java
+++ b/core/java/com/android/internal/widget/NotificationProgressBar.java
@@ -836,11 +836,16 @@ public final class NotificationProgressBar extends ProgressBar implements
} else if (part instanceof Point point) {
final float pointWidth = 2 * pointRadius;
float start = x - pointRadius;
- if (start < 0) start = 0;
- float end = start + pointWidth;
- if (end > totalWidth) {
+ float end = x + pointRadius;
+ // Only shift the points right at the start/end.
+ // For the points close to the start/end, the segment minimum width requirement
+ // would take care of shifting them to be within the bounds.
+ if (x == 0) {
+ start = 0;
+ end = pointWidth;
+ } else if (x == totalWidth) {
+ start = totalWidth - pointWidth;
end = totalWidth;
- if (totalWidth > pointWidth) start = totalWidth - pointWidth;
}
drawableParts.add(new DrawablePoint(start, end, point.mColor));
@@ -853,7 +858,7 @@ public final class NotificationProgressBar extends ProgressBar implements
private static float getSegStartOffset(Part prevPart, float pointRadius, float segPointGap,
float startX) {
if (!(prevPart instanceof Point)) return 0F;
- final float pointOffset = (startX < pointRadius) ? (pointRadius - startX) : 0;
+ final float pointOffset = (startX == 0) ? pointRadius : 0;
return pointOffset + pointRadius + segPointGap;
}
@@ -869,9 +874,7 @@ public final class NotificationProgressBar extends ProgressBar implements
return segSegGap;
}
- final float pointWidth = 2 * pointRadius;
- final float pointOffset = (endX + pointRadius > totalWidth && totalWidth > pointWidth)
- ? (endX + pointRadius - totalWidth) : 0;
+ final float pointOffset = (endX == totalWidth) ? pointRadius : 0;
return segPointGap + pointRadius + pointOffset;
}
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index 8372aecf0d27..8bf61bbcf55e 100644
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -7590,7 +7590,7 @@
<!-- Minimum required drawing width. The drawing width refers to the width after
the original segments have been adjusted for the neighboring Points and gaps. This is
enforced by stretching the segments that are too short. -->
- <attr name="minWidth" format="dimension" />
+ <attr name="minWidth" />
<!-- Height of the solid segments. -->
<attr name="height" />
<!-- Height of the faded segments. -->
diff --git a/core/tests/coretests/src/android/os/SystemHealthManagerUnitTest.java b/core/tests/coretests/src/android/os/SystemHealthManagerUnitTest.java
new file mode 100644
index 000000000000..e093e1af2eb5
--- /dev/null
+++ b/core/tests/coretests/src/android/os/SystemHealthManagerUnitTest.java
@@ -0,0 +1,153 @@
+/*
+ * Copyright (C) 2024 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.os;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.hardware.power.CpuHeadroomResult;
+import android.hardware.power.GpuHeadroomResult;
+import android.hardware.power.SupportInfo;
+import android.os.health.SystemHealthManager;
+
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+
+import com.android.internal.app.IBatteryStats;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+@RunWith(AndroidJUnit4.class)
+public class SystemHealthManagerUnitTest {
+ @Mock
+ private IBatteryStats mBatteryStats;
+ @Mock
+ private IPowerStatsService mPowerStats;
+ @Mock
+ private IHintManager mHintManager;
+ private SystemHealthManager mSystemHealthManager;
+
+ @Before
+ public void setUp() throws Exception {
+ MockitoAnnotations.initMocks(this);
+ IHintManager.HintManagerClientData clientData = new IHintManager.HintManagerClientData();
+ clientData.supportInfo = new SupportInfo();
+ clientData.maxCpuHeadroomThreads = 10;
+ clientData.supportInfo.headroom = new SupportInfo.HeadroomSupportInfo();
+ clientData.supportInfo.headroom.isCpuSupported = true;
+ clientData.supportInfo.headroom.isGpuSupported = true;
+ clientData.supportInfo.headroom.cpuMinCalculationWindowMillis = 45;
+ clientData.supportInfo.headroom.cpuMaxCalculationWindowMillis = 9999;
+ clientData.supportInfo.headroom.gpuMinCalculationWindowMillis = 46;
+ clientData.supportInfo.headroom.gpuMaxCalculationWindowMillis = 9998;
+ clientData.supportInfo.headroom.cpuMinIntervalMillis = 999;
+ clientData.supportInfo.headroom.gpuMinIntervalMillis = 998;
+ when(mHintManager.getClientData()).thenReturn(clientData);
+ mSystemHealthManager = new SystemHealthManager(mBatteryStats, mPowerStats, mHintManager);
+ }
+
+ @Test
+ public void testHeadroomParamsValueRange() {
+ assertEquals(999, mSystemHealthManager.getCpuHeadroomMinIntervalMillis());
+ assertEquals(998, mSystemHealthManager.getGpuHeadroomMinIntervalMillis());
+ assertEquals(45, (int) mSystemHealthManager.getCpuHeadroomCalculationWindowRange().first);
+ assertEquals(9999,
+ (int) mSystemHealthManager.getCpuHeadroomCalculationWindowRange().second);
+ assertEquals(46, (int) mSystemHealthManager.getGpuHeadroomCalculationWindowRange().first);
+ assertEquals(9998,
+ (int) mSystemHealthManager.getGpuHeadroomCalculationWindowRange().second);
+ assertEquals(10, (int) mSystemHealthManager.getMaxCpuHeadroomTidsSize());
+ }
+
+ @Test
+ public void testGetCpuHeadroom() throws RemoteException, InterruptedException {
+ final CpuHeadroomParams params1 = null;
+ final CpuHeadroomParamsInternal internalParams1 = new CpuHeadroomParamsInternal();
+
+ final CpuHeadroomParams params2 = new CpuHeadroomParams.Builder()
+ .setCalculationWindowMillis(100)
+ .build();
+ final CpuHeadroomParamsInternal internalParams2 = new CpuHeadroomParamsInternal();
+ internalParams2.calculationWindowMillis = 100;
+
+ final CpuHeadroomParams params3 = new CpuHeadroomParams.Builder()
+ .setCalculationType(CpuHeadroomParams.CPU_HEADROOM_CALCULATION_TYPE_AVERAGE)
+ .build();
+ final CpuHeadroomParamsInternal internalParams3 = new CpuHeadroomParamsInternal();
+ internalParams3.calculationType =
+ (byte) CpuHeadroomParams.CPU_HEADROOM_CALCULATION_TYPE_AVERAGE;
+
+ final CpuHeadroomParams params4 = new CpuHeadroomParams.Builder()
+ .setTids(1000, 1001)
+ .build();
+ final CpuHeadroomParamsInternal internalParams4 = new CpuHeadroomParamsInternal();
+ internalParams4.tids = new int[]{1000, 1001};
+
+ when(mHintManager.getCpuHeadroom(internalParams1)).thenReturn(
+ CpuHeadroomResult.globalHeadroom(99f));
+ when(mHintManager.getCpuHeadroom(internalParams2)).thenReturn(
+ CpuHeadroomResult.globalHeadroom(98f));
+ when(mHintManager.getCpuHeadroom(internalParams3)).thenReturn(
+ CpuHeadroomResult.globalHeadroom(97f));
+ when(mHintManager.getCpuHeadroom(internalParams4)).thenReturn(null);
+
+ assertEquals(99f, mSystemHealthManager.getCpuHeadroom(params1), 0.001f);
+ assertEquals(98f, mSystemHealthManager.getCpuHeadroom(params2), 0.001f);
+ assertEquals(97f, mSystemHealthManager.getCpuHeadroom(params3), 0.001f);
+ assertTrue(Float.isNaN(mSystemHealthManager.getCpuHeadroom(params4)));
+ verify(mHintManager, times(1)).getCpuHeadroom(internalParams1);
+ verify(mHintManager, times(1)).getCpuHeadroom(internalParams2);
+ verify(mHintManager, times(1)).getCpuHeadroom(internalParams3);
+ verify(mHintManager, times(1)).getCpuHeadroom(internalParams4);
+ }
+
+ @Test
+ public void testGetGpuHeadroom() throws RemoteException, InterruptedException {
+ final GpuHeadroomParams params1 = null;
+ final GpuHeadroomParamsInternal internalParams1 = new GpuHeadroomParamsInternal();
+ final GpuHeadroomParams params2 = new GpuHeadroomParams.Builder()
+ .setCalculationWindowMillis(100)
+ .build();
+ final GpuHeadroomParamsInternal internalParams2 = new GpuHeadroomParamsInternal();
+ internalParams2.calculationWindowMillis = 100;
+ final GpuHeadroomParams params3 = new GpuHeadroomParams.Builder()
+ .setCalculationType(GpuHeadroomParams.GPU_HEADROOM_CALCULATION_TYPE_AVERAGE)
+ .build();
+ final GpuHeadroomParamsInternal internalParams3 = new GpuHeadroomParamsInternal();
+ internalParams3.calculationType =
+ (byte) GpuHeadroomParams.GPU_HEADROOM_CALCULATION_TYPE_AVERAGE;
+
+ when(mHintManager.getGpuHeadroom(internalParams1)).thenReturn(
+ GpuHeadroomResult.globalHeadroom(99f));
+ when(mHintManager.getGpuHeadroom(internalParams2)).thenReturn(
+ GpuHeadroomResult.globalHeadroom(98f));
+ when(mHintManager.getGpuHeadroom(internalParams3)).thenReturn(null);
+
+ assertEquals(99f, mSystemHealthManager.getGpuHeadroom(params1), 0.001f);
+ assertEquals(98f, mSystemHealthManager.getGpuHeadroom(params2), 0.001f);
+ assertTrue(Float.isNaN(mSystemHealthManager.getGpuHeadroom(params3)));
+ verify(mHintManager, times(1)).getGpuHeadroom(internalParams1);
+ verify(mHintManager, times(1)).getGpuHeadroom(internalParams2);
+ verify(mHintManager, times(1)).getGpuHeadroom(internalParams3);
+ }
+}
diff --git a/core/tests/coretests/src/android/service/notification/StatusBarNotificationTest.java b/core/tests/coretests/src/android/service/notification/StatusBarNotificationTest.java
index 504240812559..7a4cc7f98a1a 100644
--- a/core/tests/coretests/src/android/service/notification/StatusBarNotificationTest.java
+++ b/core/tests/coretests/src/android/service/notification/StatusBarNotificationTest.java
@@ -22,6 +22,7 @@ import static junit.framework.Assert.assertNotSame;
import static junit.framework.Assert.assertNull;
import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertSame;
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.mock;
@@ -35,14 +36,19 @@ import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.metrics.LogMaker;
import android.os.UserHandle;
+import android.platform.test.annotations.EnableFlags;
+import android.platform.test.flag.junit.SetFlagsRule;
+import android.view.Display;
import androidx.test.InstrumentationRegistry;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
+import com.android.window.flags.Flags;
import org.junit.Before;
+import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
@@ -52,11 +58,16 @@ import org.mockito.MockitoAnnotations;
@SmallTest
public class StatusBarNotificationTest {
+ @Rule
+ public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
+
private final Context mMockContext = mock(Context.class);
@Mock
private Context mRealContext;
@Mock
private PackageManager mPm;
+ @Mock
+ private Context mSecondaryDisplayContext;
private static final String PKG = "com.example.o";
private static final int UID = 9583;
@@ -80,6 +91,10 @@ public class StatusBarNotificationTest {
InstrumentationRegistry.getContext().getResources());
when(mMockContext.getPackageManager()).thenReturn(mPm);
when(mMockContext.getApplicationInfo()).thenReturn(new ApplicationInfo());
+ when(mMockContext.getDisplayId()).thenReturn(Display.DEFAULT_DISPLAY);
+ when(mSecondaryDisplayContext.getPackageManager()).thenReturn(mPm);
+ when(mSecondaryDisplayContext.getApplicationInfo()).thenReturn(new ApplicationInfo());
+ when(mSecondaryDisplayContext.getDisplayId()).thenReturn(2);
when(mPm.getApplicationLabel(any())).thenReturn("");
mRealContext = InstrumentationRegistry.getContext();
@@ -221,6 +236,24 @@ public class StatusBarNotificationTest {
}
@Test
+ @EnableFlags(Flags.FLAG_ENABLE_PER_DISPLAY_PACKAGE_CONTEXT_CACHE_IN_STATUSBAR_NOTIF)
+ public void testGetPackageContext_multipleDisplaysCase() {
+ String pkg = "com.android.systemui";
+ int uid = 1000;
+ Notification notification = getNotificationBuilder(GROUP_ID_1, CHANNEL_ID).build();
+ StatusBarNotification sbn = new StatusBarNotification(
+ pkg, pkg, ID, TAG, uid, uid, notification, UserHandle.ALL, null, UID);
+ Context defaultContext = sbn.getPackageContext(mRealContext);
+ Context secondaryContext = sbn.getPackageContext(mSecondaryDisplayContext);
+ assertNotSame(mRealContext, defaultContext);
+ assertNotSame(defaultContext, secondaryContext);
+
+ // Let's make sure it caches it:
+ assertSame(defaultContext, sbn.getPackageContext(mRealContext));
+ assertSame(secondaryContext, sbn.getPackageContext(mSecondaryDisplayContext));
+ }
+
+ @Test
public void testGetUidFromKey() {
StatusBarNotification sbn = getNotification("pkg", null, "channel");
diff --git a/core/tests/coretests/src/com/android/internal/widget/NotificationProgressBarTest.java b/core/tests/coretests/src/com/android/internal/widget/NotificationProgressBarTest.java
index 5df2c1279eb8..9818e19cea02 100644
--- a/core/tests/coretests/src/com/android/internal/widget/NotificationProgressBarTest.java
+++ b/core/tests/coretests/src/com/android/internal/widget/NotificationProgressBarTest.java
@@ -336,10 +336,14 @@ public class NotificationProgressBarTest {
progress, progressMax);
List<Part> expectedParts = new ArrayList<>(
- List.of(new Segment(0.15f, Color.BLUE), new Point(Color.RED),
- new Segment(0.10f, Color.BLUE), new Point(Color.BLUE),
- new Segment(0.35f, Color.BLUE), new Point(Color.BLUE),
- new Segment(0.15f, Color.BLUE), new Point(Color.YELLOW),
+ List.of(new Segment(0.15f, Color.BLUE),
+ new Point(Color.RED),
+ new Segment(0.10f, Color.BLUE),
+ new Point(Color.BLUE),
+ new Segment(0.35f, Color.BLUE),
+ new Point(Color.BLUE),
+ new Segment(0.15f, Color.BLUE),
+ new Point(Color.YELLOW),
new Segment(0.25f, Color.BLUE)));
assertThat(parts).isEqualTo(expectedParts);
@@ -408,11 +412,16 @@ public class NotificationProgressBarTest {
progress, progressMax);
List<Part> expectedParts = new ArrayList<>(
- List.of(new Segment(0.15f, Color.RED), new Point(Color.RED),
- new Segment(0.10f, Color.RED), new Point(Color.BLUE),
- new Segment(0.25f, Color.RED), new Segment(0.10f, Color.GREEN),
- new Point(Color.BLUE), new Segment(0.15f, Color.GREEN),
- new Point(Color.YELLOW), new Segment(0.25f, Color.GREEN)));
+ List.of(new Segment(0.15f, Color.RED),
+ new Point(Color.RED),
+ new Segment(0.10f, Color.RED),
+ new Point(Color.BLUE),
+ new Segment(0.25f, Color.RED),
+ new Segment(0.10f, Color.GREEN),
+ new Point(Color.BLUE),
+ new Segment(0.15f, Color.GREEN),
+ new Point(Color.YELLOW),
+ new Segment(0.25f, Color.GREEN)));
assertThat(parts).isEqualTo(expectedParts);
@@ -464,6 +473,158 @@ public class NotificationProgressBarTest {
}
@Test
+ public void processAndConvertToParts_multipleSegmentsWithPointsAtStartAndEnd() {
+ List<ProgressStyle.Segment> segments = new ArrayList<>();
+ segments.add(new ProgressStyle.Segment(50).setColor(Color.RED));
+ segments.add(new ProgressStyle.Segment(50).setColor(Color.GREEN));
+ List<ProgressStyle.Point> points = new ArrayList<>();
+ points.add(new ProgressStyle.Point(0).setColor(Color.RED));
+ points.add(new ProgressStyle.Point(25).setColor(Color.BLUE));
+ points.add(new ProgressStyle.Point(60).setColor(Color.BLUE));
+ points.add(new ProgressStyle.Point(100).setColor(Color.YELLOW));
+ int progress = 60;
+ int progressMax = 100;
+
+ List<Part> parts = NotificationProgressBar.processAndConvertToViewParts(segments, points,
+ progress, progressMax);
+
+ List<Part> expectedParts = new ArrayList<>(
+ List.of(new Point(Color.RED),
+ new Segment(0.25f, Color.RED),
+ new Point(Color.BLUE),
+ new Segment(0.25f, Color.RED),
+ new Segment(0.10f, Color.GREEN),
+ new Point(Color.BLUE),
+ new Segment(0.4f, Color.GREEN),
+ new Point(Color.YELLOW)));
+
+ assertThat(parts).isEqualTo(expectedParts);
+
+ float drawableWidth = 300;
+ float segSegGap = 4;
+ float segPointGap = 4;
+ float pointRadius = 6;
+ boolean hasTrackerIcon = true;
+ List<DrawablePart> drawableParts = NotificationProgressBar.processAndConvertToDrawableParts(
+ parts, drawableWidth, segSegGap, segPointGap, pointRadius, hasTrackerIcon);
+
+ List<DrawablePart> expectedDrawableParts = new ArrayList<>(
+ List.of(new DrawablePoint(0, 12, Color.RED),
+ new DrawableSegment(16, 65, Color.RED),
+ new DrawablePoint(69, 81, Color.BLUE),
+ new DrawableSegment(85, 146, Color.RED),
+ new DrawableSegment(150, 170, Color.GREEN),
+ new DrawablePoint(174, 186, Color.BLUE),
+ new DrawableSegment(190, 284, Color.GREEN),
+ new DrawablePoint(288, 300, Color.YELLOW)));
+
+ assertThat(drawableParts).isEqualTo(expectedDrawableParts);
+
+ float segmentMinWidth = 16;
+ boolean isStyledByProgress = true;
+
+ Pair<List<DrawablePart>, Float> p = NotificationProgressBar.maybeStretchAndRescaleSegments(
+ parts, drawableParts, segmentMinWidth, pointRadius, (float) progress / progressMax,
+ 300, isStyledByProgress, hasTrackerIcon ? 0 : segSegGap);
+
+ // Colors with 40% opacity
+ int fadedGreen = 0x6600FF00;
+ int fadedYellow = 0x66FFFF00;
+ expectedDrawableParts = new ArrayList<>(
+ List.of(new DrawablePoint(0, 12, Color.RED),
+ new DrawableSegment(16, 65, Color.RED),
+ new DrawablePoint(69, 81, Color.BLUE),
+ new DrawableSegment(85, 146, Color.RED),
+ new DrawableSegment(150, 170, Color.GREEN),
+ new DrawablePoint(174, 186, Color.BLUE),
+ new DrawableSegment(190, 284, fadedGreen, true),
+ new DrawablePoint(288, 300, fadedYellow)));
+
+ assertThat(p.second).isEqualTo(180);
+ assertThat(p.first).isEqualTo(expectedDrawableParts);
+ }
+
+ // The points are so close to start/end that they would go out of bounds without the minimum
+ // segment width requirement.
+ @Test
+ public void processAndConvertToParts_multipleSegmentsWithPointsNearStartAndEnd() {
+ List<ProgressStyle.Segment> segments = new ArrayList<>();
+ segments.add(new ProgressStyle.Segment(50).setColor(Color.RED));
+ segments.add(new ProgressStyle.Segment(50).setColor(Color.GREEN));
+ List<ProgressStyle.Point> points = new ArrayList<>();
+ points.add(new ProgressStyle.Point(1).setColor(Color.RED));
+ points.add(new ProgressStyle.Point(25).setColor(Color.BLUE));
+ points.add(new ProgressStyle.Point(60).setColor(Color.BLUE));
+ points.add(new ProgressStyle.Point(99).setColor(Color.YELLOW));
+ int progress = 60;
+ int progressMax = 100;
+
+ List<Part> parts = NotificationProgressBar.processAndConvertToViewParts(segments, points,
+ progress, progressMax);
+
+ List<Part> expectedParts = new ArrayList<>(
+ List.of(new Segment(0.01f, Color.RED),
+ new Point(Color.RED),
+ new Segment(0.24f, Color.RED),
+ new Point(Color.BLUE),
+ new Segment(0.25f, Color.RED),
+ new Segment(0.10f, Color.GREEN),
+ new Point(Color.BLUE),
+ new Segment(0.39f, Color.GREEN),
+ new Point(Color.YELLOW),
+ new Segment(0.01f, Color.GREEN)));
+
+ assertThat(parts).isEqualTo(expectedParts);
+
+ float drawableWidth = 300;
+ float segSegGap = 4;
+ float segPointGap = 4;
+ float pointRadius = 6;
+ boolean hasTrackerIcon = true;
+ List<DrawablePart> drawableParts = NotificationProgressBar.processAndConvertToDrawableParts(
+ parts, drawableWidth, segSegGap, segPointGap, pointRadius, hasTrackerIcon);
+
+ List<DrawablePart> expectedDrawableParts = new ArrayList<>(
+ List.of(new DrawableSegment(0, -7, Color.RED),
+ new DrawablePoint(-3, 9, Color.RED),
+ new DrawableSegment(13, 65, Color.RED),
+ new DrawablePoint(69, 81, Color.BLUE),
+ new DrawableSegment(85, 146, Color.RED),
+ new DrawableSegment(150, 170, Color.GREEN),
+ new DrawablePoint(174, 186, Color.BLUE),
+ new DrawableSegment(190, 287, Color.GREEN),
+ new DrawablePoint(291, 303, Color.YELLOW),
+ new DrawableSegment(307, 300, Color.GREEN)));
+
+ assertThat(drawableParts).isEqualTo(expectedDrawableParts);
+
+ float segmentMinWidth = 16;
+ boolean isStyledByProgress = true;
+
+ Pair<List<DrawablePart>, Float> p = NotificationProgressBar.maybeStretchAndRescaleSegments(
+ parts, drawableParts, segmentMinWidth, pointRadius, (float) progress / progressMax,
+ 300, isStyledByProgress, hasTrackerIcon ? 0 : segSegGap);
+
+ // Colors with 40% opacity
+ int fadedGreen = 0x6600FF00;
+ int fadedYellow = 0x66FFFF00;
+ expectedDrawableParts = new ArrayList<>(
+ List.of(new DrawableSegment(0, 16, Color.RED),
+ new DrawablePoint(20, 32, Color.RED),
+ new DrawableSegment(36, 78.02409F, Color.RED),
+ new DrawablePoint(82.02409F, 94.02409F, Color.BLUE),
+ new DrawableSegment(98.02409F, 146.55421F, Color.RED),
+ new DrawableSegment(150.55421F, 169.44579F, Color.GREEN),
+ new DrawablePoint(173.44579F, 185.44579F, Color.BLUE),
+ new DrawableSegment(189.44579F, 264, fadedGreen, true),
+ new DrawablePoint(268, 280, fadedYellow),
+ new DrawableSegment(284, 300, fadedGreen, true)));
+
+ assertThat(p.second).isEqualTo(179.44579F);
+ assertThat(p.first).isEqualTo(expectedDrawableParts);
+ }
+
+ @Test
public void processAndConvertToParts_multipleSegmentsWithPoints_notStyledByProgress() {
List<ProgressStyle.Segment> segments = new ArrayList<>();
segments.add(new ProgressStyle.Segment(50).setColor(Color.RED));
diff --git a/data/etc/preinstalled-packages-platform.xml b/data/etc/preinstalled-packages-platform.xml
index 782327713fdc..3403bbfa2384 100644
--- a/data/etc/preinstalled-packages-platform.xml
+++ b/data/etc/preinstalled-packages-platform.xml
@@ -134,4 +134,9 @@ to pre-existing users, but cannot uninstall pre-existing system packages from pr
<install-in-user-type package="com.android.avatarpicker">
<install-in user-type="FULL" />
</install-in-user-type>
+
+ <!-- Users Widget (Users widget)-->
+ <install-in-user-type package="com.android.multiuser">
+ <install-in user-type="FULL" />
+ </install-in-user-type>
</config>
diff --git a/data/etc/privapp-permissions-platform.xml b/data/etc/privapp-permissions-platform.xml
index f136e065a405..a30570a4cce5 100644
--- a/data/etc/privapp-permissions-platform.xml
+++ b/data/etc/privapp-permissions-platform.xml
@@ -264,6 +264,7 @@ applications that come with the platform
<!-- Needed for test only -->
<permission name="android.permission.BATTERY_PREDICTION"/>
<permission name="android.permission.BATTERY_STATS"/>
+ <permission name="android.permission.ACCESS_FINE_POWER_MONITORS" />
<!-- BLUETOOTH_PRIVILEGED is needed for test only -->
<permission name="android.permission.BLUETOOTH_PRIVILEGED"/>
<permission name="android.permission.BIND_APPWIDGET"/>
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/ComponentUtils.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/common/ComponentUtils.kt
new file mode 100644
index 000000000000..67592e60e954
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/ComponentUtils.kt
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2025 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.common
+
+import android.app.PendingIntent
+import android.content.ComponentName
+import android.content.Intent
+import com.android.wm.shell.ShellTaskOrganizer
+
+/** Utils to obtain [ComponentName]s. */
+object ComponentUtils {
+ /** Retrieves the package name from an [Intent]. */
+ @JvmStatic
+ fun getPackageName(intent: Intent?): String? = intent?.component?.packageName
+
+ /** Retrieves the package name from a [PendingIntent]. */
+ @JvmStatic
+ fun getPackageName(pendingIntent: PendingIntent?): String? =
+ getPackageName(pendingIntent?.intent)
+
+ /** Retrieves the package name from a [taskId]. */
+ @JvmStatic
+ fun getPackageName(taskId: Int, taskOrganizer: ShellTaskOrganizer): String? {
+ val taskInfo = taskOrganizer.getRunningTaskInfo(taskId)
+ return getPackageName(taskInfo?.baseIntent)
+ }
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayImeController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayImeController.java
index eb1e72790a25..c9890a5b4963 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayImeController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayImeController.java
@@ -447,8 +447,10 @@ public class DisplayImeController implements DisplayController.OnDisplaysChanged
}
}
- private int imeTop(float surfaceOffset) {
- return mImeFrame.top + (int) surfaceOffset;
+ private int imeTop(float surfaceOffset, float surfacePositionY) {
+ // surfaceOffset is already offset by the surface's top inset, so we need to subtract
+ // the top inset so that the return value is in screen coordinates.
+ return mImeFrame.top + (int) (surfaceOffset - surfacePositionY);
}
private boolean calcIsFloating(InsetsSource imeSource) {
@@ -581,7 +583,7 @@ public class DisplayImeController implements DisplayController.OnDisplaysChanged
final float alpha = (mAnimateAlpha || isFloating)
? (value - hiddenY) / (shownY - hiddenY) : 1f;
t.setAlpha(animatingLeash, alpha);
- dispatchPositionChanged(mDisplayId, imeTop(value), t);
+ dispatchPositionChanged(mDisplayId, imeTop(value, defaultY), t);
t.apply();
mTransactionPool.release(t);
});
@@ -600,11 +602,12 @@ public class DisplayImeController implements DisplayController.OnDisplaysChanged
t.setPosition(animatingLeash, x, value);
if (DEBUG) {
Slog.d(TAG, "onAnimationStart d:" + mDisplayId + " top:"
- + imeTop(hiddenY) + "->" + imeTop(shownY)
+ + imeTop(hiddenY, defaultY) + "->" + imeTop(shownY, defaultY)
+ " showing:" + (mAnimationDirection == DIRECTION_SHOW));
}
- int flags = dispatchStartPositioning(mDisplayId, imeTop(hiddenY),
- imeTop(shownY), mAnimationDirection == DIRECTION_SHOW, isFloating, t);
+ int flags = dispatchStartPositioning(mDisplayId, imeTop(hiddenY, defaultY),
+ imeTop(shownY, defaultY), mAnimationDirection == DIRECTION_SHOW,
+ isFloating, t);
mAnimateAlpha = (flags & ImePositionProcessor.IME_ANIMATION_NO_ALPHA) == 0;
final float alpha = (mAnimateAlpha || isFloating)
? (value - hiddenY) / (shownY - hiddenY)
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitScreenUtils.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitScreenUtils.java
index 9113c0a53178..83e5e31bd125 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitScreenUtils.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitScreenUtils.java
@@ -27,14 +27,10 @@ import static com.android.wm.shell.shared.split.SplitScreenConstants.SPLIT_POSIT
import static com.android.wm.shell.shared.split.SplitScreenConstants.SPLIT_POSITION_UNDEFINED;
import android.app.ActivityManager;
-import android.app.PendingIntent;
-import android.content.Intent;
import android.content.res.Configuration;
import android.content.res.Resources;
import android.graphics.Rect;
-import androidx.annotation.Nullable;
-
import com.android.internal.util.ArrayUtils;
import com.android.wm.shell.Flags;
import com.android.wm.shell.ShellTaskOrganizer;
@@ -65,31 +61,6 @@ public class SplitScreenUtils {
&& ArrayUtils.contains(CONTROLLED_WINDOWING_MODES, taskInfo.getWindowingMode());
}
- /** Retrieve package name from an intent */
- @Nullable
- public static String getPackageName(Intent intent) {
- if (intent == null || intent.getComponent() == null) {
- return null;
- }
- return intent.getComponent().getPackageName();
- }
-
- /** Retrieve package name from a PendingIntent */
- @Nullable
- public static String getPackageName(PendingIntent pendingIntent) {
- if (pendingIntent == null || pendingIntent.getIntent() == null) {
- return null;
- }
- return getPackageName(pendingIntent.getIntent());
- }
-
- /** Retrieve package name from a taskId */
- @Nullable
- public static String getPackageName(int taskId, ShellTaskOrganizer taskOrganizer) {
- final ActivityManager.RunningTaskInfo taskInfo = taskOrganizer.getRunningTaskInfo(taskId);
- return taskInfo != null ? getPackageName(taskInfo.baseIntent) : null;
- }
-
/** Retrieve user id from a taskId */
public static int getUserId(int taskId, ShellTaskOrganizer taskOrganizer) {
final ActivityManager.RunningTaskInfo taskInfo = taskOrganizer.getRunningTaskInfo(taskId);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java
index e8add56619c4..ac510f89b905 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java
@@ -946,8 +946,7 @@ public abstract class WMShellModule {
FocusTransitionObserver focusTransitionObserver,
DesktopModeEventLogger desktopModeEventLogger,
DesktopModeUiEventLogger desktopModeUiEventLogger,
- WindowDecorTaskResourceLoader taskResourceLoader,
- RecentsTransitionHandler recentsTransitionHandler
+ WindowDecorTaskResourceLoader taskResourceLoader
) {
if (!DesktopModeStatus.canEnterDesktopModeOrShowAppHandle(context)) {
return Optional.empty();
@@ -963,7 +962,7 @@ public abstract class WMShellModule {
desktopTasksLimiter, appHandleEducationController, appToWebEducationController,
windowDecorCaptionHandleRepository, activityOrientationChangeHandler,
focusTransitionObserver, desktopModeEventLogger, desktopModeUiEventLogger,
- taskResourceLoader, recentsTransitionHandler));
+ taskResourceLoader));
}
@WMSingleton
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopRepository.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopRepository.kt
index 1a58363dab81..c975533abf24 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopRepository.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopRepository.kt
@@ -470,7 +470,7 @@ class DesktopRepository(
* Removes [taskId] from the respective display. If [INVALID_DISPLAY], the original display id
* will be looked up from the task id.
*/
- fun removeFreeformTask(displayId: Int, taskId: Int) {
+ fun removeTask(displayId: Int, taskId: Int) {
logD("Removes freeform task: taskId=%d", taskId)
if (displayId == INVALID_DISPLAY) {
// Removes the original display id of the task.
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTaskChangeListener.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTaskChangeListener.kt
index 947a8dddb239..c958a0975f11 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTaskChangeListener.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTaskChangeListener.kt
@@ -29,7 +29,7 @@ class DesktopTaskChangeListener(private val desktopUserRepositories: DesktopUser
val desktopRepository: DesktopRepository =
desktopUserRepositories.getProfile(taskInfo.userId)
if (!isFreeformTask(taskInfo) && desktopRepository.isActiveTask(taskInfo.taskId)) {
- desktopRepository.removeFreeformTask(taskInfo.displayId, taskInfo.taskId)
+ desktopRepository.removeTask(taskInfo.displayId, taskInfo.taskId)
return
}
if (isFreeformTask(taskInfo)) {
@@ -50,7 +50,7 @@ class DesktopTaskChangeListener(private val desktopUserRepositories: DesktopUser
desktopRepository.updateTask(taskInfo.displayId, taskInfo.taskId, taskInfo.isVisible)
} else {
// Case 2: Freeform task is changed outside Desktop Mode.
- desktopRepository.removeFreeformTask(taskInfo.displayId, taskInfo.taskId)
+ desktopRepository.removeTask(taskInfo.displayId, taskInfo.taskId)
}
}
@@ -60,7 +60,7 @@ class DesktopTaskChangeListener(private val desktopUserRepositories: DesktopUser
// Any changes to [DesktopRepository] from this method should be made carefully to minimize risk
// of race conditions and possible duplications with [onTaskChanging].
override fun onNonTransitionTaskChanging(taskInfo: RunningTaskInfo) {
- // TODO: b/367268953 - Propapagate usages from FreeformTaskListener to this method.
+ // TODO: b/367268953 - Propagate usages from FreeformTaskListener to this method.
}
override fun onTaskMovingToFront(taskInfo: RunningTaskInfo) {
@@ -68,7 +68,7 @@ class DesktopTaskChangeListener(private val desktopUserRepositories: DesktopUser
desktopUserRepositories.getProfile(taskInfo.userId)
if (!desktopRepository.isActiveTask(taskInfo.taskId)) return
if (!isFreeformTask(taskInfo)) {
- desktopRepository.removeFreeformTask(taskInfo.displayId, taskInfo.taskId)
+ desktopRepository.removeTask(taskInfo.displayId, taskInfo.taskId)
}
// TODO: b/367268953 - Connect this with DesktopRepository for handling
// task moving to front for tasks in windowing mode.
@@ -90,7 +90,7 @@ class DesktopTaskChangeListener(private val desktopUserRepositories: DesktopUser
// A task that's vanishing should be removed:
// - If it's closed by the X button which means it's marked as a closing task.
desktopRepository.removeClosingTask(taskInfo.taskId)
- desktopRepository.removeFreeformTask(taskInfo.displayId, taskInfo.taskId)
+ desktopRepository.removeTask(taskInfo.displayId, taskInfo.taskId)
} else {
desktopRepository.updateTask(taskInfo.displayId, taskInfo.taskId, isVisible = false)
desktopRepository.minimizeTask(taskInfo.displayId, taskInfo.taskId)
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksTransitionObserver.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksTransitionObserver.kt
index d61ffdaf5cf8..b9a65fee7d4d 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksTransitionObserver.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksTransitionObserver.kt
@@ -141,7 +141,7 @@ class DesktopTasksTransitionObserver(
desktopRepository.isActiveTask(taskInfo.taskId) &&
taskInfo.windowingMode != WINDOWING_MODE_FREEFORM
) {
- desktopRepository.removeFreeformTask(taskInfo.displayId, taskInfo.taskId)
+ desktopRepository.removeTask(taskInfo.displayId, taskInfo.taskId)
}
}
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopUserRepositories.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopUserRepositories.kt
index 7f3133e141ef..13576aa42737 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopUserRepositories.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopUserRepositories.kt
@@ -34,6 +34,7 @@ import com.android.wm.shell.sysui.ShellInit
import com.android.wm.shell.sysui.UserChangeListener
import java.io.PrintWriter
import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.launch
/** Manages per-user DesktopRepository instances. */
class DesktopUserRepositories(
@@ -43,7 +44,7 @@ class DesktopUserRepositories(
private val persistentRepository: DesktopPersistentRepository,
private val repositoryInitializer: DesktopRepositoryInitializer,
@ShellMainThread private val mainCoroutineScope: CoroutineScope,
- userManager: UserManager,
+ private val userManager: UserManager,
) : UserChangeListener {
private var userId: Int
private var userIdToProfileIdsMap: MutableMap<Int, List<Int>> = mutableMapOf()
@@ -100,6 +101,9 @@ class DesktopUserRepositories(
override fun onUserChanged(newUserId: Int, userContext: Context) {
logD("onUserChanged previousUserId=%d, newUserId=%d", userId, newUserId)
userId = newUserId
+ if (Flags.enableDesktopWindowingHsum()) {
+ sanitizeUsers()
+ }
}
override fun onUserProfilesChanged(profiles: MutableList<UserInfo>) {
@@ -110,10 +114,34 @@ class DesktopUserRepositories(
}
}
+ private fun sanitizeUsers() {
+ val aliveUserIds = userManager.getAliveUsers().map { it.id }
+ val usersToDelete = userIdToProfileIdsMap.keys.filterNot { it in aliveUserIds }
+
+ usersToDelete.forEach { uid ->
+ userIdToProfileIdsMap.remove(uid)
+ desktopRepoByUserId.remove(uid)
+ }
+ mainCoroutineScope.launch {
+ try {
+ persistentRepository.removeUsers(usersToDelete)
+ } catch (exception: Exception) {
+ logE(
+ "An exception occurred while updating the persistent repository \n%s",
+ exception.stackTrace,
+ )
+ }
+ }
+ }
+
private fun logD(msg: String, vararg arguments: Any?) {
ProtoLog.d(WM_SHELL_DESKTOP_MODE, "%s: $msg", TAG, *arguments)
}
+ private fun logE(msg: String, vararg arguments: Any?) {
+ ProtoLog.e(WM_SHELL_DESKTOP_MODE, "%s: $msg", TAG, *arguments)
+ }
+
companion object {
private const val TAG = "DesktopUserRepositories"
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/persistence/DesktopPersistentRepository.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/persistence/DesktopPersistentRepository.kt
index a6998e1179fa..9e41270c21f8 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/persistence/DesktopPersistentRepository.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/persistence/DesktopPersistentRepository.kt
@@ -141,6 +141,22 @@ class DesktopPersistentRepository(private val dataStore: DataStore<DesktopPersis
}
}
+ suspend fun removeUsers(uids: List<Int>) {
+ try {
+ dataStore.updateData { persistentRepositories: DesktopPersistentRepositories ->
+ val persistentRepositoriesBuilder = persistentRepositories.toBuilder()
+ uids.forEach { uid -> persistentRepositoriesBuilder.removeDesktopRepoByUser(uid) }
+ persistentRepositoriesBuilder.build()
+ }
+ } catch (exception: Exception) {
+ Log.e(
+ TAG,
+ "Error in removing user related data, data is stored in a file named $DESKTOP_REPOSITORIES_DATASTORE_FILE",
+ exception,
+ )
+ }
+ }
+
private fun getDesktop(currentRepository: DesktopRepositoryState, desktopId: Int): Desktop =
// If there are no desktops set up, create one on the default display
currentRepository.getDesktopOrDefault(
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/freeform/FreeformTaskListener.java b/libs/WindowManager/Shell/src/com/android/wm/shell/freeform/FreeformTaskListener.java
index 4b59efbcecf2..0d5aa0105659 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/freeform/FreeformTaskListener.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/freeform/FreeformTaskListener.java
@@ -127,7 +127,7 @@ public class FreeformTaskListener implements ShellTaskOrganizer.TaskListener,
// triggered on a task and the task is closing. It will be marked as minimized in
// [DesktopTasksTransitionObserver] before it gets here.
repository.removeClosingTask(taskInfo.taskId);
- repository.removeFreeformTask(taskInfo.displayId, taskInfo.taskId);
+ repository.removeTask(taskInfo.displayId, taskInfo.taskId);
}
}
mWindowDecorationViewModel.onTaskVanished(taskInfo);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java
index bd676ce69cfe..bba778d9f438 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java
@@ -68,12 +68,12 @@ import androidx.annotation.Nullable;
import com.android.internal.protolog.ProtoLog;
import com.android.wm.shell.R;
import com.android.wm.shell.ShellTaskOrganizer;
+import com.android.wm.shell.common.ComponentUtils;
import com.android.wm.shell.common.pip.PipBoundsAlgorithm;
import com.android.wm.shell.common.pip.PipBoundsState;
import com.android.wm.shell.common.pip.PipDisplayLayoutState;
import com.android.wm.shell.common.pip.PipMenuController;
import com.android.wm.shell.common.pip.PipUtils;
-import com.android.wm.shell.common.split.SplitScreenUtils;
import com.android.wm.shell.protolog.ShellProtoLogGroup;
import com.android.wm.shell.shared.TransitionUtil;
import com.android.wm.shell.shared.pip.PipContentOverlay;
@@ -1359,7 +1359,7 @@ public class PipTransition extends PipTransitionController {
public boolean isPackageActiveInPip(@Nullable String packageName) {
final TaskInfo inPipTask = mPipOrganizer.getTaskInfo();
return packageName != null && inPipTask != null && mPipOrganizer.isInPip()
- && packageName.equals(SplitScreenUtils.getPackageName(inPipTask.baseIntent));
+ && packageName.equals(ComponentUtils.getPackageName(inPipTask.baseIntent));
}
private void updatePipForUnhandledTransition(@NonNull TransitionInfo.Change pipChange,
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTransition.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTransition.java
index 38015ca6d45f..0a42c71ce095 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTransition.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTransition.java
@@ -57,12 +57,12 @@ import androidx.annotation.Nullable;
import com.android.internal.util.Preconditions;
import com.android.window.flags.Flags;
import com.android.wm.shell.ShellTaskOrganizer;
+import com.android.wm.shell.common.ComponentUtils;
import com.android.wm.shell.common.pip.PipBoundsAlgorithm;
import com.android.wm.shell.common.pip.PipBoundsState;
import com.android.wm.shell.common.pip.PipDisplayLayoutState;
import com.android.wm.shell.common.pip.PipMenuController;
import com.android.wm.shell.common.pip.PipUtils;
-import com.android.wm.shell.common.split.SplitScreenUtils;
import com.android.wm.shell.desktopmode.DesktopRepository;
import com.android.wm.shell.desktopmode.DesktopUserRepositories;
import com.android.wm.shell.desktopmode.desktopwallpaperactivity.DesktopWallpaperActivityTokenProvider;
@@ -1008,6 +1008,6 @@ public class PipTransition extends PipTransitionController implements
public boolean isPackageActiveInPip(@Nullable String packageName) {
final TaskInfo inPipTask = mPipTransitionState.getPipTaskInfo();
return packageName != null && inPipTask != null && mPipTransitionState.isInPip()
- && packageName.equals(SplitScreenUtils.getPackageName(inPipTask.baseIntent));
+ && packageName.equals(ComponentUtils.getPackageName(inPipTask.baseIntent));
}
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/recents/IRecentsAnimationRunner.aidl b/libs/WindowManager/Shell/src/com/android/wm/shell/recents/IRecentsAnimationRunner.aidl
index 8cdb8c4512a9..32c79a2d02de 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/recents/IRecentsAnimationRunner.aidl
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/recents/IRecentsAnimationRunner.aidl
@@ -17,10 +17,9 @@
package com.android.wm.shell.recents;
import android.graphics.Rect;
-import android.os.Bundle;
import android.view.RemoteAnimationTarget;
import android.window.TaskSnapshot;
-import android.window.TransitionInfo;
+import android.os.Bundle;
import com.android.wm.shell.recents.IRecentsAnimationController;
@@ -58,8 +57,7 @@ oneway interface IRecentsAnimationRunner {
*/
void onAnimationStart(in IRecentsAnimationController controller,
in RemoteAnimationTarget[] apps, in RemoteAnimationTarget[] wallpapers,
- in Rect homeContentInsets, in Rect minimizedHomeBounds, in Bundle extras,
- in TransitionInfo info) = 2;
+ in Rect homeContentInsets, in Rect minimizedHomeBounds, in Bundle extras) = 2;
/**
* Called when the task of an activity that has been started while the recents animation
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentTasksController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentTasksController.java
index 0869caa55369..975b65023f20 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentTasksController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentTasksController.java
@@ -103,6 +103,7 @@ public class RecentTasksController implements TaskStackListenerCallback,
private final RecentTasksImpl mImpl = new RecentTasksImpl();
private final ActivityTaskManager mActivityTaskManager;
private final TaskStackTransitionObserver mTaskStackTransitionObserver;
+ private final RecentsShellCommandHandler mRecentsShellCommandHandler;
private RecentsTransitionHandler mTransitionHandler = null;
private IRecentTasksListener mListener;
private final boolean mPcFeatureEnabled;
@@ -167,6 +168,7 @@ public class RecentTasksController implements TaskStackListenerCallback,
mDesktopUserRepositories = desktopUserRepositories;
mTaskStackTransitionObserver = taskStackTransitionObserver;
mMainExecutor = mainExecutor;
+ mRecentsShellCommandHandler = new RecentsShellCommandHandler(this);
shellInit.addInitCallback(this::onInit, this);
}
@@ -183,6 +185,7 @@ public class RecentTasksController implements TaskStackListenerCallback,
mShellController.addExternalInterface(IRecentTasks.DESCRIPTOR,
this::createExternalInterface, this);
mShellCommandHandler.addDumpCallback(this::dump, this);
+ mShellCommandHandler.addCommandCallback("recents", mRecentsShellCommandHandler, this);
mUserId = ActivityManager.getCurrentUser();
mDesktopUserRepositories.ifPresent(
desktopUserRepositories ->
@@ -299,7 +302,7 @@ public class RecentTasksController implements TaskStackListenerCallback,
@Override
public void onRecentTaskRemovedForAddTask(int taskId) {
mDesktopUserRepositories.ifPresent(
- desktopUserRepositories -> desktopUserRepositories.getCurrent().removeFreeformTask(
+ desktopUserRepositories -> desktopUserRepositories.getCurrent().removeTask(
INVALID_DISPLAY, taskId));
}
@@ -656,6 +659,11 @@ public class RecentTasksController implements TaskStackListenerCallback,
return mActivityTaskManager.removeTask(taskId);
}
+ /** Removes all recent tasks that are visible. */
+ public void removeAllVisibleRecentTasks() throws RemoteException {
+ ActivityTaskManager.getService().removeAllVisibleRecentTasks();
+ }
+
public void dump(@NonNull PrintWriter pw, String prefix) {
final String innerPrefix = prefix + " ";
pw.println(prefix + TAG);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentsShellCommandHandler.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentsShellCommandHandler.kt
new file mode 100644
index 000000000000..f786e079ed93
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentsShellCommandHandler.kt
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2025 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.recents
+
+import android.os.RemoteException
+import com.android.wm.shell.sysui.ShellCommandHandler.ShellCommandActionHandler
+import java.io.PrintWriter
+
+class RecentsShellCommandHandler(
+ private val recentTasksController: RecentTasksController
+) : ShellCommandActionHandler {
+ override fun onShellCommand(args: Array<out String>, pw: PrintWriter): Boolean {
+ when (args[0]) {
+ "clearAll" -> return runClearAll(pw)
+ else -> {
+ pw.println("Invalid command: " + args[0])
+ return false
+ }
+ }
+ }
+
+ override fun printShellCommandHelp(pw: PrintWriter, prefix: String) {
+ pw.println("${prefix}clearAll")
+ pw.println("$prefix Clears all visible recent tasks.")
+ }
+
+ private fun runClearAll(pw: PrintWriter): Boolean {
+ try {
+ recentTasksController.removeAllVisibleRecentTasks()
+ } catch (e: RemoteException) {
+ pw.println("Exception while removing visible recent tasks:")
+ e.printStackTrace(pw)
+ return false
+ }
+ return true
+ }
+} \ No newline at end of file
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentsTransitionHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentsTransitionHandler.java
index aeccd86e122c..db582aa30f6a 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentsTransitionHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentsTransitionHandler.java
@@ -587,8 +587,7 @@ public class RecentsTransitionHandler implements Transitions.TransitionHandler,
mListener.onAnimationStart(this,
apps.toArray(new RemoteAnimationTarget[apps.size()]),
new RemoteAnimationTarget[0],
- new Rect(0, 0, 0, 0), new Rect(), new Bundle(),
- null);
+ new Rect(0, 0, 0, 0), new Rect(), new Bundle());
for (int i = 0; i < mStateListeners.size(); i++) {
mStateListeners.get(i).onTransitionStateChanged(TRANSITION_STATE_ANIMATING);
}
@@ -819,7 +818,7 @@ public class RecentsTransitionHandler implements Transitions.TransitionHandler,
mListener.onAnimationStart(this,
apps.toArray(new RemoteAnimationTarget[apps.size()]),
wallpapers.toArray(new RemoteAnimationTarget[wallpapers.size()]),
- new Rect(0, 0, 0, 0), new Rect(), b, info);
+ new Rect(0, 0, 0, 0), new Rect(), b);
for (int i = 0; i < mStateListeners.size(); i++) {
mStateListeners.get(i).onTransitionStateChanged(TRANSITION_STATE_ANIMATING);
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java
index c724135aeced..9e88a260ac44 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java
@@ -82,6 +82,7 @@ import com.android.launcher3.icons.IconProvider;
import com.android.wm.shell.R;
import com.android.wm.shell.RootTaskDisplayAreaOrganizer;
import com.android.wm.shell.ShellTaskOrganizer;
+import com.android.wm.shell.common.ComponentUtils;
import com.android.wm.shell.common.DisplayController;
import com.android.wm.shell.common.DisplayImeController;
import com.android.wm.shell.common.DisplayInsetsController;
@@ -682,7 +683,7 @@ public class SplitScreenController implements SplitDragPolicy.Starter,
final String packageName1 = shortcutInfo.getPackage();
// NOTE: This doesn't correctly pull out packageName2 if taskId is referring to a task in
// recents that hasn't launched and is not being organized
- final String packageName2 = SplitScreenUtils.getPackageName(taskId, mTaskOrganizer);
+ final String packageName2 = ComponentUtils.getPackageName(taskId, mTaskOrganizer);
final int userId1 = shortcutInfo.getUserId();
final int userId2 = SplitScreenUtils.getUserId(taskId, mTaskOrganizer);
if (samePackage(packageName1, packageName2, userId1, userId2)) {
@@ -727,10 +728,10 @@ public class SplitScreenController implements SplitDragPolicy.Starter,
@SplitPosition int splitPosition, @PersistentSnapPosition int snapPosition,
@Nullable RemoteTransition remoteTransition, InstanceId instanceId) {
Intent fillInIntent = null;
- final String packageName1 = SplitScreenUtils.getPackageName(pendingIntent);
+ final String packageName1 = ComponentUtils.getPackageName(pendingIntent);
// NOTE: This doesn't correctly pull out packageName2 if taskId is referring to a task in
// recents that hasn't launched and is not being organized
- final String packageName2 = SplitScreenUtils.getPackageName(taskId, mTaskOrganizer);
+ final String packageName2 = ComponentUtils.getPackageName(taskId, mTaskOrganizer);
final int userId2 = SplitScreenUtils.getUserId(taskId, mTaskOrganizer);
boolean setSecondIntentMultipleTask = false;
if (samePackage(packageName1, packageName2, userId1, userId2)) {
@@ -766,8 +767,8 @@ public class SplitScreenController implements SplitDragPolicy.Starter,
InstanceId instanceId) {
Intent fillInIntent1 = null;
Intent fillInIntent2 = null;
- final String packageName1 = SplitScreenUtils.getPackageName(pendingIntent1);
- final String packageName2 = SplitScreenUtils.getPackageName(pendingIntent2);
+ final String packageName1 = ComponentUtils.getPackageName(pendingIntent1);
+ final String packageName2 = ComponentUtils.getPackageName(pendingIntent2);
final ActivityOptions activityOptions1 = options1 != null
? ActivityOptions.fromBundle(options1) : ActivityOptions.makeBasic();
final ActivityOptions activityOptions2 = options2 != null
@@ -835,7 +836,7 @@ public class SplitScreenController implements SplitDragPolicy.Starter,
if (fillInIntent == null) fillInIntent = new Intent();
fillInIntent.addFlags(FLAG_ACTIVITY_NO_USER_ACTION);
- final String packageName1 = SplitScreenUtils.getPackageName(intent);
+ final String packageName1 = ComponentUtils.getPackageName(intent);
final String packageName2 = getPackageName(reverseSplitPosition(position), hideTaskToken);
final int userId2 = getUserId(reverseSplitPosition(position), hideTaskToken);
final ComponentName component = intent.getIntent().getComponent();
@@ -900,7 +901,7 @@ public class SplitScreenController implements SplitDragPolicy.Starter,
}
}
- return taskInfo != null ? SplitScreenUtils.getPackageName(taskInfo.baseIntent) : null;
+ return taskInfo != null ? ComponentUtils.getPackageName(taskInfo.baseIntent) : null;
}
/**
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
index b6bd879c75eb..c27ef295db51 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
@@ -131,6 +131,7 @@ import com.android.internal.protolog.ProtoLog;
import com.android.launcher3.icons.IconProvider;
import com.android.wm.shell.R;
import com.android.wm.shell.ShellTaskOrganizer;
+import com.android.wm.shell.common.ComponentUtils;
import com.android.wm.shell.common.DisplayController;
import com.android.wm.shell.common.DisplayImeController;
import com.android.wm.shell.common.DisplayInsetsController;
@@ -140,7 +141,6 @@ import com.android.wm.shell.common.SyncTransactionQueue;
import com.android.wm.shell.common.split.OffscreenTouchZone;
import com.android.wm.shell.common.split.SplitDecorManager;
import com.android.wm.shell.common.split.SplitLayout;
-import com.android.wm.shell.common.split.SplitScreenUtils;
import com.android.wm.shell.common.split.SplitState;
import com.android.wm.shell.common.split.SplitWindowManager;
import com.android.wm.shell.desktopmode.DesktopTasksController;
@@ -3536,12 +3536,12 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
if (mSplitRequest.mActivateTaskId == taskInfo.taskId) {
return mSplitRequest.mActivatePosition;
}
- final String packageName1 = SplitScreenUtils.getPackageName(mSplitRequest.mStartIntent);
- final String basePackageName = SplitScreenUtils.getPackageName(taskInfo.baseIntent);
+ final String packageName1 = ComponentUtils.getPackageName(mSplitRequest.mStartIntent);
+ final String basePackageName = ComponentUtils.getPackageName(taskInfo.baseIntent);
if (packageName1 != null && packageName1.equals(basePackageName)) {
return mSplitRequest.mActivatePosition;
}
- final String packageName2 = SplitScreenUtils.getPackageName(mSplitRequest.mStartIntent2);
+ final String packageName2 = ComponentUtils.getPackageName(mSplitRequest.mStartIntent2);
if (packageName2 != null && packageName2.equals(basePackageName)) {
return mSplitRequest.mActivatePosition;
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/taskview/TaskViewTaskController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/taskview/TaskViewTaskController.java
index 8dd14983e122..5c7dd078ee45 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/taskview/TaskViewTaskController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/taskview/TaskViewTaskController.java
@@ -536,9 +536,12 @@ public class TaskViewTaskController implements ShellTaskOrganizer.TaskListener {
}
return;
}
+ // Cache it to avoid NPE and make sure to remove it from recents history.
+ // mTaskToken can be cleared in onTaskVanished() when the task is removed.
+ final WindowContainerToken taskToken = mTaskToken;
mShellExecutor.execute(() -> {
WindowContainerTransaction wct = new WindowContainerTransaction();
- wct.removeTask(mTaskToken);
+ wct.removeTask(taskToken);
mTaskViewTransitions.closeTaskView(wct, this);
});
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultMixedHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultMixedHandler.java
index 41c0a11827bb..2177986bccd5 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultMixedHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultMixedHandler.java
@@ -43,7 +43,7 @@ import com.android.internal.protolog.ProtoLog;
import com.android.window.flags.Flags;
import com.android.wm.shell.ShellTaskOrganizer;
import com.android.wm.shell.activityembedding.ActivityEmbeddingController;
-import com.android.wm.shell.common.split.SplitScreenUtils;
+import com.android.wm.shell.common.ComponentUtils;
import com.android.wm.shell.desktopmode.DesktopTasksController;
import com.android.wm.shell.keyguard.KeyguardTransitionHandler;
import com.android.wm.shell.pip.PipTransitionController;
@@ -645,7 +645,7 @@ public class DefaultMixedHandler implements MixedTransitionHandler,
// task enter split.
if (mPipHandler != null) {
return mPipHandler
- .isPackageActiveInPip(SplitScreenUtils.getPackageName(intent.getIntent()));
+ .isPackageActiveInPip(ComponentUtils.getPackageName(intent.getIntent()));
}
return false;
}
@@ -657,7 +657,7 @@ public class DefaultMixedHandler implements MixedTransitionHandler,
// task enter split.
if (mPipHandler != null) {
return mPipHandler.isPackageActiveInPip(
- SplitScreenUtils.getPackageName(taskId, shellTaskOrganizer));
+ ComponentUtils.getPackageName(taskId, shellTaskOrganizer));
}
return false;
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java
index 429e0564dd2c..9fbda46bd2b7 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java
@@ -126,8 +126,6 @@ import com.android.wm.shell.desktopmode.common.ToggleTaskSizeUtilsKt;
import com.android.wm.shell.desktopmode.education.AppHandleEducationController;
import com.android.wm.shell.desktopmode.education.AppToWebEducationController;
import com.android.wm.shell.freeform.FreeformTaskTransitionStarter;
-import com.android.wm.shell.recents.RecentsTransitionHandler;
-import com.android.wm.shell.recents.RecentsTransitionStateListener;
import com.android.wm.shell.shared.FocusTransitionListener;
import com.android.wm.shell.shared.annotations.ShellBackgroundThread;
import com.android.wm.shell.shared.annotations.ShellMainThread;
@@ -159,10 +157,8 @@ import kotlinx.coroutines.MainCoroutineDispatcher;
import java.io.PrintWriter;
import java.util.ArrayList;
-import java.util.HashSet;
import java.util.List;
import java.util.Optional;
-import java.util.Set;
import java.util.function.Supplier;
/**
@@ -251,7 +247,6 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel,
private final DesktopModeEventLogger mDesktopModeEventLogger;
private final DesktopModeUiEventLogger mDesktopModeUiEventLogger;
private final WindowDecorTaskResourceLoader mTaskResourceLoader;
- private final RecentsTransitionHandler mRecentsTransitionHandler;
public DesktopModeWindowDecorViewModel(
Context context,
@@ -287,8 +282,7 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel,
FocusTransitionObserver focusTransitionObserver,
DesktopModeEventLogger desktopModeEventLogger,
DesktopModeUiEventLogger desktopModeUiEventLogger,
- WindowDecorTaskResourceLoader taskResourceLoader,
- RecentsTransitionHandler recentsTransitionHandler) {
+ WindowDecorTaskResourceLoader taskResourceLoader) {
this(
context,
shellExecutor,
@@ -329,8 +323,7 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel,
focusTransitionObserver,
desktopModeEventLogger,
desktopModeUiEventLogger,
- taskResourceLoader,
- recentsTransitionHandler);
+ taskResourceLoader);
}
@VisibleForTesting
@@ -374,8 +367,7 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel,
FocusTransitionObserver focusTransitionObserver,
DesktopModeEventLogger desktopModeEventLogger,
DesktopModeUiEventLogger desktopModeUiEventLogger,
- WindowDecorTaskResourceLoader taskResourceLoader,
- RecentsTransitionHandler recentsTransitionHandler) {
+ WindowDecorTaskResourceLoader taskResourceLoader) {
mContext = context;
mMainExecutor = shellExecutor;
mMainHandler = mainHandler;
@@ -444,7 +436,6 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel,
mDesktopModeEventLogger = desktopModeEventLogger;
mDesktopModeUiEventLogger = desktopModeUiEventLogger;
mTaskResourceLoader = taskResourceLoader;
- mRecentsTransitionHandler = recentsTransitionHandler;
shellInit.addInitCallback(this::onInit, this);
}
@@ -459,10 +450,6 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel,
new DesktopModeOnTaskResizeAnimationListener());
mDesktopTasksController.setOnTaskRepositionAnimationListener(
new DesktopModeOnTaskRepositionAnimationListener());
- if (Flags.enableDesktopRecentsTransitionsCornersBugfix()) {
- mRecentsTransitionHandler.addTransitionStateListener(
- new DesktopModeRecentsTransitionStateListener());
- }
mDisplayController.addDisplayChangingController(mOnDisplayChangingListener);
try {
mWindowManager.registerSystemGestureExclusionListener(mGestureExclusionListener,
@@ -1872,38 +1859,6 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel,
}
}
- private class DesktopModeRecentsTransitionStateListener
- implements RecentsTransitionStateListener {
- final Set<Integer> mAnimatingTaskIds = new HashSet<>();
-
- @Override
- public void onTransitionStateChanged(int state) {
- switch (state) {
- case RecentsTransitionStateListener.TRANSITION_STATE_REQUESTED:
- for (int n = 0; n < mWindowDecorByTaskId.size(); n++) {
- int taskId = mWindowDecorByTaskId.keyAt(n);
- mAnimatingTaskIds.add(taskId);
- setIsRecentsTransitionRunningForTask(taskId, true);
- }
- return;
- case RecentsTransitionStateListener.TRANSITION_STATE_NOT_RUNNING:
- // No Recents transition running - clean up window decorations
- for (int taskId : mAnimatingTaskIds) {
- setIsRecentsTransitionRunningForTask(taskId, false);
- }
- mAnimatingTaskIds.clear();
- return;
- default:
- }
- }
-
- private void setIsRecentsTransitionRunningForTask(int taskId, boolean isRecentsRunning) {
- final DesktopModeWindowDecoration decoration = mWindowDecorByTaskId.get(taskId);
- if (decoration == null) return;
- decoration.setIsRecentsTransitionRunning(isRecentsRunning);
- }
- }
-
private class DragEventListenerImpl
implements DragPositioningCallbackUtility.DragEventListener {
@Override
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecoration.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecoration.java
index 39a989ce7c7f..4ac89546c9c7 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecoration.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecoration.java
@@ -204,7 +204,6 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin
private final MultiInstanceHelper mMultiInstanceHelper;
private final WindowDecorCaptionHandleRepository mWindowDecorCaptionHandleRepository;
private final DesktopUserRepositories mDesktopUserRepositories;
- private boolean mIsRecentsTransitionRunning = false;
private Runnable mLoadAppInfoRunnable;
private Runnable mSetAppInfoRunnable;
@@ -499,7 +498,7 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin
applyStartTransactionOnDraw, shouldSetTaskVisibilityPositionAndCrop,
mIsStatusBarVisible, mIsKeyguardVisibleAndOccluded, inFullImmersive,
mDisplayController.getInsetsState(taskInfo.displayId), hasGlobalFocus,
- displayExclusionRegion, mIsRecentsTransitionRunning);
+ displayExclusionRegion);
final WindowDecorLinearLayout oldRootView = mResult.mRootView;
final SurfaceControl oldDecorationSurface = mDecorationContainerSurface;
@@ -870,8 +869,7 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin
boolean inFullImmersiveMode,
@NonNull InsetsState displayInsetsState,
boolean hasGlobalFocus,
- @NonNull Region displayExclusionRegion,
- boolean shouldIgnoreCornerRadius) {
+ @NonNull Region displayExclusionRegion) {
final int captionLayoutId = getDesktopModeWindowDecorLayoutId(taskInfo.getWindowingMode());
final boolean isAppHeader =
captionLayoutId == R.layout.desktop_mode_app_header;
@@ -1008,19 +1006,13 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin
relayoutParams.mWindowDecorConfig = windowDecorConfig;
if (DesktopModeStatus.useRoundedCorners()) {
- relayoutParams.mCornerRadius = shouldIgnoreCornerRadius ? INVALID_CORNER_RADIUS :
- getCornerRadius(context, relayoutParams.mLayoutResId);
+ relayoutParams.mCornerRadius = taskInfo.getWindowingMode() == WINDOWING_MODE_FREEFORM
+ ? loadDimensionPixelSize(context.getResources(),
+ R.dimen.desktop_windowing_freeform_rounded_corner_radius)
+ : INVALID_CORNER_RADIUS;
}
}
- private static int getCornerRadius(@NonNull Context context, int layoutResId) {
- if (layoutResId == R.layout.desktop_mode_app_header) {
- return loadDimensionPixelSize(context.getResources(),
- R.dimen.desktop_windowing_freeform_rounded_corner_radius);
- }
- return INVALID_CORNER_RADIUS;
- }
-
/**
* If task has focused window decor, return the caption id of the fullscreen caption size
* resource. Otherwise, return ID_NULL and caption width be set to task width.
@@ -1748,17 +1740,6 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin
}
/**
- * Declares whether a Recents transition is currently active.
- *
- * <p> When a Recents transition is active we allow that transition to take ownership of the
- * corner radius of its task surfaces, so each window decoration should stop updating the corner
- * radius of its task surface during that time.
- */
- void setIsRecentsTransitionRunning(boolean isRecentsTransitionRunning) {
- mIsRecentsTransitionRunning = isRecentsTransitionRunning;
- }
-
- /**
* Called when there is a {@link MotionEvent#ACTION_HOVER_EXIT} on the maximize window button.
*/
void onMaximizeButtonHoverExit() {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/WindowDecoration.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/WindowDecoration.java
index fa7183ad0fd8..5d1bedb85b5e 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/WindowDecoration.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/WindowDecoration.java
@@ -967,4 +967,4 @@ public abstract class WindowDecoration<T extends View & TaskFocusStateConsumer>
return Objects.hash(mToken, mOwner, mFrame, Arrays.hashCode(mBoundingRects), mFlags);
}
}
-}
+} \ No newline at end of file
diff --git a/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/functional/EnterDesktopWithDragExistingWindowsTest.kt b/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/functional/EnterDesktopWithDragExistingWindowsTest.kt
new file mode 100644
index 000000000000..2b26bbfb68cb
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/functional/EnterDesktopWithDragExistingWindowsTest.kt
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2025 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.functional
+
+import android.platform.test.annotations.Postsubmit
+import com.android.wm.shell.scenarios.EnterDesktopWithDragExistingWindows
+import org.junit.runner.RunWith
+import org.junit.runners.BlockJUnit4ClassRunner
+
+/* Functional test for [EnterDesktopWithDragExistingWindows]. */
+@RunWith(BlockJUnit4ClassRunner::class)
+@Postsubmit
+class EnterDesktopWithDragExistingWindowsTest : EnterDesktopWithDragExistingWindows()
diff --git a/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/EnterDesktopWithAppHandleMenuExistingWindows.kt b/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/EnterDesktopWithAppHandleMenuExistingWindows.kt
new file mode 100644
index 000000000000..2de0830dedb5
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/EnterDesktopWithAppHandleMenuExistingWindows.kt
@@ -0,0 +1,70 @@
+/*
+ * Copyright (C) 2025 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.scenarios
+
+import android.platform.test.annotations.Postsubmit
+import android.app.Instrumentation
+import android.tools.traces.parsers.WindowManagerStateHelper
+import androidx.test.platform.app.InstrumentationRegistry
+import androidx.test.uiautomator.UiDevice
+import com.android.launcher3.tapl.LauncherInstrumentation
+import com.android.server.wm.flicker.helpers.DesktopModeAppHelper
+import com.android.server.wm.flicker.helpers.SimpleAppHelper
+import com.android.server.wm.flicker.helpers.ImeAppHelper
+import com.android.server.wm.flicker.helpers.NewTasksAppHelper
+import com.android.window.flags.Flags
+import org.junit.After
+import org.junit.Assume
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.BlockJUnit4ClassRunner
+
+@RunWith(BlockJUnit4ClassRunner::class)
+@Postsubmit
+open class EnterDesktopWithAppHandleMenuExistingWindows {
+
+ private val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation()
+ private val tapl = LauncherInstrumentation()
+ private val wmHelper = WindowManagerStateHelper(instrumentation)
+ private val device = UiDevice.getInstance(instrumentation)
+ private val imeApp = ImeAppHelper(instrumentation)
+ private val newTaskApp = NewTasksAppHelper(instrumentation)
+ private val testApp = DesktopModeAppHelper(SimpleAppHelper(instrumentation))
+
+ @Before
+ fun setup() {
+ Assume.assumeTrue(Flags.enableDesktopWindowingMode() && tapl.isTablet)
+ testApp.enterDesktopMode(wmHelper, device)
+ imeApp.launchViaIntent(wmHelper)
+ newTaskApp.launchViaIntent(wmHelper)
+ testApp.launchViaIntent(wmHelper)
+ testApp.exitDesktopWithDragToTopDragZone(wmHelper, device)
+ }
+
+ @Test
+ open fun reenterDesktopWithAppHandleMenu() {
+ testApp.enterDesktopModeFromAppHandleMenu(wmHelper, device)
+ }
+
+ @After
+ fun teardown() {
+ testApp.exit(wmHelper)
+ newTaskApp.exit(wmHelper)
+ imeApp.exit(wmHelper)
+ }
+} \ No newline at end of file
diff --git a/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/EnterDesktopWithDragExistingWindows.kt b/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/EnterDesktopWithDragExistingWindows.kt
new file mode 100644
index 000000000000..814478af67c1
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/EnterDesktopWithDragExistingWindows.kt
@@ -0,0 +1,74 @@
+/*
+ * Copyright (C) 2025 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.scenarios
+
+import android.tools.NavBar
+import android.tools.Rotation
+import android.tools.flicker.rules.ChangeDisplayOrientationRule
+import com.android.server.wm.flicker.helpers.ImeAppHelper
+import com.android.server.wm.flicker.helpers.NewTasksAppHelper
+import com.android.window.flags.Flags
+import com.android.wm.shell.Utils
+import org.junit.After
+import org.junit.Assume
+import org.junit.Before
+import org.junit.Ignore
+import org.junit.Rule
+import org.junit.Test
+
+@Ignore("Test Base Class")
+abstract class EnterDesktopWithDragExistingWindows
+constructor(
+ val rotation: Rotation = Rotation.ROTATION_0,
+ isResizeable: Boolean = true,
+ isLandscapeApp: Boolean = true,
+) : DesktopScenarioCustomAppTestBase(isResizeable, isLandscapeApp) {
+
+ @Rule
+ @JvmField
+ val testSetupRule = Utils.testSetupRule(NavBar.MODE_GESTURAL, rotation)
+ private val imeApp = ImeAppHelper(instrumentation)
+ private val newTaskApp = NewTasksAppHelper(instrumentation)
+
+ @Before
+ fun setup() {
+ Assume.assumeTrue(Flags.enableDesktopWindowingMode() && tapl.isTablet)
+ tapl.setEnableRotation(true)
+ tapl.setExpectedRotation(rotation.value)
+ ChangeDisplayOrientationRule.setRotation(rotation)
+ tapl.enableTransientTaskbar(false)
+
+ testApp.enterDesktopMode(wmHelper, device)
+ imeApp.launchViaIntent(wmHelper)
+ newTaskApp.launchViaIntent(wmHelper)
+ testApp.launchViaIntent(wmHelper)
+ testApp.exitDesktopWithDragToTopDragZone(wmHelper, device)
+ }
+
+ @Test
+ open fun reenterDesktopWithDrag() {
+ // By default this method uses drag to desktop
+ testApp.enterDesktopMode(wmHelper, device)
+ }
+
+ @After
+ fun teardown() {
+ testApp.exit(wmHelper)
+ newTaskApp.exit(wmHelper)
+ imeApp.exit(wmHelper)
+ }
+} \ No newline at end of file
diff --git a/libs/WindowManager/Shell/tests/e2e/splitscreen/scenarios/src/com/android/wm/shell/scenarios/CopyContentInSplit.kt b/libs/WindowManager/Shell/tests/e2e/splitscreen/scenarios/src/com/android/wm/shell/scenarios/CopyContentInSplit.kt
index ba4654260864..31d89f92f744 100644
--- a/libs/WindowManager/Shell/tests/e2e/splitscreen/scenarios/src/com/android/wm/shell/scenarios/CopyContentInSplit.kt
+++ b/libs/WindowManager/Shell/tests/e2e/splitscreen/scenarios/src/com/android/wm/shell/scenarios/CopyContentInSplit.kt
@@ -24,6 +24,7 @@ import androidx.test.platform.app.InstrumentationRegistry
import androidx.test.uiautomator.UiDevice
import com.android.launcher3.tapl.LauncherInstrumentation
import com.android.wm.shell.Utils
+import com.android.wm.shell.flicker.utils.RecentTasksUtils
import com.android.wm.shell.flicker.utils.SplitScreenUtils
import org.junit.After
import org.junit.Before
@@ -61,7 +62,6 @@ constructor(val rotation: Rotation = Rotation.ROTATION_0) {
@After
fun teardown() {
- primaryApp.exit(wmHelper)
- secondaryApp.exit(wmHelper)
+ RecentTasksUtils.clearAllVisibleRecentTasks(instrumentation)
}
}
diff --git a/libs/WindowManager/Shell/tests/e2e/splitscreen/scenarios/src/com/android/wm/shell/scenarios/DismissSplitScreenByDivider.kt b/libs/WindowManager/Shell/tests/e2e/splitscreen/scenarios/src/com/android/wm/shell/scenarios/DismissSplitScreenByDivider.kt
index d774a31220da..1af6cac39085 100644
--- a/libs/WindowManager/Shell/tests/e2e/splitscreen/scenarios/src/com/android/wm/shell/scenarios/DismissSplitScreenByDivider.kt
+++ b/libs/WindowManager/Shell/tests/e2e/splitscreen/scenarios/src/com/android/wm/shell/scenarios/DismissSplitScreenByDivider.kt
@@ -24,6 +24,7 @@ import androidx.test.platform.app.InstrumentationRegistry
import androidx.test.uiautomator.UiDevice
import com.android.launcher3.tapl.LauncherInstrumentation
import com.android.wm.shell.Utils
+import com.android.wm.shell.flicker.utils.RecentTasksUtils
import com.android.wm.shell.flicker.utils.SplitScreenUtils
import org.junit.After
import org.junit.Before
@@ -74,7 +75,6 @@ constructor(val rotation: Rotation = Rotation.ROTATION_0) {
@After
fun teardown() {
- primaryApp.exit(wmHelper)
- secondaryApp.exit(wmHelper)
+ RecentTasksUtils.clearAllVisibleRecentTasks(instrumentation)
}
}
diff --git a/libs/WindowManager/Shell/tests/e2e/splitscreen/scenarios/src/com/android/wm/shell/scenarios/DismissSplitScreenByGoHome.kt b/libs/WindowManager/Shell/tests/e2e/splitscreen/scenarios/src/com/android/wm/shell/scenarios/DismissSplitScreenByGoHome.kt
index 5aa161911a9a..8ad8c7bd7a7f 100644
--- a/libs/WindowManager/Shell/tests/e2e/splitscreen/scenarios/src/com/android/wm/shell/scenarios/DismissSplitScreenByGoHome.kt
+++ b/libs/WindowManager/Shell/tests/e2e/splitscreen/scenarios/src/com/android/wm/shell/scenarios/DismissSplitScreenByGoHome.kt
@@ -24,6 +24,7 @@ import androidx.test.platform.app.InstrumentationRegistry
import androidx.test.uiautomator.UiDevice
import com.android.launcher3.tapl.LauncherInstrumentation
import com.android.wm.shell.Utils
+import com.android.wm.shell.flicker.utils.RecentTasksUtils
import com.android.wm.shell.flicker.utils.SplitScreenUtils
import org.junit.After
import org.junit.Before
@@ -60,7 +61,6 @@ constructor(val rotation: Rotation = Rotation.ROTATION_0) {
@After
fun teardown() {
- primaryApp.exit(wmHelper)
- secondaryApp.exit(wmHelper)
+ RecentTasksUtils.clearAllVisibleRecentTasks(instrumentation)
}
}
diff --git a/libs/WindowManager/Shell/tests/e2e/splitscreen/scenarios/src/com/android/wm/shell/scenarios/DragDividerToResize.kt b/libs/WindowManager/Shell/tests/e2e/splitscreen/scenarios/src/com/android/wm/shell/scenarios/DragDividerToResize.kt
index 668f3678bb38..da0ace472153 100644
--- a/libs/WindowManager/Shell/tests/e2e/splitscreen/scenarios/src/com/android/wm/shell/scenarios/DragDividerToResize.kt
+++ b/libs/WindowManager/Shell/tests/e2e/splitscreen/scenarios/src/com/android/wm/shell/scenarios/DragDividerToResize.kt
@@ -24,6 +24,7 @@ import androidx.test.platform.app.InstrumentationRegistry
import androidx.test.uiautomator.UiDevice
import com.android.launcher3.tapl.LauncherInstrumentation
import com.android.wm.shell.Utils
+import com.android.wm.shell.flicker.utils.RecentTasksUtils
import com.android.wm.shell.flicker.utils.SplitScreenUtils
import org.junit.After
import org.junit.Before
@@ -61,7 +62,6 @@ constructor(val rotation: Rotation = Rotation.ROTATION_0) {
@After
fun teardown() {
- primaryApp.exit(wmHelper)
- secondaryApp.exit(wmHelper)
+ RecentTasksUtils.clearAllVisibleRecentTasks(instrumentation)
}
}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/utils/RecentTasksUtils.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/utils/RecentTasksUtils.kt
new file mode 100644
index 000000000000..aa262f9dfd6a
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/utils/RecentTasksUtils.kt
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2025 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.flicker.utils
+
+import android.app.Instrumentation
+
+object RecentTasksUtils {
+ fun clearAllVisibleRecentTasks(instrumentation: Instrumentation) {
+ instrumentation.uiAutomation.executeShellCommand(
+ "dumpsys activity service SystemUIService WMShell recents clearAll"
+ )
+ }
+} \ No newline at end of file
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopRepositoryTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopRepositoryTest.kt
index daecccef9344..6003a219d4db 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopRepositoryTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopRepositoryTest.kt
@@ -669,10 +669,10 @@ class DesktopRepositoryTest : ShellTestCase() {
}
@Test
- fun removeFreeformTask_invalidDisplay_removesTaskFromFreeformTasks() {
+ fun removeTask_invalidDisplay_removesTaskFromFreeformTasks() {
repo.addTask(DEFAULT_DISPLAY, taskId = 1, isVisible = true)
- repo.removeFreeformTask(INVALID_DISPLAY, taskId = 1)
+ repo.removeTask(INVALID_DISPLAY, taskId = 1)
val invalidDisplayTasks = repo.getFreeformTasksInZOrder(INVALID_DISPLAY)
assertThat(invalidDisplayTasks).isEmpty()
@@ -682,11 +682,11 @@ class DesktopRepositoryTest : ShellTestCase() {
@Test
@EnableFlags(FLAG_ENABLE_DESKTOP_WINDOWING_PERSISTENCE)
- fun removeFreeformTask_invalidDisplay_persistenceEnabled_removesTaskFromFreeformTasks() {
+ fun removeTask_invalidDisplay_persistenceEnabled_removesTaskFromFreeformTasks() {
runTest(StandardTestDispatcher()) {
repo.addTask(DEFAULT_DISPLAY, taskId = 1, isVisible = true)
- repo.removeFreeformTask(INVALID_DISPLAY, taskId = 1)
+ repo.removeTask(INVALID_DISPLAY, taskId = 1)
verify(persistentRepository)
.addOrUpdateDesktop(
@@ -708,10 +708,10 @@ class DesktopRepositoryTest : ShellTestCase() {
}
@Test
- fun removeFreeformTask_validDisplay_removesTaskFromFreeformTasks() {
+ fun removeTask_validDisplay_removesTaskFromFreeformTasks() {
repo.addTask(DEFAULT_DISPLAY, taskId = 1, isVisible = true)
- repo.removeFreeformTask(DEFAULT_DISPLAY, taskId = 1)
+ repo.removeTask(DEFAULT_DISPLAY, taskId = 1)
val tasks = repo.getFreeformTasksInZOrder(DEFAULT_DISPLAY)
assertThat(tasks).isEmpty()
@@ -719,11 +719,11 @@ class DesktopRepositoryTest : ShellTestCase() {
@Test
@EnableFlags(FLAG_ENABLE_DESKTOP_WINDOWING_PERSISTENCE)
- fun removeFreeformTask_validDisplay_persistenceEnabled_removesTaskFromFreeformTasks() {
+ fun removeTask_validDisplay_persistenceEnabled_removesTaskFromFreeformTasks() {
runTest(StandardTestDispatcher()) {
repo.addTask(DEFAULT_DISPLAY, taskId = 1, isVisible = true)
- repo.removeFreeformTask(DEFAULT_DISPLAY, taskId = 1)
+ repo.removeTask(DEFAULT_DISPLAY, taskId = 1)
verify(persistentRepository)
.addOrUpdateDesktop(
@@ -745,10 +745,10 @@ class DesktopRepositoryTest : ShellTestCase() {
}
@Test
- fun removeFreeformTask_validDisplay_differentDisplay_doesNotRemovesTask() {
+ fun removeTask_validDisplay_differentDisplay_doesNotRemovesTask() {
repo.addTask(DEFAULT_DISPLAY, taskId = 1, isVisible = true)
- repo.removeFreeformTask(SECOND_DISPLAY, taskId = 1)
+ repo.removeTask(SECOND_DISPLAY, taskId = 1)
val tasks = repo.getFreeformTasksInZOrder(DEFAULT_DISPLAY)
assertThat(tasks).containsExactly(1)
@@ -756,11 +756,11 @@ class DesktopRepositoryTest : ShellTestCase() {
@Test
@EnableFlags(FLAG_ENABLE_DESKTOP_WINDOWING_PERSISTENCE)
- fun removeFreeformTask_validDisplayButDifferentDisplay_persistenceEnabled_doesNotRemoveTask() {
+ fun removeTask_validDisplayButDifferentDisplay_persistenceEnabled_doesNotRemoveTask() {
runTest(StandardTestDispatcher()) {
repo.addTask(DEFAULT_DISPLAY, taskId = 1, isVisible = true)
- repo.removeFreeformTask(SECOND_DISPLAY, taskId = 1)
+ repo.removeTask(SECOND_DISPLAY, taskId = 1)
verify(persistentRepository)
.addOrUpdateDesktop(
@@ -782,57 +782,57 @@ class DesktopRepositoryTest : ShellTestCase() {
}
@Test
- fun removeFreeformTask_removesTaskBoundsBeforeMaximize() {
+ fun removeTask_removesTaskBoundsBeforeMaximize() {
val taskId = 1
repo.addTask(THIRD_DISPLAY, taskId, isVisible = true)
repo.saveBoundsBeforeMaximize(taskId, Rect(0, 0, 200, 200))
- repo.removeFreeformTask(THIRD_DISPLAY, taskId)
+ repo.removeTask(THIRD_DISPLAY, taskId)
assertThat(repo.removeBoundsBeforeMaximize(taskId)).isNull()
}
@Test
- fun removeFreeformTask_removesTaskBoundsBeforeImmersive() {
+ fun removeTask_removesTaskBoundsBeforeImmersive() {
val taskId = 1
repo.addTask(THIRD_DISPLAY, taskId, isVisible = true)
repo.saveBoundsBeforeFullImmersive(taskId, Rect(0, 0, 200, 200))
- repo.removeFreeformTask(THIRD_DISPLAY, taskId)
+ repo.removeTask(THIRD_DISPLAY, taskId)
assertThat(repo.removeBoundsBeforeFullImmersive(taskId)).isNull()
}
@Test
- fun removeFreeformTask_removesActiveTask() {
+ fun removeTask_removesActiveTask() {
val taskId = 1
val listener = TestListener()
repo.addActiveTaskListener(listener)
repo.addTask(DEFAULT_DISPLAY, taskId, isVisible = true)
- repo.removeFreeformTask(THIRD_DISPLAY, taskId)
+ repo.removeTask(THIRD_DISPLAY, taskId)
assertThat(repo.isActiveTask(taskId)).isFalse()
assertThat(listener.activeChangesOnDefaultDisplay).isEqualTo(2)
}
@Test
- fun removeFreeformTask_unminimizesTask() {
+ fun removeTask_unminimizesTask() {
val taskId = 1
repo.addTask(DEFAULT_DISPLAY, taskId, isVisible = true)
repo.minimizeTask(DEFAULT_DISPLAY, taskId)
- repo.removeFreeformTask(DEFAULT_DISPLAY, taskId)
+ repo.removeTask(DEFAULT_DISPLAY, taskId)
assertThat(repo.isMinimizedTask(taskId)).isFalse()
}
@Test
- fun removeFreeformTask_updatesTaskVisibility() {
+ fun removeTask_updatesTaskVisibility() {
val taskId = 1
repo.addTask(DEFAULT_DISPLAY, taskId, isVisible = true)
- repo.removeFreeformTask(THIRD_DISPLAY, taskId)
+ repo.removeTask(THIRD_DISPLAY, taskId)
assertThat(repo.isVisibleTask(taskId)).isFalse()
}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTaskChangeListenerTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTaskChangeListenerTest.kt
index 19ab9113bc7a..c7c0dfc5be6d 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTaskChangeListenerTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTaskChangeListenerTest.kt
@@ -68,8 +68,7 @@ class DesktopTaskChangeListenerTest : ShellTestCase() {
verify(desktopUserRepositories.current, never())
.addTask(task.displayId, task.taskId, task.isVisible)
- verify(desktopUserRepositories.current, never())
- .removeFreeformTask(task.displayId, task.taskId)
+ verify(desktopUserRepositories.current, never()).removeTask(task.displayId, task.taskId)
}
@Test
@@ -79,7 +78,7 @@ class DesktopTaskChangeListenerTest : ShellTestCase() {
desktopTaskChangeListener.onTaskOpening(task)
- verify(desktopUserRepositories.current).removeFreeformTask(task.displayId, task.taskId)
+ verify(desktopUserRepositories.current).removeTask(task.displayId, task.taskId)
}
@Test
@@ -109,7 +108,7 @@ class DesktopTaskChangeListenerTest : ShellTestCase() {
desktopTaskChangeListener.onTaskChanging(task)
- verify(desktopUserRepositories.current).removeFreeformTask(task.displayId, task.taskId)
+ verify(desktopUserRepositories.current).removeTask(task.displayId, task.taskId)
}
@Test
@@ -141,7 +140,7 @@ class DesktopTaskChangeListenerTest : ShellTestCase() {
desktopTaskChangeListener.onTaskMovingToFront(task)
- verify(desktopUserRepositories.current).removeFreeformTask(task.displayId, task.taskId)
+ verify(desktopUserRepositories.current).removeTask(task.displayId, task.taskId)
}
@Test
@@ -169,7 +168,7 @@ class DesktopTaskChangeListenerTest : ShellTestCase() {
verify(desktopUserRepositories.current, never()).minimizeTask(task.displayId, task.taskId)
verify(desktopUserRepositories.current).removeClosingTask(task.taskId)
- verify(desktopUserRepositories.current).removeFreeformTask(task.displayId, task.taskId)
+ verify(desktopUserRepositories.current).removeTask(task.displayId, task.taskId)
}
@Test
@@ -182,6 +181,6 @@ class DesktopTaskChangeListenerTest : ShellTestCase() {
desktopTaskChangeListener.onTaskClosing(task)
verify(desktopUserRepositories.current).removeClosingTask(task.taskId)
- verify(desktopUserRepositories.current).removeFreeformTask(task.displayId, task.taskId)
+ verify(desktopUserRepositories.current).removeTask(task.displayId, task.taskId)
}
}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksTransitionObserverTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksTransitionObserverTest.kt
index 622cb4cad363..ca1e3edb3fd3 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksTransitionObserverTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksTransitionObserverTest.kt
@@ -223,7 +223,7 @@ class DesktopTasksTransitionObserverTest {
)
verify(taskRepository, never()).minimizeTask(task.displayId, task.taskId)
- verify(taskRepository).removeFreeformTask(task.displayId, task.taskId)
+ verify(taskRepository).removeTask(task.displayId, task.taskId)
}
@Test
@@ -241,7 +241,7 @@ class DesktopTasksTransitionObserverTest {
)
verify(taskRepository, never()).minimizeTask(task.displayId, task.taskId)
- verify(taskRepository).removeFreeformTask(task.displayId, task.taskId)
+ verify(taskRepository).removeTask(task.displayId, task.taskId)
}
@Test
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/persistence/DesktopPersistentRepositoryTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/persistence/DesktopPersistentRepositoryTest.kt
index eae206664021..5f92326b5e5e 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/persistence/DesktopPersistentRepositoryTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/persistence/DesktopPersistentRepositoryTest.kt
@@ -85,11 +85,11 @@ class DesktopPersistentRepositoryTest : ShellTestCase() {
val desk = createDesktop(task)
val repositoryState =
DesktopRepositoryState.newBuilder().putDesktop(DEFAULT_DESKTOP_ID, desk)
- val DesktopPersistentRepositories =
+ val desktopPersistentRepositories =
DesktopPersistentRepositories.newBuilder()
.putDesktopRepoByUser(DEFAULT_USER_ID, repositoryState.build())
.build()
- testDatastore.updateData { DesktopPersistentRepositories }
+ testDatastore.updateData { desktopPersistentRepositories }
val actualDesktop = datastoreRepository.readDesktop(DEFAULT_USER_ID, DEFAULT_DESKTOP_ID)
@@ -102,8 +102,8 @@ class DesktopPersistentRepositoryTest : ShellTestCase() {
runTest(StandardTestDispatcher()) {
// Create a basic repository state
val task = createDesktopTask(1)
- val DesktopPersistentRepositories = createRepositoryWithOneDesk(task)
- testDatastore.updateData { DesktopPersistentRepositories }
+ val desktopPersistentRepositories = createRepositoryWithOneDesk(task)
+ testDatastore.updateData { desktopPersistentRepositories }
// Create a new state to be initialized
val visibleTasks = ArraySet(listOf(1, 2))
val minimizedTasks = ArraySet<Int>()
@@ -124,11 +124,46 @@ class DesktopPersistentRepositoryTest : ShellTestCase() {
}
@Test
+ fun removeUsers_removesUsersData() {
+ runTest(StandardTestDispatcher()) {
+ val task = createDesktopTask(1)
+ val desktopPersistentRepositories = createRepositoryWithOneDesk(task)
+ testDatastore.updateData { desktopPersistentRepositories }
+ // Create a new state to be initialized
+ val visibleTasks = ArraySet(listOf(1))
+ val minimizedTasks = ArraySet(listOf(1))
+ val freeformTasksInZOrder = ArrayList(listOf(1))
+ datastoreRepository.addOrUpdateDesktop(
+ visibleTasks = visibleTasks,
+ minimizedTasks = minimizedTasks,
+ freeformTasksInZOrder = freeformTasksInZOrder,
+ userId = DEFAULT_USER_ID,
+ )
+ datastoreRepository.addOrUpdateDesktop(
+ visibleTasks = visibleTasks,
+ minimizedTasks = minimizedTasks,
+ freeformTasksInZOrder = freeformTasksInZOrder,
+ userId = USER_ID_2,
+ )
+
+ datastoreRepository.removeUsers(mutableListOf(USER_ID_2))
+
+ val removedDesktopRepositoryState =
+ datastoreRepository.getDesktopRepositoryState(USER_ID_2)
+ assertThat(removedDesktopRepositoryState).isEqualTo(null)
+
+ val actualDesktop = datastoreRepository.readDesktop(DEFAULT_USER_ID, DEFAULT_DESKTOP_ID)
+ assertThat(actualDesktop?.tasksByTaskIdMap?.get(task.taskId)?.desktopTaskState)
+ .isEqualTo(DesktopTaskState.MINIMIZED)
+ }
+ }
+
+ @Test
fun addOrUpdateTask_changeTaskStateToMinimize_taskStateIsMinimized() {
runTest(StandardTestDispatcher()) {
val task = createDesktopTask(1)
- val DesktopPersistentRepositories = createRepositoryWithOneDesk(task)
- testDatastore.updateData { DesktopPersistentRepositories }
+ val desktopPersistentRepositories = createRepositoryWithOneDesk(task)
+ testDatastore.updateData { desktopPersistentRepositories }
// Create a new state to be initialized
val visibleTasks = ArraySet(listOf(1))
val minimizedTasks = ArraySet(listOf(1))
@@ -152,8 +187,8 @@ class DesktopPersistentRepositoryTest : ShellTestCase() {
fun removeTask_previouslyAddedTaskIsRemoved() {
runTest(StandardTestDispatcher()) {
val task = createDesktopTask(1)
- val DesktopPersistentRepositories = createRepositoryWithOneDesk(task)
- testDatastore.updateData { DesktopPersistentRepositories }
+ val desktopPersistentRepositories = createRepositoryWithOneDesk(task)
+ testDatastore.updateData { desktopPersistentRepositories }
// Create a new state to be initialized
val visibleTasks = ArraySet<Int>()
val minimizedTasks = ArraySet<Int>()
@@ -176,17 +211,18 @@ class DesktopPersistentRepositoryTest : ShellTestCase() {
private companion object {
const val DESKTOP_REPOSITORY_STATES_DATASTORE_TEST_FILE = "desktop_repo_test.pb"
const val DEFAULT_USER_ID = 1000
+ const val USER_ID_2 = 2000
const val DEFAULT_DESKTOP_ID = 0
fun createRepositoryWithOneDesk(task: DesktopTask): DesktopPersistentRepositories {
val desk = createDesktop(task)
val repositoryState =
DesktopRepositoryState.newBuilder().putDesktop(DEFAULT_DESKTOP_ID, desk)
- val DesktopPersistentRepositories =
+ val desktopPersistentRepositories =
DesktopPersistentRepositories.newBuilder()
.putDesktopRepoByUser(DEFAULT_USER_ID, repositoryState.build())
.build()
- return DesktopPersistentRepositories
+ return desktopPersistentRepositories
}
fun createDesktop(task: DesktopTask): Desktop? =
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/freeform/FreeformTaskListenerTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/freeform/FreeformTaskListenerTests.java
index b8629b2f00d2..fa5989a3ca99 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/freeform/FreeformTaskListenerTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/freeform/FreeformTaskListenerTests.java
@@ -224,7 +224,7 @@ public final class FreeformTaskListenerTests extends ShellTestCase {
task.displayId = INVALID_DISPLAY;
mFreeformTaskListener.onTaskVanished(task);
- verify(mDesktopUserRepositories.getCurrent(), never()).removeFreeformTask(task.displayId,
+ verify(mDesktopUserRepositories.getCurrent(), never()).removeTask(task.displayId,
task.taskId);
}
@@ -248,7 +248,7 @@ public final class FreeformTaskListenerTests extends ShellTestCase {
.minimizeTask(task.displayId, task.taskId);
verify(mDesktopUserRepositories.getCurrent()).removeClosingTask(task.taskId);
verify(mDesktopUserRepositories.getCurrent())
- .removeFreeformTask(task.displayId, task.taskId);
+ .removeTask(task.displayId, task.taskId);
}
@Test
@@ -265,7 +265,7 @@ public final class FreeformTaskListenerTests extends ShellTestCase {
verify(mDesktopUserRepositories.getCurrent(), never())
.removeClosingTask(task.taskId);
verify(mDesktopUserRepositories.getCurrent(), never())
- .removeFreeformTask(task.displayId, task.taskId);
+ .removeTask(task.displayId, task.taskId);
}
@Test
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/recents/RecentsTransitionHandlerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/recents/RecentsTransitionHandlerTest.java
index ab43119b14c0..894d238b7e15 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/recents/RecentsTransitionHandlerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/recents/RecentsTransitionHandlerTest.java
@@ -169,7 +169,7 @@ public class RecentsTransitionHandlerTest extends ShellTestCase {
final IResultReceiver finishCallback = mock(IResultReceiver.class);
final IBinder transition = startRecentsTransition(/* synthetic= */ true, runner);
- verify(runner).onAnimationStart(any(), any(), any(), any(), any(), any(), any());
+ verify(runner).onAnimationStart(any(), any(), any(), any(), any(), any());
// Finish and verify no transition remains and that the provided finish callback is called
mRecentsTransitionHandler.findController(transition).finish(true /* toHome */,
@@ -184,7 +184,7 @@ public class RecentsTransitionHandlerTest extends ShellTestCase {
final IRecentsAnimationRunner runner = mock(IRecentsAnimationRunner.class);
final IBinder transition = startRecentsTransition(/* synthetic= */ true, runner);
- verify(runner).onAnimationStart(any(), any(), any(), any(), any(), any(), any());
+ verify(runner).onAnimationStart(any(), any(), any(), any(), any(), any());
mRecentsTransitionHandler.findController(transition).cancel("test");
mMainExecutor.flushAll();
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModelTests.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModelTests.kt
index 79e9b9c8cd77..ffe8e7135513 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModelTests.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModelTests.kt
@@ -59,12 +59,11 @@ import com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn
import com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession
import com.android.window.flags.Flags
import com.android.wm.shell.R
+import com.android.wm.shell.desktopmode.common.ToggleTaskSizeInteraction
import com.android.wm.shell.desktopmode.DesktopImmersiveController
import com.android.wm.shell.desktopmode.DesktopModeEventLogger.Companion.InputMethod
import com.android.wm.shell.desktopmode.DesktopModeEventLogger.Companion.ResizeTrigger
import com.android.wm.shell.desktopmode.DesktopTasksController.SnapPosition
-import com.android.wm.shell.desktopmode.common.ToggleTaskSizeInteraction
-import com.android.wm.shell.recents.RecentsTransitionStateListener
import com.android.wm.shell.shared.desktopmode.DesktopModeStatus
import com.android.wm.shell.shared.desktopmode.DesktopModeTransitionSource
import com.android.wm.shell.splitscreen.SplitScreenController
@@ -540,8 +539,7 @@ class DesktopModeWindowDecorViewModelTests : DesktopModeWindowDecorViewModelTest
onLeftSnapClickListenerCaptor.value.invoke()
verify(mockDesktopTasksController, never())
- .snapToHalfScreen(
- eq(decor.mTaskInfo), any(), eq(currentBounds), eq(SnapPosition.LEFT),
+ .snapToHalfScreen(eq(decor.mTaskInfo), any(), eq(currentBounds), eq(SnapPosition.LEFT),
eq(ResizeTrigger.MAXIMIZE_BUTTON),
eq(InputMethod.UNKNOWN_INPUT_METHOD),
eq(decor),
@@ -618,12 +616,11 @@ class DesktopModeWindowDecorViewModelTests : DesktopModeWindowDecorViewModelTest
onRightSnapClickListenerCaptor.value.invoke()
verify(mockDesktopTasksController, never())
- .snapToHalfScreen(
- eq(decor.mTaskInfo), any(), eq(currentBounds), eq(SnapPosition.RIGHT),
+ .snapToHalfScreen(eq(decor.mTaskInfo), any(), eq(currentBounds), eq(SnapPosition.RIGHT),
eq(ResizeTrigger.MAXIMIZE_BUTTON),
eq(InputMethod.UNKNOWN_INPUT_METHOD),
eq(decor),
- )
+ )
}
@Test
@@ -1226,49 +1223,6 @@ class DesktopModeWindowDecorViewModelTests : DesktopModeWindowDecorViewModelTest
verify(task2, never()).onExclusionRegionChanged(newRegion)
}
- @Test
- @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_RECENTS_TRANSITIONS_CORNERS_BUGFIX)
- fun testRecentsTransitionStateListener_requestedState_setsTransitionRunning() {
- val task = createTask(windowingMode = WINDOWING_MODE_FREEFORM)
- val decoration = setUpMockDecorationForTask(task)
- onTaskOpening(task, SurfaceControl())
-
- desktopModeRecentsTransitionStateListener.onTransitionStateChanged(
- RecentsTransitionStateListener.TRANSITION_STATE_REQUESTED)
-
- verify(decoration).setIsRecentsTransitionRunning(true)
- }
-
- @Test
- @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_RECENTS_TRANSITIONS_CORNERS_BUGFIX)
- fun testRecentsTransitionStateListener_nonRunningState_setsTransitionNotRunning() {
- val task = createTask(windowingMode = WINDOWING_MODE_FREEFORM)
- val decoration = setUpMockDecorationForTask(task)
- onTaskOpening(task, SurfaceControl())
- desktopModeRecentsTransitionStateListener.onTransitionStateChanged(
- RecentsTransitionStateListener.TRANSITION_STATE_REQUESTED)
-
- desktopModeRecentsTransitionStateListener.onTransitionStateChanged(
- RecentsTransitionStateListener.TRANSITION_STATE_NOT_RUNNING)
-
- verify(decoration).setIsRecentsTransitionRunning(false)
- }
-
- @Test
- @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_RECENTS_TRANSITIONS_CORNERS_BUGFIX)
- fun testRecentsTransitionStateListener_requestedAndAnimating_setsTransitionRunningOnce() {
- val task = createTask(windowingMode = WINDOWING_MODE_FREEFORM)
- val decoration = setUpMockDecorationForTask(task)
- onTaskOpening(task, SurfaceControl())
-
- desktopModeRecentsTransitionStateListener.onTransitionStateChanged(
- RecentsTransitionStateListener.TRANSITION_STATE_REQUESTED)
- desktopModeRecentsTransitionStateListener.onTransitionStateChanged(
- RecentsTransitionStateListener.TRANSITION_STATE_ANIMATING)
-
- verify(decoration, times(1)).setIsRecentsTransitionRunning(true)
- }
-
private fun createOpenTaskDecoration(
@WindowingMode windowingMode: Int,
taskSurface: SurfaceControl = SurfaceControl(),
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModelTestsBase.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModelTestsBase.kt
index 8af8285d031c..b5e8cebc1277 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModelTestsBase.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModelTestsBase.kt
@@ -40,7 +40,6 @@ import android.view.SurfaceControl
import android.view.WindowInsets.Type.statusBars
import com.android.dx.mockito.inline.extended.StaticMockitoSession
import com.android.internal.jank.InteractionJankMonitor
-import com.android.window.flags.Flags
import com.android.wm.shell.RootTaskDisplayAreaOrganizer
import com.android.wm.shell.ShellTaskOrganizer
import com.android.wm.shell.ShellTestCase
@@ -66,8 +65,6 @@ import com.android.wm.shell.desktopmode.WindowDecorCaptionHandleRepository
import com.android.wm.shell.desktopmode.education.AppHandleEducationController
import com.android.wm.shell.desktopmode.education.AppToWebEducationController
import com.android.wm.shell.freeform.FreeformTaskTransitionStarter
-import com.android.wm.shell.recents.RecentsTransitionHandler
-import com.android.wm.shell.recents.RecentsTransitionStateListener
import com.android.wm.shell.splitscreen.SplitScreenController
import com.android.wm.shell.sysui.ShellCommandHandler
import com.android.wm.shell.sysui.ShellController
@@ -154,7 +151,6 @@ open class DesktopModeWindowDecorViewModelTestsBase : ShellTestCase() {
protected val mockFocusTransitionObserver = mock<FocusTransitionObserver>()
protected val mockCaptionHandleRepository = mock<WindowDecorCaptionHandleRepository>()
protected val mockDesktopRepository: DesktopRepository = mock<DesktopRepository>()
- protected val mockRecentsTransitionHandler = mock<RecentsTransitionHandler>()
protected val motionEvent = mock<MotionEvent>()
val displayLayout = mock<DisplayLayout>()
protected lateinit var spyContext: TestableContext
@@ -168,7 +164,6 @@ open class DesktopModeWindowDecorViewModelTestsBase : ShellTestCase() {
protected lateinit var mockitoSession: StaticMockitoSession
protected lateinit var shellInit: ShellInit
internal lateinit var desktopModeOnInsetsChangedListener: DesktopModeOnInsetsChangedListener
- protected lateinit var desktopModeRecentsTransitionStateListener: RecentsTransitionStateListener
protected lateinit var displayChangingListener:
DisplayChangeController.OnDisplayChangingListener
internal lateinit var desktopModeOnKeyguardChangedListener: DesktopModeKeyguardChangeListener
@@ -224,8 +219,7 @@ open class DesktopModeWindowDecorViewModelTestsBase : ShellTestCase() {
mockFocusTransitionObserver,
desktopModeEventLogger,
mock<DesktopModeUiEventLogger>(),
- mock<WindowDecorTaskResourceLoader>(),
- mockRecentsTransitionHandler,
+ mock<WindowDecorTaskResourceLoader>()
)
desktopModeWindowDecorViewModel.setSplitScreenController(mockSplitScreenController)
whenever(mockDisplayController.getDisplayLayout(any())).thenReturn(mockDisplayLayout)
@@ -262,13 +256,6 @@ open class DesktopModeWindowDecorViewModelTestsBase : ShellTestCase() {
verify(displayInsetsController)
.addGlobalInsetsChangedListener(insetsChangedCaptor.capture())
desktopModeOnInsetsChangedListener = insetsChangedCaptor.firstValue
- val recentsTransitionStateListenerCaptor = argumentCaptor<RecentsTransitionStateListener>()
- if (Flags.enableDesktopRecentsTransitionsCornersBugfix()) {
- verify(mockRecentsTransitionHandler)
- .addTransitionStateListener(recentsTransitionStateListenerCaptor.capture())
- desktopModeRecentsTransitionStateListener =
- recentsTransitionStateListenerCaptor.firstValue
- }
val keyguardChangedCaptor =
argumentCaptor<DesktopModeKeyguardChangeListener>()
verify(mockShellController).addKeyguardChangeListener(keyguardChangedCaptor.capture())
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorationTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorationTests.java
index 9ea5fd6e1abe..6b02aeffd42a 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorationTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorationTests.java
@@ -169,7 +169,6 @@ public class DesktopModeWindowDecorationTests extends ShellTestCase {
private static final boolean DEFAULT_IS_KEYGUARD_VISIBLE_AND_OCCLUDED = false;
private static final boolean DEFAULT_IS_IN_FULL_IMMERSIVE_MODE = false;
private static final boolean DEFAULT_HAS_GLOBAL_FOCUS = true;
- private static final boolean DEFAULT_SHOULD_IGNORE_CORNER_RADIUS = false;
@Rule public final SetFlagsRule mSetFlagsRule = new SetFlagsRule(DEVICE_DEFAULT);
@@ -397,31 +396,6 @@ public class DesktopModeWindowDecorationTests extends ShellTestCase {
}
@Test
- public void updateRelayoutParams_shouldIgnoreCornerRadius_roundedCornersNotSet() {
- final ActivityManager.RunningTaskInfo taskInfo = createTaskInfo(/* visible= */ true);
- taskInfo.configuration.windowConfiguration.setWindowingMode(WINDOWING_MODE_FREEFORM);
- fillRoundedCornersResources(/* fillValue= */ 30);
- RelayoutParams relayoutParams = new RelayoutParams();
-
- DesktopModeWindowDecoration.updateRelayoutParams(
- relayoutParams,
- mTestableContext,
- taskInfo,
- mMockSplitScreenController,
- DEFAULT_APPLY_START_TRANSACTION_ON_DRAW,
- DEFAULT_SHOULD_SET_TASK_POSITIONING_AND_CROP,
- DEFAULT_IS_STATUSBAR_VISIBLE,
- DEFAULT_IS_KEYGUARD_VISIBLE_AND_OCCLUDED,
- DEFAULT_IS_IN_FULL_IMMERSIVE_MODE,
- new InsetsState(),
- DEFAULT_HAS_GLOBAL_FOCUS,
- mExclusionRegion,
- /* shouldIgnoreCornerRadius= */ true);
-
- assertThat(relayoutParams.mCornerRadius).isEqualTo(INVALID_CORNER_RADIUS);
- }
-
- @Test
@EnableFlags(Flags.FLAG_ENABLE_APP_HEADER_WITH_TASK_DENSITY)
public void updateRelayoutParams_appHeader_usesTaskDensity() {
final int systemDensity = mTestableContext.getOrCreateTestableResources().getResources()
@@ -660,8 +634,7 @@ public class DesktopModeWindowDecorationTests extends ShellTestCase {
/* inFullImmersiveMode */ true,
insetsState,
DEFAULT_HAS_GLOBAL_FOCUS,
- mExclusionRegion,
- DEFAULT_SHOULD_IGNORE_CORNER_RADIUS);
+ mExclusionRegion);
// Takes status bar inset as padding, ignores caption bar inset.
assertThat(relayoutParams.mCaptionTopPadding).isEqualTo(50);
@@ -686,8 +659,7 @@ public class DesktopModeWindowDecorationTests extends ShellTestCase {
/* inFullImmersiveMode */ true,
new InsetsState(),
DEFAULT_HAS_GLOBAL_FOCUS,
- mExclusionRegion,
- DEFAULT_SHOULD_IGNORE_CORNER_RADIUS);
+ mExclusionRegion);
assertThat(relayoutParams.mIsInsetSource).isFalse();
}
@@ -711,8 +683,7 @@ public class DesktopModeWindowDecorationTests extends ShellTestCase {
DEFAULT_IS_IN_FULL_IMMERSIVE_MODE,
new InsetsState(),
DEFAULT_HAS_GLOBAL_FOCUS,
- mExclusionRegion,
- DEFAULT_SHOULD_IGNORE_CORNER_RADIUS);
+ mExclusionRegion);
// Header is always shown because it's assumed the status bar is always visible.
assertThat(relayoutParams.mIsCaptionVisible).isTrue();
@@ -736,8 +707,7 @@ public class DesktopModeWindowDecorationTests extends ShellTestCase {
DEFAULT_IS_IN_FULL_IMMERSIVE_MODE,
new InsetsState(),
DEFAULT_HAS_GLOBAL_FOCUS,
- mExclusionRegion,
- DEFAULT_SHOULD_IGNORE_CORNER_RADIUS);
+ mExclusionRegion);
assertThat(relayoutParams.mIsCaptionVisible).isTrue();
}
@@ -760,8 +730,7 @@ public class DesktopModeWindowDecorationTests extends ShellTestCase {
DEFAULT_IS_IN_FULL_IMMERSIVE_MODE,
new InsetsState(),
DEFAULT_HAS_GLOBAL_FOCUS,
- mExclusionRegion,
- DEFAULT_SHOULD_IGNORE_CORNER_RADIUS);
+ mExclusionRegion);
assertThat(relayoutParams.mIsCaptionVisible).isFalse();
}
@@ -784,8 +753,7 @@ public class DesktopModeWindowDecorationTests extends ShellTestCase {
DEFAULT_IS_IN_FULL_IMMERSIVE_MODE,
new InsetsState(),
DEFAULT_HAS_GLOBAL_FOCUS,
- mExclusionRegion,
- DEFAULT_SHOULD_IGNORE_CORNER_RADIUS);
+ mExclusionRegion);
assertThat(relayoutParams.mIsCaptionVisible).isFalse();
}
@@ -809,8 +777,7 @@ public class DesktopModeWindowDecorationTests extends ShellTestCase {
/* inFullImmersiveMode */ true,
new InsetsState(),
DEFAULT_HAS_GLOBAL_FOCUS,
- mExclusionRegion,
- DEFAULT_SHOULD_IGNORE_CORNER_RADIUS);
+ mExclusionRegion);
assertThat(relayoutParams.mIsCaptionVisible).isTrue();
@@ -826,8 +793,7 @@ public class DesktopModeWindowDecorationTests extends ShellTestCase {
/* inFullImmersiveMode */ true,
new InsetsState(),
DEFAULT_HAS_GLOBAL_FOCUS,
- mExclusionRegion,
- DEFAULT_SHOULD_IGNORE_CORNER_RADIUS);
+ mExclusionRegion);
assertThat(relayoutParams.mIsCaptionVisible).isFalse();
}
@@ -851,8 +817,7 @@ public class DesktopModeWindowDecorationTests extends ShellTestCase {
/* inFullImmersiveMode */ true,
new InsetsState(),
DEFAULT_HAS_GLOBAL_FOCUS,
- mExclusionRegion,
- DEFAULT_SHOULD_IGNORE_CORNER_RADIUS);
+ mExclusionRegion);
assertThat(relayoutParams.mIsCaptionVisible).isFalse();
}
@@ -1515,8 +1480,7 @@ public class DesktopModeWindowDecorationTests extends ShellTestCase {
DEFAULT_IS_IN_FULL_IMMERSIVE_MODE,
new InsetsState(),
DEFAULT_HAS_GLOBAL_FOCUS,
- mExclusionRegion,
- DEFAULT_SHOULD_IGNORE_CORNER_RADIUS);
+ mExclusionRegion);
}
private DesktopModeWindowDecoration createWindowDecoration(
diff --git a/media/java/android/media/quality/MediaQualityManager.java b/media/java/android/media/quality/MediaQualityManager.java
index 191b938b2e26..aeb028ccd0a6 100644
--- a/media/java/android/media/quality/MediaQualityManager.java
+++ b/media/java/android/media/quality/MediaQualityManager.java
@@ -54,14 +54,17 @@ public final class MediaQualityManager {
private final IMediaQualityManager mService;
private final Context mContext;
private final UserHandle mUserHandle;
- private final Object mLock = new Object();
- // @GuardedBy("mLock")
+ private final Object mPpLock = new Object();
+ private final Object mSpLock = new Object();
+ private final Object mAbLock = new Object();
+ private final Object mApLock = new Object();
+ // @GuardedBy("mPpLock")
private final List<PictureProfileCallbackRecord> mPpCallbackRecords = new ArrayList<>();
- // @GuardedBy("mLock")
+ // @GuardedBy("mSpLock")
private final List<SoundProfileCallbackRecord> mSpCallbackRecords = new ArrayList<>();
- // @GuardedBy("mLock")
+ // @GuardedBy("mAbLock")
private final List<AmbientBacklightCallbackRecord> mAbCallbackRecords = new ArrayList<>();
- // @GuardedBy("mLock")
+ // @GuardedBy("mApLock")
private final List<ActiveProcessingPictureListenerRecord> mApListenerRecords =
new ArrayList<>();
@@ -82,7 +85,7 @@ public final class MediaQualityManager {
IPictureProfileCallback ppCallback = new IPictureProfileCallback.Stub() {
@Override
public void onPictureProfileAdded(String profileId, PictureProfile profile) {
- synchronized (mLock) {
+ synchronized (mPpLock) {
for (PictureProfileCallbackRecord record : mPpCallbackRecords) {
// TODO: filter callback record
record.postPictureProfileAdded(profileId, profile);
@@ -91,7 +94,7 @@ public final class MediaQualityManager {
}
@Override
public void onPictureProfileUpdated(String profileId, PictureProfile profile) {
- synchronized (mLock) {
+ synchronized (mPpLock) {
for (PictureProfileCallbackRecord record : mPpCallbackRecords) {
// TODO: filter callback record
record.postPictureProfileUpdated(profileId, profile);
@@ -100,7 +103,7 @@ public final class MediaQualityManager {
}
@Override
public void onPictureProfileRemoved(String profileId, PictureProfile profile) {
- synchronized (mLock) {
+ synchronized (mPpLock) {
for (PictureProfileCallbackRecord record : mPpCallbackRecords) {
// TODO: filter callback record
record.postPictureProfileRemoved(profileId, profile);
@@ -110,7 +113,7 @@ public final class MediaQualityManager {
@Override
public void onParameterCapabilitiesChanged(
String profileId, List<ParameterCapability> caps) {
- synchronized (mLock) {
+ synchronized (mPpLock) {
for (PictureProfileCallbackRecord record : mPpCallbackRecords) {
// TODO: filter callback record
record.postParameterCapabilitiesChanged(profileId, caps);
@@ -119,7 +122,7 @@ public final class MediaQualityManager {
}
@Override
public void onError(String profileId, int err) {
- synchronized (mLock) {
+ synchronized (mPpLock) {
for (PictureProfileCallbackRecord record : mPpCallbackRecords) {
// TODO: filter callback record
record.postError(profileId, err);
@@ -130,7 +133,7 @@ public final class MediaQualityManager {
ISoundProfileCallback spCallback = new ISoundProfileCallback.Stub() {
@Override
public void onSoundProfileAdded(String profileId, SoundProfile profile) {
- synchronized (mLock) {
+ synchronized (mSpLock) {
for (SoundProfileCallbackRecord record : mSpCallbackRecords) {
// TODO: filter callback record
record.postSoundProfileAdded(profileId, profile);
@@ -139,7 +142,7 @@ public final class MediaQualityManager {
}
@Override
public void onSoundProfileUpdated(String profileId, SoundProfile profile) {
- synchronized (mLock) {
+ synchronized (mSpLock) {
for (SoundProfileCallbackRecord record : mSpCallbackRecords) {
// TODO: filter callback record
record.postSoundProfileUpdated(profileId, profile);
@@ -148,7 +151,7 @@ public final class MediaQualityManager {
}
@Override
public void onSoundProfileRemoved(String profileId, SoundProfile profile) {
- synchronized (mLock) {
+ synchronized (mSpLock) {
for (SoundProfileCallbackRecord record : mSpCallbackRecords) {
// TODO: filter callback record
record.postSoundProfileRemoved(profileId, profile);
@@ -158,7 +161,7 @@ public final class MediaQualityManager {
@Override
public void onParameterCapabilitiesChanged(
String profileId, List<ParameterCapability> caps) {
- synchronized (mLock) {
+ synchronized (mSpLock) {
for (SoundProfileCallbackRecord record : mSpCallbackRecords) {
// TODO: filter callback record
record.postParameterCapabilitiesChanged(profileId, caps);
@@ -167,7 +170,7 @@ public final class MediaQualityManager {
}
@Override
public void onError(String profileId, int err) {
- synchronized (mLock) {
+ synchronized (mSpLock) {
for (SoundProfileCallbackRecord record : mSpCallbackRecords) {
// TODO: filter callback record
record.postError(profileId, err);
@@ -178,7 +181,7 @@ public final class MediaQualityManager {
IAmbientBacklightCallback abCallback = new IAmbientBacklightCallback.Stub() {
@Override
public void onAmbientBacklightEvent(AmbientBacklightEvent event) {
- synchronized (mLock) {
+ synchronized (mAbLock) {
for (AmbientBacklightCallbackRecord record : mAbCallbackRecords) {
record.postAmbientBacklightEvent(event);
}
@@ -205,7 +208,7 @@ public final class MediaQualityManager {
@NonNull PictureProfileCallback callback) {
Preconditions.checkNotNull(callback);
Preconditions.checkNotNull(executor);
- synchronized (mLock) {
+ synchronized (mPpLock) {
mPpCallbackRecords.add(new PictureProfileCallbackRecord(callback, executor));
}
}
@@ -215,7 +218,7 @@ public final class MediaQualityManager {
*/
public void unregisterPictureProfileCallback(@NonNull final PictureProfileCallback callback) {
Preconditions.checkNotNull(callback);
- synchronized (mLock) {
+ synchronized (mPpLock) {
for (Iterator<PictureProfileCallbackRecord> it = mPpCallbackRecords.iterator();
it.hasNext(); ) {
PictureProfileCallbackRecord record = it.next();
@@ -416,7 +419,7 @@ public final class MediaQualityManager {
@NonNull SoundProfileCallback callback) {
Preconditions.checkNotNull(callback);
Preconditions.checkNotNull(executor);
- synchronized (mLock) {
+ synchronized (mSpLock) {
mSpCallbackRecords.add(new SoundProfileCallbackRecord(callback, executor));
}
}
@@ -426,7 +429,7 @@ public final class MediaQualityManager {
*/
public void unregisterSoundProfileCallback(@NonNull final SoundProfileCallback callback) {
Preconditions.checkNotNull(callback);
- synchronized (mLock) {
+ synchronized (mSpLock) {
for (Iterator<SoundProfileCallbackRecord> it = mSpCallbackRecords.iterator();
it.hasNext(); ) {
SoundProfileCallbackRecord record = it.next();
@@ -785,7 +788,7 @@ public final class MediaQualityManager {
@NonNull AmbientBacklightCallback callback) {
Preconditions.checkNotNull(callback);
Preconditions.checkNotNull(executor);
- synchronized (mLock) {
+ synchronized (mAbLock) {
mAbCallbackRecords.add(new AmbientBacklightCallbackRecord(callback, executor));
}
}
@@ -797,7 +800,7 @@ public final class MediaQualityManager {
public void unregisterAmbientBacklightCallback(
@NonNull final AmbientBacklightCallback callback) {
Preconditions.checkNotNull(callback);
- synchronized (mLock) {
+ synchronized (mAbLock) {
for (Iterator<AmbientBacklightCallbackRecord> it = mAbCallbackRecords.iterator();
it.hasNext(); ) {
AmbientBacklightCallbackRecord record = it.next();
@@ -1128,7 +1131,7 @@ public final class MediaQualityManager {
@NonNull Consumer<List<ActiveProcessingPicture>> listener) {
Preconditions.checkNotNull(listener);
Preconditions.checkNotNull(executor);
- synchronized (mLock) {
+ synchronized (mApLock) {
mApListenerRecords.add(
new ActiveProcessingPictureListenerRecord(listener, executor, false));
}
@@ -1147,7 +1150,7 @@ public final class MediaQualityManager {
@NonNull Consumer<List<ActiveProcessingPicture>> listener) {
Preconditions.checkNotNull(listener);
Preconditions.checkNotNull(executor);
- synchronized (mLock) {
+ synchronized (mApLock) {
mApListenerRecords.add(
new ActiveProcessingPictureListenerRecord(listener, executor, true));
}
@@ -1160,7 +1163,7 @@ public final class MediaQualityManager {
public void removeActiveProcessingPictureListener(
@NonNull Consumer<List<ActiveProcessingPicture>> listener) {
Preconditions.checkNotNull(listener);
- synchronized (mLock) {
+ synchronized (mApLock) {
for (Iterator<ActiveProcessingPictureListenerRecord> it = mApListenerRecords.iterator();
it.hasNext(); ) {
ActiveProcessingPictureListenerRecord record = it.next();
diff --git a/media/tests/MediaRouter/Android.bp b/media/tests/MediaRouter/Android.bp
index e4f88a65ed1a..e63c59d53f2a 100644
--- a/media/tests/MediaRouter/Android.bp
+++ b/media/tests/MediaRouter/Android.bp
@@ -9,7 +9,7 @@ package {
android_test {
name: "mediaroutertest",
- team: "trendy_team_android_media_solutions",
+ team: "trendy_team_android_media_better_together",
srcs: ["**/*.java"],
diff --git a/native/android/libandroid.map.txt b/native/android/libandroid.map.txt
index b30b779b57b5..49cbd7181d77 100644
--- a/native/android/libandroid.map.txt
+++ b/native/android/libandroid.map.txt
@@ -421,6 +421,7 @@ LIBANDROID {
LIBANDROID_PLATFORM {
global:
AThermal_setIThermalServiceForTesting;
+ ASystemHealth_setIHintManagerForTesting;
APerformanceHint_setIHintManagerForTesting;
APerformanceHint_sendHint;
APerformanceHint_getThreadIds;
diff --git a/native/android/system_health.cpp b/native/android/system_health.cpp
index 5c07ac7bfccc..1b43e71c7bf0 100644
--- a/native/android/system_health.cpp
+++ b/native/android/system_health.cpp
@@ -14,6 +14,8 @@
* limitations under the License.
*/
+#define LOG_TAG "system_health"
+
#include <aidl/android/hardware/power/CpuHeadroomParams.h>
#include <aidl/android/hardware/power/GpuHeadroomParams.h>
#include <aidl/android/os/CpuHeadroomParamsInternal.h>
@@ -23,6 +25,17 @@
#include <android/system_health.h>
#include <binder/IServiceManager.h>
#include <binder/Status.h>
+#include <system_health_private.h>
+
+#include <list>
+#include <map>
+#include <memory>
+#include <mutex>
+#include <optional>
+#include <utility>
+
+#include "android-base/thread_annotations.h"
+#include "utils/SystemClock.h"
using namespace android;
using namespace aidl::android::os;
@@ -55,9 +68,20 @@ private:
IHintManager::HintManagerClientData mClientData;
};
+static std::shared_ptr<IHintManager>* gIHintManagerForTesting = nullptr;
+static std::shared_ptr<ASystemHealthManager> gSystemHealthManagerForTesting = nullptr;
+
ASystemHealthManager* ASystemHealthManager::getInstance() {
static std::once_flag creationFlag;
static ASystemHealthManager* instance = nullptr;
+ if (gSystemHealthManagerForTesting) {
+ return gSystemHealthManagerForTesting.get();
+ }
+ if (gIHintManagerForTesting) {
+ gSystemHealthManagerForTesting =
+ std::shared_ptr<ASystemHealthManager>(create(*gIHintManagerForTesting));
+ return gSystemHealthManagerForTesting.get();
+ }
std::call_once(creationFlag, []() { instance = create(nullptr); });
return instance;
}
@@ -121,7 +145,8 @@ int ASystemHealthManager::getCpuHeadroom(const ACpuHeadroomParams* params, float
}
return EPIPE;
}
- *outHeadroom = res->get<hal::CpuHeadroomResult::Tag::globalHeadroom>();
+ *outHeadroom = res ? res->get<hal::CpuHeadroomResult::Tag::globalHeadroom>()
+ : std::numeric_limits<float>::quiet_NaN();
return OK;
}
@@ -155,37 +180,20 @@ int ASystemHealthManager::getGpuHeadroom(const AGpuHeadroomParams* params, float
}
return EPIPE;
}
- *outHeadroom = res->get<hal::GpuHeadroomResult::Tag::globalHeadroom>();
+ *outHeadroom = res ? res->get<hal::GpuHeadroomResult::Tag::globalHeadroom>()
+ : std::numeric_limits<float>::quiet_NaN();
return OK;
}
int ASystemHealthManager::getCpuHeadroomMinIntervalMillis(int64_t* outMinIntervalMillis) {
if (!mClientData.supportInfo.headroom.isCpuSupported) return ENOTSUP;
- int64_t minIntervalMillis = 0;
- ::ndk::ScopedAStatus ret = mHintManager->getCpuHeadroomMinIntervalMillis(&minIntervalMillis);
- if (!ret.isOk()) {
- ALOGE("ASystemHealth_getCpuHeadroomMinIntervalMillis fails: %s", ret.getMessage());
- if (ret.getExceptionCode() == EX_UNSUPPORTED_OPERATION) {
- return ENOTSUP;
- }
- return EPIPE;
- }
- *outMinIntervalMillis = minIntervalMillis;
+ *outMinIntervalMillis = mClientData.supportInfo.headroom.cpuMinIntervalMillis;
return OK;
}
int ASystemHealthManager::getGpuHeadroomMinIntervalMillis(int64_t* outMinIntervalMillis) {
if (!mClientData.supportInfo.headroom.isGpuSupported) return ENOTSUP;
- int64_t minIntervalMillis = 0;
- ::ndk::ScopedAStatus ret = mHintManager->getGpuHeadroomMinIntervalMillis(&minIntervalMillis);
- if (!ret.isOk()) {
- ALOGE("ASystemHealth_getGpuHeadroomMinIntervalMillis fails: %s", ret.getMessage());
- if (ret.getExceptionCode() == EX_UNSUPPORTED_OPERATION) {
- return ENOTSUP;
- }
- return EPIPE;
- }
- *outMinIntervalMillis = minIntervalMillis;
+ *outMinIntervalMillis = mClientData.supportInfo.headroom.gpuMinIntervalMillis;
return OK;
}
@@ -298,7 +306,6 @@ void ACpuHeadroomParams_setTids(ACpuHeadroomParams* _Nonnull params, const int*
size_t tidsSize) {
LOG_ALWAYS_FATAL_IF(tids == nullptr, "%s: tids should not be null", __FUNCTION__);
params->tids.resize(tidsSize);
- params->tids.clear();
for (int i = 0; i < (int)tidsSize; ++i) {
LOG_ALWAYS_FATAL_IF(tids[i] <= 0, "ACpuHeadroomParams_setTids: Invalid non-positive tid %d",
tids[i]);
@@ -355,3 +362,10 @@ void ACpuHeadroomParams_destroy(ACpuHeadroomParams* _Nullable params) {
void AGpuHeadroomParams_destroy(AGpuHeadroomParams* _Nullable params) {
delete params;
}
+
+void ASystemHealth_setIHintManagerForTesting(void* iManager) {
+ if (iManager == nullptr) {
+ gSystemHealthManagerForTesting = nullptr;
+ }
+ gIHintManagerForTesting = static_cast<std::shared_ptr<IHintManager>*>(iManager);
+}
diff --git a/native/android/tests/system_health/Android.bp b/native/android/tests/system_health/Android.bp
new file mode 100644
index 000000000000..30aeb77375ad
--- /dev/null
+++ b/native/android/tests/system_health/Android.bp
@@ -0,0 +1,66 @@
+// Copyright (C) 2024 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 {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
+cc_test {
+ name: "NativeSystemHealthUnitTestCases",
+
+ multilib: {
+ lib32: {
+ suffix: "32",
+ },
+ lib64: {
+ suffix: "64",
+ },
+ },
+
+ srcs: ["NativeSystemHealthUnitTest.cpp"],
+
+ shared_libs: [
+ "libandroid",
+ "libbinder",
+ "libbinder_ndk",
+ "liblog",
+ "libpowermanager",
+ "libutils",
+ ],
+
+ static_libs: [
+ "libbase",
+ "libgmock",
+ "libgtest",
+ ],
+ stl: "c++_shared",
+
+ test_suites: [
+ "device-tests",
+ ],
+
+ cflags: [
+ "-Wall",
+ "-Werror",
+ ],
+
+ header_libs: [
+ "libandroid_headers_private",
+ ],
+}
diff --git a/native/android/tests/system_health/NativeSystemHealthUnitTest.cpp b/native/android/tests/system_health/NativeSystemHealthUnitTest.cpp
new file mode 100644
index 000000000000..3f08fc66e392
--- /dev/null
+++ b/native/android/tests/system_health/NativeSystemHealthUnitTest.cpp
@@ -0,0 +1,231 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "NativeSystemHealthUnitTest"
+
+#include <aidl/android/os/IHintManager.h>
+#include <android/binder_manager.h>
+#include <android/binder_status.h>
+#include <android/system_health.h>
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+#include <system_health_private.h>
+
+#include <memory>
+#include <optional>
+#include <vector>
+
+using namespace std::chrono_literals;
+namespace hal = aidl::android::hardware::power;
+using aidl::android::os::CpuHeadroomParamsInternal;
+using aidl::android::os::GpuHeadroomParamsInternal;
+using aidl::android::os::IHintManager;
+using aidl::android::os::IHintSession;
+using aidl::android::os::SessionCreationConfig;
+using ndk::ScopedAStatus;
+using ndk::SpAIBinder;
+
+using namespace android;
+using namespace testing;
+
+class MockIHintManager : public IHintManager {
+public:
+ MOCK_METHOD(ScopedAStatus, createHintSessionWithConfig,
+ (const SpAIBinder& token, hal::SessionTag tag,
+ const SessionCreationConfig& creationConfig, hal::SessionConfig* config,
+ IHintManager::SessionCreationReturn* _aidl_return),
+ (override));
+ MOCK_METHOD(ScopedAStatus, setHintSessionThreads,
+ (const std::shared_ptr<IHintSession>& _, const ::std::vector<int32_t>& tids),
+ (override));
+ MOCK_METHOD(ScopedAStatus, getHintSessionThreadIds,
+ (const std::shared_ptr<IHintSession>& _, ::std::vector<int32_t>* tids), (override));
+ MOCK_METHOD(ScopedAStatus, getSessionChannel,
+ (const ::ndk::SpAIBinder& in_token,
+ std::optional<hal::ChannelConfig>* _aidl_return),
+ (override));
+ MOCK_METHOD(ScopedAStatus, closeSessionChannel, (), (override));
+ MOCK_METHOD(ScopedAStatus, getCpuHeadroom,
+ (const CpuHeadroomParamsInternal& _,
+ std::optional<hal::CpuHeadroomResult>* _aidl_return),
+ (override));
+ MOCK_METHOD(ScopedAStatus, getCpuHeadroomMinIntervalMillis, (int64_t*), (override));
+ MOCK_METHOD(ScopedAStatus, getGpuHeadroom,
+ (const GpuHeadroomParamsInternal& _,
+ std::optional<hal::GpuHeadroomResult>* _aidl_return),
+ (override));
+ MOCK_METHOD(ScopedAStatus, getGpuHeadroomMinIntervalMillis, (int64_t* _aidl_return),
+ (override));
+ MOCK_METHOD(ScopedAStatus, passSessionManagerBinder, (const SpAIBinder& sessionManager));
+ MOCK_METHOD(ScopedAStatus, registerClient,
+ (const std::shared_ptr<aidl::android::os::IHintManager::IHintManagerClient>& _,
+ aidl::android::os::IHintManager::HintManagerClientData* _aidl_return),
+ (override));
+ MOCK_METHOD(ScopedAStatus, getClientData,
+ (aidl::android::os::IHintManager::HintManagerClientData * _aidl_return),
+ (override));
+ MOCK_METHOD(SpAIBinder, asBinder, (), (override));
+ MOCK_METHOD(bool, isRemote, (), (override));
+};
+
+class NativeSystemHealthUnitTest : public Test {
+public:
+ void SetUp() override {
+ mMockIHintManager = ndk::SharedRefBase::make<NiceMock<MockIHintManager>>();
+ ASystemHealth_setIHintManagerForTesting(&mMockIHintManager);
+ ON_CALL(*mMockIHintManager, getClientData(_))
+ .WillByDefault(
+ DoAll(SetArgPointee<0>(mClientData), [] { return ScopedAStatus::ok(); }));
+ }
+
+ void TearDown() override {
+ ASystemHealth_setIHintManagerForTesting(nullptr);
+ }
+
+ IHintManager::HintManagerClientData mClientData{
+ .powerHalVersion = 6,
+ .maxCpuHeadroomThreads = 10,
+ .supportInfo{.headroom{
+ .isCpuSupported = true,
+ .isGpuSupported = true,
+ .cpuMinIntervalMillis = 999,
+ .gpuMinIntervalMillis = 998,
+ .cpuMinCalculationWindowMillis = 45,
+ .cpuMaxCalculationWindowMillis = 9999,
+ .gpuMinCalculationWindowMillis = 46,
+ .gpuMaxCalculationWindowMillis = 9998,
+ }},
+ };
+
+ std::shared_ptr<NiceMock<MockIHintManager>> mMockIHintManager = nullptr;
+};
+
+TEST_F(NativeSystemHealthUnitTest, headroomParamsValueRange) {
+ int64_t minIntervalMillis = 0;
+ int minCalculationWindowMillis = 0;
+ int maxCalculationWindowMillis = 0;
+ ASSERT_EQ(OK, ASystemHealth_getCpuHeadroomMinIntervalMillis(&minIntervalMillis));
+ ASSERT_EQ(OK,
+ ASystemHealth_getCpuHeadroomCalculationWindowRange(&minCalculationWindowMillis,
+ &maxCalculationWindowMillis));
+ ASSERT_EQ(minIntervalMillis, mClientData.supportInfo.headroom.cpuMinIntervalMillis);
+ ASSERT_EQ(minCalculationWindowMillis,
+ mClientData.supportInfo.headroom.cpuMinCalculationWindowMillis);
+ ASSERT_EQ(maxCalculationWindowMillis,
+ mClientData.supportInfo.headroom.cpuMaxCalculationWindowMillis);
+
+ ASSERT_EQ(OK, ASystemHealth_getGpuHeadroomMinIntervalMillis(&minIntervalMillis));
+ ASSERT_EQ(OK,
+ ASystemHealth_getGpuHeadroomCalculationWindowRange(&minCalculationWindowMillis,
+ &maxCalculationWindowMillis));
+ ASSERT_EQ(minIntervalMillis, mClientData.supportInfo.headroom.gpuMinIntervalMillis);
+ ASSERT_EQ(minCalculationWindowMillis,
+ mClientData.supportInfo.headroom.gpuMinCalculationWindowMillis);
+ ASSERT_EQ(maxCalculationWindowMillis,
+ mClientData.supportInfo.headroom.gpuMaxCalculationWindowMillis);
+}
+
+TEST_F(NativeSystemHealthUnitTest, getCpuHeadroom) {
+ CpuHeadroomParamsInternal internalParams1;
+ ACpuHeadroomParams* params2 = ACpuHeadroomParams_create();
+ ACpuHeadroomParams_setCalculationWindowMillis(params2, 200);
+ CpuHeadroomParamsInternal internalParams2;
+ internalParams2.calculationWindowMillis = 200;
+ ACpuHeadroomParams* params3 = ACpuHeadroomParams_create();
+ ACpuHeadroomParams_setCalculationType(params3, ACPU_HEADROOM_CALCULATION_TYPE_AVERAGE);
+ CpuHeadroomParamsInternal internalParams3;
+ internalParams3.calculationType = hal::CpuHeadroomParams::CalculationType::AVERAGE;
+ ACpuHeadroomParams* params4 = ACpuHeadroomParams_create();
+ int tids[3] = {1, 2, 3};
+ ACpuHeadroomParams_setTids(params4, tids, 3);
+ CpuHeadroomParamsInternal internalParams4;
+ internalParams4.tids = {1, 2, 3};
+
+ EXPECT_CALL(*mMockIHintManager, getCpuHeadroom(internalParams1, _))
+ .Times(Exactly(1))
+ .WillOnce(DoAll(SetArgPointee<1>(hal::CpuHeadroomResult::make<
+ hal::CpuHeadroomResult::globalHeadroom>(1.0f)),
+ [] { return ScopedAStatus::ok(); }));
+ EXPECT_CALL(*mMockIHintManager, getCpuHeadroom(internalParams2, _))
+ .Times(Exactly(1))
+ .WillOnce(DoAll(SetArgPointee<1>(hal::CpuHeadroomResult::make<
+ hal::CpuHeadroomResult::globalHeadroom>(2.0f)),
+ [] { return ScopedAStatus::ok(); }));
+ EXPECT_CALL(*mMockIHintManager, getCpuHeadroom(internalParams3, _))
+ .Times(Exactly(1))
+ .WillOnce(DoAll(SetArgPointee<1>(std::nullopt), [] { return ScopedAStatus::ok(); }));
+ EXPECT_CALL(*mMockIHintManager, getCpuHeadroom(internalParams4, _))
+ .Times(Exactly(1))
+ .WillOnce(DoAll(SetArgPointee<1>(hal::CpuHeadroomResult::make<
+ hal::CpuHeadroomResult::globalHeadroom>(4.0f)),
+ [] { return ScopedAStatus::ok(); }));
+
+ float headroom1 = 0.0f;
+ float headroom2 = 0.0f;
+ float headroom3 = 0.0f;
+ float headroom4 = 0.0f;
+ ASSERT_EQ(OK, ASystemHealth_getCpuHeadroom(nullptr, &headroom1));
+ ASSERT_EQ(OK, ASystemHealth_getCpuHeadroom(params2, &headroom2));
+ ASSERT_EQ(OK, ASystemHealth_getCpuHeadroom(params3, &headroom3));
+ ASSERT_EQ(OK, ASystemHealth_getCpuHeadroom(params4, &headroom4));
+ ASSERT_EQ(1.0f, headroom1);
+ ASSERT_EQ(2.0f, headroom2);
+ ASSERT_TRUE(isnan(headroom3));
+ ASSERT_EQ(4.0f, headroom4);
+
+ ACpuHeadroomParams_destroy(params2);
+ ACpuHeadroomParams_destroy(params3);
+ ACpuHeadroomParams_destroy(params4);
+}
+
+TEST_F(NativeSystemHealthUnitTest, getGpuHeadroom) {
+ GpuHeadroomParamsInternal internalParams1;
+ AGpuHeadroomParams* params2 = AGpuHeadroomParams_create();
+ AGpuHeadroomParams_setCalculationWindowMillis(params2, 200);
+ GpuHeadroomParamsInternal internalParams2;
+ internalParams2.calculationWindowMillis = 200;
+ AGpuHeadroomParams* params3 = AGpuHeadroomParams_create();
+ AGpuHeadroomParams_setCalculationType(params3, AGPU_HEADROOM_CALCULATION_TYPE_AVERAGE);
+ GpuHeadroomParamsInternal internalParams3;
+ internalParams3.calculationType = hal::GpuHeadroomParams::CalculationType::AVERAGE;
+
+ EXPECT_CALL(*mMockIHintManager, getGpuHeadroom(internalParams1, _))
+ .Times(Exactly(1))
+ .WillOnce(DoAll(SetArgPointee<1>(hal::GpuHeadroomResult::make<
+ hal::GpuHeadroomResult::globalHeadroom>(1.0f)),
+ [] { return ScopedAStatus::ok(); }));
+ EXPECT_CALL(*mMockIHintManager, getGpuHeadroom(internalParams2, _))
+ .Times(Exactly(1))
+ .WillOnce(DoAll(SetArgPointee<1>(hal::GpuHeadroomResult::make<
+ hal::GpuHeadroomResult::globalHeadroom>(2.0f)),
+ [] { return ScopedAStatus::ok(); }));
+ EXPECT_CALL(*mMockIHintManager, getGpuHeadroom(internalParams3, _))
+ .Times(Exactly(1))
+ .WillOnce(DoAll(SetArgPointee<1>(std::nullopt), [] { return ScopedAStatus::ok(); }));
+
+ float headroom1 = 0.0f;
+ float headroom2 = 0.0f;
+ float headroom3 = 0.0f;
+ ASSERT_EQ(OK, ASystemHealth_getGpuHeadroom(nullptr, &headroom1));
+ ASSERT_EQ(OK, ASystemHealth_getGpuHeadroom(params2, &headroom2));
+ ASSERT_EQ(OK, ASystemHealth_getGpuHeadroom(params3, &headroom3));
+ ASSERT_EQ(1.0f, headroom1);
+ ASSERT_EQ(2.0f, headroom2);
+ ASSERT_TRUE(isnan(headroom3));
+
+ AGpuHeadroomParams_destroy(params2);
+ AGpuHeadroomParams_destroy(params3);
+}
diff --git a/packages/CrashRecovery/framework/Android.bp b/packages/CrashRecovery/framework/Android.bp
index 1a3446ec56de..5dd42bb633e5 100644
--- a/packages/CrashRecovery/framework/Android.bp
+++ b/packages/CrashRecovery/framework/Android.bp
@@ -1,8 +1,8 @@
filegroup {
name: "framework-crashrecovery-sources",
srcs: [
- "java/**/*.java",
"java/**/*.aidl",
+ "java/**/*.java",
],
path: "java",
visibility: [
@@ -12,11 +12,14 @@ filegroup {
java_sdk_library {
name: "framework-platformcrashrecovery",
- srcs: [":framework-crashrecovery-sources"],
+ srcs: [
+ ":framework-crashrecovery-module-sources",
+ ":framework-crashrecovery-sources",
+ ],
defaults: ["framework-non-updatable-unbundled-defaults"],
permitted_packages: [
- "android.service.watchdog",
"android.crashrecovery",
+ "android.service.watchdog",
],
static_libs: ["android.crashrecovery.flags-aconfig-java"],
aidl: {
diff --git a/packages/CrashRecovery/services/module/java/com/android/server/RescueParty.java b/packages/CrashRecovery/services/module/java/com/android/server/RescueParty.java
index 40bc5f78a9c6..846da194b3c3 100644
--- a/packages/CrashRecovery/services/module/java/com/android/server/RescueParty.java
+++ b/packages/CrashRecovery/services/module/java/com/android/server/RescueParty.java
@@ -29,18 +29,13 @@ import android.content.pm.PackageManager;
import android.content.pm.VersionedPackage;
import android.crashrecovery.flags.Flags;
import android.os.Build;
-import android.os.Environment;
import android.os.PowerManager;
import android.os.RecoverySystem;
import android.os.SystemClock;
import android.os.SystemProperties;
-import android.os.UserHandle;
-import android.provider.DeviceConfig;
import android.provider.Settings;
import android.sysprop.CrashRecoveryProperties;
import android.text.TextUtils;
-import android.util.ArraySet;
-import android.util.ArrayUtils;
import android.util.EventLog;
import android.util.FileUtils;
import android.util.Log;
@@ -56,10 +51,7 @@ import com.android.server.crashrecovery.proto.CrashRecoveryStatsLog;
import java.io.File;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
-import java.util.ArrayList;
import java.util.HashMap;
-import java.util.HashSet;
-import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.TimeUnit;
@@ -241,87 +233,11 @@ public class RescueParty {
CrashRecoveryProperties.maxRescueLevelAttempted(level);
}
- private static Set<String> getPresetNamespacesForPackages(List<String> packageNames) {
- Set<String> resultSet = new ArraySet<String>();
- if (!Flags.deprecateFlagsAndSettingsResets()) {
- try {
- String flagVal = DeviceConfig.getString(NAMESPACE_CONFIGURATION,
- NAMESPACE_TO_PACKAGE_MAPPING_FLAG, "");
- String[] mappingEntries = flagVal.split(",");
- for (int i = 0; i < mappingEntries.length; i++) {
- if (TextUtils.isEmpty(mappingEntries[i])) {
- continue;
- }
- String[] splitEntry = mappingEntries[i].split(":");
- if (splitEntry.length != 2) {
- throw new RuntimeException("Invalid mapping entry: " + mappingEntries[i]);
- }
- String namespace = splitEntry[0];
- String packageName = splitEntry[1];
-
- if (packageNames.contains(packageName)) {
- resultSet.add(namespace);
- }
- }
- } catch (Exception e) {
- resultSet.clear();
- Slog.e(TAG, "Failed to read preset package to namespaces mapping.", e);
- } finally {
- return resultSet;
- }
- } else {
- return resultSet;
- }
- }
-
@VisibleForTesting
static long getElapsedRealtime() {
return SystemClock.elapsedRealtime();
}
- private static class RescuePartyMonitorCallback implements DeviceConfig.MonitorCallback {
- Context mContext;
-
- RescuePartyMonitorCallback(Context context) {
- this.mContext = context;
- }
-
- public void onNamespaceUpdate(@NonNull String updatedNamespace) {
- if (!Flags.deprecateFlagsAndSettingsResets()) {
- startObservingPackages(mContext, updatedNamespace);
- }
- }
-
- public void onDeviceConfigAccess(@NonNull String callingPackage,
- @NonNull String namespace) {
-
- if (!Flags.deprecateFlagsAndSettingsResets()) {
- RescuePartyObserver.getInstance(mContext).recordDeviceConfigAccess(
- callingPackage,
- namespace);
- }
- }
- }
-
- private static void startObservingPackages(Context context, @NonNull String updatedNamespace) {
- if (!Flags.deprecateFlagsAndSettingsResets()) {
- RescuePartyObserver rescuePartyObserver = RescuePartyObserver.getInstance(context);
- Set<String> callingPackages = rescuePartyObserver.getCallingPackagesSet(
- updatedNamespace);
- if (callingPackages == null) {
- return;
- }
- List<String> callingPackageList = new ArrayList<>();
- callingPackageList.addAll(callingPackages);
- Slog.i(TAG, "Starting to observe: " + callingPackageList + ", updated namespace: "
- + updatedNamespace);
- PackageWatchdog.getInstance(context).startExplicitHealthCheck(
- callingPackageList,
- DEFAULT_OBSERVING_DURATION_MS,
- rescuePartyObserver);
- }
- }
-
private static int getMaxRescueLevel(boolean mayPerformReboot) {
if (Flags.recoverabilityDetection()) {
if (!mayPerformReboot
@@ -849,34 +765,6 @@ public class RescueParty {
}
}
- private synchronized void recordDeviceConfigAccess(@NonNull String callingPackage,
- @NonNull String namespace) {
- if (!Flags.deprecateFlagsAndSettingsResets()) {
- // Record it in calling packages to namespace map
- Set<String> namespaceSet = mCallingPackageNamespaceSetMap.get(callingPackage);
- if (namespaceSet == null) {
- namespaceSet = new ArraySet<>();
- mCallingPackageNamespaceSetMap.put(callingPackage, namespaceSet);
- }
- namespaceSet.add(namespace);
- // Record it in namespace to calling packages map
- Set<String> callingPackageSet = mNamespaceCallingPackageSetMap.get(namespace);
- if (callingPackageSet == null) {
- callingPackageSet = new ArraySet<>();
- }
- callingPackageSet.add(callingPackage);
- mNamespaceCallingPackageSetMap.put(namespace, callingPackageSet);
- }
- }
-
- private synchronized Set<String> getAffectedNamespaceSet(String failedPackage) {
- return mCallingPackageNamespaceSetMap.get(failedPackage);
- }
-
- private synchronized Set<String> getAllAffectedNamespaceSet() {
- return new HashSet<String>(mNamespaceCallingPackageSetMap.keySet());
- }
-
private synchronized Set<String> getCallingPackagesSet(String namespace) {
return mNamespaceCallingPackageSetMap.get(namespace);
}
@@ -894,26 +782,6 @@ public class RescueParty {
return now < lastResetTime + TimeUnit.MINUTES.toMillis(throttleDurationMin);
}
- private static int[] getAllUserIds() {
- int systemUserId = UserHandle.SYSTEM.getIdentifier();
- int[] userIds = { systemUserId };
- try {
- for (File file : FileUtils.listFilesOrEmpty(
- Environment.getDataSystemDeviceProtectedDirectory())) {
- try {
- final int userId = Integer.parseInt(file.getName());
- if (userId != systemUserId) {
- userIds = ArrayUtils.appendInt(userIds, userId);
- }
- } catch (NumberFormatException ignored) {
- }
- }
- } catch (Throwable t) {
- Slog.w(TAG, "Trouble discovering users", t);
- }
- return userIds;
- }
-
/**
* Hacky test to check if the device has an active USB connection, which is
* a good proxy for someone doing local development work.
diff --git a/packages/CrashRecovery/services/module/java/com/android/util/ArrayUtils.java b/packages/CrashRecovery/services/module/java/com/android/util/ArrayUtils.java
index 0b7b98603419..29ff7cced897 100644
--- a/packages/CrashRecovery/services/module/java/com/android/util/ArrayUtils.java
+++ b/packages/CrashRecovery/services/module/java/com/android/util/ArrayUtils.java
@@ -16,13 +16,8 @@
package android.util;
-import android.annotation.NonNull;
import android.annotation.Nullable;
-import java.io.File;
-import java.util.List;
-import java.util.Objects;
-
/**
* Copied over from frameworks/base/core/java/com/android/internal/util/ArrayUtils.java
*
@@ -30,25 +25,6 @@ import java.util.Objects;
*/
public class ArrayUtils {
private ArrayUtils() { /* cannot be instantiated */ }
- public static final File[] EMPTY_FILE = new File[0];
-
-
- /**
- * Return first index of {@code value} in {@code array}, or {@code -1} if
- * not found.
- */
- public static <T> int indexOf(@Nullable T[] array, T value) {
- if (array == null) return -1;
- for (int i = 0; i < array.length; i++) {
- if (Objects.equals(array[i], value)) return i;
- }
- return -1;
- }
-
- /** @hide */
- public static @NonNull File[] defeatNullable(@Nullable File[] val) {
- return (val != null) ? val : EMPTY_FILE;
- }
/**
* Checks if given array is null or has zero elements.
@@ -63,53 +39,4 @@ public class ArrayUtils {
public static boolean isEmpty(@Nullable byte[] array) {
return array == null || array.length == 0;
}
-
- /**
- * Converts from List of bytes to byte array
- * @param list
- * @return byte[]
- */
- public static byte[] toPrimitive(List<byte[]> list) {
- if (list.size() == 0) {
- return new byte[0];
- }
- int byteLen = list.get(0).length;
- byte[] array = new byte[list.size() * byteLen];
- for (int i = 0; i < list.size(); i++) {
- for (int j = 0; j < list.get(i).length; j++) {
- array[i * byteLen + j] = list.get(i)[j];
- }
- }
- return array;
- }
-
- /**
- * Adds value to given array if not already present, providing set-like
- * behavior.
- */
- public static @NonNull int[] appendInt(@Nullable int[] cur, int val) {
- return appendInt(cur, val, false);
- }
-
- /**
- * Adds value to given array.
- */
- public static @NonNull int[] appendInt(@Nullable int[] cur, int val,
- boolean allowDuplicates) {
- if (cur == null) {
- return new int[] { val };
- }
- final int n = cur.length;
- if (!allowDuplicates) {
- for (int i = 0; i < n; i++) {
- if (cur[i] == val) {
- return cur;
- }
- }
- }
- int[] ret = new int[n + 1];
- System.arraycopy(cur, 0, ret, 0, n);
- ret[n] = val;
- return ret;
- }
}
diff --git a/packages/CrashRecovery/services/module/java/com/android/util/FileUtils.java b/packages/CrashRecovery/services/module/java/com/android/util/FileUtils.java
index 9c73feeffb6c..d60a9b9847ca 100644
--- a/packages/CrashRecovery/services/module/java/com/android/util/FileUtils.java
+++ b/packages/CrashRecovery/services/module/java/com/android/util/FileUtils.java
@@ -16,7 +16,6 @@
package android.util;
-import android.annotation.NonNull;
import android.annotation.Nullable;
import java.io.BufferedInputStream;
@@ -115,14 +114,4 @@ public class FileUtils {
}
return false;
}
-
- /**
- * List the files in the directory or return empty file.
- *
- * @hide
- */
- public static @NonNull File[] listFilesOrEmpty(@Nullable File dir) {
- return (dir != null) ? ArrayUtils.defeatNullable(dir.listFiles())
- : ArrayUtils.EMPTY_FILE;
- }
}
diff --git a/packages/CrashRecovery/services/module/java/com/android/util/XmlUtils.java b/packages/CrashRecovery/services/module/java/com/android/util/XmlUtils.java
index 50823f5c9c34..488b531c2b8a 100644
--- a/packages/CrashRecovery/services/module/java/com/android/util/XmlUtils.java
+++ b/packages/CrashRecovery/services/module/java/com/android/util/XmlUtils.java
@@ -16,21 +16,10 @@
package android.util;
-import android.annotation.NonNull;
-import android.system.ErrnoException;
-import android.system.Os;
-
-import com.android.modules.utils.TypedXmlPullParser;
-
-import libcore.util.XmlObjectFactory;
-
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
-import java.io.BufferedInputStream;
-import java.io.FileInputStream;
import java.io.IOException;
-import java.io.InputStream;
/**
* Bits and pieces copied from hidden API of
@@ -40,8 +29,6 @@ import java.io.InputStream;
*/
public class XmlUtils {
- private static final String STRING_ARRAY_SEPARATOR = ":";
-
/** @hide */
public static final void beginDocument(XmlPullParser parser, String firstElementName)
throws XmlPullParserException, IOException {
@@ -76,44 +63,4 @@ public class XmlUtils {
}
}
}
-
- private static XmlPullParser newPullParser() {
- try {
- XmlPullParser parser = XmlObjectFactory.newXmlPullParser();
- parser.setFeature(XmlPullParser.FEATURE_PROCESS_DOCDECL, true);
- parser.setFeature(XmlPullParser.FEATURE_PROCESS_NAMESPACES, true);
- return parser;
- } catch (XmlPullParserException e) {
- throw new AssertionError();
- }
- }
-
- /** @hide */
- public static @NonNull TypedXmlPullParser resolvePullParser(@NonNull InputStream in)
- throws IOException {
- final byte[] magic = new byte[4];
- if (in instanceof FileInputStream) {
- try {
- Os.pread(((FileInputStream) in).getFD(), magic, 0, magic.length, 0);
- } catch (ErrnoException e) {
- throw e.rethrowAsIOException();
- }
- } else {
- if (!in.markSupported()) {
- in = new BufferedInputStream(in);
- }
- in.mark(8);
- in.read(magic);
- in.reset();
- }
-
- final TypedXmlPullParser xml;
- xml = (TypedXmlPullParser) newPullParser();
- try {
- xml.setInput(in, "UTF_8");
- } catch (XmlPullParserException e) {
- throw new IOException(e);
- }
- return xml;
- }
}
diff --git a/packages/PrintSpooler/Android.bp b/packages/PrintSpooler/Android.bp
index 000e20fb4280..5c5ec69a4140 100644
--- a/packages/PrintSpooler/Android.bp
+++ b/packages/PrintSpooler/Android.bp
@@ -47,20 +47,23 @@ android_library {
resource_dirs: ["res"],
srcs: [
"src/**/*.java",
- "src/com/android/printspooler/renderer/IPdfRenderer.aidl",
"src/com/android/printspooler/renderer/IPdfEditor.aidl",
+ "src/com/android/printspooler/renderer/IPdfRenderer.aidl",
],
platform_apis: true,
static_libs: [
- "android-support-v7-recyclerview",
+ "android-support-annotations",
"android-support-compat",
- "android-support-media-compat",
- "android-support-core-utils",
"android-support-core-ui",
+ "android-support-core-utils",
"android-support-fragment",
- "android-support-annotations",
+ "android-support-media-compat",
+ "android-support-v7-recyclerview",
"printspooler_aconfig_flags_java_lib",
],
+ flags_packages: [
+ "printspooler_aconfig_declarations",
+ ],
manifest: "AndroidManifest.xml",
}
diff --git a/packages/PrintSpooler/flags/flags.aconfig b/packages/PrintSpooler/flags/flags.aconfig
index 4a76dff405d0..5d45b3f0303d 100644
--- a/packages/PrintSpooler/flags/flags.aconfig
+++ b/packages/PrintSpooler/flags/flags.aconfig
@@ -7,3 +7,14 @@ flag {
description: "Log print job creation and state transitions."
bug: "385340868"
}
+
+flag {
+ name: "print_edge2edge"
+ namespace: "printing"
+ description: "Enable edge to edge in print spooler"
+ bug: "378652618"
+ is_fixed_read_only: true
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+} \ No newline at end of file
diff --git a/packages/PrintSpooler/res/layout/select_printer_activity.xml b/packages/PrintSpooler/res/layout/select_printer_activity.xml
index 681924b334d0..c5f19b1df6fd 100644
--- a/packages/PrintSpooler/res/layout/select_printer_activity.xml
+++ b/packages/PrintSpooler/res/layout/select_printer_activity.xml
@@ -15,6 +15,7 @@
-->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/select_printer"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent">
diff --git a/packages/PrintSpooler/res/values-night/themes.xml b/packages/PrintSpooler/res/values-night/themes.xml
index 76fa7b921e77..495bbcac1fb7 100644
--- a/packages/PrintSpooler/res/values-night/themes.xml
+++ b/packages/PrintSpooler/res/values-night/themes.xml
@@ -15,7 +15,7 @@
limitations under the License.
-->
-<resources>
+<resources xmlns:android="http://schemas.android.com/apk/res/android">
<style name="Theme.AddPrinterActivity" parent="@android:style/Theme.DeviceDefault.Dialog">
<item name="android:listSeparatorTextViewStyle">@style/ListSeparator</item>
<item name="android:textAppearanceListItemSecondary">@style/ListItemSecondary</item>
@@ -24,14 +24,14 @@
<style name="Theme.SelectPrinterActivity"
parent="android:style/Theme.DeviceDefault">
<item name="android:textAppearanceListItemSecondary">@style/ListItemSecondary</item>
- <item name="android:windowOptOutEdgeToEdgeEnforcement">true</item>
+ <item name="android:windowOptOutEdgeToEdgeEnforcement" android:featureFlag="!com.android.printspooler.flags.print_edge2edge">true</item>
</style>
<style name="Theme.PrintActivity" parent="@android:style/Theme.DeviceDefault">
<item name="android:windowIsTranslucent">true</item>
<item name="android:windowActionBar">false</item>
<item name="android:windowNoTitle">true</item>
- <item name="android:windowOptOutEdgeToEdgeEnforcement">true</item>
+ <item name="android:windowOptOutEdgeToEdgeEnforcement" android:featureFlag="!com.android.printspooler.flags.print_edge2edge">true</item>
</style>
</resources>
diff --git a/packages/PrintSpooler/res/values/themes.xml b/packages/PrintSpooler/res/values/themes.xml
index 22842f724036..5fcbbf53a57d 100644
--- a/packages/PrintSpooler/res/values/themes.xml
+++ b/packages/PrintSpooler/res/values/themes.xml
@@ -14,7 +14,7 @@
limitations under the License.
-->
-<resources>
+<resources xmlns:android="http://schemas.android.com/apk/res/android">
<style name="Theme.AddPrinterActivity" parent="@android:style/Theme.DeviceDefault.Light.Dialog">
<item name="android:listSeparatorTextViewStyle">@style/ListSeparator</item>
<item name="android:textAppearanceListItemSecondary">@style/ListItemSecondary</item>
@@ -24,7 +24,7 @@
parent="android:style/Theme.DeviceDefault.Light">
<item name="android:textAppearanceListItemSecondary">@style/ListItemSecondary</item>
<item name="android:windowLightStatusBar">true</item>
- <item name="android:windowOptOutEdgeToEdgeEnforcement">true</item>
+ <item name="android:windowOptOutEdgeToEdgeEnforcement" android:featureFlag="!com.android.printspooler.flags.print_edge2edge">true</item>
</style>
<style name="Theme.PrintActivity" parent="@android:style/Theme.DeviceDefault.Light">
@@ -32,7 +32,7 @@
<item name="android:windowActionBar">false</item>
<item name="android:windowNoTitle">true</item>
<item name="android:windowLightStatusBar">true</item>
- <item name="android:windowOptOutEdgeToEdgeEnforcement">true</item>
+ <item name="android:windowOptOutEdgeToEdgeEnforcement" android:featureFlag="!com.android.printspooler.flags.print_edge2edge">true</item>
</style>
</resources>
diff --git a/packages/PrintSpooler/src/com/android/printspooler/ui/SelectPrinterActivity.java b/packages/PrintSpooler/src/com/android/printspooler/ui/SelectPrinterActivity.java
index 74acf677918e..559d3ea2e830 100644
--- a/packages/PrintSpooler/src/com/android/printspooler/ui/SelectPrinterActivity.java
+++ b/packages/PrintSpooler/src/com/android/printspooler/ui/SelectPrinterActivity.java
@@ -66,10 +66,10 @@ import android.widget.Toast;
import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.printspooler.R;
+import com.android.printspooler.flags.Flags;
import java.util.ArrayList;
import java.util.List;
-
/**
* This is an activity for selecting a printer.
*/
@@ -134,6 +134,8 @@ public final class SelectPrinterActivity extends Activity implements
mPrinterRegistry = new PrinterRegistry(this, null, LOADER_ID_PRINT_REGISTRY,
LOADER_ID_PRINT_REGISTRY_INT);
+ findViewById(R.id.select_printer).setFitsSystemWindows(Flags.printEdge2edge());
+
// Hook up the list view.
mListView = findViewById(android.R.id.list);
final DestinationAdapter adapter = new DestinationAdapter();
diff --git a/packages/PrintSpooler/src/com/android/printspooler/widget/PrintContentView.java b/packages/PrintSpooler/src/com/android/printspooler/widget/PrintContentView.java
index 6ecffa4752cf..720d5b1f4e35 100644
--- a/packages/PrintSpooler/src/com/android/printspooler/widget/PrintContentView.java
+++ b/packages/PrintSpooler/src/com/android/printspooler/widget/PrintContentView.java
@@ -25,6 +25,7 @@ import android.view.ViewGroup;
import android.view.inputmethod.InputMethodManager;
import com.android.printspooler.R;
+import com.android.printspooler.flags.Flags;
/**
* This class is a layout manager for the print screen. It has a sliding
@@ -93,6 +94,7 @@ public final class PrintContentView extends ViewGroup implements View.OnClickLis
// The options view is sliding under the static header but appears
// after it in the layout, so we will draw in opposite order.
setChildrenDrawingOrderEnabled(true);
+ setFitsSystemWindows(Flags.printEdge2edge());
}
public void setOptionsStateChangeListener(OptionsStateChangeListener listener) {
@@ -148,6 +150,7 @@ public final class PrintContentView extends ViewGroup implements View.OnClickLis
mExpandCollapseHandle = findViewById(R.id.expand_collapse_handle);
mExpandCollapseIcon = findViewById(R.id.expand_collapse_icon);
+ mOptionsContainer.setFitsSystemWindows(Flags.printEdge2edge());
mExpandCollapseHandle.setOnClickListener(this);
mSummaryContent.setOnClickListener(this);
@@ -262,7 +265,7 @@ public final class PrintContentView extends ViewGroup implements View.OnClickLis
}
// The content host can grow vertically as much as needed - we will be covering it.
- final int hostHeightMeasureSpec = MeasureSpec.makeMeasureSpec(MeasureSpec.UNSPECIFIED, 0);
+ final int hostHeightMeasureSpec = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED);
measureChild(mEmbeddedContentContainer, widthMeasureSpec, hostHeightMeasureSpec);
setMeasuredDimension(resolveSize(MeasureSpec.getSize(widthMeasureSpec), widthMeasureSpec),
@@ -271,25 +274,43 @@ public final class PrintContentView extends ViewGroup implements View.OnClickLis
@Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
- mStaticContent.layout(left, top, right, mStaticContent.getMeasuredHeight());
+ final int childLeft;
+ final int childRight;
+ final int childTop;
+ if (Flags.printEdge2edge()) {
+ childLeft = left + mPaddingLeft;
+ childRight = right - mPaddingRight;
+ childTop = top + mPaddingTop;
+ } else {
+ childLeft = left;
+ childRight = right;
+ childTop = top;
+ }
+ mStaticContent.layout(childLeft, childTop, childRight,
+ mStaticContent.getMeasuredHeight() + (Flags.printEdge2edge() ? mPaddingTop : 0));
if (mSummaryContent.getVisibility() != View.GONE) {
- mSummaryContent.layout(left, mStaticContent.getMeasuredHeight(), right,
- mStaticContent.getMeasuredHeight() + mSummaryContent.getMeasuredHeight());
+ mSummaryContent.layout(childLeft,
+ (Flags.printEdge2edge() ? mStaticContent.getBottom()
+ : mStaticContent.getMeasuredHeight()), childRight,
+ (Flags.printEdge2edge() ? mStaticContent.getBottom()
+ : mStaticContent.getMeasuredHeight())
+ + mSummaryContent.getMeasuredHeight());
}
- final int dynContentTop = mStaticContent.getMeasuredHeight() + mCurrentOptionsOffsetY;
+ final int dynContentTop = mStaticContent.getBottom() + mCurrentOptionsOffsetY;
final int dynContentBottom = dynContentTop + mDynamicContent.getMeasuredHeight();
- mDynamicContent.layout(left, dynContentTop, right, dynContentBottom);
+ mDynamicContent.layout(childLeft, dynContentTop, childRight, dynContentBottom);
MarginLayoutParams params = (MarginLayoutParams) mPrintButton.getLayoutParams();
final int printButtonLeft;
if (getLayoutDirection() == View.LAYOUT_DIRECTION_LTR) {
- printButtonLeft = right - mPrintButton.getMeasuredWidth() - params.getMarginStart();
+ printButtonLeft = childRight - mPrintButton.getMeasuredWidth()
+ - params.getMarginStart();
} else {
- printButtonLeft = left + params.getMarginStart();
+ printButtonLeft = childLeft + params.getMarginStart();
}
final int printButtonTop = dynContentBottom - mPrintButton.getMeasuredHeight() / 2;
final int printButtonRight = printButtonLeft + mPrintButton.getMeasuredWidth();
@@ -297,11 +318,13 @@ public final class PrintContentView extends ViewGroup implements View.OnClickLis
mPrintButton.layout(printButtonLeft, printButtonTop, printButtonRight, printButtonBottom);
- final int embContentTop = mStaticContent.getMeasuredHeight() + mClosedOptionsOffsetY
- + mDynamicContent.getMeasuredHeight();
- final int embContentBottom = embContentTop + mEmbeddedContentContainer.getMeasuredHeight();
+ final int embContentTop = (Flags.printEdge2edge() ? mPaddingTop : 0)
+ + mStaticContent.getMeasuredHeight()
+ + mClosedOptionsOffsetY + mDynamicContent.getMeasuredHeight();
+ final int embContentBottom = embContentTop + mEmbeddedContentContainer.getMeasuredHeight()
+ - (Flags.printEdge2edge() ? mPaddingBottom : 0);
- mEmbeddedContentContainer.layout(left, embContentTop, right, embContentBottom);
+ mEmbeddedContentContainer.layout(childLeft, embContentTop, childRight, embContentBottom);
}
@Override
diff --git a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/framework/common/BytesFormatter.kt b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/framework/common/BytesFormatter.kt
index 5b7e2a86135a..e6cc8a80ee38 100644
--- a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/framework/common/BytesFormatter.kt
+++ b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/framework/common/BytesFormatter.kt
@@ -24,6 +24,7 @@ import android.icu.text.NumberFormat
import android.icu.text.UnicodeSet
import android.icu.text.UnicodeSetSpanner
import android.icu.util.Measure
+import android.text.BidiFormatter
import android.text.format.Formatter
import android.text.format.Formatter.RoundedBytesResult
import java.math.BigDecimal
@@ -40,11 +41,17 @@ class BytesFormatter(resources: Resources) {
constructor(context: Context) : this(context.resources)
private val locale = resources.configuration.locales[0]
+ private val bidiFormatter = BidiFormatter.getInstance(locale)
fun format(bytes: Long, useCase: UseCase): String {
val rounded = RoundedBytesResult.roundBytes(bytes, useCase.flag)
val numberFormatter = getNumberFormatter(rounded.fractionDigits)
- return numberFormatter.formatRoundedBytesResult(rounded)
+ val formattedString = numberFormatter.formatRoundedBytesResult(rounded)
+ return if (useCase == UseCase.FileSize) {
+ formattedString.bidiWrap()
+ } else {
+ formattedString
+ }
}
fun formatWithUnits(bytes: Long, useCase: UseCase): Result {
@@ -74,6 +81,14 @@ class BytesFormatter(resources: Resources) {
}
}
+ /** Wraps the source string in bidi formatting characters in RTL locales. */
+ private fun String.bidiWrap(): String =
+ if (bidiFormatter.isRtlContext) {
+ bidiFormatter.unicodeWrap(this)
+ } else {
+ this
+ }
+
private companion object {
fun String.removeFirst(removed: String): String =
SPACES_AND_CONTROLS.trim(replaceFirst(removed, "")).toString()
diff --git a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/model/app/AppStorageRepository.kt b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/model/app/AppStorageRepository.kt
new file mode 100644
index 000000000000..6fd470c1e7aa
--- /dev/null
+++ b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/model/app/AppStorageRepository.kt
@@ -0,0 +1,92 @@
+/*
+ * Copyright (C) 2025 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.spaprivileged.model.app
+
+import android.content.Context
+import android.content.pm.ApplicationInfo
+import android.util.Log
+import com.android.settingslib.spaprivileged.framework.common.BytesFormatter
+import com.android.settingslib.spaprivileged.framework.common.storageStatsManager
+
+/** A repository interface for accessing and formatting app storage information. */
+interface AppStorageRepository {
+ /**
+ * Formats the size of an application into a human-readable string.
+ *
+ * This function retrieves the total size of the application, including APK file and its
+ * associated data.
+ *
+ * This function takes an [ApplicationInfo] object as input and returns a formatted string
+ * representing the size of the application. The size is formatted in units like kB, MB, GB,
+ * etc.
+ *
+ * @param app The [ApplicationInfo] object representing the application.
+ * @return A formatted string representing the size of the application.
+ */
+ fun formatSize(app: ApplicationInfo): String
+
+ /**
+ * Formats the size about an application into a human-readable string.
+ *
+ * @param sizeBytes The size in bytes to format.
+ * @return A formatted string representing the size about application.
+ */
+ fun formatSizeBytes(sizeBytes: Long): String
+
+ /**
+ * Calculates the size of an application in bytes.
+ *
+ * This function retrieves the total size of the application, including APK file and its
+ * associated data.
+ *
+ * @param app The [ApplicationInfo] object representing the application.
+ * @return The total size of the application in bytes, or null if the size could not be
+ * determined.
+ */
+ fun calculateSizeBytes(app: ApplicationInfo): Long?
+}
+
+class AppStorageRepositoryImpl(context: Context) : AppStorageRepository {
+ private val storageStatsManager = context.storageStatsManager
+ private val bytesFormatter = BytesFormatter(context)
+
+ override fun formatSize(app: ApplicationInfo): String {
+ val sizeBytes = calculateSizeBytes(app)
+ return if (sizeBytes != null) formatSizeBytes(sizeBytes) else ""
+ }
+
+ override fun formatSizeBytes(sizeBytes: Long): String =
+ bytesFormatter.format(sizeBytes, BytesFormatter.UseCase.FileSize)
+
+ override fun calculateSizeBytes(app: ApplicationInfo): Long? =
+ try {
+ val stats =
+ storageStatsManager.queryStatsForPackage(
+ app.storageUuid,
+ app.packageName,
+ app.userHandle,
+ )
+ stats.codeBytes + stats.dataBytes
+ } catch (e: Exception) {
+ Log.w(TAG, "Failed to query stats", e)
+ null
+ }
+
+ companion object {
+ private const val TAG = "AppStorageRepository"
+ }
+}
diff --git a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/AppStorageSize.kt b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/AppStorageSize.kt
index 7a4f81cc1321..7c98e9cd813b 100644
--- a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/AppStorageSize.kt
+++ b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/AppStorageSize.kt
@@ -16,42 +16,30 @@
package com.android.settingslib.spaprivileged.template.app
-import android.content.Context
import android.content.pm.ApplicationInfo
-import android.text.format.Formatter
-import android.util.Log
+import androidx.annotation.VisibleForTesting
import androidx.compose.runtime.Composable
import androidx.compose.runtime.State
import androidx.compose.runtime.remember
-import androidx.compose.ui.platform.LocalContext
import androidx.lifecycle.compose.collectAsStateWithLifecycle
-import com.android.settingslib.spaprivileged.framework.common.storageStatsManager
+import com.android.settingslib.spa.framework.compose.rememberContext
import com.android.settingslib.spaprivileged.framework.compose.placeholder
-import com.android.settingslib.spaprivileged.model.app.userHandle
+import com.android.settingslib.spaprivileged.model.app.AppStorageRepository
+import com.android.settingslib.spaprivileged.model.app.AppStorageRepositoryImpl
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.flow
import kotlinx.coroutines.flow.flowOn
-private const val TAG = "AppStorageSize"
-
@Composable
-fun ApplicationInfo.getStorageSize(): State<String> {
- val context = LocalContext.current
- return remember(this) {
- flow {
- val sizeBytes = calculateSizeBytes(context)
- this.emit(if (sizeBytes != null) Formatter.formatFileSize(context, sizeBytes) else "")
- }.flowOn(Dispatchers.IO)
- }.collectAsStateWithLifecycle(initialValue = placeholder())
-}
+fun ApplicationInfo.getStorageSize(): State<String> =
+ getStorageSize(rememberContext(::AppStorageRepositoryImpl))
-fun ApplicationInfo.calculateSizeBytes(context: Context): Long? {
- val storageStatsManager = context.storageStatsManager
- return try {
- val stats = storageStatsManager.queryStatsForPackage(storageUuid, packageName, userHandle)
- stats.codeBytes + stats.dataBytes
- } catch (e: Exception) {
- Log.w(TAG, "Failed to query stats: $e")
- null
- }
+@VisibleForTesting
+@Composable
+fun ApplicationInfo.getStorageSize(appStorageRepository: AppStorageRepository): State<String> {
+ val app = this
+ return remember(app) {
+ flow { emit(appStorageRepository.formatSize(app)) }.flowOn(Dispatchers.Default)
+ }
+ .collectAsStateWithLifecycle(initialValue = placeholder())
}
diff --git a/packages/SettingsLib/SpaPrivileged/tests/src/com/android/settingslib/spaprivileged/model/app/AppStorageRepositoryTest.kt b/packages/SettingsLib/SpaPrivileged/tests/src/com/android/settingslib/spaprivileged/model/app/AppStorageRepositoryTest.kt
new file mode 100644
index 000000000000..e8ec974bb0b8
--- /dev/null
+++ b/packages/SettingsLib/SpaPrivileged/tests/src/com/android/settingslib/spaprivileged/model/app/AppStorageRepositoryTest.kt
@@ -0,0 +1,94 @@
+/*
+ * Copyright (C) 2025 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.spaprivileged.model.app
+
+import android.app.usage.StorageStats
+import android.app.usage.StorageStatsManager
+import android.content.Context
+import android.content.pm.ApplicationInfo
+import android.content.pm.PackageManager.NameNotFoundException
+import androidx.test.core.app.ApplicationProvider
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import com.android.settingslib.spaprivileged.framework.common.storageStatsManager
+import com.google.common.truth.Truth.assertThat
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.kotlin.doReturn
+import org.mockito.kotlin.doThrow
+import org.mockito.kotlin.mock
+import org.mockito.kotlin.spy
+import org.mockito.kotlin.stub
+import java.util.UUID
+
+@RunWith(AndroidJUnit4::class)
+class AppStorageRepositoryTest {
+ private val app = ApplicationInfo().apply { storageUuid = UUID.randomUUID() }
+
+ private val mockStorageStatsManager =
+ mock<StorageStatsManager> {
+ on { queryStatsForPackage(app.storageUuid, app.packageName, app.userHandle) } doReturn
+ STATS
+ }
+
+ private val context: Context =
+ spy(ApplicationProvider.getApplicationContext()) {
+ on { storageStatsManager } doReturn mockStorageStatsManager
+ }
+
+ private val repository = AppStorageRepositoryImpl(context)
+
+ @Test
+ fun calculateSizeBytes() {
+ val sizeBytes = repository.calculateSizeBytes(app)
+
+ assertThat(sizeBytes).isEqualTo(120)
+ }
+
+ @Test
+ fun formatSize() {
+ val fileSize = repository.formatSize(app)
+
+ assertThat(fileSize).isEqualTo("120 byte")
+ }
+
+ @Test
+ fun formatSize_throwException() {
+ mockStorageStatsManager.stub {
+ on { queryStatsForPackage(app.storageUuid, app.packageName, app.userHandle) } doThrow
+ NameNotFoundException()
+ }
+
+ val fileSize = repository.formatSize(app)
+
+ assertThat(fileSize).isEqualTo("")
+ }
+
+ @Test
+ fun formatSizeBytes() {
+ val fileSize = repository.formatSizeBytes(120)
+
+ assertThat(fileSize).isEqualTo("120 byte")
+ }
+
+ companion object {
+ private val STATS =
+ StorageStats().apply {
+ codeBytes = 100
+ dataBytes = 20
+ }
+ }
+}
diff --git a/packages/SettingsLib/SpaPrivileged/tests/src/com/android/settingslib/spaprivileged/template/app/AppStorageSizeTest.kt b/packages/SettingsLib/SpaPrivileged/tests/src/com/android/settingslib/spaprivileged/template/app/AppStorageSizeTest.kt
index 60f3d0ce1be3..4f42c8254c39 100644
--- a/packages/SettingsLib/SpaPrivileged/tests/src/com/android/settingslib/spaprivileged/template/app/AppStorageSizeTest.kt
+++ b/packages/SettingsLib/SpaPrivileged/tests/src/com/android/settingslib/spaprivileged/template/app/AppStorageSizeTest.kt
@@ -16,98 +16,37 @@
package com.android.settingslib.spaprivileged.template.app
-import android.app.usage.StorageStats
-import android.app.usage.StorageStatsManager
-import android.content.Context
import android.content.pm.ApplicationInfo
-import android.content.pm.PackageManager.NameNotFoundException
-import androidx.compose.runtime.CompositionLocalProvider
-import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.test.junit4.createComposeRule
-import androidx.test.core.app.ApplicationProvider
import androidx.test.ext.junit.runners.AndroidJUnit4
import com.android.settingslib.spa.framework.compose.stateOf
-import com.android.settingslib.spaprivileged.framework.common.storageStatsManager
-import com.android.settingslib.spaprivileged.model.app.userHandle
-import java.util.UUID
-import org.junit.Before
+import com.android.settingslib.spaprivileged.model.app.AppStorageRepository
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
-import org.mockito.Mock
-import org.mockito.Spy
-import org.mockito.junit.MockitoJUnit
-import org.mockito.junit.MockitoRule
-import org.mockito.kotlin.whenever
+import org.mockito.kotlin.doReturn
+import org.mockito.kotlin.mock
+import java.util.UUID
@RunWith(AndroidJUnit4::class)
class AppStorageSizeTest {
- @get:Rule
- val mockito: MockitoRule = MockitoJUnit.rule()
-
- @get:Rule
- val composeTestRule = createComposeRule()
+ @get:Rule val composeTestRule = createComposeRule()
- @Spy
- private val context: Context = ApplicationProvider.getApplicationContext()
-
- @Mock
- private lateinit var storageStatsManager: StorageStatsManager
-
- private val app = ApplicationInfo().apply {
- storageUuid = UUID.randomUUID()
- }
+ private val app = ApplicationInfo().apply { storageUuid = UUID.randomUUID() }
- @Before
- fun setUp() {
- whenever(context.storageStatsManager).thenReturn(storageStatsManager)
- whenever(
- storageStatsManager.queryStatsForPackage(
- app.storageUuid,
- app.packageName,
- app.userHandle,
- )
- ).thenReturn(STATS)
- }
+ private val mockAppStorageRepository =
+ mock<AppStorageRepository> { on { formatSize(app) } doReturn SIZE }
@Test
fun getStorageSize() {
var storageSize = stateOf("")
- composeTestRule.setContent {
- CompositionLocalProvider(LocalContext provides context) {
- storageSize = app.getStorageSize()
- }
- }
-
- composeTestRule.waitUntil { storageSize.value == "120 B" }
- }
-
- @Test
- fun getStorageSize_throwException() {
- var storageSize = stateOf("Computing")
- whenever(
- storageStatsManager.queryStatsForPackage(
- app.storageUuid,
- app.packageName,
- app.userHandle,
- )
- ).thenThrow(NameNotFoundException())
-
- composeTestRule.setContent {
- CompositionLocalProvider(LocalContext provides context) {
- storageSize = app.getStorageSize()
- }
- }
+ composeTestRule.setContent { storageSize = app.getStorageSize(mockAppStorageRepository) }
- composeTestRule.waitUntil { storageSize.value == "" }
+ composeTestRule.waitUntil { storageSize.value == SIZE }
}
- companion object {
- private val STATS = StorageStats().apply {
- codeBytes = 100
- dataBytes = 20
- cacheBytes = 3
- }
+ private companion object {
+ const val SIZE = "120 kB"
}
}
diff --git a/packages/Shell/AndroidManifest.xml b/packages/Shell/AndroidManifest.xml
index 46bd88fcdc93..4448000324d8 100644
--- a/packages/Shell/AndroidManifest.xml
+++ b/packages/Shell/AndroidManifest.xml
@@ -169,6 +169,7 @@
<!-- Internal permissions granted to the shell. -->
<uses-permission android:name="android.permission.FORCE_BACK" />
<uses-permission android:name="android.permission.BATTERY_STATS" />
+ <uses-permission android:name="android.permission.ACCESS_FINE_POWER_MONITORS" />
<uses-permission android:name="android.permission.PACKAGE_USAGE_STATS" />
<uses-permission android:name="android.permission.REPORT_USAGE_STATS" />
<uses-permission android:name="android.permission.INTERNAL_SYSTEM_WINDOW" />
diff --git a/packages/SystemUI/Android.bp b/packages/SystemUI/Android.bp
index 6b2449fdaa49..c3c5e874b907 100644
--- a/packages/SystemUI/Android.bp
+++ b/packages/SystemUI/Android.bp
@@ -519,6 +519,7 @@ android_library {
"androidx.activity_activity-compose",
"androidx.compose.animation_animation-graphics",
"androidx.lifecycle_lifecycle-viewmodel-compose",
+ "kairos",
],
libs: [
"keepanno-annotations",
@@ -739,6 +740,7 @@ android_library {
"PlatformMotionTesting",
"SystemUICustomizationTestUtils",
"androidx.compose.runtime_runtime",
+ "kairos",
"kosmos",
"testables",
"androidx.test.rules",
diff --git a/packages/SystemUI/aconfig/accessibility.aconfig b/packages/SystemUI/aconfig/accessibility.aconfig
index b5eba08c8f87..72b3d08396c6 100644
--- a/packages/SystemUI/aconfig/accessibility.aconfig
+++ b/packages/SystemUI/aconfig/accessibility.aconfig
@@ -125,3 +125,13 @@ flag {
description: "Update hearing device icon in floating menu according to the connection status."
bug: "357882387"
}
+
+flag {
+ name: "floating_menu_notify_targets_changed_on_strict_diff"
+ namespace: "accessibility"
+ description: "Only notify listeners that the list of accessibility targets has changed if the lists are not identical."
+ bug: "376473165"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
diff --git a/packages/SystemUI/aconfig/systemui.aconfig b/packages/SystemUI/aconfig/systemui.aconfig
index 9982710737ce..153f89284587 100644
--- a/packages/SystemUI/aconfig/systemui.aconfig
+++ b/packages/SystemUI/aconfig/systemui.aconfig
@@ -84,6 +84,16 @@ flag {
}
flag {
+ name: "notification_ambient_suppression_after_inflation"
+ namespace: "systemui"
+ description: "Move the DND visual effects filter to the finalize stage of the pipeline when it is doze-dependent, but keep it in the pre-group stage when it is doze-independent."
+ bug: "373411431"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
+
+flag {
name: "notification_over_expansion_clipping_fix"
namespace: "systemui"
description: "Fix NSSL clipping when over-expanding; fixes split shade bug."
@@ -372,6 +382,13 @@ flag {
}
flag {
+ name: "status_bar_mobile_icon_kairos"
+ namespace: "systemui"
+ description: "Refactors the mobile connection icon in the status bar to use the Kairos library"
+ bug: "383172066"
+}
+
+flag {
name: "status_bar_monochrome_icons_fix"
namespace: "systemui"
description: "Fixes the status bar icon size when drawing InsetDrawables (ie. monochrome icons)"
@@ -382,14 +399,6 @@ flag {
}
flag {
- name: "status_bar_screen_sharing_chips"
- namespace: "systemui"
- description: "Show chips on the left side of the status bar when a user is screen sharing, "
- "recording, or casting"
- bug: "332662551"
-}
-
-flag {
name: "status_bar_show_audio_only_projection_chip"
namespace: "systemui"
description: "Show chip on the left side of the status bar when a user is only sharing *audio* "
@@ -1910,3 +1919,13 @@ flag {
description: "Special UI treatment for magic actions"
bug: "383567383"
}
+
+flag {
+ name: "show_audio_sharing_slider_in_volume_panel"
+ namespace: "cross_device_experiences"
+ description: "Show two sliders in volume panel when audio sharing."
+ bug: "336183611"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
diff --git a/packages/SystemUI/animation/src/com/android/systemui/animation/ActivityTransitionAnimator.kt b/packages/SystemUI/animation/src/com/android/systemui/animation/ActivityTransitionAnimator.kt
index b0c7ac09551a..c8d3430bf54b 100644
--- a/packages/SystemUI/animation/src/com/android/systemui/animation/ActivityTransitionAnimator.kt
+++ b/packages/SystemUI/animation/src/com/android/systemui/animation/ActivityTransitionAnimator.kt
@@ -232,19 +232,21 @@ constructor(
private val lifecycleListener =
object : Listener {
override fun onTransitionAnimationStart() {
- listeners.forEach { it.onTransitionAnimationStart() }
+ LinkedHashSet(listeners).forEach { it.onTransitionAnimationStart() }
}
override fun onTransitionAnimationEnd() {
- listeners.forEach { it.onTransitionAnimationEnd() }
+ LinkedHashSet(listeners).forEach { it.onTransitionAnimationEnd() }
}
override fun onTransitionAnimationProgress(linearProgress: Float) {
- listeners.forEach { it.onTransitionAnimationProgress(linearProgress) }
+ LinkedHashSet(listeners).forEach {
+ it.onTransitionAnimationProgress(linearProgress)
+ }
}
override fun onTransitionAnimationCancelled() {
- listeners.forEach { it.onTransitionAnimationCancelled() }
+ LinkedHashSet(listeners).forEach { it.onTransitionAnimationCancelled() }
}
}
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/DraggableHandler.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/DraggableHandler.kt
index 2ca846424d93..633328a836e3 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/DraggableHandler.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/DraggableHandler.kt
@@ -16,62 +16,29 @@
package com.android.compose.animation.scene
+import androidx.compose.foundation.OverscrollEffect
import androidx.compose.foundation.gestures.Orientation
import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.input.nestedscroll.NestedScrollSource
+import androidx.compose.ui.input.pointer.PointerInputChange
+import androidx.compose.ui.input.pointer.PointerType
+import androidx.compose.ui.unit.Velocity
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.round
import androidx.compose.ui.util.fastCoerceIn
import com.android.compose.animation.scene.content.Content
import com.android.compose.animation.scene.content.state.TransitionState.Companion.DistanceUnspecified
-import com.android.compose.nestedscroll.OnStopScope
-import com.android.compose.nestedscroll.PriorityNestedScrollConnection
-import com.android.compose.nestedscroll.ScrollController
+import com.android.compose.animation.scene.effect.GestureEffect
+import com.android.compose.gesture.NestedDraggable
import com.android.compose.ui.util.SpaceVectorConverter
import kotlin.math.absoluteValue
-import kotlinx.coroutines.CompletableDeferred
-import kotlinx.coroutines.NonCancellable
import kotlinx.coroutines.launch
-import kotlinx.coroutines.withContext
-internal interface DraggableHandler {
- /**
- * Start a drag with the given [pointersDown] and [overSlop].
- *
- * The returned [DragController] should be used to continue or stop the drag.
- */
- fun onDragStarted(pointersDown: PointersInfo.PointersDown?, overSlop: Float): DragController
-}
-
-/**
- * The [DragController] provides control over the transition between two scenes through the [onDrag]
- * and [onStop] methods.
- */
-internal interface DragController {
- /**
- * Drag the current scene by [delta] pixels.
- *
- * @param delta The distance to drag the scene in pixels.
- * @return the consumed [delta]
- */
- fun onDrag(delta: Float): Float
-
- /**
- * Stop the current drag with the given [velocity].
- *
- * @param velocity The velocity of the drag when it stopped.
- * @return the consumed [velocity] when the animation complete
- */
- suspend fun onStop(velocity: Float): Float
-
- /** Cancels the current drag. */
- fun onCancel()
-}
-
-internal class DraggableHandlerImpl(
+internal class DraggableHandler(
internal val layoutImpl: SceneTransitionLayoutImpl,
internal val orientation: Orientation,
-) : DraggableHandler {
+ private val gestureEffectProvider: (ContentKey) -> GestureEffect,
+) : NestedDraggable {
/** The [DraggableHandler] can only have one active [DragController] at a time. */
private var dragController: DragControllerImpl? = null
@@ -92,20 +59,36 @@ internal class DraggableHandlerImpl(
internal val positionalThreshold
get() = with(layoutImpl.density) { 56.dp.toPx() }
+ /** The [OverscrollEffect] that should consume any overscroll on this draggable. */
+ internal val overscrollEffect: OverscrollEffect = DelegatingOverscrollEffect()
+
+ override fun shouldStartDrag(change: PointerInputChange): Boolean {
+ return layoutImpl.swipeDetector.detectSwipe(change)
+ }
+
+ override fun shouldConsumeNestedScroll(sign: Float): Boolean {
+ return this.enabled()
+ }
+
override fun onDragStarted(
- pointersDown: PointersInfo.PointersDown?,
- overSlop: Float,
- ): DragController {
- check(overSlop != 0f)
- val swipes = computeSwipes(pointersDown)
+ position: Offset,
+ sign: Float,
+ pointersDown: Int,
+ pointerType: PointerType?,
+ ): NestedDraggable.Controller {
+ check(sign != 0f)
+ val swipes = computeSwipes(position, pointersDown, pointerType)
val fromContent = layoutImpl.contentForUserActions()
swipes.updateSwipesResults(fromContent)
+ val upOrLeft = swipes.upOrLeftResult
+ val downOrRight = swipes.downOrRightResult
val result =
- (if (overSlop < 0f) swipes.upOrLeftResult else swipes.downOrRightResult)
- // As we were unable to locate a valid target scene, the initial SwipeAnimation
- // cannot be defined. Consequently, a simple NoOp Controller will be returned.
- ?: return NoOpDragController
+ when {
+ sign < 0 -> upOrLeft ?: downOrRight
+ sign >= 0f -> downOrRight ?: upOrLeft
+ else -> null
+ } ?: return NoOpDragController
val swipeAnimation = createSwipeAnimation(swipes, result)
return updateDragController(swipes, swipeAnimation)
@@ -143,20 +126,109 @@ internal class DraggableHandlerImpl(
)
}
- private fun computeSwipes(pointersDown: PointersInfo.PointersDown?): Swipes {
- val fromSource = pointersDown?.let { resolveSwipeSource(it.startedPosition) }
+ private fun computeSwipes(
+ position: Offset,
+ pointersDown: Int,
+ pointerType: PointerType?,
+ ): Swipes {
+ val fromSource = resolveSwipeSource(position)
return Swipes(
- upOrLeft = resolveSwipe(orientation, isUpOrLeft = true, pointersDown, fromSource),
- downOrRight = resolveSwipe(orientation, isUpOrLeft = false, pointersDown, fromSource),
+ upOrLeft =
+ resolveSwipe(orientation, isUpOrLeft = true, fromSource, pointersDown, pointerType),
+ downOrRight =
+ resolveSwipe(orientation, isUpOrLeft = false, fromSource, pointersDown, pointerType),
)
}
+
+ /**
+ * An implementation of [OverscrollEffect] that delegates to the correct content effect
+ * depending on the current scene/overlays and transition.
+ */
+ private inner class DelegatingOverscrollEffect :
+ OverscrollEffect, SpaceVectorConverter by SpaceVectorConverter(orientation) {
+ private var currentContent: ContentKey? = null
+ private var currentDelegate: GestureEffect? = null
+ set(value) {
+ field?.let { delegate ->
+ if (delegate.isInProgress) {
+ layoutImpl.animationScope.launch { delegate.ensureApplyToFlingIsCalled() }
+ }
+ }
+
+ field = value
+ }
+
+ override val isInProgress: Boolean
+ get() = currentDelegate?.isInProgress ?: false
+
+ override fun applyToScroll(
+ delta: Offset,
+ source: NestedScrollSource,
+ performScroll: (Offset) -> Offset,
+ ): Offset {
+ val available = delta.toFloat()
+ if (available == 0f) {
+ return performScroll(delta)
+ }
+
+ ensureDelegateIsNotNull(available)
+ val delegate = checkNotNull(currentDelegate)
+ return if (delegate.node.node.isAttached) {
+ delegate.applyToScroll(delta, source, performScroll)
+ } else {
+ performScroll(delta)
+ }
+ }
+
+ override suspend fun applyToFling(
+ velocity: Velocity,
+ performFling: suspend (Velocity) -> Velocity,
+ ) {
+ val available = velocity.toFloat()
+ if (available != 0f && isDrivingTransition) {
+ ensureDelegateIsNotNull(available)
+ }
+
+ // Note: we set currentDelegate and currentContent to null before calling performFling,
+ // which can suspend and take a lot of time.
+ val delegate = currentDelegate
+ currentDelegate = null
+ currentContent = null
+
+ if (delegate != null && delegate.node.node.isAttached) {
+ delegate.applyToFling(velocity, performFling)
+ } else {
+ performFling(velocity)
+ }
+ }
+
+ private fun ensureDelegateIsNotNull(direction: Float) {
+ require(direction != 0f)
+ if (isInProgress) {
+ return
+ }
+
+ val content =
+ if (isDrivingTransition) {
+ checkNotNull(dragController).swipeAnimation.contentByDirection(direction)
+ } else {
+ layoutImpl.contentForUserActions().key
+ }
+
+ if (content != currentContent) {
+ currentContent = content
+ currentDelegate = gestureEffectProvider(content)
+ }
+ }
+ }
}
private fun resolveSwipe(
orientation: Orientation,
isUpOrLeft: Boolean,
- pointersDown: PointersInfo.PointersDown?,
fromSource: SwipeSource.Resolved?,
+ pointersDown: Int,
+ pointerType: PointerType?,
): Swipe.Resolved {
return Swipe.Resolved(
direction =
@@ -175,28 +247,22 @@ private fun resolveSwipe(
SwipeDirection.Resolved.Down
}
},
- // If the number of pointers is not specified, 1 is assumed.
- pointerCount = pointersDown?.count ?: 1,
- // Resolves the pointer type only if all pointers are of the same type.
- pointersType = pointersDown?.countByType?.keys?.singleOrNull(),
+ pointerCount = pointersDown,
+ pointerType = pointerType,
fromSource = fromSource,
)
}
/** @param swipes The [Swipes] associated to the current gesture. */
private class DragControllerImpl(
- private val draggableHandler: DraggableHandlerImpl,
+ private val draggableHandler: DraggableHandler,
val swipes: Swipes,
var swipeAnimation: SwipeAnimation<*>,
-) : DragController, SpaceVectorConverter by SpaceVectorConverter(draggableHandler.orientation) {
+) :
+ NestedDraggable.Controller,
+ SpaceVectorConverter by SpaceVectorConverter(draggableHandler.orientation) {
val layoutState = draggableHandler.layoutImpl.state
- val overscrollableContent: OverscrollableContent =
- when (draggableHandler.orientation) {
- Orientation.Vertical -> draggableHandler.layoutImpl.verticalOverscrollableContent
- Orientation.Horizontal -> draggableHandler.layoutImpl.horizontalOverscrollableContent
- }
-
/**
* Whether this handle is active. If this returns false, calling [onDrag] and [onStop] will do
* nothing.
@@ -231,57 +297,25 @@ private class DragControllerImpl(
if (delta == 0f || !isDrivingTransition || initialAnimation.isAnimatingOffset()) {
return 0f
}
+
// swipeAnimation can change during the gesture, we want to always use the initial reference
// during the whole drag gesture.
- return dragWithOverscroll(delta, animation = initialAnimation)
- }
-
- private fun <T : ContentKey> dragWithOverscroll(
- delta: Float,
- animation: SwipeAnimation<T>,
- ): Float {
- require(delta != 0f) { "delta should not be 0" }
- var overscrollEffect = overscrollableContent.currentOverscrollEffect
-
- // If we're already overscrolling, continue with the current effect for a smooth finish.
- if (overscrollEffect == null || !overscrollEffect.isInProgress) {
- // Otherwise, determine the target content (toContent or fromContent) for the new
- // overscroll effect based on the gesture's direction.
- val content = animation.contentByDirection(delta)
- overscrollEffect = overscrollableContent.applyOverscrollEffectOn(content)
- }
-
- // TODO(b/378470603) Remove this check once NestedDraggable is used to handle drags.
- if (!overscrollEffect.node.node.isAttached) {
- return drag(delta, animation)
- }
-
- return overscrollEffect
- .applyToScroll(
- delta = delta.toOffset(),
- source = NestedScrollSource.UserInput,
- performScroll = {
- val preScrollAvailable = it.toFloat()
- drag(preScrollAvailable, animation).toOffset()
- },
- )
- .toFloat()
+ return drag(delta, animation = initialAnimation)
}
private fun <T : ContentKey> drag(delta: Float, animation: SwipeAnimation<T>): Float {
- if (delta == 0f) return 0f
-
val distance = animation.distance()
val previousOffset = animation.dragOffset
val desiredOffset = previousOffset + delta
- val desiredProgress = animation.computeProgress(desiredOffset)
// Note: the distance could be negative if fromContent is above or to the left of toContent.
val newOffset =
when {
- distance == DistanceUnspecified ||
- animation.contentTransition.isWithinProgressRange(desiredProgress) ->
- desiredOffset
+ distance == DistanceUnspecified -> {
+ // Consume everything so that we don't overscroll, this will be coerced later
+ // when the distance is defined.
+ delta
+ }
distance > 0f -> desiredOffset.fastCoerceIn(0f, distance)
else -> desiredOffset.fastCoerceIn(distance, 0f)
}
@@ -290,12 +324,8 @@ private class DragControllerImpl(
return newOffset - previousOffset
}
- override suspend fun onStop(velocity: Float): Float {
- // To ensure that any ongoing animation completes gracefully and avoids an undefined state,
- // we execute the actual `onStop` logic in a non-cancellable context. This prevents the
- // coroutine from being cancelled prematurely, which could interrupt the animation.
- // TODO(b/378470603) Remove this check once NestedDraggable is used to handle drags.
- return withContext(NonCancellable) { onStop(velocity, swipeAnimation) }
+ override suspend fun onDragStopped(velocity: Float, awaitFling: suspend () -> Unit): Float {
+ return onStop(velocity, swipeAnimation, awaitFling)
}
private suspend fun <T : ContentKey> onStop(
@@ -306,6 +336,7 @@ private class DragControllerImpl(
// callbacks (like onAnimationCompleted()) might incorrectly finish a new transition that
// replaced this one.
swipeAnimation: SwipeAnimation<T>,
+ awaitFling: suspend () -> Unit,
): Float {
// The state was changed since the drag started; don't do anything.
if (!isDrivingTransition || swipeAnimation.isAnimatingOffset()) {
@@ -337,33 +368,7 @@ private class DragControllerImpl(
fromContent
}
- val overscrollEffect = overscrollableContent.applyOverscrollEffectOn(targetContent)
-
- // TODO(b/378470603) Remove this check once NestedDraggable is used to handle drags.
- if (!overscrollEffect.node.node.isAttached) {
- return swipeAnimation.animateOffset(velocity, targetContent)
- }
-
- val overscrollCompletable = CompletableDeferred<Unit>()
- try {
- overscrollEffect.applyToFling(
- velocity = velocity.toVelocity(),
- performFling = {
- val velocityLeft = it.toFloat()
- swipeAnimation
- .animateOffset(
- velocityLeft,
- targetContent,
- overscrollCompletable = overscrollCompletable,
- )
- .toVelocity()
- },
- )
- } finally {
- overscrollCompletable.complete(Unit)
- }
-
- return velocity
+ return swipeAnimation.animateOffset(velocity, targetContent, awaitFling = awaitFling)
}
/**
@@ -408,10 +413,6 @@ private class DragControllerImpl(
isCloserToTarget()
}
}
-
- override fun onCancel() {
- swipeAnimation.contentTransition.coroutineScope.launch { onStop(velocity = 0f) }
- }
}
/** The [Swipe] associated to a given fromScene, startedPosition and pointersDown. */
@@ -453,15 +454,15 @@ internal class Swipes(val upOrLeft: Swipe.Resolved, val downOrRight: Swipe.Resol
(actionSwipe.fromSource != null &&
actionSwipe.fromSource != swipe.fromSource) ||
// The action requires a specific pointerType.
- (actionSwipe.pointersType != null &&
- actionSwipe.pointersType != swipe.pointersType)
+ (actionSwipe.pointerType != null &&
+ actionSwipe.pointerType != swipe.pointerType)
) {
// This action is not eligible.
return@forEach
}
val sameFromSource = actionSwipe.fromSource == swipe.fromSource
- val samePointerType = actionSwipe.pointersType == swipe.pointersType
+ val samePointerType = actionSwipe.pointerType == swipe.pointerType
// Prioritize actions with a perfect match.
if (sameFromSource && samePointerType) {
return actionResult
@@ -496,82 +497,6 @@ internal class Swipes(val upOrLeft: Swipe.Resolved, val downOrRight: Swipe.Resol
}
}
-internal class NestedScrollHandlerImpl(
- private val draggableHandler: DraggableHandlerImpl,
- private val pointersInfoOwner: PointersInfoOwner,
-) {
- val connection: PriorityNestedScrollConnection = nestedScrollConnection()
-
- private fun nestedScrollConnection(): PriorityNestedScrollConnection {
- var lastPointersDown: PointersInfo.PointersDown? = null
-
- return PriorityNestedScrollConnection(
- orientation = draggableHandler.orientation,
- canStartPreScroll = { _, _, _ -> false },
- canStartPostScroll = { offsetAvailable, _, _ ->
- if (offsetAvailable == 0f) return@PriorityNestedScrollConnection false
-
- lastPointersDown =
- when (val info = pointersInfoOwner.pointersInfo()) {
- PointersInfo.MouseWheel -> {
- // Do not support mouse wheel interactions
- return@PriorityNestedScrollConnection false
- }
-
- is PointersInfo.PointersDown -> info
- null -> null
- }
-
- draggableHandler.layoutImpl
- .contentForUserActions()
- .shouldEnableSwipes(draggableHandler.orientation)
- },
- onStart = { firstScroll ->
- scrollController(
- dragController =
- draggableHandler.onDragStarted(
- pointersDown = lastPointersDown,
- overSlop = firstScroll,
- ),
- pointersInfoOwner = pointersInfoOwner,
- )
- },
- )
- }
-}
-
-private fun scrollController(
- dragController: DragController,
- pointersInfoOwner: PointersInfoOwner,
-): ScrollController {
- return object : ScrollController {
- override fun onScroll(deltaScroll: Float, source: NestedScrollSource): Float {
- if (pointersInfoOwner.pointersInfo() == PointersInfo.MouseWheel) {
- // Do not support mouse wheel interactions
- return 0f
- }
-
- return dragController.onDrag(delta = deltaScroll)
- }
-
- override suspend fun OnStopScope.onStop(initialVelocity: Float): Float {
- return dragController.onStop(velocity = initialVelocity)
- }
-
- override fun onCancel() {
- dragController.onCancel()
- }
-
- /**
- * We need to maintain scroll priority even if the scene transition can no longer consume
- * the scroll gesture to allow us to return to the previous scene.
- */
- override fun canCancelScroll(available: Float, consumed: Float) = false
-
- override fun canStopOnPreFling() = true
- }
-}
-
/**
* The number of pixels below which there won't be a visible difference in the transition and from
* which the animation can stop.
@@ -580,12 +505,8 @@ private fun scrollController(
// account instead.
internal const val OffsetVisibilityThreshold = 0.5f
-private object NoOpDragController : DragController {
+private object NoOpDragController : NestedDraggable.Controller {
override fun onDrag(delta: Float) = 0f
- override suspend fun onStop(velocity: Float) = 0f
-
- override fun onCancel() {
- /* do nothing */
- }
+ override suspend fun onDragStopped(velocity: Float, awaitFling: suspend () -> Unit): Float = 0f
}
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/MultiPointerDraggable.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/MultiPointerDraggable.kt
deleted file mode 100644
index 89320f1303e5..000000000000
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/MultiPointerDraggable.kt
+++ /dev/null
@@ -1,678 +0,0 @@
-/*
- * Copyright (C) 2023 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.compose.animation.scene
-
-import androidx.annotation.VisibleForTesting
-import androidx.compose.foundation.gestures.Orientation
-import androidx.compose.foundation.gestures.awaitHorizontalTouchSlopOrCancellation
-import androidx.compose.foundation.gestures.awaitVerticalTouchSlopOrCancellation
-import androidx.compose.runtime.Stable
-import androidx.compose.ui.Modifier
-import androidx.compose.ui.geometry.Offset
-import androidx.compose.ui.input.nestedscroll.NestedScrollConnection
-import androidx.compose.ui.input.nestedscroll.NestedScrollDispatcher
-import androidx.compose.ui.input.nestedscroll.NestedScrollSource
-import androidx.compose.ui.input.pointer.AwaitPointerEventScope
-import androidx.compose.ui.input.pointer.PointerEvent
-import androidx.compose.ui.input.pointer.PointerEventPass
-import androidx.compose.ui.input.pointer.PointerEventType
-import androidx.compose.ui.input.pointer.PointerId
-import androidx.compose.ui.input.pointer.PointerInputChange
-import androidx.compose.ui.input.pointer.PointerInputScope
-import androidx.compose.ui.input.pointer.PointerType
-import androidx.compose.ui.input.pointer.SuspendingPointerInputModifierNode
-import androidx.compose.ui.input.pointer.changedToDown
-import androidx.compose.ui.input.pointer.changedToUpIgnoreConsumed
-import androidx.compose.ui.input.pointer.positionChange
-import androidx.compose.ui.input.pointer.positionChangeIgnoreConsumed
-import androidx.compose.ui.input.pointer.util.VelocityTracker
-import androidx.compose.ui.input.pointer.util.addPointerInputChange
-import androidx.compose.ui.node.CompositionLocalConsumerModifierNode
-import androidx.compose.ui.node.DelegatingNode
-import androidx.compose.ui.node.ModifierNodeElement
-import androidx.compose.ui.node.PointerInputModifierNode
-import androidx.compose.ui.node.currentValueOf
-import androidx.compose.ui.platform.LocalViewConfiguration
-import androidx.compose.ui.unit.IntSize
-import androidx.compose.ui.unit.Velocity
-import androidx.compose.ui.util.fastAll
-import androidx.compose.ui.util.fastAny
-import androidx.compose.ui.util.fastFilter
-import androidx.compose.ui.util.fastFirstOrNull
-import androidx.compose.ui.util.fastForEach
-import androidx.compose.ui.util.fastSumBy
-import com.android.compose.ui.util.SpaceVectorConverter
-import kotlin.coroutines.cancellation.CancellationException
-import kotlin.math.sign
-import kotlinx.coroutines.currentCoroutineContext
-import kotlinx.coroutines.isActive
-import kotlinx.coroutines.launch
-
-/**
- * Make an element draggable in the given [orientation].
- *
- * The main difference with [multiPointerDraggable] and
- * [androidx.compose.foundation.gestures.draggable] is that [onDragStarted] also receives the number
- * of pointers that are down when the drag is started. If you don't need this information, you
- * should use `draggable` instead.
- *
- * Note that the current implementation is trivial: we wait for the touch slope on the *first* down
- * pointer, then we count the number of distinct pointers that are down right before calling
- * [onDragStarted]. This means that the drag won't start when a first pointer is down (but not
- * dragged) and a second pointer is down and dragged. This is an implementation detail that might
- * change in the future.
- */
-@VisibleForTesting
-@Stable
-internal fun Modifier.multiPointerDraggable(
- orientation: Orientation,
- onDragStarted: (pointersDown: PointersInfo.PointersDown, overSlop: Float) -> DragController,
- onFirstPointerDown: () -> Unit = {},
- swipeDetector: SwipeDetector = DefaultSwipeDetector,
- dispatcher: NestedScrollDispatcher,
-): Modifier =
- this.then(
- MultiPointerDraggableElement(
- orientation,
- onDragStarted,
- onFirstPointerDown,
- swipeDetector,
- dispatcher,
- )
- )
-
-private data class MultiPointerDraggableElement(
- private val orientation: Orientation,
- private val onDragStarted:
- (pointersDown: PointersInfo.PointersDown, overSlop: Float) -> DragController,
- private val onFirstPointerDown: () -> Unit,
- private val swipeDetector: SwipeDetector,
- private val dispatcher: NestedScrollDispatcher,
-) : ModifierNodeElement<MultiPointerDraggableNode>() {
- override fun create(): MultiPointerDraggableNode =
- MultiPointerDraggableNode(
- orientation = orientation,
- onDragStarted = onDragStarted,
- onFirstPointerDown = onFirstPointerDown,
- swipeDetector = swipeDetector,
- dispatcher = dispatcher,
- )
-
- override fun update(node: MultiPointerDraggableNode) {
- node.orientation = orientation
- node.onDragStarted = onDragStarted
- node.onFirstPointerDown = onFirstPointerDown
- node.swipeDetector = swipeDetector
- }
-}
-
-internal class MultiPointerDraggableNode(
- orientation: Orientation,
- var onDragStarted: (pointersDown: PointersInfo.PointersDown, overSlop: Float) -> DragController,
- var onFirstPointerDown: () -> Unit,
- swipeDetector: SwipeDetector = DefaultSwipeDetector,
- private val dispatcher: NestedScrollDispatcher,
-) : DelegatingNode(), PointerInputModifierNode, CompositionLocalConsumerModifierNode {
- private val pointerTracker = delegate(SuspendingPointerInputModifierNode { pointerTracker() })
- private val pointerInput = delegate(SuspendingPointerInputModifierNode { pointerInput() })
- private val velocityTracker = VelocityTracker()
-
- var swipeDetector: SwipeDetector = swipeDetector
- set(value) {
- if (value != field) {
- field = value
- pointerInput.resetPointerInputHandler()
- }
- }
-
- private var converter = SpaceVectorConverter(orientation)
-
- fun Offset.toFloat(): Float = with(converter) { this@toFloat.toFloat() }
-
- fun Velocity.toFloat(): Float = with(converter) { this@toFloat.toFloat() }
-
- fun Float.toOffset(): Offset = with(converter) { this@toOffset.toOffset() }
-
- fun Float.toVelocity(): Velocity = with(converter) { this@toVelocity.toVelocity() }
-
- var orientation: Orientation = orientation
- set(value) {
- // Reset the pointer input whenever orientation changed.
- if (value != field) {
- field = value
- converter = SpaceVectorConverter(value)
- pointerInput.resetPointerInputHandler()
- }
- }
-
- override fun onCancelPointerInput() {
- pointerTracker.onCancelPointerInput()
- pointerInput.onCancelPointerInput()
- }
-
- override fun onPointerEvent(
- pointerEvent: PointerEvent,
- pass: PointerEventPass,
- bounds: IntSize,
- ) {
- // The order is important here: the tracker is always called first.
- pointerTracker.onPointerEvent(pointerEvent, pass, bounds)
- pointerInput.onPointerEvent(pointerEvent, pass, bounds)
- }
-
- private var lastPointerEvent: PointerEvent? = null
- private var startedPosition: Offset? = null
- private var countPointersDown: Int = 0
-
- internal fun pointersInfo(): PointersInfo? {
- // This may be null, i.e. when the user uses TalkBack
- val lastPointerEvent = lastPointerEvent ?: return null
-
- if (lastPointerEvent.type == PointerEventType.Scroll) return PointersInfo.MouseWheel
-
- val startedPosition = startedPosition ?: return null
-
- return PointersInfo.PointersDown(
- startedPosition = startedPosition,
- count = countPointersDown,
- countByType =
- buildMap {
- lastPointerEvent.changes.fastForEach { change ->
- if (!change.pressed) return@fastForEach
- val newValue = (get(change.type) ?: 0) + 1
- put(change.type, newValue)
- }
- },
- )
- }
-
- private suspend fun PointerInputScope.pointerTracker() {
- val currentContext = currentCoroutineContext()
- awaitPointerEventScope {
- var velocityPointerId: PointerId? = null
- // Intercepts pointer inputs and exposes [PointersInfo], via
- // [requireAncestorPointersInfoOwner], to our descendants.
- while (currentContext.isActive) {
- // During the Initial pass, we receive the event after our ancestors.
- val pointerEvent = awaitPointerEvent(PointerEventPass.Initial)
-
- // Ignore cursor has entered the input region.
- // This will only be sent after the cursor is hovering when in the input region.
- if (pointerEvent.type == PointerEventType.Enter) continue
-
- val changes = pointerEvent.changes
- lastPointerEvent = pointerEvent
- countPointersDown = changes.countDown()
-
- when {
- // There are no more pointers down.
- countPointersDown == 0 -> {
- startedPosition = null
-
- // In case of multiple events with 0 pointers down (not pressed) we may have
- // already removed the velocityPointer
- val lastPointerUp = changes.fastFilter { it.id == velocityPointerId }
- check(lastPointerUp.isEmpty() || lastPointerUp.size == 1) {
- "There are ${lastPointerUp.size} pointers up: $lastPointerUp"
- }
- if (lastPointerUp.size == 1) {
- velocityTracker.addPointerInputChange(lastPointerUp.first())
- }
- }
-
- // The first pointer down, startedPosition was not set.
- startedPosition == null -> {
- // Mouse wheel could start with multiple pointer down
- val firstPointerDown = changes.first()
- velocityPointerId = firstPointerDown.id
- velocityTracker.resetTracking()
- velocityTracker.addPointerInputChange(firstPointerDown)
- startedPosition = firstPointerDown.position
- onFirstPointerDown()
- }
-
- // Changes with at least one pointer
- else -> {
- val pointerChange = changes.first()
-
- // Assuming that the list of changes doesn't have two changes with the same
- // id (PointerId), we can check:
- // - If the first change has `id` equals to `velocityPointerId` (this should
- // always be true unless the pointer has been removed).
- // - If it does, we've found our change event (assuming there aren't any
- // others changes with the same id in this PointerEvent - not checked).
- // - If it doesn't, we can check that the change with that id isn't in first
- // place (which should never happen - this will crash).
- check(
- pointerChange.id == velocityPointerId ||
- !changes.fastAny { it.id == velocityPointerId }
- ) {
- "$velocityPointerId is present, but not the first: $changes"
- }
-
- // If the previous pointer has been removed, we use the first available
- // change to keep tracking the velocity.
- velocityPointerId =
- if (pointerChange.pressed) {
- pointerChange.id
- } else {
- changes.first { it.pressed }.id
- }
-
- velocityTracker.addPointerInputChange(pointerChange)
- }
- }
- }
- }
- }
-
- private suspend fun PointerInputScope.pointerInput() {
- val currentContext = currentCoroutineContext()
- awaitPointerEventScope {
- while (currentContext.isActive) {
- try {
- detectDragGestures(
- orientation = orientation,
- onDragStart = { pointersDown, overSlop ->
- onDragStarted(pointersDown, overSlop)
- },
- onDrag = { controller, amount ->
- dispatchScrollEvents(
- availableOnPreScroll = amount,
- onScroll = { controller.onDrag(it) },
- source = NestedScrollSource.UserInput,
- )
- },
- onDragEnd = { controller ->
- startFlingGesture(
- initialVelocity =
- currentValueOf(LocalViewConfiguration)
- .maximumFlingVelocity
- .let {
- val maxVelocity = Velocity(it, it)
- velocityTracker.calculateVelocity(maxVelocity)
- }
- .toFloat(),
- onFling = { controller.onStop(it) },
- )
- },
- onDragCancel = { controller ->
- startFlingGesture(
- initialVelocity = 0f,
- onFling = { controller.onStop(it) },
- )
- },
- swipeDetector = swipeDetector,
- )
- } catch (exception: CancellationException) {
- // If the coroutine scope is active, we can just restart the drag cycle.
- if (!currentContext.isActive) {
- throw exception
- }
- }
- }
- }
- }
-
- /**
- * Start a fling gesture in another CoroutineScope, this is to ensure that even when the pointer
- * input scope is reset we will continue any coroutine scope that we started from these methods
- * while the pointer input scope was active.
- *
- * Note: Inspired by [androidx.compose.foundation.gestures.ScrollableNode.onDragStopped]
- */
- private fun startFlingGesture(
- initialVelocity: Float,
- onFling: suspend (velocity: Float) -> Float,
- ) {
- // Note: [AwaitPointerEventScope] is annotated as @RestrictsSuspension, we need another
- // CoroutineScope to run the fling gestures.
- // We do not need to cancel this [Job], the source will take care of emitting an
- // [onPostFling] before starting a new gesture.
- dispatcher.coroutineScope.launch {
- dispatchFlingEvents(availableOnPreFling = initialVelocity, onFling = onFling)
- }
- }
-
- /**
- * Use the nested scroll system to fire scroll events. This allows us to consume events from our
- * ancestors during the pre-scroll and post-scroll phases.
- *
- * @param availableOnPreScroll amount available before the scroll, this can be partially
- * consumed by our ancestors.
- * @param onScroll function that returns the amount consumed during a scroll given the amount
- * available after the [NestedScrollConnection.onPreScroll].
- * @param source the source of the scroll event
- * @return Total offset consumed.
- */
- private inline fun dispatchScrollEvents(
- availableOnPreScroll: Float,
- onScroll: (delta: Float) -> Float,
- source: NestedScrollSource,
- ): Float {
- // PreScroll phase
- val consumedByPreScroll =
- dispatcher
- .dispatchPreScroll(available = availableOnPreScroll.toOffset(), source = source)
- .toFloat()
-
- // Scroll phase
- val availableOnScroll = availableOnPreScroll - consumedByPreScroll
- val consumedBySelfScroll = onScroll(availableOnScroll)
-
- // PostScroll phase
- val availableOnPostScroll = availableOnScroll - consumedBySelfScroll
- val consumedByPostScroll =
- dispatcher
- .dispatchPostScroll(
- consumed = consumedBySelfScroll.toOffset(),
- available = availableOnPostScroll.toOffset(),
- source = source,
- )
- .toFloat()
-
- return consumedByPreScroll + consumedBySelfScroll + consumedByPostScroll
- }
-
- /**
- * Use the nested scroll system to fire fling events. This allows us to consume events from our
- * ancestors during the pre-fling and post-fling phases.
- *
- * @param availableOnPreFling velocity available before the fling, this can be partially
- * consumed by our ancestors.
- * @param onFling function that returns the velocity consumed during the fling given the
- * velocity available after the [NestedScrollConnection.onPreFling].
- * @return Total velocity consumed.
- */
- private suspend inline fun dispatchFlingEvents(
- availableOnPreFling: Float,
- onFling: suspend (velocity: Float) -> Float,
- ): Float {
- // PreFling phase
- val consumedByPreFling =
- dispatcher.dispatchPreFling(available = availableOnPreFling.toVelocity()).toFloat()
-
- // Fling phase
- val availableOnFling = availableOnPreFling - consumedByPreFling
- val consumedBySelfFling = onFling(availableOnFling)
-
- // PostFling phase
- val availableOnPostFling = availableOnFling - consumedBySelfFling
- val consumedByPostFling =
- dispatcher
- .dispatchPostFling(
- consumed = consumedBySelfFling.toVelocity(),
- available = availableOnPostFling.toVelocity(),
- )
- .toFloat()
-
- return consumedByPreFling + consumedBySelfFling + consumedByPostFling
- }
-
- /**
- * Detect drag gestures in the given [orientation].
- *
- * This function is a mix of [androidx.compose.foundation.gestures.awaitDownAndSlop] and
- * [androidx.compose.foundation.gestures.detectVerticalDragGestures] to add support for passing
- * the number of pointers down to [onDragStart].
- */
- private suspend fun AwaitPointerEventScope.detectDragGestures(
- orientation: Orientation,
- onDragStart: (pointersDown: PointersInfo.PointersDown, overSlop: Float) -> DragController,
- onDrag: (controller: DragController, dragAmount: Float) -> Unit,
- onDragEnd: (controller: DragController) -> Unit,
- onDragCancel: (controller: DragController) -> Unit,
- swipeDetector: SwipeDetector,
- ) {
- val consumablePointer =
- awaitConsumableEvent {
- // We are searching for an event that can be used as the starting point for the
- // drag gesture. Our options are:
- // - Initial: These events should never be consumed by the MultiPointerDraggable
- // since our ancestors can consume the gesture, but we would eliminate this
- // possibility for our descendants.
- // - Main: These events are consumed during the drag gesture, and they are a
- // good place to start if the previous event has not been consumed.
- // - Final: If the previous event has been consumed, we can wait for the Main
- // pass to finish. If none of our ancestors were interested in the event, we
- // can wait for an unconsumed event in the Final pass.
- val previousConsumed = currentEvent.changes.fastAny { it.isConsumed }
- if (previousConsumed) PointerEventPass.Final else PointerEventPass.Main
- }
- .changes
- .first()
-
- var overSlop = 0f
- val onSlopReached = { change: PointerInputChange, over: Float ->
- if (swipeDetector.detectSwipe(change)) {
- change.consume()
- overSlop = over
- }
- }
-
- // TODO(b/291055080): Replace by await[Orientation]PointerSlopOrCancellation once it
- // is public.
- val drag =
- when (orientation) {
- Orientation.Horizontal ->
- awaitHorizontalTouchSlopOrCancellation(consumablePointer.id, onSlopReached)
- Orientation.Vertical ->
- awaitVerticalTouchSlopOrCancellation(consumablePointer.id, onSlopReached)
- } ?: return
-
- val lastPointersDown =
- checkNotNull(pointersInfo()) {
- "We should have pointers down, last event: $currentEvent"
- }
- as PointersInfo.PointersDown
- // Make sure that overSlop is not 0f. This can happen when the user drags by exactly
- // the touch slop. However, the overSlop we pass to onDragStarted() is used to
- // compute the direction we are dragging in, so overSlop should never be 0f.
- if (overSlop == 0f) {
- // If the user drags in the opposite direction, the delta becomes zero because
- // we return to the original point. Therefore, we should use the previous event
- // to calculate the direction.
- val delta = (drag.position - drag.previousPosition).toFloat()
- check(delta != 0f) {
- buildString {
- append("delta is equal to 0 ")
- append("touchSlop ${currentValueOf(LocalViewConfiguration).touchSlop} ")
- append("consumablePointer.position ${consumablePointer.position} ")
- append("drag.position ${drag.position} ")
- append("drag.previousPosition ${drag.previousPosition}")
- }
- }
- overSlop = delta.sign
- }
-
- val controller = onDragStart(lastPointersDown, overSlop)
- val successful: Boolean
- try {
- onDrag(controller, overSlop)
-
- successful =
- drag(
- initialPointerId = drag.id,
- hasDragged = { it.positionChangeIgnoreConsumed().toFloat() != 0f },
- onDrag = {
- onDrag(controller, it.positionChange().toFloat())
- it.consume()
- },
- onIgnoredEvent = {
- // We are still dragging an object, but this event is not of interest to the
- // caller.
- // This event will not trigger the onDrag event, but we will consume the
- // event to prevent another pointerInput from interrupting the current
- // gesture just because the event was ignored.
- it.consume()
- },
- )
- } catch (t: Throwable) {
- onDragCancel(controller)
- throw t
- }
-
- if (successful) {
- onDragEnd(controller)
- } else {
- onDragCancel(controller)
- }
- }
-
- private suspend fun AwaitPointerEventScope.awaitConsumableEvent(
- pass: () -> PointerEventPass
- ): PointerEvent {
- fun canBeConsumed(changes: List<PointerInputChange>): Boolean {
- // At least one pointer down AND
- return changes.fastAny { it.pressed } &&
- // All pointers must be either:
- changes.fastAll {
- // A) unconsumed AND recently pressed
- it.changedToDown() ||
- // B) unconsumed AND in a new position (on the current axis)
- it.positionChange().toFloat() != 0f
- }
- }
-
- var event: PointerEvent
- do {
- event = awaitPointerEvent(pass = pass())
- } while (!canBeConsumed(event.changes))
-
- // We found a consumable event in the Main pass
- return event
- }
-
- /**
- * Continues to read drag events until all pointers are up or the drag event is canceled. The
- * initial pointer to use for driving the drag is [initialPointerId]. [hasDragged] passes the
- * result whether a change was detected from the drag function or not.
- *
- * Whenever the pointer moves, if [hasDragged] returns true, [onDrag] is called; otherwise,
- * [onIgnoredEvent] is called.
- *
- * @return true when gesture ended with all pointers up and false when the gesture was canceled.
- *
- * Note: Inspired by DragGestureDetector.kt
- */
- private suspend inline fun AwaitPointerEventScope.drag(
- initialPointerId: PointerId,
- hasDragged: (PointerInputChange) -> Boolean,
- onDrag: (PointerInputChange) -> Unit,
- onIgnoredEvent: (PointerInputChange) -> Unit,
- ): Boolean {
- val pointer = currentEvent.changes.fastFirstOrNull { it.id == initialPointerId }
- val isPointerUp = pointer?.pressed != true
- if (isPointerUp) {
- return false // The pointer has already been lifted, so the gesture is canceled
- }
- var pointerId = initialPointerId
- while (true) {
- val change = awaitDragOrUp(pointerId, hasDragged, onIgnoredEvent) ?: return false
-
- if (change.isConsumed) {
- return false
- }
-
- if (change.changedToUpIgnoreConsumed()) {
- return true
- }
-
- onDrag(change)
- pointerId = change.id
- }
- }
-
- /**
- * Waits for a single drag in one axis, final pointer up, or all pointers are up. When
- * [initialPointerId] has lifted, another pointer that is down is chosen to be the finger
- * governing the drag. When the final pointer is lifted, that [PointerInputChange] is returned.
- * When a drag is detected, that [PointerInputChange] is returned. A drag is only detected when
- * [hasDragged] returns `true`. Events that should not be captured are passed to
- * [onIgnoredEvent].
- *
- * `null` is returned if there was an error in the pointer input stream and the pointer that was
- * down was dropped before the 'up' was received.
- *
- * Note: Inspired by DragGestureDetector.kt
- */
- private suspend inline fun AwaitPointerEventScope.awaitDragOrUp(
- initialPointerId: PointerId,
- hasDragged: (PointerInputChange) -> Boolean,
- onIgnoredEvent: (PointerInputChange) -> Unit,
- ): PointerInputChange? {
- var pointerId = initialPointerId
- while (true) {
- val event = awaitPointerEvent()
- val dragEvent = event.changes.fastFirstOrNull { it.id == pointerId } ?: return null
- if (dragEvent.changedToUpIgnoreConsumed()) {
- val otherDown = event.changes.fastFirstOrNull { it.pressed }
- if (otherDown == null) {
- // This is the last "up"
- return dragEvent
- } else {
- pointerId = otherDown.id
- }
- } else if (hasDragged(dragEvent)) {
- return dragEvent
- } else {
- onIgnoredEvent(dragEvent)
- }
- }
- }
-
- private fun List<PointerInputChange>.countDown() = fastSumBy { if (it.pressed) 1 else 0 }
-}
-
-internal fun interface PointersInfoOwner {
- /**
- * Provides information about the pointers interacting with this composable.
- *
- * @return A [PointersInfo] object containing details about the pointers, including the starting
- * position and the number of pointers down, or `null` if there are no pointers down.
- */
- fun pointersInfo(): PointersInfo?
-}
-
-internal sealed interface PointersInfo {
- /**
- * Holds information about pointer interactions within a composable.
- *
- * This class stores details such as the starting position of a gesture, the number of pointers
- * down, and whether the last pointer event was a mouse wheel scroll.
- *
- * @param startedPosition The starting position of the gesture. This is the position where the
- * first pointer touched the screen, not necessarily the point where dragging begins. This may
- * be different from the initial touch position if a child composable intercepts the gesture
- * before this one.
- * @param count The number of pointers currently down.
- * @param countByType Provide a map of pointer types to the count of pointers of that type
- * currently down/pressed.
- */
- data class PointersDown(
- val startedPosition: Offset,
- val count: Int,
- val countByType: Map<PointerType, Int>,
- ) : PointersInfo {
- init {
- check(count > 0) { "We should have at least 1 pointer down, $count instead" }
- }
- }
-
- /** Indicates whether the last pointer event was a mouse wheel scroll. */
- data object MouseWheel : PointersInfo
-}
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayout.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayout.kt
index de428a7d3548..4e389471d1d0 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayout.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayout.kt
@@ -457,7 +457,7 @@ data class Swipe
private constructor(
val direction: SwipeDirection,
val pointerCount: Int = 1,
- val pointersType: PointerType? = null,
+ val pointerType: PointerType? = null,
val fromSource: SwipeSource? = null,
) : UserAction() {
companion object {
@@ -470,46 +470,46 @@ private constructor(
fun Left(
pointerCount: Int = 1,
- pointersType: PointerType? = null,
+ pointerType: PointerType? = null,
fromSource: SwipeSource? = null,
- ) = Swipe(SwipeDirection.Left, pointerCount, pointersType, fromSource)
+ ) = Swipe(SwipeDirection.Left, pointerCount, pointerType, fromSource)
fun Up(
pointerCount: Int = 1,
- pointersType: PointerType? = null,
+ pointerType: PointerType? = null,
fromSource: SwipeSource? = null,
- ) = Swipe(SwipeDirection.Up, pointerCount, pointersType, fromSource)
+ ) = Swipe(SwipeDirection.Up, pointerCount, pointerType, fromSource)
fun Right(
pointerCount: Int = 1,
- pointersType: PointerType? = null,
+ pointerType: PointerType? = null,
fromSource: SwipeSource? = null,
- ) = Swipe(SwipeDirection.Right, pointerCount, pointersType, fromSource)
+ ) = Swipe(SwipeDirection.Right, pointerCount, pointerType, fromSource)
fun Down(
pointerCount: Int = 1,
- pointersType: PointerType? = null,
+ pointerType: PointerType? = null,
fromSource: SwipeSource? = null,
- ) = Swipe(SwipeDirection.Down, pointerCount, pointersType, fromSource)
+ ) = Swipe(SwipeDirection.Down, pointerCount, pointerType, fromSource)
fun Start(
pointerCount: Int = 1,
- pointersType: PointerType? = null,
+ pointerType: PointerType? = null,
fromSource: SwipeSource? = null,
- ) = Swipe(SwipeDirection.Start, pointerCount, pointersType, fromSource)
+ ) = Swipe(SwipeDirection.Start, pointerCount, pointerType, fromSource)
fun End(
pointerCount: Int = 1,
- pointersType: PointerType? = null,
+ pointerType: PointerType? = null,
fromSource: SwipeSource? = null,
- ) = Swipe(SwipeDirection.End, pointerCount, pointersType, fromSource)
+ ) = Swipe(SwipeDirection.End, pointerCount, pointerType, fromSource)
}
override fun resolve(layoutDirection: LayoutDirection): UserAction.Resolved {
return Resolved(
direction = direction.resolve(layoutDirection),
pointerCount = pointerCount,
- pointersType = pointersType,
+ pointerType = pointerType,
fromSource = fromSource?.resolve(layoutDirection),
)
}
@@ -519,7 +519,7 @@ private constructor(
val direction: SwipeDirection.Resolved,
val pointerCount: Int,
val fromSource: SwipeSource.Resolved?,
- val pointersType: PointerType?,
+ val pointerType: PointerType?,
) : UserAction.Resolved()
}
@@ -724,6 +724,7 @@ internal fun SceneTransitionLayoutForTesting(
density = density,
layoutDirection = layoutDirection,
swipeSourceDetector = swipeSourceDetector,
+ swipeDetector = swipeDetector,
transitionInterceptionThreshold = transitionInterceptionThreshold,
builder = builder,
animationScope = animationScope,
@@ -767,8 +768,9 @@ internal fun SceneTransitionLayoutForTesting(
layoutImpl.density = density
layoutImpl.layoutDirection = layoutDirection
layoutImpl.swipeSourceDetector = swipeSourceDetector
+ layoutImpl.swipeDetector = swipeDetector
layoutImpl.transitionInterceptionThreshold = transitionInterceptionThreshold
}
- layoutImpl.Content(modifier, swipeDetector)
+ layoutImpl.Content(modifier)
}
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayoutImpl.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayoutImpl.kt
index e5bdc92b5762..b4c449d0566c 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayoutImpl.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayoutImpl.kt
@@ -31,6 +31,9 @@ import androidx.compose.runtime.snapshots.SnapshotStateMap
import androidx.compose.ui.Alignment
import androidx.compose.ui.ExperimentalComposeUiApi
import androidx.compose.ui.Modifier
+import androidx.compose.ui.input.nestedscroll.NestedScrollConnection
+import androidx.compose.ui.input.nestedscroll.NestedScrollDispatcher
+import androidx.compose.ui.input.nestedscroll.nestedScroll
import androidx.compose.ui.layout.ApproachLayoutModifierNode
import androidx.compose.ui.layout.ApproachMeasureScope
import androidx.compose.ui.layout.LookaheadScope
@@ -49,10 +52,8 @@ import com.android.compose.animation.scene.content.Content
import com.android.compose.animation.scene.content.Overlay
import com.android.compose.animation.scene.content.Scene
import com.android.compose.animation.scene.content.state.TransitionState
-import com.android.compose.animation.scene.effect.GestureEffect
import com.android.compose.ui.util.lerp
import kotlinx.coroutines.CoroutineScope
-import kotlinx.coroutines.launch
/** The type for the content of movable elements. */
internal typealias MovableElementContent = @Composable (@Composable () -> Unit) -> Unit
@@ -75,6 +76,7 @@ internal class SceneTransitionLayoutImpl(
internal var density: Density,
internal var layoutDirection: LayoutDirection,
internal var swipeSourceDetector: SwipeSourceDetector,
+ internal var swipeDetector: SwipeDetector,
internal var transitionInterceptionThreshold: Float,
builder: SceneTransitionLayoutScope.() -> Unit,
@@ -149,18 +151,6 @@ internal class SceneTransitionLayoutImpl(
_movableContents = it
}
- internal var horizontalOverscrollableContent =
- OverscrollableContent(
- animationScope = animationScope,
- overscrollEffect = { content(it).scope.horizontalOverscrollGestureEffect },
- )
-
- internal var verticalOverscrollableContent =
- OverscrollableContent(
- animationScope = animationScope,
- overscrollEffect = { content(it).scope.verticalOverscrollGestureEffect },
- )
-
/**
* The different values of a shared value keyed by a a [ValueKey] and the different elements and
* contents it is associated to.
@@ -175,8 +165,8 @@ internal class SceneTransitionLayoutImpl(
}
// TODO(b/317958526): Lazily allocate scene gesture handlers the first time they are needed.
- internal val horizontalDraggableHandler: DraggableHandlerImpl
- internal val verticalDraggableHandler: DraggableHandlerImpl
+ internal val horizontalDraggableHandler: DraggableHandler
+ internal val verticalDraggableHandler: DraggableHandler
internal val elementStateScope = ElementStateScopeImpl(this)
internal val propertyTransformationScope = PropertyTransformationScopeImpl(this)
@@ -190,16 +180,33 @@ internal class SceneTransitionLayoutImpl(
internal var lastSize: IntSize = IntSize.Zero
+ /**
+ * An empty [NestedScrollDispatcher] and [NestedScrollConnection]. These are composed above our
+ * [SwipeToSceneElement] modifiers, so that the dispatcher will be used by the nested draggables
+ * to launch fling events, making sure that they are not cancelled unless this whole layout is
+ * removed from composition.
+ */
+ private val nestedScrollDispatcher = NestedScrollDispatcher()
+ private val nestedScrollConnection = object : NestedScrollConnection {}
+
init {
updateContents(builder, layoutDirection)
// DraggableHandlerImpl must wait for the scenes to be initialized, in order to access the
// current scene (required for SwipeTransition).
horizontalDraggableHandler =
- DraggableHandlerImpl(layoutImpl = this, orientation = Orientation.Horizontal)
+ DraggableHandler(
+ layoutImpl = this,
+ orientation = Orientation.Horizontal,
+ gestureEffectProvider = { content(it).scope.horizontalOverscrollGestureEffect },
+ )
verticalDraggableHandler =
- DraggableHandlerImpl(layoutImpl = this, orientation = Orientation.Vertical)
+ DraggableHandler(
+ layoutImpl = this,
+ orientation = Orientation.Vertical,
+ gestureEffectProvider = { content(it).scope.verticalOverscrollGestureEffect },
+ )
// Make sure that the state is created on the same thread (most probably the main thread)
// than this STLImpl.
@@ -379,14 +386,15 @@ internal class SceneTransitionLayoutImpl(
}
@Composable
- internal fun Content(modifier: Modifier, swipeDetector: SwipeDetector) {
+ internal fun Content(modifier: Modifier) {
Box(
modifier
+ .nestedScroll(nestedScrollConnection, nestedScrollDispatcher)
// Handle horizontal and vertical swipes on this layout.
// Note: order here is important and will give a slight priority to the vertical
// swipes.
- .swipeToScene(horizontalDraggableHandler, swipeDetector)
- .swipeToScene(verticalDraggableHandler, swipeDetector)
+ .swipeToScene(horizontalDraggableHandler)
+ .swipeToScene(verticalDraggableHandler)
.then(LayoutElement(layoutImpl = this))
) {
LookaheadScope {
@@ -580,23 +588,3 @@ private class LayoutNode(var layoutImpl: SceneTransitionLayoutImpl) :
return layout(width, height) { placeable.place(0, 0) }
}
}
-
-internal class OverscrollableContent(
- private val animationScope: CoroutineScope,
- private val overscrollEffect: (ContentKey) -> GestureEffect,
-) {
- private var currentContent: ContentKey? = null
- var currentOverscrollEffect: GestureEffect? = null
-
- fun applyOverscrollEffectOn(contentKey: ContentKey): GestureEffect {
- if (currentContent == contentKey) return currentOverscrollEffect!!
-
- currentOverscrollEffect?.apply { animationScope.launch { ensureApplyToFlingIsCalled() } }
-
- // We are wrapping the overscroll effect.
- val overscrollEffect = overscrollEffect(contentKey)
- currentContent = contentKey
- currentOverscrollEffect = overscrollEffect
- return overscrollEffect
- }
-}
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SwipeAnimation.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SwipeAnimation.kt
index 2bfa0199f30b..b1d6d1efc264 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SwipeAnimation.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SwipeAnimation.kt
@@ -24,6 +24,7 @@ import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableFloatStateOf
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.setValue
+import androidx.compose.ui.util.fastCoerceIn
import com.android.compose.animation.scene.content.state.TransitionState
import com.android.compose.animation.scene.content.state.TransitionState.Companion.DistanceUnspecified
import kotlin.math.absoluteValue
@@ -76,7 +77,16 @@ internal fun createSwipeAnimation(
return DistanceUnspecified
}
- val distance = if (isUpOrLeft) -absoluteDistance else absoluteDistance
+ // Compute the signed distance and make sure that the offset is always coerced in the right
+ // range.
+ val distance =
+ if (isUpOrLeft) {
+ animation.dragOffset = animation.dragOffset.fastCoerceIn(-absoluteDistance, 0f)
+ -absoluteDistance
+ } else {
+ animation.dragOffset = animation.dragOffset.fastCoerceIn(0f, absoluteDistance)
+ absoluteDistance
+ }
lastDistance = distance
return distance
}
@@ -294,12 +304,10 @@ internal class SwipeAnimation<T : ContentKey>(
initialVelocity: Float,
targetContent: T,
spec: AnimationSpec<Float>? = null,
- overscrollCompletable: CompletableDeferred<Unit>? = null,
+ awaitFling: (suspend () -> Unit)? = null,
): Float {
check(!isAnimatingOffset()) { "SwipeAnimation.animateOffset() can only be called once" }
- val initialProgress = progress
-
val targetContent =
if (targetContent != currentContent && !canChangeContent(targetContent)) {
currentContent
@@ -307,18 +315,16 @@ internal class SwipeAnimation<T : ContentKey>(
targetContent
}
- // Skip the animation if we have already reached the target content and the overscroll does
- // not animate anything.
- val hasReachedTargetContent =
- (targetContent == toContent && initialProgress >= 1f) ||
- (targetContent == fromContent && initialProgress <= 0f)
- val skipAnimation =
- hasReachedTargetContent && !contentTransition.isWithinProgressRange(initialProgress)
-
val distance = distance()
- check(distance != DistanceUnspecified) { "distance is equal to $DistanceUnspecified" }
-
- val targetOffset = if (targetContent == fromContent) 0f else distance
+ val targetOffset =
+ if (targetContent == fromContent) {
+ 0f
+ } else {
+ check(distance != DistanceUnspecified) {
+ "distance is equal to $DistanceUnspecified"
+ }
+ distance
+ }
// If the effective current content changed, it should be reflected right now in the
// current state, even before the settle animation is ongoing. That way all the
@@ -350,28 +356,12 @@ internal class SwipeAnimation<T : ContentKey>(
check(isAnimatingOffset())
- // Note: we still create the animatable and set it on offsetAnimation even when
- // skipAnimation is true, just so that isUserInputOngoing and isAnimatingOffset() are
- // unchanged even despite this small skip-optimization (which is just an implementation
- // detail).
- if (skipAnimation) {
- // Unblock the job.
- offsetAnimationRunnable.complete {
- // Wait for overscroll to finish so that the transition is removed from the STLState
- // only after the overscroll is done, to avoid dropping frame right when the user
- // lifts their finger and overscroll is animated to 0.
- overscrollCompletable?.await()
- }
- return 0f
- }
-
val motionSpatialSpec =
spec
?: contentTransition.transformationSpec.motionSpatialSpec
?: layoutState.transitions.defaultMotionSpatialSpec
val velocityConsumed = CompletableDeferred<Float>()
-
offsetAnimationRunnable.complete {
val result =
animatable.animateTo(
@@ -385,9 +375,9 @@ internal class SwipeAnimation<T : ContentKey>(
velocityConsumed.complete(initialVelocity - result.endState.velocity)
// Wait for overscroll to finish so that the transition is removed from the STLState
- // only after the overscroll is done, to avoid dropping frame right when the user
- // lifts their finger and overscroll is animated to 0.
- overscrollCompletable?.await()
+ // only after the overscroll is done, to avoid dropping frame right when the user lifts
+ // their finger and overscroll is animated to 0.
+ awaitFling?.invoke()
}
return velocityConsumed.await()
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SwipeToScene.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SwipeToScene.kt
index e2212113404d..19f707df91e0 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SwipeToScene.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SwipeToScene.kt
@@ -19,153 +19,35 @@ package com.android.compose.animation.scene
import androidx.compose.foundation.gestures.Orientation
import androidx.compose.runtime.Stable
import androidx.compose.ui.Modifier
-import androidx.compose.ui.input.nestedscroll.NestedScrollDispatcher
-import androidx.compose.ui.input.nestedscroll.nestedScrollModifierNode
-import androidx.compose.ui.input.pointer.PointerEvent
-import androidx.compose.ui.input.pointer.PointerEventPass
-import androidx.compose.ui.node.DelegatingNode
-import androidx.compose.ui.node.ModifierNodeElement
-import androidx.compose.ui.node.PointerInputModifierNode
-import androidx.compose.ui.unit.IntSize
import com.android.compose.animation.scene.content.Content
+import com.android.compose.gesture.nestedDraggable
/**
* Configures the swipeable behavior of a [SceneTransitionLayout] depending on the current state.
*/
@Stable
-internal fun Modifier.swipeToScene(
- draggableHandler: DraggableHandlerImpl,
- swipeDetector: SwipeDetector,
-): Modifier {
- return then(SwipeToSceneElement(draggableHandler, swipeDetector, draggableHandler.enabled()))
+internal fun Modifier.swipeToScene(draggableHandler: DraggableHandler): Modifier {
+ return this.nestedDraggable(
+ draggable = draggableHandler,
+ orientation = draggableHandler.orientation,
+ overscrollEffect = draggableHandler.overscrollEffect,
+ enabled = draggableHandler.enabled(),
+ )
}
-private fun DraggableHandlerImpl.enabled(): Boolean {
+internal fun DraggableHandler.enabled(): Boolean {
return isDrivingTransition || contentForSwipes().shouldEnableSwipes(orientation)
}
-private fun DraggableHandlerImpl.contentForSwipes(): Content {
+private fun DraggableHandler.contentForSwipes(): Content {
return layoutImpl.contentForUserActions()
}
/** Whether swipe should be enabled in the given [orientation]. */
-internal fun Content.shouldEnableSwipes(orientation: Orientation): Boolean {
+private fun Content.shouldEnableSwipes(orientation: Orientation): Boolean {
if (userActions.isEmpty() || !areSwipesAllowed()) {
return false
}
return userActions.keys.any { it is Swipe.Resolved && it.direction.orientation == orientation }
}
-
-private data class SwipeToSceneElement(
- val draggableHandler: DraggableHandlerImpl,
- val swipeDetector: SwipeDetector,
- val enabled: Boolean,
-) : ModifierNodeElement<SwipeToSceneRootNode>() {
- override fun create(): SwipeToSceneRootNode =
- SwipeToSceneRootNode(draggableHandler, swipeDetector, enabled)
-
- override fun update(node: SwipeToSceneRootNode) {
- node.update(draggableHandler, swipeDetector, enabled)
- }
-}
-
-private class SwipeToSceneRootNode(
- draggableHandler: DraggableHandlerImpl,
- swipeDetector: SwipeDetector,
- enabled: Boolean,
-) : DelegatingNode() {
- private var delegateNode = if (enabled) create(draggableHandler, swipeDetector) else null
-
- fun update(
- draggableHandler: DraggableHandlerImpl,
- swipeDetector: SwipeDetector,
- enabled: Boolean,
- ) {
- // Disabled.
- if (!enabled) {
- delegateNode?.let { undelegate(it) }
- delegateNode = null
- return
- }
-
- // Disabled => Enabled.
- val nullableDelegate = delegateNode
- if (nullableDelegate == null) {
- delegateNode = create(draggableHandler, swipeDetector)
- return
- }
-
- // Enabled => Enabled (update).
- if (draggableHandler == nullableDelegate.draggableHandler) {
- // Simple update, just update the swipe detector directly and keep the node.
- nullableDelegate.swipeDetector = swipeDetector
- } else {
- // The draggableHandler changed, force recreate the underlying SwipeToSceneNode.
- undelegate(nullableDelegate)
- delegateNode = create(draggableHandler, swipeDetector)
- }
- }
-
- private fun create(
- draggableHandler: DraggableHandlerImpl,
- swipeDetector: SwipeDetector,
- ): SwipeToSceneNode {
- return delegate(SwipeToSceneNode(draggableHandler, swipeDetector))
- }
-}
-
-private class SwipeToSceneNode(
- val draggableHandler: DraggableHandlerImpl,
- swipeDetector: SwipeDetector,
-) : DelegatingNode(), PointerInputModifierNode {
- private val dispatcher = NestedScrollDispatcher()
- private val multiPointerDraggableNode =
- delegate(
- MultiPointerDraggableNode(
- orientation = draggableHandler.orientation,
- onDragStarted = draggableHandler::onDragStarted,
- onFirstPointerDown = ::onFirstPointerDown,
- swipeDetector = swipeDetector,
- dispatcher = dispatcher,
- )
- )
-
- var swipeDetector: SwipeDetector
- get() = multiPointerDraggableNode.swipeDetector
- set(value) {
- multiPointerDraggableNode.swipeDetector = value
- }
-
- private val nestedScrollHandlerImpl =
- NestedScrollHandlerImpl(
- draggableHandler = draggableHandler,
- pointersInfoOwner = { multiPointerDraggableNode.pointersInfo() },
- )
-
- init {
- delegate(nestedScrollModifierNode(nestedScrollHandlerImpl.connection, dispatcher))
- }
-
- private fun onFirstPointerDown() {
- // When we drag our finger across the screen, the NestedScrollConnection keeps track of all
- // the scroll events until we lift our finger. However, in some cases, the connection might
- // not receive the "up" event. This can lead to an incorrect initial state for the gesture.
- // To prevent this issue, we can call the reset() method when the first finger touches the
- // screen. This ensures that the NestedScrollConnection starts from a correct state.
- nestedScrollHandlerImpl.connection.reset()
- }
-
- override fun onDetach() {
- // Make sure we reset the scroll connection when this modifier is removed from composition
- nestedScrollHandlerImpl.connection.reset()
- }
-
- override fun onPointerEvent(
- pointerEvent: PointerEvent,
- pass: PointerEventPass,
- bounds: IntSize,
- ) = multiPointerDraggableNode.onPointerEvent(pointerEvent, pass, bounds)
-
- override fun onCancelPointerInput() = multiPointerDraggableNode.onCancelPointerInput()
-}
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/content/state/TransitionState.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/content/state/TransitionState.kt
index 097722665f8e..f772f1a0b4a6 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/content/state/TransitionState.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/content/state/TransitionState.kt
@@ -373,15 +373,6 @@ sealed interface TransitionState {
}
}
- /**
- * Checks if the given [progress] value is within the valid range for this transition.
- *
- * The valid range is between 0f and 1f, inclusive.
- */
- internal fun isWithinProgressRange(progress: Float): Boolean {
- return progress >= 0f && progress <= 1f
- }
-
internal open fun interruptionProgress(layoutImpl: SceneTransitionLayoutImpl): Float {
if (replacedTransition != null) {
return replacedTransition.interruptionProgress(layoutImpl)
diff --git a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/DraggableHandlerTest.kt b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/DraggableHandlerTest.kt
index 5a9edba26d13..4a0c330be4f8 100644
--- a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/DraggableHandlerTest.kt
+++ b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/DraggableHandlerTest.kt
@@ -38,9 +38,11 @@ import com.android.compose.animation.scene.TestScenes.SceneC
import com.android.compose.animation.scene.content.state.TransitionState
import com.android.compose.animation.scene.content.state.TransitionState.Transition
import com.android.compose.animation.scene.subjects.assertThat
+import com.android.compose.gesture.NestedDraggable
import com.android.compose.test.MonotonicClockTestScope
import com.android.compose.test.runMonotonicClockTest
import com.google.common.truth.Truth.assertThat
+import kotlin.math.sign
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Deferred
import kotlinx.coroutines.async
@@ -51,18 +53,6 @@ import org.junit.runner.RunWith
private const val SCREEN_SIZE = 100f
private val LAYOUT_SIZE = IntSize(SCREEN_SIZE.toInt(), SCREEN_SIZE.toInt())
-private fun pointersDown(
- startedPosition: Offset = Offset.Zero,
- pointersDown: Int = 1,
- pointersDownByType: Map<PointerType, Int> = mapOf(PointerType.Touch to pointersDown),
-): PointersInfo.PointersDown {
- return PointersInfo.PointersDown(
- startedPosition = startedPosition,
- count = pointersDown,
- countByType = pointersDownByType,
- )
-}
-
@RunWith(AndroidJUnit4::class)
class DraggableHandlerTest {
private class TestGestureScope(val testScope: MonotonicClockTestScope) {
@@ -123,6 +113,7 @@ class DraggableHandlerTest {
density = Density(1f),
layoutDirection = LayoutDirection.Ltr,
swipeSourceDetector = DefaultEdgeDetector,
+ swipeDetector = DefaultSwipeDetector,
transitionInterceptionThreshold = transitionInterceptionThreshold,
builder = scenesBuilder,
@@ -134,9 +125,6 @@ class DraggableHandlerTest {
val draggableHandler = layoutImpl.verticalDraggableHandler
val horizontalDraggableHandler = layoutImpl.horizontalDraggableHandler
-
- var pointerInfoOwner: () -> PointersInfo = { pointersDown() }
-
val velocityThreshold = draggableHandler.velocityThreshold
fun down(fractionOfScreen: Float) =
@@ -204,41 +192,51 @@ class DraggableHandlerTest {
}
fun onDragStarted(
- pointersInfo: PointersInfo.PointersDown = pointersDown(),
overSlop: Float,
+ position: Offset = Offset.Zero,
+ pointersDown: Int = 1,
+ pointerType: PointerType? = PointerType.Touch,
expectedConsumedOverSlop: Float = overSlop,
- ): DragController {
- // overSlop should be 0f only if the drag gesture starts with startDragImmediately
- if (overSlop == 0f) error("Consider using onDragStartedImmediately()")
+ ): NestedDraggable.Controller {
return onDragStarted(
draggableHandler = draggableHandler,
- pointersInfo = pointersInfo,
overSlop = overSlop,
+ position = position,
+ pointersDown = pointersDown,
+ pointerType = pointerType,
expectedConsumedOverSlop = expectedConsumedOverSlop,
)
}
fun onDragStarted(
- draggableHandler: DraggableHandler,
- pointersInfo: PointersInfo.PointersDown = pointersDown(),
- overSlop: Float = 0f,
+ draggableHandler: NestedDraggable,
+ overSlop: Float,
+ position: Offset = Offset.Zero,
+ pointersDown: Int = 1,
+ pointerType: PointerType? = PointerType.Touch,
expectedConsumedOverSlop: Float = overSlop,
- ): DragController {
+ ): NestedDraggable.Controller {
+ // overSlop should be 0f only if the drag gesture starts with startDragImmediately.
+ if (overSlop == 0f) error("Consider using onDragStartedImmediately()")
+
val dragController =
- draggableHandler.onDragStarted(pointersDown = pointersInfo, overSlop = overSlop)
+ draggableHandler.onDragStarted(position, overSlop.sign, pointersDown, pointerType)
- // MultiPointerDraggable will always call onDelta with the initial overSlop right after
+ // MultiPointerDraggable will always call onDelta with the initial overSlop right after.
dragController.onDragDelta(pixels = overSlop, expectedConsumedOverSlop)
return dragController
}
- fun DragController.onDragDelta(pixels: Float, expectedConsumed: Float = pixels) {
+ fun NestedDraggable.Controller.onDragDelta(
+ pixels: Float,
+ expectedConsumed: Float = pixels,
+ ) {
val consumed = onDrag(delta = pixels)
assertThat(consumed).isEqualTo(expectedConsumed)
}
- suspend fun DragController.onDragStoppedAnimateNow(
+ suspend fun NestedDraggable.Controller.onDragStoppedAnimateNow(
velocity: Float,
onAnimationStart: () -> Unit,
onAnimationEnd: (Float) -> Unit,
@@ -248,7 +246,7 @@ class DraggableHandlerTest {
onAnimationEnd(velocityConsumed.await())
}
- suspend fun DragController.onDragStoppedAnimateNow(
+ suspend fun NestedDraggable.Controller.onDragStoppedAnimateNow(
velocity: Float,
onAnimationStart: () -> Unit,
) =
@@ -258,8 +256,8 @@ class DraggableHandlerTest {
onAnimationEnd = {},
)
- fun DragController.onDragStoppedAnimateLater(velocity: Float): Deferred<Float> {
- val velocityConsumed = testScope.async { onStop(velocity) }
+ fun NestedDraggable.Controller.onDragStoppedAnimateLater(velocity: Float): Deferred<Float> {
+ val velocityConsumed = testScope.async { onDragStopped(velocity, awaitFling = {}) }
testScope.testScheduler.runCurrent()
return velocityConsumed
}
@@ -408,12 +406,13 @@ class DraggableHandlerTest {
}
@Test
- fun onDragIntoNoAction_stayIdle() = runGestureTest {
+ fun onDragIntoNoAction_overscrolls() = runGestureTest {
navigateToSceneC()
- // We are on SceneC which has no action in Down direction
+ // We are on SceneC which has no action in Down direction, we still start a transition so
+ // that we can overscroll on that scene.
onDragStarted(overSlop = 10f, expectedConsumedOverSlop = 0f)
- assertIdle(currentScene = SceneC)
+ assertTransition(fromScene = SceneC, toScene = SceneB, progress = 0f)
}
@Test
@@ -422,8 +421,7 @@ class DraggableHandlerTest {
mutableUserActionsA = mapOf(Swipe.Up to UserActionResult(SceneB), Swipe.Down to SceneC)
val dragController =
onDragStarted(
- pointersInfo =
- pointersDown(startedPosition = Offset(SCREEN_SIZE * 0.5f, SCREEN_SIZE * 0.5f)),
+ position = Offset(SCREEN_SIZE * 0.5f, SCREEN_SIZE * 0.5f),
overSlop = up(fractionOfScreen = 0.2f),
)
assertTransition(
@@ -447,7 +445,7 @@ class DraggableHandlerTest {
// Start dragging from the bottom
onDragStarted(
- pointersInfo = pointersDown(startedPosition = Offset(SCREEN_SIZE * 0.5f, SCREEN_SIZE)),
+ position = Offset(SCREEN_SIZE * 0.5f, SCREEN_SIZE),
overSlop = up(fractionOfScreen = 0.1f),
)
assertTransition(
@@ -548,8 +546,7 @@ class DraggableHandlerTest {
navigateToSceneC()
// Swipe up from the middle to transition to scene B.
- val middle = pointersDown(startedPosition = Offset(SCREEN_SIZE / 2f, SCREEN_SIZE / 2f))
- onDragStarted(pointersInfo = middle, overSlop = up(0.1f))
+ onDragStarted(position = Offset(SCREEN_SIZE / 2f, SCREEN_SIZE / 2f), overSlop = up(0.1f))
assertTransition(fromScene = SceneC, toScene = SceneB, isUserInputOngoing = true)
// Freeze the transition.
@@ -602,8 +599,11 @@ class DraggableHandlerTest {
@Test
fun transitionIsImmediatelyUpdatedWhenReleasingFinger() = runGestureTest {
// Swipe up from the middle to transition to scene B.
- val middle = pointersDown(startedPosition = Offset(SCREEN_SIZE / 2f, SCREEN_SIZE / 2f))
- val dragController = onDragStarted(pointersInfo = middle, overSlop = up(0.1f))
+ val dragController =
+ onDragStarted(
+ position = Offset(SCREEN_SIZE / 2f, SCREEN_SIZE / 2f),
+ overSlop = up(0.1f),
+ )
assertTransition(fromScene = SceneA, toScene = SceneB, isUserInputOngoing = true)
dragController.onDragStoppedAnimateLater(velocity = 0f)
@@ -613,8 +613,11 @@ class DraggableHandlerTest {
@Test
fun emptyOverscrollAbortsSettleAnimationAndExposeTheConsumedVelocity() = runGestureTest {
// Swipe up to scene B at progress = 200%.
- val middle = pointersDown(startedPosition = Offset(SCREEN_SIZE / 2f, SCREEN_SIZE / 2f))
- val dragController = onDragStarted(pointersInfo = middle, overSlop = up(0.99f))
+ val dragController =
+ onDragStarted(
+ position = Offset(SCREEN_SIZE / 2f, SCREEN_SIZE / 2f),
+ overSlop = up(0.99f),
+ )
assertTransition(fromScene = SceneA, toScene = SceneB, progress = 0.99f)
// Release the finger.
@@ -638,9 +641,11 @@ class DraggableHandlerTest {
from(SceneA, to = SceneB) {}
}
- val middle = pointersDown(startedPosition = Offset(SCREEN_SIZE / 2f, SCREEN_SIZE / 2f))
-
- val dragController = onDragStarted(pointersInfo = middle, overSlop = up(0.5f))
+ val dragController =
+ onDragStarted(
+ position = Offset(SCREEN_SIZE / 2f, SCREEN_SIZE / 2f),
+ overSlop = up(0.5f),
+ )
val transition = assertThat(transitionState).isSceneTransition()
assertThat(transition).hasFromScene(SceneA)
assertThat(transition).hasToScene(SceneB)
@@ -667,9 +672,11 @@ class DraggableHandlerTest {
from(SceneA, to = SceneC) {}
}
- val middle = pointersDown(startedPosition = Offset(SCREEN_SIZE / 2f, SCREEN_SIZE / 2f))
-
- val dragController = onDragStarted(pointersInfo = middle, overSlop = down(0.5f))
+ val dragController =
+ onDragStarted(
+ position = Offset(SCREEN_SIZE / 2f, SCREEN_SIZE / 2f),
+ overSlop = down(0.5f),
+ )
val transition = assertThat(transitionState).isSceneTransition()
assertThat(transition).hasFromScene(SceneA)
assertThat(transition).hasToScene(SceneC)
@@ -695,11 +702,9 @@ class DraggableHandlerTest {
from(SceneA, to = SceneB) { spec = spring(dampingRatio = Spring.DampingRatioNoBouncy) }
}
- val middle = pointersDown(startedPosition = Offset(SCREEN_SIZE / 2f, SCREEN_SIZE / 2f))
-
val dragController =
onDragStarted(
- pointersInfo = middle,
+ position = Offset(SCREEN_SIZE / 2f, SCREEN_SIZE / 2f),
overSlop = up(1.5f),
expectedConsumedOverSlop = up(1f),
)
@@ -729,11 +734,9 @@ class DraggableHandlerTest {
from(SceneA, to = SceneC) { spec = spring(dampingRatio = Spring.DampingRatioNoBouncy) }
}
- val middle = pointersDown(startedPosition = Offset(SCREEN_SIZE / 2f, SCREEN_SIZE / 2f))
-
val dragController =
onDragStarted(
- pointersInfo = middle,
+ position = Offset(SCREEN_SIZE / 2f, SCREEN_SIZE / 2f),
overSlop = down(1.5f),
expectedConsumedOverSlop = down(1f),
)
diff --git a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/MultiPointerDraggableTest.kt b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/MultiPointerDraggableTest.kt
deleted file mode 100644
index 5c6f91bb0e90..000000000000
--- a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/MultiPointerDraggableTest.kt
+++ /dev/null
@@ -1,866 +0,0 @@
-/*
- * Copyright (C) 2024 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.compose.animation.scene
-
-import androidx.compose.foundation.gestures.Orientation
-import androidx.compose.foundation.gestures.rememberScrollableState
-import androidx.compose.foundation.gestures.scrollable
-import androidx.compose.foundation.layout.Box
-import androidx.compose.foundation.layout.fillMaxSize
-import androidx.compose.foundation.layout.size
-import androidx.compose.runtime.getValue
-import androidx.compose.runtime.mutableStateOf
-import androidx.compose.runtime.setValue
-import androidx.compose.ui.Modifier
-import androidx.compose.ui.geometry.Offset
-import androidx.compose.ui.geometry.Size
-import androidx.compose.ui.input.nestedscroll.NestedScrollConnection
-import androidx.compose.ui.input.nestedscroll.NestedScrollDispatcher
-import androidx.compose.ui.input.nestedscroll.NestedScrollSource
-import androidx.compose.ui.input.nestedscroll.nestedScroll
-import androidx.compose.ui.input.pointer.AwaitPointerEventScope
-import androidx.compose.ui.input.pointer.PointerEventPass
-import androidx.compose.ui.input.pointer.PointerInputChange
-import androidx.compose.ui.input.pointer.pointerInput
-import androidx.compose.ui.platform.LocalDensity
-import androidx.compose.ui.platform.LocalViewConfiguration
-import androidx.compose.ui.test.TouchInjectionScope
-import androidx.compose.ui.test.junit4.createComposeRule
-import androidx.compose.ui.test.onRoot
-import androidx.compose.ui.test.performTouchInput
-import androidx.compose.ui.unit.Density
-import androidx.compose.ui.unit.Velocity
-import androidx.test.ext.junit.runners.AndroidJUnit4
-import com.android.compose.modifiers.thenIf
-import com.google.common.truth.Truth.assertThat
-import kotlin.properties.Delegates
-import kotlinx.coroutines.coroutineScope
-import kotlinx.coroutines.isActive
-import org.junit.Rule
-import org.junit.Test
-import org.junit.runner.RunWith
-
-@RunWith(AndroidJUnit4::class)
-class MultiPointerDraggableTest {
- @get:Rule val rule = createComposeRule()
-
- private val emptyConnection = object : NestedScrollConnection {}
- private val defaultDispatcher = NestedScrollDispatcher()
-
- private fun Modifier.nestedScrollDispatcher() = nestedScroll(emptyConnection, defaultDispatcher)
-
- private class SimpleDragController(
- val onDrag: (delta: Float) -> Unit,
- val onStop: (velocity: Float) -> Unit,
- ) : DragController {
- override fun onDrag(delta: Float): Float {
- onDrag.invoke(delta)
- return delta
- }
-
- override suspend fun onStop(velocity: Float): Float {
- onStop.invoke(velocity)
- return velocity
- }
-
- override fun onCancel() {
- error("MultiPointerDraggable never calls onCancel()")
- }
- }
-
- @Test
- fun cancellingPointerCallsOnDragStopped() {
- val size = 200f
- val middle = Offset(size / 2f, size / 2f)
-
- var enabled by mutableStateOf(false)
- var started = false
- var dragged = false
- var stopped = false
-
- var touchSlop = 0f
- rule.setContent {
- touchSlop = LocalViewConfiguration.current.touchSlop
- Box(
- Modifier.size(with(LocalDensity.current) { Size(size, size).toDpSize() })
- .nestedScrollDispatcher()
- .thenIf(enabled) {
- Modifier.multiPointerDraggable(
- orientation = Orientation.Vertical,
- onDragStarted = { _, _ ->
- started = true
- SimpleDragController(
- onDrag = { dragged = true },
- onStop = { stopped = true },
- )
- },
- dispatcher = defaultDispatcher,
- )
- }
- )
- }
-
- fun startDraggingDown() {
- rule.onRoot().performTouchInput {
- down(middle)
- moveBy(Offset(0f, touchSlop))
- }
- }
-
- fun releaseFinger() {
- rule.onRoot().performTouchInput { up() }
- }
-
- // Swiping down does nothing because enabled is false.
- startDraggingDown()
- assertThat(started).isFalse()
- assertThat(dragged).isFalse()
- assertThat(stopped).isFalse()
- releaseFinger()
-
- // Enable the draggable and swipe down. This should both call onDragStarted() and
- // onDragDelta().
- enabled = true
- rule.waitForIdle()
- startDraggingDown()
- assertThat(started).isTrue()
- assertThat(dragged).isTrue()
- assertThat(stopped).isFalse()
-
- // Disable the pointer input. This should call onDragStopped() even if didn't release the
- // finger yet.
- enabled = false
- rule.waitForIdle()
- assertThat(started).isTrue()
- assertThat(dragged).isTrue()
- assertThat(stopped).isTrue()
- }
-
- @Test
- fun shouldNotStartDragEventsWith0PointersDown() {
- val size = 200f
- val middle = Offset(size / 2f, size / 2f)
-
- var started = false
- var dragged = false
- var stopped = false
- var consumedByDescendant = false
-
- var touchSlop = 0f
- rule.setContent {
- touchSlop = LocalViewConfiguration.current.touchSlop
- Box(
- Modifier.size(with(LocalDensity.current) { Size(size, size).toDpSize() })
- .nestedScrollDispatcher()
- .multiPointerDraggable(
- orientation = Orientation.Vertical,
- onDragStarted = { _, _ ->
- started = true
- SimpleDragController(
- onDrag = { dragged = true },
- onStop = { stopped = true },
- )
- },
- dispatcher = defaultDispatcher,
- )
- .pointerInput(Unit) {
- coroutineScope {
- awaitPointerEventScope {
- while (isActive) {
- val change = awaitPointerEvent().changes.first()
- if (consumedByDescendant) {
- change.consume()
- }
- }
- }
- }
- }
- )
- }
-
- // The first part of the gesture is consumed by our descendant
- consumedByDescendant = true
- rule.onRoot().performTouchInput {
- down(middle)
- moveBy(Offset(0f, touchSlop))
- }
-
- // The events were consumed by our descendant, we should not start a drag gesture.
- assertThat(started).isFalse()
- assertThat(dragged).isFalse()
- assertThat(stopped).isFalse()
-
- // The next events could be consumed by us
- consumedByDescendant = false
- rule.onRoot().performTouchInput {
- // The pointer is moved to a new position without reporting it
- updatePointerBy(0, Offset(0f, touchSlop))
-
- // The pointer report an "up" (0 pointers down) with a new position
- up()
- }
-
- // The "up" event should not be used to start a drag gesture
- assertThat(started).isFalse()
- assertThat(dragged).isFalse()
- assertThat(stopped).isFalse()
- }
-
- @Test
- fun handleDisappearingScrollableDuringAGesture() {
- val size = 200f
- val middle = Offset(size / 2f, size / 2f)
-
- var started = false
- var dragged = false
- var stopped = false
- var consumedByScroll = false
- var hasScrollable by mutableStateOf(true)
-
- var touchSlop = 0f
- rule.setContent {
- touchSlop = LocalViewConfiguration.current.touchSlop
- Box(
- Modifier.size(with(LocalDensity.current) { Size(size, size).toDpSize() })
- .nestedScrollDispatcher()
- .multiPointerDraggable(
- orientation = Orientation.Vertical,
- onDragStarted = { _, _ ->
- started = true
- SimpleDragController(
- onDrag = { dragged = true },
- onStop = { stopped = true },
- )
- },
- dispatcher = defaultDispatcher,
- )
- ) {
- if (hasScrollable) {
- Box(
- Modifier.scrollable(
- // Consume all the vertical scroll gestures
- rememberScrollableState(
- consumeScrollDelta = {
- consumedByScroll = true
- it
- }
- ),
- Orientation.Vertical,
- )
- .fillMaxSize()
- )
- }
- }
- }
-
- fun startDraggingDown() {
- rule.onRoot().performTouchInput {
- down(middle)
- moveBy(Offset(0f, touchSlop))
- }
- }
-
- fun continueDraggingDown() {
- rule.onRoot().performTouchInput { moveBy(Offset(0f, touchSlop)) }
- }
-
- fun releaseFinger() {
- rule.onRoot().performTouchInput { up() }
- }
-
- // Swipe down. This should intercepted by the scrollable modifier.
- startDraggingDown()
- assertThat(consumedByScroll).isTrue()
- assertThat(started).isFalse()
- assertThat(dragged).isFalse()
- assertThat(stopped).isFalse()
-
- // Reset the scroll state for the test
- consumedByScroll = false
-
- // Suddenly remove the scrollable container
- hasScrollable = false
- rule.waitForIdle()
-
- // Swipe down. This will be intercepted by multiPointerDraggable, it will wait touchSlop
- // before consuming it.
- continueDraggingDown()
- assertThat(consumedByScroll).isFalse()
- assertThat(started).isFalse()
- assertThat(dragged).isFalse()
- assertThat(stopped).isFalse()
-
- // Swipe down. This should both call onDragStarted() and onDragDelta().
- continueDraggingDown()
- assertThat(consumedByScroll).isFalse()
- assertThat(started).isTrue()
- assertThat(dragged).isTrue()
- assertThat(stopped).isFalse()
-
- rule.waitForIdle()
- releaseFinger()
- assertThat(stopped).isTrue()
- }
-
- @Test
- fun multiPointerWaitAConsumableEventInMainPass() {
- val size = 200f
- val middle = Offset(size / 2f, size / 2f)
-
- var started = false
- var dragged = false
- var stopped = false
-
- var childConsumesOnPass: PointerEventPass? = null
-
- suspend fun AwaitPointerEventScope.childPointerInputScope() {
- awaitPointerEvent(PointerEventPass.Initial).also { initial ->
- // Check unconsumed: it should be always true
- assertThat(initial.changes.any { it.isConsumed }).isFalse()
-
- if (childConsumesOnPass == PointerEventPass.Initial) {
- initial.changes.first().consume()
- }
- }
-
- awaitPointerEvent(PointerEventPass.Main).also { main ->
- // Check unconsumed
- if (childConsumesOnPass != PointerEventPass.Initial) {
- assertThat(main.changes.any { it.isConsumed }).isFalse()
- }
-
- if (childConsumesOnPass == PointerEventPass.Main) {
- main.changes.first().consume()
- }
- }
- }
-
- var touchSlop = 0f
- rule.setContent {
- touchSlop = LocalViewConfiguration.current.touchSlop
- Box(
- Modifier.size(with(LocalDensity.current) { Size(size, size).toDpSize() })
- .nestedScrollDispatcher()
- .multiPointerDraggable(
- orientation = Orientation.Vertical,
- onDragStarted = { _, _ ->
- started = true
- SimpleDragController(
- onDrag = { dragged = true },
- onStop = { stopped = true },
- )
- },
- dispatcher = defaultDispatcher,
- )
- ) {
- Box(
- Modifier.pointerInput(Unit) {
- coroutineScope {
- awaitPointerEventScope {
- while (isActive) {
- childPointerInputScope()
- }
- }
- }
- }
- .fillMaxSize()
- )
- }
- }
-
- fun startDraggingDown() {
- rule.onRoot().performTouchInput {
- down(middle)
- moveBy(Offset(0f, touchSlop))
- }
- }
-
- fun continueDraggingDown() {
- rule.onRoot().performTouchInput { moveBy(Offset(0f, touchSlop)) }
- }
-
- childConsumesOnPass = PointerEventPass.Initial
-
- startDraggingDown()
- assertThat(started).isFalse()
- assertThat(dragged).isFalse()
- assertThat(stopped).isFalse()
-
- continueDraggingDown()
- assertThat(started).isFalse()
- assertThat(dragged).isFalse()
- assertThat(stopped).isFalse()
-
- childConsumesOnPass = PointerEventPass.Main
-
- continueDraggingDown()
- assertThat(started).isFalse()
- assertThat(dragged).isFalse()
- assertThat(stopped).isFalse()
-
- continueDraggingDown()
- assertThat(started).isFalse()
- assertThat(dragged).isFalse()
- assertThat(stopped).isFalse()
-
- childConsumesOnPass = null
-
- // Swipe down. This will be intercepted by multiPointerDraggable, it will wait touchSlop
- // before consuming it.
- continueDraggingDown()
- assertThat(started).isFalse()
- assertThat(dragged).isFalse()
- assertThat(stopped).isFalse()
-
- // Swipe down. This should both call onDragStarted() and onDragDelta().
- continueDraggingDown()
- assertThat(started).isTrue()
- assertThat(dragged).isTrue()
- assertThat(stopped).isFalse()
-
- childConsumesOnPass = PointerEventPass.Main
-
- continueDraggingDown()
- assertThat(stopped).isTrue()
-
- // Complete the gesture
- rule.onRoot().performTouchInput { up() }
- }
-
- @Test
- fun multiPointerDuringAnotherGestureWaitAConsumableEventAfterMainPass() {
- val size = 200f
- val middle = Offset(size / 2f, size / 2f)
-
- var verticalStarted = false
- var verticalDragged = false
- var verticalStopped = false
- var horizontalStarted = false
- var horizontalDragged = false
- var horizontalStopped = false
-
- var touchSlop = 0f
- rule.setContent {
- touchSlop = LocalViewConfiguration.current.touchSlop
- Box(
- Modifier.size(with(LocalDensity.current) { Size(size, size).toDpSize() })
- .nestedScrollDispatcher()
- .multiPointerDraggable(
- orientation = Orientation.Vertical,
- onDragStarted = { _, _ ->
- verticalStarted = true
- SimpleDragController(
- onDrag = { verticalDragged = true },
- onStop = { verticalStopped = true },
- )
- },
- dispatcher = defaultDispatcher,
- )
- .multiPointerDraggable(
- orientation = Orientation.Horizontal,
- onDragStarted = { _, _ ->
- horizontalStarted = true
- SimpleDragController(
- onDrag = { horizontalDragged = true },
- onStop = { horizontalStopped = true },
- )
- },
- dispatcher = defaultDispatcher,
- )
- )
- }
-
- fun startDraggingDown() {
- rule.onRoot().performTouchInput {
- down(middle)
- moveBy(Offset(0f, touchSlop))
- }
- }
-
- fun startDraggingRight() {
- rule.onRoot().performTouchInput {
- down(middle)
- moveBy(Offset(touchSlop, 0f))
- }
- }
-
- fun stopDragging() {
- rule.onRoot().performTouchInput { up() }
- }
-
- fun continueDown() {
- rule.onRoot().performTouchInput { moveBy(Offset(0f, touchSlop)) }
- }
-
- fun continueRight() {
- rule.onRoot().performTouchInput { moveBy(Offset(touchSlop, 0f)) }
- }
-
- startDraggingDown()
- assertThat(verticalStarted).isTrue()
- assertThat(verticalDragged).isTrue()
- assertThat(verticalStopped).isFalse()
-
- // Ignore right swipe, do not interrupt the dragging gesture.
- continueRight()
- assertThat(horizontalStarted).isFalse()
- assertThat(horizontalDragged).isFalse()
- assertThat(horizontalStopped).isFalse()
- assertThat(verticalStopped).isFalse()
-
- stopDragging()
- assertThat(verticalStopped).isTrue()
-
- verticalStarted = false
- verticalDragged = false
- verticalStopped = false
-
- startDraggingRight()
- assertThat(horizontalStarted).isTrue()
- assertThat(horizontalDragged).isTrue()
- assertThat(horizontalStopped).isFalse()
-
- // Ignore down swipe, do not interrupt the dragging gesture.
- continueDown()
- assertThat(verticalStarted).isFalse()
- assertThat(verticalDragged).isFalse()
- assertThat(verticalStopped).isFalse()
- assertThat(horizontalStopped).isFalse()
-
- stopDragging()
- assertThat(horizontalStopped).isTrue()
- }
-
- @Test
- fun multiPointerSwipeDetectorInteraction() {
- val size = 200f
- val middle = Offset(size / 2f, size / 2f)
-
- var started = false
-
- var capturedChange: PointerInputChange? = null
- var swipeConsume = false
-
- var touchSlop = 0f
- rule.setContent {
- touchSlop = LocalViewConfiguration.current.touchSlop
- Box(
- Modifier.size(with(LocalDensity.current) { Size(size, size).toDpSize() })
- .nestedScrollDispatcher()
- .multiPointerDraggable(
- orientation = Orientation.Vertical,
- swipeDetector =
- object : SwipeDetector {
- override fun detectSwipe(change: PointerInputChange): Boolean {
- capturedChange = change
- return swipeConsume
- }
- },
- onDragStarted = { _, _ ->
- started = true
- SimpleDragController(
- onDrag = { /* do nothing */ },
- onStop = { /* do nothing */ },
- )
- },
- dispatcher = defaultDispatcher,
- )
- ) {}
- }
-
- fun startDraggingDown() {
- rule.onRoot().performTouchInput {
- down(middle)
- moveBy(Offset(0f, touchSlop))
- }
- }
-
- fun dragDown() {
- rule.onRoot().performTouchInput { moveBy(Offset(0f, touchSlop)) }
- }
-
- startDraggingDown()
- assertThat(capturedChange).isNotNull()
- capturedChange = null
- assertThat(started).isFalse()
-
- swipeConsume = true
- // Drag in same direction
- dragDown()
- assertThat(capturedChange).isNotNull()
- capturedChange = null
-
- dragDown()
- assertThat(capturedChange).isNull()
-
- assertThat(started).isTrue()
- }
-
- @Test
- fun multiPointerSwipeDetectorInteractionZeroOffsetFromStartPosition() {
- val size = 200f
- val middle = Offset(size / 2f, size / 2f)
-
- var started = false
-
- var capturedChange: PointerInputChange? = null
- var swipeConsume = false
-
- var touchSlop = 0f
- rule.setContent {
- touchSlop = LocalViewConfiguration.current.touchSlop
- Box(
- Modifier.size(with(LocalDensity.current) { Size(size, size).toDpSize() })
- .nestedScrollDispatcher()
- .multiPointerDraggable(
- orientation = Orientation.Vertical,
- swipeDetector =
- object : SwipeDetector {
- override fun detectSwipe(change: PointerInputChange): Boolean {
- capturedChange = change
- return swipeConsume
- }
- },
- onDragStarted = { _, _ ->
- started = true
- SimpleDragController(
- onDrag = { /* do nothing */ },
- onStop = { /* do nothing */ },
- )
- },
- dispatcher = defaultDispatcher,
- )
- ) {}
- }
-
- fun startDraggingDown() {
- rule.onRoot().performTouchInput {
- down(middle)
- moveBy(Offset(0f, touchSlop))
- }
- }
-
- fun dragUp() {
- rule.onRoot().performTouchInput { moveBy(Offset(0f, -touchSlop)) }
- }
-
- startDraggingDown()
- assertThat(capturedChange).isNotNull()
- capturedChange = null
- assertThat(started).isFalse()
-
- swipeConsume = true
- // Drag in the opposite direction
- dragUp()
- assertThat(capturedChange).isNotNull()
- capturedChange = null
-
- dragUp()
- assertThat(capturedChange).isNull()
-
- assertThat(started).isTrue()
- }
-
- @Test
- fun multiPointerNestedScrollDispatcher() {
- val size = 200f
- val middle = Offset(size / 2f, size / 2f)
- var touchSlop = 0f
-
- var consumedOnPreScroll = 0f
-
- var availableOnPreScroll = Float.MIN_VALUE
- var availableOnPostScroll = Float.MIN_VALUE
- var availableOnPreFling = Float.MIN_VALUE
- var availableOnPostFling = Float.MIN_VALUE
-
- var consumedOnDrag = 0f
- var consumedOnDragStop = 0f
-
- val connection =
- object : NestedScrollConnection {
- override fun onPreScroll(available: Offset, source: NestedScrollSource): Offset {
- availableOnPreScroll = available.y
- return Offset(0f, consumedOnPreScroll)
- }
-
- override fun onPostScroll(
- consumed: Offset,
- available: Offset,
- source: NestedScrollSource,
- ): Offset {
- availableOnPostScroll = available.y
- return Offset.Zero
- }
-
- override suspend fun onPreFling(available: Velocity): Velocity {
- availableOnPreFling = available.y
- return Velocity.Zero
- }
-
- override suspend fun onPostFling(
- consumed: Velocity,
- available: Velocity,
- ): Velocity {
- availableOnPostFling = available.y
- return Velocity.Zero
- }
- }
-
- rule.setContent {
- touchSlop = LocalViewConfiguration.current.touchSlop
- Box(
- Modifier.size(with(LocalDensity.current) { Size(size, size).toDpSize() })
- .nestedScroll(connection)
- .nestedScrollDispatcher()
- .multiPointerDraggable(
- orientation = Orientation.Vertical,
- onDragStarted = { _, _ ->
- SimpleDragController(
- onDrag = { consumedOnDrag = it },
- onStop = { consumedOnDragStop = it },
- )
- },
- dispatcher = defaultDispatcher,
- )
- )
- }
-
- fun startDrag() {
- rule.onRoot().performTouchInput {
- down(middle)
- moveBy(Offset(0f, touchSlop))
- }
- }
-
- fun continueDrag() {
- rule.onRoot().performTouchInput { moveBy(Offset(0f, touchSlop)) }
- }
-
- fun stopDrag() {
- rule.onRoot().performTouchInput { up() }
- }
-
- startDrag()
-
- continueDrag()
- assertThat(availableOnPreScroll).isEqualTo(touchSlop)
- assertThat(consumedOnDrag).isEqualTo(touchSlop)
- assertThat(availableOnPostScroll).isEqualTo(0f)
-
- // Parent node consumes half of the gesture
- consumedOnPreScroll = touchSlop / 2f
- continueDrag()
- assertThat(availableOnPreScroll).isEqualTo(touchSlop)
- assertThat(consumedOnDrag).isEqualTo(touchSlop / 2f)
- assertThat(availableOnPostScroll).isEqualTo(0f)
-
- // Parent node consumes the gesture
- consumedOnPreScroll = touchSlop
- continueDrag()
- assertThat(availableOnPreScroll).isEqualTo(touchSlop)
- assertThat(consumedOnDrag).isEqualTo(0f)
- assertThat(availableOnPostScroll).isEqualTo(0f)
-
- // Parent node can intercept the velocity on stop
- stopDrag()
- assertThat(availableOnPreFling).isEqualTo(consumedOnDragStop)
- assertThat(availableOnPostFling).isEqualTo(0f)
- }
-
- @Test
- fun multiPointerOnStopVelocity() {
- val size = 200f
- val middle = Offset(size / 2f, size / 2f)
-
- var stopped = false
- var lastVelocity = -1f
- var touchSlop = 0f
- var density: Density by Delegates.notNull()
- rule.setContent {
- touchSlop = LocalViewConfiguration.current.touchSlop
- density = LocalDensity.current
- Box(
- Modifier.size(with(density) { Size(size, size).toDpSize() })
- .nestedScrollDispatcher()
- .multiPointerDraggable(
- orientation = Orientation.Vertical,
- onDragStarted = { _, _ ->
- SimpleDragController(
- onDrag = { /* do nothing */ },
- onStop = {
- stopped = true
- lastVelocity = it
- },
- )
- },
- dispatcher = defaultDispatcher,
- )
- )
- }
-
- var eventMillis: Long by Delegates.notNull()
- rule.onRoot().performTouchInput { eventMillis = eventPeriodMillis }
-
- fun swipeGesture(block: TouchInjectionScope.() -> Unit) {
- stopped = false
- rule.onRoot().performTouchInput {
- down(middle)
- block()
- up()
- }
- assertThat(stopped).isEqualTo(true)
- }
-
- val shortDistance = touchSlop / 2f
- swipeGesture {
- moveBy(delta = Offset(0f, shortDistance), delayMillis = eventMillis)
- moveBy(delta = Offset(0f, shortDistance), delayMillis = eventMillis)
- }
- assertThat(lastVelocity).isGreaterThan(0f)
- assertThat(lastVelocity).isWithin(1f).of((shortDistance / eventMillis) * 1000f)
-
- val longDistance = touchSlop * 4f
- swipeGesture {
- moveBy(delta = Offset(0f, longDistance), delayMillis = eventMillis)
- moveBy(delta = Offset(0f, longDistance), delayMillis = eventMillis)
- }
- assertThat(lastVelocity).isGreaterThan(0f)
- assertThat(lastVelocity).isWithin(1f).of((longDistance / eventMillis) * 1000f)
-
- rule.onRoot().performTouchInput {
- down(pointerId = 0, position = middle)
- down(pointerId = 1, position = middle)
- moveBy(pointerId = 0, delta = Offset(0f, longDistance), delayMillis = eventMillis)
- moveBy(pointerId = 0, delta = Offset(0f, longDistance), delayMillis = eventMillis)
- // The velocity should be:
- // (longDistance / eventMillis) pixels/ms
-
- // 1 pointer left, the second one
- up(pointerId = 0)
-
- // After a few events the velocity should be:
- // (shortDistance / eventMillis) pixels/ms
- repeat(10) {
- moveBy(pointerId = 1, delta = Offset(0f, shortDistance), delayMillis = eventMillis)
- }
- up(pointerId = 1)
- }
- assertThat(lastVelocity).isGreaterThan(0f)
- assertThat(lastVelocity).isWithin(1f).of((shortDistance / eventMillis) * 1000f)
- }
-}
diff --git a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/SwipeToSceneTest.kt b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/SwipeToSceneTest.kt
index 0355a30d5c73..e03608466dae 100644
--- a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/SwipeToSceneTest.kt
+++ b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/SwipeToSceneTest.kt
@@ -128,7 +128,7 @@ class SwipeToSceneTest {
mapOf(
Swipe.Down to SceneA,
Swipe.Down(pointerCount = 2) to SceneB,
- Swipe.Down(pointersType = PointerType.Mouse) to SceneD,
+ Swipe.Down(pointerType = PointerType.Mouse) to SceneD,
Swipe.Down(fromSource = Edge.Top) to SceneB,
Swipe.Right(fromSource = Edge.Left) to SceneB,
)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/floatingmenu/MenuViewTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/floatingmenu/MenuViewTest.java
index 5ff7bd063427..737170f6a665 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/floatingmenu/MenuViewTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/floatingmenu/MenuViewTest.java
@@ -18,17 +18,25 @@ package com.android.systemui.accessibility.floatingmenu;
import static android.app.UiModeManager.MODE_NIGHT_YES;
+import static com.android.internal.accessibility.AccessibilityShortcutController.MAGNIFICATION_COMPONENT_NAME;
+import static com.android.internal.accessibility.AccessibilityShortcutController.MAGNIFICATION_CONTROLLER_NAME;
+
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.Mockito.doNothing;
+import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import android.app.UiModeManager;
+import android.content.Context;
import android.content.res.Configuration;
import android.graphics.Rect;
import android.graphics.drawable.GradientDrawable;
+import android.platform.test.annotations.DisableFlags;
import android.platform.test.annotations.EnableFlags;
import android.testing.TestableLooper;
import android.view.WindowManager;
@@ -37,6 +45,8 @@ import android.view.accessibility.AccessibilityManager;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
+import com.android.internal.accessibility.common.ShortcutConstants;
+import com.android.internal.accessibility.dialog.AccessibilityTarget;
import com.android.settingslib.bluetooth.HearingAidDeviceManager;
import com.android.systemui.Flags;
import com.android.systemui.Prefs;
@@ -54,6 +64,9 @@ import org.mockito.Mock;
import org.mockito.junit.MockitoJUnit;
import org.mockito.junit.MockitoRule;
+import java.util.ArrayList;
+import java.util.List;
+
/** Tests for {@link MenuView}. */
@RunWith(AndroidJUnit4.class)
@TestableLooper.RunWithLooper(setAsMainLooper = true)
@@ -63,17 +76,24 @@ public class MenuViewTest extends SysuiTestCase {
private int mNightMode;
private UiModeManager mUiModeManager;
private MenuView mMenuView;
+ private MenuView mMenuViewSpy;
private String mLastPosition;
private MenuViewAppearance mStubMenuViewAppearance;
+ private MenuViewModel mMenuViewModel;
+ private final List<String> mShortcutTargets = new ArrayList<>();
@Rule
public MockitoRule mockito = MockitoJUnit.rule();
@Mock
private AccessibilityManager mAccessibilityManager;
+
@Mock
private HearingAidDeviceManager mHearingAidDeviceManager;
+ @Mock
+ private MenuView.OnTargetFeaturesChangeListener mOnTargetFeaturesChangeListener;
+
private SysuiTestableContext mSpyContext;
@Before
@@ -91,22 +111,38 @@ public class MenuViewTest extends SysuiTestCase {
mSpyContext = spy(mContext);
doNothing().when(mSpyContext).startActivity(any());
+ mContext.addMockSystemService(Context.ACCESSIBILITY_SERVICE, mAccessibilityManager);
+ mShortcutTargets.add(MAGNIFICATION_CONTROLLER_NAME);
+ doReturn(mShortcutTargets)
+ .when(mAccessibilityManager)
+ .getAccessibilityShortcutTargets(anyInt());
+
final SecureSettings secureSettings = TestUtils.mockSecureSettings(mContext);
- final MenuViewModel stubMenuViewModel = new MenuViewModel(mContext, mAccessibilityManager,
- secureSettings, mHearingAidDeviceManager);
+ mMenuViewModel =
+ new MenuViewModel(
+ mContext, mAccessibilityManager, secureSettings, mHearingAidDeviceManager);
final WindowManager stubWindowManager = mContext.getSystemService(WindowManager.class);
mStubMenuViewAppearance = new MenuViewAppearance(mSpyContext, stubWindowManager);
- mMenuView = spy(new MenuView(mSpyContext, stubMenuViewModel, mStubMenuViewAppearance,
- secureSettings));
+ mMenuView =
+ new MenuView(mSpyContext, mMenuViewModel, mStubMenuViewAppearance, secureSettings);
+ mMenuView.setOnTargetFeaturesChangeListener(mOnTargetFeaturesChangeListener);
mLastPosition = Prefs.getString(mSpyContext,
Prefs.Key.ACCESSIBILITY_FLOATING_MENU_POSITION, /* defaultValue= */ null);
+
+ mMenuViewSpy =
+ spy(
+ new MenuView(
+ mSpyContext,
+ mMenuViewModel,
+ mStubMenuViewAppearance,
+ secureSettings));
}
@Test
public void onConfigurationChanged_updateViewModel() {
- mMenuView.onConfigurationChanged(/* newConfig= */ null);
+ mMenuViewSpy.onConfigurationChanged(/* newConfig= */ null);
- verify(mMenuView).loadLayoutResources();
+ verify(mMenuViewSpy).loadLayoutResources();
}
@Test
@@ -179,6 +215,75 @@ public class MenuViewTest extends SysuiTestCase {
assertThat(radiiAnimator.isStarted()).isTrue();
}
+ @Test
+ @DisableFlags(Flags.FLAG_FLOATING_MENU_NOTIFY_TARGETS_CHANGED_ON_STRICT_DIFF)
+ public void onTargetFeaturesChanged_listenerCalled_flagDisabled() {
+ // Call show() to start observing the target features change listener.
+ mMenuView.show();
+
+ // The target features change listener should be called when the observer is added.
+ verify(mOnTargetFeaturesChangeListener, times(1)).onChange(any());
+
+ // When the target features list changes, the listener should be called.
+ mMenuViewModel.onTargetFeaturesChanged(
+ List.of(
+ new TestAccessibilityTarget(mContext, 123),
+ new TestAccessibilityTarget(mContext, 456)));
+ verify(mOnTargetFeaturesChangeListener, times(2)).onChange(any());
+
+ // Double check that when the target features list changes, the listener should be called.
+ List<AccessibilityTarget> newFeaturesList =
+ List.of(
+ new TestAccessibilityTarget(mContext, 123),
+ new TestAccessibilityTarget(mContext, 789),
+ new TestAccessibilityTarget(mContext, 456));
+ mMenuViewModel.onTargetFeaturesChanged(newFeaturesList);
+ verify(mOnTargetFeaturesChangeListener, times(3)).onChange(any());
+
+ // When the target features list doesn't change, the listener will still be called.
+ mMenuViewModel.onTargetFeaturesChanged(newFeaturesList);
+ verify(mOnTargetFeaturesChangeListener, times(4)).onChange(any());
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_FLOATING_MENU_NOTIFY_TARGETS_CHANGED_ON_STRICT_DIFF)
+ public void onTargetFeaturesChanged_listenerCalled_flagEnabled() {
+ // Call show() to start observing the target features change listener.
+ mMenuView.show();
+
+ // The target features change listener should be called when the observer is added.
+ verify(mOnTargetFeaturesChangeListener, times(1)).onChange(any());
+
+ // When the target features list changes, the listener should be called.
+ mMenuViewModel.onTargetFeaturesChanged(
+ List.of(
+ new TestAccessibilityTarget(mContext, 123),
+ new TestAccessibilityTarget(mContext, 456)));
+ verify(mOnTargetFeaturesChangeListener, times(2)).onChange(any());
+
+ // Double check that when the target features list changes, the listener should be called.
+ List<AccessibilityTarget> newFeaturesList =
+ List.of(
+ new TestAccessibilityTarget(mContext, 123),
+ new TestAccessibilityTarget(mContext, 789),
+ new TestAccessibilityTarget(mContext, 456));
+ mMenuViewModel.onTargetFeaturesChanged(newFeaturesList);
+ verify(mOnTargetFeaturesChangeListener, times(3)).onChange(any());
+
+ // When the target features list doesn't change, the listener should not be called again.
+ mMenuViewModel.onTargetFeaturesChanged(newFeaturesList);
+ verify(mOnTargetFeaturesChangeListener, times(3)).onChange(any());
+
+ // When the target features list changes order (but the UIDs of the targets don't change),
+ // the listener should be called.
+ mMenuViewModel.onTargetFeaturesChanged(
+ List.of(
+ new TestAccessibilityTarget(mContext, 789),
+ new TestAccessibilityTarget(mContext, 123),
+ new TestAccessibilityTarget(mContext, 456)));
+ verify(mOnTargetFeaturesChangeListener, times(4)).onChange(any());
+ }
+
private InstantInsetLayerDrawable getMenuViewInsetLayer() {
return (InstantInsetLayerDrawable) mMenuView.getBackground();
}
@@ -196,6 +301,23 @@ public class MenuViewTest extends SysuiTestCase {
return radiiAnimator;
}
+ /** Simplified AccessibilityTarget for testing MenuView. */
+ private static class TestAccessibilityTarget extends AccessibilityTarget {
+ TestAccessibilityTarget(Context context, int uid) {
+ // Set fields unused by tests to defaults that allow test compilation.
+ super(
+ context,
+ ShortcutConstants.UserShortcutType.SOFTWARE,
+ 0,
+ false,
+ MAGNIFICATION_COMPONENT_NAME.flattenToString(),
+ uid,
+ null,
+ null,
+ null);
+ }
+ }
+
@After
public void tearDown() throws Exception {
mUiModeManager.setNightMode(mNightMode);
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/hearingaid/HearingDevicesDialogDelegateTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/hearingaid/HearingDevicesDialogDelegateTest.java
index 43d0d69c428f..e4539b75f317 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/hearingaid/HearingDevicesDialogDelegateTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/hearingaid/HearingDevicesDialogDelegateTest.java
@@ -66,7 +66,6 @@ import com.android.settingslib.bluetooth.LocalBluetoothAdapter;
import com.android.settingslib.bluetooth.LocalBluetoothManager;
import com.android.settingslib.bluetooth.LocalBluetoothProfileManager;
import com.android.settingslib.bluetooth.VolumeControlProfile;
-import com.android.systemui.Flags;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.animation.DialogTransitionAnimator;
import com.android.systemui.bluetooth.qsdialog.DeviceItem;
@@ -227,7 +226,6 @@ public class HearingDevicesDialogDelegateTest extends SysuiTestCase {
}
@Test
- @EnableFlags(Flags.FLAG_HEARING_DEVICES_DIALOG_RELATED_TOOLS)
public void showDialog_noLiveCaption_noRelatedToolsInConfig_relatedToolLayoutGone() {
mContext.getOrCreateTestableResources().addOverride(
R.array.config_quickSettingsHearingDevicesRelatedToolName, new String[]{});
@@ -239,7 +237,6 @@ public class HearingDevicesDialogDelegateTest extends SysuiTestCase {
}
@Test
- @EnableFlags(Flags.FLAG_HEARING_DEVICES_DIALOG_RELATED_TOOLS)
public void showDialog_hasLiveCaption_noRelatedToolsInConfig_showOneRelatedTool() {
when(mPackageManager.queryIntentActivities(
eq(LIVE_CAPTION_INTENT), anyInt())).thenReturn(
@@ -254,7 +251,6 @@ public class HearingDevicesDialogDelegateTest extends SysuiTestCase {
}
@Test
- @EnableFlags(Flags.FLAG_HEARING_DEVICES_DIALOG_RELATED_TOOLS)
public void showDialog_hasLiveCaption_oneRelatedToolInConfig_showTwoRelatedTools()
throws PackageManager.NameNotFoundException {
when(mPackageManager.queryIntentActivities(eq(LIVE_CAPTION_INTENT), anyInt()))
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/clipboardoverlay/ClipboardImageLoaderTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/clipboardoverlay/ClipboardImageLoaderTest.kt
index 791f1f2e1f26..6fdeb2b8ebb0 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/clipboardoverlay/ClipboardImageLoaderTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/clipboardoverlay/ClipboardImageLoaderTest.kt
@@ -17,14 +17,17 @@ package com.android.systemui.clipboardoverlay
import android.content.ContentResolver
import android.content.Context
+import android.content.pm.UserInfo
import android.net.Uri
+import android.platform.test.annotations.DisableFlags
+import android.platform.test.annotations.EnableFlags
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
+import com.android.systemui.Flags.FLAG_CLIPBOARD_OVERLAY_MULTIUSER
import com.android.systemui.SysuiTestCase
-import com.android.systemui.util.mockito.whenever
+import com.android.systemui.settings.FakeUserTracker
import java.io.IOException
import kotlinx.coroutines.CoroutineScope
-import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.test.StandardTestDispatcher
import kotlinx.coroutines.test.runTest
import org.junit.Assert.assertNull
@@ -36,44 +39,90 @@ import org.mockito.ArgumentMatchers.eq
import org.mockito.Mock
import org.mockito.Mockito.verify
import org.mockito.MockitoAnnotations
+import org.mockito.kotlin.whenever
@SmallTest
-@OptIn(ExperimentalCoroutinesApi::class)
@RunWith(AndroidJUnit4::class)
class ClipboardImageLoaderTest : SysuiTestCase() {
@Mock private lateinit var mockContext: Context
@Mock private lateinit var mockContentResolver: ContentResolver
+ @Mock private lateinit var mockSecondaryContentResolver: ContentResolver
private lateinit var clipboardImageLoader: ClipboardImageLoader
+ private var fakeUserTracker: FakeUserTracker =
+ FakeUserTracker(userContentResolverProvider = { mockContentResolver })
+
+ private val userInfos = listOf(UserInfo(0, "system", 0), UserInfo(50, "secondary", 0))
@Before
fun setup() {
MockitoAnnotations.initMocks(this)
+
+ fakeUserTracker.set(userInfos, 0)
}
@Test
@Throws(IOException::class)
+ @DisableFlags(FLAG_CLIPBOARD_OVERLAY_MULTIUSER)
+ fun test_imageLoadSuccess_legacy() = runTest {
+ val testDispatcher = StandardTestDispatcher(this.testScheduler)
+ fakeUserTracker =
+ FakeUserTracker(userContentResolverProvider = { mockSecondaryContentResolver })
+ fakeUserTracker.set(userInfos, 1)
+
+ clipboardImageLoader =
+ ClipboardImageLoader(
+ mockContext,
+ fakeUserTracker,
+ testDispatcher,
+ CoroutineScope(testDispatcher),
+ )
+ val testUri = Uri.parse("testUri")
+ whenever<ContentResolver?>(mockContext.contentResolver)
+ .thenReturn(mockSecondaryContentResolver)
+ whenever(mockContext.resources).thenReturn(context.resources)
+
+ clipboardImageLoader.load(testUri)
+
+ verify(mockSecondaryContentResolver).loadThumbnail(eq(testUri), any(), any())
+ }
+
+ @Test
+ @Throws(IOException::class)
+ @EnableFlags(FLAG_CLIPBOARD_OVERLAY_MULTIUSER)
fun test_imageLoadSuccess() = runTest {
val testDispatcher = StandardTestDispatcher(this.testScheduler)
+ fakeUserTracker =
+ FakeUserTracker(userContentResolverProvider = { mockSecondaryContentResolver })
+ fakeUserTracker.set(userInfos, 1)
+
clipboardImageLoader =
- ClipboardImageLoader(mockContext, testDispatcher, CoroutineScope(testDispatcher))
+ ClipboardImageLoader(
+ mockContext,
+ fakeUserTracker,
+ testDispatcher,
+ CoroutineScope(testDispatcher),
+ )
val testUri = Uri.parse("testUri")
- whenever(mockContext.contentResolver).thenReturn(mockContentResolver)
whenever(mockContext.resources).thenReturn(context.resources)
clipboardImageLoader.load(testUri)
- verify(mockContentResolver).loadThumbnail(eq(testUri), any(), any())
+ verify(mockSecondaryContentResolver).loadThumbnail(eq(testUri), any(), any())
}
- @OptIn(ExperimentalCoroutinesApi::class)
@Test
@Throws(IOException::class)
fun test_imageLoadFailure() = runTest {
val testDispatcher = StandardTestDispatcher(this.testScheduler)
clipboardImageLoader =
- ClipboardImageLoader(mockContext, testDispatcher, CoroutineScope(testDispatcher))
+ ClipboardImageLoader(
+ mockContext,
+ fakeUserTracker,
+ testDispatcher,
+ CoroutineScope(testDispatcher),
+ )
val testUri = Uri.parse("testUri")
whenever(mockContext.contentResolver).thenReturn(mockContentResolver)
whenever(mockContext.resources).thenReturn(context.resources)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/HearingDevicesTileTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/HearingDevicesTileTest.java
index 7d41a20e628f..307b87a74dad 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/HearingDevicesTileTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/HearingDevicesTileTest.java
@@ -27,8 +27,6 @@ import static org.mockito.Mockito.when;
import android.content.Intent;
import android.os.Handler;
-import android.platform.test.annotations.DisableFlags;
-import android.platform.test.annotations.EnableFlags;
import android.provider.Settings;
import android.service.quicksettings.Tile;
import android.testing.TestableLooper;
@@ -38,7 +36,6 @@ import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
import com.android.internal.logging.MetricsLogger;
-import com.android.systemui.Flags;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.accessibility.hearingaid.HearingDevicesChecker;
import com.android.systemui.accessibility.hearingaid.HearingDevicesDialogManager;
@@ -124,18 +121,6 @@ public class HearingDevicesTileTest extends SysuiTestCase {
}
@Test
- @EnableFlags(Flags.FLAG_HEARING_AIDS_QS_TILE_DIALOG)
- public void isAvailable_flagEnabled_true() {
- assertThat(mTile.isAvailable()).isTrue();
- }
-
- @Test
- @DisableFlags(Flags.FLAG_HEARING_AIDS_QS_TILE_DIALOG)
- public void isAvailable_flagDisabled_false() {
- assertThat(mTile.isAvailable()).isFalse();
- }
-
- @Test
public void longClick_expectedAction() {
mTile.longClick(null);
mTestableLooper.processAllMessages();
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/hearingdevices/domain/interactor/HearingDevicesTileDataInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/hearingdevices/domain/interactor/HearingDevicesTileDataInteractorTest.kt
index 1dfa2cd26491..9099d3d911bc 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/hearingdevices/domain/interactor/HearingDevicesTileDataInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/hearingdevices/domain/interactor/HearingDevicesTileDataInteractorTest.kt
@@ -17,12 +17,9 @@
package com.android.systemui.qs.tiles.impl.hearingdevices.domain.interactor
import android.os.UserHandle
-import android.platform.test.annotations.DisableFlags
-import android.platform.test.annotations.EnableFlags
import android.platform.test.annotations.EnabledOnRavenwood
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
-import com.android.systemui.Flags
import com.android.systemui.SysuiTestCase
import com.android.systemui.accessibility.hearingaid.HearingDevicesChecker
import com.android.systemui.coroutines.collectLastValue
@@ -66,24 +63,14 @@ class HearingDevicesTileDataInteractorTest : SysuiTestCase() {
underTest = HearingDevicesTileDataInteractor(testScope.testScheduler, controller, checker)
}
- @EnableFlags(Flags.FLAG_HEARING_AIDS_QS_TILE_DIALOG)
@Test
- fun availability_flagEnabled_returnTrue() =
+ fun availability_returnTrue() =
testScope.runTest {
val availability by collectLastValue(underTest.availability(testUser))
assertThat(availability).isTrue()
}
- @DisableFlags(Flags.FLAG_HEARING_AIDS_QS_TILE_DIALOG)
- @Test
- fun availability_flagDisabled_returnFalse() =
- testScope.runTest {
- val availability by collectLastValue(underTest.availability(testUser))
-
- assertThat(availability).isFalse()
- }
-
@Test
fun tileData_bluetoothStateChanged_dataMatchesChecker() =
testScope.runTest {
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/call/ui/viewmodel/CallChipViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/call/ui/viewmodel/CallChipViewModelTest.kt
index 75d000b63d62..ac3089d9286b 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/call/ui/viewmodel/CallChipViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/call/ui/viewmodel/CallChipViewModelTest.kt
@@ -283,14 +283,16 @@ class CallChipViewModelTest : SysuiTestCase() {
testScope.runTest {
val latest by collectLastValue(underTest.chip)
- val intent = mock<PendingIntent>()
- repo.setOngoingCallState(inCallModel(startTimeMs = 1000, intent = intent))
+ val pendingIntent = mock<PendingIntent>()
+ repo.setOngoingCallState(inCallModel(startTimeMs = 1000, intent = pendingIntent))
val clickListener = (latest as OngoingActivityChipModel.Shown).onClickListener
assertThat(clickListener).isNotNull()
clickListener!!.onClick(chipView)
- verify(kosmos.activityStarter).postStartActivityDismissingKeyguard(intent, null)
+ // Ensure that the SysUI didn't modify the notification's intent by verifying it
+ // directly matches the `PendingIntent` set -- see b/212467440.
+ verify(kosmos.activityStarter).postStartActivityDismissingKeyguard(pendingIntent, null)
}
@Test
@@ -298,14 +300,16 @@ class CallChipViewModelTest : SysuiTestCase() {
testScope.runTest {
val latest by collectLastValue(underTest.chip)
- val intent = mock<PendingIntent>()
- repo.setOngoingCallState(inCallModel(startTimeMs = 0, intent = intent))
+ val pendingIntent = mock<PendingIntent>()
+ repo.setOngoingCallState(inCallModel(startTimeMs = 0, intent = pendingIntent))
val clickListener = (latest as OngoingActivityChipModel.Shown).onClickListener
assertThat(clickListener).isNotNull()
clickListener!!.onClick(chipView)
- verify(kosmos.activityStarter).postStartActivityDismissingKeyguard(intent, null)
+ // Ensure that the SysUI didn't modify the notification's intent by verifying it
+ // directly matches the `PendingIntent` set -- see b/212467440.
+ verify(kosmos.activityStarter).postStartActivityDismissingKeyguard(pendingIntent, null)
}
companion object {
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/coordinator/RankingCoordinatorTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/coordinator/RankingCoordinatorTest.java
index c05b13163d32..c5752691da44 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/coordinator/RankingCoordinatorTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/coordinator/RankingCoordinatorTest.java
@@ -35,6 +35,7 @@ import static org.mockito.Mockito.when;
import android.app.Notification;
import android.app.NotificationChannel;
import android.app.NotificationManager;
+import android.platform.test.annotations.EnableFlags;
import androidx.annotation.Nullable;
import androidx.test.ext.junit.runners.AndroidJUnit4;
@@ -61,6 +62,7 @@ import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
import org.mockito.Captor;
import org.mockito.Mock;
+import org.mockito.Mockito;
import org.mockito.MockitoAnnotations;
import java.util.ArrayList;
@@ -83,6 +85,7 @@ public class RankingCoordinatorTest extends SysuiTestCase {
private NotificationEntry mEntry;
private NotifFilter mCapturedSuspendedFilter;
+ private NotifFilter mCapturedDndStatelessFilter;
private NotifFilter mCapturedDozingFilter;
private StatusBarStateController.StateListener mStatusBarStateCallback;
private RankingCoordinator mRankingCoordinator;
@@ -107,6 +110,15 @@ public class RankingCoordinatorTest extends SysuiTestCase {
mRankingCoordinator.attach(mNotifPipeline);
verify(mNotifPipeline, times(2)).addPreGroupFilter(mNotifFilterCaptor.capture());
mCapturedSuspendedFilter = mNotifFilterCaptor.getAllValues().get(0);
+ if (com.android.systemui.Flags.notificationAmbientSuppressionAfterInflation()) {
+ verify(mNotifPipeline).addFinalizeFilter(mNotifFilterCaptor.capture());
+ mCapturedDndStatelessFilter = mNotifFilterCaptor.getAllValues().get(1);
+ mCapturedDozingFilter = mNotifFilterCaptor.getAllValues().get(2);
+ } else {
+ verify(mNotifPipeline, never()).addFinalizeFilter(any());
+ mCapturedDndStatelessFilter = null;
+ mCapturedDozingFilter = mNotifFilterCaptor.getAllValues().get(1);
+ }
mCapturedDozingFilter = mNotifFilterCaptor.getAllValues().get(1);
mCapturedDozingFilter.setInvalidationListener(mInvalidationListener);
@@ -159,6 +171,40 @@ public class RankingCoordinatorTest extends SysuiTestCase {
}
@Test
+ @EnableFlags(com.android.systemui.Flags.FLAG_NOTIFICATION_AMBIENT_SUPPRESSION_AFTER_INFLATION)
+ public void filterStatelessVisualEffectsSuppression() {
+ Mockito.clearInvocations(mStatusBarStateController);
+
+ // WHEN should suppress ambient
+ mEntry.setRanking(getRankingForUnfilteredNotif()
+ .setSuppressedVisualEffects(SUPPRESSED_EFFECT_AMBIENT)
+ .build());
+
+ // THEN do not filter out the notification
+ assertFalse(mCapturedDozingFilter.shouldFilterOut(mEntry, 0));
+
+ // WHEN should suppress list
+ mEntry.setRanking(getRankingForUnfilteredNotif()
+ .setSuppressedVisualEffects(SUPPRESSED_EFFECT_NOTIFICATION_LIST)
+ .build());
+
+ // THEN do not filter out the notification
+ assertFalse(mCapturedDozingFilter.shouldFilterOut(mEntry, 0));
+
+ // WHEN should suppress both ambient and list
+ mEntry.setRanking(getRankingForUnfilteredNotif()
+ .setSuppressedVisualEffects(
+ SUPPRESSED_EFFECT_AMBIENT | SUPPRESSED_EFFECT_NOTIFICATION_LIST)
+ .build());
+
+ // THEN we should filter out the notification!
+ assertTrue(mCapturedDozingFilter.shouldFilterOut(mEntry, 0));
+
+ // VERIFY that we don't check the dozing state
+ verify(mStatusBarStateController, never()).isDozing();
+ }
+
+ @Test
public void filterDozingSuppressAmbient() {
// GIVEN should suppress ambient
mEntry.setRanking(getRankingForUnfilteredNotif()
@@ -200,7 +246,7 @@ public class RankingCoordinatorTest extends SysuiTestCase {
// WHEN it's not dozing (showing the notification list)
when(mStatusBarStateController.isDozing()).thenReturn(false);
-
+
// THEN filter out the notification
assertTrue(mCapturedDozingFilter.shouldFilterOut(mEntry, 0));
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicyTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicyTest.kt
index e1589b6ad4bd..4e0393341faf 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicyTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicyTest.kt
@@ -45,13 +45,10 @@ import com.android.systemui.display.domain.interactor.ConnectedDisplayInteractor
import com.android.systemui.kosmos.testScope
import com.android.systemui.privacy.PrivacyItemController
import com.android.systemui.privacy.logging.PrivacyLogger
-import com.android.systemui.screenrecord.RecordingController
import com.android.systemui.settings.UserTracker
import com.android.systemui.statusbar.CommandQueue
import com.android.systemui.statusbar.phone.ui.StatusBarIconController
import com.android.systemui.statusbar.policy.BluetoothController
-import com.android.systemui.statusbar.policy.CastController
-import com.android.systemui.statusbar.policy.CastDevice
import com.android.systemui.statusbar.policy.DataSaverController
import com.android.systemui.statusbar.policy.DeviceProvisionedController
import com.android.systemui.statusbar.policy.HotspotController
@@ -92,7 +89,6 @@ import org.mockito.Mockito.verify
import org.mockito.Mockito.`when` as whenever
import org.mockito.MockitoAnnotations
import org.mockito.kotlin.any
-import org.mockito.kotlin.argumentCaptor
import org.mockito.kotlin.eq
import org.mockito.kotlin.reset
@@ -108,8 +104,6 @@ class PhoneStatusBarPolicyTest : SysuiTestCase() {
companion object {
private const val ZEN_SLOT = "zen"
private const val ALARM_SLOT = "alarm"
- private const val CAST_SLOT = "cast"
- private const val SCREEN_RECORD_SLOT = "screen_record"
private const val CONNECTED_DISPLAY_SLOT = "connected_display"
private const val MANAGED_PROFILE_SLOT = "managed_profile"
}
@@ -117,7 +111,6 @@ class PhoneStatusBarPolicyTest : SysuiTestCase() {
@Mock private lateinit var iconController: StatusBarIconController
@Mock private lateinit var commandQueue: CommandQueue
@Mock private lateinit var broadcastDispatcher: BroadcastDispatcher
- @Mock private lateinit var castController: CastController
@Mock private lateinit var hotspotController: HotspotController
@Mock private lateinit var bluetoothController: BluetoothController
@Mock private lateinit var nextAlarmController: NextAlarmController
@@ -133,7 +126,6 @@ class PhoneStatusBarPolicyTest : SysuiTestCase() {
@Mock private lateinit var userTracker: UserTracker
@Mock private lateinit var devicePolicyManager: DevicePolicyManager
@Mock private lateinit var devicePolicyManagerResources: DevicePolicyResourcesManager
- @Mock private lateinit var recordingController: RecordingController
@Mock private lateinit var telecomManager: TelecomManager
@Mock private lateinit var sharedPreferences: SharedPreferences
@Mock private lateinit var dateFormatUtil: DateFormatUtil
@@ -302,101 +294,6 @@ class PhoneStatusBarPolicyTest : SysuiTestCase() {
}
@Test
- @DisableFlags(Flags.FLAG_STATUS_BAR_SCREEN_SHARING_CHIPS)
- fun cast_chipsFlagOff_iconShown() {
- statusBarPolicy.init()
- clearInvocations(iconController)
-
- val callbackCaptor = argumentCaptor<CastController.Callback>()
- verify(castController).addCallback(callbackCaptor.capture())
-
- whenever(castController.castDevices)
- .thenReturn(
- listOf(
- CastDevice(
- "id",
- "name",
- "description",
- CastDevice.CastState.Connected,
- CastDevice.CastOrigin.MediaProjection,
- )
- )
- )
- callbackCaptor.firstValue.onCastDevicesChanged()
-
- verify(iconController).setIconVisibility(CAST_SLOT, true)
- }
-
- @Test
- @EnableFlags(Flags.FLAG_STATUS_BAR_SCREEN_SHARING_CHIPS)
- fun cast_chipsFlagOn_noCallbackRegistered() {
- statusBarPolicy.init()
-
- verify(castController, never()).addCallback(any())
- }
-
- @Test
- @DisableFlags(Flags.FLAG_STATUS_BAR_SCREEN_SHARING_CHIPS)
- fun screenRecord_chipsFlagOff_iconShown_forAllStates() {
- statusBarPolicy.init()
- clearInvocations(iconController)
-
- val callbackCaptor = argumentCaptor<RecordingController.RecordingStateChangeCallback>()
- verify(recordingController).addCallback(callbackCaptor.capture())
-
- callbackCaptor.firstValue.onCountdown(3000)
- testableLooper.processAllMessages()
- verify(iconController).setIconVisibility(SCREEN_RECORD_SLOT, true)
- clearInvocations(iconController)
-
- callbackCaptor.firstValue.onCountdownEnd()
- testableLooper.processAllMessages()
- verify(iconController).setIconVisibility(SCREEN_RECORD_SLOT, false)
- clearInvocations(iconController)
-
- callbackCaptor.firstValue.onRecordingStart()
- testableLooper.processAllMessages()
- verify(iconController).setIconVisibility(SCREEN_RECORD_SLOT, true)
- clearInvocations(iconController)
-
- callbackCaptor.firstValue.onRecordingEnd()
- testableLooper.processAllMessages()
- verify(iconController).setIconVisibility(SCREEN_RECORD_SLOT, false)
- clearInvocations(iconController)
- }
-
- @Test
- @EnableFlags(Flags.FLAG_STATUS_BAR_SCREEN_SHARING_CHIPS)
- fun screenRecord_chipsFlagOn_noCallbackRegistered() {
- statusBarPolicy.init()
-
- verify(recordingController, never()).addCallback(any())
- }
-
- @Test
- @EnableFlags(Flags.FLAG_STATUS_BAR_SCREEN_SHARING_CHIPS)
- fun screenRecord_chipsFlagOn_methodsDoNothing() {
- statusBarPolicy.init()
- clearInvocations(iconController)
-
- statusBarPolicy.onCountdown(3000)
- testableLooper.processAllMessages()
- verify(iconController, never()).setIconVisibility(eq(SCREEN_RECORD_SLOT), any())
-
- statusBarPolicy.onCountdownEnd()
- testableLooper.processAllMessages()
- verify(iconController, never()).setIconVisibility(eq(SCREEN_RECORD_SLOT), any())
-
- statusBarPolicy.onRecordingStart()
- testableLooper.processAllMessages()
- verify(iconController, never()).setIconVisibility(eq(SCREEN_RECORD_SLOT), any())
-
- statusBarPolicy.onRecordingEnd()
- testableLooper.processAllMessages()
- verify(iconController, never()).setIconVisibility(eq(SCREEN_RECORD_SLOT), any())
- }
-
- @Test
@EnableFlags(android.app.Flags.FLAG_MODES_UI, android.app.Flags.FLAG_MODES_UI_ICONS)
fun zenModeInteractorActiveModeChanged_showsModeIcon() =
testScope.runTest {
@@ -514,7 +411,6 @@ class PhoneStatusBarPolicyTest : SysuiTestCase() {
executor,
testableLooper.looper,
context.resources,
- castController,
hotspotController,
bluetoothController,
nextAlarmController,
@@ -530,7 +426,6 @@ class PhoneStatusBarPolicyTest : SysuiTestCase() {
userManager,
userTracker,
devicePolicyManager,
- recordingController,
telecomManager,
/* displayId = */ 0,
sharedPreferences,
@@ -589,11 +484,6 @@ class PhoneStatusBarPolicyTest : SysuiTestCase() {
override fun getConfig(): ZenModeConfig = throw NotImplementedError()
- fun setConsolidatedPolicy(policy: NotificationManager.Policy) {
- this.consolidatedPolicy = policy
- callbacks.forEach { it.onConsolidatedPolicyChanged(consolidatedPolicy) }
- }
-
override fun getConsolidatedPolicy(): NotificationManager.Policy = consolidatedPolicy
override fun getNextAlarm() = throw NotImplementedError()
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallControllerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallControllerTest.kt
index b98409906f8d..6da06a36f63d 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallControllerTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallControllerTest.kt
@@ -21,21 +21,18 @@ import android.app.IActivityManager
import android.app.IUidObserver
import android.app.PendingIntent
import android.platform.test.annotations.DisableFlags
-import android.platform.test.annotations.EnableFlags
import android.testing.TestableLooper
import android.view.LayoutInflater
import android.view.View
import android.widget.LinearLayout
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
-import com.android.systemui.Flags.FLAG_STATUS_BAR_SCREEN_SHARING_CHIPS
import com.android.systemui.SysuiTestCase
import com.android.systemui.concurrency.fakeExecutor
import com.android.systemui.dump.DumpManager
import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.testScope
import com.android.systemui.log.logcatLogBuffer
-import com.android.systemui.plugins.activityStarter
import com.android.systemui.res.R
import com.android.systemui.statusbar.StatusBarIconView
import com.android.systemui.statusbar.data.repository.fakeStatusBarModeRepository
@@ -50,7 +47,6 @@ import com.android.systemui.statusbar.phone.ongoingcall.data.repository.ongoingC
import com.android.systemui.statusbar.phone.ongoingcall.shared.model.OngoingCallModel
import com.android.systemui.statusbar.window.StatusBarWindowController
import com.android.systemui.statusbar.window.StatusBarWindowControllerStore
-import com.android.systemui.util.time.fakeSystemClock
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.test.runCurrent
@@ -77,7 +73,6 @@ import org.mockito.kotlin.whenever
class OngoingCallControllerTest : SysuiTestCase() {
private val kosmos = Kosmos()
- private val clock = kosmos.fakeSystemClock
private val mainExecutor = kosmos.fakeExecutor
private val testScope = kosmos.testScope
private val statusBarModeRepository = kosmos.fakeStatusBarModeRepository
@@ -88,7 +83,6 @@ class OngoingCallControllerTest : SysuiTestCase() {
private val mockSwipeStatusBarAwayGestureHandler = mock<SwipeStatusBarAwayGestureHandler>()
private val mockOngoingCallListener = mock<OngoingCallListener>()
- private val mockActivityStarter = kosmos.activityStarter
private val mockIActivityManager = mock<IActivityManager>()
private val mockStatusBarWindowController = mock<StatusBarWindowController>()
private val mockStatusBarWindowControllerStore = mock<StatusBarWindowControllerStore>()
@@ -111,8 +105,6 @@ class OngoingCallControllerTest : SysuiTestCase() {
context,
ongoingCallRepository,
kosmos.activeNotificationsInteractor,
- clock,
- mockActivityStarter,
mainExecutor,
mockIActivityManager,
DumpManager(),
@@ -223,48 +215,6 @@ class OngoingCallControllerTest : SysuiTestCase() {
)
}
- /** Regression test for b/192379214. */
- @Test
- @DisableFlags(android.app.Flags.FLAG_SORT_SECTION_BY_TIME, FLAG_STATUS_BAR_SCREEN_SHARING_CHIPS)
- fun notifRepoHasCallNotif_notificationWhenIsZero_timeHidden() {
- setNotifOnRepo(
- activeNotificationModel(
- key = "notif",
- callType = CallType.Ongoing,
- contentIntent = null,
- whenTime = 0,
- )
- )
-
- chipView.measure(
- View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED),
- View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED),
- )
-
- assertThat(chipView.findViewById<View>(R.id.ongoing_activity_chip_time)?.measuredWidth)
- .isEqualTo(0)
- }
-
- @Test
- @DisableFlags(FLAG_STATUS_BAR_SCREEN_SHARING_CHIPS)
- fun notifRepoHasCallNotif_notificationWhenIsValid_timeShown() {
- setNotifOnRepo(
- activeNotificationModel(
- key = "notif",
- callType = CallType.Ongoing,
- whenTime = clock.currentTimeMillis(),
- )
- )
-
- chipView.measure(
- View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED),
- View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED),
- )
-
- assertThat(chipView.findViewById<View>(R.id.ongoing_activity_chip_time)?.measuredWidth)
- .isGreaterThan(0)
- }
-
/** Regression test for b/194731244. */
@Test
fun repoHasCallNotif_updatedManyTimes_uidObserverOnlyRegisteredOnce() {
@@ -493,52 +443,6 @@ class OngoingCallControllerTest : SysuiTestCase() {
verify(mockOngoingCallListener).onOngoingCallStateChanged(any())
}
- /** Regression test for b/212467440. */
- @Test
- @DisableFlags(FLAG_STATUS_BAR_SCREEN_SHARING_CHIPS)
- fun chipClicked_activityStarterTriggeredWithUnmodifiedIntent() {
- val pendingIntent = mock<PendingIntent>()
- setNotifOnRepo(
- activeNotificationModel(
- key = "notif",
- uid = CALL_UID,
- contentIntent = pendingIntent,
- callType = CallType.Ongoing,
- )
- )
-
- chipView.performClick()
-
- // Ensure that the sysui didn't modify the notification's intent -- see b/212467440.
- verify(mockActivityStarter).postStartActivityDismissingKeyguard(eq(pendingIntent), any())
- }
-
- @Test
- @DisableFlags(FLAG_STATUS_BAR_SCREEN_SHARING_CHIPS)
- fun callNotificationAdded_chipIsClickable() {
- setCallNotifOnRepo()
-
- assertThat(chipView.hasOnClickListeners()).isTrue()
- }
-
- @Test
- @EnableFlags(FLAG_STATUS_BAR_SCREEN_SHARING_CHIPS)
- fun callNotificationAdded_newChipsEnabled_chipNotClickable() {
- setCallNotifOnRepo()
-
- assertThat(chipView.hasOnClickListeners()).isFalse()
- }
-
- @Test
- @DisableFlags(FLAG_STATUS_BAR_SCREEN_SHARING_CHIPS)
- fun fullscreenIsTrue_chipStillClickable() {
- setCallNotifOnRepo()
- statusBarModeRepository.defaultDisplay.isInFullscreenMode.value = true
- testScope.runCurrent()
-
- assertThat(chipView.hasOnClickListeners()).isTrue()
- }
-
@Test
fun callStartedInImmersiveMode_swipeGestureCallbackAdded() {
statusBarModeRepository.defaultDisplay.isInFullscreenMode.value = true
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/FakeHomeStatusBarViewModel.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/FakeHomeStatusBarViewModel.kt
index 6feada1c9769..937f333b0065 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/FakeHomeStatusBarViewModel.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/FakeHomeStatusBarViewModel.kt
@@ -46,6 +46,8 @@ class FakeHomeStatusBarViewModel(
override val isHomeStatusBarAllowedByScene = MutableStateFlow(false)
+ override val shouldHomeStatusBarBeVisible = MutableStateFlow(false)
+
override val shouldShowOperatorNameView = MutableStateFlow(false)
override val isClockVisible =
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/HomeStatusBarViewModelImplTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/HomeStatusBarViewModelImplTest.kt
index e95bc3378423..be4af868b740 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/HomeStatusBarViewModelImplTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/HomeStatusBarViewModelImplTest.kt
@@ -575,6 +575,98 @@ class HomeStatusBarViewModelImplTest : SysuiTestCase() {
}
@Test
+ fun shouldHomeStatusBarBeVisible_keyguardNotGone_noHun_false() =
+ kosmos.runTest {
+ // Do not transition from keyguard. i.e., we don't call transitionKeyguardToGone()
+
+ // Nothing disabled
+ fakeDisableFlagsRepository.disableFlags.value =
+ DisableFlagsModel(DISABLE_NONE, DISABLE2_NONE)
+
+ val latest by collectLastValue(underTest.shouldHomeStatusBarBeVisible)
+ assertThat(latest).isFalse()
+ }
+
+ @Test
+ fun shouldHomeStatusBarBeVisible_keyguardNotGone_hun_true() =
+ kosmos.runTest {
+ // Keyguard gone
+ transitionKeyguardToGone()
+
+ // Nothing disabled
+ fakeDisableFlagsRepository.disableFlags.value =
+ DisableFlagsModel(DISABLE_NONE, DISABLE2_NONE)
+
+ // there is an active HUN
+ headsUpNotificationRepository.setNotifications(
+ UnconfinedFakeHeadsUpRowRepository(
+ key = "key",
+ pinnedStatus = MutableStateFlow(PinnedStatus.PinnedByUser),
+ )
+ )
+
+ val latest by collectLastValue(underTest.shouldHomeStatusBarBeVisible)
+ assertThat(latest).isTrue()
+ }
+
+ @Test
+ fun shouldHomeStatusBarBeVisible_keyguardGone_noHun_notInCamera_true() =
+ kosmos.runTest {
+ // Keyguard gone
+ transitionKeyguardToGone()
+
+ // Nothing disabled
+ fakeDisableFlagsRepository.disableFlags.value =
+ DisableFlagsModel(DISABLE_NONE, DISABLE2_NONE)
+
+ val latest by collectLastValue(underTest.shouldHomeStatusBarBeVisible)
+ assertThat(latest).isTrue()
+ }
+
+ @Test
+ fun shouldHomeStatusBarBeVisible_keyguardGone_hun_notInCamera_true() =
+ kosmos.runTest {
+ // Keyguard gone
+ transitionKeyguardToGone()
+
+ // Nothing disabled
+ fakeDisableFlagsRepository.disableFlags.value =
+ DisableFlagsModel(DISABLE_NONE, DISABLE2_NONE)
+
+ // there is an active HUN
+ headsUpNotificationRepository.setNotifications(
+ UnconfinedFakeHeadsUpRowRepository(
+ key = "key",
+ pinnedStatus = MutableStateFlow(PinnedStatus.PinnedByUser),
+ )
+ )
+
+ val latest by collectLastValue(underTest.shouldHomeStatusBarBeVisible)
+ assertThat(latest).isTrue()
+ }
+
+ @Test
+ fun shouldHomeStatusBarBeVisible_keyguardGone_noHun_inCamera_false() =
+ kosmos.runTest {
+ // Keyguard gone
+ transitionKeyguardToGone()
+
+ // Nothing disabled
+ fakeDisableFlagsRepository.disableFlags.value =
+ DisableFlagsModel(DISABLE_NONE, DISABLE2_NONE)
+
+ fakeKeyguardTransitionRepository.sendTransitionSteps(
+ from = KeyguardState.LOCKSCREEN,
+ to = KeyguardState.OCCLUDED,
+ testScope = testScope,
+ )
+ kosmos.keyguardInteractor.onCameraLaunchDetected(CAMERA_LAUNCH_SOURCE_POWER_DOUBLE_TAP)
+
+ val latest by collectLastValue(underTest.shouldHomeStatusBarBeVisible)
+ assertThat(latest).isFalse()
+ }
+
+ @Test
fun isClockVisible_allowedByDisableFlags_visible() =
kosmos.runTest {
val latest by collectLastValue(underTest.isClockVisible)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/StatusBarOperatorNameViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/StatusBarOperatorNameViewModelTest.kt
index 20cc85f08b01..8608b0bf2f0b 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/StatusBarOperatorNameViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/StatusBarOperatorNameViewModelTest.kt
@@ -39,14 +39,12 @@ class StatusBarOperatorNameViewModelTest : SysuiTestCase() {
kosmos.runTest {
val intr1 = fakeMobileIconsInteractor.getMobileConnectionInteractorForSubId(1)
val intr2 = fakeMobileIconsInteractor.getMobileConnectionInteractorForSubId(2)
- val invalidIntr = fakeMobileIconsInteractor.getMobileConnectionInteractorForSubId(-1)
// GIVEN default data subId is 1
fakeMobileIconsInteractor.defaultDataSubId.value = 1
intr1.carrierName.value = "Test Name 1"
intr2.carrierName.value = "Test Name 2"
- invalidIntr.carrierName.value = "default network name"
val latest by collectLastValue(underTest.operatorName)
@@ -56,8 +54,19 @@ class StatusBarOperatorNameViewModelTest : SysuiTestCase() {
assertThat(latest).isEqualTo("Test Name 2")
- fakeMobileIconsInteractor.defaultDataSubId.value = -1
+ fakeMobileIconsInteractor.defaultDataSubId.value = null
- assertThat(latest).isEqualTo("default network name")
+ assertThat(latest).isNull()
+ }
+
+ @Test
+ fun operatorName_noDefaultDataSubId_null() =
+ kosmos.runTest {
+ // GIVEN defaultDataSubId is null
+ fakeMobileIconsInteractor.defaultDataSubId.value = null
+
+ val latest by collectLastValue(underTest.operatorName)
+
+ assertThat(latest).isNull()
}
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/dialog/domain/interactor/VolumeDialogCallbacksInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/dialog/domain/interactor/VolumeDialogCallbacksInteractorTest.kt
new file mode 100644
index 000000000000..fec186e862be
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/dialog/domain/interactor/VolumeDialogCallbacksInteractorTest.kt
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2025 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.volume.dialog.domain.interactor
+
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.kosmos.collectLastValue
+import com.android.systemui.kosmos.runTest
+import com.android.systemui.kosmos.useUnconfinedTestDispatcher
+import com.android.systemui.testKosmos
+import com.android.systemui.volume.dialog.domain.model.VolumeDialogEventModel
+import com.google.common.truth.Truth.assertThat
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class VolumeDialogCallbacksInteractorTest : SysuiTestCase() {
+
+ private val kosmos = testKosmos().apply { useUnconfinedTestDispatcher() }
+
+ private val underTest: VolumeDialogCallbacksInteractor by lazy {
+ kosmos.volumeDialogCallbacksInteractor
+ }
+
+ @Test
+ fun initialEvent_isSubscribedToEvents() =
+ kosmos.runTest {
+ val event by collectLastValue(underTest.event)
+ assertThat(event).isInstanceOf(VolumeDialogEventModel.SubscribedToEvents::class.java)
+ }
+}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/dialog/sliders/domain/interactor/VolumeDialogSliderInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/dialog/sliders/domain/interactor/VolumeDialogSliderInteractorTest.kt
index bfafdab003aa..1001c245cc95 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/dialog/sliders/domain/interactor/VolumeDialogSliderInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/dialog/sliders/domain/interactor/VolumeDialogSliderInteractorTest.kt
@@ -16,75 +16,115 @@
package com.android.systemui.volume.dialog.sliders.domain.interactor
+import android.media.AudioManager
+import android.service.notification.ZenPolicy
import android.testing.TestableLooper
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
+import com.android.settingslib.notification.modes.TestModeBuilder
import com.android.systemui.SysuiTestCase
-import com.android.systemui.coroutines.collectLastValue
-import com.android.systemui.kosmos.testScope
+import com.android.systemui.kosmos.collectLastValue
+import com.android.systemui.kosmos.runTest
+import com.android.systemui.kosmos.useUnconfinedTestDispatcher
import com.android.systemui.plugins.VolumeDialogController
import com.android.systemui.plugins.fakeVolumeDialogController
+import com.android.systemui.statusbar.policy.data.repository.zenModeRepository
import com.android.systemui.testKosmos
+import com.android.systemui.volume.dialog.sliders.domain.model.VolumeDialogSliderType
import com.android.systemui.volume.dialog.sliders.domain.model.volumeDialogSliderType
import com.google.common.truth.Truth.assertThat
-import kotlinx.coroutines.ExperimentalCoroutinesApi
-import kotlinx.coroutines.test.runCurrent
-import kotlinx.coroutines.test.runTest
-import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
-@OptIn(ExperimentalCoroutinesApi::class)
@SmallTest
@RunWith(AndroidJUnit4::class)
@TestableLooper.RunWithLooper
class VolumeDialogSliderInteractorTest : SysuiTestCase() {
- private val kosmos = testKosmos()
-
- private lateinit var underTest: VolumeDialogSliderInteractor
+ private val kosmos =
+ testKosmos().apply {
+ useUnconfinedTestDispatcher()
+ zenModeRepository.addMode(
+ TestModeBuilder()
+ .setName("Blocks media, Active")
+ .setZenPolicy(ZenPolicy.Builder().allowMedia(false).build())
+ .setActive(true)
+ .build()
+ )
+ }
- @Before
- fun setUp() {
- underTest = kosmos.volumeDialogSliderInteractor
+ private val underTest: VolumeDialogSliderInteractor by lazy {
+ kosmos.volumeDialogSliderInteractor
}
@Test
fun settingStreamVolume_setsActiveStream() =
- with(kosmos) {
- testScope.runTest {
- runCurrent()
- // initialize the stream model
- fakeVolumeDialogController.setStreamVolume(volumeDialogSliderType.audioStream, 0)
+ kosmos.runTest {
+ // initialize the stream model
+ fakeVolumeDialogController.setStreamVolume(volumeDialogSliderType.audioStream, 0)
- val sliderModel by collectLastValue(underTest.slider)
- underTest.setStreamVolume(1)
- runCurrent()
+ val sliderModel by collectLastValue(underTest.slider)
+ underTest.setStreamVolume(1)
- assertThat(sliderModel!!.isActive).isTrue()
- }
+ assertThat(sliderModel!!.isActive).isTrue()
}
@Test
fun streamVolumeIs_minMaxAreEnforced() =
- with(kosmos) {
- testScope.runTest {
- runCurrent()
- fakeVolumeDialogController.updateState {
- states.put(
- volumeDialogSliderType.audioStream,
- VolumeDialogController.StreamState().apply {
- levelMin = 0
- level = 2
- levelMax = 1
- },
- )
- }
-
- val sliderModel by collectLastValue(underTest.slider)
- runCurrent()
-
- assertThat(sliderModel!!.level).isEqualTo(1)
+ kosmos.runTest {
+ fakeVolumeDialogController.updateState {
+ states.put(
+ volumeDialogSliderType.audioStream,
+ VolumeDialogController.StreamState().apply {
+ levelMin = 0
+ level = 2
+ levelMax = 1
+ },
+ )
}
+
+ val sliderModel by collectLastValue(underTest.slider)
+
+ assertThat(sliderModel!!.level).isEqualTo(1)
+ }
+
+ @Test
+ fun streamCantBeBlockedByZenMode_isDisabledByZenMode_false() =
+ kosmos.runTest {
+ volumeDialogSliderType = VolumeDialogSliderType.Stream(AudioManager.STREAM_VOICE_CALL)
+
+ val isDisabledByZenMode by collectLastValue(underTest.isDisabledByZenMode)
+
+ assertThat(isDisabledByZenMode).isFalse()
+ }
+
+ @Test
+ fun remoteMediaStream_zenModeRestrictive_IsNotDisabledByZenMode() =
+ kosmos.runTest {
+ volumeDialogSliderType = VolumeDialogSliderType.RemoteMediaStream(0)
+
+ val isDisabledByZenMode by collectLastValue(underTest.isDisabledByZenMode)
+
+ assertThat(isDisabledByZenMode).isFalse()
+ }
+
+ @Test
+ fun audioSharingStream_zenModeRestrictive_IsNotDisabledByZenMode() =
+ kosmos.runTest {
+ volumeDialogSliderType = VolumeDialogSliderType.AudioSharingStream(0)
+
+ val isDisabledByZenMode by collectLastValue(underTest.isDisabledByZenMode)
+
+ assertThat(isDisabledByZenMode).isFalse()
+ }
+
+ @Test
+ fun streamBlockedByZenMode_isDisabledByZenMode_true() =
+ kosmos.runTest {
+ volumeDialogSliderType = VolumeDialogSliderType.Stream(AudioManager.STREAM_MUSIC)
+
+ val isDisabledByZenMode by collectLastValue(underTest.isDisabledByZenMode)
+
+ assertThat(isDisabledByZenMode).isTrue()
}
}
diff --git a/packages/SystemUI/res/layout/window_magnification_settings_view.xml b/packages/SystemUI/res/layout/window_magnification_settings_view.xml
index afd4fa7a5edd..7f7350472fa5 100644
--- a/packages/SystemUI/res/layout/window_magnification_settings_view.xml
+++ b/packages/SystemUI/res/layout/window_magnification_settings_view.xml
@@ -132,7 +132,8 @@
android:layout_height="wrap_content"
android:track="@drawable/settingslib_track_selector"
android:thumb="@drawable/settingslib_thumb_selector"
- android:theme="@style/MainSwitch.Settingslib"/>
+ android:theme="@style/MainSwitch.Settingslib"
+ android:minHeight="48dp" />
</LinearLayout>
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index 80fb8b9fcebc..a8ee60973586 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -1963,7 +1963,7 @@
<!-- Text displayed indicating that the user is connected to a satellite signal. -->
<string name="satellite_connected_carrier_text">Satellite SOS</string>
<!-- Text displayed indicating that the user might be able to use satellite SOS. -->
- <string name="satellite_emergency_only_carrier_text">Emergency calls or SOS</string>
+ <string name="satellite_emergency_only_carrier_text">Emergency calls or SOS only</string>
<!-- Content description skeleton. Input strings should be carrier name and signal bar description [CHAR LIMIT=NONE]-->
<string name="accessibility_phone_string_format"><xliff:g id="carrier_name" example="Carrier Name">%1$s</xliff:g>, <xliff:g id="signal_strength_description" example="two bars">%2$s</xliff:g>.</string>
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/RecentsAnimationListener.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/RecentsAnimationListener.java
index ff6bcdb150f8..51892aac606a 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/RecentsAnimationListener.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/RecentsAnimationListener.java
@@ -19,7 +19,6 @@ package com.android.systemui.shared.system;
import android.graphics.Rect;
import android.os.Bundle;
import android.view.RemoteAnimationTarget;
-import android.window.TransitionInfo;
import com.android.systemui.shared.recents.model.ThumbnailData;
@@ -31,7 +30,7 @@ public interface RecentsAnimationListener {
*/
void onAnimationStart(RecentsAnimationControllerCompat controller,
RemoteAnimationTarget[] apps, RemoteAnimationTarget[] wallpapers,
- Rect homeContentInsets, Rect minimizedHomeBounds, Bundle extras, TransitionInfo info);
+ Rect homeContentInsets, Rect minimizedHomeBounds, Bundle extras);
/**
* Called when the animation into Recents was canceled. This call is made on the binder thread.
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardPINView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardPINView.java
index 622b67f25da3..e22736b69213 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardPINView.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardPINView.java
@@ -297,10 +297,13 @@ public class KeyguardPINView extends KeyguardPinBasedInputView {
DisappearAnimationUtils disappearAnimationUtils = needsSlowUnlockTransition
? mDisappearAnimationUtilsLocked
: mDisappearAnimationUtils;
+ android.util.Log.i("KeyguardPINView", "startDisappearAnimation: " + finishRunnable);
disappearAnimationUtils.createAnimation(
this, 0, 200, mDisappearYTranslation, false,
mDisappearAnimationUtils.getInterpolator(), () -> {
if (finishRunnable != null) {
+ android.util.Log.i("KeyguardPINView",
+ "startDisappearAnimation, invoking run()");
finishRunnable.run();
}
},
diff --git a/packages/SystemUI/src/com/android/systemui/KairosActivatable.kt b/packages/SystemUI/src/com/android/systemui/KairosActivatable.kt
new file mode 100644
index 000000000000..5e29ba91ce42
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/KairosActivatable.kt
@@ -0,0 +1,212 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui
+
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Application
+import com.android.systemui.kairos.BuildScope
+import com.android.systemui.kairos.Events
+import com.android.systemui.kairos.EventsLoop
+import com.android.systemui.kairos.ExperimentalKairosApi
+import com.android.systemui.kairos.Incremental
+import com.android.systemui.kairos.IncrementalLoop
+import com.android.systemui.kairos.KairosNetwork
+import com.android.systemui.kairos.State
+import com.android.systemui.kairos.StateLoop
+import com.android.systemui.kairos.launchKairosNetwork
+import com.android.systemui.kairos.launchScope
+import dagger.Binds
+import dagger.Module
+import dagger.Provides
+import dagger.multibindings.ClassKey
+import dagger.multibindings.IntoMap
+import dagger.multibindings.Multibinds
+import javax.inject.Inject
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.launch
+
+/**
+ * A Kairos-powered class that needs late-initialization within a Kairos [BuildScope].
+ *
+ * If your class is a [SysUISingleton], you can leverage Dagger to automatically initialize your
+ * instance after SystemUI has initialized:
+ * ```kotlin
+ * class MyClass : KairosActivatable { ... }
+ *
+ * @dagger.Module
+ * interface MyModule {
+ * @Binds
+ * @IntoSet
+ * fun bindKairosActivatable(impl: MyClass): KairosActivatable
+ * }
+ * ```
+ *
+ * Alternatively, you can utilize Dagger's [dagger.assisted.AssistedInject]:
+ * ```kotlin
+ * class MyClass @AssistedInject constructor(...) : KairosActivatable {
+ * @AssistedFactory
+ * interface Factory {
+ * fun create(...): MyClass
+ * }
+ * }
+ *
+ * // When you need an instance:
+ *
+ * class OtherClass @Inject constructor(
+ * private val myClassFactory: MyClass.Factory,
+ * ) {
+ * fun BuildScope.foo() {
+ * val myClass = activated { myClassFactory.create() }
+ * ...
+ * }
+ * }
+ * ```
+ *
+ * @see activated
+ */
+@ExperimentalKairosApi
+fun interface KairosActivatable {
+ /** Initializes any Kairos fields that require a [BuildScope] in order to be constructed. */
+ fun BuildScope.activate()
+}
+
+/** Constructs [KairosActivatable] instances. */
+@ExperimentalKairosApi
+fun interface KairosActivatableFactory<T : KairosActivatable> {
+ fun BuildScope.create(): T
+}
+
+/** Instantiates, [activates][KairosActivatable.activate], and returns a [KairosActivatable]. */
+@ExperimentalKairosApi
+fun <T : KairosActivatable> BuildScope.activated(factory: KairosActivatableFactory<T>): T =
+ factory.run { create() }.apply { activate() }
+
+/**
+ * Utilities for defining [State] and [Events] from a constructor without a provided [BuildScope].
+ * These instances are not active until the builder is [activated][activate]; while you can
+ * immediately use them with other Kairos APIs, the Kairos transaction will be suspended until
+ * initialization is complete.
+ *
+ * ```kotlin
+ * class MyRepository(private val dataSource: DataSource) : KairosBuilder by kairosBuilder() {
+ * val dataSourceEvent = buildEvents<SomeData> {
+ * // inside this lambda, we have access to a BuildScope, which can be used to create
+ * // new inputs to the Kairos network
+ * dataSource.someDataFlow.toEvents()
+ * }
+ * }
+ * ```
+ */
+@ExperimentalKairosApi
+interface KairosBuilder : KairosActivatable {
+ /**
+ * Returns a forward-reference to a [State] that will be instantiated when this builder is
+ * [activated][activate].
+ */
+ fun <R> buildState(block: BuildScope.() -> State<R>): State<R>
+
+ /**
+ * Returns a forward-reference to an [Events] that will be instantiated when this builder is
+ * [activated][activate].
+ */
+ fun <R> buildEvents(block: BuildScope.() -> Events<R>): Events<R>
+
+ fun <K, V> buildIncremental(block: BuildScope.() -> Incremental<K, V>): Incremental<K, V>
+
+ /** Defers [block] until this builder is [activated][activate]. */
+ fun onActivated(block: BuildScope.() -> Unit)
+}
+
+/** Returns an [KairosBuilder] that can only be [activated][KairosActivatable.activate] once. */
+@ExperimentalKairosApi fun kairosBuilder(): KairosBuilder = KairosBuilderImpl()
+
+@OptIn(ExperimentalKairosApi::class)
+private class KairosBuilderImpl @Inject constructor() : KairosBuilder {
+
+ // TODO: atomic?
+ // TODO: are two lists really necessary?
+ private var _builds: MutableList<KairosActivatable>? = mutableListOf()
+ private var _startables: MutableList<KairosActivatable>? = mutableListOf()
+
+ private val startables
+ get() = checkNotNull(_startables) { "Kairos network has already been initialized" }
+
+ private val builds
+ get() = checkNotNull(_builds) { "Kairos network has already been initialized" }
+
+ override fun <R> buildState(block: BuildScope.() -> State<R>): State<R> =
+ StateLoop<R>().apply { builds.add { loopback = block() } }
+
+ override fun <R> buildEvents(block: BuildScope.() -> Events<R>): Events<R> =
+ EventsLoop<R>().apply { builds.add { loopback = block() } }
+
+ override fun <K, V> buildIncremental(
+ block: BuildScope.() -> Incremental<K, V>
+ ): Incremental<K, V> = IncrementalLoop<K, V>().apply { builds.add { loopback = block() } }
+
+ override fun onActivated(block: BuildScope.() -> Unit) {
+ startables.add { block() }
+ }
+
+ override fun BuildScope.activate() {
+ builds.forEach { it.run { activate() } }
+ _builds = null
+ deferredBuildScopeAction {
+ startables.forEach { it.run { activate() } }
+ _startables = null
+ }
+ }
+}
+
+/** Initializes [KairosActivatables][KairosActivatable] after SystemUI is initialized. */
+@SysUISingleton
+@ExperimentalKairosApi
+class KairosCoreStartable
+@Inject
+constructor(
+ @Application private val appScope: CoroutineScope,
+ private val kairosNetwork: KairosNetwork,
+ private val activatables: dagger.Lazy<Set<@JvmSuppressWildcards KairosActivatable>>,
+) : CoreStartable {
+ override fun start() {
+ appScope.launch {
+ kairosNetwork.activateSpec {
+ for (activatable in activatables.get()) {
+ launchScope { activatable.run { activate() } }
+ }
+ }
+ }
+ }
+}
+
+@Module
+@ExperimentalKairosApi
+interface KairosCoreStartableModule {
+ @Binds
+ @IntoMap
+ @ClassKey(KairosCoreStartable::class)
+ fun bindCoreStartable(impl: KairosCoreStartable): CoreStartable
+
+ @Multibinds fun kairosActivatables(): Set<@JvmSuppressWildcards KairosActivatable>
+
+ companion object {
+ @Provides
+ @SysUISingleton
+ fun provideKairosNetwork(@Application scope: CoroutineScope): KairosNetwork =
+ scope.launchKairosNetwork()
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuInfoRepository.java b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuInfoRepository.java
index a1cb0367421b..bb6ab51aa56a 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuInfoRepository.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuInfoRepository.java
@@ -266,7 +266,7 @@ class MenuInfoRepository {
mSecureSettings.getUriFor(Settings.Secure.ACCESSIBILITY_BUTTON_TARGETS),
/* notifyForDescendants */ false, mMenuTargetFeaturesContentObserver,
UserHandle.USER_CURRENT);
- if (!com.android.systemui.Flags.floatingMenuNarrowTargetContentObserver()) {
+ if (com.android.systemui.Flags.floatingMenuNotifyTargetsChangedOnStrictDiff()) {
mSecureSettings.registerContentObserverForUserSync(
mSecureSettings.getUriFor(ENABLED_ACCESSIBILITY_SERVICES),
/* notifyForDescendants */ false,
@@ -287,7 +287,7 @@ class MenuInfoRepository {
UserHandle.USER_CURRENT);
mContext.registerComponentCallbacks(mComponentCallbacks);
- if (!com.android.systemui.Flags.floatingMenuNarrowTargetContentObserver()) {
+ if (com.android.systemui.Flags.floatingMenuNotifyTargetsChangedOnStrictDiff()) {
mAccessibilityManager.addAccessibilityServicesStateChangeListener(
mA11yServicesStateChangeListener);
}
@@ -317,7 +317,7 @@ class MenuInfoRepository {
mContext.getContentResolver().unregisterContentObserver(mMenuFadeOutContentObserver);
mContext.unregisterComponentCallbacks(mComponentCallbacks);
- if (!com.android.systemui.Flags.floatingMenuNarrowTargetContentObserver()) {
+ if (com.android.systemui.Flags.floatingMenuNotifyTargetsChangedOnStrictDiff()) {
mAccessibilityManager.removeAccessibilityServicesStateChangeListener(
mA11yServicesStateChangeListener);
}
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuView.java b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuView.java
index 3f49010aaaab..ae39b72b2585 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuView.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuView.java
@@ -284,13 +284,36 @@ class MenuView extends FrameLayout implements
onEdgeChanged();
onPositionChanged();
- if (mFeaturesChangeListener != null) {
+ boolean shouldSendFeatureChangeNotification =
+ com.android.systemui.Flags.floatingMenuNotifyTargetsChangedOnStrictDiff()
+ ? !areFeatureListsIdentical(targetFeatures, newTargetFeatures)
+ : true;
+ if (mFeaturesChangeListener != null && shouldSendFeatureChangeNotification) {
mFeaturesChangeListener.onChange(newTargetFeatures);
}
mMenuAnimationController.fadeOutIfEnabled();
}
+ /**
+ * Returns true if the given feature lists are identical lists, i.e. the same list of {@link
+ * AccessibilityTarget} (equality checked via UID) in the same order.
+ */
+ private boolean areFeatureListsIdentical(
+ List<AccessibilityTarget> currentFeatures, List<AccessibilityTarget> newFeatures) {
+ if (currentFeatures.size() != newFeatures.size()) {
+ return false;
+ }
+
+ for (int i = 0; i < currentFeatures.size(); i++) {
+ if (currentFeatures.get(i).getUid() != newFeatures.get(i).getUid()) {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
private void onMenuFadeEffectInfoChanged(MenuFadeEffectInfo fadeEffectInfo) {
mMenuAnimationController.updateOpacityWith(fadeEffectInfo.isFadeEffectEnabled(),
fadeEffectInfo.getOpacity());
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/hearingaid/HearingDevicesDialogDelegate.java b/packages/SystemUI/src/com/android/systemui/accessibility/hearingaid/HearingDevicesDialogDelegate.java
index 73aabc3cf95a..438184d4d2d6 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/hearingaid/HearingDevicesDialogDelegate.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/hearingaid/HearingDevicesDialogDelegate.java
@@ -286,9 +286,7 @@ public class HearingDevicesDialogDelegate implements SystemUIDialog.Delegate,
if (com.android.settingslib.flags.Flags.hearingDevicesAmbientVolumeControl()) {
setupAmbientControls();
}
- if (com.android.systemui.Flags.hearingDevicesDialogRelatedTools()) {
- setupRelatedToolsView(dialog);
- }
+ setupRelatedToolsView(dialog);
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/hearingaid/HearingDevicesDialogReceiver.java b/packages/SystemUI/src/com/android/systemui/accessibility/hearingaid/HearingDevicesDialogReceiver.java
index 02e65fd9031b..90b8fa0177b7 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/hearingaid/HearingDevicesDialogReceiver.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/hearingaid/HearingDevicesDialogReceiver.java
@@ -22,8 +22,6 @@ import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
-import com.android.systemui.Flags;
-
import javax.inject.Inject;
/**
@@ -43,10 +41,6 @@ public class HearingDevicesDialogReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
- if (!Flags.hearingAidsQsTileDialog()) {
- return;
- }
-
if (ACTION.equals(intent.getAction())) {
mDialogManager.showDialog(/* expandable= */ null, LAUNCH_SOURCE_A11Y);
}
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/qs/QSAccessibilityModule.kt b/packages/SystemUI/src/com/android/systemui/accessibility/qs/QSAccessibilityModule.kt
index 610e3f8a8c84..fb47d429e271 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/qs/QSAccessibilityModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/qs/QSAccessibilityModule.kt
@@ -411,7 +411,7 @@ interface QSAccessibilityModule {
stateInteractor: HearingDevicesTileDataInteractor,
userActionInteractor: HearingDevicesTileUserActionInteractor,
): QSTileViewModel {
- return if (Flags.hearingAidsQsTileDialog() && Flags.qsNewTilesFuture()) {
+ return if (Flags.qsNewTilesFuture()) {
factory.create(
TileSpec.create(HEARING_DEVICES_TILE_SPEC),
userActionInteractor,
diff --git a/packages/SystemUI/src/com/android/systemui/bouncer/ui/binder/KeyguardBouncerViewBinder.kt b/packages/SystemUI/src/com/android/systemui/bouncer/ui/binder/KeyguardBouncerViewBinder.kt
index 434a9ce58c3b..d5c815d649c4 100644
--- a/packages/SystemUI/src/com/android/systemui/bouncer/ui/binder/KeyguardBouncerViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/bouncer/ui/binder/KeyguardBouncerViewBinder.kt
@@ -170,6 +170,8 @@ object KeyguardBouncerViewBinder {
launch {
viewModel.startDisappearAnimation.collect {
+ android.util.Log.i("KeyguardBouncerViewBinder",
+ "viewModel.startDisappearAnimation: $it")
securityContainerController.startDisappearAnimation(it)
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardImageLoader.kt b/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardImageLoader.kt
index 8814a1298e44..c0ad3b8c97c9 100644
--- a/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardImageLoader.kt
+++ b/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardImageLoader.kt
@@ -20,15 +20,17 @@ import android.graphics.Bitmap
import android.net.Uri
import android.util.Log
import android.util.Size
-import com.android.systemui.res.R
+import com.android.app.tracing.coroutines.launchTraced as launch
+import com.android.systemui.Flags.clipboardOverlayMultiuser
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.dagger.qualifiers.Background
+import com.android.systemui.res.R
+import com.android.systemui.settings.UserTracker
import java.io.IOException
import java.util.function.Consumer
import javax.inject.Inject
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.CoroutineScope
-import com.android.app.tracing.coroutines.launchTraced as launch
import kotlinx.coroutines.withContext
import kotlinx.coroutines.withTimeoutOrNull
@@ -36,8 +38,9 @@ class ClipboardImageLoader
@Inject
constructor(
private val context: Context,
+ private val userTracker: UserTracker,
@Background private val bgDispatcher: CoroutineDispatcher,
- @Application private val mainScope: CoroutineScope
+ @Application private val mainScope: CoroutineScope,
) {
private val TAG: String = "ClipboardImageLoader"
@@ -46,7 +49,15 @@ constructor(
withContext(bgDispatcher) {
try {
val size = context.resources.getDimensionPixelSize(R.dimen.overlay_x_scale)
- context.contentResolver.loadThumbnail(uri, Size(size, size * 4), null)
+ if (clipboardOverlayMultiuser()) {
+ userTracker.userContentResolver.loadThumbnail(
+ uri,
+ Size(size, size * 4),
+ null,
+ )
+ } else {
+ context.contentResolver.loadThumbnail(uri, Size(size, size * 4), null)
+ }
} catch (e: IOException) {
Log.e(TAG, "Thumbnail loading failed!", e)
null
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java
index 7ebe52f3bd58..c02784dfab1b 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java
@@ -31,6 +31,7 @@ import com.android.systemui.BootCompleteCache;
import com.android.systemui.BootCompleteCacheImpl;
import com.android.systemui.CameraProtectionModule;
import com.android.systemui.CoreStartable;
+import com.android.systemui.KairosCoreStartableModule;
import com.android.systemui.SystemUISecondaryUserService;
import com.android.systemui.activity.ActivityManagerModule;
import com.android.systemui.ambient.dagger.AmbientModule;
@@ -232,6 +233,7 @@ import javax.inject.Named;
FlagsModule.class,
FlagDependenciesModule.class,
FooterActionsModule.class,
+ KairosCoreStartableModule.class,
GestureModule.class,
InputMethodModule.class,
KeyEventRepositoryModule.class,
diff --git a/packages/SystemUI/src/com/android/systemui/inputdevice/tutorial/ui/composable/ActionTutorialContent.kt b/packages/SystemUI/src/com/android/systemui/inputdevice/tutorial/ui/composable/ActionTutorialContent.kt
index c40adfe6baf8..08e0a9d52faa 100644
--- a/packages/SystemUI/src/com/android/systemui/inputdevice/tutorial/ui/composable/ActionTutorialContent.kt
+++ b/packages/SystemUI/src/com/android/systemui/inputdevice/tutorial/ui/composable/ActionTutorialContent.kt
@@ -33,6 +33,7 @@ import androidx.compose.foundation.layout.safeDrawingPadding
import androidx.compose.foundation.layout.width
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
+import androidx.compose.material3.windowsizeclass.WindowHeightSizeClass
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
@@ -45,11 +46,13 @@ import androidx.compose.ui.graphics.graphicsLayer
import androidx.compose.ui.platform.LocalConfiguration
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp
+import com.android.compose.windowsizeclass.LocalWindowSizeClass
import com.android.systemui.inputdevice.tutorial.ui.composable.TutorialActionState.Error
import com.android.systemui.inputdevice.tutorial.ui.composable.TutorialActionState.Finished
import com.android.systemui.inputdevice.tutorial.ui.composable.TutorialActionState.InProgress
import com.android.systemui.inputdevice.tutorial.ui.composable.TutorialActionState.InProgressAfterError
import com.android.systemui.inputdevice.tutorial.ui.composable.TutorialActionState.NotStarted
+import com.android.systemui.keyboard.shortcut.ui.composable.hasCompactWindowSize
sealed interface TutorialActionState {
data object NotStarted : TutorialActionState
@@ -82,18 +85,25 @@ fun ActionTutorialContent(
) {
Column(
verticalArrangement = Arrangement.Center,
- modifier =
- Modifier.fillMaxSize()
- .background(config.colors.background)
- .safeDrawingPadding()
- .padding(start = 48.dp, top = 100.dp, end = 48.dp, bottom = 8.dp),
+ modifier = Modifier.fillMaxSize().background(config.colors.background).safeDrawingPadding(),
) {
+ val isCompactWindow = hasCompactWindowSize()
when (LocalConfiguration.current.orientation) {
Configuration.ORIENTATION_LANDSCAPE -> {
- HorizontalDescriptionAndAnimation(actionState, config, Modifier.weight(1f))
+ HorizontalDescriptionAndAnimation(
+ actionState,
+ config,
+ isCompactWindow,
+ Modifier.weight(1f),
+ )
}
else -> {
- VerticalDescriptionAndAnimation(actionState, config, Modifier.weight(1f))
+ VerticalDescriptionAndAnimation(
+ actionState,
+ config,
+ isCompactWindow,
+ Modifier.weight(1f),
+ )
}
}
val buttonAlpha by animateFloatAsState(if (actionState is Finished) 1f else 0f)
@@ -109,11 +119,15 @@ fun ActionTutorialContent(
private fun HorizontalDescriptionAndAnimation(
actionState: TutorialActionState,
config: TutorialScreenConfig,
+ isCompactWindow: Boolean,
modifier: Modifier = Modifier,
) {
- Row(modifier = modifier.fillMaxWidth()) {
- TutorialDescription(actionState, config, modifier = Modifier.weight(1f))
- Spacer(modifier = Modifier.width(70.dp))
+ Row(
+ modifier =
+ modifier.fillMaxWidth().padding(start = 48.dp, top = 100.dp, end = 48.dp, bottom = 8.dp)
+ ) {
+ TutorialDescription(actionState, config, isCompactWindow, modifier = Modifier.weight(1f))
+ Spacer(modifier = Modifier.width(24.dp))
TutorialAnimation(actionState, config, modifier = Modifier.weight(1f))
}
}
@@ -122,20 +136,25 @@ private fun HorizontalDescriptionAndAnimation(
private fun VerticalDescriptionAndAnimation(
actionState: TutorialActionState,
config: TutorialScreenConfig,
+ isCompactWindow: Boolean,
modifier: Modifier = Modifier,
) {
- Column(modifier = modifier.fillMaxWidth().padding(horizontal = 40.dp, vertical = 40.dp)) {
- Spacer(modifier = Modifier.weight(0.1f))
+ val horizontalPadding = if (isCompactWindow) 24.dp else 96.dp
+ // Represents the majority of tablets in portrait - we need extra spacer at the top and bottom
+ val isTablet = LocalWindowSizeClass.current.heightSizeClass == WindowHeightSizeClass.Expanded
+ Column(
+ modifier =
+ modifier.fillMaxWidth().padding(start = 0.dp, top = 100.dp, end = 0.dp, bottom = 8.dp)
+ ) {
+ if (isTablet) Spacer(modifier = Modifier.weight(0.3f))
TutorialDescription(
actionState,
config,
- modifier =
- Modifier.weight(0.2f)
- // extra padding to better align with animation which has embedded padding
- .padding(horizontal = 15.dp),
+ isCompactWindow,
+ modifier = Modifier.weight(1f).padding(horizontal = horizontalPadding),
)
- Spacer(modifier = Modifier.width(70.dp))
- TutorialAnimation(actionState, config, modifier = Modifier.weight(1f))
+ TutorialAnimation(actionState, config, modifier = Modifier.weight(1.8f).fillMaxWidth())
+ if (isTablet) Spacer(modifier = Modifier.weight(0.3f))
}
}
@@ -143,6 +162,7 @@ private fun VerticalDescriptionAndAnimation(
fun TutorialDescription(
actionState: TutorialActionState,
config: TutorialScreenConfig,
+ isCompactWindow: Boolean,
modifier: Modifier = Modifier,
) {
val focusRequester = remember { FocusRequester() }
@@ -159,7 +179,9 @@ fun TutorialDescription(
Column(verticalArrangement = Arrangement.Top, modifier = modifier) {
Text(
text = stringResource(id = titleTextId),
- style = MaterialTheme.typography.displayLarge,
+ style =
+ if (isCompactWindow) MaterialTheme.typography.headlineLarge
+ else MaterialTheme.typography.displayMedium,
color = config.colors.title,
modifier = Modifier.focusRequester(focusRequester).focusable(),
)
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/animation/ColorSchemeTransition.kt b/packages/SystemUI/src/com/android/systemui/media/controls/ui/animation/ColorSchemeTransition.kt
index 21407f3bd6d4..02b403786768 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/animation/ColorSchemeTransition.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/animation/ColorSchemeTransition.kt
@@ -27,6 +27,7 @@ import android.graphics.drawable.RippleDrawable
import com.android.internal.R
import com.android.internal.annotations.VisibleForTesting
import com.android.settingslib.Utils
+import com.android.systemui.Flags
import com.android.systemui.media.controls.ui.view.MediaViewHolder
import com.android.systemui.monet.ColorScheme
import com.android.systemui.surfaceeffects.loadingeffect.LoadingEffect
@@ -51,7 +52,7 @@ interface ColorTransition {
open class AnimatingColorTransition(
private val defaultColor: Int,
private val extractColor: (ColorScheme) -> Int,
- private val applyColor: (Int) -> Unit
+ private val applyColor: (Int) -> Unit,
) : AnimatorUpdateListener, ColorTransition {
private val argbEvaluator = ArgbEvaluator()
@@ -105,35 +106,52 @@ internal constructor(
private val mediaViewHolder: MediaViewHolder,
private val multiRippleController: MultiRippleController,
private val turbulenceNoiseController: TurbulenceNoiseController,
- animatingColorTransitionFactory: AnimatingColorTransitionFactory
+ animatingColorTransitionFactory: AnimatingColorTransitionFactory,
) {
constructor(
context: Context,
mediaViewHolder: MediaViewHolder,
multiRippleController: MultiRippleController,
- turbulenceNoiseController: TurbulenceNoiseController
+ turbulenceNoiseController: TurbulenceNoiseController,
) : this(
context,
mediaViewHolder,
multiRippleController,
turbulenceNoiseController,
- ::AnimatingColorTransition
+ ::AnimatingColorTransition,
)
+
var loadingEffect: LoadingEffect? = null
- val bgColor = context.getColor(com.google.android.material.R.color.material_dynamic_neutral20)
+ val bgColor =
+ if (Flags.mediaControlsUiUpdate()) {
+ context.getColor(R.color.materialColorOnSurface)
+ } else {
+ context.getColor(com.google.android.material.R.color.material_dynamic_neutral20)
+ }
+
+ val textColor = context.getColor(R.color.materialColorInverseOnSurface)
+ val buttonBgColor = context.getColor(R.color.materialColorPrimary)
+ val insideButtonColor = context.getColor(R.color.materialColorOnPrimary)
+
val surfaceColor =
animatingColorTransitionFactory(bgColor, ::surfaceFromScheme) { surfaceColor ->
val colorList = ColorStateList.valueOf(surfaceColor)
- mediaViewHolder.seamlessIcon.imageTintList = colorList
- mediaViewHolder.seamlessText.setTextColor(surfaceColor)
mediaViewHolder.albumView.backgroundTintList = colorList
mediaViewHolder.gutsViewHolder.setSurfaceColor(surfaceColor)
+
+ if (Flags.mediaControlsUiUpdate()) return@animatingColorTransitionFactory
+ mediaViewHolder.seamlessIcon.imageTintList = colorList
+ mediaViewHolder.seamlessText.setTextColor(surfaceColor)
}
val accentPrimary =
animatingColorTransitionFactory(
- loadDefaultColor(R.attr.textColorPrimary),
- ::accentPrimaryFromScheme
+ if (Flags.mediaControlsUiUpdate()) {
+ buttonBgColor
+ } else {
+ loadDefaultColor(R.attr.textColorPrimary)
+ },
+ ::accentPrimaryFromScheme,
) { accentPrimary ->
val accentColorList = ColorStateList.valueOf(accentPrimary)
mediaViewHolder.actionPlayPause.backgroundTintList = accentColorList
@@ -145,8 +163,12 @@ internal constructor(
val accentSecondary =
animatingColorTransitionFactory(
- loadDefaultColor(R.attr.textColorPrimary),
- ::accentSecondaryFromScheme
+ if (Flags.mediaControlsUiUpdate()) {
+ buttonBgColor
+ } else {
+ loadDefaultColor(R.attr.textColorPrimary)
+ },
+ ::accentSecondaryFromScheme,
) { accentSecondary ->
val colorList = ColorStateList.valueOf(accentSecondary)
(mediaViewHolder.seamlessButton.background as? RippleDrawable)?.let {
@@ -157,7 +179,11 @@ internal constructor(
val colorSeamless =
animatingColorTransitionFactory(
- loadDefaultColor(R.attr.textColorPrimary),
+ if (Flags.mediaControlsUiUpdate()) {
+ buttonBgColor
+ } else {
+ loadDefaultColor(R.attr.textColorPrimary)
+ },
{ colorScheme: ColorScheme ->
// A1-100 dark in dark theme, A1-200 in light theme
if (
@@ -170,13 +196,17 @@ internal constructor(
{ seamlessColor: Int ->
val accentColorList = ColorStateList.valueOf(seamlessColor)
mediaViewHolder.seamlessButton.backgroundTintList = accentColorList
- }
+ },
)
val textPrimary =
animatingColorTransitionFactory(
- loadDefaultColor(R.attr.textColorPrimary),
- ::textPrimaryFromScheme
+ if (Flags.mediaControlsUiUpdate()) {
+ textColor
+ } else {
+ loadDefaultColor(R.attr.textColorPrimary)
+ },
+ ::textPrimaryFromScheme,
) { textPrimary ->
mediaViewHolder.titleText.setTextColor(textPrimary)
val textColorList = ColorStateList.valueOf(textPrimary)
@@ -192,25 +222,41 @@ internal constructor(
val textPrimaryInverse =
animatingColorTransitionFactory(
- loadDefaultColor(R.attr.textColorPrimaryInverse),
- ::textPrimaryInverseFromScheme
+ if (Flags.mediaControlsUiUpdate()) {
+ insideButtonColor
+ } else {
+ loadDefaultColor(R.attr.textColorPrimaryInverse)
+ },
+ ::textPrimaryInverseFromScheme,
) { textPrimaryInverse ->
- mediaViewHolder.actionPlayPause.imageTintList =
- ColorStateList.valueOf(textPrimaryInverse)
+ val colorList = ColorStateList.valueOf(textPrimaryInverse)
+ mediaViewHolder.actionPlayPause.imageTintList = colorList
+
+ if (!Flags.mediaControlsUiUpdate()) return@animatingColorTransitionFactory
+ mediaViewHolder.seamlessIcon.imageTintList = colorList
+ mediaViewHolder.seamlessText.setTextColor(textPrimaryInverse)
}
val textSecondary =
animatingColorTransitionFactory(
- loadDefaultColor(R.attr.textColorSecondary),
- ::textSecondaryFromScheme
+ if (Flags.mediaControlsUiUpdate()) {
+ textColor
+ } else {
+ loadDefaultColor(R.attr.textColorSecondary)
+ },
+ ::textSecondaryFromScheme,
) { textSecondary ->
mediaViewHolder.artistText.setTextColor(textSecondary)
}
val textTertiary =
animatingColorTransitionFactory(
- loadDefaultColor(R.attr.textColorTertiary),
- ::textTertiaryFromScheme
+ if (Flags.mediaControlsUiUpdate()) {
+ textColor
+ } else {
+ loadDefaultColor(R.attr.textColorTertiary)
+ },
+ ::textTertiaryFromScheme,
) { textTertiary ->
mediaViewHolder.seekBar.progressBackgroundTintList =
ColorStateList.valueOf(textTertiary)
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/HearingDevicesTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/HearingDevicesTile.java
index 74563fff8775..22b742f2f05b 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/HearingDevicesTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/HearingDevicesTile.java
@@ -29,7 +29,6 @@ import android.widget.Button;
import androidx.annotation.Nullable;
import com.android.internal.logging.MetricsLogger;
-import com.android.systemui.Flags;
import com.android.systemui.accessibility.hearingaid.HearingDevicesChecker;
import com.android.systemui.accessibility.hearingaid.HearingDevicesDialogManager;
import com.android.systemui.animation.Expandable;
@@ -138,9 +137,4 @@ public class HearingDevicesTile extends QSTileImpl<BooleanState> {
public CharSequence getTileLabel() {
return mContext.getString(R.string.quick_settings_hearing_devices_label);
}
-
- @Override
- public boolean isAvailable() {
- return Flags.hearingAidsQsTileDialog();
- }
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/hearingdevices/domain/interactor/HearingDevicesTileDataInteractor.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/hearingdevices/domain/interactor/HearingDevicesTileDataInteractor.kt
index ec0a4e9db896..33b7feb49b09 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/hearingdevices/domain/interactor/HearingDevicesTileDataInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/hearingdevices/domain/interactor/HearingDevicesTileDataInteractor.kt
@@ -17,7 +17,6 @@
package com.android.systemui.qs.tiles.impl.hearingdevices.domain.interactor
import android.os.UserHandle
-import com.android.systemui.Flags
import com.android.systemui.accessibility.hearingaid.HearingDevicesChecker
import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.qs.tiles.base.interactor.DataUpdateTrigger
@@ -62,8 +61,7 @@ constructor(
.flowOn(backgroundContext)
.distinctUntilChanged()
- override fun availability(user: UserHandle): Flow<Boolean> =
- flowOf(Flags.hearingAidsQsTileDialog())
+ override fun availability(user: UserHandle): Flow<Boolean> = flowOf(true)
private fun getModel() =
HearingDevicesTileModel(
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/core/StatusBarRootModernization.kt b/packages/SystemUI/src/com/android/systemui/statusbar/core/StatusBarRootModernization.kt
index 057213fa4b18..3c30f3cbec85 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/core/StatusBarRootModernization.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/core/StatusBarRootModernization.kt
@@ -25,6 +25,9 @@ object StatusBarRootModernization {
/** Aconfig flag for removing the fragment */
const val FLAG_NAME = Flags.FLAG_STATUS_BAR_ROOT_MODERNIZATION
+ /** Shows a "compose->bar" text in the status bar for debug purposes */
+ const val SHOW_DISAMBIGUATION = false
+
/** A token used for dependency declaration */
val token: FlagToken
get() = FlagToken(FLAG_NAME, isEnabled)
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/RankingCoordinator.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/RankingCoordinator.java
index 1d37dcf13037..69b069d792e3 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/RankingCoordinator.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/RankingCoordinator.java
@@ -74,7 +74,12 @@ public class RankingCoordinator implements Coordinator {
mStatusBarStateController.addCallback(mStatusBarStateCallback);
pipeline.addPreGroupFilter(mSuspendedFilter);
- pipeline.addPreGroupFilter(mDndVisualEffectsFilter);
+ if (com.android.systemui.Flags.notificationAmbientSuppressionAfterInflation()) {
+ pipeline.addPreGroupFilter(mDndPreGroupFilter);
+ pipeline.addFinalizeFilter(mDndVisualEffectsFilter);
+ } else {
+ pipeline.addPreGroupFilter(mDndVisualEffectsFilter);
+ }
}
public NotifSectioner getAlertingSectioner() {
@@ -191,6 +196,16 @@ public class RankingCoordinator implements Coordinator {
}
};
+ private final NotifFilter mDndPreGroupFilter = new NotifFilter("DndPreGroupFilter") {
+ @Override
+ public boolean shouldFilterOut(NotificationEntry entry, long now) {
+ // Entries with both flags set should be suppressed ASAP regardless of dozing state.
+ // As a result of being doze-independent, they can also be suppressed early in the
+ // pipeline.
+ return entry.shouldSuppressNotificationList() && entry.shouldSuppressAmbient();
+ }
+ };
+
private final StatusBarStateController.StateListener mStatusBarStateCallback =
new StatusBarStateController.StateListener() {
private boolean mPrevDozeAmountIsOne = false;
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 76591ac4e453..7b55e83a0a99 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
@@ -1245,15 +1245,19 @@ public class NotificationStackScrollLayout
@Override
public void setHeadsUpTop(float headsUpTop) {
if (SceneContainerFlag.isUnexpectedlyInLegacyMode()) return;
- mAmbientState.setHeadsUpTop(headsUpTop);
- requestChildrenUpdate();
+ if (mAmbientState.getHeadsUpTop() != headsUpTop) {
+ mAmbientState.setHeadsUpTop(headsUpTop);
+ requestChildrenUpdate();
+ }
}
@Override
public void setHeadsUpBottom(float headsUpBottom) {
if (SceneContainerFlag.isUnexpectedlyInLegacyMode()) return;
- mAmbientState.setHeadsUpBottom(headsUpBottom);
- mStateAnimator.setHeadsUpAppearHeightBottom(Math.round(headsUpBottom));
+ if (mAmbientState.getHeadsUpBottom() != headsUpBottom) {
+ mAmbientState.setHeadsUpBottom(headsUpBottom);
+ mStateAnimator.setHeadsUpAppearHeightBottom(Math.round(headsUpBottom));
+ }
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java
index a2b4e7b474a0..880df79d8e6b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java
@@ -40,12 +40,10 @@ import android.service.notification.ZenModeConfig;
import android.telecom.TelecomManager;
import android.text.format.DateFormat;
import android.util.Log;
-import android.view.View;
import androidx.lifecycle.Observer;
import com.android.internal.statusbar.StatusBarIcon;
-import com.android.systemui.Flags;
import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.dagger.qualifiers.DisplayId;
import com.android.systemui.dagger.qualifiers.Main;
@@ -60,13 +58,10 @@ import com.android.systemui.qs.tiles.DndTile;
import com.android.systemui.qs.tiles.RotationLockTile;
import com.android.systemui.res.R;
import com.android.systemui.screenrecord.RecordingController;
-import com.android.systemui.screenrecord.data.model.ScreenRecordModel;
import com.android.systemui.settings.UserTracker;
import com.android.systemui.statusbar.CommandQueue;
import com.android.systemui.statusbar.phone.ui.StatusBarIconController;
import com.android.systemui.statusbar.policy.BluetoothController;
-import com.android.systemui.statusbar.policy.CastController;
-import com.android.systemui.statusbar.policy.CastDevice;
import com.android.systemui.statusbar.policy.DataSaverController;
import com.android.systemui.statusbar.policy.DataSaverController.Listener;
import com.android.systemui.statusbar.policy.DeviceProvisionedController;
@@ -139,7 +134,6 @@ public class PhoneStatusBarPolicy
private final TelecomManager mTelecomManager;
private final Handler mHandler;
- private final CastController mCast;
private final HotspotController mHotspot;
private final NextAlarmController mNextAlarmController;
private final AlarmManager mAlarmManager;
@@ -161,7 +155,6 @@ public class PhoneStatusBarPolicy
private final Executor mMainExecutor;
private final Executor mUiBgExecutor;
private final SensorPrivacyController mSensorPrivacyController;
- private final RecordingController mRecordingController;
private final RingerModeTracker mRingerModeTracker;
private final PrivacyLogger mPrivacyLogger;
private final ZenModeInteractor mZenModeInteractor;
@@ -180,7 +173,7 @@ public class PhoneStatusBarPolicy
public PhoneStatusBarPolicy(StatusBarIconController iconController,
CommandQueue commandQueue, BroadcastDispatcher broadcastDispatcher,
@Main Executor mainExecutor, @UiBackground Executor uiBgExecutor, @Main Looper looper,
- @Main Resources resources, CastController castController,
+ @Main Resources resources,
HotspotController hotspotController, BluetoothController bluetoothController,
NextAlarmController nextAlarmController, UserInfoController userInfoController,
RotationLockController rotationLockController, DataSaverController dataSaverController,
@@ -190,7 +183,7 @@ public class PhoneStatusBarPolicy
LocationController locationController,
SensorPrivacyController sensorPrivacyController, AlarmManager alarmManager,
UserManager userManager, UserTracker userTracker,
- DevicePolicyManager devicePolicyManager, RecordingController recordingController,
+ DevicePolicyManager devicePolicyManager,
@Nullable TelecomManager telecomManager, @DisplayId int displayId,
@Main SharedPreferences sharedPreferences, DateFormatUtil dateFormatUtil,
RingerModeTracker ringerModeTracker,
@@ -206,7 +199,6 @@ public class PhoneStatusBarPolicy
mBroadcastDispatcher = broadcastDispatcher;
mHandler = new Handler(looper);
mResources = resources;
- mCast = castController;
mHotspot = hotspotController;
mBluetooth = bluetoothController;
mNextAlarmController = nextAlarmController;
@@ -223,7 +215,6 @@ public class PhoneStatusBarPolicy
mLocationController = locationController;
mPrivacyItemController = privacyItemController;
mSensorPrivacyController = sensorPrivacyController;
- mRecordingController = recordingController;
mMainExecutor = mainExecutor;
mUiBgExecutor = uiBgExecutor;
mTelecomManager = telecomManager;
@@ -368,11 +359,6 @@ public class PhoneStatusBarPolicy
this::onMainActiveModeChanged);
}
mZenController.addCallback(mZenControllerCallback);
- if (!Flags.statusBarScreenSharingChips()) {
- // If the flag is enabled, the cast icon is handled in the new screen sharing chips
- // instead of here so we don't need to listen for events here.
- mCast.addCallback(mCastCallback);
- }
mHotspot.addCallback(mHotspotCallback);
mNextAlarmController.addCallback(mNextAlarmCallback);
mDataSaver.addCallback(this);
@@ -380,11 +366,6 @@ public class PhoneStatusBarPolicy
mPrivacyItemController.addCallback(this);
mSensorPrivacyController.addCallback(mSensorPrivacyListener);
mLocationController.addCallback(this);
- if (!Flags.statusBarScreenSharingChips()) {
- // If the flag is enabled, the screen record icon is handled in the new screen sharing
- // chips instead of here so we don't need to listen for events here.
- mRecordingController.addCallback(this);
- }
mJavaAdapter.alwaysCollectFlow(mConnectedDisplayInteractor.getConnectedDisplayState(),
this::onConnectedDisplayAvailabilityChanged);
@@ -583,33 +564,6 @@ public class PhoneStatusBarPolicy
}
}
- private void updateCast() {
- if (Flags.statusBarScreenSharingChips()) {
- // The cast icon is handled in the new screen sharing chips instead of here.
- return;
- }
-
- boolean isCasting = false;
- for (CastDevice device : mCast.getCastDevices()) {
- if (device.isCasting()) {
- isCasting = true;
- break;
- }
- }
- if (DEBUG) Log.v(TAG, "updateCast: isCasting: " + isCasting);
- mHandler.removeCallbacks(mRemoveCastIconRunnable);
- if (isCasting && !mRecordingController.isRecording()) { // screen record has its own icon
- mIconController.setIcon(mSlotCast, R.drawable.stat_sys_cast,
- mResources.getString(R.string.accessibility_casting));
- mIconController.setIconVisibility(mSlotCast, true);
- } else {
- // don't turn off the screen-record icon for a few seconds, just to make sure the user
- // has seen it
- if (DEBUG) Log.v(TAG, "updateCast: hiding icon in 3 sec...");
- mHandler.postDelayed(mRemoveCastIconRunnable, 3000);
- }
- }
-
private void updateProfileIcon() {
// getLastResumedActivityUserId needs to acquire the AM lock, which may be contended in
// some cases. Since it doesn't really matter here whether it's updated in this frame
@@ -678,13 +632,6 @@ public class PhoneStatusBarPolicy
}
};
- private final CastController.Callback mCastCallback = new CastController.Callback() {
- @Override
- public void onCastDevicesChanged() {
- updateCast();
- }
- };
-
private final NextAlarmController.NextAlarmChangeCallback mNextAlarmCallback =
new NextAlarmController.NextAlarmChangeCallback() {
@Override
@@ -856,85 +803,6 @@ public class PhoneStatusBarPolicy
}
};
- private Runnable mRemoveCastIconRunnable = new Runnable() {
- @Override
- public void run() {
- if (Flags.statusBarScreenSharingChips()) {
- // The cast icon is handled in the new screen sharing chips instead of here.
- return;
- }
- if (DEBUG) Log.v(TAG, "updateCast: hiding icon NOW");
- mIconController.setIconVisibility(mSlotCast, false);
- }
- };
-
- // Screen Recording
- @Override
- public void onCountdown(long millisUntilFinished) {
- if (Flags.statusBarScreenSharingChips()) {
- // The screen record icon is handled in the new screen sharing chips instead of here.
- return;
- }
- if (DEBUG) Log.d(TAG, "screenrecord: countdown " + millisUntilFinished);
- int countdown =
- (int) ScreenRecordModel.Starting.Companion.toCountdownSeconds(millisUntilFinished);
- int resourceId = R.drawable.stat_sys_screen_record;
- String description = Integer.toString(countdown);
- switch (countdown) {
- case 1:
- resourceId = R.drawable.stat_sys_screen_record_1;
- break;
- case 2:
- resourceId = R.drawable.stat_sys_screen_record_2;
- break;
- case 3:
- resourceId = R.drawable.stat_sys_screen_record_3;
- break;
- }
- mIconController.setIcon(mSlotScreenRecord, resourceId, description);
- mIconController.setIconVisibility(mSlotScreenRecord, true);
- // Set as assertive so talkback will announce the countdown
- mIconController.setIconAccessibilityLiveRegion(mSlotScreenRecord,
- View.ACCESSIBILITY_LIVE_REGION_ASSERTIVE);
- }
-
- @Override
- public void onCountdownEnd() {
- if (Flags.statusBarScreenSharingChips()) {
- // The screen record icon is handled in the new screen sharing chips instead of here.
- return;
- }
- if (DEBUG) Log.d(TAG, "screenrecord: hiding icon during countdown");
- mHandler.post(() -> mIconController.setIconVisibility(mSlotScreenRecord, false));
- // Reset talkback priority
- mHandler.post(() -> mIconController.setIconAccessibilityLiveRegion(mSlotScreenRecord,
- View.ACCESSIBILITY_LIVE_REGION_NONE));
- }
-
- @Override
- public void onRecordingStart() {
- if (Flags.statusBarScreenSharingChips()) {
- // The screen record icon is handled in the new screen sharing chips instead of here.
- return;
- }
- if (DEBUG) Log.d(TAG, "screenrecord: showing icon");
- mIconController.setIcon(mSlotScreenRecord,
- R.drawable.stat_sys_screen_record,
- mResources.getString(R.string.screenrecord_ongoing_screen_only));
- mHandler.post(() -> mIconController.setIconVisibility(mSlotScreenRecord, true));
- }
-
- @Override
- public void onRecordingEnd() {
- if (Flags.statusBarScreenSharingChips()) {
- // The screen record icon is handled in the new screen sharing chips instead of here.
- return;
- }
- // Ensure this is on the main thread
- if (DEBUG) Log.d(TAG, "screenrecord: hiding icon");
- mHandler.post(() -> mIconController.setIconVisibility(mSlotScreenRecord, false));
- }
-
private void onConnectedDisplayAvailabilityChanged(ConnectedDisplayInteractor.State state) {
boolean visible = state != ConnectedDisplayInteractor.State.DISCONNECTED;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragment.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragment.java
index e622d8f52894..1f1be261a854 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragment.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragment.java
@@ -42,7 +42,6 @@ import com.android.app.animation.Interpolators;
import com.android.app.animation.InterpolatorsAndroidX;
import com.android.keyguard.KeyguardUpdateMonitor;
import com.android.systemui.Dumpable;
-import com.android.systemui.Flags;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.demomode.DemoMode;
import com.android.systemui.demomode.DemoModeController;
@@ -698,20 +697,9 @@ public class CollapsedStatusBarFragment extends Fragment implements CommandQueue
boolean showClock = externalModel.getShowClock() && !headsUpVisible;
- boolean showPrimaryOngoingActivityChip;
- if (Flags.statusBarScreenSharingChips()) {
- // If this flag is on, the ongoing activity status comes from
- // CollapsedStatusBarViewBinder, which updates the mHasPrimaryOngoingActivity variable.
- showPrimaryOngoingActivityChip = mHasPrimaryOngoingActivity;
- } else {
- // If this flag is off, the only ongoing activity is the ongoing call, and we pull it
- // from the controller directly.
- showPrimaryOngoingActivityChip = mOngoingCallController.hasOngoingCall();
- }
+ boolean showPrimaryOngoingActivityChip = mHasPrimaryOngoingActivity;
boolean showSecondaryOngoingActivityChip =
- Flags.statusBarScreenSharingChips()
- && StatusBarNotifChips.isEnabled()
- && mHasSecondaryOngoingActivity;
+ StatusBarNotifChips.isEnabled() && mHasSecondaryOngoingActivity;
return new StatusBarVisibilityModel(
showClock,
@@ -869,10 +857,6 @@ public class CollapsedStatusBarFragment extends Fragment implements CommandQueue
/**
* Displays the primary ongoing activity chip.
- *
- * If Flags.statusBarScreenSharingChips is disabled, this chip will only ever contain the
- * ongoing call information, If that flag is enabled, it will support different kinds of ongoing
- * activities. See b/332662551.
*/
private void showPrimaryOngoingActivityChip(boolean animate) {
StatusBarRootModernization.assertInLegacyMode();
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 f56c2d5dc5e8..4eb69babfadb 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
@@ -24,18 +24,14 @@ import android.content.Context
import android.view.View
import androidx.annotation.VisibleForTesting
import com.android.app.tracing.coroutines.launchTraced as launch
-import com.android.internal.jank.InteractionJankMonitor
import com.android.systemui.CoreStartable
import com.android.systemui.Dumpable
-import com.android.systemui.Flags
-import com.android.systemui.animation.ActivityTransitionAnimator
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.dump.DumpManager
import com.android.systemui.log.LogBuffer
import com.android.systemui.log.core.LogLevel
-import com.android.systemui.plugins.ActivityStarter
import com.android.systemui.res.R
import com.android.systemui.statusbar.StatusBarIconView
import com.android.systemui.statusbar.chips.ui.view.ChipBackgroundContainer
@@ -49,7 +45,6 @@ import com.android.systemui.statusbar.phone.ongoingcall.data.repository.OngoingC
import com.android.systemui.statusbar.phone.ongoingcall.shared.model.OngoingCallModel
import com.android.systemui.statusbar.policy.CallbackController
import com.android.systemui.statusbar.window.StatusBarWindowControllerStore
-import com.android.systemui.util.time.SystemClock
import java.io.PrintWriter
import java.util.concurrent.Executor
import javax.inject.Inject
@@ -69,8 +64,6 @@ constructor(
private val context: Context,
private val ongoingCallRepository: OngoingCallRepository,
private val activeNotificationsInteractor: ActiveNotificationsInteractor,
- private val systemClock: SystemClock,
- private val activityStarter: ActivityStarter,
@Main private val mainExecutor: Executor,
private val iActivityManager: IActivityManager,
private val dumpManager: DumpManager,
@@ -109,7 +102,6 @@ constructor(
scope.launch {
statusBarModeRepository.defaultDisplay.isInFullscreenMode.collect {
isFullscreen = it
- updateChipClickListener()
updateGestureListening()
}
}
@@ -194,7 +186,7 @@ constructor(
if (notifModel == null) {
logger.log(TAG, LogLevel.DEBUG, {}, { "NotifInteractorCallModel: null" })
- removeChip()
+ removeChipInfo()
} else if (notifModel.callType != CallType.Ongoing) {
logger.log(
TAG,
@@ -202,7 +194,7 @@ constructor(
{ str1 = notifModel.callType.name },
{ "Notification Interactor sent ActiveNotificationModel with callType=$str1" },
)
- removeChip()
+ removeChipInfo()
} else {
logger.log(
TAG,
@@ -243,29 +235,10 @@ constructor(
val timeView = currentChipView?.getTimeView()
if (currentChipView != null && timeView != null) {
- if (!Flags.statusBarScreenSharingChips()) {
- // If the new status bar screen sharing chips are enabled, then the display logic
- // for *all* status bar chips (both the call chip and the screen sharing chips) are
- // handled by CollapsedStatusBarViewBinder, *not* this class. We need to disable
- // this class from making any display changes because the new chips use the same
- // view as the old call chip.
- // TODO(b/332662551): We should move this whole controller class to recommended
- // architecture so that we don't need to awkwardly disable only some parts of this
- // class.
- if (currentCallNotificationInfo.hasValidStartTime()) {
- timeView.setShouldHideText(false)
- timeView.base =
- currentCallNotificationInfo.callStartTime -
- systemClock.currentTimeMillis() + systemClock.elapsedRealtime()
- timeView.start()
- } else {
- timeView.setShouldHideText(true)
- timeView.stop()
- }
- updateChipClickListener()
- }
-
- // But, this class still needs to do the non-display logic regardless of the flag.
+ // Current behavior: Displaying the call chip is handled by HomeStatusBarViewBinder, but
+ // this class is still responsible for the non-display logic.
+ // Future behavior: if StatusBarChipsModernization flag is enabled, this class is
+ // completely deprecated and does nothing.
uidObserver.registerWithUid(currentCallNotificationInfo.uid)
if (!currentCallNotificationInfo.statusBarSwipedAway) {
statusBarWindowControllerStore.defaultDisplay
@@ -286,33 +259,6 @@ constructor(
}
}
- private fun updateChipClickListener() {
- StatusBarChipsModernization.assertInLegacyMode()
-
- if (Flags.statusBarScreenSharingChips()) {
- return
- }
-
- if (callNotificationInfo == null) {
- return
- }
- val currentChipView = chipView
- val backgroundView =
- currentChipView?.findViewById<View>(R.id.ongoing_activity_chip_background)
- val intent = callNotificationInfo?.intent
- if (currentChipView != null && backgroundView != null && intent != null) {
- currentChipView.setOnClickListener {
- activityStarter.postStartActivityDismissingKeyguard(
- intent,
- ActivityTransitionAnimator.Controller.fromView(
- backgroundView,
- InteractionJankMonitor.CUJ_STATUS_BAR_APP_LAUNCH_FROM_CALL_CHIP,
- ),
- )
- }
- }
- }
-
/** Returns true if the given [procState] represents a process that's visible to the user. */
private fun isProcessVisibleToUser(procState: Int): Boolean {
StatusBarChipsModernization.assertInLegacyMode()
@@ -336,13 +282,10 @@ constructor(
}
}
- private fun removeChip() {
+ private fun removeChipInfo() {
StatusBarChipsModernization.assertInLegacyMode()
callNotificationInfo = null
- if (!Flags.statusBarScreenSharingChips()) {
- tearDownChipView()
- }
statusBarWindowControllerStore.defaultDisplay.setOngoingProcessRequiresStatusBarVisible(
false
)
@@ -395,13 +338,7 @@ constructor(
val isOngoing: Boolean,
/** True if the user has swiped away the status bar while in this phone call. */
val statusBarSwipedAway: Boolean,
- ) {
- /**
- * Returns true if the notification information has a valid call start time. See
- * b/192379214.
- */
- fun hasValidStartTime(): Boolean = callStartTime > 0
- }
+ )
override fun dump(pw: PrintWriter, args: Array<out String>) {
pw.println("Active call notification: $callNotificationInfo")
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/CarrierConfigRepository.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/CarrierConfigRepository.kt
index 30c529a9034a..3e7094a0b5e8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/CarrierConfigRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/CarrierConfigRepository.kt
@@ -34,6 +34,9 @@ interface CarrierConfigRepository {
*/
suspend fun startObservingCarrierConfigUpdates()
- /** Gets a cached [SystemUiCarrierConfig], or creates a new one which will track the defaults */
- fun getOrCreateConfigForSubId(subId: Int): SystemUiCarrierConfig
+ /**
+ * Gets a cached [SystemUiCarrierConfig], or creates a new one which will track the defaults. A
+ * null [maybeSubId] will return the default carrier config.
+ */
+ fun getOrCreateConfigForSubId(maybeSubId: Int?): SystemUiCarrierConfig
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/CarrierConfigRepositoryImpl.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/CarrierConfigRepositoryImpl.kt
index 9a97f19f6593..b32037992501 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/CarrierConfigRepositoryImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/CarrierConfigRepositoryImpl.kt
@@ -20,6 +20,7 @@ import android.content.IntentFilter
import android.os.PersistableBundle
import android.telephony.CarrierConfigManager
import android.telephony.SubscriptionManager
+import android.telephony.SubscriptionManager.INVALID_SUBSCRIPTION_ID
import android.util.SparseArray
import androidx.annotation.VisibleForTesting
import androidx.core.util.getOrElse
@@ -51,7 +52,7 @@ constructor(
private val defaultConfig: PersistableBundle by lazy { CarrierConfigManager.getDefaultConfig() }
// Used for logging the default config in the dumpsys
private val defaultConfigForLogs: SystemUiCarrierConfig by lazy {
- SystemUiCarrierConfig(-1, defaultConfig)
+ SystemUiCarrierConfig(INVALID_SUBSCRIPTION_ID, defaultConfig)
}
private val configs = SparseArray<SystemUiCarrierConfig>()
@@ -89,7 +90,10 @@ constructor(
configToUpdate.processNewCarrierConfig(config)
}
- override fun getOrCreateConfigForSubId(subId: Int): SystemUiCarrierConfig {
+ override fun getOrCreateConfigForSubId(maybeSubId: Int?): SystemUiCarrierConfig {
+ // See CarrierConfigManager#getConfigForSubId(), passing INVALID_SUBSCRIPTION_ID yields
+ // the default carrier config
+ val subId = maybeSubId ?: INVALID_SUBSCRIPTION_ID
return configs.getOrElse(subId) {
val config = SystemUiCarrierConfig(subId, defaultConfig)
val carrierConfig = carrierConfigManager?.getConfigForSubId(subId)
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/MobileConnectionsRepository.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/MobileConnectionsRepository.kt
index 32e9c85bea81..09a626940c79 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/MobileConnectionsRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/MobileConnectionsRepository.kt
@@ -48,8 +48,8 @@ interface MobileConnectionsRepository {
*/
val activeSubChangedInGroupEvent: Flow<Unit>
- /** Tracks [SubscriptionManager.getDefaultDataSubscriptionId] */
- val defaultDataSubId: StateFlow<Int>
+ /** Tracks [SubscriptionManager.getDefaultDataSubscriptionId]. Null if there is no default */
+ val defaultDataSubId: StateFlow<Int?>
/**
* True if the default network connection is a mobile-like connection and false otherwise.
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/MobileRepositorySwitcher.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/MobileRepositorySwitcher.kt
index fc766915e4ef..252ebe6a32b0 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/MobileRepositorySwitcher.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/MobileRepositorySwitcher.kt
@@ -164,7 +164,7 @@ constructor(
override fun getIsAnySimSecure(): Boolean = activeRepo.value.getIsAnySimSecure()
- override val defaultDataSubId: StateFlow<Int> =
+ override val defaultDataSubId: StateFlow<Int?> =
activeRepo
.flatMapLatest { it.defaultDataSubId }
.stateIn(scope, SharingStarted.WhileSubscribed(), realRepository.defaultDataSubId.value)
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/demo/DemoMobileConnectionsRepository.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/demo/DemoMobileConnectionsRepository.kt
index 936954f3b484..b608e53b4bce 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/demo/DemoMobileConnectionsRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/demo/DemoMobileConnectionsRepository.kt
@@ -166,7 +166,7 @@ constructor(
private fun <K, V> Map<K, V>.reverse() = entries.associateBy({ it.value }) { it.key }
// TODO(b/261029387): add a command for this value
- override val defaultDataSubId = MutableStateFlow(INVALID_SUBSCRIPTION_ID)
+ override val defaultDataSubId: MutableStateFlow<Int?> = MutableStateFlow(null)
// TODO(b/261029387): not yet supported
override val mobileIsDefault: StateFlow<Boolean> = MutableStateFlow(true)
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionsRepositoryImpl.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionsRepositoryImpl.kt
index 589db1690d3f..aa6da61958e0 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionsRepositoryImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionsRepositoryImpl.kt
@@ -58,6 +58,7 @@ import com.android.systemui.util.kotlin.pairwise
import com.android.systemui.utils.coroutines.flow.conflatedCallbackFlow
import java.io.PrintWriter
import java.lang.ref.WeakReference
+import java.util.concurrent.ConcurrentHashMap
import javax.inject.Inject
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.CoroutineScope
@@ -108,9 +109,8 @@ constructor(
) : MobileConnectionsRepository, Dumpable {
// TODO(b/333912012): for now, we are never invalidating the cache. We can do better though
- private var subIdRepositoryCache:
- MutableMap<Int, WeakReference<FullMobileConnectionRepository>> =
- mutableMapOf()
+ private var subIdRepositoryCache =
+ ConcurrentHashMap<Int, WeakReference<FullMobileConnectionRepository>>()
private val defaultNetworkName =
NetworkNameModel.Default(
@@ -249,7 +249,7 @@ constructor(
tableLogger,
LOGGING_PREFIX,
columnName = "activeSubId",
- initialValue = INVALID_SUBSCRIPTION_ID,
+ initialValue = null,
)
.stateIn(scope, started = SharingStarted.WhileSubscribed(), null)
@@ -264,22 +264,31 @@ constructor(
}
.stateIn(scope, SharingStarted.WhileSubscribed(), null)
- override val defaultDataSubId: StateFlow<Int> =
+ override val defaultDataSubId: StateFlow<Int?> =
broadcastDispatcher
.broadcastFlow(
IntentFilter(TelephonyManager.ACTION_DEFAULT_DATA_SUBSCRIPTION_CHANGED)
) { intent, _ ->
- intent.getIntExtra(PhoneConstants.SUBSCRIPTION_KEY, INVALID_SUBSCRIPTION_ID)
+ val subId =
+ intent.getIntExtra(PhoneConstants.SUBSCRIPTION_KEY, INVALID_SUBSCRIPTION_ID)
+ if (subId == INVALID_SUBSCRIPTION_ID) {
+ null
+ } else {
+ subId
+ }
}
.distinctUntilChanged()
.logDiffsForTable(
tableLogger,
LOGGING_PREFIX,
columnName = "defaultSubId",
- initialValue = INVALID_SUBSCRIPTION_ID,
+ initialValue = null,
)
- .onStart { emit(subscriptionManagerProxy.getDefaultDataSubscriptionId()) }
- .stateIn(scope, SharingStarted.WhileSubscribed(), INVALID_SUBSCRIPTION_ID)
+ .onStart {
+ val subId = subscriptionManagerProxy.getDefaultDataSubscriptionId()
+ emit(if (subId == INVALID_SUBSCRIPTION_ID) null else subId)
+ }
+ .stateIn(scope, SharingStarted.WhileSubscribed(), null)
private val carrierConfigChangedEvent =
broadcastDispatcher
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconsInteractor.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconsInteractor.kt
index 78731faa6167..be56461a96ee 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconsInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconsInteractor.kt
@@ -72,7 +72,7 @@ interface MobileIconsInteractor {
val filteredSubscriptions: Flow<List<SubscriptionModel>>
/** Subscription ID of the current default data subscription */
- val defaultDataSubId: Flow<Int>
+ val defaultDataSubId: Flow<Int?>
/**
* The current list of [MobileIconInteractor]s associated with the current list of
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconsViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconsViewModel.kt
index 2cd01170b5ef..67fdb3ae7681 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconsViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconsViewModel.kt
@@ -29,6 +29,7 @@ import com.android.systemui.statusbar.pipeline.mobile.ui.MobileViewLogger
import com.android.systemui.statusbar.pipeline.mobile.ui.VerboseMobileViewLogger
import com.android.systemui.statusbar.pipeline.mobile.ui.view.ModernStatusBarMobileView
import com.android.systemui.statusbar.pipeline.shared.ConnectivityConstants
+import java.util.concurrent.ConcurrentHashMap
import javax.inject.Inject
import kotlin.coroutines.CoroutineContext
import kotlinx.coroutines.CoroutineScope
@@ -62,7 +63,7 @@ constructor(
@Background private val scope: CoroutineScope,
) {
@VisibleForTesting
- val reuseCache = mutableMapOf<Int, Pair<MobileIconViewModel, CoroutineScope>>()
+ val reuseCache = ConcurrentHashMap<Int, Pair<MobileIconViewModel, CoroutineScope>>()
val subscriptionIdsFlow: StateFlow<List<Int>> =
interactor.filteredSubscriptions
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/binder/HomeStatusBarViewBinder.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/binder/HomeStatusBarViewBinder.kt
index 31d6d86d1b37..8daa8037c367 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/binder/HomeStatusBarViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/binder/HomeStatusBarViewBinder.kt
@@ -24,7 +24,6 @@ import androidx.lifecycle.Lifecycle
import androidx.lifecycle.lifecycleScope
import androidx.lifecycle.repeatOnLifecycle
import com.android.app.animation.Interpolators
-import com.android.systemui.Flags
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.lifecycle.repeatWhenAttached
import com.android.systemui.res.R
@@ -116,11 +115,7 @@ constructor(
}
}
- if (
- Flags.statusBarScreenSharingChips() &&
- !StatusBarNotifChips.isEnabled &&
- !StatusBarChipsModernization.isEnabled
- ) {
+ if (!StatusBarNotifChips.isEnabled && !StatusBarChipsModernization.isEnabled) {
val primaryChipViewBinding =
OngoingActivityChipBinder.createBinding(
view.requireViewById(R.id.ongoing_activity_chip_primary)
@@ -166,11 +161,7 @@ constructor(
}
}
- if (
- Flags.statusBarScreenSharingChips() &&
- StatusBarNotifChips.isEnabled &&
- !StatusBarChipsModernization.isEnabled
- ) {
+ if (StatusBarNotifChips.isEnabled && !StatusBarChipsModernization.isEnabled) {
// Create view bindings here so we don't keep re-fetching child views each time
// the chip model changes.
val primaryChipViewBinding =
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/composable/StatusBarRoot.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/composable/StatusBarRoot.kt
index 71e19188f309..b1cc208e9b43 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/composable/StatusBarRoot.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/composable/StatusBarRoot.kt
@@ -32,6 +32,7 @@ import androidx.compose.ui.draw.alpha
import androidx.compose.ui.platform.ComposeView
import androidx.compose.ui.platform.ViewCompositionStrategy
import androidx.compose.ui.viewinterop.AndroidView
+import androidx.core.view.isVisible
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import com.android.app.tracing.coroutines.launchTraced as launch
import com.android.compose.theme.PlatformTheme
@@ -39,6 +40,7 @@ import com.android.keyguard.AlphaOptimizedLinearLayout
import com.android.systemui.plugins.DarkIconDispatcher
import com.android.systemui.res.R
import com.android.systemui.statusbar.chips.ui.compose.OngoingActivityChips
+import com.android.systemui.statusbar.core.StatusBarRootModernization
import com.android.systemui.statusbar.data.repository.DarkIconDispatcherStore
import com.android.systemui.statusbar.events.domain.interactor.SystemStatusEventAnimationInteractor
import com.android.systemui.statusbar.featurepods.popups.StatusBarPopupChips
@@ -142,10 +144,14 @@ fun StatusBarRoot(
Box(Modifier.fillMaxSize()) {
// TODO(b/364360986): remove this before rolling the flag forward
- Disambiguation(viewModel = statusBarViewModel)
+ if (StatusBarRootModernization.SHOW_DISAMBIGUATION) {
+ Disambiguation(viewModel = statusBarViewModel)
+ }
Row(Modifier.fillMaxSize()) {
val scope = rememberCoroutineScope()
+ val visible =
+ statusBarViewModel.shouldHomeStatusBarBeVisible.collectAsStateWithLifecycle(false)
AndroidView(
factory = { context ->
val inflater = LayoutInflater.from(context)
@@ -280,7 +286,12 @@ fun StatusBarRoot(
}
onViewCreated(phoneStatusBarView)
phoneStatusBarView
- }
+ },
+ update = { view ->
+ // Show or hide the entire status bar. This is important so that we aren't
+ // visible when first inflated
+ view.isVisible = visible.value
+ },
)
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/HomeStatusBarViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/HomeStatusBarViewModel.kt
index d9d9a29ee2b6..6ff4354fcc46 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/HomeStatusBarViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/HomeStatusBarViewModel.kt
@@ -80,6 +80,9 @@ import kotlinx.coroutines.flow.stateIn
* so that it's all in one place and easily testable outside of the fragment.
*/
interface HomeStatusBarViewModel {
+ /** Should the entire status bar be hidden? */
+ val shouldHomeStatusBarBeVisible: Flow<Boolean>
+
/**
* True if the device is currently transitioning from lockscreen to occluded and false
* otherwise.
@@ -271,10 +274,12 @@ constructor(
isHomeScreenStatusBarAllowedLegacy
}
- private val shouldHomeStatusBarBeVisible =
- combine(isHomeStatusBarAllowed, keyguardInteractor.isSecureCameraActive) {
+ override val shouldHomeStatusBarBeVisible =
+ combine(
isHomeStatusBarAllowed,
- isSecureCameraActive ->
+ keyguardInteractor.isSecureCameraActive,
+ headsUpNotificationInteractor.statusBarHeadsUpStatus,
+ ) { isHomeStatusBarAllowed, isSecureCameraActive, headsUpState ->
// When launching the camera over the lockscreen, the status icons would typically
// become visible momentarily before animating out, since we're not yet aware that the
// launching camera activity is fullscreen. Even once the activity finishes launching,
@@ -282,7 +287,7 @@ constructor(
// tells us to hide them.
// To ensure that this high-visibility animation is smooth, keep the icons hidden during
// a camera launch. See b/257292822.
- isHomeStatusBarAllowed && !isSecureCameraActive
+ headsUpState.isPinned || (isHomeStatusBarAllowed && !isSecureCameraActive)
}
private val isAnyChipVisible =
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/StatusBarOperatorNameViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/StatusBarOperatorNameViewModel.kt
index 7ae74c3bfb65..0b83c4e3dea3 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/StatusBarOperatorNameViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/StatusBarOperatorNameViewModel.kt
@@ -22,6 +22,7 @@ import javax.inject.Inject
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.flatMapLatest
+import kotlinx.coroutines.flow.flowOf
/**
* View model for the operator name (aka carrier name) of the carrier for the default data
@@ -34,6 +35,10 @@ class StatusBarOperatorNameViewModel
constructor(mobileIconsInteractor: MobileIconsInteractor) {
val operatorName: Flow<String?> =
mobileIconsInteractor.defaultDataSubId.flatMapLatest {
- mobileIconsInteractor.getMobileConnectionInteractorForSubId(it).carrierName
+ if (it == null) {
+ flowOf(null)
+ } else {
+ mobileIconsInteractor.getMobileConnectionInteractorForSubId(it).carrierName
+ }
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/composable/TutorialSelectionScreen.kt b/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/composable/TutorialSelectionScreen.kt
index 66a900bd72d8..c8a58400069e 100644
--- a/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/composable/TutorialSelectionScreen.kt
+++ b/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/composable/TutorialSelectionScreen.kt
@@ -218,9 +218,11 @@ private fun TutorialButton(
verticalArrangement = Arrangement.Center,
horizontalAlignment = Alignment.CenterHorizontally,
) {
+ // contentDescription is set to null because the icon is decorative and we don't want to
+ // repeat the text twice
Icon(
imageVector = icon,
- contentDescription = text,
+ contentDescription = null,
modifier = Modifier.width(30.dp).height(30.dp),
tint = iconColor,
)
diff --git a/packages/SystemUI/src/com/android/systemui/volume/dialog/domain/interactor/VolumeDialogCallbacksInteractor.kt b/packages/SystemUI/src/com/android/systemui/volume/dialog/domain/interactor/VolumeDialogCallbacksInteractor.kt
index 3b0c8a6b46f8..b52c1075e9f0 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/dialog/domain/interactor/VolumeDialogCallbacksInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/dialog/domain/interactor/VolumeDialogCallbacksInteractor.kt
@@ -25,6 +25,7 @@ import com.android.systemui.volume.dialog.dagger.scope.VolumeDialogPluginScope
import com.android.systemui.volume.dialog.domain.model.VolumeDialogEventModel
import javax.inject.Inject
import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.channels.BufferOverflow
import kotlinx.coroutines.channels.ProducerScope
import kotlinx.coroutines.channels.awaitClose
import kotlinx.coroutines.flow.Flow
@@ -54,9 +55,10 @@ constructor(
callbackFlow {
val producer = VolumeDialogEventModelProducer(this)
volumeDialogController.addCallback(producer, bgHandler)
+ send(VolumeDialogEventModel.SubscribedToEvents)
awaitClose { volumeDialogController.removeCallback(producer) }
}
- .buffer(BUFFER_CAPACITY)
+ .buffer(capacity = BUFFER_CAPACITY, onBufferOverflow = BufferOverflow.DROP_OLDEST)
.shareIn(replay = 0, scope = coroutineScope, started = SharingStarted.WhileSubscribed())
private class VolumeDialogEventModelProducer(
diff --git a/packages/SystemUI/src/com/android/systemui/volume/dialog/domain/interactor/VolumeDialogStateInteractor.kt b/packages/SystemUI/src/com/android/systemui/volume/dialog/domain/interactor/VolumeDialogStateInteractor.kt
index 51e79242daaf..26d2414acec1 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/dialog/domain/interactor/VolumeDialogStateInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/dialog/domain/interactor/VolumeDialogStateInteractor.kt
@@ -31,7 +31,6 @@ import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach
-import kotlinx.coroutines.flow.onStart
/**
* Exposes [VolumeDialogController.getState] in the [volumeDialogState].
@@ -65,12 +64,14 @@ constructor(
is VolumeDialogEventModel.ShowSafetyWarning -> {
setSafetyWarning(VolumeDialogSafetyWarningModel.Visible(event.flags))
}
+ is VolumeDialogEventModel.SubscribedToEvents -> {
+ volumeDialogController.getState()
+ }
else -> {
// do nothing
}
}
}
- .onStart { volumeDialogController.getState() }
.launchIn(coroutineScope)
}
diff --git a/packages/SystemUI/src/com/android/systemui/volume/dialog/domain/model/VolumeDialogEventModel.kt b/packages/SystemUI/src/com/android/systemui/volume/dialog/domain/model/VolumeDialogEventModel.kt
index 80e423838251..9793d2be6b98 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/dialog/domain/model/VolumeDialogEventModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/dialog/domain/model/VolumeDialogEventModel.kt
@@ -52,4 +52,11 @@ sealed interface VolumeDialogEventModel {
VolumeDialogEventModel
data object VolumeChangedFromKey : VolumeDialogEventModel
+
+ /**
+ * Signals that the
+ * [com.android.systemui.volume.dialog.domain.interactor.VolumeDialogCallbacksInteractor] is
+ * ready to process the events.
+ */
+ data object SubscribedToEvents : VolumeDialogEventModel
}
diff --git a/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/domain/interactor/VolumeDialogSliderInteractor.kt b/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/domain/interactor/VolumeDialogSliderInteractor.kt
index 3988acbea7c2..b86252ddd1b7 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/domain/interactor/VolumeDialogSliderInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/domain/interactor/VolumeDialogSliderInteractor.kt
@@ -47,7 +47,7 @@ constructor(
) {
val isDisabledByZenMode: Flow<Boolean> =
- if (sliderType is VolumeDialogSliderType.Stream) {
+ if (zenModeInteractor.canBeBlockedByZenMode(sliderType)) {
zenModeInteractor.activeModesBlockingStream(AudioStream(sliderType.audioStream)).map {
it.mainMode != null
}
@@ -75,3 +75,8 @@ constructor(
}
}
}
+
+private fun ZenModeInteractor.canBeBlockedByZenMode(sliderType: VolumeDialogSliderType): Boolean {
+ return sliderType is VolumeDialogSliderType.Stream &&
+ canBeBlockedByZenMode(AudioStream(sliderType.audioStream))
+}
diff --git a/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/viewmodel/VolumeDialogSliderIconProvider.kt b/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/viewmodel/VolumeDialogSliderIconProvider.kt
index daf4c8275d20..71fe22ba4b01 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/viewmodel/VolumeDialogSliderIconProvider.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/viewmodel/VolumeDialogSliderIconProvider.kt
@@ -26,6 +26,7 @@ import com.android.settingslib.volume.shared.model.AudioStream
import com.android.settingslib.volume.shared.model.RingerMode
import com.android.systemui.res.R
import com.android.systemui.statusbar.policy.domain.interactor.ZenModeInteractor
+import com.android.systemui.statusbar.policy.domain.model.ActiveZenModes
import javax.inject.Inject
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.combine
@@ -49,10 +50,10 @@ constructor(
isRoutedToBluetooth: Boolean,
): Flow<Drawable> {
return combine(
- zenModeInteractor.activeModesBlockingStream(AudioStream(stream)),
+ zenModeInteractor.activeModesBlockingStream(stream),
ringerModeForStream(stream),
) { activeModesBlockingStream, ringerMode ->
- if (activeModesBlockingStream.mainMode?.icon != null) {
+ if (activeModesBlockingStream?.mainMode?.icon != null) {
return@combine activeModesBlockingStream.mainMode.icon.drawable
} else {
context.getDrawable(
@@ -141,3 +142,16 @@ constructor(
}
}
}
+
+private fun ZenModeInteractor.activeModesBlockingStream(stream: Int): Flow<ActiveZenModes?> {
+ return if (AudioStream.supportedStreamTypes.contains(stream)) {
+ val audioStream = AudioStream(stream)
+ if (canBeBlockedByZenMode(audioStream)) {
+ activeModesBlockingStream(audioStream)
+ } else {
+ flowOf(null)
+ }
+ } else {
+ flowOf(null)
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/animation/ActivityTransitionAnimatorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/animation/ActivityTransitionAnimatorTest.kt
index 37eb148a5ea7..fd751d9cc7c3 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/animation/ActivityTransitionAnimatorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/animation/ActivityTransitionAnimatorTest.kt
@@ -466,6 +466,28 @@ class ActivityTransitionAnimatorTest : SysuiTestCase() {
assertNull(runner.delegate)
}
+ @Test
+ fun concurrentListenerModification_doesNotThrow() {
+ // Need a second listener to trigger the concurrent modification.
+ activityTransitionAnimator.addListener(object : ActivityTransitionAnimator.Listener {})
+ `when`(listener.onTransitionAnimationStart()).thenAnswer {
+ activityTransitionAnimator.removeListener(listener)
+ listener
+ }
+
+ val runner = activityTransitionAnimator.createEphemeralRunner(controller)
+ runner.onAnimationStart(
+ TRANSIT_NONE,
+ arrayOf(fakeWindow()),
+ emptyArray(),
+ emptyArray(),
+ iCallback,
+ )
+
+ waitForIdleSync()
+ verify(listener).onTransitionAnimationStart()
+ }
+
private fun controllerFactory(
cookie: ActivityTransitionAnimator.TransitionCookie =
mock(ActivityTransitionAnimator.TransitionCookie::class.java),
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragmentTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragmentTest.java
index 30ab416b1cbd..f9df6c79e140 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragmentTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragmentTest.java
@@ -16,7 +16,6 @@ package com.android.systemui.statusbar.phone.fragment;
import static android.view.Display.DEFAULT_DISPLAY;
-import static com.android.systemui.Flags.FLAG_STATUS_BAR_SCREEN_SHARING_CHIPS;
import static com.android.systemui.shade.ShadeExpansionStateManagerKt.STATE_CLOSED;
import static com.android.systemui.shade.ShadeExpansionStateManagerKt.STATE_OPEN;
@@ -496,160 +495,6 @@ public class CollapsedStatusBarFragmentTest extends SysuiBaseFragmentTest {
}
@Test
- @DisableFlags({
- FLAG_STATUS_BAR_SCREEN_SHARING_CHIPS,
- StatusBarRootModernization.FLAG_NAME,
- StatusBarChipsModernization.FLAG_NAME
- })
- public void disable_noOngoingCall_chipHidden() {
- CollapsedStatusBarFragment fragment = resumeAndGetFragment();
-
- when(mOngoingCallController.hasOngoingCall()).thenReturn(false);
-
- fragment.disable(DEFAULT_DISPLAY, 0, 0, false);
-
- assertEquals(View.GONE, getPrimaryOngoingActivityChipView().getVisibility());
- }
-
- @Test
- @DisableFlags({
- FLAG_STATUS_BAR_SCREEN_SHARING_CHIPS,
- StatusBarRootModernization.FLAG_NAME,
- StatusBarChipsModernization.FLAG_NAME
- })
- public void disable_hasOngoingCall_chipDisplayedAndNotificationIconsHidden() {
- CollapsedStatusBarFragment fragment = resumeAndGetFragment();
-
- when(mOngoingCallController.hasOngoingCall()).thenReturn(true);
-
- fragment.disable(DEFAULT_DISPLAY, 0, 0, false);
-
- assertEquals(View.VISIBLE, getPrimaryOngoingActivityChipView().getVisibility());
- assertEquals(View.INVISIBLE, getNotificationAreaView().getVisibility());
- }
-
- @Test
- @DisableFlags({
- FLAG_STATUS_BAR_SCREEN_SHARING_CHIPS,
- StatusBarRootModernization.FLAG_NAME,
- StatusBarChipsModernization.FLAG_NAME
- })
- public void disable_hasOngoingCallButNotificationIconsDisabled_chipHidden() {
- CollapsedStatusBarFragment fragment = resumeAndGetFragment();
-
- when(mOngoingCallController.hasOngoingCall()).thenReturn(true);
-
- fragment.disable(DEFAULT_DISPLAY,
- StatusBarManager.DISABLE_NOTIFICATION_ICONS, 0, false);
-
- assertEquals(View.GONE, getPrimaryOngoingActivityChipView().getVisibility());
- }
-
- @Test
- @DisableFlags({
- FLAG_STATUS_BAR_SCREEN_SHARING_CHIPS,
- StatusBarRootModernization.FLAG_NAME,
- StatusBarChipsModernization.FLAG_NAME
- })
- public void disable_hasOngoingCallButAlsoHun_chipHidden() {
- CollapsedStatusBarFragment fragment = resumeAndGetFragment();
-
- when(mOngoingCallController.hasOngoingCall()).thenReturn(true);
- when(mHeadsUpAppearanceController.shouldHeadsUpStatusBarBeVisible()).thenReturn(true);
-
- fragment.disable(DEFAULT_DISPLAY, 0, 0, false);
-
- assertEquals(View.GONE, getPrimaryOngoingActivityChipView().getVisibility());
- }
-
- @Test
- @DisableFlags({
- FLAG_STATUS_BAR_SCREEN_SHARING_CHIPS,
- StatusBarRootModernization.FLAG_NAME,
- StatusBarChipsModernization.FLAG_NAME
- })
- public void disable_ongoingCallEnded_chipHidden() {
- CollapsedStatusBarFragment fragment = resumeAndGetFragment();
-
- // Ongoing call started
- when(mOngoingCallController.hasOngoingCall()).thenReturn(true);
- fragment.disable(DEFAULT_DISPLAY, 0, 0, false);
-
- assertEquals(View.VISIBLE, getPrimaryOngoingActivityChipView().getVisibility());
-
- // Ongoing call ended
- when(mOngoingCallController.hasOngoingCall()).thenReturn(false);
- fragment.disable(DEFAULT_DISPLAY, 0, 0, false);
-
- assertEquals(View.GONE, getPrimaryOngoingActivityChipView().getVisibility());
-
- // Ongoing call started
- when(mOngoingCallController.hasOngoingCall()).thenReturn(true);
- fragment.disable(DEFAULT_DISPLAY, 0, 0, false);
-
- assertEquals(View.VISIBLE, getPrimaryOngoingActivityChipView().getVisibility());
- }
-
- @Test
- @DisableFlags({
- FLAG_STATUS_BAR_SCREEN_SHARING_CHIPS,
- StatusBarRootModernization.FLAG_NAME,
- StatusBarChipsModernization.FLAG_NAME
- })
- public void disable_hasOngoingCall_hidesNotifsWithoutAnimation() {
- CollapsedStatusBarFragment fragment = resumeAndGetFragment();
- // Enable animations for testing so that we can verify we still aren't animating
- fragment.enableAnimationsForTesting();
- fragment.disable(DEFAULT_DISPLAY, 0, 0, false);
-
- // Ongoing call started
- when(mOngoingCallController.hasOngoingCall()).thenReturn(true);
- fragment.disable(DEFAULT_DISPLAY, 0, 0, true);
-
- // Notification area is hidden without delay
- assertEquals(0f, getNotificationAreaView().getAlpha(), 0.01);
- assertEquals(View.INVISIBLE, getNotificationAreaView().getVisibility());
- }
-
- @Test
- @DisableFlags({
- FLAG_STATUS_BAR_SCREEN_SHARING_CHIPS,
- StatusBarRootModernization.FLAG_NAME,
- StatusBarChipsModernization.FLAG_NAME
- })
- public void screenSharingChipsDisabled_ignoresNewCallback() {
- CollapsedStatusBarFragment fragment = resumeAndGetFragment();
-
- // WHEN there *is* an ongoing call via old callback
- when(mOngoingCallController.hasOngoingCall()).thenReturn(true);
- fragment.disable(DEFAULT_DISPLAY, 0, 0, true);
-
- // WHEN there's *no* ongoing activity via new callback
- mCollapsedStatusBarViewBinder.getListener().onOngoingActivityStatusChanged(
- /* hasPrimaryOngoingActivity= */ false,
- /* hasSecondaryOngoingActivity= */ false,
- /* shouldAnimate= */ false);
-
- // THEN the old callback value is used, so the view is shown
- assertEquals(View.VISIBLE, getPrimaryOngoingActivityChipView().getVisibility());
-
- // WHEN there's *no* ongoing call via old callback
- when(mOngoingCallController.hasOngoingCall()).thenReturn(false);
- fragment.disable(DEFAULT_DISPLAY, 0, 0, false);
-
- // WHEN there *are* ongoing activities via new callback
- mCollapsedStatusBarViewBinder.getListener().onOngoingActivityStatusChanged(
- /* hasPrimaryOngoingActivity= */ true,
- /* hasSecondaryOngoingActivity= */ true,
- /* shouldAnimate= */ false);
-
- // THEN the old callback value is used, so the views are hidden
- assertEquals(View.GONE, getPrimaryOngoingActivityChipView().getVisibility());
- assertEquals(View.GONE, getSecondaryOngoingActivityChipView().getVisibility());
- }
-
- @Test
- @EnableFlags(FLAG_STATUS_BAR_SCREEN_SHARING_CHIPS)
@DisableFlags({StatusBarRootModernization.FLAG_NAME, StatusBarChipsModernization.FLAG_NAME})
public void noOngoingActivity_chipHidden() {
resumeAndGetFragment();
@@ -667,7 +512,6 @@ public class CollapsedStatusBarFragmentTest extends SysuiBaseFragmentTest {
}
@Test
- @EnableFlags(FLAG_STATUS_BAR_SCREEN_SHARING_CHIPS)
@DisableFlags({StatusBarRootModernization.FLAG_NAME, StatusBarChipsModernization.FLAG_NAME})
public void hasPrimaryOngoingActivity_primaryChipDisplayedAndNotificationIconsHidden() {
resumeAndGetFragment();
@@ -683,7 +527,6 @@ public class CollapsedStatusBarFragmentTest extends SysuiBaseFragmentTest {
@Test
@EnableFlags({
- FLAG_STATUS_BAR_SCREEN_SHARING_CHIPS,
StatusBarNotifChips.FLAG_NAME,
StatusBarRootModernization.FLAG_NAME,
StatusBarChipsModernization.FLAG_NAME
@@ -712,7 +555,6 @@ public class CollapsedStatusBarFragmentTest extends SysuiBaseFragmentTest {
}
@Test
- @EnableFlags(FLAG_STATUS_BAR_SCREEN_SHARING_CHIPS)
@DisableFlags({
StatusBarNotifChips.FLAG_NAME,
StatusBarRootModernization.FLAG_NAME,
@@ -730,7 +572,7 @@ public class CollapsedStatusBarFragmentTest extends SysuiBaseFragmentTest {
}
@Test
- @EnableFlags({FLAG_STATUS_BAR_SCREEN_SHARING_CHIPS, StatusBarNotifChips.FLAG_NAME})
+ @EnableFlags({StatusBarNotifChips.FLAG_NAME})
@DisableFlags({StatusBarRootModernization.FLAG_NAME, StatusBarChipsModernization.FLAG_NAME})
public void hasSecondaryOngoingActivity_flagOn_secondaryChipShownAndNotificationIconsHidden() {
resumeAndGetFragment();
@@ -745,7 +587,6 @@ public class CollapsedStatusBarFragmentTest extends SysuiBaseFragmentTest {
}
@Test
- @EnableFlags(FLAG_STATUS_BAR_SCREEN_SHARING_CHIPS)
@DisableFlags({
StatusBarNotifChips.FLAG_NAME,
StatusBarRootModernization.FLAG_NAME,
@@ -766,7 +607,7 @@ public class CollapsedStatusBarFragmentTest extends SysuiBaseFragmentTest {
}
@Test
- @EnableFlags({FLAG_STATUS_BAR_SCREEN_SHARING_CHIPS, StatusBarNotifChips.FLAG_NAME})
+ @EnableFlags({StatusBarNotifChips.FLAG_NAME})
@DisableFlags({StatusBarRootModernization.FLAG_NAME, StatusBarChipsModernization.FLAG_NAME})
public void hasOngoingActivitiesButNotificationIconsDisabled_chipsHidden_notifsFlagOn() {
CollapsedStatusBarFragment fragment = resumeAndGetFragment();
@@ -784,7 +625,6 @@ public class CollapsedStatusBarFragmentTest extends SysuiBaseFragmentTest {
}
@Test
- @EnableFlags(FLAG_STATUS_BAR_SCREEN_SHARING_CHIPS)
@DisableFlags({
StatusBarNotifChips.FLAG_NAME,
StatusBarRootModernization.FLAG_NAME,
@@ -805,7 +645,7 @@ public class CollapsedStatusBarFragmentTest extends SysuiBaseFragmentTest {
}
@Test
- @EnableFlags({FLAG_STATUS_BAR_SCREEN_SHARING_CHIPS, StatusBarNotifChips.FLAG_NAME})
+ @EnableFlags({StatusBarNotifChips.FLAG_NAME})
@DisableFlags({StatusBarRootModernization.FLAG_NAME, StatusBarChipsModernization.FLAG_NAME})
public void hasOngoingActivitiesButAlsoHun_chipsHidden_notifsFlagOn() {
CollapsedStatusBarFragment fragment = resumeAndGetFragment();
@@ -823,7 +663,6 @@ public class CollapsedStatusBarFragmentTest extends SysuiBaseFragmentTest {
}
@Test
- @EnableFlags(FLAG_STATUS_BAR_SCREEN_SHARING_CHIPS)
@DisableFlags({
StatusBarNotifChips.FLAG_NAME,
StatusBarRootModernization.FLAG_NAME,
@@ -850,7 +689,7 @@ public class CollapsedStatusBarFragmentTest extends SysuiBaseFragmentTest {
}
@Test
- @EnableFlags({FLAG_STATUS_BAR_SCREEN_SHARING_CHIPS, StatusBarNotifChips.FLAG_NAME})
+ @EnableFlags({StatusBarNotifChips.FLAG_NAME})
@DisableFlags({StatusBarRootModernization.FLAG_NAME, StatusBarChipsModernization.FLAG_NAME})
public void primaryOngoingActivityEnded_chipHidden_notifsFlagOn() {
resumeAndGetFragment();
@@ -873,7 +712,7 @@ public class CollapsedStatusBarFragmentTest extends SysuiBaseFragmentTest {
}
@Test
- @EnableFlags({FLAG_STATUS_BAR_SCREEN_SHARING_CHIPS, StatusBarNotifChips.FLAG_NAME})
+ @EnableFlags({StatusBarNotifChips.FLAG_NAME})
@DisableFlags({StatusBarRootModernization.FLAG_NAME, StatusBarChipsModernization.FLAG_NAME})
public void secondaryOngoingActivityEnded_chipHidden() {
resumeAndGetFragment();
@@ -896,7 +735,6 @@ public class CollapsedStatusBarFragmentTest extends SysuiBaseFragmentTest {
}
@Test
- @EnableFlags(FLAG_STATUS_BAR_SCREEN_SHARING_CHIPS)
@DisableFlags({
StatusBarNotifChips.FLAG_NAME,
StatusBarRootModernization.FLAG_NAME,
@@ -919,7 +757,7 @@ public class CollapsedStatusBarFragmentTest extends SysuiBaseFragmentTest {
}
@Test
- @EnableFlags({FLAG_STATUS_BAR_SCREEN_SHARING_CHIPS, StatusBarNotifChips.FLAG_NAME})
+ @EnableFlags({StatusBarNotifChips.FLAG_NAME})
@DisableFlags({StatusBarRootModernization.FLAG_NAME, StatusBarChipsModernization.FLAG_NAME})
public void hasOngoingActivity_hidesNotifsWithoutAnimation_notifsFlagOn() {
CollapsedStatusBarFragment fragment = resumeAndGetFragment();
@@ -938,13 +776,12 @@ public class CollapsedStatusBarFragmentTest extends SysuiBaseFragmentTest {
}
@Test
- @EnableFlags(FLAG_STATUS_BAR_SCREEN_SHARING_CHIPS)
@DisableFlags({
StatusBarNotifChips.FLAG_NAME,
StatusBarRootModernization.FLAG_NAME,
StatusBarChipsModernization.FLAG_NAME
})
- public void screenSharingChipsEnabled_ignoresOngoingCallController_notifsFlagOff() {
+ public void ignoresOngoingCallController_notifsFlagOff() {
CollapsedStatusBarFragment fragment = resumeAndGetFragment();
// WHEN there *is* an ongoing call via old callback
@@ -975,9 +812,9 @@ public class CollapsedStatusBarFragmentTest extends SysuiBaseFragmentTest {
}
@Test
- @EnableFlags({FLAG_STATUS_BAR_SCREEN_SHARING_CHIPS, StatusBarNotifChips.FLAG_NAME})
+ @EnableFlags({StatusBarNotifChips.FLAG_NAME})
@DisableFlags({StatusBarRootModernization.FLAG_NAME, StatusBarChipsModernization.FLAG_NAME})
- public void screenSharingChipsEnabled_ignoresOngoingCallController_notifsFlagOn() {
+ public void ignoresOngoingCallController_notifsFlagOn() {
CollapsedStatusBarFragment fragment = resumeAndGetFragment();
// WHEN there *is* an ongoing call via old callback
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionsRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionsRepositoryTest.kt
index d7456dfb33c3..6c60f55a3904 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionsRepositoryTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionsRepositoryTest.kt
@@ -829,7 +829,7 @@ class MobileConnectionsRepositoryTest : SysuiTestCase() {
testScope.runTest {
val latest by collectLastValue(underTest.defaultDataSubId)
- assertThat(latest).isEqualTo(INVALID_SUBSCRIPTION_ID)
+ assertThat(latest).isEqualTo(null)
val intent2 =
Intent(TelephonyManager.ACTION_DEFAULT_DATA_SUBSCRIPTION_CHANGED)
@@ -856,6 +856,31 @@ class MobileConnectionsRepositoryTest : SysuiTestCase() {
}
@Test
+ fun defaultDataSubId_filtersOutInvalidSubIds() =
+ testScope.runTest {
+ subscriptionManagerProxy.defaultDataSubId = INVALID_SUBSCRIPTION_ID
+ val latest by collectLastValue(underTest.defaultDataSubId)
+
+ assertThat(latest).isNull()
+ }
+
+ @Test
+ fun defaultDataSubId_filtersOutInvalidSubIds_fromValidToInvalid() =
+ testScope.runTest {
+ subscriptionManagerProxy.defaultDataSubId = 2
+ val latest by collectLastValue(underTest.defaultDataSubId)
+
+ assertThat(latest).isEqualTo(2)
+
+ val intent =
+ Intent(TelephonyManager.ACTION_DEFAULT_DATA_SUBSCRIPTION_CHANGED)
+ .putExtra(PhoneConstants.SUBSCRIPTION_KEY, INVALID_SUBSCRIPTION_ID)
+ fakeBroadcastDispatcher.sendIntentToMatchingReceiversOnly(context, intent)
+
+ assertThat(latest).isNull()
+ }
+
+ @Test
fun defaultDataSubId_fetchesCurrentOnRestart() =
testScope.runTest {
subscriptionManagerProxy.defaultDataSubId = 2
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/plugins/FakeVolumeDialogController.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/plugins/FakeVolumeDialogController.kt
index 43eb93e4dd53..9d73ae3f176f 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/plugins/FakeVolumeDialogController.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/plugins/FakeVolumeDialogController.kt
@@ -197,7 +197,7 @@ class FakeVolumeDialogController(private val audioManager: AudioManager) : Volum
}
}
-private inline fun CopyOnWriteArraySet<VolumeDialogController.Callbacks>.sendEvent(
+private inline fun Collection<VolumeDialogController.Callbacks>.sendEvent(
event: (callback: VolumeDialogController.Callbacks) -> Unit
) {
for (callback in this) {
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/FakeCarrierConfigRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/FakeCarrierConfigRepository.kt
index adf6ca1d1710..eebfca79efe3 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/FakeCarrierConfigRepository.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/FakeCarrierConfigRepository.kt
@@ -17,6 +17,7 @@
package com.android.systemui.statusbar.pipeline.mobile.data.repository
import android.os.PersistableBundle
+import android.telephony.SubscriptionManager.INVALID_SUBSCRIPTION_ID
import com.android.systemui.statusbar.pipeline.mobile.data.model.SystemUiCarrierConfig
class FakeCarrierConfigRepository : CarrierConfigRepository {
@@ -24,8 +25,12 @@ class FakeCarrierConfigRepository : CarrierConfigRepository {
val configsById = mutableMapOf<Int, SystemUiCarrierConfig>()
- override fun getOrCreateConfigForSubId(subId: Int): SystemUiCarrierConfig =
- configsById.getOrPut(subId) { SystemUiCarrierConfig(subId, createDefaultTestConfig()) }
+ override fun getOrCreateConfigForSubId(maybeSubId: Int?): SystemUiCarrierConfig {
+ val subId = maybeSubId ?: INVALID_SUBSCRIPTION_ID
+ return configsById.getOrPut(subId) {
+ SystemUiCarrierConfig(subId, createDefaultTestConfig())
+ }
+ }
}
val CarrierConfigRepository.fake
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/FakeMobileIconsInteractor.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/FakeMobileIconsInteractor.kt
index 3b8adb4a8307..352f6cf011e1 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/FakeMobileIconsInteractor.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/FakeMobileIconsInteractor.kt
@@ -55,7 +55,7 @@ class FakeMobileIconsInteractor(
override val filteredSubscriptions = MutableStateFlow<List<SubscriptionModel>>(listOf())
- override val defaultDataSubId = MutableStateFlow(DEFAULT_DATA_SUB_ID)
+ override val defaultDataSubId: MutableStateFlow<Int?> = MutableStateFlow(DEFAULT_DATA_SUB_ID)
private val _activeDataConnectionHasDataEnabled = MutableStateFlow(false)
override val activeDataConnectionHasDataEnabled = _activeDataConnectionHasDataEnabled
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/volume/dialog/sliders/dagger/VolumeDialogSliderComponentKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/volume/dialog/sliders/dagger/VolumeDialogSliderComponentKosmos.kt
index 4f79f7b4b41a..cb38cc3576ad 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/volume/dialog/sliders/dagger/VolumeDialogSliderComponentKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/volume/dialog/sliders/dagger/VolumeDialogSliderComponentKosmos.kt
@@ -18,6 +18,7 @@ package com.android.systemui.volume.dialog.sliders.dagger
import android.content.applicationContext
import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.testDispatcher
import com.android.systemui.kosmos.testScope
import com.android.systemui.plugins.volumeDialogController
import com.android.systemui.statusbar.policy.data.repository.zenModeRepository
@@ -74,6 +75,7 @@ private fun Kosmos.setupVolumeDialogSliderComponent(
volumeDialogSliderType = type
applicationContext = parentKosmos.applicationContext
testScope = parentKosmos.testScope
+ testDispatcher = parentKosmos.testDispatcher
volumeDialogController = parentKosmos.volumeDialogController
mediaControllerInteractor = parentKosmos.mediaControllerInteractor
diff --git a/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/BuildScope.kt b/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/BuildScope.kt
index bd2173cd2393..c9fae70353f1 100644
--- a/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/BuildScope.kt
+++ b/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/BuildScope.kt
@@ -100,7 +100,7 @@ interface BuildScope : HasNetwork, StateScope {
* observation of new emissions. It will however *not* cancel any running effects from previous
* emissions. To achieve this behavior, use [launchScope] or [asyncScope] to create a child
* build scope:
- * ``` kotlin
+ * ```
* val job = launchScope {
* events.observe { x ->
* launchEffect { longRunningEffect(x) }
@@ -141,7 +141,7 @@ interface BuildScope : HasNetwork, StateScope {
*
* By default, [builder] is only running while the returned [Events] is being
* [observed][observe]. If you want it to run at all times, simply add a no-op observer:
- * ``` kotlin
+ * ```
* events { ... }.apply { observe() }
* ```
*/
@@ -158,7 +158,7 @@ interface BuildScope : HasNetwork, StateScope {
*
* By default, [builder] is only running while the returned [Events] is being
* [observed][observe]. If you want it to run at all times, simply add a no-op observer:
- * ``` kotlin
+ * ```
* events { ... }.apply { observe() }
* ```
*
@@ -196,7 +196,7 @@ interface BuildScope : HasNetwork, StateScope {
* outside of the current Kairos transaction; when [transform] returns, the returned value is
* emitted from the result [Events] in a new transaction.
*
- * ``` kotlin
+ * ```
* fun <A, B> Events<A>.mapAsyncLatest(transform: suspend (A) -> B): Events<B> =
* mapLatestBuild { a -> asyncEvent { transform(a) } }.flatten()
* ```
@@ -571,7 +571,7 @@ interface BuildScope : HasNetwork, StateScope {
/**
* Shorthand for:
- * ``` kotlin
+ * ```
* flow.toEvents().holdState(initialValue)
* ```
*/
@@ -579,7 +579,7 @@ interface BuildScope : HasNetwork, StateScope {
/**
* Shorthand for:
- * ``` kotlin
+ * ```
* flow.scan(initialValue, operation).toEvents().holdState(initialValue)
* ```
*/
@@ -588,7 +588,7 @@ interface BuildScope : HasNetwork, StateScope {
/**
* Shorthand for:
- * ``` kotlin
+ * ```
* flow.scan(initialValue) { a, f -> f(a) }.toEvents().holdState(initialValue)
* ```
*/
@@ -665,7 +665,7 @@ interface BuildScope : HasNetwork, StateScope {
* be used to make further modifications to the Kairos network, and/or perform side-effects via
* [effect].
*
- * ``` kotlin
+ * ```
* fun <A> State<A>.observeBuild(block: BuildScope.(A) -> Unit = {}): Job = launchScope {
* block(sample())
* changes.observeBuild(block)
@@ -698,7 +698,7 @@ interface BuildScope : HasNetwork, StateScope {
* outside of the current Kairos transaction; when it completes, the returned [Events] emits in a
* new transaction.
*
- * ``` kotlin
+ * ```
* fun <A> BuildScope.asyncEvent(block: suspend () -> A): Events<A> =
* events { emit(block()) }.apply { observe() }
* ```
@@ -719,7 +719,7 @@ fun <A> BuildScope.asyncEvent(block: suspend () -> A): Events<A> =
* executed if this [BuildScope] is still active by that time. It can be deactivated due to a
* -Latest combinator, for example.
*
- * ``` kotlin
+ * ```
* fun BuildScope.effect(
* context: CoroutineContext = EmptyCoroutineContext,
* block: EffectScope.() -> Unit,
@@ -740,7 +740,7 @@ fun BuildScope.effect(
* done because the current [BuildScope] might be deactivated within this transaction, perhaps due
* to a -Latest combinator. If this happens, then the coroutine will never actually be started.
*
- * ``` kotlin
+ * ```
* fun BuildScope.launchEffect(block: suspend KairosScope.() -> Unit): Job =
* effect { effectCoroutineScope.launch { block() } }
* ```
@@ -757,7 +757,7 @@ fun BuildScope.launchEffect(block: suspend KairosCoroutineScope.() -> Unit): Job
* to a -Latest combinator. If this happens, then the coroutine will never actually be started.
*
* Shorthand for:
- * ``` kotlin
+ * ```
* fun <R> BuildScope.asyncEffect(block: suspend KairosScope.() -> R): Deferred<R> =
* CompletableDeferred<R>.apply {
* effect { effectCoroutineScope.launch { complete(block()) } }
@@ -789,7 +789,7 @@ fun BuildScope.launchScope(block: BuildSpec<*>): Job = asyncScope(block).second
*
* By default, [builder] is only running while the returned [Events] is being
* [observed][BuildScope.observe]. If you want it to run at all times, simply add a no-op observer:
- * ``` kotlin
+ * ```
* events { ... }.apply { observe() }
* ```
*
@@ -813,7 +813,7 @@ fun <In, Out> BuildScope.coalescingEvents(
*
* By default, [builder] is only running while the returned [Events] is being
* [observed][BuildScope.observe]. If you want it to run at all times, simply add a no-op observer:
- * ``` kotlin
+ * ```
* events { ... }.apply { observe() }
* ```
*
diff --git a/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/Events.kt b/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/Events.kt
index 8f468c153743..1a13773d71bf 100644
--- a/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/Events.kt
+++ b/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/Events.kt
@@ -105,7 +105,7 @@ class EventsLoop<A> : Events<A>() {
*
* Useful for recursive definitions.
*
- * ``` kotlin
+ * ```
* fun <A> Lazy<Events<A>>.defer() = deferredEvents { value }
* ```
*
@@ -122,7 +122,7 @@ class EventsLoop<A> : Events<A>() {
*
* Useful for recursive definitions.
*
- * ``` kotlin
+ * ```
* fun <A> DeferredValue<Events<A>>.defer() = deferredEvents { get() }
* ```
*
@@ -160,7 +160,7 @@ fun <A, B> Events<A>.mapMaybe(transform: TransactionScope.(A) -> Maybe<B>): Even
* Returns an [Events] that contains only the non-null results of applying [transform] to each value
* of the original [Events].
*
- * ``` kotlin
+ * ```
* fun <A> Events<A>.mapNotNull(transform: TransactionScope.(A) -> B?): Events<B> =
* mapMaybe { if (it == null) absent else present(it) }
* ```
@@ -201,7 +201,7 @@ fun <A, B> Events<A>.mapCheap(transform: TransactionScope.(A) -> B): Events<B> =
* Returns an [Events] that invokes [action] before each value of the original [Events] is emitted.
* Useful for logging and debugging.
*
- * ``` kotlin
+ * ```
* fun <A> Events<A>.onEach(action: TransactionScope.(A) -> Unit): Events<A> =
* map { it.also { action(it) } }
* ```
@@ -220,7 +220,7 @@ fun <A> Events<A>.onEach(action: TransactionScope.(A) -> Unit): Events<A> = map
* Splits an [Events] of pairs into a pair of [Events], where each returned [Events] emits half of
* the original.
*
- * ``` kotlin
+ * ```
* fun <A, B> Events<Pair<A, B>>.unzip(): Pair<Events<A>, Events<B>> {
* val lefts = map { it.first }
* val rights = map { it.second }
diff --git a/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/Filter.kt b/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/Filter.kt
index 8ca5ac8652db..d412b849e441 100644
--- a/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/Filter.kt
+++ b/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/Filter.kt
@@ -29,7 +29,7 @@ fun <A> Events<A>.filter(state: State<Boolean>): Events<A> = filter { state.samp
/**
* Returns an [Events] containing only values of the original [Events] that are not null.
*
- * ``` kotlin
+ * ```
* fun <A> Events<A?>.filterNotNull(): Events<A> = mapNotNull { it }
* ```
*
@@ -41,7 +41,7 @@ fun <A> Events<A?>.filterNotNull(): Events<A> = mapCheap { it.toMaybe() }.filter
/**
* Returns an [Events] containing only values of the original [Events] that are instances of [A].
*
- * ``` kotlin
+ * ```
* inline fun <reified A> Events<*>.filterIsInstance(): Events<A> =
* mapNotNull { it as? A }
* ```
@@ -55,7 +55,7 @@ inline fun <reified A> Events<*>.filterIsInstance(): Events<A> =
/**
* Returns an [Events] containing only values of the original [Events] that are present.
*
- * ``` kotlin
+ * ```
* fun <A> Events<Maybe<A>>.filterPresent(): Events<A> = mapMaybe { it }
* ```
*
@@ -69,7 +69,7 @@ fun <A> Events<Maybe<A>>.filterPresent(): Events<A> =
* Returns an [Events] containing only values of the original [Events] that satisfy the given
* [predicate].
*
- * ``` kotlin
+ * ```
* fun <A> Events<A>.filter(predicate: TransactionScope.(A) -> Boolean): Events<A> =
* mapMaybe { if (predicate(it)) present(it) else absent }
* ```
diff --git a/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/GroupBy.kt b/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/GroupBy.kt
index 45da34ac9ae6..27fc1b4cf54c 100644
--- a/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/GroupBy.kt
+++ b/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/GroupBy.kt
@@ -63,7 +63,7 @@ fun <K, A> Events<Map<K, A>>.groupByKey(numKeys: Int? = null): GroupedEvents<K,
* downstream [Events]. The downstream [Events] are associated with a [key][K], which is derived
* from each emission of the original [Events] via [extractKey].
*
- * ``` kotlin
+ * ```
* fun <K, A> Events<A>.groupBy(
* numKeys: Int? = null,
* extractKey: TransactionScope.(A) -> K,
@@ -108,7 +108,7 @@ class GroupedEvents<in K, out A> internal constructor(internal val impl: DemuxIm
* Using this is equivalent to `upstream.filter(predicate) to upstream.filter { !predicate(it) }`
* but is more efficient; specifically, [partition] will only invoke [predicate] once per element.
*
- * ``` kotlin
+ * ```
* fun <A> Events<A>.partition(
* predicate: TransactionScope.(A) -> Boolean
* ): Pair<Events<A>, Events<A>> =
@@ -133,7 +133,7 @@ fun <A> Events<A>.partition(
* [First]s and once for [Second]s, but is slightly more efficient; specifically, the
* [filterIsInstance] check is only performed once per element.
*
- * ``` kotlin
+ * ```
* fun <A, B> Events<Either<A, B>>.partitionEither(): Pair<Events<A>, Events<B>> =
* map { it.asThese() }.partitionThese()
* ```
diff --git a/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/Incremental.kt b/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/Incremental.kt
index d88ae3b81349..02941bdded30 100644
--- a/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/Incremental.kt
+++ b/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/Incremental.kt
@@ -62,7 +62,7 @@ fun <K, V> incrementalOf(value: Map<K, V>): Incremental<K, V> {
*
* Useful for recursive definitions.
*
- * ``` kotlin
+ * ```
* fun <A> Lazy<Incremental<K, V>>.defer() = deferredIncremental { value }
* ```
*/
@@ -78,7 +78,7 @@ fun <K, V> Lazy<Incremental<K, V>>.defer(): Incremental<K, V> = deferInline { va
*
* Useful for recursive definitions.
*
- * ``` kotlin
+ * ```
* fun <A> DeferredValue<Incremental<K, V>>.defer() = deferredIncremental { get() }
* ```
*/
diff --git a/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/Merge.kt b/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/Merge.kt
index de9dca43b5d5..cc4ce53ca40a 100644
--- a/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/Merge.kt
+++ b/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/Merge.kt
@@ -33,7 +33,7 @@ import com.android.systemui.kairos.util.map
* function is used to combine coincident emissions to produce the result value to be emitted by the
* merged [Events].
*
- * ``` kotlin
+ * ```
* fun <A> Events<A>.mergeWith(
* other: Events<A>,
* transformCoincidence: TransactionScope.(A, A) -> A = { a, _ -> a },
@@ -62,7 +62,7 @@ fun <A> Events<A>.mergeWith(
* Merges the given [Events] into a single [Events] that emits events from all. All coincident
* emissions are collected into the emitted [List], preserving the input ordering.
*
- * ``` kotlin
+ * ```
* fun <A> merge(vararg events: Events<A>): Events<List<A>> = events.asIterable().merge()
* ```
*
@@ -76,7 +76,7 @@ fun <A> merge(vararg events: Events<A>): Events<List<A>> = events.asIterable().m
* Merges the given [Events] into a single [Events] that emits events from all. In the case of
* coincident emissions, the emission from the left-most [Events] is emitted.
*
- * ``` kotlin
+ * ```
* fun <A> mergeLeft(vararg events: Events<A>): Events<A> = events.asIterable().mergeLeft()
* ```
*
@@ -92,7 +92,7 @@ fun <A> mergeLeft(vararg events: Events<A>): Events<A> = events.asIterable().mer
* function is used to combine coincident emissions to produce the result value to be emitted by the
* merged [Events].
*
- * ``` kotlin
+ * ```
* fun <A> merge(vararg events: Events<A>, transformCoincidence: (A, A) -> A): Events<A> =
* merge(*events).map { l -> l.reduce(transformCoincidence) }
* ```
@@ -117,7 +117,7 @@ fun <A> Iterable<Events<A>>.merge(): Events<List<A>> =
* coincident emissions, the emission from the left-most [Events] is emitted.
*
* Semantically equivalent to the following definition:
- * ``` kotlin
+ * ```
* fun <A> Iterable<Events<A>>.mergeLeft(): Events<A> =
* merge().mapCheap { it.first() }
* ```
@@ -135,7 +135,7 @@ fun <A> Iterable<Events<A>>.mergeLeft(): Events<A> =
* Creates a new [Events] that emits events from all given [Events]. All simultaneous emissions are
* collected into the emitted [List], preserving the input ordering.
*
- * ``` kotlin
+ * ```
* fun <A> Sequence<Events<A>>.merge(): Events<List<A>> = asIterable().merge()
* ```
*
@@ -148,7 +148,7 @@ fun <A> Iterable<Events<A>>.mergeLeft(): Events<A> =
* collected into the emitted [Map], and are given the same key of the associated [Events] in the
* input [Map].
*
- * ``` kotlin
+ * ```
* fun <K, A> Map<K, Events<A>>.merge(): Events<Map<K, A>> =
* asSequence()
* .map { (k, events) -> events.map { a -> k to a } }
@@ -173,7 +173,7 @@ fun <K, A> Map<K, Events<A>>.merge(): Events<Map<K, A>> =
* [Map.applyPatch][com.android.systemui.kairos.util.applyPatch].
*
* Conceptually this is equivalent to:
- * ``` kotlin
+ * ```
* fun <K, V> State<Map<K, V>>.mergeEventsIncrementally(): Events<Map<K, V>> =
* map { it.merge() }.switchEvents()
* ```
@@ -218,7 +218,7 @@ fun <K, V> Incremental<K, Events<V>>.mergeEventsIncrementally(): Events<Map<K, V
* [Map.applyPatch][com.android.systemui.kairos.util.applyPatch].
*
* Conceptually this is equivalent to:
- * ``` kotlin
+ * ```
* fun <K, V> State<Map<K, V>>.mergeEventsIncrementallyPromptly(): Events<Map<K, V>> =
* map { it.merge() }.switchEventsPromptly()
* ```
diff --git a/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/State.kt b/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/State.kt
index 22ca83c6a15a..e8b005ec8788 100644
--- a/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/State.kt
+++ b/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/State.kt
@@ -76,7 +76,7 @@ fun <A> stateOf(value: A): State<A> {
*
* Useful for recursive definitions.
*
- * ``` kotlin
+ * ```
* fun <A> Lazy<State<A>>.defer() = deferredState { value }
* ```
*/
@@ -91,7 +91,7 @@ fun <A> stateOf(value: A): State<A> {
*
* Useful for recursive definitions.
*
- * ``` kotlin
+ * ```
* fun <A> DeferredValue<State<A>>.defer() = deferredState { get() }
* ```
*/
@@ -150,7 +150,7 @@ fun <A, B> State<A>.mapCheapUnsafe(transform: KairosScope.(A) -> B): State<B> {
* Splits a [State] of pairs into a pair of [Events][State], where each returned [State] holds half
* of the original.
*
- * ``` kotlin
+ * ```
* fun <A, B> State<Pair<A, B>>.unzip(): Pair<State<A>, State<B>> {
* val first = map { it.first }
* val second = map { it.second }
@@ -186,7 +186,7 @@ fun <A, B> State<A>.flatMap(transform: KairosScope.(A) -> State<B>): State<B> {
/**
* Returns a [State] that behaves like the current value of the original [State].
*
- * ``` kotlin
+ * ```
* fun <A> State<State<A>>.flatten() = flatMap { it }
* ```
*
@@ -201,7 +201,7 @@ fun <A, B> State<A>.flatMap(transform: KairosScope.(A) -> State<B>): State<B> {
* recent value is used.
*
* Effectively equivalent to:
- * ``` kotlin
+ * ```
* ConflatedMutableEvents(kairosNetwork).holdState(initialValue)
* ```
*/
@@ -328,7 +328,7 @@ private inline fun <A> deferInline(crossinline block: InitScope.() -> State<A>):
* Like [changes] but also includes the old value of this [State].
*
* Shorthand for:
- * ``` kotlin
+ * ```
* stateChanges.map { WithPrev(previousValue = sample(), newValue = it) }
* ```
*/
diff --git a/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/StateScope.kt b/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/StateScope.kt
index faeffe84e2e8..8020896d228a 100644
--- a/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/StateScope.kt
+++ b/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/StateScope.kt
@@ -116,7 +116,7 @@ interface StateScope : TransactionScope {
* [Events] emitted from this, following the patch rules outlined in
* [Map.applyPatch][com.android.systemui.kairos.util.applyPatch].
*
- * ``` kotlin
+ * ```
* fun <K, V> Events<MapPatch<K, Events<V>>>.mergeEventsIncrementally(
* initialEvents: DeferredValue<Map<K, Events<V>>>,
* ): Events<Map<K, V>> =
@@ -135,7 +135,7 @@ interface StateScope : TransactionScope {
* [Events] emitted from this, following the patch rules outlined in
* [Map.applyPatch][com.android.systemui.kairos.util.applyPatch].
*
- * ``` kotlin
+ * ```
* fun <K, V> Events<MapPatch<K, Events<V>>>.mergeEventsIncrementallyPromptly(
* initialEvents: DeferredValue<Map<K, Events<V>>>,
* ): Events<Map<K, V>> =
@@ -155,7 +155,7 @@ interface StateScope : TransactionScope {
* [Events] emitted from this, following the patch rules outlined in
* [Map.applyPatch][com.android.systemui.kairos.util.applyPatch].
*
- * ``` kotlin
+ * ```
* fun <K, V> Events<MapPatch<K, Events<V>>>.mergeEventsIncrementally(
* initialEvents: Map<K, Events<V>>,
* ): Events<Map<K, V>> =
@@ -174,7 +174,7 @@ interface StateScope : TransactionScope {
* [Events] emitted from this, following the patch rules outlined in
* [Map.applyPatch][com.android.systemui.kairos.util.applyPatch].
*
- * ``` kotlin
+ * ```
* fun <K, V> Events<MapPatch<K, Events<V>>>.mergeEventsIncrementallyPromptly(
* initialEvents: Map<K, Events<V>>,
* ): Events<Map<K, V>> =
@@ -220,7 +220,7 @@ interface StateScope : TransactionScope {
* [mapLatestStateful], accumulation is not stopped with each subsequent emission of the
* original [Events].
*
- * ``` kotlin
+ * ```
* fun <A, B> Events<A>.mapStateful(transform: StateScope.(A) -> B): Events<B> =
* map { statefully { transform(it) } }.applyStatefuls()
* ```
@@ -234,7 +234,7 @@ interface StateScope : TransactionScope {
*
* Unlike [applyLatestStateful], state accumulation is not stopped with each state change.
*
- * ``` kotlin
+ * ```
* fun <A> State<Stateful<A>>.applyStatefuls(): State<A> =
* changes
* .applyStatefuls()
@@ -252,7 +252,7 @@ interface StateScope : TransactionScope {
* Returns an [Events] that acts like the most recent [Events] to be emitted from the original
* [Events].
*
- * ``` kotlin
+ * ```
* fun <A> Events<Events<A>>.flatten() = holdState(emptyEvents).switchEvents()
* ```
*
@@ -267,7 +267,7 @@ interface StateScope : TransactionScope {
* [transform] can perform state accumulation via its [StateScope] receiver. With each
* invocation of [transform], state accumulation from previous invocation is stopped.
*
- * ``` kotlin
+ * ```
* fun <A, B> Events<A>.mapLatestStateful(transform: StateScope.(A) -> B): Events<B> =
* map { statefully { transform(it) } }.applyLatestStateful()
* ```
@@ -282,7 +282,7 @@ interface StateScope : TransactionScope {
* [transform] can perform state accumulation via its [StateScope] receiver. With each
* invocation of [transform], state accumulation from previous invocation is stopped.
*
- * ``` kotlin
+ * ```
* fun <A, B> Events<A>.flatMapLatestStateful(
* transform: StateScope.(A) -> Events<B>
* ): Events<B> =
@@ -495,7 +495,7 @@ interface StateScope : TransactionScope {
*
* The optional [numKeys] argument is an optimization used to initialize the internal storage.
*
- * ``` kotlin
+ * ```
* fun <K, A, B> Events<MapPatch<K, A>>.mapLatestStatefulForKey(
* numKeys: Int? = null,
* transform: StateScope.(A) -> B,
@@ -516,7 +516,7 @@ interface StateScope : TransactionScope {
* If the original [Events] is emitting an event at this exact time, then it will be the only
* even emitted from the result [Events].
*
- * ``` kotlin
+ * ```
* fun <A> Events<A>.nextOnly(): Events<A> =
* EventsLoop<A>().apply {
* loopback = map { emptyEvents }.holdState(this@nextOnly).switchEvents()
@@ -535,7 +535,7 @@ interface StateScope : TransactionScope {
/**
* Returns an [Events] that skips the next emission of the original [Events].
*
- * ``` kotlin
+ * ```
* fun <A> Events<A>.skipNext(): Events<A> =
* nextOnly().map { this@skipNext }.holdState(emptyEvents).switchEvents()
* ```
@@ -554,7 +554,7 @@ interface StateScope : TransactionScope {
* If the original [Events] emits at the same time as [stop], then the returned [Events] will
* emit that value.
*
- * ``` kotlin
+ * ```
* fun <A> Events<A>.takeUntil(stop: Events<*>): Events<A> =
* stop.map { emptyEvents }.nextOnly().holdState(this).switchEvents()
* ```
@@ -586,7 +586,7 @@ interface StateScope : TransactionScope {
* Returns an [Events] that emits values from the original [Events] up to and including a value
* is emitted that satisfies [predicate].
*
- * ``` kotlin
+ * ```
* fun <A> Events<A>.takeUntil(predicate: TransactionScope.(A) -> Boolean): Events<A> =
* takeUntil(filter(predicate))
* ```
@@ -602,7 +602,7 @@ interface StateScope : TransactionScope {
* have been processed; this keeps the value of the [State] consistent during the entire Kairos
* transaction.
*
- * ``` kotlin
+ * ```
* fun <A, B> Events<A>.foldState(
* initialValue: B,
* transform: TransactionScope.(A, B) -> B,
@@ -630,7 +630,7 @@ interface StateScope : TransactionScope {
* have been processed; this keeps the value of the [State] consistent during the entire Kairos
* transaction.
*
- * ``` kotlin
+ * ```
* fun <A, B> Events<A>.foldStateDeferred(
* initialValue: DeferredValue<B>,
* transform: TransactionScope.(A, B) -> B,
@@ -663,7 +663,7 @@ interface StateScope : TransactionScope {
* have been processed; this keeps the value of the [State] consistent during the entire Kairos
* transaction.
*
- * ``` kotlin
+ * ```
* fun <A> Events<Stateful<A>>.holdLatestStateful(init: Stateful<A>): State<A> {
* val (changes, initApplied) = applyLatestStateful(init)
* return changes.holdStateDeferred(initApplied)
@@ -724,7 +724,7 @@ interface StateScope : TransactionScope {
* Returns an [Events] that wraps each emission of the original [Events] into an [IndexedValue],
* containing the emitted value and its index (starting from zero).
*
- * ``` kotlin
+ * ```
* fun <A> Events<A>.withIndex(): Events<IndexedValue<A>> {
* val index = fold(0) { _, oldIdx -> oldIdx + 1 }
* return sample(index) { a, idx -> IndexedValue(idx, a) }
@@ -740,7 +740,7 @@ interface StateScope : TransactionScope {
* Returns an [Events] containing the results of applying [transform] to each value of the
* original [Events] and its index (starting from zero).
*
- * ``` kotlin
+ * ```
* fun <A> Events<A>.mapIndexed(transform: TransactionScope.(Int, A) -> B): Events<B> {
* val index = foldState(0) { _, i -> i + 1 }
* return sample(index) { a, idx -> transform(idx, a) }
@@ -755,7 +755,7 @@ interface StateScope : TransactionScope {
/**
* Returns an [Events] where all subsequent repetitions of the same value are filtered out.
*
- * ``` kotlin
+ * ```
* fun <A> Events<A>.distinctUntilChanged(): Events<A> {
* val state: State<Any?> = holdState(Any())
* return filter { it != state.sample() }
@@ -774,7 +774,7 @@ interface StateScope : TransactionScope {
* Note that the returned [Events] will not emit anything until [other] has emitted at least one
* value.
*
- * ``` kotlin
+ * ```
* fun <A, B, C> Events<A>.sample(
* other: Events<B>,
* transform: TransactionScope.(A, B) -> C,
@@ -796,7 +796,7 @@ interface StateScope : TransactionScope {
* Returns a [State] that samples the [Transactional] held by the given [State] within the same
* transaction that the state changes.
*
- * ``` kotlin
+ * ```
* fun <A> State<Transactional<A>>.sampleTransactionals(): State<A> =
* changes
* .sampleTransactionals()
@@ -815,7 +815,7 @@ interface StateScope : TransactionScope {
* Note that this is less efficient than [State.map], which should be preferred if [transform]
* does not need access to [TransactionScope].
*
- * ``` kotlin
+ * ```
* fun <A, B> State<A>.mapTransactionally(transform: TransactionScope.(A) -> B): State<B> =
* map { transactionally { transform(it) } }.sampleTransactionals()
* ```
@@ -830,7 +830,7 @@ interface StateScope : TransactionScope {
* Note that this is less efficient than [combine], which should be preferred if [transform]
* does not need access to [TransactionScope].
*
- * ``` kotlin
+ * ```
* fun <A, B, Z> combineTransactionally(
* stateA: State<A>,
* stateB: State<B>,
@@ -895,7 +895,7 @@ interface StateScope : TransactionScope {
* Note that this is less efficient than [flatMap], which should be preferred if [transform]
* does not need access to [TransactionScope].
*
- * ``` kotlin
+ * ```
* fun <A, B> State<A>.flatMapTransactionally(
* transform: TransactionScope.(A) -> State<B>
* ): State<B> = map { transactionally { transform(it) } }.sampleTransactionals().flatten()
@@ -950,7 +950,7 @@ interface StateScope : TransactionScope {
* Returns an [Incremental] that reflects the state of the original [Incremental], but also adds
* / removes entries based on the state of the original's values.
*
- * ``` kotlin
+ * ```
* fun <K, V> Incremental<K, State<Maybe<V>>>.applyStateIncrementally(): Incremental<K, V> =
* mapValues { (_, v) -> v.changes }
* .mergeEventsIncrementallyPromptly()
@@ -971,7 +971,7 @@ interface StateScope : TransactionScope {
* / removes entries based on the [State] returned from applying [transform] to the original's
* entries.
*
- * ``` kotlin
+ * ```
* fun <K, V, U> Incremental<K, V>.mapIncrementalState(
* transform: KairosScope.(Map.Entry<K, V>) -> State<Maybe<U>>
* ): Incremental<K, U> = mapValues { transform(it) }.applyStateIncrementally()
@@ -986,7 +986,7 @@ interface StateScope : TransactionScope {
* / removes entries based on the [State] returned from applying [transform] to the original's
* entries, such that entries are added when that state is `true`, and removed when `false`.
*
- * ``` kotlin
+ * ```
* fun <K, V> Incremental<K, V>.filterIncrementally(
* transform: KairosScope.(Map.Entry<K, V>) -> State<Boolean>
* ): Incremental<K, V> = mapIncrementalState { entry ->
@@ -1004,7 +1004,7 @@ interface StateScope : TransactionScope {
* Returns an [Incremental] that samples the [Transactionals][Transactional] held by the
* original within the same transaction that the incremental [updates].
*
- * ``` kotlin
+ * ```
* fun <K, V> Incremental<K, Transactional<V>>.sampleTransactionals(): Incremental<K, V> =
* updates
* .map { patch -> patch.mapValues { (k, mv) -> mv.map { it.sample() } } }
@@ -1027,7 +1027,7 @@ interface StateScope : TransactionScope {
* Note that this is less efficient than [mapValues], which should be preferred if [transform]
* does not need access to [TransactionScope].
*
- * ``` kotlin
+ * ```
* fun <K, V, U> Incremental<K, V>.mapValuesTransactionally(
* transform: TransactionScope.(Map.Entry<K, V>) -> U
* ): Incremental<K, U> =
diff --git a/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/Transactional.kt b/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/Transactional.kt
index cf98821fdadb..5050511a1371 100644
--- a/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/Transactional.kt
+++ b/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/Transactional.kt
@@ -51,7 +51,7 @@ fun <A> transactionalOf(value: A): Transactional<A> =
*
* Useful for recursive definitions.
*
- * ``` kotlin
+ * ```
* fun <A> DeferredValue<Transactional<A>>.defer() = deferredTransactional { get() }
* ```
*/
@@ -67,7 +67,7 @@ fun <A> DeferredValue<Transactional<A>>.defer(): Transactional<A> = deferInline
*
* Useful for recursive definitions.
*
- * ``` kotlin
+ * ```
* fun <A> Lazy<Transactional<A>>.defer() = deferredTransactional { value }
* ```
*/
diff --git a/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/internal/store/MapK.kt b/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/internal/store/MapK.kt
index e193a4957bd0..3dbc6f00c623 100644
--- a/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/internal/store/MapK.kt
+++ b/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/internal/store/MapK.kt
@@ -21,28 +21,28 @@ package com.android.systemui.kairos.internal.store
*
* Let's say you want to write a class that is generic over both a map, and the type of data within
* the map:
- * ``` kotlin
+ * ```
* class Foo<TMap, TKey, TValue> {
* val container: TMap<TKey, TElement> // disallowed!
* }
* ```
*
* You can use `MapK` to represent the "higher-kinded" type variable `TMap`:
- * ``` kotlin
+ * ```
* class Foo<TMap, TKey, TValue> {
* val container: MapK<TMap, TKey, TValue> // OK!
* }
* ```
*
* Note that Kotlin will not let you use the generic type without parameters as `TMap`:
- * ``` kotlin
+ * ```
* val fooHk: MapK<HashMap, Int, String> // not allowed: HashMap requires two type parameters
* ```
*
* To work around this, you need to declare a special type-witness object. This object is only used
* at compile time and can be stripped out by a minifier because it's never used at runtime.
*
- * ``` kotlin
+ * ```
* class Foo<A, B> : MapK<FooWitness, A, B> { ... }
* object FooWitness
*
diff --git a/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/util/MapPatch.kt b/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/util/MapPatch.kt
index 8fe41bc20dfa..dde5d822f052 100644
--- a/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/util/MapPatch.kt
+++ b/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/util/MapPatch.kt
@@ -47,7 +47,7 @@ fun <K, V> Map<K, V>.applyPatch(patch: MapPatch<K, V>): Map<K, V> {
* Returns a [MapPatch] that, when applied, includes all of the values from the original [Map].
*
* Shorthand for:
- * ``` kotlin
+ * ```
* mapValues { (key, value) -> Maybe.present(value) }
* ```
*/
diff --git a/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/util/Maybe.kt b/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/util/Maybe.kt
index 4754bc443329..4373705597ed 100644
--- a/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/util/Maybe.kt
+++ b/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/util/Maybe.kt
@@ -69,7 +69,7 @@ object MaybeScope {
*
* This can be used instead of Kotlin's built-in nullability (`?.` and `?:`) operators when dealing
* with complex combinations of nullables:
- * ``` kotlin
+ * ```
* val aMaybe: Maybe<Any> = ...
* val bMaybe: Maybe<Any> = ...
* val result: String = maybe {
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
index 8e037c3ba90c..b08aa5b37b33 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
@@ -4385,13 +4385,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
private void launchAccessibilityFrameworkFeature(int displayId, ComponentName assignedTarget) {
if (assignedTarget.equals(ACCESSIBILITY_HEARING_AIDS_COMPONENT_NAME)) {
- //import com.android.systemui.Flags;
- if (com.android.systemui.Flags.hearingAidsQsTileDialog()) {
- launchHearingDevicesDialog();
- } else {
- launchAccessibilitySubSettings(displayId,
- ACCESSIBILITY_HEARING_AIDS_COMPONENT_NAME);
- }
+ launchHearingDevicesDialog();
}
}
diff --git a/services/backup/java/com/android/server/backup/BackupAgentConnectionManager.java b/services/backup/java/com/android/server/backup/BackupAgentConnectionManager.java
index 6ced096e8778..d8fbde4115d9 100644
--- a/services/backup/java/com/android/server/backup/BackupAgentConnectionManager.java
+++ b/services/backup/java/com/android/server/backup/BackupAgentConnectionManager.java
@@ -16,7 +16,7 @@
package com.android.server.backup;
-import static com.android.server.backup.BackupManagerService.MORE_DEBUG;
+import static com.android.server.backup.BackupManagerService.DEBUG;
import static com.android.server.backup.BackupManagerService.TAG;
import android.annotation.Nullable;
@@ -302,7 +302,7 @@ public class BackupAgentConnectionManager {
// that the package being backed up doesn't get stuck in restricted mode until the
// backup time-out elapses.
for (int token : mOperationStorage.operationTokensForPackage(packageName)) {
- if (MORE_DEBUG) {
+ if (DEBUG) {
Slog.d(TAG,
mUserIdMsg + "agentDisconnected: will handleCancel(all) for token:"
+ Integer.toHexString(token));
diff --git a/services/backup/java/com/android/server/backup/BackupManagerConstants.java b/services/backup/java/com/android/server/backup/BackupManagerConstants.java
index b753d01562ca..6aa5e2c05457 100644
--- a/services/backup/java/com/android/server/backup/BackupManagerConstants.java
+++ b/services/backup/java/com/android/server/backup/BackupManagerConstants.java
@@ -16,8 +16,6 @@
package com.android.server.backup;
-import static com.android.server.backup.BackupManagerService.DEBUG_SCHEDULING;
-
import android.app.AlarmManager;
import android.app.job.JobInfo;
import android.content.ContentResolver;
@@ -168,85 +166,55 @@ public class BackupManagerConstants extends KeyValueSettingObserver {
// group the calls of these methods in a block syncrhonized on
// a reference of this object.
public synchronized long getKeyValueBackupIntervalMilliseconds() {
- if (DEBUG_SCHEDULING) {
- Slog.v(
- TAG,
- "getKeyValueBackupIntervalMilliseconds(...) returns "
- + mKeyValueBackupIntervalMilliseconds);
- }
+ Slog.d(TAG, "getKeyValueBackupIntervalMilliseconds(...) returns "
+ + mKeyValueBackupIntervalMilliseconds);
return mKeyValueBackupIntervalMilliseconds;
}
public synchronized long getKeyValueBackupFuzzMilliseconds() {
- if (DEBUG_SCHEDULING) {
- Slog.v(
- TAG,
- "getKeyValueBackupFuzzMilliseconds(...) returns "
- + mKeyValueBackupFuzzMilliseconds);
- }
+ Slog.d(TAG, "getKeyValueBackupFuzzMilliseconds(...) returns "
+ + mKeyValueBackupFuzzMilliseconds);
return mKeyValueBackupFuzzMilliseconds;
}
public synchronized boolean getKeyValueBackupRequireCharging() {
- if (DEBUG_SCHEDULING) {
- Slog.v(
- TAG,
- "getKeyValueBackupRequireCharging(...) returns "
- + mKeyValueBackupRequireCharging);
- }
+ Slog.d(TAG,
+ "getKeyValueBackupRequireCharging(...) returns " + mKeyValueBackupRequireCharging);
return mKeyValueBackupRequireCharging;
}
public synchronized int getKeyValueBackupRequiredNetworkType() {
- if (DEBUG_SCHEDULING) {
- Slog.v(
- TAG,
- "getKeyValueBackupRequiredNetworkType(...) returns "
- + mKeyValueBackupRequiredNetworkType);
- }
+ Slog.d(TAG, "getKeyValueBackupRequiredNetworkType(...) returns "
+ + mKeyValueBackupRequiredNetworkType);
return mKeyValueBackupRequiredNetworkType;
}
public synchronized long getFullBackupIntervalMilliseconds() {
- if (DEBUG_SCHEDULING) {
- Slog.v(
- TAG,
- "getFullBackupIntervalMilliseconds(...) returns "
- + mFullBackupIntervalMilliseconds);
- }
+ Slog.d(TAG, "getFullBackupIntervalMilliseconds(...) returns "
+ + mFullBackupIntervalMilliseconds);
return mFullBackupIntervalMilliseconds;
}
public synchronized boolean getFullBackupRequireCharging() {
- if (DEBUG_SCHEDULING) {
- Slog.v(TAG, "getFullBackupRequireCharging(...) returns " + mFullBackupRequireCharging);
- }
+ Slog.d(TAG, "getFullBackupRequireCharging(...) returns " + mFullBackupRequireCharging);
return mFullBackupRequireCharging;
}
public synchronized int getFullBackupRequiredNetworkType() {
- if (DEBUG_SCHEDULING) {
- Slog.v(
- TAG,
- "getFullBackupRequiredNetworkType(...) returns "
- + mFullBackupRequiredNetworkType);
- }
+ Slog.d(TAG,
+ "getFullBackupRequiredNetworkType(...) returns " + mFullBackupRequiredNetworkType);
return mFullBackupRequiredNetworkType;
}
/** Returns an array of package names that should be notified whenever a backup finishes. */
public synchronized String[] getBackupFinishedNotificationReceivers() {
- if (DEBUG_SCHEDULING) {
- Slog.v(
- TAG,
- "getBackupFinishedNotificationReceivers(...) returns "
- + TextUtils.join(", ", mBackupFinishedNotificationReceivers));
- }
+ Slog.d(TAG, "getBackupFinishedNotificationReceivers(...) returns " + TextUtils.join(", ",
+ mBackupFinishedNotificationReceivers));
return mBackupFinishedNotificationReceivers;
}
public synchronized long getWakelockTimeoutMillis() {
- Slog.v(TAG, "wakelock timeout: " + mWakelockTimeoutMillis);
+ Slog.d(TAG, "wakelock timeout: " + mWakelockTimeoutMillis);
return mWakelockTimeoutMillis;
}
}
diff --git a/services/backup/java/com/android/server/backup/BackupManagerService.java b/services/backup/java/com/android/server/backup/BackupManagerService.java
index 8804faf2d312..5edf08cc1d80 100644
--- a/services/backup/java/com/android/server/backup/BackupManagerService.java
+++ b/services/backup/java/com/android/server/backup/BackupManagerService.java
@@ -94,9 +94,7 @@ import java.util.Set;
*/
public class BackupManagerService extends IBackupManager.Stub implements BackupManagerInternal {
public static final String TAG = "BackupManagerService";
- public static final boolean DEBUG = true;
- public static final boolean MORE_DEBUG = false;
- public static final boolean DEBUG_SCHEDULING = true;
+ public static final boolean DEBUG = false;
@VisibleForTesting
static final String DUMP_RUNNING_USERS_MESSAGE = "Backup Manager is running for users:";
@@ -187,9 +185,7 @@ public class BackupManagerService extends IBackupManager.Stub implements BackupM
mUserRemovedReceiver, new IntentFilter(Intent.ACTION_USER_REMOVED));
UserHandle mainUser = getUserManager().getMainUser();
mDefaultBackupUserId = mainUser == null ? UserHandle.USER_SYSTEM : mainUser.getIdentifier();
- if (DEBUG) {
- Slog.d(TAG, "Default backup user id = " + mDefaultBackupUserId);
- }
+ Slog.d(TAG, "Default backup user id = " + mDefaultBackupUserId);
}
@VisibleForTesting
diff --git a/services/backup/java/com/android/server/backup/BackupWakeLock.java b/services/backup/java/com/android/server/backup/BackupWakeLock.java
new file mode 100644
index 000000000000..d839e7a1583b
--- /dev/null
+++ b/services/backup/java/com/android/server/backup/BackupWakeLock.java
@@ -0,0 +1,95 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.backup;
+
+import static com.android.server.backup.BackupManagerService.TAG;
+
+import android.annotation.Nullable;
+import android.os.PowerManager;
+import android.os.WorkSource;
+import android.util.Slog;
+
+/**
+ * Wrapper over {@link PowerManager.WakeLock} to prevent double-free exceptions on release()
+ * after quit().
+ *
+ * <p>There should be a single instance of this class per {@link UserBackupManagerService}.
+ */
+public class BackupWakeLock {
+ private final PowerManager.WakeLock mPowerManagerWakeLock;
+ private boolean mHasQuit = false;
+ private final String mUserIdMessage;
+ private final BackupManagerConstants mBackupManagerConstants;
+
+ public BackupWakeLock(PowerManager.WakeLock powerManagerWakeLock, int userId,
+ BackupManagerConstants backupManagerConstants) {
+ mPowerManagerWakeLock = powerManagerWakeLock;
+ mUserIdMessage = "[UserID:" + userId + "] ";
+ mBackupManagerConstants = backupManagerConstants;
+ }
+
+ /** Acquires the {@link PowerManager.WakeLock} if hasn't been quit. */
+ public synchronized void acquire() {
+ if (mHasQuit) {
+ Slog.d(TAG, mUserIdMessage + "Ignore wakelock acquire after quit: "
+ + mPowerManagerWakeLock.getTag());
+ return;
+ }
+ // Set a timeout for the wakelock. Otherwise if we fail internally and never call
+ // release(), the device might stay awake and drain battery indefinitely.
+ mPowerManagerWakeLock.acquire(mBackupManagerConstants.getWakelockTimeoutMillis());
+ Slog.d(TAG, mUserIdMessage + "Acquired wakelock:" + mPowerManagerWakeLock.getTag());
+ }
+
+ /** Releases the {@link PowerManager.WakeLock} if hasn't been quit. */
+ public synchronized void release() {
+ if (mHasQuit) {
+ Slog.d(TAG, mUserIdMessage + "Ignore wakelock release after quit: "
+ + mPowerManagerWakeLock.getTag());
+ return;
+ }
+
+ if (!mPowerManagerWakeLock.isHeld()) {
+ Slog.w(TAG, mUserIdMessage + "Wakelock not held: " + mPowerManagerWakeLock.getTag());
+ return;
+ }
+
+ mPowerManagerWakeLock.release();
+ Slog.d(TAG, mUserIdMessage + "Released wakelock:" + mPowerManagerWakeLock.getTag());
+ }
+
+ /**
+ * Returns true if the {@link PowerManager.WakeLock} has been acquired but not yet released.
+ */
+ public synchronized boolean isHeld() {
+ return mPowerManagerWakeLock.isHeld();
+ }
+
+ /** Release the {@link PowerManager.WakeLock} till it isn't held. */
+ public synchronized void quit() {
+ while (mPowerManagerWakeLock.isHeld()) {
+ Slog.d(TAG, mUserIdMessage + "Releasing wakelock: " + mPowerManagerWakeLock.getTag());
+ mPowerManagerWakeLock.release();
+ }
+ mHasQuit = true;
+ }
+
+ /** Calls {@link PowerManager.WakeLock#setWorkSource} on the underlying wake lock. */
+ public void setWorkSource(@Nullable WorkSource workSource) {
+ mPowerManagerWakeLock.setWorkSource(workSource);
+ }
+}
diff --git a/services/backup/java/com/android/server/backup/KeyValueAdbBackupEngine.java b/services/backup/java/com/android/server/backup/KeyValueAdbBackupEngine.java
index b343ec8e709b..9f4407d99e18 100644
--- a/services/backup/java/com/android/server/backup/KeyValueAdbBackupEngine.java
+++ b/services/backup/java/com/android/server/backup/KeyValueAdbBackupEngine.java
@@ -180,9 +180,7 @@ public class KeyValueAdbBackupEngine {
Slog.e(TAG, "Key-value backup failed on package " + packageName);
return false;
}
- if (DEBUG) {
- Slog.i(TAG, "Key-value backup success for package " + packageName);
- }
+ Slog.i(TAG, "Key-value backup success for package " + packageName);
return true;
} catch (RemoteException e) {
Slog.e(TAG, "Error invoking agent for backup on " + packageName + ". " + e);
@@ -210,9 +208,7 @@ public class KeyValueAdbBackupEngine {
AppMetadataBackupWriter writer =
new AppMetadataBackupWriter(output, mPackageManager);
- if (DEBUG) {
- Slog.d(TAG, "Writing manifest for " + mPackage.packageName);
- }
+ Slog.d(TAG, "Writing manifest for " + mPackage.packageName);
writer.backupManifest(
mPackage,
@@ -223,9 +219,7 @@ public class KeyValueAdbBackupEngine {
/* withApk */ false);
mManifestFile.delete();
- if (DEBUG) {
- Slog.d(TAG, "Writing key-value package payload" + mPackage.packageName);
- }
+ Slog.d(TAG, "Writing key-value package payload" + mPackage.packageName);
FullBackup.backupToTar(mPackage.packageName, FullBackup.KEY_VALUE_DATA_TOKEN, null,
mDataDir.getAbsolutePath(),
mBackupDataName.getAbsolutePath(),
@@ -283,9 +277,7 @@ public class KeyValueAdbBackupEngine {
if (!mBackupManagerService.waitUntilOperationComplete(token)) {
Slog.e(TAG, "Full backup failed on package " + mCurrentPackage.packageName);
} else {
- if (DEBUG) {
- Slog.d(TAG, "Full package backup success: " + mCurrentPackage.packageName);
- }
+ Slog.d(TAG, "Full package backup success: " + mCurrentPackage.packageName);
}
} catch (IOException e) {
Slog.e(TAG, "Error backing up " + mCurrentPackage.packageName + ": " + e);
diff --git a/services/backup/java/com/android/server/backup/KeyValueAdbRestoreEngine.java b/services/backup/java/com/android/server/backup/KeyValueAdbRestoreEngine.java
index 3184bd87601a..b68a0e42908b 100644
--- a/services/backup/java/com/android/server/backup/KeyValueAdbRestoreEngine.java
+++ b/services/backup/java/com/android/server/backup/KeyValueAdbRestoreEngine.java
@@ -89,10 +89,8 @@ public class KeyValueAdbRestoreEngine implements Runnable {
ParcelFileDescriptor newState = ParcelFileDescriptor.open(newStateName,
MODE_READ_WRITE | MODE_CREATE | MODE_TRUNCATE);
- if (DEBUG) {
- Slog.i(TAG, "Starting restore of package " + pkg + " for version code "
+ Slog.i(TAG, "Starting restore of package " + pkg + " for version code "
+ info.version);
- }
agent.doRestore(backupData, info.version, newState, mToken,
mBackupManagerService.getBackupManagerBinder());
} catch (IOException e) {
diff --git a/services/backup/java/com/android/server/backup/KeyValueBackupJob.java b/services/backup/java/com/android/server/backup/KeyValueBackupJob.java
index 9a788be2f46d..30fdb65fc3cf 100644
--- a/services/backup/java/com/android/server/backup/KeyValueBackupJob.java
+++ b/services/backup/java/com/android/server/backup/KeyValueBackupJob.java
@@ -16,7 +16,6 @@
package com.android.server.backup;
-import static com.android.server.backup.BackupManagerService.DEBUG_SCHEDULING;
import static com.android.server.pm.PackageManagerService.PLATFORM_PACKAGE_NAME;
import android.app.AlarmManager;
@@ -92,9 +91,7 @@ public class KeyValueBackupJob extends JobService {
if (delay <= 0) {
delay = interval + new Random().nextInt((int) fuzz);
}
- if (DEBUG_SCHEDULING) {
- Slog.v(TAG, "Scheduling k/v pass in " + (delay / 1000 / 60) + " minutes");
- }
+ Slog.d(TAG, "Scheduling k/v pass in " + (delay / 1000 / 60) + " minutes");
JobInfo.Builder builder = new JobInfo.Builder(getJobIdForUserId(userId),
sKeyValueJobService)
diff --git a/services/backup/java/com/android/server/backup/PackageManagerBackupAgent.java b/services/backup/java/com/android/server/backup/PackageManagerBackupAgent.java
index 52108bf1568d..e17063ab7803 100644
--- a/services/backup/java/com/android/server/backup/PackageManagerBackupAgent.java
+++ b/services/backup/java/com/android/server/backup/PackageManagerBackupAgent.java
@@ -240,7 +240,7 @@ public class PackageManagerBackupAgent extends BackupAgent {
try {
if (!mExisting.contains(ANCESTRAL_RECORD_KEY)) {
// The old state does not store info on ancestral record
- Slog.v(
+ Slog.d(
TAG,
"No ancestral record version in the old state. Storing "
+ "ancestral record version key");
@@ -249,7 +249,7 @@ public class PackageManagerBackupAgent extends BackupAgent {
upgradingAncestralRecordVersion = true;
} else if (mStoredAncestralRecordVersion != ANCESTRAL_RECORD_VERSION) {
// The current ancestral record version has changed from the old state
- Slog.v(
+ Slog.d(
TAG,
"Ancestral record version has changed from old state. Storing"
+ "ancestral record version key");
diff --git a/services/backup/java/com/android/server/backup/ProcessedPackagesJournal.java b/services/backup/java/com/android/server/backup/ProcessedPackagesJournal.java
index edc2379ff641..3a6f228a7f6f 100644
--- a/services/backup/java/com/android/server/backup/ProcessedPackagesJournal.java
+++ b/services/backup/java/com/android/server/backup/ProcessedPackagesJournal.java
@@ -46,7 +46,6 @@ import java.util.Set;
final class ProcessedPackagesJournal {
private static final String TAG = "ProcessedPackagesJournal";
private static final String JOURNAL_FILE_NAME = "processed";
- private static final boolean DEBUG = BackupManagerService.DEBUG;
// using HashSet instead of ArraySet since we expect 100-500 elements range
@GuardedBy("mProcessedPackages")
@@ -136,9 +135,7 @@ final class ProcessedPackagesJournal {
new BufferedInputStream(new FileInputStream(journalFile)))) {
while (true) {
String packageName = oldJournal.readUTF();
- if (DEBUG) {
- Slog.v(TAG, " + " + packageName);
- }
+ Slog.d(TAG, " + " + packageName);
mProcessedPackages.add(packageName);
}
} catch (EOFException e) {
diff --git a/services/backup/java/com/android/server/backup/TransportManager.java b/services/backup/java/com/android/server/backup/TransportManager.java
index a792db0f7c78..d33bfecf1ba9 100644
--- a/services/backup/java/com/android/server/backup/TransportManager.java
+++ b/services/backup/java/com/android/server/backup/TransportManager.java
@@ -62,7 +62,7 @@ import java.util.function.Predicate;
/** Handles in-memory bookkeeping of all BackupTransport objects. */
public class TransportManager {
private static final String TAG = "BackupTransportManager";
- private static final boolean MORE_DEBUG = false;
+ private static final boolean DEBUG = false;
@VisibleForTesting
public static final String SERVICE_ACTION_TRANSPORT_HOST = "android.backup.TRANSPORT_HOST";
@@ -155,7 +155,7 @@ public class TransportManager {
enabled = mPackageManager.getApplicationEnabledSetting(packageName);
} catch (IllegalArgumentException ex) {
// packageName doesn't exist: likely due to a race with it being uninstalled.
- if (MORE_DEBUG) {
+ if (DEBUG) {
Slog.d(TAG, addUserIdToLogMessage(mUserId, "Package " + packageName
+ " was changed, but no longer exists."));
}
@@ -163,7 +163,7 @@ public class TransportManager {
}
switch (enabled) {
case COMPONENT_ENABLED_STATE_ENABLED: {
- if (MORE_DEBUG) {
+ if (DEBUG) {
Slog.d(TAG, addUserIdToLogMessage(mUserId, "Package " + packageName
+ " was enabled."));
}
@@ -174,7 +174,7 @@ public class TransportManager {
// Package is set to its default enabled state (as specified in its manifest).
// Unless explicitly specified in manifest, the default enabled state
// is 'enabled'. Here, we assume that default state always means enabled.
- if (MORE_DEBUG) {
+ if (DEBUG) {
Slog.d(TAG, addUserIdToLogMessage(mUserId, "Package " + packageName
+ " was put in default enabled state."));
}
@@ -182,7 +182,7 @@ public class TransportManager {
return;
}
case COMPONENT_ENABLED_STATE_DISABLED: {
- if (MORE_DEBUG) {
+ if (DEBUG) {
Slog.d(TAG, addUserIdToLogMessage(mUserId, "Package " + packageName
+ " was disabled."));
}
@@ -190,7 +190,7 @@ public class TransportManager {
return;
}
case COMPONENT_ENABLED_STATE_DISABLED_USER: {
- if (MORE_DEBUG) {
+ if (DEBUG) {
Slog.d(TAG, addUserIdToLogMessage(mUserId, "Package " + packageName
+ " was disabled by user."));
}
diff --git a/services/backup/java/com/android/server/backup/UserBackupManagerService.java b/services/backup/java/com/android/server/backup/UserBackupManagerService.java
index ac1f50f85d64..5af2346650ed 100644
--- a/services/backup/java/com/android/server/backup/UserBackupManagerService.java
+++ b/services/backup/java/com/android/server/backup/UserBackupManagerService.java
@@ -19,8 +19,6 @@ package com.android.server.backup;
import static android.content.pm.ApplicationInfo.PRIVATE_FLAG_BACKUP_IN_FOREGROUND;
import static com.android.server.backup.BackupManagerService.DEBUG;
-import static com.android.server.backup.BackupManagerService.DEBUG_SCHEDULING;
-import static com.android.server.backup.BackupManagerService.MORE_DEBUG;
import static com.android.server.backup.BackupManagerService.TAG;
import static com.android.server.backup.internal.BackupHandler.MSG_BACKUP_OPERATION_TIMEOUT;
import static com.android.server.backup.internal.BackupHandler.MSG_FULL_CONFIRMATION_TIMEOUT;
@@ -88,7 +86,6 @@ import android.os.RemoteException;
import android.os.SELinux;
import android.os.SystemClock;
import android.os.UserHandle;
-import android.os.WorkSource;
import android.provider.Settings;
import android.text.TextUtils;
import android.util.ArraySet;
@@ -174,88 +171,6 @@ import java.util.concurrent.atomic.AtomicInteger;
/** System service that performs backup/restore operations. */
public class UserBackupManagerService {
- /**
- * Wrapper over {@link PowerManager.WakeLock} to prevent double-free exceptions on release()
- * after quit().
- */
- public static class BackupWakeLock {
- private final PowerManager.WakeLock mPowerManagerWakeLock;
- private boolean mHasQuit = false;
- private final int mUserId;
- private final BackupManagerConstants mBackupManagerConstants;
-
- public BackupWakeLock(PowerManager.WakeLock powerManagerWakeLock, int userId,
- BackupManagerConstants backupManagerConstants) {
- mPowerManagerWakeLock = powerManagerWakeLock;
- mUserId = userId;
- mBackupManagerConstants = backupManagerConstants;
- }
-
- /** Acquires the {@link PowerManager.WakeLock} if hasn't been quit. */
- public synchronized void acquire() {
- if (mHasQuit) {
- Slog.v(
- TAG,
- addUserIdToLogMessage(
- mUserId,
- "Ignore wakelock acquire after quit: "
- + mPowerManagerWakeLock.getTag()));
- return;
- }
- // Set a timeout for the wakelock. Otherwise if we fail internally and never call
- // release(), the device might stay awake and drain battery indefinitely.
- mPowerManagerWakeLock.acquire(mBackupManagerConstants.getWakelockTimeoutMillis());
- Slog.v(
- TAG,
- addUserIdToLogMessage(
- mUserId, "Acquired wakelock:" + mPowerManagerWakeLock.getTag()));
- }
-
- /** Releases the {@link PowerManager.WakeLock} if hasn't been quit. */
- public synchronized void release() {
- if (mHasQuit) {
- Slog.v(
- TAG,
- addUserIdToLogMessage(
- mUserId,
- "Ignore wakelock release after quit: "
- + mPowerManagerWakeLock.getTag()));
- return;
- }
-
- if (!mPowerManagerWakeLock.isHeld()) {
- Slog.w(TAG, addUserIdToLogMessage(mUserId,
- "Wakelock not held: " + mPowerManagerWakeLock.getTag()));
- return;
- }
-
- mPowerManagerWakeLock.release();
- Slog.v(
- TAG,
- addUserIdToLogMessage(
- mUserId, "Released wakelock:" + mPowerManagerWakeLock.getTag()));
- }
-
- /**
- * Returns true if the {@link PowerManager.WakeLock} has been acquired but not yet released.
- */
- public synchronized boolean isHeld() {
- return mPowerManagerWakeLock.isHeld();
- }
-
- /** Release the {@link PowerManager.WakeLock} till it isn't held. */
- public synchronized void quit() {
- while (mPowerManagerWakeLock.isHeld()) {
- Slog.v(
- TAG,
- addUserIdToLogMessage(
- mUserId, "Releasing wakelock: " + mPowerManagerWakeLock.getTag()));
- mPowerManagerWakeLock.release();
- }
- mHasQuit = true;
- }
- }
-
// Persistently track the need to do a full init.
private static final String INIT_SENTINEL_FILE_NAME = "_need_init_";
@@ -436,11 +351,7 @@ public class UserBackupManagerService {
currentTransport = null;
}
- if (DEBUG) {
- Slog.v(
- TAG,
- addUserIdToLogMessage(userId, "Starting with transport " + currentTransport));
- }
+ Slog.d(TAG, addUserIdToLogMessage(userId, "Starting with transport " + currentTransport));
TransportManager transportManager =
new TransportManager(userId, context, transportWhitelist, currentTransport);
@@ -450,11 +361,7 @@ public class UserBackupManagerService {
HandlerThread userBackupThread =
new HandlerThread("backup-" + userId, Process.THREAD_PRIORITY_BACKGROUND);
userBackupThread.start();
- if (DEBUG) {
- Slog.d(
- TAG,
- addUserIdToLogMessage(userId, "Started thread " + userBackupThread.getName()));
- }
+ Slog.d(TAG, addUserIdToLogMessage(userId, "Started thread " + userBackupThread.getName()));
return createAndInitializeService(
userId,
@@ -491,7 +398,7 @@ public class UserBackupManagerService {
// if so delete expired events and do not print them to dumpsys
BackupManagerMonitorDumpsysUtils backupManagerMonitorDumpsysUtils =
new BackupManagerMonitorDumpsysUtils();
- if (backupManagerMonitorDumpsysUtils.deleteExpiredBMMEvents() && DEBUG){
+ if (backupManagerMonitorDumpsysUtils.deleteExpiredBMMEvents()) {
Slog.d(TAG, "BMM Events recorded for dumpsys have expired");
}
return new UserBackupManagerService(
@@ -766,20 +673,10 @@ public class UserBackupManagerService {
mSetupComplete = setupComplete;
}
- public BackupWakeLock getWakelock() {
+ public BackupWakeLock getWakeLock() {
return mWakelock;
}
- /**
- * Sets the {@link WorkSource} of the {@link PowerManager.WakeLock} returned by {@link
- * #getWakelock()}.
- */
- @VisibleForTesting
- public void setWorkSource(@Nullable WorkSource workSource) {
- // TODO: This is for testing, unfortunately WakeLock is final and WorkSource is not exposed
- mWakelock.mPowerManagerWakeLock.setWorkSource(workSource);
- }
-
public Handler getBackupHandler() {
return mBackupHandler;
}
@@ -937,7 +834,7 @@ public class UserBackupManagerService {
}
private void initPackageTracking() {
- if (MORE_DEBUG) Slog.v(TAG, addUserIdToLogMessage(mUserId, "` tracking"));
+ if (DEBUG) Slog.v(TAG, addUserIdToLogMessage(mUserId, "` tracking"));
// Remember our ancestral dataset
mTokenFile = new File(mBaseStateDir, "ancestral");
@@ -959,7 +856,7 @@ public class UserBackupManagerService {
}
} catch (FileNotFoundException fnf) {
// Probably innocuous
- Slog.v(TAG, addUserIdToLogMessage(mUserId, "No ancestral data"));
+ Slog.d(TAG, addUserIdToLogMessage(mUserId, "No ancestral data"));
} catch (IOException e) {
Slog.w(TAG, addUserIdToLogMessage(mUserId, "Unable to read token file"), e);
}
@@ -1038,16 +935,12 @@ public class UserBackupManagerService {
pkg.applicationInfo)) {
schedule.add(new FullBackupEntry(pkgName, lastBackup));
} else {
- if (DEBUG) {
- Slog.i(TAG, addUserIdToLogMessage(mUserId, "Package " + pkgName
+ Slog.i(TAG, addUserIdToLogMessage(mUserId, "Package " + pkgName
+ " no longer eligible for full backup"));
- }
}
} catch (NameNotFoundException e) {
- if (DEBUG) {
- Slog.i(TAG, addUserIdToLogMessage(mUserId, "Package " + pkgName
+ Slog.i(TAG, addUserIdToLogMessage(mUserId, "Package " + pkgName
+ " not installed; dropping from full backup"));
- }
}
}
@@ -1058,7 +951,7 @@ public class UserBackupManagerService {
&& mScheduledBackupEligibility.appIsEligibleForBackup(
app.applicationInfo)) {
if (!foundApps.contains(app.packageName)) {
- if (MORE_DEBUG) {
+ if (DEBUG) {
Slog.i(
TAG,
addUserIdToLogMessage(
@@ -1169,7 +1062,7 @@ public class UserBackupManagerService {
if (!packageNames.isEmpty()) {
String msg = "Stale backup journals: Scheduled " + packageNames.size()
+ " package(s) total";
- if (MORE_DEBUG) {
+ if (DEBUG) {
msg += ": " + packageNames;
}
Slog.i(TAG, addUserIdToLogMessage(mUserId, msg));
@@ -1209,7 +1102,7 @@ public class UserBackupManagerService {
public void recordInitPending(
boolean isPending, String transportName, String transportDirName) {
synchronized (mQueueLock) {
- if (MORE_DEBUG) {
+ if (DEBUG) {
Slog.i(
TAG,
addUserIdToLogMessage(
@@ -1276,20 +1169,11 @@ public class UserBackupManagerService {
}
private void onTransportRegistered(String transportName, String transportDirName) {
- if (DEBUG) {
- long timeMs = SystemClock.elapsedRealtime() - mRegisterTransportsRequestedTime;
- Slog.d(
- TAG,
- addUserIdToLogMessage(
- mUserId,
- "Transport "
- + transportName
- + " registered "
- + timeMs
- + "ms after first request (delay = "
- + INITIALIZATION_DELAY_MILLIS
- + "ms)"));
- }
+ long timeMs = SystemClock.elapsedRealtime() - mRegisterTransportsRequestedTime;
+ Slog.d(TAG, addUserIdToLogMessage(mUserId,
+ "Transport " + transportName + " registered " + timeMs
+ + "ms after first request (delay = " + INITIALIZATION_DELAY_MILLIS
+ + "ms)"));
File stateDir = new File(mBaseStateDir, transportDirName);
stateDir.mkdirs();
@@ -1313,7 +1197,7 @@ public class UserBackupManagerService {
*/
private BroadcastReceiver mPackageTrackingReceiver = new BroadcastReceiver() {
public void onReceive(Context context, Intent intent) {
- if (MORE_DEBUG) {
+ if (DEBUG) {
Slog.d(TAG, addUserIdToLogMessage(mUserId, "Received broadcast " + intent));
}
@@ -1344,7 +1228,7 @@ public class UserBackupManagerService {
intent.getStringArrayExtra(
Intent.EXTRA_CHANGED_COMPONENT_NAME_LIST);
- if (MORE_DEBUG) {
+ if (DEBUG) {
Slog.i(
TAG,
addUserIdToLogMessage(
@@ -1416,13 +1300,8 @@ public class UserBackupManagerService {
mBackupHandler.post(
() -> mTransportManager.onPackageAdded(packageName));
} catch (NameNotFoundException e) {
- if (DEBUG) {
- Slog.w(
- TAG,
- addUserIdToLogMessage(
- mUserId,
- "Can't resolve new app " + packageName));
- }
+ Slog.w(TAG, addUserIdToLogMessage(mUserId,
+ "Can't resolve new app " + packageName));
}
}
@@ -1454,7 +1333,7 @@ public class UserBackupManagerService {
// Look for apps that define the android:backupAgent attribute
List<PackageInfo> targetApps = allAgentPackages();
if (packageNames != null) {
- if (MORE_DEBUG) {
+ if (DEBUG) {
Slog.v(
TAG,
addUserIdToLogMessage(
@@ -1464,7 +1343,7 @@ public class UserBackupManagerService {
addPackageParticipantsLockedInner(packageName, targetApps);
}
} else {
- if (MORE_DEBUG) {
+ if (DEBUG) {
Slog.v(TAG, addUserIdToLogMessage(mUserId, "addPackageParticipantsLocked: all"));
}
addPackageParticipantsLockedInner(null, targetApps);
@@ -1473,7 +1352,7 @@ public class UserBackupManagerService {
private void addPackageParticipantsLockedInner(String packageName,
List<PackageInfo> targetPkgs) {
- if (MORE_DEBUG) {
+ if (DEBUG) {
Slog.v(
TAG,
addUserIdToLogMessage(
@@ -1489,10 +1368,10 @@ public class UserBackupManagerService {
mBackupParticipants.put(uid, set);
}
set.add(pkg.packageName);
- if (MORE_DEBUG) Slog.v(TAG, addUserIdToLogMessage(mUserId, "Agent found; added"));
+ if (DEBUG) Slog.v(TAG, addUserIdToLogMessage(mUserId, "Agent found; added"));
// Schedule a backup for it on general principles
- if (MORE_DEBUG) {
+ if (DEBUG) {
Slog.i(
TAG,
addUserIdToLogMessage(
@@ -1512,7 +1391,7 @@ public class UserBackupManagerService {
return;
}
- if (MORE_DEBUG) {
+ if (DEBUG) {
Slog.v(
TAG,
addUserIdToLogMessage(
@@ -1528,7 +1407,7 @@ public class UserBackupManagerService {
if (set != null && set.contains(pkg)) {
removePackageFromSetLocked(set, pkg);
if (set.isEmpty()) {
- if (MORE_DEBUG) {
+ if (DEBUG) {
Slog.v(
TAG,
addUserIdToLogMessage(
@@ -1549,7 +1428,7 @@ public class UserBackupManagerService {
// Note that we deliberately leave it 'known' in the "ever backed up"
// bookkeeping so that its current-dataset data will be retrieved
// if the app is subsequently reinstalled
- if (MORE_DEBUG) {
+ if (DEBUG) {
Slog.v(
TAG,
addUserIdToLogMessage(mUserId, " removing participant " + packageName));
@@ -1627,15 +1506,13 @@ public class UserBackupManagerService {
af.writeInt(-1);
} else {
af.writeInt(mAncestralPackages.size());
- if (DEBUG) {
- Slog.v(
- TAG,
- addUserIdToLogMessage(
- mUserId, "Ancestral packages: " + mAncestralPackages.size()));
- }
+ Slog.d(
+ TAG,
+ addUserIdToLogMessage(
+ mUserId, "Ancestral packages: " + mAncestralPackages.size()));
for (String pkgName : mAncestralPackages) {
af.writeUTF(pkgName);
- if (MORE_DEBUG) Slog.v(TAG, addUserIdToLogMessage(mUserId, " " + pkgName));
+ if (DEBUG) Slog.v(TAG, addUserIdToLogMessage(mUserId, " " + pkgName));
}
}
} catch (IOException e) {
@@ -1686,7 +1563,7 @@ public class UserBackupManagerService {
}
if (!shouldClearData) {
- if (MORE_DEBUG) {
+ if (DEBUG) {
Slog.i(
TAG,
addUserIdToLogMessage(
@@ -1761,7 +1638,7 @@ public class UserBackupManagerService {
long token = mAncestralToken;
synchronized (mQueueLock) {
if (mCurrentToken != 0 && mProcessedPackagesJournal.hasBeenProcessed(packageName)) {
- if (MORE_DEBUG) {
+ if (DEBUG) {
Slog.i(
TAG,
addUserIdToLogMessage(
@@ -1770,7 +1647,7 @@ public class UserBackupManagerService {
token = mCurrentToken;
}
}
- if (MORE_DEBUG) {
+ if (DEBUG) {
Slog.i(TAG, addUserIdToLogMessage(mUserId, "getAvailableRestoreToken() == " + token));
}
return token;
@@ -1885,7 +1762,7 @@ public class UserBackupManagerService {
EventLog.writeEvent(EventLogTags.BACKUP_REQUESTED, packages.length, kvBackupList.size(),
fullBackupList.size());
- if (MORE_DEBUG) {
+ if (DEBUG) {
Slog.i(
TAG,
addUserIdToLogMessage(
@@ -1909,7 +1786,7 @@ public class UserBackupManagerService {
/** Cancel all running backups. */
public void cancelBackups() {
mContext.enforceCallingPermission(android.Manifest.permission.BACKUP, "cancelBackups");
- if (MORE_DEBUG) {
+ if (DEBUG) {
Slog.i(TAG, addUserIdToLogMessage(mUserId, "cancelBackups() called."));
}
final long oldToken = Binder.clearCallingIdentity();
@@ -1946,7 +1823,7 @@ public class UserBackupManagerService {
+ operationType));
return;
}
- if (MORE_DEBUG) {
+ if (DEBUG) {
Slog.v(
TAG,
addUserIdToLogMessage(
@@ -2025,12 +1902,8 @@ public class UserBackupManagerService {
FullBackupJob.schedule(mUserId, mContext, latency,
/* userBackupManagerService */ this);
} else {
- if (DEBUG_SCHEDULING) {
- Slog.i(
- TAG,
- addUserIdToLogMessage(
- mUserId, "Full backup queue empty; not scheduling"));
- }
+ Slog.i(TAG,
+ addUserIdToLogMessage(mUserId, "Full backup queue empty; not scheduling"));
}
}
}
@@ -2098,13 +1971,8 @@ public class UserBackupManagerService {
File stateDir = new File(mBaseStateDir, transportDirName);
File pmState = new File(stateDir, PACKAGE_MANAGER_SENTINEL);
if (pmState.length() <= 0) {
- if (DEBUG) {
- Slog.i(
- TAG,
- addUserIdToLogMessage(
- mUserId,
- "Full backup requested but dataset not yet initialized"));
- }
+ Slog.i(TAG, addUserIdToLogMessage(mUserId,
+ "Full backup requested but dataset not yet initialized"));
return false;
}
} catch (Exception e) {
@@ -2143,7 +2011,7 @@ public class UserBackupManagerService {
// Backups are globally disabled, so don't proceed. We also don't reschedule
// the job driving automatic backups; that job will be scheduled again when
// the user enables backup.
- if (MORE_DEBUG) {
+ if (DEBUG) {
Slog.i(TAG, addUserIdToLogMessage(mUserId, "beginFullBackup but enabled=" + mEnabled
+ " setupComplete=" + mSetupComplete + "; ignoring"));
}
@@ -2155,22 +2023,14 @@ public class UserBackupManagerService {
final PowerSaveState result =
mPowerManager.getPowerSaveState(ServiceType.FULL_BACKUP);
if (result.batterySaverEnabled) {
- if (DEBUG) {
- Slog.i(
- TAG,
- addUserIdToLogMessage(
- mUserId, "Deferring scheduled full backups in battery saver mode"));
- }
+ Slog.i(TAG, addUserIdToLogMessage(mUserId,
+ "Deferring scheduled full backups in battery saver mode"));
FullBackupJob.schedule(mUserId, mContext, keyValueBackupInterval,
/* userBackupManagerService */ this);
return false;
}
- if (DEBUG_SCHEDULING) {
- Slog.i(
- TAG,
- addUserIdToLogMessage(mUserId, "Beginning scheduled full backup operation"));
- }
+ Slog.i(TAG, addUserIdToLogMessage(mUserId, "Beginning scheduled full backup operation"));
// Great; we're able to run full backup jobs now. See if we have any work to do.
synchronized (mQueueLock) {
@@ -2193,12 +2053,8 @@ public class UserBackupManagerService {
// have emptied the queue.
if (mFullBackupQueue.size() == 0) {
// no work to do so just bow out
- if (DEBUG) {
- Slog.i(
- TAG,
- addUserIdToLogMessage(
- mUserId, "Backup queue empty; doing nothing"));
- }
+ Slog.i(TAG,
+ addUserIdToLogMessage(mUserId, "Backup queue empty; doing nothing"));
runBackup = false;
break;
}
@@ -2207,7 +2063,7 @@ public class UserBackupManagerService {
String transportName = mTransportManager.getCurrentTransportName();
if (!fullBackupAllowable(transportName)) {
- if (MORE_DEBUG) {
+ if (DEBUG) {
Slog.i(
TAG,
addUserIdToLogMessage(
@@ -2226,7 +2082,7 @@ public class UserBackupManagerService {
runBackup = (timeSinceRun >= fullBackupInterval);
if (!runBackup) {
// It's too early to back up the next thing in the queue, so bow out
- if (MORE_DEBUG) {
+ if (DEBUG) {
Slog.i(
TAG,
addUserIdToLogMessage(
@@ -2245,7 +2101,7 @@ public class UserBackupManagerService {
// The head app isn't supposed to get full-data backups [any more];
// so we cull it and force a loop around to consider the new head
// app.
- if (MORE_DEBUG) {
+ if (DEBUG) {
Slog.i(
TAG,
addUserIdToLogMessage(
@@ -2269,17 +2125,11 @@ public class UserBackupManagerService {
final long nextEligible = System.currentTimeMillis()
+ BUSY_BACKOFF_MIN_MILLIS
+ mTokenGenerator.nextInt(BUSY_BACKOFF_FUZZ);
- if (DEBUG_SCHEDULING) {
- SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
- Slog.i(
- TAG,
- addUserIdToLogMessage(
- mUserId,
- "Full backup time but "
- + entry.packageName
- + " is busy; deferring to "
- + sdf.format(new Date(nextEligible))));
- }
+ SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
+ Slog.i(TAG, addUserIdToLogMessage(mUserId,
+ "Full backup time but " + entry.packageName
+ + " is busy; deferring to " + sdf.format(
+ new Date(nextEligible))));
// This relocates the app's entry from the head of the queue to
// its order-appropriate position further down, so upon looping
// a new candidate will be considered at the head.
@@ -2319,14 +2169,9 @@ public class UserBackupManagerService {
}
if (!runBackup) {
- if (DEBUG_SCHEDULING) {
- Slog.i(
- TAG,
- addUserIdToLogMessage(
- mUserId,
- "Nothing pending full backup or failed to start the "
- + "operation; rescheduling +" + latency));
- }
+ Slog.i(TAG, addUserIdToLogMessage(mUserId,
+ "Nothing pending full backup or failed to start the "
+ + "operation; rescheduling +" + latency));
final long deferTime = latency; // pin for the closure
FullBackupJob.schedule(mUserId, mContext, deferTime,
/* userBackupManagerService */ this);
@@ -2360,12 +2205,7 @@ public class UserBackupManagerService {
}
}
if (pftbt != null) {
- if (DEBUG_SCHEDULING) {
- Slog.i(
- TAG,
- addUserIdToLogMessage(
- mUserId, "Telling running backup to stop"));
- }
+ Slog.i(TAG, addUserIdToLogMessage(mUserId, "Telling running backup to stop"));
pftbt.handleCancel(true);
}
}
@@ -2376,7 +2216,7 @@ public class UserBackupManagerService {
/** Used by both incremental and full restore to restore widget data. */
public void restoreWidgetData(String packageName, byte[] widgetData) {
// Apply the restored widget state and generate the ID update for the app
- if (MORE_DEBUG) {
+ if (DEBUG) {
Slog.i(TAG, addUserIdToLogMessage(mUserId, "Incorporating restored widget data"));
}
AppWidgetBackupBridge.restoreWidgetState(packageName, widgetData, mUserId);
@@ -2412,7 +2252,7 @@ public class UserBackupManagerService {
// one already there, then overwrite it, but no harm done.
BackupRequest req = new BackupRequest(packageName);
if (mPendingBackups.put(packageName, req) == null) {
- if (MORE_DEBUG) {
+ if (DEBUG) {
Slog.d(
TAG,
addUserIdToLogMessage(
@@ -2495,7 +2335,7 @@ public class UserBackupManagerService {
public void initializeTransports(String[] transportNames, IBackupObserver observer) {
mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP,
"initializeTransport");
- Slog.v(
+ Slog.d(
TAG,
addUserIdToLogMessage(
mUserId, "initializeTransport(): " + Arrays.asList(transportNames)));
@@ -2517,7 +2357,7 @@ public class UserBackupManagerService {
public void setAncestralSerialNumber(long ancestralSerialNumber) {
mContext.enforceCallingPermission(android.Manifest.permission.BACKUP,
"setAncestralSerialNumber");
- Slog.v(
+ Slog.d(
TAG,
addUserIdToLogMessage(
mUserId, "Setting ancestral work profile id to " + ancestralSerialNumber));
@@ -2571,13 +2411,8 @@ public class UserBackupManagerService {
/** Clear the given package's backup data from the current transport. */
public void clearBackupData(String transportName, String packageName) {
- if (DEBUG) {
- Slog.v(
- TAG,
- addUserIdToLogMessage(
- mUserId,
- "clearBackupData() of " + packageName + " on " + transportName));
- }
+ Slog.d(TAG, addUserIdToLogMessage(mUserId,
+ "clearBackupData() of " + packageName + " on " + transportName));
PackageInfo info;
try {
@@ -2601,7 +2436,7 @@ public class UserBackupManagerService {
} else {
// a caller with full permission can ask to back up any participating app
// !!! TODO: allow data-clear of ANY app?
- if (MORE_DEBUG) {
+ if (DEBUG) {
Slog.v(
TAG,
addUserIdToLogMessage(
@@ -2612,7 +2447,7 @@ public class UserBackupManagerService {
if (apps.contains(packageName)) {
// found it; fire off the clear request
- if (MORE_DEBUG) {
+ if (DEBUG) {
Slog.v(
TAG,
addUserIdToLogMessage(mUserId, "Found the app - running clear process"));
@@ -2657,25 +2492,19 @@ public class UserBackupManagerService {
final PowerSaveState result =
mPowerManager.getPowerSaveState(ServiceType.KEYVALUE_BACKUP);
if (result.batterySaverEnabled) {
- if (DEBUG) {
- Slog.v(
- TAG,
- addUserIdToLogMessage(
- mUserId, "Not running backup while in battery save mode"));
- }
+ Slog.d(TAG, addUserIdToLogMessage(mUserId,
+ "Not running backup while in battery save mode"));
// Try again in several hours.
KeyValueBackupJob.schedule(mUserId, mContext,
/* userBackupManagerService */ this);
} else {
- if (DEBUG) {
- Slog.v(TAG, addUserIdToLogMessage(mUserId, "Scheduling immediate backup pass"));
- }
+ Slog.d(TAG, addUserIdToLogMessage(mUserId, "Scheduling immediate backup pass"));
synchronized (getQueueLock()) {
if (getPendingInits().size() > 0) {
// If there are pending init operations, we process those and then settle
// into the usual periodic backup schedule.
- if (MORE_DEBUG) {
+ if (DEBUG) {
Slog.v(
TAG,
addUserIdToLogMessage(
@@ -2749,26 +2578,11 @@ public class UserBackupManagerService {
return;
}
- if (DEBUG) {
- Slog.v(
- TAG,
- addUserIdToLogMessage(
- mUserId,
- "Requesting backup: apks="
- + includeApks
- + " obb="
- + includeObbs
- + " shared="
- + includeShared
- + " all="
- + doAllApps
- + " system="
- + includeSystem
- + " includekeyvalue="
- + doKeyValue
- + " pkgs="
- + Arrays.toString(pkgList)));
- }
+ Slog.d(TAG, addUserIdToLogMessage(mUserId,
+ "Requesting backup: apks=" + includeApks + " obb=" + includeObbs + " shared="
+ + includeShared + " all=" + doAllApps + " system=" + includeSystem
+ + " includekeyvalue=" + doKeyValue + " pkgs=" + Arrays.toString(
+ pkgList)));
Slog.i(TAG, addUserIdToLogMessage(mUserId, "Beginning adb backup..."));
BackupEligibilityRules eligibilityRules = getEligibilityRulesForOperation(
@@ -2782,11 +2596,7 @@ public class UserBackupManagerService {
}
// start up the confirmation UI
- if (DEBUG) {
- Slog.d(
- TAG,
- addUserIdToLogMessage(mUserId, "Starting backup confirmation UI"));
- }
+ Slog.d(TAG, addUserIdToLogMessage(mUserId, "Starting backup confirmation UI"));
if (!startConfirmationUi(token, FullBackup.FULL_BACKUP_INTENT_ACTION)) {
Slog.e(
TAG,
@@ -2804,9 +2614,7 @@ public class UserBackupManagerService {
startConfirmationTimeout(token, params);
// wait for the backup to be performed
- if (DEBUG) {
- Slog.d(TAG, addUserIdToLogMessage(mUserId, "Waiting for backup completion..."));
- }
+ Slog.d(TAG, addUserIdToLogMessage(mUserId, "Waiting for backup completion..."));
waitForCompletion(params);
} finally {
try {
@@ -2841,9 +2649,7 @@ public class UserBackupManagerService {
mUserId,
"Full backup not currently possible -- key/value backup not yet run?"));
} else {
- if (DEBUG) {
- Slog.d(TAG, addUserIdToLogMessage(mUserId, "fullTransportBackup()"));
- }
+ Slog.d(TAG, addUserIdToLogMessage(mUserId, "fullTransportBackup()"));
final long oldId = Binder.clearCallingIdentity();
try {
@@ -2886,9 +2692,7 @@ public class UserBackupManagerService {
}
}
- if (DEBUG) {
- Slog.d(TAG, addUserIdToLogMessage(mUserId, "Done with full transport backup."));
- }
+ Slog.d(TAG, addUserIdToLogMessage(mUserId, "Done with full transport backup."));
}
/**
@@ -2922,12 +2726,8 @@ public class UserBackupManagerService {
}
// start up the confirmation UI
- if (DEBUG) {
- Slog.d(
- TAG,
- addUserIdToLogMessage(
- mUserId, "Starting restore confirmation UI, token=" + token));
- }
+ Slog.d(TAG, addUserIdToLogMessage(mUserId,
+ "Starting restore confirmation UI, token=" + token));
if (!startConfirmationUi(token, FullBackup.FULL_RESTORE_INTENT_ACTION)) {
Slog.e(
TAG,
@@ -2945,9 +2745,7 @@ public class UserBackupManagerService {
startConfirmationTimeout(token, params);
// wait for the restore to be performed
- if (DEBUG) {
- Slog.d(TAG, addUserIdToLogMessage(mUserId, "Waiting for restore completion..."));
- }
+ Slog.d(TAG, addUserIdToLogMessage(mUserId, "Waiting for restore completion..."));
waitForCompletion(params);
} finally {
try {
@@ -3022,7 +2820,7 @@ public class UserBackupManagerService {
}
private void startConfirmationTimeout(int token, AdbParams params) {
- if (MORE_DEBUG) {
+ if (DEBUG) {
Slog.d(TAG, addUserIdToLogMessage(mUserId, "Posting conf timeout msg after "
+ TIMEOUT_FULL_CONFIRMATION + " millis"));
}
@@ -3055,13 +2853,8 @@ public class UserBackupManagerService {
*/
public void acknowledgeAdbBackupOrRestore(int token, boolean allow,
String curPassword, String encPpassword, IFullBackupRestoreObserver observer) {
- if (DEBUG) {
- Slog.d(
- TAG,
- addUserIdToLogMessage(
- mUserId,
- "acknowledgeAdbBackupOrRestore : token=" + token + " allow=" + allow));
- }
+ Slog.d(TAG, addUserIdToLogMessage(mUserId,
+ "acknowledgeAdbBackupOrRestore : token=" + token + " allow=" + allow));
// TODO: possibly require not just this signature-only permission, but even
// require that the specific designated confirmation-UI app uid is the caller?
@@ -3088,7 +2881,7 @@ public class UserBackupManagerService {
params.encryptPassword = encPpassword;
- if (MORE_DEBUG) {
+ if (DEBUG) {
Slog.d(
TAG,
addUserIdToLogMessage(
@@ -3198,7 +2991,7 @@ public class UserBackupManagerService {
scheduleNextFullBackupJob(0);
} else if (!enable) {
// No longer enabled, so stop running backups
- if (MORE_DEBUG) {
+ if (DEBUG) {
Slog.i(TAG, addUserIdToLogMessage(mUserId, "Opting out of backup"));
}
@@ -3285,7 +3078,7 @@ public class UserBackupManagerService {
mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP,
"getCurrentTransport");
String currentTransport = mTransportManager.getCurrentTransportName();
- if (MORE_DEBUG) {
+ if (DEBUG) {
Slog.v(
TAG,
addUserIdToLogMessage(
@@ -3429,7 +3222,7 @@ public class UserBackupManagerService {
final long oldId = Binder.clearCallingIdentity();
try {
if (!mTransportManager.isTransportRegistered(transportName)) {
- Slog.v(
+ Slog.d(
TAG,
addUserIdToLogMessage(
mUserId,
@@ -3441,7 +3234,7 @@ public class UserBackupManagerService {
String previousTransportName = mTransportManager.selectTransport(transportName);
updateStateForTransport(transportName);
- Slog.v(
+ Slog.d(
TAG,
addUserIdToLogMessage(
mUserId,
@@ -3467,7 +3260,7 @@ public class UserBackupManagerService {
final long oldId = Binder.clearCallingIdentity();
try {
String transportString = transportComponent.flattenToShortString();
- Slog.v(
+ Slog.d(
TAG,
addUserIdToLogMessage(
mUserId,
@@ -3562,7 +3355,7 @@ public class UserBackupManagerService {
"getConfigurationIntent");
try {
Intent intent = mTransportManager.getTransportConfigurationIntent(transportName);
- if (MORE_DEBUG) {
+ if (DEBUG) {
Slog.d(
TAG,
addUserIdToLogMessage(
@@ -3595,7 +3388,7 @@ public class UserBackupManagerService {
try {
String string = mTransportManager.getTransportCurrentDestinationString(transportName);
- if (MORE_DEBUG) {
+ if (DEBUG) {
Slog.d(
TAG,
addUserIdToLogMessage(
@@ -3619,7 +3412,7 @@ public class UserBackupManagerService {
try {
Intent intent = mTransportManager.getTransportDataManagementIntent(transportName);
- if (MORE_DEBUG) {
+ if (DEBUG) {
Slog.d(
TAG,
addUserIdToLogMessage(
@@ -3646,7 +3439,7 @@ public class UserBackupManagerService {
try {
CharSequence label = mTransportManager.getTransportDataManagementLabel(transportName);
- if (MORE_DEBUG) {
+ if (DEBUG) {
Slog.d(
TAG,
addUserIdToLogMessage(
@@ -3682,20 +3475,11 @@ public class UserBackupManagerService {
boolean skip = false;
long restoreSet = getAvailableRestoreToken(packageName);
- if (DEBUG) {
- Slog.v(
- TAG,
- addUserIdToLogMessage(
- mUserId,
- "restoreAtInstall pkg="
- + packageName
- + " token="
- + Integer.toHexString(token)
- + " restoreSet="
- + Long.toHexString(restoreSet)));
- }
+ Slog.d(TAG, addUserIdToLogMessage(mUserId,
+ "restoreAtInstall pkg=" + packageName + " token=" + Integer.toHexString(token)
+ + " restoreSet=" + Long.toHexString(restoreSet)));
if (restoreSet == 0) {
- if (MORE_DEBUG) Slog.i(TAG, addUserIdToLogMessage(mUserId, "No restore set"));
+ if (DEBUG) Slog.i(TAG, addUserIdToLogMessage(mUserId, "No restore set"));
skip = true;
}
@@ -3705,7 +3489,7 @@ public class UserBackupManagerService {
TransportConnection transportConnection =
mTransportManager.getCurrentTransportClient("BMS.restoreAtInstall()");
if (transportConnection == null) {
- if (DEBUG) Slog.w(TAG, addUserIdToLogMessage(mUserId, "No transport client"));
+ Slog.w(TAG, addUserIdToLogMessage(mUserId, "No transport client"));
skip = true;
} else if (Flags.enableIncreasedBmmLoggingForRestoreAtInstall()) {
try {
@@ -3729,12 +3513,8 @@ public class UserBackupManagerService {
}
if (!mAutoRestore) {
- if (DEBUG) {
- Slog.w(
- TAG,
- addUserIdToLogMessage(
- mUserId, "Non-restorable state: auto=" + mAutoRestore));
- }
+ Slog.w(TAG,
+ addUserIdToLogMessage(mUserId, "Non-restorable state: auto=" + mAutoRestore));
skip = true;
}
@@ -3751,7 +3531,7 @@ public class UserBackupManagerService {
mWakelock.release();
};
- if (MORE_DEBUG) {
+ if (DEBUG) {
Slog.d(
TAG,
addUserIdToLogMessage(mUserId, "Restore at install of " + packageName));
@@ -3796,7 +3576,7 @@ public class UserBackupManagerService {
}
// Tell the PackageManager to proceed with the post-install handling for this package.
- if (DEBUG) Slog.v(TAG, addUserIdToLogMessage(mUserId, "Finishing install immediately"));
+ Slog.d(TAG, addUserIdToLogMessage(mUserId, "Finishing install immediately"));
try {
mPackageManagerBinder.finishPackageInstall(token, false);
} catch (RemoteException e) { /* can't happen */ }
@@ -3812,13 +3592,8 @@ public class UserBackupManagerService {
/** Hand off a restore session. */
public IRestoreSession beginRestoreSession(String packageName, String transport) {
- if (DEBUG) {
- Slog.v(
- TAG,
- addUserIdToLogMessage(
- mUserId,
- "beginRestoreSession: pkg=" + packageName + " transport=" + transport));
- }
+ Slog.d(TAG, addUserIdToLogMessage(mUserId,
+ "beginRestoreSession: pkg=" + packageName + " transport=" + transport));
boolean needPermission = true;
if (transport == null) {
@@ -3849,13 +3624,8 @@ public class UserBackupManagerService {
mContext.enforceCallingOrSelfPermission(
android.Manifest.permission.BACKUP, "beginRestoreSession");
} else {
- if (DEBUG) {
- Slog.d(
- TAG,
- addUserIdToLogMessage(
- mUserId,
- "restoring self on current transport; no permission needed"));
- }
+ Slog.d(TAG, addUserIdToLogMessage(mUserId,
+ "restoring self on current transport; no permission needed"));
}
int backupDestination;
@@ -3905,12 +3675,8 @@ public class UserBackupManagerService {
if (currentSession != mActiveRestoreSession) {
Slog.e(TAG, addUserIdToLogMessage(mUserId, "ending non-current restore session"));
} else {
- if (DEBUG) {
- Slog.v(
- TAG,
- addUserIdToLogMessage(
- mUserId, "Clearing restore session and halting timeout"));
- }
+ Slog.d(TAG, addUserIdToLogMessage(mUserId,
+ "Clearing restore session and halting timeout"));
mActiveRestoreSession = null;
mBackupHandler.removeMessages(MSG_RESTORE_SESSION_TIMEOUT);
}
diff --git a/services/backup/java/com/android/server/backup/fullbackup/AppMetadataBackupWriter.java b/services/backup/java/com/android/server/backup/fullbackup/AppMetadataBackupWriter.java
index d13f71116c81..1cd8d8146d92 100644
--- a/services/backup/java/com/android/server/backup/fullbackup/AppMetadataBackupWriter.java
+++ b/services/backup/java/com/android/server/backup/fullbackup/AppMetadataBackupWriter.java
@@ -1,6 +1,6 @@
package com.android.server.backup.fullbackup;
-import static com.android.server.backup.BackupManagerService.MORE_DEBUG;
+import static com.android.server.backup.BackupManagerService.DEBUG;
import static com.android.server.backup.BackupManagerService.TAG;
import static com.android.server.backup.UserBackupManagerService.BACKUP_MANIFEST_VERSION;
import static com.android.server.backup.UserBackupManagerService.BACKUP_METADATA_VERSION;
@@ -261,7 +261,7 @@ public class AppMetadataBackupWriter {
new Environment.UserEnvironment(userId);
File obbDir = userEnv.buildExternalStorageAppObbDirs(packageInfo.packageName)[0];
if (obbDir != null) {
- if (MORE_DEBUG) {
+ if (DEBUG) {
Log.i(TAG, "obb dir: " + obbDir.getAbsolutePath());
}
File[] obbFiles = obbDir.listFiles();
diff --git a/services/backup/java/com/android/server/backup/fullbackup/FullBackupEngine.java b/services/backup/java/com/android/server/backup/fullbackup/FullBackupEngine.java
index cf617a523bec..ebb1194c7c4a 100644
--- a/services/backup/java/com/android/server/backup/fullbackup/FullBackupEngine.java
+++ b/services/backup/java/com/android/server/backup/fullbackup/FullBackupEngine.java
@@ -17,7 +17,6 @@
package com.android.server.backup.fullbackup;
import static com.android.server.backup.BackupManagerService.DEBUG;
-import static com.android.server.backup.BackupManagerService.MORE_DEBUG;
import static com.android.server.backup.BackupManagerService.TAG;
import static com.android.server.backup.UserBackupManagerService.BACKUP_MANIFEST_FILENAME;
import static com.android.server.backup.UserBackupManagerService.BACKUP_METADATA_FILENAME;
@@ -111,7 +110,7 @@ public class FullBackupEngine {
shouldWriteApk(mPackage.applicationInfo, mIncludeApks, isSharedStorage);
if (!isSharedStorage) {
- if (MORE_DEBUG) {
+ if (DEBUG) {
Slog.d(TAG, "Writing manifest for " + packageName);
}
@@ -137,9 +136,7 @@ public class FullBackupEngine {
appMetadataBackupWriter.backupObb(mUserId, mPackage);
}
- if (DEBUG) {
- Slog.d(TAG, "Calling doFullBackup() on " + packageName);
- }
+ Slog.d(TAG, "Calling doFullBackup() on " + packageName);
long timeout =
isSharedStorage
@@ -216,14 +213,14 @@ public class FullBackupEngine {
public int preflightCheck() throws RemoteException {
if (mPreflightHook == null) {
- if (MORE_DEBUG) {
+ if (DEBUG) {
Slog.v(TAG, "No preflight check");
}
return BackupTransport.TRANSPORT_OK;
}
if (initializeAgent()) {
int result = mPreflightHook.preflightFullBackup(mPkg, mAgent);
- if (MORE_DEBUG) {
+ if (DEBUG) {
Slog.v(TAG, "preflight returned " + result);
}
@@ -262,7 +259,7 @@ public class FullBackupEngine {
if (!backupManagerService.waitUntilOperationComplete(mOpToken)) {
Slog.e(TAG, "Full backup failed on package " + mPkg.packageName);
} else {
- if (MORE_DEBUG) {
+ if (DEBUG) {
Slog.d(TAG, "Full package backup success: " + mPkg.packageName);
}
result = BackupTransport.TRANSPORT_OK;
@@ -310,7 +307,7 @@ public class FullBackupEngine {
private boolean initializeAgent() {
if (mAgent == null) {
- if (MORE_DEBUG) {
+ if (DEBUG) {
Slog.d(TAG, "Binding to full backup agent : " + mPkg.packageName);
}
mAgent =
diff --git a/services/backup/java/com/android/server/backup/fullbackup/FullBackupObbConnection.java b/services/backup/java/com/android/server/backup/fullbackup/FullBackupObbConnection.java
index be6ac26ba92a..93499b9d763b 100644
--- a/services/backup/java/com/android/server/backup/fullbackup/FullBackupObbConnection.java
+++ b/services/backup/java/com/android/server/backup/fullbackup/FullBackupObbConnection.java
@@ -16,7 +16,7 @@
package com.android.server.backup.fullbackup;
-import static com.android.server.backup.BackupManagerService.MORE_DEBUG;
+import static com.android.server.backup.BackupManagerService.DEBUG;
import static com.android.server.backup.BackupManagerService.TAG;
import android.app.backup.IBackupManager;
@@ -58,7 +58,7 @@ public class FullBackupObbConnection implements ServiceConnection {
}
public void establish() {
- if (MORE_DEBUG) {
+ if (DEBUG) {
Slog.i(TAG, "Initiating bind of OBB service on " + this);
}
Intent obbIntent = new Intent().setComponent(new ComponentName(
@@ -124,14 +124,14 @@ public class FullBackupObbConnection implements ServiceConnection {
private void waitForConnection() {
synchronized (this) {
while (mService == null) {
- if (MORE_DEBUG) {
+ if (DEBUG) {
Slog.i(TAG, "...waiting for OBB service binding...");
}
try {
this.wait();
} catch (InterruptedException e) { /* never interrupted */ }
}
- if (MORE_DEBUG) {
+ if (DEBUG) {
Slog.i(TAG, "Connected to OBB service; continuing");
}
}
@@ -141,7 +141,7 @@ public class FullBackupObbConnection implements ServiceConnection {
public void onServiceConnected(ComponentName name, IBinder service) {
synchronized (this) {
mService = IObbBackupService.Stub.asInterface(service);
- if (MORE_DEBUG) {
+ if (DEBUG) {
Slog.i(TAG, "OBB service connection " + mService + " connected on " + this);
}
this.notifyAll();
@@ -152,7 +152,7 @@ public class FullBackupObbConnection implements ServiceConnection {
public void onServiceDisconnected(ComponentName name) {
synchronized (this) {
mService = null;
- if (MORE_DEBUG) {
+ if (DEBUG) {
Slog.i(TAG, "OBB service connection disconnected on " + this);
}
this.notifyAll();
diff --git a/services/backup/java/com/android/server/backup/fullbackup/PerformAdbBackupTask.java b/services/backup/java/com/android/server/backup/fullbackup/PerformAdbBackupTask.java
index 0ba0d710af38..0d4364e14e03 100644
--- a/services/backup/java/com/android/server/backup/fullbackup/PerformAdbBackupTask.java
+++ b/services/backup/java/com/android/server/backup/fullbackup/PerformAdbBackupTask.java
@@ -17,7 +17,6 @@
package com.android.server.backup.fullbackup;
import static com.android.server.backup.BackupManagerService.DEBUG;
-import static com.android.server.backup.BackupManagerService.MORE_DEBUG;
import static com.android.server.backup.BackupManagerService.TAG;
import static com.android.server.backup.BackupPasswordManager.PBKDF_CURRENT;
import static com.android.server.backup.UserBackupManagerService.BACKUP_FILE_HEADER_MAGIC;
@@ -121,7 +120,7 @@ public class PerformAdbBackupTask extends FullBackupTask implements BackupRestor
} else {
mEncryptPassword = encryptPassword;
}
- if (MORE_DEBUG) {
+ if (DEBUG) {
Slog.w(TAG, "Encrypting backup with passphrase=" + mEncryptPassword);
}
mCompress = doCompress;
@@ -265,7 +264,7 @@ public class PerformAdbBackupTask extends FullBackupTask implements BackupRestor
List<String> pkgs =
AppWidgetBackupBridge.getWidgetParticipants(UserHandle.USER_SYSTEM);
if (pkgs != null) {
- if (MORE_DEBUG) {
+ if (DEBUG) {
Slog.i(TAG, "Adding widget participants to backup set:");
StringBuilder sb = new StringBuilder(128);
sb.append(" ");
@@ -297,16 +296,12 @@ public class PerformAdbBackupTask extends FullBackupTask implements BackupRestor
if (!mBackupEligibilityRules.appIsEligibleForBackup(pkg.applicationInfo)
|| mBackupEligibilityRules.appIsStopped(pkg.applicationInfo)) {
iter.remove();
- if (DEBUG) {
- Slog.i(TAG, "Package " + pkg.packageName
+ Slog.i(TAG, "Package " + pkg.packageName
+ " is not eligible for backup, removing.");
- }
} else if (mBackupEligibilityRules.appIsKeyValueOnly(pkg)) {
iter.remove();
- if (DEBUG) {
- Slog.i(TAG, "Package " + pkg.packageName
+ Slog.i(TAG, "Package " + pkg.packageName
+ " is key-value.");
- }
keyValueBackupQueue.add(pkg);
}
}
@@ -326,9 +321,7 @@ public class PerformAdbBackupTask extends FullBackupTask implements BackupRestor
// Verify that the given password matches the currently-active
// backup password, if any
if (!mUserBackupManagerService.backupPasswordMatches(mCurrentPassword)) {
- if (DEBUG) {
- Slog.w(TAG, "Backup password mismatch; aborting");
- }
+ Slog.w(TAG, "Backup password mismatch; aborting");
return;
}
@@ -402,10 +395,8 @@ public class PerformAdbBackupTask extends FullBackupTask implements BackupRestor
int N = backupQueue.size();
for (int i = 0; i < N; i++) {
pkg = backupQueue.get(i);
- if (DEBUG) {
- Slog.i(TAG, "--- Performing full backup for package " + pkg.packageName
+ Slog.i(TAG, "--- Performing full backup for package " + pkg.packageName
+ " ---");
- }
final boolean isSharedStorage =
pkg.packageName.equals(
SHARED_BACKUP_AGENT_PACKAGE);
@@ -441,10 +432,8 @@ public class PerformAdbBackupTask extends FullBackupTask implements BackupRestor
// And for key-value backup if enabled
if (mKeyValue) {
for (PackageInfo keyValuePackage : keyValueBackupQueue) {
- if (DEBUG) {
- Slog.i(TAG, "--- Performing key-value backup for package "
+ Slog.i(TAG, "--- Performing key-value backup for package "
+ keyValuePackage.packageName + " ---");
- }
KeyValueAdbBackupEngine kvBackupEngine =
new KeyValueAdbBackupEngine(out, keyValuePackage,
mUserBackupManagerService,
@@ -478,10 +467,8 @@ public class PerformAdbBackupTask extends FullBackupTask implements BackupRestor
}
sendEndBackup();
obbConnection.tearDown();
- if (DEBUG) {
- Slog.d(TAG, "Full backup pass complete.");
- }
- mUserBackupManagerService.getWakelock().release();
+ Slog.d(TAG, "Full backup pass complete.");
+ mUserBackupManagerService.getWakeLock().release();
}
}
@@ -499,9 +486,7 @@ public class PerformAdbBackupTask extends FullBackupTask implements BackupRestor
@Override
public void handleCancel(boolean cancelAll) {
final PackageInfo target = mCurrentTarget;
- if (DEBUG) {
- Slog.w(TAG, "adb backup cancel of " + target);
- }
+ Slog.w(TAG, "adb backup cancel of " + target);
if (target != null) {
mUserBackupManagerService.getBackupAgentConnectionManager().unbindAgent(
target.applicationInfo, /* allowKill= */ true);
diff --git a/services/backup/java/com/android/server/backup/fullbackup/PerformFullTransportBackupTask.java b/services/backup/java/com/android/server/backup/fullbackup/PerformFullTransportBackupTask.java
index 990c9416e38d..bd34f33226a1 100644
--- a/services/backup/java/com/android/server/backup/fullbackup/PerformFullTransportBackupTask.java
+++ b/services/backup/java/com/android/server/backup/fullbackup/PerformFullTransportBackupTask.java
@@ -19,8 +19,6 @@ package com.android.server.backup.fullbackup;
import static android.app.backup.BackupAnnotations.OperationType.BACKUP;
import static com.android.server.backup.BackupManagerService.DEBUG;
-import static com.android.server.backup.BackupManagerService.DEBUG_SCHEDULING;
-import static com.android.server.backup.BackupManagerService.MORE_DEBUG;
import android.annotation.Nullable;
import android.app.IBackupAgent;
@@ -201,9 +199,7 @@ public class PerformFullTransportBackupTask extends FullBackupTask implements Ba
mBackupEligibilityRules = backupEligibilityRules;
if (backupManagerService.isBackupOperationInProgress()) {
- if (DEBUG) {
- Slog.d(TAG, "Skipping full backup. A backup is already in progress.");
- }
+ Slog.d(TAG, "Skipping full backup. A backup is already in progress.");
mCancelAll = true;
return;
}
@@ -219,7 +215,7 @@ public class PerformFullTransportBackupTask extends FullBackupTask implements Ba
// that run as system-domain uids but do not define their own backup agents,
// as well as any explicit mention of the 'special' shared-storage agent
// package (we handle that one at the end).
- if (MORE_DEBUG) {
+ if (DEBUG) {
Slog.d(TAG, "Ignoring ineligible package " + pkg);
}
mBackupManagerMonitorEventSender.monitorEvent(
@@ -233,7 +229,7 @@ public class PerformFullTransportBackupTask extends FullBackupTask implements Ba
} else if (!mBackupEligibilityRules.appGetsFullBackup(info)) {
// Cull any packages that are found in the queue but now aren't supposed
// to get full-data backup operations.
- if (MORE_DEBUG) {
+ if (DEBUG) {
Slog.d(TAG, "Ignoring full-data backup of key/value participant "
+ pkg);
}
@@ -249,7 +245,7 @@ public class PerformFullTransportBackupTask extends FullBackupTask implements Ba
// Cull any packages in the 'stopped' state: they've either just been
// installed or have explicitly been force-stopped by the user. In both
// cases we do not want to launch them for backup.
- if (MORE_DEBUG) {
+ if (DEBUG) {
Slog.d(TAG, "Ignoring stopped package " + pkg);
}
mBackupManagerMonitorEventSender.monitorEvent(
@@ -346,12 +342,10 @@ public class PerformFullTransportBackupTask extends FullBackupTask implements Ba
if (!mUserBackupManagerService.isEnabled()
|| !mUserBackupManagerService.isSetupComplete()) {
// Backups are globally disabled, so don't proceed.
- if (DEBUG) {
- Slog.i(TAG, "full backup requested but enabled=" + mUserBackupManagerService
- .isEnabled()
- + " setupComplete=" + mUserBackupManagerService.isSetupComplete()
- + "; ignoring");
- }
+ Slog.i(TAG,
+ "full backup requested but enabled=" + mUserBackupManagerService.isEnabled()
+ + " setupComplete=" + mUserBackupManagerService.isSetupComplete()
+ + "; ignoring");
int monitoringEvent;
if (mUserBackupManagerService.isSetupComplete()) {
monitoringEvent = BackupManagerMonitor.LOG_EVENT_ID_BACKUP_DISABLED;
@@ -405,10 +399,8 @@ public class PerformFullTransportBackupTask extends FullBackupTask implements Ba
mBackupRunner = null;
PackageInfo currentPackage = mPackages.get(i);
String packageName = currentPackage.packageName;
- if (DEBUG) {
- Slog.i(TAG, "Initiating full-data transport backup of " + packageName
- + " token: " + mCurrentOpToken);
- }
+ Slog.i(TAG, "Initiating full-data transport backup of " + packageName + " token: "
+ + mCurrentOpToken);
EventLog.writeEvent(EventLogTags.FULL_BACKUP_PACKAGE, packageName);
transportPipes = ParcelFileDescriptor.createPipe();
@@ -462,7 +454,7 @@ public class PerformFullTransportBackupTask extends FullBackupTask implements Ba
final long preflightResult = mBackupRunner.getPreflightResultBlocking();
// Preflight result is negative if some error happened on preflight.
if (preflightResult < 0) {
- if (MORE_DEBUG) {
+ if (DEBUG) {
Slog.d(TAG, "Backup error after preflight of package "
+ packageName + ": " + preflightResult
+ ", not running backup.");
@@ -479,7 +471,7 @@ public class PerformFullTransportBackupTask extends FullBackupTask implements Ba
int nRead = 0;
do {
nRead = in.read(buffer);
- if (MORE_DEBUG) {
+ if (DEBUG) {
Slog.v(TAG, "in.read(buffer) from app: " + nRead);
}
if (nRead > 0) {
@@ -549,12 +541,12 @@ public class PerformFullTransportBackupTask extends FullBackupTask implements Ba
backupPackageStatus = backupRunnerResult;
}
} else {
- if (MORE_DEBUG) {
+ if (DEBUG) {
Slog.i(TAG, "Transport-level failure; cancelling agent work");
}
}
- if (MORE_DEBUG) {
+ if (DEBUG) {
Slog.i(TAG, "Done delivering backup data: result="
+ backupPackageStatus);
}
@@ -567,10 +559,7 @@ public class PerformFullTransportBackupTask extends FullBackupTask implements Ba
// Also ask the transport how long it wants us to wait before
// moving on to the next package, if any.
backoff = transport.requestFullBackupTime();
- if (DEBUG_SCHEDULING) {
- Slog.i(TAG, "Transport suggested backoff=" + backoff);
- }
-
+ Slog.i(TAG, "Transport suggested backoff=" + backoff);
}
// Roll this package to the end of the backup queue if we're
@@ -581,13 +570,9 @@ public class PerformFullTransportBackupTask extends FullBackupTask implements Ba
}
if (backupPackageStatus == BackupTransport.TRANSPORT_PACKAGE_REJECTED) {
- BackupObserverUtils
- .sendBackupOnPackageResult(mBackupObserver, packageName,
- BackupManager.ERROR_TRANSPORT_PACKAGE_REJECTED);
- if (DEBUG) {
- Slog.i(TAG, "Transport rejected backup of " + packageName
- + ", skipping");
- }
+ BackupObserverUtils.sendBackupOnPackageResult(mBackupObserver, packageName,
+ BackupManager.ERROR_TRANSPORT_PACKAGE_REJECTED);
+ Slog.i(TAG, "Transport rejected backup of " + packageName + ", skipping");
EventLog.writeEvent(EventLogTags.FULL_BACKUP_AGENT_FAILURE, packageName,
"transport rejected");
// This failure state can come either a-priori from the transport, or
@@ -602,11 +587,8 @@ public class PerformFullTransportBackupTask extends FullBackupTask implements Ba
BackupObserverUtils
.sendBackupOnPackageResult(mBackupObserver, packageName,
BackupManager.ERROR_TRANSPORT_QUOTA_EXCEEDED);
- if (DEBUG) {
- Slog.i(TAG, "Transport quota exceeded for package: " + packageName);
- EventLog.writeEvent(EventLogTags.FULL_BACKUP_QUOTA_EXCEEDED,
- packageName);
- }
+ Slog.i(TAG, "Transport quota exceeded for package: " + packageName);
+ EventLog.writeEvent(EventLogTags.FULL_BACKUP_QUOTA_EXCEEDED, packageName);
mUserBackupManagerService.getBackupAgentConnectionManager().unbindAgent(
currentPackage.applicationInfo, /* allowKill= */ true);
// Do nothing, clean up, and continue looping.
@@ -675,9 +657,7 @@ public class PerformFullTransportBackupTask extends FullBackupTask implements Ba
backupRunStatus = BackupManager.ERROR_BACKUP_CANCELLED;
}
- if (DEBUG) {
- Slog.i(TAG, "Full backup completed with status: " + backupRunStatus);
- }
+ Slog.i(TAG, "Full backup completed with status: " + backupRunStatus);
BackupObserverUtils.sendBackupFinished(mBackupObserver, backupRunStatus);
cleanUpPipes(transportPipes);
@@ -708,7 +688,7 @@ public class PerformFullTransportBackupTask extends FullBackupTask implements Ba
.getBackupAgentConnectionManager().clearNoRestrictedModePackages();
Slog.i(TAG, "Full data backup pass finished.");
- mUserBackupManagerService.getWakelock().release();
+ mUserBackupManagerService.getWakeLock().release();
}
}
@@ -781,7 +761,7 @@ public class PerformFullTransportBackupTask extends FullBackupTask implements Ba
try {
mUserBackupManagerService.prepareOperationTimeout(
mCurrentOpToken, fullBackupAgentTimeoutMillis, this, OpType.BACKUP_WAIT);
- if (MORE_DEBUG) {
+ if (DEBUG) {
Slog.d(TAG, "Preflighting full payload of " + pkg.packageName);
}
agent.doMeasureFullBackup(mQuota, mCurrentOpToken,
@@ -799,7 +779,7 @@ public class PerformFullTransportBackupTask extends FullBackupTask implements Ba
if (totalSize < 0) {
return (int) totalSize;
}
- if (MORE_DEBUG) {
+ if (DEBUG) {
Slog.v(TAG, "Got preflight response; size=" + totalSize);
}
@@ -807,7 +787,7 @@ public class PerformFullTransportBackupTask extends FullBackupTask implements Ba
mTransportConnection.connectOrThrow("PFTBT$SPBP.preflightFullBackup()");
result = transport.checkFullBackupSize(totalSize);
if (result == BackupTransport.TRANSPORT_QUOTA_EXCEEDED) {
- if (MORE_DEBUG) {
+ if (DEBUG) {
Slog.d(TAG, "Package hit quota limit on preflight " +
pkg.packageName + ": " + totalSize + " of " + mQuota);
}
@@ -830,7 +810,7 @@ public class PerformFullTransportBackupTask extends FullBackupTask implements Ba
@Override
public void operationComplete(long result) {
// got the callback, and our preflightFullBackup() method is waiting for the result
- if (MORE_DEBUG) {
+ if (DEBUG) {
Slog.i(TAG, "Preflight op complete, result=" + result);
}
mResult.set(result);
@@ -840,7 +820,7 @@ public class PerformFullTransportBackupTask extends FullBackupTask implements Ba
@Override
public void handleCancel(boolean cancelAll) {
- if (MORE_DEBUG) {
+ if (DEBUG) {
Slog.i(TAG, "Preflight cancelled; failing");
}
mResult.set(BackupTransport.AGENT_ERROR);
@@ -997,9 +977,7 @@ public class PerformFullTransportBackupTask extends FullBackupTask implements Ba
@Override
public void handleCancel(boolean cancelAll) {
- if (DEBUG) {
- Slog.w(TAG, "Full backup cancel of " + mTarget.packageName);
- }
+ Slog.w(TAG, "Full backup cancel of " + mTarget.packageName);
mBackupManagerMonitorEventSender.monitorEvent(
BackupManagerMonitor.LOG_EVENT_ID_FULL_BACKUP_CANCEL,
diff --git a/services/backup/java/com/android/server/backup/internal/BackupHandler.java b/services/backup/java/com/android/server/backup/internal/BackupHandler.java
index 38c7dd1e230b..87cf8a313651 100644
--- a/services/backup/java/com/android/server/backup/internal/BackupHandler.java
+++ b/services/backup/java/com/android/server/backup/internal/BackupHandler.java
@@ -17,7 +17,6 @@
package com.android.server.backup.internal;
import static com.android.server.backup.BackupManagerService.DEBUG;
-import static com.android.server.backup.BackupManagerService.MORE_DEBUG;
import static com.android.server.backup.BackupManagerService.TAG;
import android.app.backup.BackupAnnotations.BackupDestination;
@@ -136,8 +135,8 @@ public class BackupHandler extends Handler {
public void handleMessage(Message msg) {
if (msg.what == MSG_STOP) {
- Slog.v(TAG, "Stopping backup handler");
- backupManagerService.getWakelock().quit();
+ Slog.d(TAG, "Stopping backup handler");
+ backupManagerService.getWakeLock().quit();
mBackupThread.quitSafely();
}
@@ -163,7 +162,7 @@ public class BackupHandler extends Handler {
transportManager
.disposeOfTransportClient(transportConnection, callerLogString);
}
- Slog.v(TAG, "Backup requested but no transport available");
+ Slog.d(TAG, "Backup requested but no transport available");
break;
}
@@ -177,14 +176,12 @@ public class BackupHandler extends Handler {
return;
}
- if (DEBUG) {
- Slog.v(TAG, "Running a backup pass");
- }
+ Slog.d(TAG, "Running a backup pass");
// Acquire the wakelock and pass it to the backup thread. It will be released
// once backup concludes.
backupManagerService.setBackupRunning(true);
- backupManagerService.getWakelock().acquire();
+ backupManagerService.getWakeLock().acquire();
// Do we have any work to do? Construct the work queue
// then release the synchronization lock to actually run
@@ -193,9 +190,7 @@ public class BackupHandler extends Handler {
for (BackupRequest b : backupManagerService.getPendingBackups().values()) {
queue.add(b.packageName);
}
- if (DEBUG) {
- Slog.v(TAG, "clearing pending backups");
- }
+ Slog.d(TAG, "clearing pending backups");
backupManagerService.getPendingBackups().clear();
// Start a new backup-queue journal file too
@@ -249,7 +244,7 @@ public class BackupHandler extends Handler {
staged = false;
}
} else {
- Slog.v(TAG, "Backup requested but nothing pending");
+ Slog.d(TAG, "Backup requested but nothing pending");
staged = false;
}
@@ -259,7 +254,7 @@ public class BackupHandler extends Handler {
synchronized (backupManagerService.getQueueLock()) {
backupManagerService.setBackupRunning(false);
}
- backupManagerService.getWakelock().release();
+ backupManagerService.getWakeLock().release();
}
break;
}
@@ -267,7 +262,7 @@ public class BackupHandler extends Handler {
case MSG_BACKUP_RESTORE_STEP: {
try {
BackupRestoreTask task = (BackupRestoreTask) msg.obj;
- if (MORE_DEBUG) {
+ if (DEBUG) {
Slog.v(TAG, "Got next step for " + task + ", executing");
}
task.execute();
@@ -324,16 +319,12 @@ public class BackupHandler extends Handler {
synchronized (backupManagerService.getPendingRestores()) {
if (backupManagerService.isRestoreInProgress()) {
- if (DEBUG) {
- Slog.d(TAG, "Restore in progress, queueing.");
- }
+ Slog.d(TAG, "Restore in progress, queueing.");
backupManagerService.getPendingRestores().add(task);
// This task will be picked up and executed when the the currently running
// restore task finishes.
} else {
- if (DEBUG) {
- Slog.d(TAG, "Starting restore.");
- }
+ Slog.d(TAG, "Starting restore.");
backupManagerService.setRestoreInProgress(true);
Message restoreMsg = obtainMessage(MSG_BACKUP_RESTORE_STEP, task);
sendMessage(restoreMsg);
@@ -471,11 +462,11 @@ public class BackupHandler extends Handler {
case MSG_REQUEST_BACKUP: {
BackupParams params = (BackupParams) msg.obj;
- if (MORE_DEBUG) {
+ if (DEBUG) {
Slog.d(TAG, "MSG_REQUEST_BACKUP observer=" + params.observer);
}
backupManagerService.setBackupRunning(true);
- backupManagerService.getWakelock().acquire();
+ backupManagerService.getWakeLock().acquire();
KeyValueBackupTask.start(
backupManagerService,
@@ -496,7 +487,7 @@ public class BackupHandler extends Handler {
case MSG_SCHEDULE_BACKUP_PACKAGE: {
String pkgName = (String) msg.obj;
- if (MORE_DEBUG) {
+ if (DEBUG) {
Slog.d(TAG, "MSG_SCHEDULE_BACKUP_PACKAGE " + pkgName);
}
backupManagerService.dataChangedImpl(pkgName);
diff --git a/services/backup/java/com/android/server/backup/internal/LifecycleOperationStorage.java b/services/backup/java/com/android/server/backup/internal/LifecycleOperationStorage.java
index a94167eb9fa7..0b974e2d0a8a 100644
--- a/services/backup/java/com/android/server/backup/internal/LifecycleOperationStorage.java
+++ b/services/backup/java/com/android/server/backup/internal/LifecycleOperationStorage.java
@@ -17,7 +17,6 @@
package com.android.server.backup.internal;
import static com.android.server.backup.BackupManagerService.DEBUG;
-import static com.android.server.backup.BackupManagerService.MORE_DEBUG;
import android.annotation.UserIdInt;
import android.util.Slog;
@@ -206,7 +205,7 @@ public class LifecycleOperationStorage implements OperationStorage {
* @return true if the operation was ACKed prior to or during this call.
*/
public boolean waitUntilOperationComplete(int token, IntConsumer callback) {
- if (MORE_DEBUG) {
+ if (DEBUG) {
Slog.i(TAG, "[UserID:" + mUserId + "] Blocking until operation complete for "
+ Integer.toHexString(token));
}
@@ -227,7 +226,7 @@ public class LifecycleOperationStorage implements OperationStorage {
}
// When the wait is notified we loop around and recheck the current state
} else {
- if (MORE_DEBUG) {
+ if (DEBUG) {
Slog.d(TAG, "[UserID:" + mUserId
+ "] Unblocked waiting for operation token="
+ Integer.toHexString(token));
@@ -244,7 +243,7 @@ public class LifecycleOperationStorage implements OperationStorage {
if (op != null) {
callback.accept(op.type);
}
- if (MORE_DEBUG) {
+ if (DEBUG) {
Slog.v(TAG, "[UserID:" + mUserId + "] operation " + Integer.toHexString(token)
+ " complete: finalState=" + finalState);
}
@@ -263,7 +262,7 @@ public class LifecycleOperationStorage implements OperationStorage {
* operation from PENDING to ACKNOWLEDGED state.
*/
public void onOperationComplete(int token, long result, Consumer<BackupRestoreTask> callback) {
- if (MORE_DEBUG) {
+ if (DEBUG) {
Slog.v(TAG, "[UserID:" + mUserId + "] onOperationComplete: "
+ Integer.toHexString(token) + " result=" + result);
}
@@ -277,10 +276,8 @@ public class LifecycleOperationStorage implements OperationStorage {
op = null;
mOperations.remove(token);
} else if (op.state == OpState.ACKNOWLEDGED) {
- if (DEBUG) {
- Slog.w(TAG, "[UserID:" + mUserId + "] Received duplicate ack for token="
+ Slog.w(TAG, "[UserID:" + mUserId + "] Received duplicate ack for token="
+ Integer.toHexString(token));
- }
op = null;
mOperations.remove(token);
} else if (op.state == OpState.PENDING) {
@@ -317,7 +314,7 @@ public class LifecycleOperationStorage implements OperationStorage {
Operation op = null;
synchronized (mOperationsLock) {
op = mOperations.get(token);
- if (MORE_DEBUG) {
+ if (DEBUG) {
if (op == null) {
Slog.w(TAG, "[UserID:" + mUserId + "] Cancel of token "
+ Integer.toHexString(token) + " but no op found");
@@ -326,17 +323,13 @@ public class LifecycleOperationStorage implements OperationStorage {
int state = (op != null) ? op.state : OpState.TIMEOUT;
if (state == OpState.ACKNOWLEDGED) {
// The operation finished cleanly, so we have nothing more to do.
- if (DEBUG) {
- Slog.w(TAG, "[UserID:" + mUserId + "] Operation already got an ack."
+ Slog.w(TAG, "[UserID:" + mUserId + "] Operation already got an ack."
+ "Should have been removed from mCurrentOperations.");
- }
op = null;
mOperations.delete(token);
} else if (state == OpState.PENDING) {
- if (DEBUG) {
- Slog.v(TAG, "[UserID:" + mUserId + "] Cancel: token="
+ Slog.d(TAG, "[UserID:" + mUserId + "] Cancel: token="
+ Integer.toHexString(token));
- }
op.state = OpState.TIMEOUT;
// Can't delete op from mOperations here. waitUntilOperationComplete may be
// called after we receive cancel here. We need this op's state there.
@@ -347,7 +340,7 @@ public class LifecycleOperationStorage implements OperationStorage {
// If there's a TimeoutHandler for this event, call it
if (op != null && op.callback != null) {
- if (MORE_DEBUG) {
+ if (DEBUG) {
Slog.v(TAG, "[UserID:" + mUserId + " Invoking cancel on " + op.callback);
}
op.callback.handleCancel(cancelAll);
diff --git a/services/backup/java/com/android/server/backup/internal/PerformClearTask.java b/services/backup/java/com/android/server/backup/internal/PerformClearTask.java
index de0177c1b62c..b9ac5648a5f4 100644
--- a/services/backup/java/com/android/server/backup/internal/PerformClearTask.java
+++ b/services/backup/java/com/android/server/backup/internal/PerformClearTask.java
@@ -75,7 +75,7 @@ public class PerformClearTask implements Runnable {
}
mListener.onFinished(callerLogString);
// Last but not least, release the cpu
- mBackupManagerService.getWakelock().release();
+ mBackupManagerService.getWakeLock().release();
}
}
}
diff --git a/services/backup/java/com/android/server/backup/internal/RunInitializeReceiver.java b/services/backup/java/com/android/server/backup/internal/RunInitializeReceiver.java
index 160124b6f1d3..98563a7d7d8e 100644
--- a/services/backup/java/com/android/server/backup/internal/RunInitializeReceiver.java
+++ b/services/backup/java/com/android/server/backup/internal/RunInitializeReceiver.java
@@ -16,7 +16,6 @@
package com.android.server.backup.internal;
-import static com.android.server.backup.BackupManagerService.DEBUG;
import static com.android.server.backup.BackupManagerService.TAG;
import static com.android.server.backup.UserBackupManagerService.RUN_INITIALIZE_ACTION;
@@ -47,9 +46,7 @@ public class RunInitializeReceiver extends BroadcastReceiver {
synchronized (mUserBackupManagerService.getQueueLock()) {
Set<String> pendingInits = mUserBackupManagerService.getPendingInits();
- if (DEBUG) {
- Slog.v(TAG, "Running a device init; " + pendingInits.size() + " pending");
- }
+ Slog.d(TAG, "Running a device init; " + pendingInits.size() + " pending");
if (pendingInits.size() > 0) {
String[] transports = pendingInits.toArray(new String[pendingInits.size()]);
diff --git a/services/backup/java/com/android/server/backup/internal/SetupObserver.java b/services/backup/java/com/android/server/backup/internal/SetupObserver.java
index f399fe9e7ab9..309fe29b6866 100644
--- a/services/backup/java/com/android/server/backup/internal/SetupObserver.java
+++ b/services/backup/java/com/android/server/backup/internal/SetupObserver.java
@@ -16,7 +16,7 @@
package com.android.server.backup.internal;
-import static com.android.server.backup.BackupManagerService.MORE_DEBUG;
+import static com.android.server.backup.BackupManagerService.DEBUG;
import static com.android.server.backup.BackupManagerService.TAG;
import static com.android.server.backup.UserBackupManagerService.getSetupCompleteSettingForUser;
@@ -57,7 +57,7 @@ public class SetupObserver extends ContentObserver {
boolean resolvedSetupComplete = previousSetupComplete || newSetupComplete;
mUserBackupManagerService.setSetupComplete(resolvedSetupComplete);
- if (MORE_DEBUG) {
+ if (DEBUG) {
Slog.d(
TAG,
"Setup complete change: was="
@@ -73,7 +73,7 @@ public class SetupObserver extends ContentObserver {
if (resolvedSetupComplete
&& !previousSetupComplete
&& mUserBackupManagerService.isEnabled()) {
- if (MORE_DEBUG) {
+ if (DEBUG) {
Slog.d(TAG, "Setup complete so starting backups");
}
KeyValueBackupJob.schedule(mUserBackupManagerService.getUserId(), mContext,
diff --git a/services/backup/java/com/android/server/backup/keyvalue/KeyValueBackupReporter.java b/services/backup/java/com/android/server/backup/keyvalue/KeyValueBackupReporter.java
index 20c8cf6c6923..af0c895c1261 100644
--- a/services/backup/java/com/android/server/backup/keyvalue/KeyValueBackupReporter.java
+++ b/services/backup/java/com/android/server/backup/keyvalue/KeyValueBackupReporter.java
@@ -54,13 +54,10 @@ import java.util.List;
@VisibleForTesting
public class KeyValueBackupReporter {
@VisibleForTesting static final String TAG = "KeyValueBackupTask";
- private static final boolean DEBUG = BackupManagerService.DEBUG;
- @VisibleForTesting static final boolean MORE_DEBUG = BackupManagerService.MORE_DEBUG;
+ @VisibleForTesting static final boolean DEBUG = BackupManagerService.DEBUG;
static void onNewThread(String threadName) {
- if (DEBUG) {
- Slog.d(TAG, "Spinning thread " + threadName);
- }
+ Slog.d(TAG, "Spinning thread " + threadName);
}
private final UserBackupManagerService mBackupManagerService;
@@ -87,9 +84,7 @@ public class KeyValueBackupReporter {
}
void onSkipBackup() {
- if (DEBUG) {
- Slog.d(TAG, "Skipping backup since one is already in progress");
- }
+ Slog.d(TAG, "Skipping backup since one is already in progress");
}
void onEmptyQueueAtStart() {
@@ -97,9 +92,7 @@ public class KeyValueBackupReporter {
}
void onQueueReady(List<String> queue) {
- if (DEBUG) {
- Slog.v(TAG, "Beginning backup of " + queue.size() + " targets");
- }
+ Slog.d(TAG, "Beginning backup of " + queue.size() + " targets");
}
void onTransportReady(String transportName) {
@@ -167,7 +160,7 @@ public class KeyValueBackupReporter {
}
void onAgentError(String packageName) {
- if (MORE_DEBUG) {
+ if (DEBUG) {
Slog.i(TAG, "Agent failure for " + packageName + ", re-staging");
}
BackupObserverUtils.sendBackupOnPackageResult(
@@ -175,13 +168,11 @@ public class KeyValueBackupReporter {
}
void onExtractAgentData(String packageName) {
- if (DEBUG) {
- Slog.d(TAG, "Invoking agent on " + packageName);
- }
+ Slog.d(TAG, "Invoking agent on " + packageName);
}
void onAgentFilesReady(File backupDataFile) {
- if (MORE_DEBUG) {
+ if (DEBUG) {
Slog.d(TAG, "Data file: " + backupDataFile);
}
}
@@ -216,7 +207,7 @@ public class KeyValueBackupReporter {
null, BackupManagerMonitor.EXTRA_LOG_ILLEGAL_KEY, key));
BackupObserverUtils.sendBackupOnPackageResult(
mObserver, packageName, BackupManager.ERROR_AGENT_FAILURE);
- if (MORE_DEBUG) {
+ if (DEBUG) {
Slog.i(
TAG,
"Agent failure for " + packageName + " with illegal key " + key + ", dropped");
@@ -232,7 +223,7 @@ public class KeyValueBackupReporter {
}
void onWriteWidgetData(boolean priorStateExists, @Nullable byte[] widgetState) {
- if (MORE_DEBUG) {
+ if (DEBUG) {
Slog.i(
TAG,
"Checking widget update: state="
@@ -243,13 +234,13 @@ public class KeyValueBackupReporter {
}
void onTransportPerformBackup(String packageName) {
- if (MORE_DEBUG) {
+ if (DEBUG) {
Slog.v(TAG, "Sending non-empty data to transport for " + packageName);
}
}
void onEmptyData(PackageInfo packageInfo) {
- if (MORE_DEBUG) {
+ if (DEBUG) {
Slog.i(TAG, "No backup data written, not calling transport");
}
mBackupManagerMonitorEventSender.monitorEvent(
@@ -273,7 +264,7 @@ public class KeyValueBackupReporter {
}
void onPackageBackupQuotaExceeded(String packageName) {
- if (MORE_DEBUG) {
+ if (DEBUG) {
Slog.d(TAG, "Package " + packageName + " hit quota limit on key-value backup");
}
BackupObserverUtils.sendBackupOnPackageResult(
@@ -319,7 +310,7 @@ public class KeyValueBackupReporter {
}
void onCancel() {
- if (MORE_DEBUG) {
+ if (DEBUG) {
Slog.v(TAG, "Cancel received");
}
}
@@ -363,7 +354,7 @@ public class KeyValueBackupReporter {
}
void onRevertTask() {
- if (MORE_DEBUG) {
+ if (DEBUG) {
Slog.i(TAG, "Reverting backup queue, re-staging everything");
}
}
@@ -373,7 +364,7 @@ public class KeyValueBackupReporter {
}
void onRemoteCallReturned(RemoteResult result, String logIdentifier) {
- if (MORE_DEBUG) {
+ if (DEBUG) {
Slog.v(TAG, "Agent call " + logIdentifier + " returned " + result);
}
}
@@ -388,7 +379,7 @@ public class KeyValueBackupReporter {
void onTransportNotInitialized(@Nullable String transportName) {
EventLog.writeEvent(EventLogTags.BACKUP_RESET, transportName);
- if (MORE_DEBUG) {
+ if (DEBUG) {
Slog.d(TAG, "Transport requires initialization, rerunning");
}
}
diff --git a/services/backup/java/com/android/server/backup/keyvalue/KeyValueBackupTask.java b/services/backup/java/com/android/server/backup/keyvalue/KeyValueBackupTask.java
index 689c43a1cc96..494b9d59a238 100644
--- a/services/backup/java/com/android/server/backup/keyvalue/KeyValueBackupTask.java
+++ b/services/backup/java/com/android/server/backup/keyvalue/KeyValueBackupTask.java
@@ -826,7 +826,7 @@ public class KeyValueBackupTask implements BackupRestoreTask, Runnable {
}
mTaskFinishedListener.onFinished(callerLogString);
mReporter.onBackupFinished(getBackupFinishedStatus(mCancelled, status));
- mBackupManagerService.getWakelock().release();
+ mBackupManagerService.getWakeLock().release();
}
private int getBackupFinishedStatus(boolean cancelled, int transportStatus) {
@@ -878,12 +878,13 @@ public class KeyValueBackupTask implements BackupRestoreTask, Runnable {
* the transport or not. It's the caller responsibility to do the clean-up or delegate it.
*/
private void extractAgentData(PackageInfo packageInfo) throws AgentException, TaskException {
- mBackupManagerService.setWorkSource(new WorkSource(packageInfo.applicationInfo.uid));
+ mBackupManagerService.getWakeLock().setWorkSource(
+ new WorkSource(packageInfo.applicationInfo.uid));
try {
mAgent = bindAgent(packageInfo);
extractAgentData(packageInfo, mAgent);
} finally {
- mBackupManagerService.setWorkSource(null);
+ mBackupManagerService.getWakeLock().setWorkSource(null);
}
}
diff --git a/services/backup/java/com/android/server/backup/restore/ActiveRestoreSession.java b/services/backup/java/com/android/server/backup/restore/ActiveRestoreSession.java
index 9f7b62763ed3..41134d68916a 100644
--- a/services/backup/java/com/android/server/backup/restore/ActiveRestoreSession.java
+++ b/services/backup/java/com/android/server/backup/restore/ActiveRestoreSession.java
@@ -17,7 +17,6 @@
package com.android.server.backup.restore;
import static com.android.server.backup.BackupManagerService.DEBUG;
-import static com.android.server.backup.BackupManagerService.MORE_DEBUG;
import static com.android.server.backup.internal.BackupHandler.MSG_RESTORE_SESSION_TIMEOUT;
import static com.android.server.backup.internal.BackupHandler.MSG_RUN_GET_RESTORE_SETS;
import static com.android.server.backup.internal.BackupHandler.MSG_RUN_RESTORE;
@@ -41,6 +40,7 @@ import android.util.Slog;
import com.android.internal.annotations.VisibleForTesting;
import com.android.server.LocalServices;
+import com.android.server.backup.BackupWakeLock;
import com.android.server.backup.Flags;
import com.android.server.backup.TransportManager;
import com.android.server.backup.UserBackupManagerService;
@@ -121,7 +121,7 @@ public class ActiveRestoreSession extends IRestoreSession.Stub {
// comes in.
mBackupManagerService.getBackupHandler().removeMessages(MSG_RESTORE_SESSION_TIMEOUT);
- UserBackupManagerService.BackupWakeLock wakelock = mBackupManagerService.getWakelock();
+ BackupWakeLock wakelock = mBackupManagerService.getWakeLock();
wakelock.acquire();
// Prevent lambda from leaking 'this'
@@ -150,10 +150,8 @@ public class ActiveRestoreSession extends IRestoreSession.Stub {
android.Manifest.permission.BACKUP,
"performRestore");
- if (DEBUG) {
- Slog.d(TAG, "restoreAll token=" + Long.toHexString(token)
+ Slog.d(TAG, "restoreAll token=" + Long.toHexString(token)
+ " observer=" + observer);
- }
if (mEnded) {
throw new IllegalStateException("Restore session already ended");
@@ -213,40 +211,38 @@ public class ActiveRestoreSession extends IRestoreSession.Stub {
android.Manifest.permission.BACKUP,
"performRestore");
- if (DEBUG) {
- StringBuilder b = new StringBuilder(128);
- b.append("restorePackages token=");
- b.append(Long.toHexString(token));
- b.append(" observer=");
- if (observer == null) {
- b.append("null");
- } else {
- b.append(observer.toString());
- }
- b.append(" monitor=");
- if (monitor == null) {
- b.append("null");
- } else {
- b.append(monitor.toString());
- }
- b.append(" packages=");
- if (packages == null) {
- b.append("null");
- } else {
- b.append('{');
- boolean first = true;
- for (String s : packages) {
- if (!first) {
- b.append(", ");
- } else {
- first = false;
- }
- b.append(s);
+ StringBuilder b = new StringBuilder(128);
+ b.append("restorePackages token=");
+ b.append(Long.toHexString(token));
+ b.append(" observer=");
+ if (observer == null) {
+ b.append("null");
+ } else {
+ b.append(observer.toString());
+ }
+ b.append(" monitor=");
+ if (monitor == null) {
+ b.append("null");
+ } else {
+ b.append(monitor.toString());
+ }
+ b.append(" packages=");
+ if (packages == null) {
+ b.append("null");
+ } else {
+ b.append('{');
+ boolean first = true;
+ for (String s : packages) {
+ if (!first) {
+ b.append(", ");
+ } else {
+ first = false;
}
- b.append('}');
+ b.append(s);
}
- Slog.d(TAG, b.toString());
+ b.append('}');
}
+ Slog.d(TAG, b.toString());
if (mEnded) {
throw new IllegalStateException("Restore session already ended");
@@ -325,10 +321,8 @@ public class ActiveRestoreSession extends IRestoreSession.Stub {
public synchronized int restorePackage(String packageName, IRestoreObserver observer,
IBackupManagerMonitor monitor) {
- if (DEBUG) {
- Slog.v(TAG, "restorePackage pkg=" + packageName + " obs=" + observer
+ Slog.d(TAG, "restorePackage pkg=" + packageName + " obs=" + observer
+ "monitor=" + monitor);
- }
if (mEnded) {
throw new IllegalStateException("Restore session already ended");
@@ -379,18 +373,14 @@ public class ActiveRestoreSession extends IRestoreSession.Stub {
// Check whether there is data for it in the current dataset, falling back
// to the ancestral dataset if not.
long token = mBackupManagerService.getAvailableRestoreToken(packageName);
- if (DEBUG) {
- Slog.v(TAG, "restorePackage pkg=" + packageName
+ Slog.d(TAG, "restorePackage pkg=" + packageName
+ " token=" + Long.toHexString(token));
- }
// If we didn't come up with a place to look -- no ancestral dataset and
// the app has never been backed up from this device -- there's nothing
// to do but return failure.
if (token == 0) {
- if (DEBUG) {
- Slog.w(TAG, "No data available for this package; not restoring");
- }
+ Slog.w(TAG, "No data available for this package; not restoring");
return -1;
}
@@ -431,9 +421,9 @@ public class ActiveRestoreSession extends IRestoreSession.Stub {
Handler backupHandler = mBackupManagerService.getBackupHandler();
backupHandler.removeMessages(MSG_RESTORE_SESSION_TIMEOUT);
- UserBackupManagerService.BackupWakeLock wakelock = mBackupManagerService.getWakelock();
+ BackupWakeLock wakelock = mBackupManagerService.getWakeLock();
wakelock.acquire();
- if (MORE_DEBUG) {
+ if (DEBUG) {
Slog.d(TAG, callerLogString);
}
@@ -473,9 +463,7 @@ public class ActiveRestoreSession extends IRestoreSession.Stub {
}
public synchronized void endRestoreSession() {
- if (DEBUG) {
- Slog.d(TAG, "endRestoreSession");
- }
+ Slog.d(TAG, "endRestoreSession");
if (mTimedOut) {
Slog.i(TAG, "Session already timed out");
diff --git a/services/backup/java/com/android/server/backup/restore/AdbRestoreFinishedLatch.java b/services/backup/java/com/android/server/backup/restore/AdbRestoreFinishedLatch.java
index cfc0f203e6ec..cb491c6f384e 100644
--- a/services/backup/java/com/android/server/backup/restore/AdbRestoreFinishedLatch.java
+++ b/services/backup/java/com/android/server/backup/restore/AdbRestoreFinishedLatch.java
@@ -17,7 +17,6 @@
package com.android.server.backup.restore;
import static com.android.server.backup.BackupManagerService.DEBUG;
-import static com.android.server.backup.BackupManagerService.MORE_DEBUG;
import android.util.Slog;
@@ -72,7 +71,7 @@ public class AdbRestoreFinishedLatch implements BackupRestoreTask {
@Override
public void operationComplete(long result) {
- if (MORE_DEBUG) {
+ if (DEBUG) {
Slog.w(TAG, "adb onRestoreFinished() complete");
}
mLatch.countDown();
@@ -81,9 +80,7 @@ public class AdbRestoreFinishedLatch implements BackupRestoreTask {
@Override
public void handleCancel(boolean cancelAll) {
- if (DEBUG) {
- Slog.w(TAG, "adb onRestoreFinished() timed out");
- }
+ Slog.w(TAG, "adb onRestoreFinished() timed out");
mLatch.countDown();
mOperationStorage.removeOperation(mCurrentOpToken);
}
diff --git a/services/backup/java/com/android/server/backup/restore/FullRestoreEngine.java b/services/backup/java/com/android/server/backup/restore/FullRestoreEngine.java
index 237ffa3bdb21..19e565dab719 100644
--- a/services/backup/java/com/android/server/backup/restore/FullRestoreEngine.java
+++ b/services/backup/java/com/android/server/backup/restore/FullRestoreEngine.java
@@ -17,7 +17,6 @@
package com.android.server.backup.restore;
import static com.android.server.backup.BackupManagerService.DEBUG;
-import static com.android.server.backup.BackupManagerService.MORE_DEBUG;
import static com.android.server.backup.BackupManagerService.TAG;
import static com.android.server.backup.UserBackupManagerService.BACKUP_MANIFEST_FILENAME;
import static com.android.server.backup.UserBackupManagerService.BACKUP_METADATA_FILENAME;
@@ -215,12 +214,12 @@ public class FullRestoreEngine extends RestoreEngine {
FileMetadata info;
try {
- if (MORE_DEBUG) {
+ if (DEBUG) {
Slog.v(TAG, "Reading tar header for restoring file");
}
info = tarBackupReader.readTarHeaders();
if (info != null) {
- if (MORE_DEBUG) {
+ if (DEBUG) {
info.dump();
}
@@ -249,9 +248,7 @@ public class FullRestoreEngine extends RestoreEngine {
// Clean up the previous agent relationship if necessary,
// and let the observer know we're considering a new app.
if (mAgent != null) {
- if (DEBUG) {
- Slog.d(TAG, "Saw new package; finalizing old one");
- }
+ Slog.d(TAG, "Saw new package; finalizing old one");
// Now we're really done
tearDownPipes();
tearDownAgent(mTargetApp, mIsAdbRestore);
@@ -307,9 +304,7 @@ public class FullRestoreEngine extends RestoreEngine {
// If we're in accept-if-apk state, then the first file we
// see MUST be the apk.
if (info.domain.equals(FullBackup.APK_TREE_TOKEN)) {
- if (DEBUG) {
- Slog.d(TAG, "APK file; installing");
- }
+ Slog.d(TAG, "APK file; installing");
// Try to install the app.
String installerPackageName = mPackageInstallers.get(pkg);
boolean isSuccessfullyInstalled = RestoreUtils.installApk(
@@ -336,9 +331,7 @@ public class FullRestoreEngine extends RestoreEngine {
case ACCEPT:
if (info.domain.equals(FullBackup.APK_TREE_TOKEN)) {
- if (DEBUG) {
- Slog.d(TAG, "apk present but ACCEPT");
- }
+ Slog.d(TAG, "apk present but ACCEPT");
// we can take the data without the apk, so we
// *want* to do so. skip the apk by declaring this
// one file not-okay without changing the restore
@@ -364,11 +357,11 @@ public class FullRestoreEngine extends RestoreEngine {
// If the policy is satisfied, go ahead and set up to pipe the
// data to the agent.
- if (MORE_DEBUG && okay && mAgent != null) {
+ if (DEBUG && okay && mAgent != null) {
Slog.i(TAG, "Reusing existing agent instance");
}
if (okay && mAgent == null) {
- if (MORE_DEBUG) {
+ if (DEBUG) {
Slog.d(TAG, "Need to launch agent for " + pkg);
}
@@ -389,20 +382,18 @@ public class FullRestoreEngine extends RestoreEngine {
boolean forceClear = shouldForceClearAppDataOnFullRestore(
mTargetApp.packageName);
if (mTargetApp.backupAgentName == null || forceClear) {
- if (DEBUG) {
- Slog.d(TAG,
+ Slog.d(TAG,
"Clearing app data preparatory to full restore");
- }
mBackupManagerService.clearApplicationDataBeforeRestore(pkg);
} else {
- if (MORE_DEBUG) {
+ if (DEBUG) {
Slog.d(TAG, "backup agent ("
+ mTargetApp.backupAgentName + ") => no clear");
}
}
mClearedPackages.add(pkg);
} else {
- if (MORE_DEBUG) {
+ if (DEBUG) {
Slog.d(TAG, "We've initialized this app already; no clear "
+ "required");
}
@@ -459,10 +450,8 @@ public class FullRestoreEngine extends RestoreEngine {
OpType.RESTORE_WAIT);
if (FullBackup.OBB_TREE_TOKEN.equals(info.domain)) {
- if (DEBUG) {
- Slog.d(TAG, "Restoring OBB file for " + pkg
+ Slog.d(TAG, "Restoring OBB file for " + pkg
+ " : " + info.path);
- }
mObbConnection.restoreObbFile(pkg, mPipes[0],
info.size, info.type, info.path, info.mode,
info.mtime, token,
@@ -470,10 +459,8 @@ public class FullRestoreEngine extends RestoreEngine {
} else if (FullBackup.KEY_VALUE_DATA_TOKEN.equals(info.domain)) {
// This is only possible during adb restore.
// TODO: Refactor to clearly separate the flows.
- if (DEBUG) {
- Slog.d(TAG, "Restoring key-value file for " + pkg
+ Slog.d(TAG, "Restoring key-value file for " + pkg
+ " : " + info.path);
- }
// Set the version saved from manifest entry.
info.version = mAppVersion;
KeyValueAdbRestoreEngine restoreEngine =
@@ -483,7 +470,7 @@ public class FullRestoreEngine extends RestoreEngine {
mAgent, token);
new Thread(restoreEngine, "restore-key-value-runner").start();
} else {
- if (MORE_DEBUG) {
+ if (DEBUG) {
Slog.d(TAG, "Invoking agent to restore file " + info.path);
}
// fire up the app's agent listening on the socket. If
@@ -519,7 +506,7 @@ public class FullRestoreEngine extends RestoreEngine {
// Copy over the data if the agent is still good
if (okay) {
- if (MORE_DEBUG) {
+ if (DEBUG) {
Slog.v(TAG, " copying to restore agent: " + toCopy + " bytes");
}
boolean pipeOkay = true;
@@ -586,7 +573,7 @@ public class FullRestoreEngine extends RestoreEngine {
// dropped file, or an already-ignored package: skip to the
// next stream entry by reading and discarding this file.
if (!okay) {
- if (MORE_DEBUG) {
+ if (DEBUG) {
Slog.d(TAG, "[discarding file content]");
}
long bytesToConsume = (info.size + 511) & ~511;
@@ -603,9 +590,7 @@ public class FullRestoreEngine extends RestoreEngine {
}
}
} catch (IOException e) {
- if (DEBUG) {
- Slog.w(TAG, "io exception on restore socket read: " + e.getMessage());
- }
+ Slog.w(TAG, "io exception on restore socket read: " + e.getMessage());
logBMMEvent(BackupManagerMonitor.LOG_EVENT_ID_FAILED_TO_READ_DATA_FROM_TRANSPORT,
onlyPackage);
setResult(RestoreEngine.TRANSPORT_FAILURE);
@@ -614,7 +599,7 @@ public class FullRestoreEngine extends RestoreEngine {
// If we got here we're either running smoothly or we've finished
if (info == null) {
- if (MORE_DEBUG) {
+ if (DEBUG) {
Slog.i(TAG, "No [more] data for this package; tearing down");
}
tearDownPipes();
@@ -713,7 +698,7 @@ public class FullRestoreEngine extends RestoreEngine {
mBackupManagerService.prepareOperationTimeout(
token, fullBackupAgentTimeoutMillis, latch, OpType.RESTORE_WAIT);
if (mTargetApp.processName.equals("system")) {
- if (MORE_DEBUG) {
+ if (DEBUG) {
Slog.d(TAG, "system agent - restoreFinished on thread");
}
Runnable runner = new AdbRestoreFinishedRunnable(mAgent, token,
@@ -749,7 +734,7 @@ public class FullRestoreEngine extends RestoreEngine {
return true;
}
if (FullBackup.CACHE_TREE_TOKEN.equals(info.domain)) {
- if (MORE_DEBUG) {
+ if (DEBUG) {
Slog.i(TAG, "Dropping cache file path " + info.path);
}
return false;
@@ -761,7 +746,7 @@ public class FullRestoreEngine extends RestoreEngine {
// API. Respect the no-backup intention and don't let the data get to
// the app.
if (info.path.startsWith("no_backup/")) {
- if (MORE_DEBUG) {
+ if (DEBUG) {
Slog.i(TAG, "Dropping no_backup file path " + info.path);
}
return false;
@@ -774,7 +759,7 @@ public class FullRestoreEngine extends RestoreEngine {
private static boolean isCanonicalFilePath(String path) {
if (path.contains("..") || path.contains("//")) {
- if (MORE_DEBUG) {
+ if (DEBUG) {
Slog.w(TAG, "Dropping invalid path " + path);
}
return false;
diff --git a/services/backup/java/com/android/server/backup/restore/PerformAdbRestoreTask.java b/services/backup/java/com/android/server/backup/restore/PerformAdbRestoreTask.java
index 2374dee7755e..5a3494c2bc6e 100644
--- a/services/backup/java/com/android/server/backup/restore/PerformAdbRestoreTask.java
+++ b/services/backup/java/com/android/server/backup/restore/PerformAdbRestoreTask.java
@@ -17,7 +17,6 @@
package com.android.server.backup.restore;
import static com.android.server.backup.BackupManagerService.DEBUG;
-import static com.android.server.backup.BackupManagerService.MORE_DEBUG;
import static com.android.server.backup.BackupManagerService.TAG;
import static com.android.server.backup.BackupPasswordManager.PBKDF_CURRENT;
import static com.android.server.backup.BackupPasswordManager.PBKDF_FALLBACK;
@@ -93,9 +92,7 @@ public class PerformAdbRestoreTask implements Runnable {
FileInputStream rawInStream = null;
try {
if (!mBackupManagerService.backupPasswordMatches(mCurrentPassword)) {
- if (DEBUG) {
- Slog.w(TAG, "Backup password mismatch; aborting");
- }
+ Slog.w(TAG, "Backup password mismatch; aborting");
return;
}
@@ -122,7 +119,7 @@ public class PerformAdbRestoreTask implements Runnable {
tarInputStream);
mEngineThread.run();
- if (MORE_DEBUG) {
+ if (DEBUG) {
Slog.v(TAG, "Done consuming input tarfile.");
}
} catch (Exception e) {
@@ -144,7 +141,7 @@ public class PerformAdbRestoreTask implements Runnable {
mObbConnection.tearDown();
mObserver = FullBackupRestoreObserverUtils.sendEndRestore(mObserver);
Slog.d(TAG, "Full restore pass complete.");
- mBackupManagerService.getWakelock().release();
+ mBackupManagerService.getWakeLock().release();
}
}
diff --git a/services/backup/java/com/android/server/backup/restore/PerformUnifiedRestoreTask.java b/services/backup/java/com/android/server/backup/restore/PerformUnifiedRestoreTask.java
index ec9d340abe45..707ae03b3964 100644
--- a/services/backup/java/com/android/server/backup/restore/PerformUnifiedRestoreTask.java
+++ b/services/backup/java/com/android/server/backup/restore/PerformUnifiedRestoreTask.java
@@ -19,7 +19,6 @@ package com.android.server.backup.restore;
import static android.app.backup.BackupAnnotations.OperationType.RESTORE;
import static com.android.server.backup.BackupManagerService.DEBUG;
-import static com.android.server.backup.BackupManagerService.MORE_DEBUG;
import static com.android.server.backup.BackupManagerService.TAG;
import static com.android.server.backup.UserBackupManagerService.KEY_WIDGET_STATE;
import static com.android.server.backup.UserBackupManagerService.PACKAGE_MANAGER_SENTINEL;
@@ -253,9 +252,7 @@ public class PerformUnifiedRestoreTask implements BackupRestoreTask {
mUserId,
backupEligibilityRules);
filterSet = packagesToNames(apps);
- if (DEBUG) {
- Slog.i(TAG, "Full restore; asking about " + filterSet.length + " apps");
- }
+ Slog.i(TAG, "Full restore; asking about " + filterSet.length + " apps");
}
mAcceptSet = new ArrayList<>(filterSet.length);
@@ -315,7 +312,7 @@ public class PerformUnifiedRestoreTask implements BackupRestoreTask {
}
}
- if (MORE_DEBUG) {
+ if (DEBUG) {
Slog.v(TAG, "Restore; accept set size is " + mAcceptSet.size());
for (PackageInfo info : mAcceptSet) {
Slog.v(TAG, " " + info.packageName);
@@ -335,7 +332,7 @@ public class PerformUnifiedRestoreTask implements BackupRestoreTask {
// Execute one tick of whatever state machine the task implements
@Override
public void execute() {
- if (MORE_DEBUG) {
+ if (DEBUG) {
Slog.v(TAG, "*** Executing restore step " + mState);
}
switch (mState) {
@@ -517,7 +514,7 @@ public class PerformUnifiedRestoreTask implements BackupRestoreTask {
mCurrentPackage.applicationInfo.uid = Process.SYSTEM_UID;
mPmAgent = backupManagerService.makeMetadataAgent(null);
mAgent = IBackupAgent.Stub.asInterface(mPmAgent.onBind());
- if (MORE_DEBUG) {
+ if (DEBUG) {
Slog.v(TAG, "initiating restore for PMBA");
}
initiateOneRestore(mCurrentPackage, 0);
@@ -596,9 +593,7 @@ public class PerformUnifiedRestoreTask implements BackupRestoreTask {
return;
} else if (mRestoreDescription == RestoreDescription.NO_MORE_PACKAGES) {
// Yay we've reached the end cleanly
- if (DEBUG) {
- Slog.v(TAG, "No more packages; finishing restore");
- }
+ Slog.d(TAG, "No more packages; finishing restore");
int millis = (int) (SystemClock.elapsedRealtime() - mStartRealtime);
EventLog.writeEvent(
EventLogTags.RESTORE_SUCCESS, mRestoreAttemptedAppsCount, millis);
@@ -606,9 +601,7 @@ public class PerformUnifiedRestoreTask implements BackupRestoreTask {
return;
}
- if (DEBUG) {
- Slog.i(TAG, "Next restore package: " + mRestoreDescription);
- }
+ Slog.i(TAG, "Next restore package: " + mRestoreDescription);
mRestoreAttemptedAppsCount++;
sendOnRestorePackage(mRestoreAttemptedAppsCount, pkgName);
@@ -715,7 +708,7 @@ public class PerformUnifiedRestoreTask implements BackupRestoreTask {
}
}
- if (MORE_DEBUG) {
+ if (DEBUG) {
Slog.v(
TAG,
"Package "
@@ -776,7 +769,7 @@ public class PerformUnifiedRestoreTask implements BackupRestoreTask {
/* extras= */ addRestoreOperationTypeToEvent(/* extras= */ null));
if (mCurrentPackage.applicationInfo.backupAgentName == null
|| "".equals(mCurrentPackage.applicationInfo.backupAgentName)) {
- if (MORE_DEBUG) {
+ if (DEBUG) {
Slog.i(
TAG,
"Data exists for package "
@@ -852,9 +845,7 @@ public class PerformUnifiedRestoreTask implements BackupRestoreTask {
private void initiateOneRestore(PackageInfo app, long appVersionCode) {
final String packageName = app.packageName;
- if (DEBUG) {
- Slog.d(TAG, "initiateOneRestore packageName=" + packageName);
- }
+ Slog.d(TAG, "initiateOneRestore packageName=" + packageName);
// !!! TODO: get the dirs from the transport
mBackupDataName = new File(backupManagerService.getDataDir(), packageName + ".restore");
@@ -1010,9 +1001,7 @@ public class PerformUnifiedRestoreTask implements BackupRestoreTask {
// is this a special key?
if (key.equals(KEY_WIDGET_STATE)) {
- if (DEBUG) {
- Slog.i(TAG, "Restoring widget state for " + packageName);
- }
+ Slog.i(TAG, "Restoring widget state for " + packageName);
mWidgetData = new byte[size];
in.readEntityData(mWidgetData, 0, size);
} else {
@@ -1044,7 +1033,7 @@ public class PerformUnifiedRestoreTask implements BackupRestoreTask {
/* extras= */ addRestoreOperationTypeToEvent(/* extras= */ null));
try {
StreamFeederThread feeder = new StreamFeederThread();
- if (MORE_DEBUG) {
+ if (DEBUG) {
Slog.i(
TAG,
"Spinning threads for stream restore of " + mCurrentPackage.packageName);
@@ -1070,9 +1059,7 @@ public class PerformUnifiedRestoreTask implements BackupRestoreTask {
// state RESTORE_FINISHED : provide the "no more data" signpost callback at the end
private void restoreFinished() {
- if (DEBUG) {
- Slog.d(TAG, "restoreFinished packageName=" + mCurrentPackage.packageName);
- }
+ Slog.d(TAG, "restoreFinished packageName=" + mCurrentPackage.packageName);
try {
long restoreAgentFinishedTimeoutMillis =
mAgentTimeoutParameters.getRestoreAgentFinishedTimeoutMillis();
@@ -1167,7 +1154,7 @@ public class PerformUnifiedRestoreTask implements BackupRestoreTask {
if (result > 0) {
// The transport wrote this many bytes of restore data to the
// pipe, so pass it along to the engine.
- if (MORE_DEBUG) {
+ if (DEBUG) {
Slog.v(TAG, " <- transport provided chunk size " + result);
}
if (result > bufferSize) {
@@ -1179,13 +1166,13 @@ public class PerformUnifiedRestoreTask implements BackupRestoreTask {
int n = transportIn.read(buffer, 0, toCopy);
engineOut.write(buffer, 0, n);
toCopy -= n;
- if (MORE_DEBUG) {
+ if (DEBUG) {
Slog.v(TAG, " -> wrote " + n + " to engine, left=" + toCopy);
}
}
} else if (result == BackupTransport.NO_MORE_DATA) {
// Clean finish. Wind up and we're done!
- if (MORE_DEBUG) {
+ if (DEBUG) {
Slog.i(
TAG,
"Got clean full-restore EOF for "
@@ -1213,7 +1200,7 @@ public class PerformUnifiedRestoreTask implements BackupRestoreTask {
status = result;
}
}
- if (MORE_DEBUG) {
+ if (DEBUG) {
Slog.v(TAG, "Done copying to engine, falling through");
}
} catch (IOException e) {
@@ -1322,9 +1309,7 @@ public class PerformUnifiedRestoreTask implements BackupRestoreTask {
@Override
public void handleCancel(boolean cancelAll) {
mOperationStorage.removeOperation(mEphemeralOpToken);
- if (DEBUG) {
- Slog.w(TAG, "Full-data restore target timed out; shutting down");
- }
+ Slog.w(TAG, "Full-data restore target timed out; shutting down");
Bundle monitoringExtras = addRestoreOperationTypeToEvent(/* extras= */ null);
mBackupManagerMonitorEventSender.monitorEvent(
BackupManagerMonitor.LOG_EVENT_ID_FULL_RESTORE_TIMEOUT,
@@ -1342,7 +1327,7 @@ public class PerformUnifiedRestoreTask implements BackupRestoreTask {
// state FINAL : tear everything down and we're done.
private void finalizeRestore() {
- if (MORE_DEBUG) {
+ if (DEBUG) {
Slog.d(TAG, "finishing restore mObserver=" + mObserver);
}
@@ -1366,7 +1351,7 @@ public class PerformUnifiedRestoreTask implements BackupRestoreTask {
// If we have a PM token, we must under all circumstances be sure to
// handshake when we've finished.
if (mPmToken > 0) {
- if (MORE_DEBUG) {
+ if (DEBUG) {
Slog.v(TAG, "finishing PM token " + mPmToken);
}
try {
@@ -1404,9 +1389,7 @@ public class PerformUnifiedRestoreTask implements BackupRestoreTask {
synchronized (backupManagerService.getPendingRestores()) {
if (backupManagerService.getPendingRestores().size() > 0) {
- if (DEBUG) {
- Slog.d(TAG, "Starting next pending restore.");
- }
+ Slog.d(TAG, "Starting next pending restore.");
PerformUnifiedRestoreTask task = backupManagerService.getPendingRestores().remove();
backupManagerService
.getBackupHandler()
@@ -1417,7 +1400,7 @@ public class PerformUnifiedRestoreTask implements BackupRestoreTask {
} else {
backupManagerService.setRestoreInProgress(false);
- if (MORE_DEBUG) {
+ if (DEBUG) {
Slog.d(TAG, "No pending restores.");
}
}
@@ -1499,7 +1482,7 @@ public class PerformUnifiedRestoreTask implements BackupRestoreTask {
public void operationComplete(long unusedResult) {
mOperationStorage.removeOperation(mEphemeralOpToken);
- if (MORE_DEBUG) {
+ if (DEBUG) {
Slog.i(
TAG,
"operationComplete() during restore: target="
@@ -1590,7 +1573,7 @@ public class PerformUnifiedRestoreTask implements BackupRestoreTask {
@VisibleForTesting
void executeNextState(UnifiedRestoreState nextState) {
- if (MORE_DEBUG) {
+ if (DEBUG) {
Slog.i(TAG, " => executing next step on " + this + " nextState=" + nextState);
}
mState = nextState;
@@ -1689,25 +1672,19 @@ public class PerformUnifiedRestoreTask implements BackupRestoreTask {
// (and not in the denylist)
// - The package has restoreAnyVersion set to true and is not part of the denylist
if (mVToUDenylist.contains(mCurrentPackage.packageName)){
- if (DEBUG) {
- Slog.i(TAG, mCurrentPackage.packageName + " : Package is in V to U denylist");
- }
+ Slog.i(TAG, mCurrentPackage.packageName + " : Package is in V to U denylist");
return false;
} else if ((mCurrentPackage.applicationInfo.flags
& ApplicationInfo.FLAG_RESTORE_ANY_VERSION)
== 0) {
// package has restoreAnyVersion set to false
- if (DEBUG) {
- Slog.i(TAG, mCurrentPackage.packageName
+ Slog.i(TAG, mCurrentPackage.packageName
+ " : Package has restoreAnyVersion=false and is in V to U allowlist");
- }
return mVToUAllowlist.contains(mCurrentPackage.packageName);
} else {
// package has restoreAnyVersion set to true and is nor in denylist
- if (DEBUG) {
- Slog.i(TAG, mCurrentPackage.packageName
+ Slog.i(TAG, mCurrentPackage.packageName
+ " : Package has restoreAnyVersion=true and is not in V to U denylist");
- }
return true;
}
}
diff --git a/services/backup/java/com/android/server/backup/utils/BackupEligibilityRules.java b/services/backup/java/com/android/server/backup/utils/BackupEligibilityRules.java
index 508b62cd83f0..30243013a79b 100644
--- a/services/backup/java/com/android/server/backup/utils/BackupEligibilityRules.java
+++ b/services/backup/java/com/android/server/backup/utils/BackupEligibilityRules.java
@@ -16,7 +16,6 @@
package com.android.server.backup.utils;
-import static com.android.server.backup.BackupManagerService.MORE_DEBUG;
import static com.android.server.backup.BackupManagerService.TAG;
import static com.android.server.backup.UserBackupManagerService.PACKAGE_MANAGER_SENTINEL;
import static com.android.server.backup.UserBackupManagerService.SETTINGS_PACKAGE;
@@ -46,6 +45,7 @@ import android.util.Slog;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.ArrayUtils;
+import com.android.server.backup.BackupManagerService;
import com.android.server.backup.SetUtils;
import com.android.server.backup.transport.BackupTransportClient;
import com.android.server.backup.transport.TransportConnection;
@@ -413,8 +413,8 @@ public class BackupEligibilityRules {
// partition will be signed with the device's platform certificate, so on
// different phones the same system app will have different signatures.)
if ((target.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0) {
- if (MORE_DEBUG) {
- Slog.v(TAG, "System app " + target.packageName + " - skipping sig check");
+ if (BackupManagerService.DEBUG) {
+ Slog.d(TAG, "System app " + target.packageName + " - skipping sig check");
}
return true;
}
@@ -431,10 +431,8 @@ public class BackupEligibilityRules {
return false;
}
- if (DEBUG) {
- Slog.v(TAG, "signaturesMatch(): stored=" + Arrays.toString(storedSigs)
+ Slog.d(TAG, "signaturesMatch(): stored=" + Arrays.toString(storedSigs)
+ " device=" + Arrays.toString(signingInfo.getApkContentsSigners()));
- }
final int nStored = storedSigs.length;
if (nStored == 1) {
diff --git a/services/backup/java/com/android/server/backup/utils/BackupManagerMonitorEventSender.java b/services/backup/java/com/android/server/backup/utils/BackupManagerMonitorEventSender.java
index 549d08c03933..c4519b1173eb 100644
--- a/services/backup/java/com/android/server/backup/utils/BackupManagerMonitorEventSender.java
+++ b/services/backup/java/com/android/server/backup/utils/BackupManagerMonitorEventSender.java
@@ -22,7 +22,6 @@ import static android.app.backup.BackupManagerMonitor.EXTRA_LOG_OPERATION_TYPE;
import static android.app.backup.BackupManagerMonitor.LOG_EVENT_CATEGORY_AGENT;
import static android.app.backup.BackupManagerMonitor.LOG_EVENT_ID_AGENT_LOGGING_RESULTS;
-import static com.android.server.backup.BackupManagerService.DEBUG;
import static com.android.server.backup.BackupManagerService.TAG;
import android.annotation.Nullable;
@@ -115,15 +114,11 @@ public class BackupManagerMonitorEventSender {
if (mMonitor != null) {
mMonitor.onEvent(bundle);
} else {
- if (DEBUG) {
- Slog.w(TAG, "backup manager monitor is null unable to send event");
- }
+ Slog.w(TAG, "backup manager monitor is null unable to send event");
}
} catch (RemoteException e) {
mMonitor = null;
- if (DEBUG) {
- Slog.w(TAG, "backup manager monitor went away");
- }
+ Slog.w(TAG, "backup manager monitor went away");
}
}
diff --git a/services/backup/java/com/android/server/backup/utils/BackupObserverUtils.java b/services/backup/java/com/android/server/backup/utils/BackupObserverUtils.java
index c0cf2ef86920..95e6cfc2f252 100644
--- a/services/backup/java/com/android/server/backup/utils/BackupObserverUtils.java
+++ b/services/backup/java/com/android/server/backup/utils/BackupObserverUtils.java
@@ -16,7 +16,6 @@
package com.android.server.backup.utils;
-import static com.android.server.backup.BackupManagerService.DEBUG;
import static com.android.server.backup.BackupManagerService.TAG;
import android.app.backup.BackupProgress;
@@ -38,9 +37,7 @@ public class BackupObserverUtils {
try {
observer.onUpdate(packageName, progress);
} catch (RemoteException e) {
- if (DEBUG) {
- Slog.w(TAG, "Backup observer went away: onUpdate");
- }
+ Slog.w(TAG, "Backup observer went away: onUpdate");
}
}
}
@@ -55,9 +52,7 @@ public class BackupObserverUtils {
try {
observer.onResult(packageName, status);
} catch (RemoteException e) {
- if (DEBUG) {
- Slog.w(TAG, "Backup observer went away: onResult");
- }
+ Slog.w(TAG, "Backup observer went away: onResult");
}
}
}
@@ -71,9 +66,7 @@ public class BackupObserverUtils {
try {
observer.backupFinished(status);
} catch (RemoteException e) {
- if (DEBUG) {
- Slog.w(TAG, "Backup observer went away: backupFinished");
- }
+ Slog.w(TAG, "Backup observer went away: backupFinished");
}
}
}
diff --git a/services/backup/java/com/android/server/backup/utils/RestoreUtils.java b/services/backup/java/com/android/server/backup/utils/RestoreUtils.java
index 5a8533a2daee..23e5bb965d7a 100644
--- a/services/backup/java/com/android/server/backup/utils/RestoreUtils.java
+++ b/services/backup/java/com/android/server/backup/utils/RestoreUtils.java
@@ -16,7 +16,6 @@
package com.android.server.backup.utils;
-import static com.android.server.backup.BackupManagerService.DEBUG;
import static com.android.server.backup.BackupManagerService.TAG;
import android.content.Context;
@@ -78,9 +77,7 @@ public class RestoreUtils {
int userId) {
boolean okay = true;
- if (DEBUG) {
- Slog.d(TAG, "Installing from backup: " + info.packageName);
- }
+ Slog.d(TAG, "Installing from backup: " + info.packageName);
try {
LocalIntentReceiver receiver = new LocalIntentReceiver();
diff --git a/services/backup/java/com/android/server/backup/utils/TarBackupReader.java b/services/backup/java/com/android/server/backup/utils/TarBackupReader.java
index 22eefb3b73c2..44b536adcf31 100644
--- a/services/backup/java/com/android/server/backup/utils/TarBackupReader.java
+++ b/services/backup/java/com/android/server/backup/utils/TarBackupReader.java
@@ -36,7 +36,6 @@ import static android.app.backup.BackupManagerMonitor.LOG_EVENT_ID_VERSIONS_MATC
import static android.app.backup.BackupManagerMonitor.LOG_EVENT_ID_VERSION_OF_BACKUP_OLDER;
import static com.android.server.backup.BackupManagerService.DEBUG;
-import static com.android.server.backup.BackupManagerService.MORE_DEBUG;
import static com.android.server.backup.BackupManagerService.TAG;
import static com.android.server.backup.UserBackupManagerService.BACKUP_MANIFEST_FILENAME;
import static com.android.server.backup.UserBackupManagerService.BACKUP_MANIFEST_VERSION;
@@ -175,7 +174,7 @@ public class TarBackupReader {
}
case 0: {
// presume EOF
- if (MORE_DEBUG) {
+ if (DEBUG) {
Slog.w(TAG, "Saw type=0 in tar header block, info=" + info);
}
return null;
@@ -195,9 +194,7 @@ public class TarBackupReader {
info.path = info.path.substring(FullBackup.SHARED_PREFIX.length());
info.packageName = SHARED_BACKUP_AGENT_PACKAGE;
info.domain = FullBackup.SHARED_STORAGE_TOKEN;
- if (DEBUG) {
- Slog.i(TAG, "File in shared storage: " + info.path);
- }
+ Slog.i(TAG, "File in shared storage: " + info.path);
} else if (FullBackup.APPS_PREFIX.regionMatches(0,
info.path, 0, FullBackup.APPS_PREFIX.length())) {
// App content! Parse out the package name and domain
@@ -227,11 +224,9 @@ public class TarBackupReader {
}
}
} catch (IOException e) {
+ Slog.e(TAG, "Parse error in header: " + e.getMessage());
if (DEBUG) {
- Slog.e(TAG, "Parse error in header: " + e.getMessage());
- if (MORE_DEBUG) {
- hexLog(block);
- }
+ hexLog(block);
}
throw e;
}
@@ -254,20 +249,20 @@ public class TarBackupReader {
if (size <= 0) {
throw new IllegalArgumentException("size must be > 0");
}
- if (MORE_DEBUG) {
+ if (DEBUG) {
Slog.i(TAG, " ... readExactly(" + size + ") called");
}
int soFar = 0;
while (soFar < size) {
int nRead = in.read(buffer, offset + soFar, size - soFar);
if (nRead <= 0) {
- if (MORE_DEBUG) {
+ if (DEBUG) {
Slog.w(TAG, "- wanted exactly " + size + " but got only " + soFar);
}
break;
}
soFar += nRead;
- if (MORE_DEBUG) {
+ if (DEBUG) {
Slog.v(TAG, " + got " + nRead + "; now wanting " + (size - soFar));
}
}
@@ -290,7 +285,7 @@ public class TarBackupReader {
}
byte[] buffer = new byte[(int) info.size];
- if (MORE_DEBUG) {
+ if (DEBUG) {
Slog.i(TAG,
" readAppManifestAndReturnSignatures() looking for " + info.size + " bytes");
}
@@ -514,10 +509,7 @@ public class TarBackupReader {
null);
}
} else {
- if (DEBUG) {
- Slog.i(TAG,
- "Restore manifest from " + info.packageName + " but allowBackup=false");
- }
+ Slog.i(TAG, "Restore manifest from " + info.packageName + " but allowBackup=false");
mBackupManagerMonitorEventSender.monitorEvent(
LOG_EVENT_ID_FULL_RESTORE_ALLOW_BACKUP_FALSE,
pkgInfo,
@@ -529,10 +521,8 @@ public class TarBackupReader {
// the restore properly only if the dataset provides the
// apk file and we can successfully install it.
if (allowApks) {
- if (DEBUG) {
- Slog.i(TAG, "Package " + info.packageName
- + " not installed; requiring apk in dataset");
- }
+ Slog.i(TAG,
+ "Package " + info.packageName + " not installed; requiring apk in dataset");
policy = RestorePolicy.ACCEPT_IF_APK;
} else {
policy = RestorePolicy.IGNORE;
@@ -570,7 +560,7 @@ public class TarBackupReader {
long partial = (size + 512) % 512;
if (partial > 0) {
final int needed = 512 - (int) partial;
- if (MORE_DEBUG) {
+ if (DEBUG) {
Slog.i(TAG, "Skipping tar padding: " + needed + " bytes");
}
byte[] buffer = new byte[needed];
@@ -619,7 +609,7 @@ public class TarBackupReader {
}
switch (token) {
case BACKUP_WIDGET_METADATA_TOKEN: {
- if (MORE_DEBUG) {
+ if (DEBUG) {
Slog.i(TAG, "Got widget metadata for " + info.packageName);
}
mWidgetData = new byte[size];
@@ -627,10 +617,9 @@ public class TarBackupReader {
break;
}
default: {
- if (DEBUG) {
- Slog.i(TAG, "Ignoring metadata blob " + Integer.toHexString(token)
- + " for " + info.packageName);
- }
+ Slog.i(TAG,
+ "Ignoring metadata blob " + Integer.toHexString(token) + " for "
+ + info.packageName);
in.skipBytes(size);
break;
}
@@ -759,9 +748,7 @@ public class TarBackupReader {
} else if ("size".equals(keyStr)) {
info.size = Long.parseLong(valStr);
} else {
- if (DEBUG) {
- Slog.i(TAG, "Unhandled pax key: " + key);
- }
+ Slog.i(TAG, "Unhandled pax key: " + key);
}
offset += linelen;
diff --git a/services/companion/java/com/android/server/companion/virtual/VirtualDeviceImpl.java b/services/companion/java/com/android/server/companion/virtual/VirtualDeviceImpl.java
index 260ea75a1f4c..5edd9d7041ba 100644
--- a/services/companion/java/com/android/server/companion/virtual/VirtualDeviceImpl.java
+++ b/services/companion/java/com/android/server/companion/virtual/VirtualDeviceImpl.java
@@ -21,7 +21,6 @@ import static android.Manifest.permission.ADD_TRUSTED_DISPLAY;
import static android.app.admin.DevicePolicyManager.NEARBY_STREAMING_ENABLED;
import static android.app.admin.DevicePolicyManager.NEARBY_STREAMING_NOT_CONTROLLED_BY_POLICY;
import static android.app.admin.DevicePolicyManager.NEARBY_STREAMING_SAME_MANAGED_ACCOUNT_ONLY;
-import static android.companion.virtual.VirtualDeviceParams.ACTIVITY_POLICY_DEFAULT_ALLOWED;
import static android.companion.virtual.VirtualDeviceParams.DEVICE_POLICY_CUSTOM;
import static android.companion.virtual.VirtualDeviceParams.DEVICE_POLICY_DEFAULT;
import static android.companion.virtual.VirtualDeviceParams.NAVIGATION_POLICY_DEFAULT_ALLOWED;
@@ -492,17 +491,10 @@ final class VirtualDeviceImpl extends IVirtualDevice.Stub
this, getDeviceId(), getPersistentDeviceId(), mParams.getName());
}
- if (Flags.dynamicPolicy()) {
- mActivityPolicyExemptions = new ArraySet<>(
- mParams.getDevicePolicy(POLICY_TYPE_ACTIVITY) == DEVICE_POLICY_DEFAULT
- ? mParams.getBlockedActivities()
- : mParams.getAllowedActivities());
- } else {
- mActivityPolicyExemptions =
- mParams.getDefaultActivityPolicy() == ACTIVITY_POLICY_DEFAULT_ALLOWED
- ? mParams.getBlockedActivities()
- : mParams.getAllowedActivities();
- }
+ mActivityPolicyExemptions = new ArraySet<>(
+ mParams.getDevicePolicy(POLICY_TYPE_ACTIVITY) == DEVICE_POLICY_DEFAULT
+ ? mParams.getBlockedActivities()
+ : mParams.getAllowedActivities());
if (Flags.vdmCustomIme() && mParams.getInputMethodComponent() != null) {
final String imeId = mParams.getInputMethodComponent().flattenToShortString();
@@ -587,12 +579,8 @@ final class VirtualDeviceImpl extends IVirtualDevice.Stub
@Override // Binder call
public @VirtualDeviceParams.DevicePolicy int getDevicePolicy(
@VirtualDeviceParams.PolicyType int policyType) {
- if (Flags.dynamicPolicy()) {
- synchronized (mVirtualDeviceLock) {
- return mDevicePolicies.get(policyType, DEVICE_POLICY_DEFAULT);
- }
- } else {
- return mParams.getDevicePolicy(policyType);
+ synchronized (mVirtualDeviceLock) {
+ return mDevicePolicies.get(policyType, DEVICE_POLICY_DEFAULT);
}
}
@@ -891,9 +879,6 @@ final class VirtualDeviceImpl extends IVirtualDevice.Stub
public void setDevicePolicy(@VirtualDeviceParams.DynamicPolicyType int policyType,
@VirtualDeviceParams.DevicePolicy int devicePolicy) {
checkCallerIsDeviceOwner();
- if (!Flags.dynamicPolicy()) {
- return;
- }
switch (policyType) {
case POLICY_TYPE_RECENTS:
@@ -924,23 +909,21 @@ final class VirtualDeviceImpl extends IVirtualDevice.Stub
}
break;
case POLICY_TYPE_CLIPBOARD:
- if (Flags.crossDeviceClipboard()) {
- if (devicePolicy == DEVICE_POLICY_CUSTOM
+ if (devicePolicy == DEVICE_POLICY_CUSTOM
&& mContext.checkCallingOrSelfPermission(ADD_TRUSTED_DISPLAY)
!= PackageManager.PERMISSION_GRANTED) {
- throw new SecurityException("Requires ADD_TRUSTED_DISPLAY permission to "
- + "set a custom clipboard policy.");
- }
- synchronized (mVirtualDeviceLock) {
- for (int i = 0; i < mVirtualDisplays.size(); i++) {
- VirtualDisplayWrapper wrapper = mVirtualDisplays.valueAt(i);
- if (!wrapper.isTrusted() && !wrapper.isMirror()) {
- throw new SecurityException("All displays must be trusted for "
- + "devices with custom clipboard policy.");
- }
+ throw new SecurityException("Requires ADD_TRUSTED_DISPLAY permission to "
+ + "set a custom clipboard policy.");
+ }
+ synchronized (mVirtualDeviceLock) {
+ for (int i = 0; i < mVirtualDisplays.size(); i++) {
+ VirtualDisplayWrapper wrapper = mVirtualDisplays.valueAt(i);
+ if (!wrapper.isTrusted() && !wrapper.isMirror()) {
+ throw new SecurityException("All displays must be trusted for "
+ + "devices with custom clipboard policy.");
}
- mDevicePolicies.put(policyType, devicePolicy);
}
+ mDevicePolicies.put(policyType, devicePolicy);
}
break;
case POLICY_TYPE_BLOCKED_ACTIVITY:
@@ -1443,15 +1426,11 @@ final class VirtualDeviceImpl extends IVirtualDevice.Stub
private GenericWindowPolicyController createWindowPolicyControllerLocked(
@NonNull Set<String> displayCategories) {
final boolean activityLaunchAllowedByDefault =
- Flags.dynamicPolicy()
- ? getDevicePolicy(POLICY_TYPE_ACTIVITY) == DEVICE_POLICY_DEFAULT
- : mParams.getDefaultActivityPolicy() == ACTIVITY_POLICY_DEFAULT_ALLOWED;
+ getDevicePolicy(POLICY_TYPE_ACTIVITY) == DEVICE_POLICY_DEFAULT;
final boolean crossTaskNavigationAllowedByDefault =
mParams.getDefaultNavigationPolicy() == NAVIGATION_POLICY_DEFAULT_ALLOWED;
final boolean showTasksInHostDeviceRecents =
getDevicePolicy(POLICY_TYPE_RECENTS) == DEVICE_POLICY_DEFAULT;
- final ComponentName homeComponent =
- Flags.vdmCustomHome() ? mParams.getHomeComponent() : null;
if (mActivityListenerAdapter == null) {
mActivityListenerAdapter = new GwpcActivityListener();
@@ -1472,7 +1451,7 @@ final class VirtualDeviceImpl extends IVirtualDevice.Stub
mActivityListenerAdapter,
displayCategories,
showTasksInHostDeviceRecents,
- homeComponent);
+ mParams.getHomeComponent());
gwpc.registerRunningAppsChangedListener(/* listener= */ this);
return gwpc;
}
diff --git a/services/companion/java/com/android/server/companion/virtual/VirtualDeviceManagerService.java b/services/companion/java/com/android/server/companion/virtual/VirtualDeviceManagerService.java
index 40726b4331e2..a60fa693350c 100644
--- a/services/companion/java/com/android/server/companion/virtual/VirtualDeviceManagerService.java
+++ b/services/companion/java/com/android/server/companion/virtual/VirtualDeviceManagerService.java
@@ -216,17 +216,14 @@ public class VirtualDeviceManagerService extends SystemService {
VIRTUAL_DEVICE_SERVICE_ORDERED_ID,
mActivityInterceptorCallback);
- if (Flags.persistentDeviceIdApi()) {
- CompanionDeviceManager cdm =
- getContext().getSystemService(CompanionDeviceManager.class);
- if (cdm != null) {
- onCdmAssociationsChanged(cdm.getAllAssociations(UserHandle.USER_ALL));
- cdm.addOnAssociationsChangedListener(getContext().getMainExecutor(),
- this::onCdmAssociationsChanged, UserHandle.USER_ALL);
- } else {
- Slog.e(TAG, "Failed to find CompanionDeviceManager. No CDM association info "
- + " will be available.");
- }
+ CompanionDeviceManager cdm = getContext().getSystemService(CompanionDeviceManager.class);
+ if (cdm != null) {
+ onCdmAssociationsChanged(cdm.getAllAssociations(UserHandle.USER_ALL));
+ cdm.addOnAssociationsChangedListener(getContext().getMainExecutor(),
+ this::onCdmAssociationsChanged, UserHandle.USER_ALL);
+ } else {
+ Slog.e(TAG, "Failed to find CompanionDeviceManager. No CDM association info "
+ + " will be available.");
}
if (android.companion.virtualdevice.flags.Flags.deviceAwareDisplayPower()) {
mStrongAuthTracker = new StrongAuthTracker(getContext());
@@ -338,14 +335,6 @@ public class VirtualDeviceManagerService extends SystemService {
final long identity = Binder.clearCallingIdentity();
try {
getContext().sendBroadcastAsUser(i, UserHandle.ALL);
-
- if (!Flags.persistentDeviceIdApi()) {
- synchronized (mVirtualDeviceManagerLock) {
- if (mVirtualDevices.size() == 0) {
- unregisterCdmAssociationListener();
- }
- }
- }
} finally {
Binder.restoreCallingIdentity(identity);
}
@@ -379,21 +368,6 @@ public class VirtualDeviceManagerService extends SystemService {
}
}
- @RequiresPermission(android.Manifest.permission.MANAGE_COMPANION_DEVICES)
- private void registerCdmAssociationListener() {
- final CompanionDeviceManager cdm = getContext().getSystemService(
- CompanionDeviceManager.class);
- cdm.addOnAssociationsChangedListener(getContext().getMainExecutor(),
- mCdmAssociationListener);
- }
-
- @RequiresPermission(android.Manifest.permission.MANAGE_COMPANION_DEVICES)
- private void unregisterCdmAssociationListener() {
- final CompanionDeviceManager cdm = getContext().getSystemService(
- CompanionDeviceManager.class);
- cdm.removeOnAssociationsChangedListener(mCdmAssociationListener);
- }
-
void onCdmAssociationsChanged(List<AssociationInfo> associations) {
ArrayMap<String, AssociationInfo> vdmAssociations = new ArrayMap<>();
for (int i = 0; i < associations.size(); ++i) {
@@ -479,9 +453,8 @@ public class VirtualDeviceManagerService extends SystemService {
AssociationInfo associationInfo = getAssociationInfo(packageName, associationId);
if (associationInfo == null) {
throw new IllegalArgumentException("No association with ID " + associationId);
- } else if (!VIRTUAL_DEVICE_COMPANION_DEVICE_PROFILES
- .contains(associationInfo.getDeviceProfile())
- && Flags.persistentDeviceIdApi()) {
+ } else if (!VIRTUAL_DEVICE_COMPANION_DEVICE_PROFILES.contains(
+ associationInfo.getDeviceProfile())) {
throw new IllegalArgumentException("Unsupported CDM Association device profile "
+ associationInfo.getDeviceProfile() + " for virtual device creation.");
}
@@ -522,14 +495,6 @@ public class VirtualDeviceManagerService extends SystemService {
Counter.logIncrement("virtual_devices.value_virtual_devices_created_count");
synchronized (mVirtualDeviceManagerLock) {
- if (!Flags.persistentDeviceIdApi() && mVirtualDevices.size() == 0) {
- final long callingId = Binder.clearCallingIdentity();
- try {
- registerCdmAssociationListener();
- } finally {
- Binder.restoreCallingIdentity(callingId);
- }
- }
mVirtualDevices.put(deviceId, virtualDevice);
}
diff --git a/services/core/java/com/android/server/am/ActivityManagerConstants.java b/services/core/java/com/android/server/am/ActivityManagerConstants.java
index 4b8770b3cd35..50876dafa819 100644
--- a/services/core/java/com/android/server/am/ActivityManagerConstants.java
+++ b/services/core/java/com/android/server/am/ActivityManagerConstants.java
@@ -277,7 +277,9 @@ final class ActivityManagerConstants extends ContentObserver {
private static final long DEFAULT_FOLLOW_UP_OOMADJ_UPDATE_WAIT_DURATION = 1000L;
/** The default value to {@link #KEY_FREEZER_CUTOFF_ADJ} */
- private static final int DEFAULT_FREEZER_CUTOFF_ADJ = ProcessList.CACHED_APP_MIN_ADJ;
+ private static final int DEFAULT_FREEZER_CUTOFF_ADJ =
+ Flags.prototypeAggressiveFreezing() ? ProcessList.HOME_APP_ADJ
+ : ProcessList.CACHED_APP_MIN_ADJ;
/**
* Same as {@link TEMPORARY_ALLOW_LIST_TYPE_FOREGROUND_SERVICE_NOT_ALLOWED}
diff --git a/services/core/java/com/android/server/am/flags.aconfig b/services/core/java/com/android/server/am/flags.aconfig
index cfcede8ee40d..d9be47193c89 100644
--- a/services/core/java/com/android/server/am/flags.aconfig
+++ b/services/core/java/com/android/server/am/flags.aconfig
@@ -270,6 +270,16 @@ flag {
}
flag {
+ name: "prototype_aggressive_freezing"
+ namespace: "backstage_power"
+ description: "Grant PROCESS_CAPABILITY_CPU_TIME to as many valid states and aggressively freeze other states"
+ bug: "370798593"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
+
+flag {
name: "add_modify_raw_oom_adj_service_level"
namespace: "backstage_power"
description: "Add a SERVICE_ADJ level to the modifyRawOomAdj method"
diff --git a/services/core/java/com/android/server/appop/DiscreteOpsDbHelper.java b/services/core/java/com/android/server/appop/DiscreteOpsDbHelper.java
index e4c36cc214e8..695032e6476c 100644
--- a/services/core/java/com/android/server/appop/DiscreteOpsDbHelper.java
+++ b/services/core/java/com/android/server/appop/DiscreteOpsDbHelper.java
@@ -110,7 +110,12 @@ class DiscreteOpsDbHelper extends SQLiteOpenHelper {
}
db.setTransactionSuccessful();
} finally {
- db.endTransaction();
+ try {
+ db.endTransaction();
+ } catch (SQLiteException exception) {
+ Slog.e(LOG_TAG, "Couldn't commit transaction when inserting discrete ops, database"
+ + " file size (bytes) : " + getDatabaseFile().length(), exception);
+ }
}
}
diff --git a/services/core/java/com/android/server/appop/DiscreteOpsSqlRegistry.java b/services/core/java/com/android/server/appop/DiscreteOpsSqlRegistry.java
index 4b3981cd4bc0..30c2a82296ca 100644
--- a/services/core/java/com/android/server/appop/DiscreteOpsSqlRegistry.java
+++ b/services/core/java/com/android/server/appop/DiscreteOpsSqlRegistry.java
@@ -43,6 +43,8 @@ import java.io.File;
import java.io.PrintWriter;
import java.text.SimpleDateFormat;
import java.time.Duration;
+import java.time.Instant;
+import java.time.temporal.ChronoUnit;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
@@ -176,6 +178,8 @@ public class DiscreteOpsSqlRegistry extends DiscreteOpsRegistry {
writeAndClearOldAccessHistory();
boolean assembleChains = attributionExemptPkgs != null;
IntArray opCodes = getAppOpCodes(filter, opNamesFilter);
+ beginTimeMillis = Math.max(beginTimeMillis, Instant.now().minus(sDiscreteHistoryCutoff,
+ ChronoUnit.MILLIS).toEpochMilli());
List<DiscreteOp> discreteOps = mDiscreteOpsDbHelper.getDiscreteOps(filter, uidFilter,
packageNameFilter, attributionTagFilter, opCodes, opFlagsFilter, beginTimeMillis,
endTimeMillis, -1, null);
diff --git a/services/core/java/com/android/server/display/feature/DisplayManagerFlags.java b/services/core/java/com/android/server/display/feature/DisplayManagerFlags.java
index 4e57d6791ff6..43aa6f46da78 100644
--- a/services/core/java/com/android/server/display/feature/DisplayManagerFlags.java
+++ b/services/core/java/com/android/server/display/feature/DisplayManagerFlags.java
@@ -258,6 +258,11 @@ public class DisplayManagerFlags {
Flags::subscribeGranularDisplayEvents
);
+ private final FlagState mBaseDensityForExternalDisplays = new FlagState(
+ Flags.FLAG_BASE_DENSITY_FOR_EXTERNAL_DISPLAYS,
+ Flags::baseDensityForExternalDisplays
+ );
+
/**
* @return {@code true} if 'port' is allowed in display layout configuration file.
*/
@@ -553,6 +558,14 @@ public class DisplayManagerFlags {
}
/**
+ * @return {@code true} if the flag for base density for external displays is enabled
+ */
+ public boolean isBaseDensityForExternalDisplaysEnabled() {
+ return mBaseDensityForExternalDisplays.isEnabled();
+ }
+
+
+ /**
* dumps all flagstates
* @param pw printWriter
*/
@@ -606,6 +619,7 @@ public class DisplayManagerFlags {
pw.println(" " + mDisplayListenerPerformanceImprovementsFlagState);
pw.println(" " + mSubscribeGranularDisplayEvents);
pw.println(" " + mEnableDisplayContentModeManagementFlagState);
+ pw.println(" " + mBaseDensityForExternalDisplays);
}
private static class FlagState {
diff --git a/services/core/java/com/android/server/display/feature/display_flags.aconfig b/services/core/java/com/android/server/display/feature/display_flags.aconfig
index afae07c88f8d..00a9dcb71b4b 100644
--- a/services/core/java/com/android/server/display/feature/display_flags.aconfig
+++ b/services/core/java/com/android/server/display/feature/display_flags.aconfig
@@ -471,3 +471,11 @@ flag {
purpose: PURPOSE_BUGFIX
}
}
+
+flag {
+ name: "base_density_for_external_displays"
+ namespace: "lse_desktop_experience"
+ description: "Feature flag for setting a base density for external displays."
+ bug: "382954433"
+ is_fixed_read_only: true
+}
diff --git a/services/core/java/com/android/server/hdmi/PowerStatusMonitorActionFromPlayback.java b/services/core/java/com/android/server/hdmi/PowerStatusMonitorActionFromPlayback.java
index d05ded5367d0..a2465d1c3a5d 100644
--- a/services/core/java/com/android/server/hdmi/PowerStatusMonitorActionFromPlayback.java
+++ b/services/core/java/com/android/server/hdmi/PowerStatusMonitorActionFromPlayback.java
@@ -28,21 +28,17 @@ import com.android.internal.annotations.VisibleForTesting;
*/
public class PowerStatusMonitorActionFromPlayback extends HdmiCecFeatureAction {
private static final String TAG = "PowerStatusMonitorActionFromPlayback";
+ // State that waits for next monitoring.
+ private static final int STATE_WAIT_FOR_NEXT_MONITORING = 1;
// State that waits for <Report Power Status> once sending <Give Device Power Status>
- // to all external devices.
- private static final int STATE_WAIT_FOR_REPORT_POWER_STATUS = 1;
- // State that waits for next monitoring.
- private static final int STATE_WAIT_FOR_NEXT_MONITORING = 2;
+ // to the TV.
+ private static final int STATE_WAIT_FOR_REPORT_POWER_STATUS = 2;
+
// Monitoring interval (60s)
@VisibleForTesting
protected static final int MONITORING_INTERVAL_MS = 60000;
// Timeout once sending <Give Device Power Status>
- private static final int REPORT_POWER_STATUS_TIMEOUT_MS = 5000;
- // Maximum number of retries in case the <Give Device Power Status> failed being sent or times
- // out.
- private static final int GIVE_POWER_STATUS_FOR_SOURCE_RETRIES = 5;
- private int mPowerStatusRetries = 0;
PowerStatusMonitorActionFromPlayback(HdmiCecLocalDevice source) {
super(source);
@@ -68,11 +64,10 @@ public class PowerStatusMonitorActionFromPlayback extends HdmiCecFeatureAction {
private boolean handleReportPowerStatusFromTv(HdmiCecMessage cmd) {
int powerStatus = cmd.getParams()[0] & 0xFF;
- mState = STATE_WAIT_FOR_NEXT_MONITORING;
- addTimer(mState, MONITORING_INTERVAL_MS);
if (powerStatus == POWER_STATUS_STANDBY) {
Slog.d(TAG, "TV reported it turned off, going to sleep.");
source().getService().standby();
+ finish();
return true;
}
return false;
@@ -80,34 +75,28 @@ public class PowerStatusMonitorActionFromPlayback extends HdmiCecFeatureAction {
@Override
void handleTimerEvent(int state) {
+ if (mState != state) {
+ return;
+ }
+
switch (mState) {
case STATE_WAIT_FOR_NEXT_MONITORING:
- mPowerStatusRetries = 0;
queryPowerStatus();
break;
case STATE_WAIT_FOR_REPORT_POWER_STATUS:
- handleTimeout();
+ mState = STATE_WAIT_FOR_NEXT_MONITORING;
+ addTimer(mState, MONITORING_INTERVAL_MS);
+ break;
+ default:
break;
}
}
private void queryPowerStatus() {
sendCommand(HdmiCecMessageBuilder.buildGiveDevicePowerStatus(getSourceAddress(),
- Constants.ADDR_TV));
+ Constants.ADDR_TV));
mState = STATE_WAIT_FOR_REPORT_POWER_STATUS;
- addTimer(mState, REPORT_POWER_STATUS_TIMEOUT_MS);
- }
-
- private void handleTimeout() {
- if (mState == STATE_WAIT_FOR_REPORT_POWER_STATUS) {
- if (mPowerStatusRetries++ < GIVE_POWER_STATUS_FOR_SOURCE_RETRIES) {
- queryPowerStatus();
- } else {
- mPowerStatusRetries = 0;
- mState = STATE_WAIT_FOR_NEXT_MONITORING;
- addTimer(mState, MONITORING_INTERVAL_MS);
- }
- }
+ addTimer(mState, HdmiConfig.TIMEOUT_MS);
}
}
diff --git a/services/core/java/com/android/server/inputmethod/ImeVisibilityStateComputer.java b/services/core/java/com/android/server/inputmethod/ImeVisibilityStateComputer.java
index 281db0ae9518..5fe8318dbb3f 100644
--- a/services/core/java/com/android/server/inputmethod/ImeVisibilityStateComputer.java
+++ b/services/core/java/com/android/server/inputmethod/ImeVisibilityStateComputer.java
@@ -40,6 +40,7 @@ import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.UserIdInt;
+import android.content.pm.PackageManager;
import android.content.res.Configuration;
import android.os.Binder;
import android.os.IBinder;
@@ -58,6 +59,7 @@ import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.inputmethod.SoftInputShowHideReason;
import com.android.server.LocalServices;
+import com.android.server.pm.UserManagerInternal;
import com.android.server.wm.WindowManagerInternal;
import java.io.PrintWriter;
@@ -78,6 +80,7 @@ public final class ImeVisibilityStateComputer {
private final int mUserId;
private final InputMethodManagerService mService;
+ private final UserManagerInternal mUserManagerInternal;
private final WindowManagerInternal mWindowManagerInternal;
final InputMethodManagerService.ImeDisplayValidator mImeDisplayValidator;
@@ -188,6 +191,7 @@ public final class ImeVisibilityStateComputer {
public ImeVisibilityStateComputer(@NonNull InputMethodManagerService service,
@UserIdInt int userId) {
this(service,
+ LocalServices.getService(UserManagerInternal.class),
LocalServices.getService(WindowManagerInternal.class),
LocalServices.getService(WindowManagerInternal.class)::getDisplayImePolicy,
new ImeVisibilityPolicy(), userId);
@@ -196,12 +200,15 @@ public final class ImeVisibilityStateComputer {
@VisibleForTesting
public ImeVisibilityStateComputer(@NonNull InputMethodManagerService service,
@NonNull Injector injector) {
- this(service, injector.getWmService(), injector.getImeValidator(),
- new ImeVisibilityPolicy(), injector.getUserId());
+ this(service, injector.getUserManagerService(), injector.getWmService(),
+ injector.getImeValidator(), new ImeVisibilityPolicy(), injector.getUserId());
}
interface Injector {
@NonNull
+ UserManagerInternal getUserManagerService();
+
+ @NonNull
WindowManagerInternal getWmService();
@NonNull
@@ -212,11 +219,13 @@ public final class ImeVisibilityStateComputer {
}
private ImeVisibilityStateComputer(InputMethodManagerService service,
+ UserManagerInternal userManagerInternal,
WindowManagerInternal wmService,
InputMethodManagerService.ImeDisplayValidator imeDisplayValidator,
ImeVisibilityPolicy imePolicy, @UserIdInt int userId) {
mUserId = userId;
mService = service;
+ mUserManagerInternal = userManagerInternal;
mWindowManagerInternal = wmService;
mImeDisplayValidator = imeDisplayValidator;
mPolicy = imePolicy;
@@ -337,7 +346,16 @@ public final class ImeVisibilityStateComputer {
@GuardedBy("ImfLock.class")
int computeImeDisplayId(@NonNull ImeTargetWindowState state, int displayId) {
- final int displayToShowIme = computeImeDisplayIdForTarget(displayId, mImeDisplayValidator);
+ final int displayToShowIme;
+ final PackageManager pm = mService.mContext.getPackageManager();
+ if (pm.hasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE)
+ && mUserManagerInternal.isVisibleBackgroundFullUser(mUserId)
+ && Flags.fallbackDisplayForSecondaryUserOnSecondaryDisplay()) {
+ displayToShowIme = mService.computeImeDisplayIdForVisibleBackgroundUserOnAutomotive(
+ displayId, mUserId, mImeDisplayValidator);
+ } else {
+ displayToShowIme = computeImeDisplayIdForTarget(displayId, mImeDisplayValidator);
+ }
state.setImeDisplayId(displayToShowIme);
final boolean imeHiddenByPolicy = displayToShowIme == INVALID_DISPLAY;
mPolicy.setImeHiddenByDisplayPolicy(imeHiddenByPolicy);
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
index 1f414ac07ba3..45c7cffd462b 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
@@ -2345,8 +2345,32 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl.
* {@link WindowManager#DISPLAY_IME_POLICY_HIDE}
*/
static int computeImeDisplayIdForTarget(int displayId, @NonNull ImeDisplayValidator checker) {
- if (displayId == DEFAULT_DISPLAY || displayId == INVALID_DISPLAY) {
- return FALLBACK_DISPLAY_ID;
+ return computeImeDisplayIdForTargetInner(displayId, checker, FALLBACK_DISPLAY_ID);
+ }
+
+ /**
+ * Find the display where the IME should be shown for a visible background user.
+ *
+ * @param displayId the ID of the display where the IME client target is
+ * @param userId the ID of the user who own the IME
+ * @param checker instance of {@link ImeDisplayValidator} which is used for
+ * checking display config to adjust the final target display
+ * @return the ID of the display where the IME should be shown or
+ * {@link android.view.Display#INVALID_DISPLAY} if the display has an ImePolicy of
+ * {@link WindowManager#DISPLAY_IME_POLICY_HIDE}
+ */
+ int computeImeDisplayIdForVisibleBackgroundUserOnAutomotive(
+ int displayId, @UserIdInt int userId, @NonNull ImeDisplayValidator checker) {
+ // Visible background user can be assigned to a secondary display, not the default display.
+ // The main display assigned to the user will be used as the fallback display.
+ final int mainDisplayId = mUserManagerInternal.getMainDisplayAssignedToUser(userId);
+ return computeImeDisplayIdForTargetInner(displayId, checker, mainDisplayId);
+ }
+
+ private static int computeImeDisplayIdForTargetInner(
+ int displayId, @NonNull ImeDisplayValidator checker, int fallbackDisplayId) {
+ if (displayId == fallbackDisplayId || displayId == INVALID_DISPLAY) {
+ return fallbackDisplayId;
}
// Show IME window on fallback display when the display doesn't support system decorations
@@ -2356,9 +2380,8 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl.
return displayId;
} else if (result == DISPLAY_IME_POLICY_HIDE) {
return INVALID_DISPLAY;
- } else {
- return FALLBACK_DISPLAY_ID;
}
+ return fallbackDisplayId;
}
@GuardedBy("ImfLock.class")
diff --git a/services/core/java/com/android/server/location/fudger/LocationFudger.java b/services/core/java/com/android/server/location/fudger/LocationFudger.java
index 28e21b71dcc9..39ee0cbeacce 100644
--- a/services/core/java/com/android/server/location/fudger/LocationFudger.java
+++ b/services/core/java/com/android/server/location/fudger/LocationFudger.java
@@ -68,6 +68,17 @@ public class LocationFudger {
private static final double MAX_LATITUDE =
90.0 - (1.0 / APPROXIMATE_METERS_PER_DEGREE_AT_EQUATOR);
+ // The average edge length in km of an S2 cell, indexed by S2 levels 0 to
+ // 13. Level 13 is the highest level used for coarsening.
+ // This approximation assumes the S2 cells are squares.
+ // For density-based coarsening, we use the edge to set the accuracy of the
+ // coarsened location.
+ // The values are from http://s2geometry.io/resources/s2cell_statistics.html
+ // We take square root of the average area.
+ private static final float[] S2_CELL_AVG_EDGE_PER_LEVEL = new float[] {
+ 9220.14f, 4610.07f, 2305.04f, 1152.52f, 576.26f, 288.13f, 144.06f,
+ 72.03f, 36.02f, 20.79f, 9f, 5.05f, 2.25f, 1.13f, 0.57f};
+
private final float mAccuracyM;
private final Clock mClock;
private final Random mRandom;
@@ -194,12 +205,14 @@ public class LocationFudger {
// The new algorithm is applied if and only if (1) the flag is on, (2) the cache has been
// set, and (3) the cache has successfully queried the provider for the default coarsening
// value.
+ float accuracy = mAccuracyM;
if (Flags.populationDensityProvider() && Flags.densityBasedCoarseLocations()
&& cacheCopy != null) {
if (cacheCopy.hasDefaultValue()) {
// New algorithm that snaps to the center of a S2 cell.
int level = cacheCopy.getCoarseningLevel(latitude, longitude);
coarsened = snapToCenterOfS2Cell(latitude, longitude, level);
+ accuracy = getS2CellApproximateEdge(level);
} else {
// Try to fetch the default value. The answer won't come in time, but will be used
// for the next location to coarsen.
@@ -214,7 +227,7 @@ public class LocationFudger {
coarse.setLatitude(coarsened[LAT_INDEX]);
coarse.setLongitude(coarsened[LNG_INDEX]);
- coarse.setAccuracy(Math.max(mAccuracyM, coarse.getAccuracy()));
+ coarse.setAccuracy(Math.max(accuracy, coarse.getAccuracy()));
synchronized (this) {
mCachedFineLocation = fine;
@@ -224,6 +237,19 @@ public class LocationFudger {
return coarse;
}
+ // Returns the average edge length in meters of an S2 cell at the given
+ // level. This is computed as if the S2 cell were a square. We do not need
+ // an exact value, only a rough approximation.
+ @VisibleForTesting
+ protected float getS2CellApproximateEdge(int level) {
+ if (level < 0) {
+ level = 0;
+ } else if (level >= S2_CELL_AVG_EDGE_PER_LEVEL.length) {
+ level = S2_CELL_AVG_EDGE_PER_LEVEL.length - 1;
+ }
+ return S2_CELL_AVG_EDGE_PER_LEVEL[level] * 1000;
+ }
+
// quantize location by snapping to a grid. this is the primary means of obfuscation. it
// gives nice consistent results and is very effective at hiding the true location (as
// long as you are not sitting on a grid boundary, which the random offsets mitigate).
diff --git a/services/core/java/com/android/server/locksettings/LockSettingsService.java b/services/core/java/com/android/server/locksettings/LockSettingsService.java
index 0d0cdd83cc73..a0e543300ce7 100644
--- a/services/core/java/com/android/server/locksettings/LockSettingsService.java
+++ b/services/core/java/com/android/server/locksettings/LockSettingsService.java
@@ -137,6 +137,7 @@ import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.messages.nano.SystemMessageProto.SystemMessage;
import com.android.internal.notification.SystemNotificationChannels;
+import com.android.internal.pm.RoSystemFeatures;
import com.android.internal.util.ArrayUtils;
import com.android.internal.util.DumpUtils;
import com.android.internal.util.IndentingPrintWriter;
@@ -1325,7 +1326,7 @@ public class LockSettingsService extends ILockSettings.Stub {
mContext.enforceCallingOrSelfPermission(
Manifest.permission.MANAGE_WEAK_ESCROW_TOKEN,
"Requires MANAGE_WEAK_ESCROW_TOKEN permission.");
- if (!mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE)) {
+ if (!RoSystemFeatures.hasFeatureAutomotive(mContext)) {
throw new IllegalArgumentException(
"Weak escrow token are only for automotive devices.");
}
@@ -3613,7 +3614,7 @@ public class LockSettingsService extends ILockSettings.Stub {
}
// Escrow tokens are enabled on automotive builds.
- if (mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE)) {
+ if (RoSystemFeatures.hasFeatureAutomotive(mContext)) {
return;
}
diff --git a/services/core/java/com/android/server/media/MediaSessionDeviceConfig.java b/services/core/java/com/android/server/media/MediaSessionDeviceConfig.java
index 7e747ce60785..2c6cebf6feb3 100644
--- a/services/core/java/com/android/server/media/MediaSessionDeviceConfig.java
+++ b/services/core/java/com/android/server/media/MediaSessionDeviceConfig.java
@@ -46,6 +46,7 @@ class MediaSessionDeviceConfig {
private static volatile long sMediaSessionCallbackFgsAllowlistDurationMs =
DEFAULT_MEDIA_SESSION_CALLBACK_FGS_ALLOWLIST_DURATION_MS;
+
/**
* Denotes the duration for which an app receiving a media session callback and the FGS started
* there can be temporarily allowed to have while-in-use permissions such as
@@ -58,6 +59,16 @@ class MediaSessionDeviceConfig {
private static volatile long sMediaSessionCallbackFgsWhileInUseTempAllowDurationMs =
DEFAULT_MEDIA_SESSION_CALLBACK_FGS_WHILE_IN_USE_TEMP_ALLOW_DURATION_MS;
+ /**
+ * Denotes the duration (in milliseconds) that a media session can remain in an engaged state,
+ * where it is only considered engaged if transitioning from active playback.
+ */
+ private static final String KEY_MEDIA_SESSION_TEMP_USER_ENGAGED_DURATION_MS =
+ "media_session_temp_user_engaged_duration_ms";
+ private static final long DEFAULT_MEDIA_SESSION_TEMP_USER_ENGAGED_DURATION_MS = 600_000;
+ private static volatile long sMediaSessionTempUserEngagedDurationMs =
+ DEFAULT_MEDIA_SESSION_TEMP_USER_ENGAGED_DURATION_MS;
+
private static void refresh(DeviceConfig.Properties properties) {
final Set<String> keys = properties.getKeyset();
properties.getKeyset().forEach(key -> {
@@ -73,6 +84,11 @@ class MediaSessionDeviceConfig {
case KEY_MEDIA_SESSION_CALLBACK_FGS_WHILE_IN_USE_TEMP_ALLOW_DURATION_MS:
sMediaSessionCallbackFgsWhileInUseTempAllowDurationMs = properties.getLong(key,
DEFAULT_MEDIA_SESSION_CALLBACK_FGS_WHILE_IN_USE_TEMP_ALLOW_DURATION_MS);
+ break;
+ case KEY_MEDIA_SESSION_TEMP_USER_ENGAGED_DURATION_MS:
+ sMediaSessionTempUserEngagedDurationMs = properties.getLong(key,
+ DEFAULT_MEDIA_SESSION_TEMP_USER_ENGAGED_DURATION_MS);
+ break;
}
});
}
@@ -110,6 +126,15 @@ class MediaSessionDeviceConfig {
return sMediaSessionCallbackFgsWhileInUseTempAllowDurationMs;
}
+ /**
+ * Returns the duration (in milliseconds) that a media session can remain in an engaged state,
+ * where it is only considered engaged if transitioning from active playback. After this
+ * duration, the session is disengaged until explicit user action triggers active playback.
+ */
+ public static long getMediaSessionTempUserEngagedDurationMs() {
+ return sMediaSessionTempUserEngagedDurationMs;
+ }
+
public static void dump(PrintWriter pw, String prefix) {
pw.println("Media session config:");
final String dumpFormat = prefix + " %s: [cur: %s, def: %s]";
@@ -125,5 +150,9 @@ class MediaSessionDeviceConfig {
KEY_MEDIA_SESSION_CALLBACK_FGS_WHILE_IN_USE_TEMP_ALLOW_DURATION_MS,
sMediaSessionCallbackFgsWhileInUseTempAllowDurationMs,
DEFAULT_MEDIA_SESSION_CALLBACK_FGS_WHILE_IN_USE_TEMP_ALLOW_DURATION_MS));
+ pw.println(TextUtils.formatSimple(dumpFormat,
+ KEY_MEDIA_SESSION_TEMP_USER_ENGAGED_DURATION_MS,
+ sMediaSessionTempUserEngagedDurationMs,
+ DEFAULT_MEDIA_SESSION_TEMP_USER_ENGAGED_DURATION_MS));
}
}
diff --git a/services/core/java/com/android/server/media/MediaSessionRecord.java b/services/core/java/com/android/server/media/MediaSessionRecord.java
index 5f7c86f7b670..d873075a19e4 100644
--- a/services/core/java/com/android/server/media/MediaSessionRecord.java
+++ b/services/core/java/com/android/server/media/MediaSessionRecord.java
@@ -264,15 +264,6 @@ public class MediaSessionRecord extends MediaSessionRecordImpl implements IBinde
*/
private static final int USER_DISENGAGED = 2;
- /**
- * Indicates the duration of the temporary engaged state, in milliseconds.
- *
- * <p>When switching to an {@linkplain PlaybackState#isActive() inactive state}, the user is
- * treated as temporarily engaged, meaning the corresponding session is only considered in an
- * engaged state for the duration of this timeout, and only if coming from an engaged state.
- */
- private static final int TEMP_USER_ENGAGED_TIMEOUT_MS = 600000;
-
public MediaSessionRecord(
int ownerPid,
int ownerUid,
@@ -605,7 +596,8 @@ public class MediaSessionRecord extends MediaSessionRecordImpl implements IBinde
if (mUserEngagementState == USER_TEMPORARILY_ENGAGED) {
mHandler.postDelayed(
mUserEngagementTimeoutExpirationRunnable,
- TEMP_USER_ENGAGED_TIMEOUT_MS);
+ MediaSessionDeviceConfig
+ .getMediaSessionTempUserEngagedDurationMs());
}
}
}
@@ -1079,7 +1071,8 @@ public class MediaSessionRecord extends MediaSessionRecordImpl implements IBinde
mUserEngagementState = newUserEngagedState;
if (newUserEngagedState == USER_TEMPORARILY_ENGAGED && !isGlobalPrioritySessionActive) {
mHandler.postDelayed(
- mUserEngagementTimeoutExpirationRunnable, TEMP_USER_ENGAGED_TIMEOUT_MS);
+ mUserEngagementTimeoutExpirationRunnable,
+ MediaSessionDeviceConfig.getMediaSessionTempUserEngagedDurationMs());
} else {
mHandler.removeCallbacks(mUserEngagementTimeoutExpirationRunnable);
}
diff --git a/services/core/java/com/android/server/media/quality/MediaQualityService.java b/services/core/java/com/android/server/media/quality/MediaQualityService.java
index d440d3ab3521..d9df09dad853 100644
--- a/services/core/java/com/android/server/media/quality/MediaQualityService.java
+++ b/services/core/java/com/android/server/media/quality/MediaQualityService.java
@@ -110,7 +110,7 @@ public class MediaQualityService extends SystemService {
if ((pp.getPackageName() != null && !pp.getPackageName().isEmpty()
&& !incomingPackageEqualsCallingUidPackage(pp.getPackageName()))
&& !hasGlobalPictureQualityServicePermission()) {
- notifyError(null, PictureProfile.ERROR_NO_PERMISSION,
+ notifyPictureProfileError(null, PictureProfile.ERROR_NO_PERMISSION,
Binder.getCallingUid(), Binder.getCallingPid());
}
@@ -136,7 +136,7 @@ public class MediaQualityService extends SystemService {
public void updatePictureProfile(String id, PictureProfile pp, UserHandle user) {
Long dbId = mPictureProfileTempIdMap.getKey(id);
if (!hasPermissionToUpdatePictureProfile(dbId, pp)) {
- notifyError(id, PictureProfile.ERROR_NO_PERMISSION,
+ notifyPictureProfileError(id, PictureProfile.ERROR_NO_PERMISSION,
Binder.getCallingUid(), Binder.getCallingPid());
}
@@ -165,7 +165,7 @@ public class MediaQualityService extends SystemService {
Long dbId = mPictureProfileTempIdMap.getKey(id);
if (!hasPermissionToRemovePictureProfile(dbId)) {
- notifyError(id, PictureProfile.ERROR_NO_PERMISSION,
+ notifyPictureProfileError(id, PictureProfile.ERROR_NO_PERMISSION,
Binder.getCallingUid(), Binder.getCallingPid());
}
@@ -176,7 +176,7 @@ public class MediaQualityService extends SystemService {
int result = db.delete(mMediaQualityDbHelper.PICTURE_QUALITY_TABLE_NAME, selection,
selectionArgs);
if (result == 0) {
- notifyError(id, PictureProfile.ERROR_INVALID_ARGUMENT,
+ notifyPictureProfileError(id, PictureProfile.ERROR_INVALID_ARGUMENT,
Binder.getCallingUid(), Binder.getCallingPid());
}
mPictureProfileTempIdMap.remove(dbId);
@@ -246,7 +246,7 @@ public class MediaQualityService extends SystemService {
public List<PictureProfile> getPictureProfilesByPackage(
String packageName, Bundle options, UserHandle user) {
if (!hasGlobalPictureQualityServicePermission()) {
- notifyError(null, PictureProfile.ERROR_NO_PERMISSION,
+ notifyPictureProfileError(null, PictureProfile.ERROR_NO_PERMISSION,
Binder.getCallingUid(), Binder.getCallingPid());
}
@@ -259,8 +259,7 @@ public class MediaQualityService extends SystemService {
}
@Override
- public List<PictureProfile> getAvailablePictureProfiles(
- Bundle options, UserHandle user) {
+ public List<PictureProfile> getAvailablePictureProfiles(Bundle options, UserHandle user) {
String packageName = getPackageOfCallingUid();
if (packageName != null) {
return getPictureProfilesByPackage(packageName, options, user);
@@ -271,7 +270,7 @@ public class MediaQualityService extends SystemService {
@Override
public boolean setDefaultPictureProfile(String profileId, UserHandle user) {
if (!hasGlobalPictureQualityServicePermission()) {
- notifyError(profileId, PictureProfile.ERROR_NO_PERMISSION,
+ notifyPictureProfileError(profileId, PictureProfile.ERROR_NO_PERMISSION,
Binder.getCallingUid(), Binder.getCallingPid());
}
// TODO: pass the profile ID to MediaQuality HAL when ready.
@@ -281,7 +280,7 @@ public class MediaQualityService extends SystemService {
@Override
public List<String> getPictureProfilePackageNames(UserHandle user) {
if (!hasGlobalPictureQualityServicePermission()) {
- notifyError(null, PictureProfile.ERROR_NO_PERMISSION,
+ notifyPictureProfileError(null, PictureProfile.ERROR_NO_PERMISSION,
Binder.getCallingUid(), Binder.getCallingPid());
}
String [] column = {BaseParameters.PARAMETER_PACKAGE};
@@ -294,13 +293,31 @@ public class MediaQualityService extends SystemService {
}
@Override
- public List<PictureProfileHandle> getPictureProfileHandle(String[] id, UserHandle user) {
- return new ArrayList<>();
+ public List<PictureProfileHandle> getPictureProfileHandle(String[] ids, UserHandle user) {
+ List<PictureProfileHandle> toReturn = new ArrayList<>();
+ for (String id : ids) {
+ Long key = mPictureProfileTempIdMap.getKey(id);
+ if (key != null) {
+ toReturn.add(new PictureProfileHandle(key));
+ } else {
+ toReturn.add(null);
+ }
+ }
+ return toReturn;
}
@Override
- public List<SoundProfileHandle> getSoundProfileHandle(String[] id, UserHandle user) {
- return new ArrayList<>();
+ public List<SoundProfileHandle> getSoundProfileHandle(String[] ids, UserHandle user) {
+ List<SoundProfileHandle> toReturn = new ArrayList<>();
+ for (String id : ids) {
+ Long key = mSoundProfileTempIdMap.getKey(id);
+ if (key != null) {
+ toReturn.add(new SoundProfileHandle(key));
+ } else {
+ toReturn.add(null);
+ }
+ }
+ return toReturn;
}
@Override
@@ -308,8 +325,8 @@ public class MediaQualityService extends SystemService {
if ((sp.getPackageName() != null && !sp.getPackageName().isEmpty()
&& !incomingPackageEqualsCallingUidPackage(sp.getPackageName()))
&& !hasGlobalPictureQualityServicePermission()) {
- //TODO: error handling
- return null;
+ notifySoundProfileError(null, SoundProfile.ERROR_NO_PERMISSION,
+ Binder.getCallingUid(), Binder.getCallingPid());
}
SQLiteDatabase db = mMediaQualityDbHelper.getWritableDatabase();
@@ -332,10 +349,9 @@ public class MediaQualityService extends SystemService {
@Override
public void updateSoundProfile(String id, SoundProfile sp, UserHandle user) {
Long dbId = mSoundProfileTempIdMap.getKey(id);
-
if (!hasPermissionToUpdateSoundProfile(dbId, sp)) {
- //TODO: error handling
- return;
+ notifySoundProfileError(id, SoundProfile.ERROR_NO_PERMISSION,
+ Binder.getCallingUid(), Binder.getCallingPid());
}
ContentValues values = getContentValues(dbId,
@@ -359,22 +375,23 @@ public class MediaQualityService extends SystemService {
@Override
public void removeSoundProfile(String id, UserHandle user) {
- Long intId = mSoundProfileTempIdMap.getKey(id);
- if (!hasPermissionToRemoveSoundProfile(intId)) {
- //TODO: error handling
- return;
+ Long dbId = mSoundProfileTempIdMap.getKey(id);
+ if (!hasPermissionToRemoveSoundProfile(dbId)) {
+ notifySoundProfileError(id, SoundProfile.ERROR_NO_PERMISSION,
+ Binder.getCallingUid(), Binder.getCallingPid());
}
- if (intId != null) {
+ if (dbId != null) {
SQLiteDatabase db = mMediaQualityDbHelper.getWritableDatabase();
String selection = BaseParameters.PARAMETER_ID + " = ?";
- String[] selectionArgs = {Long.toString(intId)};
+ String[] selectionArgs = {Long.toString(dbId)};
int result = db.delete(mMediaQualityDbHelper.SOUND_QUALITY_TABLE_NAME, selection,
selectionArgs);
if (result == 0) {
- //TODO: error handling
+ notifySoundProfileError(id, SoundProfile.ERROR_INVALID_ARGUMENT,
+ Binder.getCallingUid(), Binder.getCallingPid());
}
- mSoundProfileTempIdMap.remove(intId);
+ mSoundProfileTempIdMap.remove(dbId);
}
}
@@ -403,7 +420,7 @@ public class MediaQualityService extends SystemService {
return null;
}
if (count > 1) {
- Log.wtf(TAG, String.format(Locale.US, "%d entries found for id=%s"
+ Log.wtf(TAG, String.format(Locale.US, "%d entries found for name=%s"
+ " in %s. Should only ever be 0 or 1.", count, name,
mMediaQualityDbHelper.SOUND_QUALITY_TABLE_NAME));
return null;
@@ -441,8 +458,8 @@ public class MediaQualityService extends SystemService {
public List<SoundProfile> getSoundProfilesByPackage(
String packageName, Bundle options, UserHandle user) {
if (!hasGlobalSoundQualityServicePermission()) {
- //TODO: error handling
- return new ArrayList<>();
+ notifySoundProfileError(null, SoundProfile.ERROR_NO_PERMISSION,
+ Binder.getCallingUid(), Binder.getCallingPid());
}
boolean includeParams =
@@ -465,8 +482,8 @@ public class MediaQualityService extends SystemService {
@Override
public boolean setDefaultSoundProfile(String profileId, UserHandle user) {
if (!hasGlobalSoundQualityServicePermission()) {
- //TODO: error handling
- return false;
+ notifySoundProfileError(profileId, SoundProfile.ERROR_NO_PERMISSION,
+ Binder.getCallingUid(), Binder.getCallingPid());
}
// TODO: pass the profile ID to MediaQuality HAL when ready.
return false;
@@ -475,8 +492,8 @@ public class MediaQualityService extends SystemService {
@Override
public List<String> getSoundProfilePackageNames(UserHandle user) {
if (!hasGlobalSoundQualityServicePermission()) {
- //TODO: error handling
- return new ArrayList<>();
+ notifySoundProfileError(null, SoundProfile.ERROR_NO_PERMISSION,
+ Binder.getCallingUid(), Binder.getCallingPid());
}
String [] column = {BaseParameters.PARAMETER_NAME};
List<SoundProfile> soundProfiles = getSoundProfilesBasedOnConditions(column,
@@ -718,23 +735,48 @@ public class MediaQualityService extends SystemService {
}
}
- private void notifyError(String profileId, int errorCode, int uid, int pid) {
+ private void notifyPictureProfileError(String profileId, int errorCode, int uid, int pid) {
UserState userState = getOrCreateUserStateLocked(UserHandle.USER_SYSTEM);
- int n = userState.mCallbacks.beginBroadcast();
+ int n = userState.mPictureProfileCallbacks.beginBroadcast();
for (int i = 0; i < n; ++i) {
try {
- IPictureProfileCallback callback = userState.mCallbacks.getBroadcastItem(i);
- Pair<Integer, Integer> pidUid = userState.mCallbackPidUidMap.get(callback);
+ IPictureProfileCallback callback = userState.mPictureProfileCallbacks
+ .getBroadcastItem(i);
+ Pair<Integer, Integer> pidUid = userState.mPictureProfileCallbackPidUidMap
+ .get(callback);
if (pidUid.first == pid && pidUid.second == uid) {
- userState.mCallbacks.getBroadcastItem(i).onError(profileId, errorCode);
+ userState.mPictureProfileCallbacks.getBroadcastItem(i)
+ .onError(profileId, errorCode);
}
} catch (RemoteException e) {
Slog.e(TAG, "failed to report added input to callback", e);
}
}
- userState.mCallbacks.finishBroadcast();
+ userState.mPictureProfileCallbacks.finishBroadcast();
+ }
+
+ private void notifySoundProfileError(String profileId, int errorCode, int uid, int pid) {
+ UserState userState = getOrCreateUserStateLocked(UserHandle.USER_SYSTEM);
+ int n = userState.mSoundProfileCallbacks.beginBroadcast();
+
+ for (int i = 0; i < n; ++i) {
+ try {
+ ISoundProfileCallback callback = userState.mSoundProfileCallbacks
+ .getBroadcastItem(i);
+ Pair<Integer, Integer> pidUid = userState.mSoundProfileCallbackPidUidMap
+ .get(callback);
+
+ if (pidUid.first == pid && pidUid.second == uid) {
+ userState.mSoundProfileCallbacks.getBroadcastItem(i)
+ .onError(profileId, errorCode);
+ }
+ } catch (RemoteException e) {
+ Slog.e(TAG, "failed to report added input to callback", e);
+ }
+ }
+ userState.mSoundProfileCallbacks.finishBroadcast();
}
@Override
@@ -743,11 +785,18 @@ public class MediaQualityService extends SystemService {
int callingUid = Binder.getCallingUid();
UserState userState = getOrCreateUserStateLocked(Binder.getCallingUid());
- userState.mCallbackPidUidMap.put(callback, Pair.create(callingPid, callingUid));
+ userState.mPictureProfileCallbackPidUidMap.put(callback,
+ Pair.create(callingPid, callingUid));
}
@Override
public void registerSoundProfileCallback(final ISoundProfileCallback callback) {
+ int callingPid = Binder.getCallingPid();
+ int callingUid = Binder.getCallingUid();
+
+ UserState userState = getOrCreateUserStateLocked(Binder.getCallingUid());
+ userState.mSoundProfileCallbackPidUidMap.put(callback,
+ Pair.create(callingPid, callingUid));
}
@Override
@@ -781,8 +830,8 @@ public class MediaQualityService extends SystemService {
@Override
public List<String> getPictureProfileAllowList(UserHandle user) {
if (!hasGlobalPictureQualityServicePermission()) {
- //TODO: error handling
- return new ArrayList<>();
+ notifyPictureProfileError(null, PictureProfile.ERROR_NO_PERMISSION,
+ Binder.getCallingUid(), Binder.getCallingPid());
}
return new ArrayList<>();
}
@@ -790,15 +839,16 @@ public class MediaQualityService extends SystemService {
@Override
public void setPictureProfileAllowList(List<String> packages, UserHandle user) {
if (!hasGlobalPictureQualityServicePermission()) {
- //TODO: error handling
+ notifyPictureProfileError(null, PictureProfile.ERROR_NO_PERMISSION,
+ Binder.getCallingUid(), Binder.getCallingPid());
}
}
@Override
public List<String> getSoundProfileAllowList(UserHandle user) {
if (!hasGlobalSoundQualityServicePermission()) {
- //TODO: error handling
- return new ArrayList<>();
+ notifySoundProfileError(null, SoundProfile.ERROR_NO_PERMISSION,
+ Binder.getCallingUid(), Binder.getCallingPid());
}
return new ArrayList<>();
}
@@ -806,7 +856,8 @@ public class MediaQualityService extends SystemService {
@Override
public void setSoundProfileAllowList(List<String> packages, UserHandle user) {
if (!hasGlobalSoundQualityServicePermission()) {
- //TODO: error handling
+ notifySoundProfileError(null, SoundProfile.ERROR_NO_PERMISSION,
+ Binder.getCallingUid(), Binder.getCallingPid());
}
}
@@ -818,7 +869,8 @@ public class MediaQualityService extends SystemService {
@Override
public void setAutoPictureQualityEnabled(boolean enabled, UserHandle user) {
if (!hasGlobalPictureQualityServicePermission()) {
- //TODO: error handling
+ notifyPictureProfileError(null, PictureProfile.ERROR_NO_PERMISSION,
+ Binder.getCallingUid(), Binder.getCallingPid());
}
try {
@@ -849,7 +901,8 @@ public class MediaQualityService extends SystemService {
@Override
public void setSuperResolutionEnabled(boolean enabled, UserHandle user) {
if (!hasGlobalPictureQualityServicePermission()) {
- //TODO: error handling
+ notifyPictureProfileError(null, PictureProfile.ERROR_NO_PERMISSION,
+ Binder.getCallingUid(), Binder.getCallingPid());
}
try {
@@ -880,7 +933,8 @@ public class MediaQualityService extends SystemService {
@Override
public void setAutoSoundQualityEnabled(boolean enabled, UserHandle user) {
if (!hasGlobalSoundQualityServicePermission()) {
- //TODO: error handling
+ notifySoundProfileError(null, SoundProfile.ERROR_NO_PERMISSION,
+ Binder.getCallingUid(), Binder.getCallingPid());
}
try {
@@ -914,7 +968,7 @@ public class MediaQualityService extends SystemService {
}
}
- private class MediaQualityManagerCallbackList extends
+ private class MediaQualityManagerPictureProfileCallbackList extends
RemoteCallbackList<IPictureProfileCallback> {
@Override
public void onCallbackDied(IPictureProfileCallback callback) {
@@ -922,13 +976,27 @@ public class MediaQualityService extends SystemService {
}
}
+ private class MediaQualityManagerSoundProfileCallbackList extends
+ RemoteCallbackList<ISoundProfileCallback> {
+ @Override
+ public void onCallbackDied(ISoundProfileCallback callback) {
+ //todo
+ }
+ }
+
private final class UserState {
// A list of callbacks.
- private final MediaQualityManagerCallbackList mCallbacks =
- new MediaQualityManagerCallbackList();
+ private final MediaQualityManagerPictureProfileCallbackList mPictureProfileCallbacks =
+ new MediaQualityManagerPictureProfileCallbackList();
+
+ private final MediaQualityManagerSoundProfileCallbackList mSoundProfileCallbacks =
+ new MediaQualityManagerSoundProfileCallbackList();
+
+ private final Map<IPictureProfileCallback, Pair<Integer, Integer>>
+ mPictureProfileCallbackPidUidMap = new HashMap<>();
- private final Map<IPictureProfileCallback, Pair<Integer, Integer>> mCallbackPidUidMap =
- new HashMap<>();
+ private final Map<ISoundProfileCallback, Pair<Integer, Integer>>
+ mSoundProfileCallbackPidUidMap = new HashMap<>();
private UserState(Context context, int userId) {
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index 341038f878d9..05aa4134cbd5 100644
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -158,6 +158,7 @@ import static android.view.WindowManager.LayoutParams.TYPE_TOAST;
import static android.view.contentprotection.flags.Flags.rapidClearNotificationsByListenerAppOpEnabled;
import static com.android.internal.util.FrameworkStatsLog.DND_MODE_RULE;
+import static com.android.internal.util.FrameworkStatsLog.NOTIFICATION_BUNDLE_PREFERENCES;
import static com.android.internal.util.FrameworkStatsLog.PACKAGE_NOTIFICATION_CHANNEL_GROUP_PREFERENCES;
import static com.android.internal.util.FrameworkStatsLog.PACKAGE_NOTIFICATION_CHANNEL_PREFERENCES;
import static com.android.internal.util.FrameworkStatsLog.PACKAGE_NOTIFICATION_PREFERENCES;
@@ -362,6 +363,7 @@ import com.android.server.lights.LightsManager;
import com.android.server.notification.GroupHelper.NotificationAttributes;
import com.android.server.notification.ManagedServices.ManagedServiceInfo;
import com.android.server.notification.ManagedServices.UserProfiles;
+import com.android.server.notification.NotificationRecordLogger.NotificationPullStatsEvent;
import com.android.server.notification.NotificationRecordLogger.NotificationReportedEvent;
import com.android.server.notification.toast.CustomToastRecord;
import com.android.server.notification.toast.TextToastRecord;
@@ -2856,6 +2858,7 @@ public class NotificationManagerService extends SystemService {
mStatsManager.clearPullAtomCallback(PACKAGE_NOTIFICATION_PREFERENCES);
mStatsManager.clearPullAtomCallback(PACKAGE_NOTIFICATION_CHANNEL_PREFERENCES);
mStatsManager.clearPullAtomCallback(PACKAGE_NOTIFICATION_CHANNEL_GROUP_PREFERENCES);
+ mStatsManager.clearPullAtomCallback(NOTIFICATION_BUNDLE_PREFERENCES);
mStatsManager.clearPullAtomCallback(DND_MODE_RULE);
}
if (mAppOps != null) {
@@ -2960,6 +2963,12 @@ public class NotificationManagerService extends SystemService {
ConcurrentUtils.DIRECT_EXECUTOR,
mPullAtomCallback
);
+ mStatsManager.setPullAtomCallback(
+ NOTIFICATION_BUNDLE_PREFERENCES,
+ null, // use default PullAtomMetadata values
+ ConcurrentUtils.DIRECT_EXECUTOR,
+ mPullAtomCallback
+ );
}
private class StatsPullAtomCallbackImpl implements StatsManager.StatsPullAtomCallback {
@@ -2969,6 +2978,7 @@ public class NotificationManagerService extends SystemService {
case PACKAGE_NOTIFICATION_PREFERENCES:
case PACKAGE_NOTIFICATION_CHANNEL_PREFERENCES:
case PACKAGE_NOTIFICATION_CHANNEL_GROUP_PREFERENCES:
+ case NOTIFICATION_BUNDLE_PREFERENCES:
case DND_MODE_RULE:
return pullNotificationStates(atomTag, data);
default:
@@ -2980,8 +2990,15 @@ public class NotificationManagerService extends SystemService {
private int pullNotificationStates(int atomTag, List<StatsEvent> data) {
switch(atomTag) {
case PACKAGE_NOTIFICATION_PREFERENCES:
- mPreferencesHelper.pullPackagePreferencesStats(data,
- getAllUsersNotificationPermissions());
+ if (notificationClassificationUi()) {
+ Set<String> pkgs = mAssistants.getPackagesWithKeyTypeAdjustmentSettings();
+ mPreferencesHelper.pullPackagePreferencesStats(data,
+ getAllUsersNotificationPermissions(),
+ getPackageSpecificAdjustmentKeyTypes(pkgs));
+ } else {
+ mPreferencesHelper.pullPackagePreferencesStats(data,
+ getAllUsersNotificationPermissions());
+ }
break;
case PACKAGE_NOTIFICATION_CHANNEL_PREFERENCES:
mPreferencesHelper.pullPackageChannelPreferencesStats(data);
@@ -2989,6 +3006,11 @@ public class NotificationManagerService extends SystemService {
case PACKAGE_NOTIFICATION_CHANNEL_GROUP_PREFERENCES:
mPreferencesHelper.pullPackageChannelGroupPreferencesStats(data);
break;
+ case NOTIFICATION_BUNDLE_PREFERENCES:
+ if (notificationClassification() && notificationClassificationUi()) {
+ mAssistants.pullBundlePreferencesStats(data);
+ }
+ break;
case DND_MODE_RULE:
mZenModeHelper.pullRules(data);
break;
@@ -7481,6 +7503,24 @@ public class NotificationManagerService extends SystemService {
return allPermissions;
}
+ @VisibleForTesting
+ @FlaggedApi(android.app.Flags.FLAG_NOTIFICATION_CLASSIFICATION_UI)
+ protected @NonNull Map<String, Set<Integer>> getPackageSpecificAdjustmentKeyTypes(
+ Set<String> pkgs) {
+ ArrayMap<String, Set<Integer>> pkgToAllowedTypes = new ArrayMap<>();
+ for (String pkg : pkgs) {
+ int[] allowedTypesArray = mAssistants.getAllowedAdjustmentKeyTypesForPackage(pkg);
+ if (allowedTypesArray != null) {
+ Set<Integer> allowedTypes = new ArraySet<Integer>();
+ for (int i : allowedTypesArray) {
+ allowedTypes.add(i);
+ }
+ pkgToAllowedTypes.append(pkg, allowedTypes);
+ }
+ }
+ return pkgToAllowedTypes;
+ }
+
private void dumpJson(PrintWriter pw, @NonNull DumpFilter filter,
ArrayMap<Pair<Integer, String>, Pair<Boolean, Boolean>> pkgPermissions) {
JSONObject dump = new JSONObject();
@@ -12056,6 +12096,22 @@ public class NotificationManagerService extends SystemService {
}
@FlaggedApi(android.app.Flags.FLAG_NOTIFICATION_CLASSIFICATION_UI)
+ protected @NonNull Set<String> getPackagesWithKeyTypeAdjustmentSettings() {
+ if (notificationClassificationUi()) {
+ Set<String> packagesWithModifications = new ArraySet<String>();
+ synchronized (mLock) {
+ for (String pkg : mClassificationTypePackagesEnabledTypes.keySet()) {
+ if (mClassificationTypePackagesEnabledTypes.get(pkg) != null) {
+ packagesWithModifications.add(pkg);
+ }
+ }
+ }
+ return packagesWithModifications;
+ }
+ return new ArraySet<String>();
+ }
+
+ @FlaggedApi(android.app.Flags.FLAG_NOTIFICATION_CLASSIFICATION_UI)
protected @NonNull int[] getAllowedAdjustmentKeyTypesForPackage(String pkg) {
synchronized (mLock) {
if (notificationClassificationUi()) {
@@ -12656,6 +12712,32 @@ public class NotificationManagerService extends SystemService {
Slog.e(TAG, "unable to notify assistant (capabilities): " + info, ex);
}
}
+
+ /**
+ * Fills out {@link BundlePreferences} proto and wraps it in a {@link StatsEvent}.
+ */
+ @FlaggedApi(android.service.notification.Flags.FLAG_NOTIFICATION_CLASSIFICATION)
+ protected void pullBundlePreferencesStats(List<StatsEvent> events) {
+ boolean bundlesAllowed = true;
+ synchronized (mLock) {
+ List<String> unsupportedAdjustments = new ArrayList(
+ mNasUnsupported.getOrDefault(
+ UserHandle.getUserId(Binder.getCallingUid()),
+ new HashSet<>())
+ );
+ bundlesAllowed = !unsupportedAdjustments.contains(Adjustment.KEY_TYPE);
+ }
+
+ int[] allowedBundleTypes = getAllowedAdjustmentKeyTypes();
+
+ events.add(FrameworkStatsLog.buildStatsEvent(
+ NOTIFICATION_BUNDLE_PREFERENCES,
+ /* optional int32 event_id = 1 */
+ NotificationPullStatsEvent.NOTIFICATION_BUNDLE_PREFERENCES_PULLED.getId(),
+ /* optional bool bundles_allowed = 2 */ bundlesAllowed,
+ /* repeated android.stats.notification.BundleTypes allowed_bundle_types = 3 */
+ allowedBundleTypes));
+ }
}
/**
diff --git a/services/core/java/com/android/server/notification/NotificationRecordLogger.java b/services/core/java/com/android/server/notification/NotificationRecordLogger.java
index 3943aa583fee..6c0035b82a86 100644
--- a/services/core/java/com/android/server/notification/NotificationRecordLogger.java
+++ b/services/core/java/com/android/server/notification/NotificationRecordLogger.java
@@ -32,8 +32,6 @@ import android.service.notification.NotificationListenerService;
import android.service.notification.NotificationStats;
import android.util.Log;
-import com.android.internal.config.sysui.SystemUiSystemPropertiesFlags;
-import com.android.internal.config.sysui.SystemUiSystemPropertiesFlags.NotificationFlags;
import com.android.internal.logging.InstanceId;
import com.android.internal.logging.UiEvent;
import com.android.internal.logging.UiEventLogger;
@@ -368,6 +366,19 @@ interface NotificationRecordLogger {
}
}
+ enum NotificationPullStatsEvent implements UiEventLogger.UiEventEnum {
+ @UiEvent(doc = "Notification Bundle Preferences pulled.")
+ NOTIFICATION_BUNDLE_PREFERENCES_PULLED(2072);
+
+ private final int mId;
+ NotificationPullStatsEvent(int id) {
+ mId = id;
+ }
+ @Override public int getId() {
+ return mId;
+ }
+ }
+
/**
* A helper for extracting logging information from one or two NotificationRecords.
*/
diff --git a/services/core/java/com/android/server/notification/PreferencesHelper.java b/services/core/java/com/android/server/notification/PreferencesHelper.java
index 36eabae69b22..45b155049c72 100644
--- a/services/core/java/com/android/server/notification/PreferencesHelper.java
+++ b/services/core/java/com/android/server/notification/PreferencesHelper.java
@@ -16,6 +16,7 @@
package com.android.server.notification;
+import static android.app.Flags.notificationClassificationUi;
import static android.app.AppOpsManager.OP_SYSTEM_ALERT_WINDOW;
import static android.app.NotificationChannel.DEFAULT_CHANNEL_ID;
import static android.app.NotificationChannel.NEWS_ID;
@@ -2523,6 +2524,25 @@ public class PreferencesHelper implements RankingConfig {
*/
public void pullPackagePreferencesStats(List<StatsEvent> events,
ArrayMap<Pair<Integer, String>, Pair<Boolean, Boolean>> pkgPermissions) {
+ pullPackagePreferencesStats(events, pkgPermissions, new ArrayMap<String, Set<Integer>>());
+ }
+
+
+ /**
+ * Fills out {@link PackageNotificationPreferences} proto and wraps it in a {@link StatsEvent}.
+ * @param events Newly filled out StatsEvent protos are added to this list as output.
+ * @param pkgPermissions Maps from a pair representing a uid and package to a pair of booleans,
+ * where the first represents whether the notification permission was
+ * granted to that package, and the second represents whether the
+ * permission was user-set.
+ * @param pkgAdjustmentKeyTypes A map of package names that are not allowed to have their
+ * notifications classified into differently typed notification
+ * channels, and the channels that they're allowed to be
+ * classified into.
+ */
+ public void pullPackagePreferencesStats(List<StatsEvent> events,
+ ArrayMap<Pair<Integer, String>, Pair<Boolean, Boolean>> pkgPermissions,
+ @NonNull Map<String, Set<Integer>> pkgAdjustmentKeyTypes) {
Set<Pair<Integer, String>> pkgsWithPermissionsToHandle = null;
if (pkgPermissions != null) {
pkgsWithPermissionsToHandle = pkgPermissions.keySet();
@@ -2568,6 +2588,14 @@ public class PreferencesHelper implements RankingConfig {
isFsiPermissionUserSet(r.pkg, r.uid, fsiState,
currentPermissionFlags);
+ if (!notificationClassificationUi()
+ && pkgAdjustmentKeyTypes.keySet().size() > 0) {
+ Slog.w(TAG, "Pkg adjustment types improperly allowed without flag set");
+ }
+
+ int[] allowedBundleTypes =
+ getAllowedTypesForPackage(pkgAdjustmentKeyTypes, r.pkg);
+
events.add(FrameworkStatsLog.buildStatsEvent(
PACKAGE_NOTIFICATION_PREFERENCES,
/* optional int32 uid = 1 [(is_uid) = true] */ r.uid,
@@ -2576,7 +2604,9 @@ public class PreferencesHelper implements RankingConfig {
/* optional int32 user_locked_fields = 4 */ r.lockedAppFields,
/* optional bool user_set_importance = 5 */ importanceIsUserSet,
/* optional FsiState fsi_state = 6 */ fsiState,
- /* optional bool is_fsi_permission_user_set = 7 */ fsiIsUserSet));
+ /* optional bool is_fsi_permission_user_set = 7 */ fsiIsUserSet,
+ /* repeated int32 allowed_bundle_types = 8 */ allowedBundleTypes
+ ));
}
}
@@ -2587,6 +2617,10 @@ public class PreferencesHelper implements RankingConfig {
break;
}
pulledEvents++;
+
+ int[] allowedBundleTypes =
+ getAllowedTypesForPackage(pkgAdjustmentKeyTypes, p.second);
+
// Because all fields are required in FrameworkStatsLog.buildStatsEvent, we have
// to fill in default values for all the unspecified fields.
events.add(FrameworkStatsLog.buildStatsEvent(
@@ -2598,9 +2632,29 @@ public class PreferencesHelper implements RankingConfig {
/* optional int32 user_locked_fields = 4 */ DEFAULT_LOCKED_APP_FIELDS,
/* optional bool user_set_importance = 5 */ pkgPermissions.get(p).second,
/* optional FsiState fsi_state = 6 */ 0,
- /* optional bool is_fsi_permission_user_set = 7 */ false));
+ /* optional bool is_fsi_permission_user_set = 7 */ false,
+ /* repeated BundleTypes allowed_bundle_types = 8 */ allowedBundleTypes));
+ }
+ }
+ }
+
+ private int[] getAllowedTypesForPackage(@NonNull
+ Map<String, Set<Integer>> pkgAdjustmentKeyTypes,
+ String pkg) {
+ int[] allowedBundleTypes = new int[]{};
+ if (notificationClassificationUi()) {
+ if (pkgAdjustmentKeyTypes.containsKey(pkg)) {
+ // Convert from set to int[]
+ Set<Integer> types = pkgAdjustmentKeyTypes.get(pkg);
+ allowedBundleTypes = new int[types.size()];
+ int i = 0;
+ for (int val : types) {
+ allowedBundleTypes[i] = val;
+ i++;
+ }
}
}
+ return allowedBundleTypes;
}
/**
diff --git a/services/core/java/com/android/server/pm/InstallPackageHelper.java b/services/core/java/com/android/server/pm/InstallPackageHelper.java
index a0fbc008475c..4cca85590967 100644
--- a/services/core/java/com/android/server/pm/InstallPackageHelper.java
+++ b/services/core/java/com/android/server/pm/InstallPackageHelper.java
@@ -849,7 +849,9 @@ final class InstallPackageHelper {
int token;
if (mPm.mNextInstallToken < 0) mPm.mNextInstallToken = 1;
token = mPm.mNextInstallToken++;
- mPm.mRunningInstalls.put(token, request);
+ synchronized (mPm.mRunningInstalls) {
+ mPm.mRunningInstalls.put(token, request);
+ }
if (DEBUG_INSTALL) Log.v(TAG, "+ starting restore round-trip " + token);
@@ -3037,13 +3039,14 @@ final class InstallPackageHelper {
if (android.permission.flags.Flags.enhancedConfirmationModeApisEnabled()
&& android.security.Flags.extendEcmToAllSettings()) {
final int appId = request.getAppId();
- mPm.mHandler.post(() -> {
+ // TODO: b/388960315 - Implement a long-term solution to race condition
+ mPm.mHandler.postDelayed(() -> {
for (int userId : firstUserIds) {
// MODE_DEFAULT means that the app's guardedness will be decided lazily
setAccessRestrictedSettingsMode(packageName, appId, userId,
AppOpsManager.MODE_DEFAULT);
}
- });
+ }, 1000L);
} else {
// Apply restricted settings on potentially dangerous packages. Needs to happen
// after appOpsManager is notified of the new package
diff --git a/services/core/java/com/android/server/pm/PackageHandler.java b/services/core/java/com/android/server/pm/PackageHandler.java
index bc03b10b41b4..9916be680374 100644
--- a/services/core/java/com/android/server/pm/PackageHandler.java
+++ b/services/core/java/com/android/server/pm/PackageHandler.java
@@ -82,14 +82,20 @@ final class PackageHandler extends Handler {
case POST_INSTALL: {
if (DEBUG_INSTALL) Log.v(TAG, "Handling post-install for " + msg.arg1);
- InstallRequest request = mPm.mRunningInstalls.get(msg.arg1);
- final boolean didRestore = (msg.arg2 != 0);
- mPm.mRunningInstalls.delete(msg.arg1);
+ final InstallRequest request;
+ final int token;
+ final boolean didRestore;
+ synchronized (mPm.mRunningInstalls) {
+ request= mPm.mRunningInstalls.get(msg.arg1);
+ token = msg.arg1;
+ didRestore = (msg.arg2 != 0);
+ mPm.mRunningInstalls.delete(token);
+ }
if (request == null) {
if (DEBUG_INSTALL) {
Slog.i(TAG, "InstallRequest is null. Nothing to do for post-install "
- + "token " + msg.arg1);
+ + "token " + token);
}
break;
}
@@ -100,10 +106,10 @@ final class PackageHandler extends Handler {
mPm.handlePackagePostInstall(request, didRestore);
} else if (DEBUG_INSTALL) {
// No post-install when we run restore from installExistingPackageForUser
- Slog.i(TAG, "Nothing to do for post-install token " + msg.arg1);
+ Slog.i(TAG, "Nothing to do for post-install token " + token);
}
- Trace.asyncTraceEnd(TRACE_TAG_PACKAGE_MANAGER, "postInstall", msg.arg1);
+ Trace.asyncTraceEnd(TRACE_TAG_PACKAGE_MANAGER, "postInstall", token);
} break;
case DEFERRED_NO_KILL_POST_DELETE: {
CleanUpArgs args = (CleanUpArgs) msg.obj;
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index aaa4fdf12411..f60e086e7c5d 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -960,6 +960,7 @@ public class PackageManagerService implements PackageSender, TestUtilityService
// Stores a list of users whose package restrictions file needs to be updated
final ArraySet<Integer> mDirtyUsers = new ArraySet<>();
+ @GuardedBy("mRunningInstalls")
final SparseArray<InstallRequest> mRunningInstalls = new SparseArray<>();
int mNextInstallToken = 1; // nonzero; will be wrapped back to 1 when ++ overflows
@@ -3291,20 +3292,23 @@ public class PackageManagerService implements PackageSender, TestUtilityService
// and been launched through some other means, so it is not in a problematic
// state for observers to see the FIRST_LAUNCH signal.
mHandler.post(() -> {
- for (int i = 0; i < mRunningInstalls.size(); i++) {
- final InstallRequest installRequest = mRunningInstalls.valueAt(i);
- if (installRequest.getReturnCode() != PackageManager.INSTALL_SUCCEEDED) {
- continue;
- }
- if (packageName.equals(installRequest.getPkg().getPackageName())) {
- // right package; but is it for the right user?
- for (int uIndex = 0; uIndex < installRequest.getNewUsers().length; uIndex++) {
- if (userId == installRequest.getNewUsers()[uIndex]) {
- if (DEBUG_BACKUP) {
- Slog.i(TAG, "Package " + packageName
- + " being restored so deferring FIRST_LAUNCH");
+ synchronized (mRunningInstalls) {
+ for (int i = 0; i < mRunningInstalls.size(); i++) {
+ final InstallRequest installRequest = mRunningInstalls.valueAt(i);
+ if (installRequest.getReturnCode() != PackageManager.INSTALL_SUCCEEDED) {
+ continue;
+ }
+ final int[] newUsers = installRequest.getNewUsers();
+ if (packageName.equals(installRequest.getPkg().getPackageName())) {
+ // right package; but is it for the right user?
+ for (int uIndex = 0; uIndex < newUsers.length; uIndex++) {
+ if (userId == newUsers[uIndex]) {
+ if (DEBUG_BACKUP) {
+ Slog.i(TAG, "Package " + packageName
+ + " being restored so deferring FIRST_LAUNCH");
+ }
+ return;
}
- return;
}
}
}
diff --git a/services/core/java/com/android/server/pm/PackageSetting.java b/services/core/java/com/android/server/pm/PackageSetting.java
index fb16b862b275..a902f5ff372f 100644
--- a/services/core/java/com/android/server/pm/PackageSetting.java
+++ b/services/core/java/com/android/server/pm/PackageSetting.java
@@ -1848,8 +1848,10 @@ public class PackageSetting extends SettingBase implements PackageStateInternal
boolean manifestOverrideEnabled = (mPageSizeAppCompatFlags
& ApplicationInfo.PAGE_SIZE_APP_COMPAT_FLAG_MANIFEST_OVERRIDE_ENABLED) != 0;
boolean settingsOverrideEnabled = (mPageSizeAppCompatFlags
- & ApplicationInfo.PAGE_SIZE_APP_COMPAT_FLAG_MANIFEST_OVERRIDE_ENABLED) != 0;
- if (manifestOverrideEnabled || settingsOverrideEnabled) {
+ & ApplicationInfo.PAGE_SIZE_APP_COMPAT_FLAG_SETTINGS_OVERRIDE_ENABLED) != 0;
+ boolean settingsOverrideDisabled = (mPageSizeAppCompatFlags
+ & ApplicationInfo.PAGE_SIZE_APP_COMPAT_FLAG_SETTINGS_OVERRIDE_DISABLED) != 0;
+ if (manifestOverrideEnabled || settingsOverrideEnabled || settingsOverrideDisabled) {
return null;
}
diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java
index 81956fbb55e6..f4d4c5be035e 100644
--- a/services/core/java/com/android/server/pm/UserManagerService.java
+++ b/services/core/java/com/android/server/pm/UserManagerService.java
@@ -3312,13 +3312,18 @@ public class UserManagerService extends IUserManager.Stub {
}
}
-
-
private void sendUserInfoChangedBroadcast(@UserIdInt int userId) {
Intent changedIntent = new Intent(Intent.ACTION_USER_INFO_CHANGED);
changedIntent.putExtra(Intent.EXTRA_USER_HANDLE, userId);
changedIntent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
mContext.sendBroadcastAsUser(changedIntent, UserHandle.ALL);
+
+ // This intent allow system UI apps to refresh the content even if process was freezed.
+ Intent bgIntent = new Intent(Intent.ACTION_USER_INFO_CHANGED_BACKGROUND);
+ bgIntent.putExtra(Intent.EXTRA_USER_HANDLE, userId);
+ bgIntent.addFlags(Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND);
+ mContext.sendBroadcastAsUser(bgIntent, UserHandle.ALL,
+ Manifest.permission.MANAGE_USERS);
}
@Override
diff --git a/services/core/java/com/android/server/policy/DeviceStateProviderImpl.java b/services/core/java/com/android/server/policy/DeviceStateProviderImpl.java
index e75f852eb437..a755ee1cd0fe 100644
--- a/services/core/java/com/android/server/policy/DeviceStateProviderImpl.java
+++ b/services/core/java/com/android/server/policy/DeviceStateProviderImpl.java
@@ -45,6 +45,7 @@ import com.android.server.devicestate.DeviceStateProvider;
import com.android.server.input.InputManagerInternal;
import com.android.server.policy.devicestate.config.Conditions;
import com.android.server.policy.devicestate.config.DeviceStateConfig;
+import com.android.server.policy.devicestate.config.Flags;
import com.android.server.policy.devicestate.config.LidSwitchCondition;
import com.android.server.policy.devicestate.config.NumericRange;
import com.android.server.policy.devicestate.config.Properties;
@@ -140,7 +141,16 @@ public final class DeviceStateProviderImpl implements DeviceStateProvider,
private static final String PROPERTY_FEATURE_REAR_DISPLAY_OUTER_DEFAULT =
"com.android.server.policy.PROPERTY_FEATURE_REAR_DISPLAY_OUTER_DEFAULT";
-
+ // Deprecated flag definitions to maintain backwards compatibility.
+ private static final String FLAG_CANCEL_OVERRIDE_REQUESTS = "FLAG_CANCEL_OVERRIDE_REQUESTS";
+ private static final String FLAG_APP_INACCESSIBLE = "FLAG_APP_INACCESSIBLE";
+ private static final String FLAG_EMULATED_ONLY = "FLAG_EMULATED_ONLY";
+ private static final String FLAG_CANCEL_WHEN_REQUESTER_NOT_ON_TOP =
+ "FLAG_CANCEL_WHEN_REQUESTER_NOT_ON_TOP";
+ private static final String FLAG_UNSUPPORTED_WHEN_THERMAL_STATUS_CRITICAL =
+ "FLAG_UNSUPPORTED_WHEN_THERMAL_STATUS_CRITICAL";
+ private static final String FLAG_UNSUPPORTED_WHEN_POWER_SAVE_MODE =
+ "FLAG_UNSUPPORTED_WHEN_POWER_SAVE_MODE";
/** Interface that allows reading the device state configuration. */
interface ReadableConfig {
@@ -185,15 +195,29 @@ public final class DeviceStateProviderImpl implements DeviceStateProvider,
new HashSet<>();
Set<@DeviceState.DeviceStateProperties Integer> physicalProperties =
new HashSet<>();
- final Properties configFlags = stateConfig.getProperties();
- if (configFlags != null) {
- List<String> configPropertyStrings = configFlags.getProperty();
+ final Properties configProperties = stateConfig.getProperties();
+ if (configProperties != null) {
+ List<String> configPropertyStrings = configProperties.getProperty();
for (int i = 0; i < configPropertyStrings.size(); i++) {
final String configPropertyString = configPropertyStrings.get(i);
addPropertyByString(configPropertyString, systemProperties,
physicalProperties);
}
}
+
+ if (android.hardware.devicestate.feature.flags
+ .Flags.deviceStateConfigurationFlag()) {
+ // Parse through the deprecated flag configuration to keep compatibility.
+ final Flags configFlags = stateConfig.getFlags();
+ if (configFlags != null) {
+ List<String> configFlagStrings = configFlags.getFlag();
+ for (int i = 0; i < configFlagStrings.size(); i++) {
+ final String configFlagString = configFlagStrings.get(i);
+ addFlagByString(configFlagString, systemProperties);
+ }
+ }
+ }
+
DeviceState.Configuration deviceStateConfiguration =
new DeviceState.Configuration.Builder(state, name)
.setSystemProperties(systemProperties)
@@ -292,6 +316,34 @@ public final class DeviceStateProviderImpl implements DeviceStateProvider,
}
}
+ private static void addFlagByString(String flagString,
+ Set<@DeviceState.SystemDeviceStateProperties Integer> systemProperties) {
+ switch (flagString) {
+ case FLAG_APP_INACCESSIBLE:
+ systemProperties.add(DeviceState.PROPERTY_APP_INACCESSIBLE);
+ break;
+ case FLAG_EMULATED_ONLY:
+ systemProperties.add(DeviceState.PROPERTY_EMULATED_ONLY);
+ break;
+ case FLAG_CANCEL_OVERRIDE_REQUESTS:
+ systemProperties.add(DeviceState.PROPERTY_POLICY_CANCEL_OVERRIDE_REQUESTS);
+ break;
+ case FLAG_CANCEL_WHEN_REQUESTER_NOT_ON_TOP:
+ systemProperties.add(DeviceState.PROPERTY_POLICY_CANCEL_WHEN_REQUESTER_NOT_ON_TOP);
+ break;
+ case FLAG_UNSUPPORTED_WHEN_POWER_SAVE_MODE:
+ systemProperties.add(DeviceState.PROPERTY_POLICY_UNSUPPORTED_WHEN_POWER_SAVE_MODE);
+ break;
+ case FLAG_UNSUPPORTED_WHEN_THERMAL_STATUS_CRITICAL:
+ systemProperties.add(
+ DeviceState.PROPERTY_POLICY_UNSUPPORTED_WHEN_THERMAL_STATUS_CRITICAL);
+ break;
+ default:
+ Slog.w(TAG, "Parsed unknown flag with name: " + flagString);
+ break;
+ }
+ }
+
// Lock for internal state.
private final Object mLock = new Object();
private final Context mContext;
diff --git a/services/core/java/com/android/server/powerstats/PowerStatsService.java b/services/core/java/com/android/server/powerstats/PowerStatsService.java
index 89fa9b61b745..b723da3c7769 100644
--- a/services/core/java/com/android/server/powerstats/PowerStatsService.java
+++ b/services/core/java/com/android/server/powerstats/PowerStatsService.java
@@ -19,6 +19,7 @@ package com.android.server.powerstats;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.Context;
+import android.content.pm.PackageManager;
import android.hardware.power.stats.Channel;
import android.hardware.power.stats.EnergyConsumer;
import android.hardware.power.stats.EnergyConsumerResult;
@@ -37,6 +38,7 @@ import android.os.IPowerStatsService;
import android.os.Looper;
import android.os.PowerMonitor;
import android.os.PowerMonitorReadings;
+import android.os.Process;
import android.os.ResultReceiver;
import android.os.UserHandle;
import android.power.PowerStatsInternal;
@@ -83,7 +85,8 @@ public class PowerStatsService extends SystemService {
private static final String METER_CACHE_FILENAME = "meterCache";
private static final String MODEL_CACHE_FILENAME = "modelCache";
private static final String RESIDENCY_CACHE_FILENAME = "residencyCache";
- private static final long MAX_POWER_MONITOR_AGE_MILLIS = 30_000;
+ private static final long MAX_POWER_MONITOR_AGE_MILLIS = 20_000;
+ private static final long MAX_FINE_POWER_MONITOR_AGE_MILLIS = 250;
static final String KEY_POWER_MONITOR_API_ENABLED = "power_monitor_api_enabled";
@@ -203,6 +206,11 @@ public class PowerStatsService extends SystemService {
IntervalRandomNoiseGenerator createIntervalRandomNoiseGenerator() {
return new IntervalRandomNoiseGenerator(INTERVAL_RANDOM_NOISE_GENERATION_ALPHA);
}
+
+ boolean checkFinePowerMonitorsPermission(Context context, int callingUid) {
+ return context.checkPermission(android.Manifest.permission.ACCESS_FINE_POWER_MONITORS,
+ Process.INVALID_PID, callingUid) == PackageManager.PERMISSION_GRANTED;
+ }
}
private final IBinder mService = new IPowerStatsService.Stub() {
@@ -571,6 +579,7 @@ public class PowerStatsService extends SystemService {
private boolean mPowerMonitorApiEnabled = true;
private volatile PowerMonitor[] mPowerMonitors;
private PowerMonitorState[] mPowerMonitorStates;
+ private PowerMonitorState[] mFinePowerMonitorStates;
private IntervalRandomNoiseGenerator mIntervalRandomNoiseGenerator;
private void setPowerMonitorApiEnabled(boolean powerMonitorApiEnabled) {
@@ -578,6 +587,7 @@ public class PowerStatsService extends SystemService {
mPowerMonitorApiEnabled = powerMonitorApiEnabled;
mPowerMonitors = null;
mPowerMonitorStates = null;
+ mFinePowerMonitorStates = null;
}
}
@@ -598,6 +608,7 @@ public class PowerStatsService extends SystemService {
if (!mPowerMonitorApiEnabled) {
mPowerMonitors = new PowerMonitor[0];
mPowerMonitorStates = new PowerMonitorState[0];
+ mFinePowerMonitorStates = new PowerMonitorState[0];
return;
}
@@ -628,6 +639,7 @@ public class PowerStatsService extends SystemService {
}
mPowerMonitors = monitors.toArray(new PowerMonitor[monitors.size()]);
mPowerMonitorStates = states.toArray(new PowerMonitorState[monitors.size()]);
+ mFinePowerMonitorStates = states.toArray(new PowerMonitorState[monitors.size()]);
}
}
@@ -710,24 +722,38 @@ public class PowerStatsService extends SystemService {
ResultReceiver resultReceiver, int callingUid) {
ensurePowerMonitors();
+ @PowerMonitorReadings.PowerMonitorGranularity int granularity =
+ mInjector.checkFinePowerMonitorsPermission(mContext, callingUid)
+ ? PowerMonitorReadings.GRANULARITY_FINE
+ : PowerMonitorReadings.GRANULARITY_UNSPECIFIED;
+
+ PowerMonitorState[] allPowerMonitorStates;
+ long maxAge;
+ if (granularity == PowerMonitorReadings.GRANULARITY_FINE) {
+ allPowerMonitorStates = mFinePowerMonitorStates;
+ maxAge = MAX_FINE_POWER_MONITOR_AGE_MILLIS;
+ } else {
+ allPowerMonitorStates = mPowerMonitorStates;
+ maxAge = MAX_POWER_MONITOR_AGE_MILLIS;
+ }
+
long earliestTimestamp = Long.MAX_VALUE;
PowerMonitorState[] powerMonitorStates = new PowerMonitorState[powerMonitorIndices.length];
for (int i = 0; i < powerMonitorIndices.length; i++) {
int index = powerMonitorIndices[i];
- if (index < 0 || index >= mPowerMonitorStates.length) {
+ if (index < 0 || index >= allPowerMonitorStates.length) {
resultReceiver.send(IPowerStatsService.RESULT_UNSUPPORTED_POWER_MONITOR, null);
return;
}
- powerMonitorStates[i] = mPowerMonitorStates[index];
- if (mPowerMonitorStates[index] != null
- && mPowerMonitorStates[index].timestampMs < earliestTimestamp) {
- earliestTimestamp = mPowerMonitorStates[index].timestampMs;
+ powerMonitorStates[i] = allPowerMonitorStates[index];
+ if (allPowerMonitorStates[index] != null
+ && allPowerMonitorStates[index].timestampMs < earliestTimestamp) {
+ earliestTimestamp = allPowerMonitorStates[index].timestampMs;
}
}
- if (earliestTimestamp == 0
- || mClock.elapsedRealtime() - earliestTimestamp > MAX_POWER_MONITOR_AGE_MILLIS) {
+ if (earliestTimestamp == 0 || mClock.elapsedRealtime() - earliestTimestamp > maxAge) {
updateEnergyConsumers(powerMonitorStates);
updateEnergyMeasurements(powerMonitorStates);
mIntervalRandomNoiseGenerator.refresh();
@@ -765,6 +791,7 @@ public class PowerStatsService extends SystemService {
Bundle result = new Bundle();
result.putLongArray(IPowerStatsService.KEY_ENERGY, energy);
result.putLongArray(IPowerStatsService.KEY_TIMESTAMPS, timestamps);
+ result.putInt(IPowerStatsService.KEY_GRANULARITY, granularity);
resultReceiver.send(IPowerStatsService.RESULT_SUCCESS, result);
}
diff --git a/services/core/java/com/android/server/selinux/QuotaExceededException.java b/services/core/java/com/android/server/selinux/QuotaExceededException.java
new file mode 100644
index 000000000000..26d4d827af6b
--- /dev/null
+++ b/services/core/java/com/android/server/selinux/QuotaExceededException.java
@@ -0,0 +1,23 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.server.selinux;
+
+/** An exception raised when the quota has been reached.
+ *
+ * This exception is raised in EventLogCollection.add(). See QuotaLimiter
+ * for the implementation details.
+ */
+class QuotaExceededException extends Exception {}
diff --git a/services/core/java/com/android/server/selinux/SelinuxAuditLogsCollector.java b/services/core/java/com/android/server/selinux/SelinuxAuditLogsCollector.java
index 0aa705892376..54365ff03db0 100644
--- a/services/core/java/com/android/server/selinux/SelinuxAuditLogsCollector.java
+++ b/services/core/java/com/android/server/selinux/SelinuxAuditLogsCollector.java
@@ -28,10 +28,8 @@ import com.android.server.utils.Slogf;
import java.io.IOException;
import java.time.Instant;
-import java.util.ArrayDeque;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Queue;
+import java.util.AbstractCollection;
+import java.util.Iterator;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.function.Supplier;
import java.util.regex.Matcher;
@@ -57,6 +55,7 @@ class SelinuxAuditLogsCollector {
private final Supplier<String> mAuditDomainSupplier;
private final RateLimiter mRateLimiter;
private final QuotaLimiter mQuotaLimiter;
+ private EventLogCollection mEventCollection;
@VisibleForTesting Instant mLastWrite = Instant.MIN;
@@ -69,6 +68,7 @@ class SelinuxAuditLogsCollector {
mAuditDomainSupplier = auditDomainSupplier;
mRateLimiter = rateLimiter;
mQuotaLimiter = quotaLimiter;
+ mEventCollection = new EventLogCollection();
}
SelinuxAuditLogsCollector(RateLimiter rateLimiter, QuotaLimiter quotaLimiter) {
@@ -86,75 +86,72 @@ class SelinuxAuditLogsCollector {
mStopRequested.set(stopRequested);
}
- /**
- * Collect and push SELinux audit logs for the provided {@code tagCode}.
+ /** A Collection to work around EventLog.readEvents() constraints.
+ *
+ * This collection only supports add(). Any other method inherited from
+ * Collection will throw an UnsupportedOperationException exception.
*
- * @return true if the job was completed. If the job was interrupted, return false.
+ * This collection ensures that we are processing one event at a time and
+ * avoid collecting all the event objects before processing (e.g.,
+ * ArrayList), which could lead to an OOM situation.
*/
- boolean collect(int tagCode) {
- Queue<Event> logLines = new ArrayDeque<>();
- Instant latestTimestamp = collectLogLines(tagCode, logLines);
-
- boolean quotaExceeded = writeAuditLogs(logLines);
- if (quotaExceeded) {
- Slog.w(TAG, "Too many SELinux logs in the queue, I am giving up.");
- mLastWrite = latestTimestamp; // next run we will ignore all these logs.
- logLines.clear();
+ class EventLogCollection extends AbstractCollection<Event> {
+
+ SelinuxAuditLogBuilder mAuditLogBuilder;
+ int mAuditsWritten = 0;
+ Instant mLatestTimestamp;
+
+ void reset() {
+ mAuditsWritten = 0;
+ mLatestTimestamp = mLastWrite;
+ mAuditLogBuilder = new SelinuxAuditLogBuilder(mAuditDomainSupplier.get());
}
- return logLines.isEmpty();
- }
+ int getAuditsWritten() {
+ return mAuditsWritten;
+ }
- private Instant collectLogLines(int tagCode, Queue<Event> logLines) {
- List<Event> events = new ArrayList<>();
- try {
- EventLog.readEvents(new int[] {tagCode}, events);
- } catch (IOException e) {
- Slog.e(TAG, "Error reading event logs", e);
+ Instant getLatestTimestamp() {
+ return mLatestTimestamp;
}
- Instant latestTimestamp = mLastWrite;
- for (Event event : events) {
- Instant eventTime = Instant.ofEpochSecond(0, event.getTimeNanos());
- if (eventTime.isAfter(latestTimestamp)) {
- latestTimestamp = eventTime;
+ @Override
+ public Iterator<Event> iterator() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public int size() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public boolean add(Event event) {
+ if (mStopRequested.get()) {
+ throw new IllegalStateException(new InterruptedException());
}
+
+ Instant eventTime = Instant.ofEpochSecond(/* epochSecond= */ 0, event.getTimeNanos());
if (eventTime.compareTo(mLastWrite) <= 0) {
- continue;
+ return true;
}
Object eventData = event.getData();
if (!(eventData instanceof String)) {
- continue;
+ return true;
}
- logLines.add(event);
- }
- return latestTimestamp;
- }
-
- private boolean writeAuditLogs(Queue<Event> logLines) {
- final SelinuxAuditLogBuilder auditLogBuilder =
- new SelinuxAuditLogBuilder(mAuditDomainSupplier.get());
- int auditsWritten = 0;
-
- while (!mStopRequested.get() && !logLines.isEmpty()) {
- Event event = logLines.poll();
- String logLine = (String) event.getData();
- Instant logTime = Instant.ofEpochSecond(0, event.getTimeNanos());
+ String logLine = (String) eventData;
if (!SELINUX_MATCHER.reset(logLine).matches()) {
- continue;
+ return true;
}
- auditLogBuilder.reset(SELINUX_MATCHER.group("denial"));
- final SelinuxAuditLog auditLog = auditLogBuilder.build();
+ mAuditLogBuilder.reset(SELINUX_MATCHER.group("denial"));
+ final SelinuxAuditLog auditLog = mAuditLogBuilder.build();
if (auditLog == null) {
- continue;
+ return true;
}
if (!mQuotaLimiter.acquire()) {
- if (DEBUG) {
- Slogf.d(TAG, "Running out of quota after %d logs.", auditsWritten);
- }
- return true;
+ throw new IllegalStateException(new QuotaExceededException());
}
mRateLimiter.acquire();
@@ -169,16 +166,50 @@ class SelinuxAuditLogsCollector {
auditLog.mTClass,
auditLog.mPath,
auditLog.mPermissive);
- auditsWritten++;
- if (logTime.isAfter(mLastWrite)) {
- mLastWrite = logTime;
+ mAuditsWritten++;
+ if (eventTime.isAfter(mLatestTimestamp)) {
+ mLatestTimestamp = eventTime;
+ }
+
+ return true;
+ }
+ }
+
+ /**
+ * Collect and push SELinux audit logs for the provided {@code tagCode}.
+ *
+ * @return true if the job was completed. If the job was interrupted or
+ * failed because of IOException, return false.
+ * @throws QuotaExceededException if it ran out of quota.
+ */
+ boolean collect(int tagCode) throws QuotaExceededException {
+ mEventCollection.reset();
+ try {
+ EventLog.readEvents(new int[] {tagCode}, mEventCollection);
+ } catch (IllegalStateException e) {
+ if (e.getCause() instanceof QuotaExceededException) {
+ if (DEBUG) {
+ Slogf.d(TAG, "Running out of quota after %d logs.",
+ mEventCollection.getAuditsWritten());
+ }
+ // next run we will ignore all these logs.
+ mLastWrite = mEventCollection.getLatestTimestamp();
+ throw (QuotaExceededException) e.getCause();
+ } else if (e.getCause() instanceof InterruptedException) {
+ mLastWrite = mEventCollection.getLatestTimestamp();
+ return false;
}
+ throw e;
+ } catch (IOException e) {
+ Slog.e(TAG, "Error reading event logs", e);
+ return false;
}
+ mLastWrite = mEventCollection.getLatestTimestamp();
if (DEBUG) {
- Slogf.d(TAG, "Written %d logs", auditsWritten);
+ Slogf.d(TAG, "Written %d logs", mEventCollection.getAuditsWritten());
}
- return false;
+ return true;
}
}
diff --git a/services/core/java/com/android/server/selinux/SelinuxAuditLogsJob.java b/services/core/java/com/android/server/selinux/SelinuxAuditLogsJob.java
index 0092c3797156..e55e5900f265 100644
--- a/services/core/java/com/android/server/selinux/SelinuxAuditLogsJob.java
+++ b/services/core/java/com/android/server/selinux/SelinuxAuditLogsJob.java
@@ -51,8 +51,12 @@ final class SelinuxAuditLogsJob {
return;
}
mIsRunning.set(true);
- boolean done = mAuditLogsCollector.collect(SelinuxAuditLogsService.AUDITD_TAG_CODE);
- if (done) {
+ try {
+ boolean done = mAuditLogsCollector.collect(SelinuxAuditLogsService.AUDITD_TAG_CODE);
+ if (done) {
+ jobService.jobFinished(params, /* wantsReschedule= */ false);
+ }
+ } catch (QuotaExceededException e) {
jobService.jobFinished(params, /* wantsReschedule= */ false);
}
mIsRunning.set(false);
diff --git a/services/core/java/com/android/server/timezonedetector/Environment.java b/services/core/java/com/android/server/timezonedetector/Environment.java
new file mode 100644
index 000000000000..795fb02373ff
--- /dev/null
+++ b/services/core/java/com/android/server/timezonedetector/Environment.java
@@ -0,0 +1,80 @@
+/*
+ * Copyright 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.timezonedetector;
+
+import android.annotation.CurrentTimeMillisLong;
+import android.annotation.ElapsedRealtimeLong;
+import android.annotation.NonNull;
+
+import com.android.server.SystemTimeZone;
+
+import java.io.PrintWriter;
+
+/**
+ * Used by the time zone detector code to interact with device state besides that available from
+ * {@link ServiceConfigAccessor}. It can be faked for testing.
+ */
+public interface Environment {
+
+ /**
+ * Returns the device's currently configured time zone. May return an empty string.
+ */
+ @NonNull
+ String getDeviceTimeZone();
+
+ /**
+ * Returns the confidence of the device's current time zone.
+ */
+ @SystemTimeZone.TimeZoneConfidence
+ int getDeviceTimeZoneConfidence();
+
+ /**
+ * Sets the device's time zone, associated confidence, and records a debug log entry.
+ */
+ void setDeviceTimeZoneAndConfidence(
+ @NonNull String zoneId, @SystemTimeZone.TimeZoneConfidence int confidence,
+ @NonNull String logInfo);
+
+ /**
+ * Returns the time according to the elapsed realtime clock, the same as {@link
+ * android.os.SystemClock#elapsedRealtime()}.
+ */
+ @ElapsedRealtimeLong
+ long elapsedRealtimeMillis();
+
+ /**
+ * Returns the current time in milliseconds, the same as
+ * {@link java.lang.System#currentTimeMillis()}.
+ */
+ @CurrentTimeMillisLong
+ long currentTimeMillis();
+
+ /**
+ * Adds a standalone entry to the time zone debug log.
+ */
+ void addDebugLogEntry(@NonNull String logMsg);
+
+ /**
+ * Dumps the time zone debug log to the supplied {@link PrintWriter}.
+ */
+ void dumpDebugLog(PrintWriter printWriter);
+
+ /**
+ * Requests that the supplied runnable be invoked asynchronously.
+ */
+ void runAsync(@NonNull Runnable runnable);
+}
diff --git a/services/core/java/com/android/server/timezonedetector/EnvironmentImpl.java b/services/core/java/com/android/server/timezonedetector/EnvironmentImpl.java
index 449b41a09c51..8491b4818c2e 100644
--- a/services/core/java/com/android/server/timezonedetector/EnvironmentImpl.java
+++ b/services/core/java/com/android/server/timezonedetector/EnvironmentImpl.java
@@ -16,6 +16,7 @@
package com.android.server.timezonedetector;
+import android.annotation.CurrentTimeMillisLong;
import android.annotation.ElapsedRealtimeLong;
import android.annotation.NonNull;
import android.os.Handler;
@@ -31,9 +32,9 @@ import java.io.PrintWriter;
import java.util.Objects;
/**
- * The real implementation of {@link TimeZoneDetectorStrategyImpl.Environment}.
+ * The real implementation of {@link Environment}.
*/
-final class EnvironmentImpl implements TimeZoneDetectorStrategyImpl.Environment {
+final class EnvironmentImpl implements Environment {
private static final String TIMEZONE_PROPERTY = "persist.sys.timezone";
@@ -69,6 +70,11 @@ final class EnvironmentImpl implements TimeZoneDetectorStrategyImpl.Environment
}
@Override
+ public @CurrentTimeMillisLong long currentTimeMillis() {
+ return System.currentTimeMillis();
+ }
+
+ @Override
public void addDebugLogEntry(@NonNull String logMsg) {
SystemTimeZone.addDebugLogEntry(logMsg);
}
diff --git a/services/core/java/com/android/server/timezonedetector/NotifyingTimeZoneChangeListener.java b/services/core/java/com/android/server/timezonedetector/NotifyingTimeZoneChangeListener.java
index 2e73829ca143..cf85a9a6a706 100644
--- a/services/core/java/com/android/server/timezonedetector/NotifyingTimeZoneChangeListener.java
+++ b/services/core/java/com/android/server/timezonedetector/NotifyingTimeZoneChangeListener.java
@@ -29,6 +29,7 @@ import static com.android.server.timezonedetector.TimeZoneDetectorStrategy.ORIGI
import android.annotation.DurationMillisLong;
import android.annotation.IntDef;
+import android.annotation.NonNull;
import android.annotation.RequiresPermission;
import android.annotation.UserIdInt;
import android.app.ActivityManagerInternal;
@@ -44,7 +45,6 @@ import android.icu.text.DateFormat;
import android.icu.text.SimpleDateFormat;
import android.icu.util.TimeZone;
import android.os.Handler;
-import android.os.SystemClock;
import android.os.UserHandle;
import android.util.IndentingPrintWriter;
import android.util.Log;
@@ -153,6 +153,9 @@ public class NotifyingTimeZoneChangeListener implements TimeZoneChangeListener {
}
};
+ @NonNull
+ private final Environment mEnvironment;
+
private final Object mConfigurationLock = new Object();
@GuardedBy("mConfigurationLock")
private ConfigurationInternal mConfigurationInternal;
@@ -170,12 +173,14 @@ public class NotifyingTimeZoneChangeListener implements TimeZoneChangeListener {
/** Create and initialise a new {@code TimeZoneChangeTrackerImpl} */
@RequiresPermission("android.permission.INTERACT_ACROSS_USERS_FULL")
public static NotifyingTimeZoneChangeListener create(Handler handler, Context context,
- ServiceConfigAccessor serviceConfigAccessor) {
+ ServiceConfigAccessor serviceConfigAccessor,
+ @NonNull Environment environment) {
NotifyingTimeZoneChangeListener changeTracker =
new NotifyingTimeZoneChangeListener(handler,
context,
serviceConfigAccessor,
- context.getSystemService(NotificationManager.class));
+ context.getSystemService(NotificationManager.class),
+ environment);
// Pretend there was an update to initialize configuration.
changeTracker.handleConfigurationUpdate();
@@ -184,9 +189,9 @@ public class NotifyingTimeZoneChangeListener implements TimeZoneChangeListener {
}
@VisibleForTesting
- NotifyingTimeZoneChangeListener(
- Handler handler, Context context, ServiceConfigAccessor serviceConfigAccessor,
- NotificationManager notificationManager) {
+ NotifyingTimeZoneChangeListener(Handler handler, Context context,
+ ServiceConfigAccessor serviceConfigAccessor, NotificationManager notificationManager,
+ @NonNull Environment environment) {
mHandler = Objects.requireNonNull(handler);
mContext = Objects.requireNonNull(context);
mServiceConfigAccessor = Objects.requireNonNull(serviceConfigAccessor);
@@ -194,6 +199,7 @@ public class NotifyingTimeZoneChangeListener implements TimeZoneChangeListener {
this::handleConfigurationUpdate);
mActivityManagerInternal = LocalServices.getService(ActivityManagerInternal.class);
mNotificationManager = notificationManager;
+ mEnvironment = Objects.requireNonNull(environment);
}
@RequiresPermission("android.permission.INTERACT_ACROSS_USERS_FULL")
@@ -420,7 +426,7 @@ public class NotifyingTimeZoneChangeListener implements TimeZoneChangeListener {
if (!changeEvent.getOldZoneId().equals(lastChangeEvent.getNewZoneId())) {
int changeEventId = mNextChangeEventId.getAndIncrement();
TimeZoneChangeEvent syntheticChangeEvent = new TimeZoneChangeEvent(
- SystemClock.elapsedRealtime(), System.currentTimeMillis(),
+ mEnvironment.elapsedRealtimeMillis(), mEnvironment.currentTimeMillis(),
ORIGIN_UNKNOWN, UserHandle.USER_NULL, lastChangeEvent.getNewZoneId(),
changeEvent.getOldZoneId(), 0, "Synthetic");
TimeZoneChangeRecord syntheticTrackedChangeEvent =
diff --git a/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorStrategyImpl.java b/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorStrategyImpl.java
index b2b06b0af5fa..042d81ab6885 100644
--- a/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorStrategyImpl.java
+++ b/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorStrategyImpl.java
@@ -25,7 +25,6 @@ import static android.app.timezonedetector.TelephonyTimeZoneSuggestion.QUALITY_S
import static com.android.server.SystemTimeZone.TIME_ZONE_CONFIDENCE_HIGH;
import static com.android.server.SystemTimeZone.TIME_ZONE_CONFIDENCE_LOW;
-import android.annotation.ElapsedRealtimeLong;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.RequiresPermission;
@@ -54,7 +53,6 @@ import com.android.server.SystemTimeZone.TimeZoneConfidence;
import com.android.server.flags.Flags;
import com.android.server.timezonedetector.ConfigurationInternal.DetectionMode;
-import java.io.PrintWriter;
import java.time.Duration;
import java.util.ArrayList;
import java.util.List;
@@ -67,55 +65,6 @@ import java.util.Objects;
*/
public final class TimeZoneDetectorStrategyImpl implements TimeZoneDetectorStrategy {
- /**
- * Used by {@link TimeZoneDetectorStrategyImpl} to interact with device state besides that
- * available from {@link #mServiceConfigAccessor}. It can be faked for testing.
- */
- @VisibleForTesting
- public interface Environment {
-
- /**
- * Returns the device's currently configured time zone. May return an empty string.
- */
- @NonNull
- String getDeviceTimeZone();
-
- /**
- * Returns the confidence of the device's current time zone.
- */
- @TimeZoneConfidence
- int getDeviceTimeZoneConfidence();
-
- /**
- * Sets the device's time zone, associated confidence, and records a debug log entry.
- */
- void setDeviceTimeZoneAndConfidence(
- @NonNull String zoneId, @TimeZoneConfidence int confidence,
- @NonNull String logInfo);
-
- /**
- * Returns the time according to the elapsed realtime clock, the same as {@link
- * android.os.SystemClock#elapsedRealtime()}.
- */
- @ElapsedRealtimeLong
- long elapsedRealtimeMillis();
-
- /**
- * Adds a standalone entry to the time zone debug log.
- */
- void addDebugLogEntry(@NonNull String logMsg);
-
- /**
- * Dumps the time zone debug log to the supplied {@link PrintWriter}.
- */
- void dumpDebugLog(PrintWriter printWriter);
-
- /**
- * Requests that the supplied runnable be invoked asynchronously.
- */
- void runAsync(@NonNull Runnable runnable);
- }
-
private static final String LOG_TAG = TimeZoneDetectorService.TAG;
private static final boolean DBG = TimeZoneDetectorService.DBG;
@@ -263,10 +212,10 @@ public final class TimeZoneDetectorStrategyImpl implements TimeZoneDetectorStrat
public static TimeZoneDetectorStrategyImpl create(
@NonNull Context context, @NonNull Handler handler,
@NonNull ServiceConfigAccessor serviceConfigAccessor) {
-
Environment environment = new EnvironmentImpl(handler);
TimeZoneChangeListener changeEventTracker =
- NotifyingTimeZoneChangeListener.create(handler, context, serviceConfigAccessor);
+ NotifyingTimeZoneChangeListener.create(handler, context, serviceConfigAccessor,
+ environment);
return new TimeZoneDetectorStrategyImpl(
serviceConfigAccessor, environment, changeEventTracker);
}
diff --git a/services/core/java/com/android/server/wm/ActivityStarter.java b/services/core/java/com/android/server/wm/ActivityStarter.java
index 0ab2ffe3e298..bdbd0d15d982 100644
--- a/services/core/java/com/android/server/wm/ActivityStarter.java
+++ b/services/core/java/com/android/server/wm/ActivityStarter.java
@@ -1964,7 +1964,6 @@ class ActivityStarter {
if (mLastStartActivityRecord != null) {
targetTaskTop.mLaunchSourceType = mLastStartActivityRecord.mLaunchSourceType;
}
- targetTaskTop.mTransitionController.collect(targetTaskTop);
recordTransientLaunchIfNeeded(targetTaskTop);
// Recycle the target task for this launch.
startResult =
diff --git a/services/core/java/com/android/server/wm/BackNavigationController.java b/services/core/java/com/android/server/wm/BackNavigationController.java
index 37575f00363e..ab32e54b92dd 100644
--- a/services/core/java/com/android/server/wm/BackNavigationController.java
+++ b/services/core/java/com/android/server/wm/BackNavigationController.java
@@ -36,6 +36,7 @@ import static com.android.server.wm.BackNavigationProto.SHOW_WALLPAPER;
import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_PREDICT_BACK;
import static com.android.server.wm.WindowContainer.SYNC_STATE_NONE;
+import android.annotation.BinderThread;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.Context;
@@ -44,6 +45,7 @@ import android.content.res.ResourceId;
import android.graphics.Point;
import android.graphics.Rect;
import android.os.Bundle;
+import android.os.IBinder;
import android.os.RemoteCallback;
import android.os.RemoteException;
import android.os.SystemProperties;
@@ -705,12 +707,34 @@ class BackNavigationController {
private WindowState mNavigatingWindow;
private RemoteCallback mObserver;
+ private final IBinder.DeathRecipient mListenerDeathRecipient =
+ new IBinder.DeathRecipient() {
+ @Override
+ @BinderThread
+ public void binderDied() {
+ synchronized (mWindowManagerService.mGlobalLock) {
+ stopMonitorForRemote();
+ stopMonitorTransition();
+ }
+ }
+ };
+
void startMonitor(@NonNull WindowState window, @NonNull RemoteCallback observer) {
mNavigatingWindow = window;
mObserver = observer;
+ try {
+ mObserver.getInterface().asBinder().linkToDeath(mListenerDeathRecipient,
+ 0 /* flags */);
+ } catch (RemoteException r) {
+ Slog.e(TAG, "Failed to link to death");
+ }
}
void stopMonitorForRemote() {
+ if (mObserver != null) {
+ mObserver.getInterface().asBinder().unlinkToDeath(mListenerDeathRecipient,
+ 0 /* flags */);
+ }
mObserver = null;
}
diff --git a/services/core/java/com/android/server/wm/LaunchParamsController.java b/services/core/java/com/android/server/wm/LaunchParamsController.java
index d3c3d2834124..ba1401ab3978 100644
--- a/services/core/java/com/android/server/wm/LaunchParamsController.java
+++ b/services/core/java/com/android/server/wm/LaunchParamsController.java
@@ -79,7 +79,8 @@ class LaunchParamsController {
* @param result The resulting params.
*/
void calculate(Task task, WindowLayout layout, ActivityRecord activity, ActivityRecord source,
- ActivityOptions options, @Nullable Request request, int phase, LaunchParams result) {
+ ActivityOptions options, @Nullable Request request,
+ @LaunchParamsModifier.Phase int phase, LaunchParams result) {
result.reset();
if (task != null || activity != null) {
diff --git a/services/core/java/com/android/server/wm/RootWindowContainer.java b/services/core/java/com/android/server/wm/RootWindowContainer.java
index 57fe0bb4937e..04f09d5fe627 100644
--- a/services/core/java/com/android/server/wm/RootWindowContainer.java
+++ b/services/core/java/com/android/server/wm/RootWindowContainer.java
@@ -152,7 +152,6 @@ import com.android.server.LocalServices;
import com.android.server.am.ActivityManagerService;
import com.android.server.am.AppTimeTracker;
import com.android.server.am.UserState;
-import com.android.server.display.feature.DisplayManagerFlags;
import com.android.server.pm.UserManagerInternal;
import com.android.server.policy.PermissionPolicyInternal;
import com.android.server.policy.WindowManagerPolicy;
@@ -1541,20 +1540,18 @@ class RootWindowContainer extends WindowContainer<DisplayContent>
ActivityInfo aInfo = resolveHomeActivity(userId, homeIntent);
boolean lookForSecondaryHomeActivityInPrimaryHomePackage = aInfo != null;
- if (android.companion.virtual.flags.Flags.vdmCustomHome()) {
- // Resolve the externally set home activity for this display, if any. If it is unset or
- // we fail to resolve it, fallback to the default secondary home activity.
- final ComponentName customHomeComponent =
- taskDisplayArea.getDisplayContent() != null
- ? taskDisplayArea.getDisplayContent().getCustomHomeComponent()
- : null;
- if (customHomeComponent != null) {
- homeIntent.setComponent(customHomeComponent);
- ActivityInfo customHomeActivityInfo = resolveHomeActivity(userId, homeIntent);
- if (customHomeActivityInfo != null) {
- aInfo = customHomeActivityInfo;
- lookForSecondaryHomeActivityInPrimaryHomePackage = false;
- }
+ // Resolve the externally set home activity for this display, if any. If it is unset or
+ // we fail to resolve it, fallback to the default secondary home activity.
+ final ComponentName customHomeComponent =
+ taskDisplayArea.getDisplayContent() != null
+ ? taskDisplayArea.getDisplayContent().getCustomHomeComponent()
+ : null;
+ if (customHomeComponent != null) {
+ homeIntent.setComponent(customHomeComponent);
+ ActivityInfo customHomeActivityInfo = resolveHomeActivity(userId, homeIntent);
+ if (customHomeActivityInfo != null) {
+ aInfo = customHomeActivityInfo;
+ lookForSecondaryHomeActivityInPrimaryHomePackage = false;
}
}
diff --git a/services/core/java/com/android/server/wm/TaskLaunchParamsModifier.java b/services/core/java/com/android/server/wm/TaskLaunchParamsModifier.java
index c39671d76929..e3a5b66b83fd 100644
--- a/services/core/java/com/android/server/wm/TaskLaunchParamsModifier.java
+++ b/services/core/java/com/android/server/wm/TaskLaunchParamsModifier.java
@@ -96,9 +96,10 @@ class TaskLaunchParamsModifier implements LaunchParamsModifier {
}
@Override
+ @Result
public int onCalculate(@Nullable Task task, @Nullable ActivityInfo.WindowLayout layout,
@Nullable ActivityRecord activity, @Nullable ActivityRecord source,
- @Nullable ActivityOptions options, @Nullable Request request, int phase,
+ @Nullable ActivityOptions options, @Nullable Request request, @Phase int phase,
LaunchParams currentParams, LaunchParams outParams) {
initLogBuilder(task, activity);
final int result = calculate(task, layout, activity, source, options, request, phase,
@@ -107,9 +108,10 @@ class TaskLaunchParamsModifier implements LaunchParamsModifier {
return result;
}
+ @Result
private int calculate(@Nullable Task task, @Nullable ActivityInfo.WindowLayout layout,
@Nullable ActivityRecord activity, @Nullable ActivityRecord source,
- @Nullable ActivityOptions options, @Nullable Request request, int phase,
+ @Nullable ActivityOptions options, @Nullable Request request, @Phase int phase,
LaunchParams currentParams, LaunchParams outParams) {
final ActivityRecord root;
if (task != null) {
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index e4ef3d186bdb..dd6e15b74a58 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -331,6 +331,7 @@ import android.window.WindowContextInfo;
import com.android.internal.R;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.annotations.VisibleForTesting.Visibility;
import com.android.internal.os.IResultReceiver;
import com.android.internal.os.TransferPipe;
import com.android.internal.policy.IKeyguardDismissCallback;
@@ -600,6 +601,7 @@ public class WindowManagerService extends IWindowManager.Stub
final boolean mLimitedAlphaCompositing;
final int mMaxUiWidth;
+ @NonNull
@VisibleForTesting
WindowManagerPolicy mPolicy;
@@ -1054,13 +1056,16 @@ public class WindowManagerService extends IWindowManager.Stub
private boolean mAnimationsDisabled = false;
boolean mPointerLocationEnabled = false;
+ @NonNull
final AppCompatConfiguration mAppCompatConfiguration;
private boolean mIsIgnoreOrientationRequestDisabled;
+ @NonNull
final InputManagerService mInputManager;
final DisplayManagerInternal mDisplayManagerInternal;
final DisplayManager mDisplayManager;
+ @NonNull
final ActivityTaskManagerService mAtmService;
/** Indicates whether this device supports wide color gamut / HDR rendering */
@@ -1116,7 +1121,9 @@ public class WindowManagerService extends IWindowManager.Stub
static WindowManagerThreadPriorityBooster sThreadPriorityBooster =
new WindowManagerThreadPriorityBooster();
+ @NonNull
Supplier<SurfaceControl.Builder> mSurfaceControlFactory;
+ @NonNull
Supplier<SurfaceControl.Transaction> mTransactionFactory;
private final SurfaceControl.Transaction mTransaction;
@@ -1188,9 +1195,11 @@ public class WindowManagerService extends IWindowManager.Stub
private volatile boolean mDisableSecureWindows = false;
- public static WindowManagerService main(final Context context, final InputManagerService im,
- final boolean showBootMsgs, WindowManagerPolicy policy,
- ActivityTaskManagerService atm) {
+ /** Creates an instance of the WindowManagerService for the system server. */
+ public static WindowManagerService main(@NonNull final Context context,
+ @NonNull final InputManagerService im, final boolean showBootMsgs,
+ @NonNull final WindowManagerPolicy policy,
+ @NonNull final ActivityTaskManagerService atm) {
// Using SysUI context to have access to Material colors extracted from Wallpaper.
final AppCompatConfiguration appCompat = new AppCompatConfiguration(
ActivityThread.currentActivityThread().getSystemUiContext());
@@ -1204,15 +1213,19 @@ public class WindowManagerService extends IWindowManager.Stub
/**
* Creates and returns an instance of the WindowManagerService. This call allows the caller
- * to override factories that can be used to stub native calls during test.
+ * to override factories that can be used to stub native calls during test. Tests should use
+ * {@link WindowManagerServiceTestSupport} instead of calling this directly to ensure
+ * proper initialization and cleanup of dependencies.
*/
- @VisibleForTesting
- public static WindowManagerService main(final Context context, final InputManagerService im,
- final boolean showBootMsgs, WindowManagerPolicy policy, ActivityTaskManagerService atm,
- DisplayWindowSettingsProvider displayWindowSettingsProvider,
- Supplier<SurfaceControl.Transaction> transactionFactory,
- Supplier<SurfaceControl.Builder> surfaceControlFactory,
- AppCompatConfiguration appCompat) {
+ @VisibleForTesting(visibility = Visibility.PRIVATE)
+ static WindowManagerService main(@NonNull final Context context,
+ @NonNull final InputManagerService im, boolean showBootMsgs,
+ @NonNull final WindowManagerPolicy policy,
+ @NonNull final ActivityTaskManagerService atm,
+ @NonNull final DisplayWindowSettingsProvider displayWindowSettingsProvider,
+ @NonNull final Supplier<SurfaceControl.Transaction> transactionFactory,
+ @NonNull final Supplier<SurfaceControl.Builder> surfaceControlFactory,
+ @NonNull final AppCompatConfiguration appCompat) {
final WindowManagerService[] wms = new WindowManagerService[1];
DisplayThread.getHandler().runWithScissors(() ->
@@ -1238,12 +1251,13 @@ public class WindowManagerService extends IWindowManager.Stub
new WindowManagerShellCommand(this).exec(this, in, out, err, args, callback, result);
}
- private WindowManagerService(Context context, InputManagerService inputManager,
- boolean showBootMsgs, WindowManagerPolicy policy, ActivityTaskManagerService atm,
- DisplayWindowSettingsProvider displayWindowSettingsProvider,
- Supplier<SurfaceControl.Transaction> transactionFactory,
- Supplier<SurfaceControl.Builder> surfaceControlFactory,
- AppCompatConfiguration appCompat) {
+ private WindowManagerService(@NonNull Context context,
+ @NonNull InputManagerService inputManager, boolean showBootMsgs,
+ @NonNull WindowManagerPolicy policy, @NonNull ActivityTaskManagerService atm,
+ @NonNull DisplayWindowSettingsProvider displayWindowSettingsProvider,
+ @NonNull Supplier<SurfaceControl.Transaction> transactionFactory,
+ @NonNull Supplier<SurfaceControl.Builder> surfaceControlFactory,
+ @NonNull AppCompatConfiguration appCompat) {
installLock(this, INDEX_WINDOW);
mGlobalLock = atm.getGlobalLock();
mAtmService = atm;
diff --git a/services/core/java/com/android/server/wm/WindowStateAnimator.java b/services/core/java/com/android/server/wm/WindowStateAnimator.java
index 0154d95d888d..d973fb014e35 100644
--- a/services/core/java/com/android/server/wm/WindowStateAnimator.java
+++ b/services/core/java/com/android/server/wm/WindowStateAnimator.java
@@ -480,18 +480,6 @@ class WindowStateAnimator {
if (mLastHidden) {
showRobustly(t);
mLastHidden = false;
- final DisplayContent displayContent = w.getDisplayContent();
- if (!displayContent.getLastHasContent()) {
- // This draw means the difference between unique content and mirroring.
- // Run another pass through performLayout to set mHasContent in the
- // LogicalDisplay.
- displayContent.pendingLayoutChanges |= FINISH_LAYOUT_REDO_ANIM;
- if (DEBUG_LAYOUT_REPEATS) {
- mService.mWindowPlacerLocked.debugLayoutRepeats(
- "showSurfaceRobustlyLocked " + w,
- displayContent.pendingLayoutChanges);
- }
- }
}
}
}
diff --git a/services/core/xsd/device-state-config/device-state-config.xsd b/services/core/xsd/device-state-config/device-state-config.xsd
index 4a947327070a..324c9b422ecb 100644
--- a/services/core/xsd/device-state-config/device-state-config.xsd
+++ b/services/core/xsd/device-state-config/device-state-config.xsd
@@ -41,6 +41,7 @@
<xs:annotation name="nullable" />
</xs:element>
<xs:element name="properties" type="properties" />
+ <xs:element name="flags" type="flags" />
<xs:element name="conditions" type="conditions" />
</xs:sequence>
</xs:complexType>
@@ -53,6 +54,14 @@
</xs:sequence>
</xs:complexType>
+ <xs:complexType name="flags">
+ <xs:sequence>
+ <xs:element name="flag" type="xs:string" minOccurs="0" maxOccurs="unbounded">
+ <xs:annotation name="nullable" />
+ </xs:element>
+ </xs:sequence>
+ </xs:complexType>
+
<xs:complexType name="conditions">
<xs:sequence>
<xs:element name="lid-switch" type="lidSwitchCondition" minOccurs="0">
diff --git a/services/core/xsd/device-state-config/schema/current.txt b/services/core/xsd/device-state-config/schema/current.txt
index 5bb216e69e4d..c94f3a80b898 100644
--- a/services/core/xsd/device-state-config/schema/current.txt
+++ b/services/core/xsd/device-state-config/schema/current.txt
@@ -11,10 +11,12 @@ package com.android.server.policy.devicestate.config {
public class DeviceState {
ctor public DeviceState();
method public com.android.server.policy.devicestate.config.Conditions getConditions();
+ method public com.android.server.policy.devicestate.config.Flags getFlags();
method public java.math.BigInteger getIdentifier();
method @Nullable public String getName();
method public com.android.server.policy.devicestate.config.Properties getProperties();
method public void setConditions(com.android.server.policy.devicestate.config.Conditions);
+ method public void setFlags(com.android.server.policy.devicestate.config.Flags);
method public void setIdentifier(java.math.BigInteger);
method public void setName(@Nullable String);
method public void setProperties(com.android.server.policy.devicestate.config.Properties);
@@ -25,6 +27,11 @@ package com.android.server.policy.devicestate.config {
method public java.util.List<com.android.server.policy.devicestate.config.DeviceState> getDeviceState();
}
+ public class Flags {
+ ctor public Flags();
+ method @Nullable public java.util.List<java.lang.String> getFlag();
+ }
+
public class LidSwitchCondition {
ctor public LidSwitchCondition();
method public boolean getOpen();
diff --git a/services/incremental/IncrementalService.cpp b/services/incremental/IncrementalService.cpp
index a8e6f689b424..dae481a3c215 100644
--- a/services/incremental/IncrementalService.cpp
+++ b/services/incremental/IncrementalService.cpp
@@ -1999,9 +1999,9 @@ bool IncrementalService::configureNativeBinaries(StorageId storage, std::string_
// Create new lib file without signature info
incfs::NewFileParams libFileParams = {
.size = entry.uncompressed_length,
- .signature = {},
// Metadata of the new lib file is its relative path
.metadata = {targetLibPath.c_str(), (IncFsSize)targetLibPath.size()},
+ .signature = {},
};
incfs::FileId libFileId = idFromMetadata(targetLibPath);
if (auto res = mIncFs->makeFile(ifs->control, targetLibPathAbsolute, 0755, libFileId,
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index 8e06ed8cc283..fadab1f8832e 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -3130,13 +3130,8 @@ public final class SystemServer implements Dumpable {
&& context.getPackageManager().hasSystemFeature(
PackageManager.FEATURE_BLUETOOTH_LE))) {
t.traceBegin("RangingService");
- // TODO: b/375264320 - Remove after RELEASE_RANGING_STACK is ramped to next.
- try {
- mSystemServiceManager.startServiceFromJar(RANGING_SERVICE_CLASS,
- RANGING_APEX_SERVICE_JAR_PATH);
- } catch (Throwable e) {
- Slog.d(TAG, "service-ranging.jar not found, not starting RangingService");
- }
+ mSystemServiceManager.startServiceFromJar(RANGING_SERVICE_CLASS,
+ RANGING_APEX_SERVICE_JAR_PATH);
t.traceEnd();
}
}
diff --git a/services/robotests/backup/src/com/android/server/backup/BackupManagerServiceRoboTest.java b/services/robotests/backup/src/com/android/server/backup/BackupManagerServiceRoboTest.java
index b80d68de0f2e..ba64ed44cbb5 100644
--- a/services/robotests/backup/src/com/android/server/backup/BackupManagerServiceRoboTest.java
+++ b/services/robotests/backup/src/com/android/server/backup/BackupManagerServiceRoboTest.java
@@ -1518,14 +1518,13 @@ public class BackupManagerServiceRoboTest {
}
/**
- * Test verifying that {@link BackupManagerService#MORE_DEBUG} is set to {@code false}. This is
+ * Test verifying that {@link BackupManagerService#DEBUG} is set to {@code false}. This is
* specifically to prevent overloading the logs in production.
*/
@Test
- public void testMoreDebug_isFalse() throws Exception {
- boolean moreDebug = BackupManagerService.MORE_DEBUG;
-
- assertThat(moreDebug).isFalse();
+ public void testDebug_isFalse() {
+ boolean debug = BackupManagerService.DEBUG;
+ assertThat(debug).isFalse();
}
/** Test that the constructor handles {@code null} parameters. */
diff --git a/services/robotests/backup/src/com/android/server/backup/keyvalue/KeyValueBackupReporterTest.java b/services/robotests/backup/src/com/android/server/backup/keyvalue/KeyValueBackupReporterTest.java
index 2db2438b9a21..2749b0acf193 100644
--- a/services/robotests/backup/src/com/android/server/backup/keyvalue/KeyValueBackupReporterTest.java
+++ b/services/robotests/backup/src/com/android/server/backup/keyvalue/KeyValueBackupReporterTest.java
@@ -57,8 +57,8 @@ public class KeyValueBackupReporterTest {
}
@Test
- public void testMoreDebug_isFalse() {
- assertThat(KeyValueBackupReporter.MORE_DEBUG).isFalse();
+ public void testDebug_isFalse() {
+ assertThat(KeyValueBackupReporter.DEBUG).isFalse();
}
@Test
diff --git a/services/robotests/backup/src/com/android/server/backup/keyvalue/KeyValueBackupTaskTest.java b/services/robotests/backup/src/com/android/server/backup/keyvalue/KeyValueBackupTaskTest.java
index de16b7ee8126..2dd16f68dc56 100644
--- a/services/robotests/backup/src/com/android/server/backup/keyvalue/KeyValueBackupTaskTest.java
+++ b/services/robotests/backup/src/com/android/server/backup/keyvalue/KeyValueBackupTaskTest.java
@@ -109,6 +109,7 @@ import com.android.server.EventLogTags;
import com.android.server.LocalServices;
import com.android.server.backup.BackupAgentConnectionManager;
import com.android.server.backup.BackupRestoreTask;
+import com.android.server.backup.BackupWakeLock;
import com.android.server.backup.DataChangedJournal;
import com.android.server.backup.KeyValueBackupJob;
import com.android.server.backup.PackageManagerBackupAgent;
@@ -201,7 +202,7 @@ public class KeyValueBackupTaskTest {
private TransportData mTransport;
private ShadowLooper mShadowBackupLooper;
private Handler mBackupHandler;
- private UserBackupManagerService.BackupWakeLock mWakeLock;
+ private BackupWakeLock mWakeLock;
private KeyValueBackupReporter mReporter;
private PackageManager mPackageManager;
private ShadowPackageManager mShadowPackageManager;
@@ -238,7 +239,7 @@ public class KeyValueBackupTaskTest {
mPackageManager = mApplication.getPackageManager();
mShadowPackageManager = shadowOf(mPackageManager);
- mWakeLock = createBackupWakeLock(mApplication);
+ mWakeLock = spy(createBackupWakeLock(mApplication));
mBackupManager = spy(FakeIBackupManager.class);
// Needed to be able to use a real BMS instead of a mock
@@ -737,17 +738,16 @@ public class KeyValueBackupTaskTest {
// In production (for non-system agents) the call is asynchronous, but here is
// synchronous, so it's fine to verify here.
// Verify has set work source and hasn't unset yet.
- verify(mBackupManagerService)
- .setWorkSource(
- argThat(workSource -> workSource.getUid(0) == PACKAGE_1.uid));
- verify(mBackupManagerService, never()).setWorkSource(null);
+ verify(mWakeLock).setWorkSource(
+ argThat(workSource -> workSource.getUid(0) == PACKAGE_1.uid));
+ verify(mWakeLock, never()).setWorkSource(null);
});
KeyValueBackupTask task = createKeyValueBackupTask(transportMock, PACKAGE_1);
runTask(task);
// More verifications inside agent call above
- verify(mBackupManagerService).setWorkSource(null);
+ verify(mWakeLock).setWorkSource(null);
}
/**
@@ -765,7 +765,7 @@ public class KeyValueBackupTaskTest {
runTask(task);
- verify(mBackupManagerService).setWorkSource(null);
+ verify(mWakeLock).setWorkSource(null);
verify(mObserver).onResult(PACKAGE_1.packageName, ERROR_AGENT_FAILURE);
verify(mObserver).backupFinished(SUCCESS);
assertBackupPendingFor(PACKAGE_1);
@@ -798,7 +798,7 @@ public class KeyValueBackupTaskTest {
runTask(task);
- verify(mBackupManagerService).setWorkSource(null);
+ verify(mWakeLock).setWorkSource(null);
verify(mObserver).onResult(PACKAGE_1.packageName, ERROR_AGENT_FAILURE);
verify(mObserver).backupFinished(SUCCESS);
assertBackupPendingFor(PACKAGE_1);
@@ -815,7 +815,7 @@ public class KeyValueBackupTaskTest {
runTask(task);
- verify(mBackupManagerService).setWorkSource(null);
+ verify(mWakeLock).setWorkSource(null);
verify(mObserver).onResult(PACKAGE_1.packageName, ERROR_AGENT_FAILURE);
verify(mObserver).backupFinished(SUCCESS);
assertBackupPendingFor(PACKAGE_1);
@@ -833,7 +833,7 @@ public class KeyValueBackupTaskTest {
runTask(task);
- verify(mBackupManagerService).setWorkSource(null);
+ verify(mWakeLock).setWorkSource(null);
verify(mObserver).onResult(PACKAGE_1.packageName, ERROR_AGENT_FAILURE);
verify(mObserver).backupFinished(SUCCESS);
assertBackupPendingFor(PACKAGE_1);
@@ -864,7 +864,7 @@ public class KeyValueBackupTaskTest {
runTask(task);
- verify(mBackupManagerService).setWorkSource(null);
+ verify(mWakeLock).setWorkSource(null);
verify(mBackupAgentConnectionManager).unbindAgent(argThat(applicationInfo(PACKAGE_1)),
eq(false));
}
@@ -918,7 +918,7 @@ public class KeyValueBackupTaskTest {
runTask(task);
- verify(mBackupManagerService).setWorkSource(null);
+ verify(mWakeLock).setWorkSource(null);
}
@Test
diff --git a/services/robotests/backup/src/com/android/server/backup/restore/ActiveRestoreSessionTest.java b/services/robotests/backup/src/com/android/server/backup/restore/ActiveRestoreSessionTest.java
index 73ddbe8cec7c..ffec68aa4082 100644
--- a/services/robotests/backup/src/com/android/server/backup/restore/ActiveRestoreSessionTest.java
+++ b/services/robotests/backup/src/com/android/server/backup/restore/ActiveRestoreSessionTest.java
@@ -51,6 +51,7 @@ import android.platform.test.annotations.Presubmit;
import com.android.server.EventLogTags;
import com.android.server.backup.BackupAgentTimeoutParameters;
+import com.android.server.backup.BackupWakeLock;
import com.android.server.backup.OperationStorage;
import com.android.server.backup.TransportManager;
import com.android.server.backup.UserBackupManagerService;
@@ -103,7 +104,7 @@ public class ActiveRestoreSessionTest {
@Mock private OperationStorage mOperationStorage;
private ShadowLooper mShadowBackupLooper;
private ShadowApplication mShadowApplication;
- private UserBackupManagerService.BackupWakeLock mWakeLock;
+ private BackupWakeLock mWakeLock;
private TransportData mTransport;
private RestoreSet mRestoreSet1;
private RestoreSet mRestoreSet2;
diff --git a/services/robotests/backup/src/com/android/server/backup/testing/BackupManagerServiceTestUtils.java b/services/robotests/backup/src/com/android/server/backup/testing/BackupManagerServiceTestUtils.java
index 4d04c8b76fc7..10d23dc18e58 100644
--- a/services/robotests/backup/src/com/android/server/backup/testing/BackupManagerServiceTestUtils.java
+++ b/services/robotests/backup/src/com/android/server/backup/testing/BackupManagerServiceTestUtils.java
@@ -39,6 +39,7 @@ import android.util.Log;
import com.android.server.backup.BackupAgentTimeoutParameters;
import com.android.server.backup.BackupManagerConstants;
import com.android.server.backup.BackupManagerService;
+import com.android.server.backup.BackupWakeLock;
import com.android.server.backup.TransportManager;
import com.android.server.backup.UserBackupManagerService;
@@ -114,7 +115,7 @@ public class BackupManagerServiceTestUtils {
TransportManager transportManager,
PackageManager packageManager,
Handler backupHandler,
- UserBackupManagerService.BackupWakeLock wakeLock,
+ BackupWakeLock wakeLock,
BackupAgentTimeoutParameters agentTimeoutParameters) {
when(backupManagerService.getContext()).thenReturn(application);
@@ -123,7 +124,7 @@ public class BackupManagerServiceTestUtils {
when(backupManagerService.getBackupHandler()).thenReturn(backupHandler);
when(backupManagerService.getQueueLock()).thenReturn(new Object());
when(backupManagerService.getActivityManager()).thenReturn(mock(IActivityManager.class));
- when(backupManagerService.getWakelock()).thenReturn(wakeLock);
+ when(backupManagerService.getWakeLock()).thenReturn(wakeLock);
when(backupManagerService.getAgentTimeoutParameters()).thenReturn(agentTimeoutParameters);
AccessorMock backupEnabled = mockAccessor(false);
@@ -161,10 +162,12 @@ public class BackupManagerServiceTestUtils {
});
}
- public static UserBackupManagerService.BackupWakeLock createBackupWakeLock(
- Application application) {
+ /**
+ * Creates a wakelock for testing.
+ */
+ public static BackupWakeLock createBackupWakeLock(Application application) {
PowerManager powerManager = application.getSystemService(PowerManager.class);
- return new UserBackupManagerService.BackupWakeLock(
+ return new BackupWakeLock(
powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "*backup*"), 0,
new BackupManagerConstants(Handler.getMain(), application.getContentResolver()));
}
diff --git a/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/ImeVisibilityStateComputerTest.java b/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/ImeVisibilityStateComputerTest.java
index 8ce2422563a3..70eeae648dd0 100644
--- a/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/ImeVisibilityStateComputerTest.java
+++ b/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/ImeVisibilityStateComputerTest.java
@@ -50,6 +50,7 @@ import android.view.inputmethod.InputMethodManager;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import com.android.internal.annotations.GuardedBy;
+import com.android.server.pm.UserManagerInternal;
import com.android.server.wm.WindowManagerInternal;
import org.junit.Before;
@@ -74,6 +75,11 @@ public class ImeVisibilityStateComputerTest extends InputMethodManagerServiceTes
super.setUp();
ImeVisibilityStateComputer.Injector injector = new ImeVisibilityStateComputer.Injector() {
@Override
+ public UserManagerInternal getUserManagerService() {
+ return mMockUserManagerInternal;
+ }
+
+ @Override
public WindowManagerInternal getWmService() {
return mMockWindowManagerInternal;
}
diff --git a/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/UserDataRepositoryTest.java b/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/UserDataRepositoryTest.java
index b984624b3e9b..6adb01ccf11f 100644
--- a/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/UserDataRepositoryTest.java
+++ b/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/UserDataRepositoryTest.java
@@ -23,6 +23,7 @@ import android.view.WindowManager;
import androidx.annotation.NonNull;
+import com.android.server.pm.UserManagerInternal;
import com.android.server.wm.WindowManagerInternal;
import org.junit.After;
@@ -49,6 +50,9 @@ public final class UserDataRepositoryTest {
private InputMethodManagerService mMockInputMethodManagerService;
@Mock
+ private UserManagerInternal mMockUserManagerInternal;
+
+ @Mock
private WindowManagerInternal mMockWindowManagerInternal;
@NonNull
@@ -70,6 +74,12 @@ public final class UserDataRepositoryTest {
new ImeVisibilityStateComputer.Injector() {
@NonNull
@Override
+ public UserManagerInternal getUserManagerService() {
+ return mMockUserManagerInternal;
+ }
+
+ @NonNull
+ @Override
public WindowManagerInternal getWmService() {
return mMockWindowManagerInternal;
}
diff --git a/services/tests/mockingservicestests/src/com/android/server/backup/fullbackup/PerformFullTransportBackupTaskTest.java b/services/tests/mockingservicestests/src/com/android/server/backup/fullbackup/PerformFullTransportBackupTaskTest.java
index e618433862f2..2b7a62a9f9fe 100644
--- a/services/tests/mockingservicestests/src/com/android/server/backup/fullbackup/PerformFullTransportBackupTaskTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/backup/fullbackup/PerformFullTransportBackupTaskTest.java
@@ -35,6 +35,7 @@ import androidx.test.runner.AndroidJUnit4;
import com.android.server.backup.BackupAgentConnectionManager;
import com.android.server.backup.BackupAgentTimeoutParameters;
+import com.android.server.backup.BackupWakeLock;
import com.android.server.backup.OperationStorage;
import com.android.server.backup.TransportManager;
import com.android.server.backup.UserBackupManagerService;
@@ -81,7 +82,7 @@ public class PerformFullTransportBackupTaskTest {
@Mock
TransportManager mTransportManager;
@Mock
- UserBackupManagerService.BackupWakeLock mWakeLock;
+ BackupWakeLock mWakeLock;
private final List<String> mEligiblePackages = new ArrayList<>();
@@ -94,7 +95,7 @@ public class PerformFullTransportBackupTaskTest {
when(mBackupManagerService.getPackageManager()).thenReturn(mPackageManager);
when(mBackupManagerService.getQueueLock()).thenReturn("something!");
when(mBackupManagerService.isEnabled()).thenReturn(true);
- when(mBackupManagerService.getWakelock()).thenReturn(mWakeLock);
+ when(mBackupManagerService.getWakeLock()).thenReturn(mWakeLock);
when(mBackupManagerService.isSetupComplete()).thenReturn(true);
when(mBackupManagerService.getAgentTimeoutParameters()).thenReturn(
mBackupAgentTimeoutParameters);
diff --git a/services/tests/mockingservicestests/src/com/android/server/location/fudger/LocationFudgerTest.java b/services/tests/mockingservicestests/src/com/android/server/location/fudger/LocationFudgerTest.java
index f442eb69594e..ebaa2e84c044 100644
--- a/services/tests/mockingservicestests/src/com/android/server/location/fudger/LocationFudgerTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/location/fudger/LocationFudgerTest.java
@@ -271,4 +271,31 @@ public class LocationFudgerTest {
assertThat(center[0]).isEqualTo(expected[0]);
assertThat(center[1]).isEqualTo(expected[1]);
}
+
+ @Test
+ public void getS2CellApproximateEdge_returnsCorrectRadius() {
+ int level = 10;
+
+ float radius = mFudger.getS2CellApproximateEdge(level);
+
+ assertThat(radius).isEqualTo(9000); // in meters
+ }
+
+ @Test
+ public void getS2CellApproximateEdge_doesNotThrow() {
+ int level = -1;
+
+ mFudger.getS2CellApproximateEdge(level);
+
+ // No exception thrown.
+ }
+
+ @Test
+ public void getS2CellApproximateEdge_doesNotThrow2() {
+ int level = 14;
+
+ mFudger.getS2CellApproximateEdge(level);
+
+ // No exception thrown.
+ }
}
diff --git a/services/tests/powerstatstests/src/com/android/server/powerstats/PowerStatsServiceTest.java b/services/tests/powerstatstests/src/com/android/server/powerstats/PowerStatsServiceTest.java
index 115cdf6cee63..e654b40af1b5 100644
--- a/services/tests/powerstatstests/src/com/android/server/powerstats/PowerStatsServiceTest.java
+++ b/services/tests/powerstatstests/src/com/android/server/powerstats/PowerStatsServiceTest.java
@@ -37,11 +37,13 @@ import android.os.Bundle;
import android.os.IPowerStatsService;
import android.os.Looper;
import android.os.PowerMonitor;
+import android.os.PowerMonitorReadings;
import android.os.ResultReceiver;
import android.platform.test.annotations.EnableFlags;
import android.platform.test.flag.junit.SetFlagsRule;
import android.provider.DeviceConfig;
import android.provider.DeviceConfigInterface;
+import android.util.IntArray;
import androidx.test.InstrumentationRegistry;
@@ -129,6 +131,8 @@ public class PowerStatsServiceTest {
}
}
+ private final IntArray mFinePowerMonitorsPermissionGranted = new IntArray();
+
private final PowerStatsService.Injector mInjector = new PowerStatsService.Injector() {
@Override
@@ -220,6 +224,11 @@ public class PowerStatsServiceTest {
IntervalRandomNoiseGenerator createIntervalRandomNoiseGenerator() {
return mMockNoiseGenerator;
}
+
+ @Override
+ boolean checkFinePowerMonitorsPermission(Context context, int callingUid) {
+ return mFinePowerMonitorsPermissionGranted.contains(callingUid);
+ }
};
public static final class TestPowerStatsHALWrapper implements IPowerStatsHALWrapper {
@@ -1109,6 +1118,8 @@ public class PowerStatsServiceTest {
public int resultCode;
public long[] energyUws;
public long[] timestamps;
+ @PowerMonitorReadings.PowerMonitorGranularity
+ public int granularity;
GetPowerMonitorsResult() {
super(null);
@@ -1120,12 +1131,23 @@ public class PowerStatsServiceTest {
if (resultData != null) {
energyUws = resultData.getLongArray(IPowerStatsService.KEY_ENERGY);
timestamps = resultData.getLongArray(IPowerStatsService.KEY_TIMESTAMPS);
+ granularity = resultData.getInt(IPowerStatsService.KEY_GRANULARITY);
}
}
}
@Test
public void getPowerMonitors() {
+ testGetPowerMonitors(PowerMonitorReadings.GRANULARITY_UNSPECIFIED);
+ }
+
+ @Test
+ public void getPowerMonitors_finePowerMonitorPermissionGranted() {
+ mFinePowerMonitorsPermissionGranted.add(APP_UID);
+ testGetPowerMonitors(PowerMonitorReadings.GRANULARITY_FINE);
+ }
+
+ private void testGetPowerMonitors(int expectedGranularity) {
mMockClock.realtime = 10 * 60_000;
mMockNoiseGenerator.reseed(314);
@@ -1161,6 +1183,7 @@ public class PowerStatsServiceTest {
assertThat(result.energyUws).isEqualTo(new long[]{42, 142, 314, 514});
assertThat(result.timestamps).isEqualTo(new long[]{600_000, 600_100, 600_000, 600_200});
+ assertThat(result.granularity).isEqualTo(expectedGranularity);
// Test caching/throttling
mMockClock.realtime += 1;
@@ -1180,6 +1203,7 @@ public class PowerStatsServiceTest {
assertThat(result.energyUws).isEqualTo(new long[]{42, 314});
assertThat(result.timestamps).isEqualTo(new long[]{600_000, 600_000});
+ assertThat(result.granularity).isEqualTo(expectedGranularity);
mMockClock.realtime += 10 * 60000;
@@ -1189,6 +1213,7 @@ public class PowerStatsServiceTest {
// This time, random noise is added
assertThat(result.energyUws).isEqualTo(new long[]{298, 399});
assertThat(result.timestamps).isEqualTo(new long[]{600_301, 600_401});
+ assertThat(result.granularity).isEqualTo(expectedGranularity);
}
@Test
@@ -1234,7 +1259,6 @@ public class PowerStatsServiceTest {
assertThrows(NullPointerException.class, () -> iPowerStatsService.getPowerMonitorReadings(
new int[] {0}, null));
}
-
@Test
public void getEnergyConsumedAsync_halException() {
mPowerStatsHALWrapper.exception = new IllegalArgumentException();
diff --git a/services/tests/selinux/src/com/android/server/selinux/SelinuxAuditLogsCollectorTest.java b/services/tests/selinux/src/com/android/server/selinux/SelinuxAuditLogsCollectorTest.java
index db58c74e8431..29f55ff53e6e 100644
--- a/services/tests/selinux/src/com/android/server/selinux/SelinuxAuditLogsCollectorTest.java
+++ b/services/tests/selinux/src/com/android/server/selinux/SelinuxAuditLogsCollectorTest.java
@@ -20,6 +20,7 @@ import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
import static com.google.common.truth.Truth.assertThat;
+import static org.junit.Assert.assertThrows;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyInt;
@@ -81,7 +82,7 @@ public class SelinuxAuditLogsCollectorTest {
}
@Test
- public void testWriteAuditLogs() {
+ public void testWriteAuditLogs() throws Exception {
writeTestLog("granted", "perm", TEST_DOMAIN, "ttype", "tclass");
writeTestLog("denied", "perm1", TEST_DOMAIN, "ttype1", "tclass1");
@@ -117,7 +118,7 @@ public class SelinuxAuditLogsCollectorTest {
}
@Test
- public void testWriteAuditLogs_multiplePerms() {
+ public void testWriteAuditLogs_multiplePerms() throws Exception {
writeTestLog("denied", "perm1 perm2", TEST_DOMAIN, "ttype", "tclass");
writeTestLog("denied", "perm3 perm4", TEST_DOMAIN, "ttype", "tclass");
@@ -153,7 +154,7 @@ public class SelinuxAuditLogsCollectorTest {
}
@Test
- public void testWriteAuditLogs_withPaths() {
+ public void testWriteAuditLogs_withPaths() throws Exception {
writeTestLog("denied", "perm", TEST_DOMAIN, "ttype", "tclass", "/good/path");
writeTestLog("denied", "perm", TEST_DOMAIN, "ttype", "tclass", "/very/long/path");
writeTestLog("denied", "perm", TEST_DOMAIN, "ttype", "tclass", "/short_path");
@@ -217,7 +218,7 @@ public class SelinuxAuditLogsCollectorTest {
}
@Test
- public void testWriteAuditLogs_withCategories() {
+ public void testWriteAuditLogs_withCategories() throws Exception {
writeTestLog("denied", "perm", TEST_DOMAIN, new int[] {123}, "ttype", null, "tclass");
writeTestLog("denied", "perm", TEST_DOMAIN, new int[] {123, 456}, "ttype", null, "tclass");
writeTestLog("denied", "perm", TEST_DOMAIN, null, "ttype", new int[] {666}, "tclass");
@@ -288,7 +289,7 @@ public class SelinuxAuditLogsCollectorTest {
}
@Test
- public void testWriteAuditLogs_withPathAndCategories() {
+ public void testWriteAuditLogs_withPathAndCategories() throws Exception {
writeTestLog(
"denied",
"perm",
@@ -318,7 +319,7 @@ public class SelinuxAuditLogsCollectorTest {
}
@Test
- public void testWriteAuditLogs_permissive() {
+ public void testWriteAuditLogs_permissive() throws Exception {
writeTestLog("denied", "perm", TEST_DOMAIN, "ttype", "tclass");
writeTestLog("denied", "perm", TEST_DOMAIN, "ttype", "tclass", true);
writeTestLog("denied", "perm", TEST_DOMAIN, "ttype", "tclass", false);
@@ -356,7 +357,7 @@ public class SelinuxAuditLogsCollectorTest {
}
@Test
- public void testNotWriteAuditLogs_notTestDomain() {
+ public void testNotWriteAuditLogs_notTestDomain() throws Exception {
writeTestLog("denied", "perm", "stype", "ttype", "tclass");
boolean done = mSelinuxAutidLogsCollector.collect(ANSWER_TAG);
@@ -379,7 +380,7 @@ public class SelinuxAuditLogsCollectorTest {
}
@Test
- public void testWriteAuditLogs_upToQuota() {
+ public void testWriteAuditLogs_upToQuota() throws Exception {
writeTestLog("denied", "perm", TEST_DOMAIN, "ttype", "tclass");
writeTestLog("denied", "perm", TEST_DOMAIN, "ttype", "tclass");
writeTestLog("denied", "perm", TEST_DOMAIN, "ttype", "tclass");
@@ -389,9 +390,9 @@ public class SelinuxAuditLogsCollectorTest {
writeTestLog("denied", "perm", TEST_DOMAIN, "ttype", "tclass");
writeTestLog("denied", "perm", TEST_DOMAIN, "ttype", "tclass");
- boolean done = mSelinuxAutidLogsCollector.collect(ANSWER_TAG);
+ assertThrows(QuotaExceededException.class, () ->
+ mSelinuxAutidLogsCollector.collect(ANSWER_TAG));
- assertThat(done).isTrue();
verify(
() ->
FrameworkStatsLog.write(
@@ -409,7 +410,7 @@ public class SelinuxAuditLogsCollectorTest {
}
@Test
- public void testWriteAuditLogs_resetQuota() {
+ public void testWriteAuditLogs_resetQuota() throws Exception {
writeTestLog("denied", "perm", TEST_DOMAIN, "ttype", "tclass");
writeTestLog("denied", "perm", TEST_DOMAIN, "ttype", "tclass");
writeTestLog("denied", "perm", TEST_DOMAIN, "ttype", "tclass");
@@ -418,8 +419,8 @@ public class SelinuxAuditLogsCollectorTest {
writeTestLog("denied", "perm", TEST_DOMAIN, "ttype", "tclass");
writeTestLog("denied", "perm", TEST_DOMAIN, "ttype", "tclass");
- boolean done = mSelinuxAutidLogsCollector.collect(ANSWER_TAG);
- assertThat(done).isTrue();
+ assertThrows(QuotaExceededException.class, () ->
+ mSelinuxAutidLogsCollector.collect(ANSWER_TAG));
verify(
() ->
FrameworkStatsLog.write(
@@ -442,8 +443,8 @@ public class SelinuxAuditLogsCollectorTest {
writeTestLog("denied", "perm", TEST_DOMAIN, "ttype", "tclass");
// move the clock forward to reset the quota limiter.
mClock.currentTimeMillis += Duration.ofHours(1).toMillis();
- done = mSelinuxAutidLogsCollector.collect(ANSWER_TAG);
- assertThat(done).isTrue();
+ assertThrows(QuotaExceededException.class, () ->
+ mSelinuxAutidLogsCollector.collect(ANSWER_TAG));
verify(
() ->
FrameworkStatsLog.write(
@@ -461,14 +462,11 @@ public class SelinuxAuditLogsCollectorTest {
}
@Test
- public void testNotWriteAuditLogs_stopRequested() {
- writeTestLog("denied", "perm", TEST_DOMAIN, "ttype", "tclass");
+ public void testNotWriteAuditLogs_stopRequested() throws Exception {
writeTestLog("denied", "perm", TEST_DOMAIN, "ttype", "tclass");
writeTestLog("denied", "perm", TEST_DOMAIN, "ttype", "tclass");
writeTestLog("denied", "perm", TEST_DOMAIN, "ttype", "tclass");
writeTestLog("denied", "perm", TEST_DOMAIN, "ttype", "tclass");
- // These are not pushed.
- writeTestLog("denied", "perm", TEST_DOMAIN, "ttype", "tclass");
writeTestLog("denied", "perm", TEST_DOMAIN, "ttype", "tclass");
mSelinuxAutidLogsCollector.setStopRequested(true);
@@ -509,7 +507,7 @@ public class SelinuxAuditLogsCollectorTest {
}
@Test
- public void testAuditLogs_resumeJobDoesNotExceedLimit() {
+ public void testAuditLogs_resumeJobDoesNotExceedLimit() throws Exception {
writeTestLog("denied", "perm", TEST_DOMAIN, "ttype", "tclass");
mSelinuxAutidLogsCollector.setStopRequested(true);
diff --git a/services/tests/selinux/src/com/android/server/selinux/SelinuxAuditLogsJobTest.java b/services/tests/selinux/src/com/android/server/selinux/SelinuxAuditLogsJobTest.java
index 2aea8a033f87..344f3295f682 100644
--- a/services/tests/selinux/src/com/android/server/selinux/SelinuxAuditLogsJobTest.java
+++ b/services/tests/selinux/src/com/android/server/selinux/SelinuxAuditLogsJobTest.java
@@ -53,7 +53,7 @@ public class SelinuxAuditLogsJobTest {
}
@Test
- public void testFinishSuccessfully() {
+ public void testFinishSuccessfully() throws Exception {
when(mAuditLogsCollector.collect(anyInt())).thenReturn(true);
mAuditLogsJob.start(mJobService, mParams);
@@ -63,7 +63,7 @@ public class SelinuxAuditLogsJobTest {
}
@Test
- public void testInterrupt() {
+ public void testInterrupt() throws Exception {
when(mAuditLogsCollector.collect(anyInt())).thenReturn(false);
mAuditLogsJob.start(mJobService, mParams);
@@ -73,7 +73,7 @@ public class SelinuxAuditLogsJobTest {
}
@Test
- public void testInterruptAndResume() {
+ public void testInterruptAndResume() throws Exception {
when(mAuditLogsCollector.collect(anyInt())).thenReturn(false);
mAuditLogsJob.start(mJobService, mParams);
verify(mJobService, never()).jobFinished(any(), anyBoolean());
@@ -85,7 +85,7 @@ public class SelinuxAuditLogsJobTest {
}
@Test
- public void testRequestStop() throws InterruptedException {
+ public void testRequestStop() throws Exception {
Semaphore isRunning = new Semaphore(0);
Semaphore stopRequested = new Semaphore(0);
AtomicReference<Throwable> uncaughtException = new AtomicReference<>();
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityManagerServiceTest.java
index dafe4827b2fe..4d8aef49f080 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityManagerServiceTest.java
@@ -829,26 +829,6 @@ public class AccessibilityManagerServiceTest {
@SmallTest
@Test
- @DisableFlags(com.android.systemui.Flags.FLAG_HEARING_AIDS_QS_TILE_DIALOG)
- public void testPerformAccessibilityShortcut_hearingAids_startActivityWithExpectedComponent() {
- final AccessibilityUserState userState = mA11yms.mUserStates.get(
- mA11yms.getCurrentUserIdLocked());
- mFakePermissionEnforcer.grant(Manifest.permission.MANAGE_ACCESSIBILITY);
- userState.updateShortcutTargetsLocked(
- Set.of(ACCESSIBILITY_HEARING_AIDS_COMPONENT_NAME.flattenToString()), HARDWARE);
-
- mA11yms.performAccessibilityShortcut(
- Display.DEFAULT_DISPLAY, HARDWARE,
- ACCESSIBILITY_HEARING_AIDS_COMPONENT_NAME.flattenToString());
- mTestableLooper.processAllMessages();
-
- assertStartActivityWithExpectedComponentName(mTestableContext.getMockContext(),
- ACCESSIBILITY_HEARING_AIDS_COMPONENT_NAME.flattenToString());
- }
-
- @SmallTest
- @Test
- @EnableFlags(com.android.systemui.Flags.FLAG_HEARING_AIDS_QS_TILE_DIALOG)
public void testPerformAccessibilityShortcut_hearingAids_sendExpectedBroadcast() {
final AccessibilityUserState userState = mA11yms.mUserStates.get(
mA11yms.getCurrentUserIdLocked());
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDevicePlaybackTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDevicePlaybackTest.java
index 861e72d4ac79..cfdf17668229 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDevicePlaybackTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDevicePlaybackTest.java
@@ -2868,6 +2868,9 @@ public class HdmiCecLocalDevicePlaybackTest {
assertThat(mPowerManager.isInteractive()).isTrue();
mNativeWrapper.clearResultMessages();
+ mTestLooper.moveTimeForward(TIMEOUT_MS);
+ mTestLooper.dispatchAll();
+
mTestLooper.moveTimeForward(MONITORING_INTERVAL_MS);
mTestLooper.dispatchAll();
diff --git a/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest1.java b/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest1.java
index 776f05dfb6ea..f536cae53e3a 100644
--- a/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest1.java
+++ b/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest1.java
@@ -167,7 +167,7 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest {
* Test for {@link ShortcutService#getLastResetTimeLocked()} and
* {@link ShortcutService#getNextResetTimeLocked()}.
*/
- public void testUpdateAndGetNextResetTimeLocked() {
+ public void disabled_testUpdateAndGetNextResetTimeLocked() {
assertResetTimes(START_TIME, START_TIME + INTERVAL);
// Advance clock.
@@ -284,7 +284,7 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest {
assertEquals(START_TIME + 5 * INTERVAL, mManager.getRateLimitResetTime());
}
- public void testSetDynamicShortcuts() {
+ public void disabled_testSetDynamicShortcuts() {
setCaller(CALLING_PACKAGE_1, USER_10);
final Icon icon1 = Icon.createWithResource(getTestContext(), R.drawable.icon1);
@@ -596,7 +596,7 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest {
eq(CALLING_PACKAGE_2), any(), eq(USER_10));
}
- public void testUnlimitedCalls() {
+ public void disabled_testUnlimitedCalls() {
setCaller(CALLING_PACKAGE_1, USER_10);
final ShortcutInfo si1 = makeShortcut("shortcut1");
@@ -1205,7 +1205,7 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest {
maxSize));
}
- public void testShrinkBitmap() {
+ public void disabled_testShrinkBitmap() {
checkShrinkBitmap(32, 32, R.drawable.black_512x512, 32);
checkShrinkBitmap(511, 511, R.drawable.black_512x512, 511);
checkShrinkBitmap(512, 512, R.drawable.black_512x512, 512);
@@ -1302,7 +1302,7 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest {
assertFalse(p11_1_3.getName().contains("_"));
}
- public void testUpdateShortcuts() {
+ public void disabled_testUpdateShortcuts() {
runWithCaller(CALLING_PACKAGE_1, USER_10, () -> {
assertTrue(mManager.setDynamicShortcuts(list(
makeShortcut("s1"),
@@ -1433,7 +1433,7 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest {
});
}
- public void testUpdateShortcuts_icons() {
+ public void disabled_testUpdateShortcuts_icons() {
runWithCaller(CALLING_PACKAGE_1, USER_10, () -> {
assertTrue(mManager.setDynamicShortcuts(list(
makeShortcut("s1")
@@ -1527,7 +1527,7 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest {
});
}
- public void testShortcutManagerGetShortcuts_shortcutTypes() {
+ public void disabled_testShortcutManagerGetShortcuts_shortcutTypes() {
// Create 3 manifest and 3 dynamic shortcuts
addManifestShortcutResource(
@@ -2281,7 +2281,7 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest {
assertEquals("ABC", findById(list, "s1").getTitle());
}
- public void testPinShortcutAndGetPinnedShortcuts() {
+ public void disabled_testPinShortcutAndGetPinnedShortcuts() {
runWithCaller(CALLING_PACKAGE_1, USER_10, () -> {
final ShortcutInfo s1_1 = makeShortcutWithTimestamp("s1", 1000);
final ShortcutInfo s1_2 = makeShortcutWithTimestamp("s2", 2000);
@@ -3478,7 +3478,7 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest {
});
}
- public void testStartShortcut() {
+ public void disabled_testStartShortcut() {
// Create some shortcuts.
runWithCaller(CALLING_PACKAGE_1, USER_10, () -> {
final ShortcutInfo s1_1 = makeShortcut(
@@ -3902,7 +3902,7 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest {
// === Test for persisting ===
- public void testSaveAndLoadUser_empty() {
+ public void disabled_testSaveAndLoadUser_empty() {
assertTrue(mManager.setDynamicShortcuts(list()));
Log.i(TAG, "Saved state");
@@ -4074,7 +4074,7 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest {
assertNull(ShortcutPackage.loadFromFile(mService, user, corruptedShortcutPackage, false));
}
- public void testSaveCorruptAndLoadUser() throws Exception {
+ public void disabled_testSaveCorruptAndLoadUser() throws Exception {
// First, create some shortcuts and save.
runWithCaller(CALLING_PACKAGE_1, USER_10, () -> {
final Icon icon1 = Icon.createWithResource(getTestContext(), R.drawable.black_64x16);
@@ -6658,7 +6658,7 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest {
}
- public void testSaveAndLoad_crossProfile() {
+ public void disabled_testSaveAndLoad_crossProfile() {
prepareCrossProfileDataSet();
dumpsysOnLogcat("Before save & load");
@@ -8471,7 +8471,7 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest {
});
}
- public void testShortcutsPushedOutByManifest() {
+ public void disabled_testShortcutsPushedOutByManifest() {
// Change the max number of shortcuts.
mService.updateConfigurationLocked(ConfigConstants.KEY_MAX_SHORTCUTS + "=3");
@@ -8708,7 +8708,7 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest {
}
}
- public void testShareTargetInfo_saveToXml() throws IOException, XmlPullParserException {
+ public void disabled_testShareTargetInfo_saveToXml() throws IOException, XmlPullParserException {
List<ShareTargetInfo> expectedValues = new ArrayList<>();
expectedValues.add(new ShareTargetInfo(
new ShareTargetInfo.TargetData[]{new ShareTargetInfo.TargetData(
@@ -8902,7 +8902,7 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest {
});
}
- public void testUpdateShortcuts_ExcludesHiddenFromLauncherShortcuts() {
+ public void disabled_testUpdateShortcuts_ExcludesHiddenFromLauncherShortcuts() {
final ShortcutInfo s1 = makeShortcut("s1");
final ShortcutInfo s2 = makeShortcut("s2");
final ShortcutInfo s3 = makeShortcut("s3");
diff --git a/services/tests/servicestests/src/com/android/server/policy/DeviceStateProviderImplTest.java b/services/tests/servicestests/src/com/android/server/policy/DeviceStateProviderImplTest.java
index 551808243640..7f2935e0e497 100644
--- a/services/tests/servicestests/src/com/android/server/policy/DeviceStateProviderImplTest.java
+++ b/services/tests/servicestests/src/com/android/server/policy/DeviceStateProviderImplTest.java
@@ -18,6 +18,7 @@ package com.android.server.policy;
import static android.content.Context.SENSOR_SERVICE;
+import static android.hardware.devicestate.feature.flags.Flags.FLAG_DEVICE_STATE_CONFIGURATION_FLAG;
import static com.android.server.devicestate.DeviceStateProvider.SUPPORTED_DEVICE_STATES_CHANGED_INITIALIZED;
import static com.android.server.devicestate.DeviceStateProvider.SUPPORTED_DEVICE_STATES_CHANGED_POWER_SAVE_DISABLED;
@@ -42,6 +43,9 @@ import android.hardware.SensorEvent;
import android.hardware.SensorManager;
import android.hardware.devicestate.DeviceState;
import android.os.PowerManager;
+import android.platform.test.annotations.RequiresFlagsEnabled;
+import android.platform.test.flag.junit.CheckFlagsRule;
+import android.platform.test.flag.junit.DeviceFlagsValueProvider;
import androidx.annotation.NonNull;
@@ -51,6 +55,7 @@ import com.android.server.input.InputManagerInternal;
import org.junit.After;
import org.junit.Before;
+import org.junit.Rule;
import org.junit.Test;
import org.mockito.ArgumentCaptor;
import org.mockito.Mockito;
@@ -84,6 +89,10 @@ public final class DeviceStateProviderImplTest {
private Context mContext;
private SensorManager mSensorManager;
+ @Rule
+ public final CheckFlagsRule mCheckFlagsRule =
+ DeviceFlagsValueProvider.createCheckFlagsRule();
+
@Before
public void setup() {
LocalServices.addService(InputManagerInternal.class, mock(InputManagerInternal.class));
@@ -605,6 +614,37 @@ public final class DeviceStateProviderImplTest {
verify(listener, never()).onStateChanged(mIntegerCaptor.capture());
}
+ @RequiresFlagsEnabled(FLAG_DEVICE_STATE_CONFIGURATION_FLAG)
+ @Test
+ public void test_createConfigWithFlags() {
+ String configString = "<device-state-config>\n"
+ + " <device-state>\n"
+ + " <identifier>1</identifier>\n"
+ + " <flags>\n"
+ + " <flag>FLAG_CANCEL_OVERRIDE_REQUESTS</flag>\n"
+ + " </flags>\n"
+ + " </device-state>\n"
+ + " <device-state>\n"
+ + " <identifier>2</identifier>\n"
+ + " <name>CLOSED</name>\n"
+ + " </device-state>\n"
+ + "</device-state-config>\n";
+ DeviceStateProviderImpl.ReadableConfig config = new TestReadableConfig(configString);
+ DeviceStateProviderImpl provider = DeviceStateProviderImpl.createFromConfig(mContext,
+ config);
+
+ DeviceStateProvider.Listener listener = mock(DeviceStateProvider.Listener.class);
+ provider.setListener(listener);
+
+ verify(listener).onSupportedDeviceStatesChanged(mDeviceStateArrayCaptor.capture(),
+ eq(SUPPORTED_DEVICE_STATES_CHANGED_INITIALIZED));
+ final DeviceState[] expectedStates = new DeviceState[]{
+ createDeviceState(1, "",
+ Set.of(DeviceState.PROPERTY_POLICY_CANCEL_OVERRIDE_REQUESTS)),
+ createDeviceState(2, "CLOSED", EMPTY_PROPERTY_SET)};
+ assertArrayEquals(expectedStates, mDeviceStateArrayCaptor.getValue());
+ }
+
private DeviceState createDeviceState(int identifier, @NonNull String name,
@NonNull Set<@DeviceState.DeviceStateProperties Integer> systemProperties) {
DeviceState.Configuration configuration = new DeviceState.Configuration.Builder(identifier,
diff --git a/services/tests/timetests/src/com/android/server/timezonedetector/FakeEnvironment.java b/services/tests/timetests/src/com/android/server/timezonedetector/FakeEnvironment.java
new file mode 100644
index 000000000000..5d181b81eb4c
--- /dev/null
+++ b/services/tests/timetests/src/com/android/server/timezonedetector/FakeEnvironment.java
@@ -0,0 +1,141 @@
+/*
+ * Copyright 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.timezonedetector;
+
+import static com.android.server.SystemTimeZone.TIME_ZONE_CONFIDENCE_LOW;
+
+import android.annotation.CurrentTimeMillisLong;
+import android.annotation.ElapsedRealtimeLong;
+
+import com.android.server.SystemTimeZone;
+
+import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * A partially implemented, fake implementation of Environment for tests.
+ */
+public class FakeEnvironment implements Environment {
+
+ private final TestState<String> mTimeZoneId = new TestState<>();
+ private final TestState<Integer> mTimeZoneConfidence = new TestState<>();
+ private final List<Runnable> mAsyncRunnables = new ArrayList<>();
+ private @ElapsedRealtimeLong long mElapsedRealtimeMillis;
+ private @CurrentTimeMillisLong long mInitializationTimeMillis;
+
+ FakeEnvironment() {
+ // Ensure the fake environment starts with the defaults a fresh device would.
+ initializeTimeZoneSetting("", TIME_ZONE_CONFIDENCE_LOW);
+ }
+
+ void initializeClock(@CurrentTimeMillisLong long currentTimeMillis,
+ @ElapsedRealtimeLong long elapsedRealtimeMillis) {
+ mInitializationTimeMillis = currentTimeMillis - elapsedRealtimeMillis;
+ mElapsedRealtimeMillis = elapsedRealtimeMillis;
+ }
+
+ void initializeTimeZoneSetting(String zoneId,
+ @SystemTimeZone.TimeZoneConfidence int timeZoneConfidence) {
+ mTimeZoneId.init(zoneId);
+ mTimeZoneConfidence.init(timeZoneConfidence);
+ }
+
+ void incrementClock() {
+ mElapsedRealtimeMillis++;
+ }
+
+ @Override
+ public String getDeviceTimeZone() {
+ return mTimeZoneId.getLatest();
+ }
+
+ @Override
+ public int getDeviceTimeZoneConfidence() {
+ return mTimeZoneConfidence.getLatest();
+ }
+
+ @Override
+ public void setDeviceTimeZoneAndConfidence(
+ String zoneId, @SystemTimeZone.TimeZoneConfidence int confidence, String logInfo) {
+ mTimeZoneId.set(zoneId);
+ mTimeZoneConfidence.set(confidence);
+ }
+
+ void assertTimeZoneNotChanged() {
+ mTimeZoneId.assertHasNotBeenSet();
+ mTimeZoneConfidence.assertHasNotBeenSet();
+ }
+
+ void assertTimeZoneChangedTo(String timeZoneId,
+ @SystemTimeZone.TimeZoneConfidence int confidence) {
+ mTimeZoneId.assertHasBeenSet();
+ mTimeZoneId.assertChangeCount(1);
+ mTimeZoneId.assertLatestEquals(timeZoneId);
+
+ mTimeZoneConfidence.assertHasBeenSet();
+ mTimeZoneConfidence.assertChangeCount(1);
+ mTimeZoneConfidence.assertLatestEquals(confidence);
+ }
+
+ void commitAllChanges() {
+ mTimeZoneId.commitLatest();
+ mTimeZoneConfidence.commitLatest();
+ }
+
+ @Override
+ @ElapsedRealtimeLong
+ public long elapsedRealtimeMillis() {
+ return mElapsedRealtimeMillis;
+ }
+
+ @Override
+ @CurrentTimeMillisLong
+ public long currentTimeMillis() {
+ return mInitializationTimeMillis + mElapsedRealtimeMillis;
+ }
+
+ @Override
+ public void addDebugLogEntry(String logMsg) {
+ // No-op for tests
+ }
+
+ @Override
+ public void dumpDebugLog(PrintWriter printWriter) {
+ // No-op for tests
+ }
+
+ /**
+ * Adds the supplied runnable to a list but does not run them. To run all the runnables that
+ * have been supplied, call {@code #runAsyncRunnables}.
+ */
+ @Override
+ public void runAsync(Runnable runnable) {
+ mAsyncRunnables.add(runnable);
+ }
+
+ /**
+ * Requests that the runnable that have been supplied to {@code #runAsync} are invoked
+ * asynchronously and cleared.
+ */
+ public void runAsyncRunnables() {
+ for (Runnable runnable : mAsyncRunnables) {
+ runnable.run();
+ }
+ mAsyncRunnables.clear();
+ }
+}
diff --git a/services/tests/timetests/src/com/android/server/timezonedetector/NotifyingTimeZoneChangeListenerTest.java b/services/tests/timetests/src/com/android/server/timezonedetector/NotifyingTimeZoneChangeListenerTest.java
index 23c13bd04368..45cc891bff55 100644
--- a/services/tests/timetests/src/com/android/server/timezonedetector/NotifyingTimeZoneChangeListenerTest.java
+++ b/services/tests/timetests/src/com/android/server/timezonedetector/NotifyingTimeZoneChangeListenerTest.java
@@ -85,9 +85,6 @@ public class NotifyingTimeZoneChangeListenerTest {
return List.of(ORIGIN_LOCATION, ORIGIN_TELEPHONY);
}
- private static final long ARBITRARY_ELAPSED_REALTIME_MILLIS = 1234;
- /** A time zone used for initialization that does not occur elsewhere in tests. */
- private static final String ARBITRARY_TIME_ZONE_ID = "Etc/UTC";
private static final String INTERACT_ACROSS_USERS_FULL_PERMISSION =
"android.permission.INTERACT_ACROSS_USERS_FULL";
@@ -99,6 +96,7 @@ public class NotifyingTimeZoneChangeListenerTest {
private HandlerThread mHandlerThread;
private TestHandler mHandler;
private FakeServiceConfigAccessor mServiceConfigAccessor;
+ private FakeEnvironment mFakeEnvironment;
private int mUid;
private NotifyingTimeZoneChangeListener mTimeZoneChangeTracker;
@@ -106,6 +104,9 @@ public class NotifyingTimeZoneChangeListenerTest {
@Before
public void setUp() {
mUid = Process.myUid();
+ mFakeEnvironment = new FakeEnvironment();
+ mFakeEnvironment.initializeClock(1735689600L, 1234L);
+
// Create a thread + handler for processing the work that the service posts.
mHandlerThread = new HandlerThread("TimeZoneDetectorInternalTest");
mHandlerThread.start();
@@ -138,7 +139,7 @@ public class NotifyingTimeZoneChangeListenerTest {
mNotificationManager = new FakeNotificationManager(mContext, InstantSource.system());
mTimeZoneChangeTracker = new NotifyingTimeZoneChangeListener(mHandler, mContext,
- mServiceConfigAccessor, mNotificationManager);
+ mServiceConfigAccessor, mNotificationManager, mFakeEnvironment);
}
@After
@@ -151,7 +152,7 @@ public class NotifyingTimeZoneChangeListenerTest {
public void process_autoDetectionOff_noManualTracking_shouldTrackWithoutNotifying() {
enableTimeZoneNotifications();
- TimeZoneChangeRecord expectedChangeEvent = new TimeZoneChangeRecord(
+ TimeZoneChangeRecord expectedTimeZoneChangeRecord = new TimeZoneChangeRecord(
/* id= */ 1,
new TimeZoneChangeEvent(
/* elapsedRealtimeMillis= */ 0,
@@ -162,13 +163,14 @@ public class NotifyingTimeZoneChangeListenerTest {
/* newZoneId= */ "Europe/London",
/* newConfidence= */ 1,
/* cause= */ "NO_REASON"));
- expectedChangeEvent.setStatus(STATUS_UNTRACKED, SIGNAL_TYPE_NONE);
+ expectedTimeZoneChangeRecord.setStatus(STATUS_UNTRACKED, SIGNAL_TYPE_NONE);
assertNull(mTimeZoneChangeTracker.getLastTimeZoneChangeRecord());
- mTimeZoneChangeTracker.process(expectedChangeEvent.getEvent());
+ mTimeZoneChangeTracker.process(expectedTimeZoneChangeRecord.getEvent());
- assertEquals(expectedChangeEvent, mTimeZoneChangeTracker.getLastTimeZoneChangeRecord());
+ assertEquals(expectedTimeZoneChangeRecord,
+ mTimeZoneChangeTracker.getLastTimeZoneChangeRecord());
assertEquals(0, mNotificationManager.getNotifications().size());
mHandler.assertTotalMessagesEnqueued(0);
}
@@ -177,7 +179,7 @@ public class NotifyingTimeZoneChangeListenerTest {
public void process_autoDetectionOff_shouldTrackWithoutNotifying() {
enableNotificationsWithManualChangeTracking();
- TimeZoneChangeRecord expectedChangeEvent = new TimeZoneChangeRecord(
+ TimeZoneChangeRecord expectedTimeZoneChangeRecord = new TimeZoneChangeRecord(
/* id= */ 1,
new TimeZoneChangeEvent(
/* elapsedRealtimeMillis= */ 0,
@@ -188,13 +190,14 @@ public class NotifyingTimeZoneChangeListenerTest {
/* newZoneId= */ "Europe/London",
/* newConfidence= */ 1,
/* cause= */ "NO_REASON"));
- expectedChangeEvent.setStatus(STATUS_UNTRACKED, SIGNAL_TYPE_NONE);
+ expectedTimeZoneChangeRecord.setStatus(STATUS_UNTRACKED, SIGNAL_TYPE_NONE);
assertNull(mTimeZoneChangeTracker.getLastTimeZoneChangeRecord());
- mTimeZoneChangeTracker.process(expectedChangeEvent.getEvent());
+ mTimeZoneChangeTracker.process(expectedTimeZoneChangeRecord.getEvent());
- assertEquals(expectedChangeEvent, mTimeZoneChangeTracker.getLastTimeZoneChangeRecord());
+ assertEquals(expectedTimeZoneChangeRecord,
+ mTimeZoneChangeTracker.getLastTimeZoneChangeRecord());
assertEquals(0, mNotificationManager.getNotifications().size());
mHandler.assertTotalMessagesEnqueued(1);
}
@@ -214,7 +217,7 @@ public class NotifyingTimeZoneChangeListenerTest {
enableNotificationsWithManualChangeTracking();
- TimeZoneChangeRecord expectedChangeEvent = new TimeZoneChangeRecord(
+ TimeZoneChangeRecord expectedTimeZoneChangeRecord = new TimeZoneChangeRecord(
/* id= */ 1,
new TimeZoneChangeEvent(
/* elapsedRealtimeMillis= */ 0,
@@ -225,19 +228,20 @@ public class NotifyingTimeZoneChangeListenerTest {
/* newZoneId= */ "Europe/London",
/* newConfidence= */ 1,
/* cause= */ "NO_REASON"));
- expectedChangeEvent.setStatus(STATUS_UNKNOWN, SIGNAL_TYPE_UNKNOWN);
+ expectedTimeZoneChangeRecord.setStatus(STATUS_UNKNOWN, SIGNAL_TYPE_UNKNOWN);
assertNull(mTimeZoneChangeTracker.getLastTimeZoneChangeRecord());
// lastTrackedChangeEvent == null
- mTimeZoneChangeTracker.process(expectedChangeEvent.getEvent());
- TimeZoneChangeRecord trackedEvent1 = mTimeZoneChangeTracker.getLastTimeZoneChangeRecord();
+ mTimeZoneChangeTracker.process(expectedTimeZoneChangeRecord.getEvent());
+ TimeZoneChangeRecord timeZoneChangeRecord1 =
+ mTimeZoneChangeTracker.getLastTimeZoneChangeRecord();
- assertEquals(expectedChangeEvent, trackedEvent1);
+ assertEquals(expectedTimeZoneChangeRecord, timeZoneChangeRecord1);
assertEquals(1, mNotificationManager.getNotifications().size());
mHandler.assertTotalMessagesEnqueued(1);
- expectedChangeEvent = new TimeZoneChangeRecord(
+ expectedTimeZoneChangeRecord = new TimeZoneChangeRecord(
/* id= */ 2,
new TimeZoneChangeEvent(
/* elapsedRealtimeMillis= */ 1000L,
@@ -248,14 +252,15 @@ public class NotifyingTimeZoneChangeListenerTest {
/* newZoneId= */ "Europe/Paris",
/* newConfidence= */ 1,
/* cause= */ "NO_REASON"));
- expectedChangeEvent.setStatus(STATUS_UNKNOWN, SIGNAL_TYPE_UNKNOWN);
+ expectedTimeZoneChangeRecord.setStatus(STATUS_UNKNOWN, SIGNAL_TYPE_UNKNOWN);
// lastTrackedChangeEvent != null
- mTimeZoneChangeTracker.process(expectedChangeEvent.getEvent());
- TimeZoneChangeRecord trackedEvent2 = mTimeZoneChangeTracker.getLastTimeZoneChangeRecord();
+ mTimeZoneChangeTracker.process(expectedTimeZoneChangeRecord.getEvent());
+ TimeZoneChangeRecord timeZoneChangeRecord2 =
+ mTimeZoneChangeTracker.getLastTimeZoneChangeRecord();
- assertEquals(STATUS_SUPERSEDED, trackedEvent1.getStatus());
- assertEquals(expectedChangeEvent, trackedEvent2);
+ assertEquals(STATUS_SUPERSEDED, timeZoneChangeRecord1.getStatus());
+ assertEquals(expectedTimeZoneChangeRecord, timeZoneChangeRecord2);
assertEquals(2, mNotificationManager.getNotifications().size());
mHandler.assertTotalMessagesEnqueued(2);
@@ -263,7 +268,7 @@ public class NotifyingTimeZoneChangeListenerTest {
// Test manual change within revert threshold
{
- expectedChangeEvent = new TimeZoneChangeRecord(
+ expectedTimeZoneChangeRecord = new TimeZoneChangeRecord(
/* id= */ 3,
new TimeZoneChangeEvent(
/* elapsedRealtimeMillis= */ 999L + AUTO_REVERT_THRESHOLD,
@@ -275,16 +280,16 @@ public class NotifyingTimeZoneChangeListenerTest {
/* newZoneId= */ "Europe/London",
/* newConfidence= */ 1,
/* cause= */ "NO_REASON"));
- expectedChangeEvent.setStatus(STATUS_UNTRACKED, SIGNAL_TYPE_NONE);
+ expectedTimeZoneChangeRecord.setStatus(STATUS_UNTRACKED, SIGNAL_TYPE_NONE);
- mTimeZoneChangeTracker.process(expectedChangeEvent.getEvent());
- TimeZoneChangeRecord trackedEvent3 =
+ mTimeZoneChangeTracker.process(expectedTimeZoneChangeRecord.getEvent());
+ TimeZoneChangeRecord timeZoneChangeRecord3 =
mTimeZoneChangeTracker.getLastTimeZoneChangeRecord();
// The user manually changed the time zone within a short period of receiving the
// notification, indicating that they rejected the automatic change of time zone
- assertEquals(STATUS_REJECTED, trackedEvent2.getStatus());
- assertEquals(expectedChangeEvent, trackedEvent3);
+ assertEquals(STATUS_REJECTED, timeZoneChangeRecord2.getStatus());
+ assertEquals(expectedTimeZoneChangeRecord, timeZoneChangeRecord3);
assertEquals(2, mNotificationManager.getNotifications().size());
mHandler.assertTotalMessagesEnqueued(3);
}
@@ -293,12 +298,12 @@ public class NotifyingTimeZoneChangeListenerTest {
{
// [START] Reset previous event
enableNotificationsWithManualChangeTracking();
- mTimeZoneChangeTracker.process(trackedEvent2.getEvent());
- trackedEvent2 = mTimeZoneChangeTracker.getLastTimeZoneChangeRecord();
+ mTimeZoneChangeTracker.process(timeZoneChangeRecord2.getEvent());
+ timeZoneChangeRecord2 = mTimeZoneChangeTracker.getLastTimeZoneChangeRecord();
disableTimeZoneAutoDetection();
// [END] Reset previous event
- expectedChangeEvent = new TimeZoneChangeRecord(
+ expectedTimeZoneChangeRecord = new TimeZoneChangeRecord(
/* id= */ 5,
new TimeZoneChangeEvent(
/* elapsedRealtimeMillis= */ 1001L + AUTO_REVERT_THRESHOLD,
@@ -310,15 +315,15 @@ public class NotifyingTimeZoneChangeListenerTest {
/* newZoneId= */ "Europe/London",
/* newConfidence= */ 1,
/* cause= */ "NO_REASON"));
- expectedChangeEvent.setStatus(STATUS_UNTRACKED, SIGNAL_TYPE_NONE);
+ expectedTimeZoneChangeRecord.setStatus(STATUS_UNTRACKED, SIGNAL_TYPE_NONE);
- mTimeZoneChangeTracker.process(expectedChangeEvent.getEvent());
- TimeZoneChangeRecord trackedEvent3 =
+ mTimeZoneChangeTracker.process(expectedTimeZoneChangeRecord.getEvent());
+ TimeZoneChangeRecord timeZoneChangeRecord3 =
mTimeZoneChangeTracker.getLastTimeZoneChangeRecord();
// The user manually changed the time zone outside of the period we consider as a revert
- assertEquals(STATUS_SUPERSEDED, trackedEvent2.getStatus());
- assertEquals(expectedChangeEvent, trackedEvent3);
+ assertEquals(STATUS_SUPERSEDED, timeZoneChangeRecord2.getStatus());
+ assertEquals(expectedTimeZoneChangeRecord, timeZoneChangeRecord3);
assertEquals(3, mNotificationManager.getNotifications().size());
mHandler.assertTotalMessagesEnqueued(5);
}
@@ -339,7 +344,7 @@ public class NotifyingTimeZoneChangeListenerTest {
enableNotificationsWithManualChangeTracking();
- TimeZoneChangeRecord expectedChangeEvent = new TimeZoneChangeRecord(
+ TimeZoneChangeRecord expectedTimeZoneChangeRecord = new TimeZoneChangeRecord(
/* id= */ 1,
new TimeZoneChangeEvent(
/* elapsedRealtimeMillis= */ 0,
@@ -350,19 +355,20 @@ public class NotifyingTimeZoneChangeListenerTest {
/* newZoneId= */ "Europe/London",
/* newConfidence= */ 1,
/* cause= */ "NO_REASON"));
- expectedChangeEvent.setStatus(STATUS_UNKNOWN, SIGNAL_TYPE_UNKNOWN);
+ expectedTimeZoneChangeRecord.setStatus(STATUS_UNKNOWN, SIGNAL_TYPE_UNKNOWN);
assertNull(mTimeZoneChangeTracker.getLastTimeZoneChangeRecord());
// lastTrackedChangeEvent == null
- mTimeZoneChangeTracker.process(expectedChangeEvent.getEvent());
- TimeZoneChangeRecord trackedEvent1 = mTimeZoneChangeTracker.getLastTimeZoneChangeRecord();
+ mTimeZoneChangeTracker.process(expectedTimeZoneChangeRecord.getEvent());
+ TimeZoneChangeRecord timeZoneChangeRecord1 =
+ mTimeZoneChangeTracker.getLastTimeZoneChangeRecord();
- assertEquals(expectedChangeEvent, trackedEvent1);
+ assertEquals(expectedTimeZoneChangeRecord, timeZoneChangeRecord1);
assertEquals(1, mNotificationManager.getNotifications().size());
mHandler.assertTotalMessagesEnqueued(1);
- expectedChangeEvent = new TimeZoneChangeRecord(
+ expectedTimeZoneChangeRecord = new TimeZoneChangeRecord(
/* id= */ 3,
new TimeZoneChangeEvent(
/* elapsedRealtimeMillis= */ 1000L,
@@ -373,14 +379,15 @@ public class NotifyingTimeZoneChangeListenerTest {
/* newZoneId= */ "Europe/Paris",
/* newConfidence= */ 1,
/* cause= */ "NO_REASON"));
- expectedChangeEvent.setStatus(STATUS_UNKNOWN, SIGNAL_TYPE_UNKNOWN);
+ expectedTimeZoneChangeRecord.setStatus(STATUS_UNKNOWN, SIGNAL_TYPE_UNKNOWN);
// lastTrackedChangeEvent != null
- mTimeZoneChangeTracker.process(expectedChangeEvent.getEvent());
- TimeZoneChangeRecord trackedEvent2 = mTimeZoneChangeTracker.getLastTimeZoneChangeRecord();
+ mTimeZoneChangeTracker.process(expectedTimeZoneChangeRecord.getEvent());
+ TimeZoneChangeRecord timeZoneChangeRecord2 =
+ mTimeZoneChangeTracker.getLastTimeZoneChangeRecord();
- assertEquals(STATUS_SUPERSEDED, trackedEvent1.getStatus());
- assertEquals(expectedChangeEvent, trackedEvent2);
+ assertEquals(STATUS_SUPERSEDED, timeZoneChangeRecord1.getStatus());
+ assertEquals(expectedTimeZoneChangeRecord, timeZoneChangeRecord2);
assertEquals(2, mNotificationManager.getNotifications().size());
mHandler.assertTotalMessagesEnqueued(2);
}
@@ -400,7 +407,7 @@ public class NotifyingTimeZoneChangeListenerTest {
enableNotificationsWithManualChangeTracking();
- TimeZoneChangeRecord expectedChangeEvent = new TimeZoneChangeRecord(
+ TimeZoneChangeRecord expectedTimeZoneChangeRecord = new TimeZoneChangeRecord(
/* id= */ 1,
new TimeZoneChangeEvent(
/* elapsedRealtimeMillis= */ 0,
@@ -411,15 +418,16 @@ public class NotifyingTimeZoneChangeListenerTest {
/* newZoneId= */ "Europe/Rome",
/* newConfidence= */ 1,
/* cause= */ "NO_REASON"));
- expectedChangeEvent.setStatus(STATUS_UNKNOWN, SIGNAL_TYPE_UNKNOWN);
+ expectedTimeZoneChangeRecord.setStatus(STATUS_UNKNOWN, SIGNAL_TYPE_UNKNOWN);
assertNull(mTimeZoneChangeTracker.getLastTimeZoneChangeRecord());
// lastTrackedChangeEvent == null
- mTimeZoneChangeTracker.process(expectedChangeEvent.getEvent());
- TimeZoneChangeRecord trackedEvent1 = mTimeZoneChangeTracker.getLastTimeZoneChangeRecord();
+ mTimeZoneChangeTracker.process(expectedTimeZoneChangeRecord.getEvent());
+ TimeZoneChangeRecord timeZoneChangeRecord1 =
+ mTimeZoneChangeTracker.getLastTimeZoneChangeRecord();
- assertEquals(expectedChangeEvent, trackedEvent1);
+ assertEquals(expectedTimeZoneChangeRecord, timeZoneChangeRecord1);
// No notification sent for the same UTC offset
assertEquals(0, mNotificationManager.getNotifications().size());
mHandler.assertTotalMessagesEnqueued(1);
diff --git a/services/tests/timetests/src/com/android/server/timezonedetector/TimeZoneDetectorStrategyImplTest.java b/services/tests/timetests/src/com/android/server/timezonedetector/TimeZoneDetectorStrategyImplTest.java
index 9a01fa2eb966..44232e7ddfa2 100644
--- a/services/tests/timetests/src/com/android/server/timezonedetector/TimeZoneDetectorStrategyImplTest.java
+++ b/services/tests/timetests/src/com/android/server/timezonedetector/TimeZoneDetectorStrategyImplTest.java
@@ -63,7 +63,6 @@ import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
-import android.annotation.ElapsedRealtimeLong;
import android.annotation.NonNull;
import android.annotation.UserIdInt;
import android.app.time.LocationTimeZoneAlgorithmStatus;
@@ -94,7 +93,6 @@ import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
-import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
@@ -114,6 +112,7 @@ public class TimeZoneDetectorStrategyImplTest {
@Rule
public final SetFlagsRule mSetFlagsRule = mClassRule.createSetFlagsRule();
+ private static final long ARBITRARY_CURRENT_TIME_MILLIS = 1735689600; // 2025-01-01 00:00:00
private static final long ARBITRARY_ELAPSED_REALTIME_MILLIS = 1234;
/** A time zone used for initialization that does not occur elsewhere in tests. */
private static final String ARBITRARY_TIME_ZONE_ID = "Etc/UTC";
@@ -1220,7 +1219,7 @@ public class TimeZoneDetectorStrategyImplTest {
.build();
Script script = new Script()
- .initializeClock(ARBITRARY_ELAPSED_REALTIME_MILLIS)
+ .initializeClock(ARBITRARY_CURRENT_TIME_MILLIS, ARBITRARY_ELAPSED_REALTIME_MILLIS)
.initializeTimeZoneSetting(ARBITRARY_TIME_ZONE_ID, TIME_ZONE_CONFIDENCE_LOW)
.simulateConfigurationInternalChange(config)
.resetConfigurationTracking();
@@ -1420,7 +1419,7 @@ public class TimeZoneDetectorStrategyImplTest {
.build();
Script script = new Script()
- .initializeClock(ARBITRARY_ELAPSED_REALTIME_MILLIS)
+ .initializeClock(ARBITRARY_CURRENT_TIME_MILLIS, ARBITRARY_ELAPSED_REALTIME_MILLIS)
.initializeTimeZoneSetting(ARBITRARY_TIME_ZONE_ID, TIME_ZONE_CONFIDENCE_LOW)
.simulateConfigurationInternalChange(config)
.resetConfigurationTracking();
@@ -1591,7 +1590,7 @@ public class TimeZoneDetectorStrategyImplTest {
.build();
Script script = new Script()
- .initializeClock(ARBITRARY_ELAPSED_REALTIME_MILLIS)
+ .initializeClock(ARBITRARY_CURRENT_TIME_MILLIS, ARBITRARY_ELAPSED_REALTIME_MILLIS)
.initializeTimeZoneSetting(ARBITRARY_TIME_ZONE_ID, TIME_ZONE_CONFIDENCE_LOW)
.simulateConfigurationInternalChange(config)
.resetConfigurationTracking();
@@ -1666,7 +1665,7 @@ public class TimeZoneDetectorStrategyImplTest {
@Test
public void testGetTimeZoneState() {
Script script = new Script()
- .initializeClock(ARBITRARY_ELAPSED_REALTIME_MILLIS)
+ .initializeClock(ARBITRARY_CURRENT_TIME_MILLIS, ARBITRARY_ELAPSED_REALTIME_MILLIS)
.initializeTimeZoneSetting(ARBITRARY_TIME_ZONE_ID, TIME_ZONE_CONFIDENCE_LOW)
.simulateConfigurationInternalChange(CONFIG_AUTO_DISABLED_GEO_DISABLED)
.resetConfigurationTracking();
@@ -1688,7 +1687,7 @@ public class TimeZoneDetectorStrategyImplTest {
@Test
public void testSetTimeZoneState() {
Script script = new Script()
- .initializeClock(ARBITRARY_ELAPSED_REALTIME_MILLIS)
+ .initializeClock(ARBITRARY_CURRENT_TIME_MILLIS, ARBITRARY_ELAPSED_REALTIME_MILLIS)
.initializeTimeZoneSetting(ARBITRARY_TIME_ZONE_ID, TIME_ZONE_CONFIDENCE_LOW)
.simulateConfigurationInternalChange(CONFIG_AUTO_DISABLED_GEO_DISABLED)
.resetConfigurationTracking();
@@ -1715,7 +1714,7 @@ public class TimeZoneDetectorStrategyImplTest {
private void testConfirmTimeZone(ConfigurationInternal config) {
String timeZoneId = "Europe/London";
Script script = new Script()
- .initializeClock(ARBITRARY_ELAPSED_REALTIME_MILLIS)
+ .initializeClock(ARBITRARY_CURRENT_TIME_MILLIS, ARBITRARY_ELAPSED_REALTIME_MILLIS)
.initializeTimeZoneSetting(timeZoneId, TIME_ZONE_CONFIDENCE_LOW)
.simulateConfigurationInternalChange(config)
.resetConfigurationTracking();
@@ -1902,97 +1901,6 @@ public class TimeZoneDetectorStrategyImplTest {
mFakeEnvironment.elapsedRealtimeMillis(), Arrays.asList(zoneIds));
}
- static class FakeEnvironment implements TimeZoneDetectorStrategyImpl.Environment {
-
- private final TestState<String> mTimeZoneId = new TestState<>();
- private final TestState<Integer> mTimeZoneConfidence = new TestState<>();
- private final List<Runnable> mAsyncRunnables = new ArrayList<>();
- private @ElapsedRealtimeLong long mElapsedRealtimeMillis;
-
- FakeEnvironment() {
- // Ensure the fake environment starts with the defaults a fresh device would.
- initializeTimeZoneSetting("", TIME_ZONE_CONFIDENCE_LOW);
- }
-
- void initializeClock(@ElapsedRealtimeLong long elapsedRealtimeMillis) {
- mElapsedRealtimeMillis = elapsedRealtimeMillis;
- }
-
- void initializeTimeZoneSetting(String zoneId, @TimeZoneConfidence int timeZoneConfidence) {
- mTimeZoneId.init(zoneId);
- mTimeZoneConfidence.init(timeZoneConfidence);
- }
-
- void incrementClock() {
- mElapsedRealtimeMillis++;
- }
-
- @Override
- public String getDeviceTimeZone() {
- return mTimeZoneId.getLatest();
- }
-
- @Override
- public int getDeviceTimeZoneConfidence() {
- return mTimeZoneConfidence.getLatest();
- }
-
- @Override
- public void setDeviceTimeZoneAndConfidence(
- String zoneId, @TimeZoneConfidence int confidence, String logInfo) {
- mTimeZoneId.set(zoneId);
- mTimeZoneConfidence.set(confidence);
- }
-
- void assertTimeZoneNotChanged() {
- mTimeZoneId.assertHasNotBeenSet();
- mTimeZoneConfidence.assertHasNotBeenSet();
- }
-
- void assertTimeZoneChangedTo(String timeZoneId, @TimeZoneConfidence int confidence) {
- mTimeZoneId.assertHasBeenSet();
- mTimeZoneId.assertChangeCount(1);
- mTimeZoneId.assertLatestEquals(timeZoneId);
-
- mTimeZoneConfidence.assertHasBeenSet();
- mTimeZoneConfidence.assertChangeCount(1);
- mTimeZoneConfidence.assertLatestEquals(confidence);
- }
-
- void commitAllChanges() {
- mTimeZoneId.commitLatest();
- mTimeZoneConfidence.commitLatest();
- }
-
- @Override
- @ElapsedRealtimeLong
- public long elapsedRealtimeMillis() {
- return mElapsedRealtimeMillis;
- }
-
- @Override
- public void addDebugLogEntry(String logMsg) {
- // No-op for tests
- }
-
- @Override
- public void dumpDebugLog(PrintWriter printWriter) {
- // No-op for tests
- }
-
- @Override
- public void runAsync(Runnable runnable) {
- mAsyncRunnables.add(runnable);
- }
-
- public void runAsyncRunnables() {
- for (Runnable runnable : mAsyncRunnables) {
- runnable.run();
- }
- mAsyncRunnables.clear();
- }
- }
-
private void assertStateChangeNotificationsSent(
TestStateChangeListener stateChangeListener, int expectedCount) {
// The fake environment needs to be told to run posted work.
@@ -2013,8 +1921,8 @@ public class TimeZoneDetectorStrategyImplTest {
return this;
}
- Script initializeClock(long elapsedRealtimeMillis) {
- mFakeEnvironment.initializeClock(elapsedRealtimeMillis);
+ Script initializeClock(long currentTimeMillis, long elapsedRealtimeMillis) {
+ mFakeEnvironment.initializeClock(currentTimeMillis, elapsedRealtimeMillis);
return this;
}
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationAssistantsTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationAssistantsTest.java
index 4f5cdb73edd2..1df8e3deb84b 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationAssistantsTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationAssistantsTest.java
@@ -64,6 +64,8 @@ import android.testing.TestableContext;
import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.IntArray;
+import android.util.StatsEvent;
+import android.util.StatsEventTestUtils;
import android.util.Xml;
import androidx.test.runner.AndroidJUnit4;
@@ -71,9 +73,17 @@ import androidx.test.runner.AndroidJUnit4;
import com.android.internal.util.CollectionUtils;
import com.android.modules.utils.TypedXmlPullParser;
import com.android.modules.utils.TypedXmlSerializer;
+import com.android.os.AtomsProto;
+import com.android.os.notification.NotificationBundlePreferences;
+import com.android.os.notification.NotificationExtensionAtoms;
+import com.android.os.notification.NotificationProtoEnums;
import com.android.server.UiServiceTestCase;
import com.android.server.notification.NotificationManagerService.NotificationAssistants;
+import com.google.protobuf.CodedInputStream;
+import com.google.protobuf.CodedOutputStream;
+import com.google.protobuf.ExtensionRegistryLite;
+
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
@@ -120,6 +130,8 @@ public class NotificationAssistantsTest extends UiServiceTestCase {
ComponentName mCn = new ComponentName("a", "b");
+ private ExtensionRegistryLite mRegistry;
+
// Helper function to hold mApproved lock, avoid GuardedBy lint errors
private boolean isUserSetServicesEmpty(NotificationAssistants assistant, int userId) {
@@ -204,6 +216,8 @@ public class NotificationAssistantsTest extends UiServiceTestCase {
when(mUserProfiles.getCurrentProfileIds()).thenReturn(profileIds);
when(mNm.isNASMigrationDone(anyInt())).thenReturn(true);
when(mNm.canUseManagedServices(any(), anyInt(), any())).thenReturn(true);
+ mRegistry = ExtensionRegistryLite.newInstance();
+ NotificationExtensionAtoms.registerAllExtensions(mRegistry);
}
@Test
@@ -749,6 +763,28 @@ public class NotificationAssistantsTest extends UiServiceTestCase {
}
@Test
+ @EnableFlags({android.service.notification.Flags.FLAG_NOTIFICATION_CLASSIFICATION,
+ android.app.Flags.FLAG_NOTIFICATION_CLASSIFICATION_UI})
+ public void testGetPackagesWithKeyTypeAdjustmentSettings() throws Exception {
+ String pkg = "my.package";
+ String pkg2 = "my.package.2";
+ setDefaultAllowedAdjustmentKeyTypes(mAssistants);
+ assertThat(mAssistants.isTypeAdjustmentAllowedForPackage(pkg, TYPE_PROMOTION)).isTrue();
+ assertThat(mAssistants.getPackagesWithKeyTypeAdjustmentSettings()).isEmpty();
+
+ mAssistants.setAssistantAdjustmentKeyTypeStateForPackage(pkg, TYPE_PROMOTION, true);
+ assertThat(mAssistants.getPackagesWithKeyTypeAdjustmentSettings())
+ .containsExactly(pkg);
+ mAssistants.setAssistantAdjustmentKeyTypeStateForPackage(pkg, TYPE_PROMOTION, false);
+ assertThat(mAssistants.getPackagesWithKeyTypeAdjustmentSettings())
+ .containsExactly(pkg);
+ mAssistants.setAssistantAdjustmentKeyTypeStateForPackage(pkg2, TYPE_NEWS, true);
+ mAssistants.setAssistantAdjustmentKeyTypeStateForPackage(pkg2, TYPE_PROMOTION, false);
+ assertThat(mAssistants.getPackagesWithKeyTypeAdjustmentSettings())
+ .containsExactly(pkg, pkg2);
+ }
+
+ @Test
@EnableFlags(android.app.Flags.FLAG_NOTIFICATION_CLASSIFICATION_UI)
public void testSetAssistantAdjustmentKeyTypeStateForPackage_usesGlobalDefault() {
String pkg = "my.package";
@@ -892,4 +928,88 @@ public class NotificationAssistantsTest extends UiServiceTestCase {
assertThat(mAssistants.getAllowedAdjustmentKeyTypesForPackage(pkg)).asList()
.containsExactly(TYPE_NEWS, TYPE_PROMOTION, TYPE_SOCIAL_MEDIA);
}
+
+ @Test
+ @SuppressWarnings("GuardedBy")
+ @EnableFlags({android.service.notification.Flags.FLAG_NOTIFICATION_CLASSIFICATION,
+ android.app.Flags.FLAG_NOTIFICATION_CLASSIFICATION_UI})
+ public void testPullBundlePreferencesStats_fillsOutStatsEvent()
+ throws Exception {
+ // Create the current user and enable the package
+ int userId = ActivityManager.getCurrentUser();
+ mAssistants.loadDefaultsFromConfig(true);
+ mAssistants.setPackageOrComponentEnabled(mCn.flattenToString(), userId, true,
+ true, true);
+ ManagedServices.ManagedServiceInfo info =
+ mAssistants.new ManagedServiceInfo(null, mCn, userId, false, null, 35, 2345256);
+
+ // Ensure bundling is enabled
+ mAssistants.setAdjustmentTypeSupportedState(info, Adjustment.KEY_TYPE, true);
+ // Enable these specific bundle types
+ mAssistants.setAssistantAdjustmentKeyTypeState(TYPE_PROMOTION, false);
+ mAssistants.setAssistantAdjustmentKeyTypeState(TYPE_NEWS, true);
+ mAssistants.setAssistantAdjustmentKeyTypeState(TYPE_CONTENT_RECOMMENDATION, true);
+
+ // When pullBundlePreferencesStats is run with the given preferences
+ ArrayList<StatsEvent> events = new ArrayList<>();
+ mAssistants.pullBundlePreferencesStats(events);
+
+ // The StatsEvent is filled out with the expected NotificationBundlePreferences values.
+ assertThat(events.size()).isEqualTo(1);
+ AtomsProto.Atom atom = StatsEventTestUtils.convertToAtom(events.get(0));
+
+ // The returned atom does not have external extensions registered.
+ // So we serialize and then deserialize with extensions registered.
+ ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
+ CodedOutputStream codedos = CodedOutputStream.newInstance(outputStream);
+ atom.writeTo(codedos);
+ codedos.flush();
+
+ ByteArrayInputStream inputStream = new ByteArrayInputStream(outputStream.toByteArray());
+ CodedInputStream codedis = CodedInputStream.newInstance(inputStream);
+ atom = AtomsProto.Atom.parseFrom(codedis, mRegistry);
+ assertTrue(atom.hasExtension(NotificationExtensionAtoms.notificationBundlePreferences));
+ NotificationBundlePreferences p =
+ atom.getExtension(NotificationExtensionAtoms.notificationBundlePreferences);
+ assertThat(p.getBundlesAllowed()).isTrue();
+ assertThat(p.getAllowedBundleTypes(0).getNumber())
+ .isEqualTo(NotificationProtoEnums.TYPE_NEWS);
+ assertThat(p.getAllowedBundleTypes(1).getNumber())
+ .isEqualTo(NotificationProtoEnums.TYPE_CONTENT_RECOMMENDATION);
+
+ // Disable the top-level bundling setting
+ mAssistants.setAdjustmentTypeSupportedState(info, Adjustment.KEY_TYPE, false);
+ // Enable these specific bundle types
+ mAssistants.setAssistantAdjustmentKeyTypeState(TYPE_PROMOTION, true);
+ mAssistants.setAssistantAdjustmentKeyTypeState(TYPE_NEWS, false);
+ mAssistants.setAssistantAdjustmentKeyTypeState(TYPE_CONTENT_RECOMMENDATION, true);
+
+ ArrayList<StatsEvent> eventsDisabled = new ArrayList<>();
+ mAssistants.pullBundlePreferencesStats(eventsDisabled);
+
+ // The StatsEvent is filled out with the expected NotificationBundlePreferences values.
+ assertThat(eventsDisabled.size()).isEqualTo(1);
+ AtomsProto.Atom atomDisabled = StatsEventTestUtils.convertToAtom(eventsDisabled.get(0));
+
+ // The returned atom does not have external extensions registered.
+ // So we serialize and then deserialize with extensions registered.
+ outputStream = new ByteArrayOutputStream();
+ codedos = CodedOutputStream.newInstance(outputStream);
+ atomDisabled.writeTo(codedos);
+ codedos.flush();
+
+ inputStream = new ByteArrayInputStream(outputStream.toByteArray());
+ codedis = CodedInputStream.newInstance(inputStream);
+ atomDisabled = AtomsProto.Atom.parseFrom(codedis, mRegistry);
+ assertTrue(atomDisabled.hasExtension(NotificationExtensionAtoms
+ .notificationBundlePreferences));
+
+ NotificationBundlePreferences p2 =
+ atomDisabled.getExtension(NotificationExtensionAtoms.notificationBundlePreferences);
+ assertThat(p2.getBundlesAllowed()).isFalse();
+ assertThat(p2.getAllowedBundleTypes(0).getNumber())
+ .isEqualTo(NotificationProtoEnums.TYPE_PROMOTION);
+ assertThat(p2.getAllowedBundleTypes(1).getNumber())
+ .isEqualTo(NotificationProtoEnums.TYPE_CONTENT_RECOMMENDATION);
+ }
} \ No newline at end of file
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java
index f41805d40b0d..4bc286117ac6 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java
@@ -19,6 +19,7 @@ import static android.app.AppOpsManager.MODE_ALLOWED;
import static android.app.AppOpsManager.MODE_DEFAULT;
import static android.app.AppOpsManager.OP_SYSTEM_ALERT_WINDOW;
import static android.app.Flags.FLAG_MODES_UI;
+import static android.app.Flags.FLAG_NOTIFICATION_CLASSIFICATION_UI;
import static android.app.Notification.VISIBILITY_PRIVATE;
import static android.app.Notification.VISIBILITY_SECRET;
import static android.app.NotificationChannel.ALLOW_BUBBLE_ON;
@@ -161,6 +162,7 @@ import com.android.modules.utils.TypedXmlSerializer;
import com.android.os.AtomsProto;
import com.android.os.AtomsProto.PackageNotificationChannelPreferences;
import com.android.os.AtomsProto.PackageNotificationPreferences;
+import com.android.os.notification.NotificationProtoEnums;
import com.android.server.UiServiceTestCase;
import com.android.server.notification.PermissionHelper.PackagePermission;
import com.android.server.uri.UriGrantsManagerInternal;
@@ -256,7 +258,8 @@ public class PreferencesHelperTest extends UiServiceTestCase {
public static List<FlagsParameterization> getParams() {
return FlagsParameterization.allCombinationsOf(
android.app.Flags.FLAG_API_RICH_ONGOING,
- FLAG_NOTIFICATION_CLASSIFICATION, FLAG_MODES_UI);
+ FLAG_NOTIFICATION_CLASSIFICATION, FLAG_NOTIFICATION_CLASSIFICATION_UI,
+ FLAG_MODES_UI);
}
public PreferencesHelperTest(FlagsParameterization flags) {
@@ -6135,6 +6138,7 @@ public class PreferencesHelperTest extends UiServiceTestCase {
}
@Test
+ @DisableFlags({FLAG_NOTIFICATION_CLASSIFICATION_UI})
public void testPullPackagePreferencesStats_postPermissionMigration()
throws InvalidProtocolBufferException {
// make sure there's at least one channel for each package we want to test
@@ -6155,6 +6159,11 @@ public class PreferencesHelperTest extends UiServiceTestCase {
mHelper.canShowBadge(PKG_O, UID_O);
mHelper.canShowBadge(PKG_P, UID_P);
+ ArrayList<StatsEvent> events = new ArrayList<>();
+
+ mHelper.pullPackagePreferencesStats(events, appPermissions,
+ new ArrayMap<String, Set<Integer>>());
+
// expected output. format: uid -> importance, as only uid (and not package name)
// is in PackageNotificationPreferences
ArrayMap<Integer, Pair<Integer, Boolean>> expected = new ArrayMap<>();
@@ -6162,9 +6171,6 @@ public class PreferencesHelperTest extends UiServiceTestCase {
expected.put(UID_O, new Pair<>(IMPORTANCE_NONE, true)); // banned by permissions
expected.put(UID_P, new Pair<>(IMPORTANCE_UNSPECIFIED, false)); // default: unspecified
- ArrayList<StatsEvent> events = new ArrayList<>();
- mHelper.pullPackagePreferencesStats(events, appPermissions);
-
assertEquals("total number of packages", 3, events.size());
for (StatsEvent ev : events) {
AtomsProto.Atom atom = StatsEventTestUtils.convertToAtom(ev);
@@ -6180,6 +6186,74 @@ public class PreferencesHelperTest extends UiServiceTestCase {
}
@Test
+ @EnableFlags({FLAG_NOTIFICATION_CLASSIFICATION, FLAG_NOTIFICATION_CLASSIFICATION_UI})
+ public void testPullPackagePreferencesStats_createsExpectedStatsEvents()
+ throws InvalidProtocolBufferException {
+ // make sure there's at least one channel for each package we want to test
+ NotificationChannel channelA = new NotificationChannel("a", "a", IMPORTANCE_DEFAULT);
+ mHelper.createNotificationChannel(PKG_N_MR1, UID_N_MR1, channelA, true, false,
+ UID_N_MR1, false);
+ NotificationChannel channelB = new NotificationChannel("b", "b", IMPORTANCE_DEFAULT);
+ mHelper.createNotificationChannel(PKG_O, UID_O, channelB, true, false, UID_O, false);
+ NotificationChannel channelC = new NotificationChannel("c", "c", IMPORTANCE_DEFAULT);
+ mHelper.createNotificationChannel(PKG_P, UID_P, channelC, true, false, UID_P, false);
+
+ // build a collection of app permissions that should be passed in and used
+ ArrayMap<Pair<Integer, String>, Pair<Boolean, Boolean>> pkgPermissions = new ArrayMap<>();
+ pkgPermissions.put(new Pair<>(UID_N_MR1, PKG_N_MR1), new Pair<>(true, false));
+ pkgPermissions.put(new Pair<>(UID_O, PKG_O), new Pair<>(false, true)); // in local prefs
+
+ // local preferences
+ mHelper.canShowBadge(PKG_O, UID_O);
+ mHelper.canShowBadge(PKG_P, UID_P);
+
+ // Sets bundles_allowed to true for these packages.
+ ArrayMap<String, Set<Integer>> packageSpecificAdjustmentKeyTypes = new ArrayMap<>();
+ Set<Integer> nMr1BundlesSet = new ArraySet<Integer>();
+ nMr1BundlesSet.add(TYPE_NEWS);
+ nMr1BundlesSet.add(TYPE_SOCIAL_MEDIA);
+ packageSpecificAdjustmentKeyTypes.put(PKG_N_MR1, nMr1BundlesSet);
+ Set<Integer> pBundlesSet = new ArraySet<Integer>();
+ packageSpecificAdjustmentKeyTypes.put(PKG_P, pBundlesSet);
+
+ ArrayList<StatsEvent> events = new ArrayList<>();
+
+ mHelper.pullPackagePreferencesStats(events, pkgPermissions,
+ packageSpecificAdjustmentKeyTypes);
+
+ assertEquals("total number of packages", 3, events.size());
+
+ AtomsProto.Atom atom0 = StatsEventTestUtils.convertToAtom(events.get(0));
+ assertTrue(atom0.hasPackageNotificationPreferences());
+ PackageNotificationPreferences p0 = atom0.getPackageNotificationPreferences();
+ assertThat(p0.getUid()).isEqualTo(UID_O);
+ assertThat(p0.getImportance()).isEqualTo(IMPORTANCE_NONE); // banned by permissions
+ assertThat(p0.getUserSetImportance()).isTrue();
+ assertThat(p0.getAllowedBundleTypesList()).hasSize(0);
+
+ AtomsProto.Atom atom1 = StatsEventTestUtils.convertToAtom(events.get(1));
+ assertTrue(atom1.hasPackageNotificationPreferences());
+ PackageNotificationPreferences p1 = atom1.getPackageNotificationPreferences();
+ assertThat(p1.getUid()).isEqualTo(UID_N_MR1);
+ assertThat(p1.getImportance()).isEqualTo(IMPORTANCE_DEFAULT);
+ assertThat(p1.getUserSetImportance()).isFalse();
+ assertThat(p1.getAllowedBundleTypesList()).hasSize(2);
+
+ assertThat(p1.getAllowedBundleTypes(0).getNumber())
+ .isEqualTo(NotificationProtoEnums.TYPE_SOCIAL_MEDIA);
+ assertThat(p1.getAllowedBundleTypes(1).getNumber())
+ .isEqualTo(NotificationProtoEnums.TYPE_NEWS);
+
+ AtomsProto.Atom atom2 = StatsEventTestUtils.convertToAtom(events.get(2));
+ assertTrue(atom2.hasPackageNotificationPreferences());
+ PackageNotificationPreferences p2 = atom2.getPackageNotificationPreferences();
+ assertThat(p2.getUid()).isEqualTo(UID_P);
+ assertThat(p2.getImportance()).isEqualTo(IMPORTANCE_UNSPECIFIED); // default: unspecified
+ assertThat(p2.getUserSetImportance()).isFalse();
+ assertThat(p2.getAllowedBundleTypesList()).hasSize(0);
+ }
+
+ @Test
public void testUnlockNotificationChannelImportance() {
NotificationChannel channel = new NotificationChannel("a", "a", IMPORTANCE_LOW);
mHelper.createNotificationChannel(PKG_O, UID_O, channel, true, false, UID_O, false);
diff --git a/services/tests/wmtests/Android.bp b/services/tests/wmtests/Android.bp
index 132f95bea360..b328fc2d5868 100644
--- a/services/tests/wmtests/Android.bp
+++ b/services/tests/wmtests/Android.bp
@@ -11,19 +11,46 @@ package {
default_applicable_licenses: ["frameworks_base_license"],
}
-// Include all test java files.
filegroup {
- name: "wmtests-sources",
+ name: "wmtests-support-sources",
srcs: [
- "src/**/*.java",
+ "src/com/android/server/wm/WindowManagerServiceTestSupport.kt",
],
+ path: "src",
+ visibility: ["//visibility:private"],
+}
+
+java_library {
+ name: "wmtests-support",
+ srcs: [":wmtests-support-sources"],
+ static_libs: [
+ "com.android.window.flags.window-aconfig-java",
+ "kotlin-stdlib",
+ "services.core",
+ ],
+ lint: {
+ test: true,
+ },
+ visibility: [
+ "//frameworks/base/services/tests/wmtests",
+ "//frameworks/opt/car/services/updatableServices/tests",
+ ],
+}
+
+// Include all test files, but exclude test support files.
+filegroup {
+ name: "wmtests-sources",
+ srcs: ["src/**/*.java"],
+ exclude_srcs: [":wmtests-support-sources"],
+ path: "src",
+ visibility: ["//visibility:private"],
}
java_genrule {
name: "wmtests.protologsrc",
srcs: [
- ":protolog-impl",
":protolog-groups",
+ ":protolog-impl",
":wmtests-sources",
],
tools: ["protologtool"],
@@ -52,33 +79,34 @@ android_test {
],
static_libs: [
- "frameworks-base-testutils",
- "services.core",
- "service-permission.stubs.system_server",
- "androidx.test.runner",
+ "CtsSurfaceValidatorLib",
+ "android.view.inputmethod.flags-aconfig-java",
"androidx.test.rules",
+ "androidx.test.runner",
+ "com.android.window.flags.window-aconfig-java",
+ "flag-junit",
"flickerlib",
+ "frameworks-base-testutils",
+ "hamcrest-library",
"junit-params",
+ "mockito-kotlin2",
"mockito-target-extended-minus-junit4",
+ "platform-compat-test-rules",
"platform-test-annotations",
+ "service-permission.stubs.system_server",
+ "service-sdksandbox.impl",
+ "services.core",
"servicestests-utils",
+ "testables",
"testng",
"truth",
- "testables",
- "hamcrest-library",
- "flag-junit",
- "platform-compat-test-rules",
- "CtsSurfaceValidatorLib",
- "service-sdksandbox.impl",
- "com.android.window.flags.window-aconfig-java",
- "android.view.inputmethod.flags-aconfig-java",
- "flag-junit",
+ "wmtests-support",
],
libs: [
"android.hardware.power-V1-java",
- "android.test.mock.stubs.system",
"android.test.base.stubs.system",
+ "android.test.mock.stubs.system",
"android.test.runner.stubs.system",
],
@@ -94,8 +122,8 @@ android_test {
platform_apis: true,
test_suites: [
- "device-tests",
"automotive-tests",
+ "device-tests",
],
certificate: "platform",
diff --git a/services/tests/wmtests/src/com/android/server/wm/SystemServicesTestRule.java b/services/tests/wmtests/src/com/android/server/wm/SystemServicesTestRule.java
index a12831e1ccf1..a95093d7e113 100644
--- a/services/tests/wmtests/src/com/android/server/wm/SystemServicesTestRule.java
+++ b/services/tests/wmtests/src/com/android/server/wm/SystemServicesTestRule.java
@@ -43,6 +43,7 @@ import static org.mockito.Mockito.CALLS_REAL_METHODS;
import static org.mockito.Mockito.when;
import static org.mockito.Mockito.withSettings;
+import android.annotation.Nullable;
import android.app.ActivityManagerInternal;
import android.app.ActivityThread;
import android.app.AppOpsManager;
@@ -67,7 +68,6 @@ import android.os.Looper;
import android.os.PowerManager;
import android.os.PowerManagerInternal;
import android.os.PowerSaveState;
-import android.os.StrictMode;
import android.os.UserHandle;
import android.os.UserManager;
import android.provider.DeviceConfig;
@@ -75,7 +75,6 @@ import android.util.Log;
import android.view.DisplayInfo;
import android.view.InputChannel;
import android.view.SurfaceControl;
-import android.window.ConfigurationChangeSetting;
import com.android.dx.mockito.inline.extended.StaticMockitoSession;
import com.android.internal.os.BackgroundThread;
@@ -96,11 +95,9 @@ import com.android.server.input.InputManagerService;
import com.android.server.pm.UserManagerInternal;
import com.android.server.pm.UserManagerService;
import com.android.server.policy.PermissionPolicyInternal;
-import com.android.server.policy.WindowManagerPolicy;
import com.android.server.statusbar.StatusBarManagerInternal;
import com.android.server.testutils.StubTransaction;
import com.android.server.uri.UriGrantsManagerInternal;
-import com.android.window.flags.Flags;
import org.junit.rules.TestRule;
import org.junit.runner.Description;
@@ -144,13 +141,15 @@ public class SystemServicesTestRule implements TestRule {
private ActivityTaskManagerService mAtmService;
private WindowManagerService mWmService;
private InputManagerService mImService;
- private Runnable mOnBeforeServicesCreated;
+ @Nullable
+ private final Runnable mOnBeforeServicesCreated;
+
/**
* Spied {@link SurfaceControl.Transaction} class than can be used to verify calls.
*/
SurfaceControl.Transaction mTransaction;
- public SystemServicesTestRule(Runnable onBeforeServicesCreated) {
+ public SystemServicesTestRule(@Nullable Runnable onBeforeServicesCreated) {
mOnBeforeServicesCreated = onBeforeServicesCreated;
}
@@ -398,15 +397,13 @@ public class SystemServicesTestRule implements TestRule {
}
private void setUpWindowManagerService() {
- TestWindowManagerPolicy wmPolicy = new TestWindowManagerPolicy();
- TestDisplayWindowSettingsProvider testDisplayWindowSettingsProvider =
- new TestDisplayWindowSettingsProvider();
- // Suppress StrictMode violation (DisplayWindowSettings) to avoid log flood.
- DisplayThread.getHandler().post(StrictMode::allowThreadDiskWritesMask);
- mWmService = WindowManagerService.main(
- mContext, mImService, false, wmPolicy, mAtmService,
- testDisplayWindowSettingsProvider, StubTransaction::new,
- MockSurfaceControlBuilder::new, mAppCompat);
+ // Use a spied Transaction class to prevent native code calls and verify interactions.
+ mTransaction = spy(StubTransaction.class);
+
+ mWmService = WindowManagerServiceTestSupport.setUpService(mContext, mImService,
+ new TestWindowManagerPolicy(), mAtmService, new TestDisplayWindowSettingsProvider(),
+ mTransaction, new MockSurfaceControlBuilder(), mAppCompat);
+
spyOn(mWmService);
spyOn(mWmService.mRoot);
// Invoked during {@link ActivityStack} creation.
@@ -418,10 +415,6 @@ public class SystemServicesTestRule implements TestRule {
spyOn(mWmService.mDisplayWindowSettings);
spyOn(mWmService.mDisplayWindowSettingsProvider);
- // Setup factory classes to prevent calls to native code.
- mTransaction = spy(StubTransaction.class);
- // Return a spied Transaction class than can be used to verify calls.
- mWmService.mTransactionFactory = () -> mTransaction;
mWmService.mSurfaceAnimationRunner = new SurfaceAnimationRunner(
null, null, mTransaction, mWmService.mPowerManagerInternal);
@@ -488,12 +481,12 @@ public class SystemServicesTestRule implements TestRule {
}
private static void tearDownLocalServices() {
+ WindowManagerServiceTestSupport.tearDownService();
+
LocalServices.removeServiceForTest(DisplayManagerInternal.class);
LocalServices.removeServiceForTest(PowerManagerInternal.class);
LocalServices.removeServiceForTest(ActivityManagerInternal.class);
LocalServices.removeServiceForTest(ActivityTaskManagerInternal.class);
- LocalServices.removeServiceForTest(WindowManagerInternal.class);
- LocalServices.removeServiceForTest(WindowManagerPolicy.class);
LocalServices.removeServiceForTest(PackageManagerInternal.class);
LocalServices.removeServiceForTest(UriGrantsManagerInternal.class);
LocalServices.removeServiceForTest(PermissionPolicyInternal.class);
@@ -501,12 +494,7 @@ public class SystemServicesTestRule implements TestRule {
LocalServices.removeServiceForTest(UsageStatsManagerInternal.class);
LocalServices.removeServiceForTest(StatusBarManagerInternal.class);
LocalServices.removeServiceForTest(UserManagerInternal.class);
- LocalServices.removeServiceForTest(ImeTargetVisibilityPolicy.class);
LocalServices.removeServiceForTest(GrammaticalInflectionManagerInternal.class);
- if (Flags.condenseConfigurationChangeForSimpleMode()) {
- LocalServices.removeServiceForTest(
- ConfigurationChangeSetting.ConfigurationChangeSettingInternal.class);
- }
}
Description getDescription() {
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceTestSupport.kt b/services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceTestSupport.kt
new file mode 100644
index 000000000000..a165d20eb5c1
--- /dev/null
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceTestSupport.kt
@@ -0,0 +1,112 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.wm
+
+import android.content.Context
+import android.os.StrictMode
+import android.view.SurfaceControl
+import android.window.ConfigurationChangeSetting
+import com.android.server.DisplayThread
+import com.android.server.LocalServices
+import com.android.server.input.InputManagerService
+import com.android.server.policy.WindowManagerPolicy
+import com.android.window.flags.Flags
+
+/**
+ * Provides support for tests that require a [WindowManagerService].
+ *
+ * It provides functionalities for setting up and tearing down the service with proper dependencies,
+ * which can be used across different test modules.
+ */
+object WindowManagerServiceTestSupport {
+
+ /**
+ * Sets up and initializes a [WindowManagerService] instance with the provided dependencies.
+ *
+ * This method constructs a [WindowManagerService] using the provided dependencies for testing.
+ * It's marked as `internal` due to the package-private classes [DisplayWindowSettingsProvider]
+ * and [AppCompatConfiguration]. The `@JvmName` annotation is used to bypass name mangling and
+ * allow access from Java via `WindowManagerServiceTestSupport.setUpService`.
+ *
+ * **Important:** Before calling this method, ensure that any previous [WindowManagerService]
+ * instance and its related services are properly torn down. In your test's setup, it is
+ * recommended to call [tearDownService] before calling [setUpService] to handle cases where a
+ * previous test might have crashed and left services in an inconsistent state. This is crucial
+ * for test reliability.
+ *
+ * Example usage in a test's `setUp()` method:
+ * ```
+ * @Before
+ * fun setUp() {
+ * WindowManagerServiceTestSupport.tearDownService() // Clean up before setup.
+ * mWindowManagerService = WindowManagerServiceTestSupport.setUpService(...)
+ * // ... rest of your setup logic ...
+ * }
+ * ```
+ *
+ * @param context the [Context] for the service.
+ * @param im the [InputManagerService] to use.
+ * @param policy the [WindowManagerPolicy] to use.
+ * @param atm the [ActivityTaskManagerService] to use.
+ * @param displayWindowSettingsProvider the [DisplayWindowSettingsProvider] to use.
+ * @param surfaceControlTransaction the [SurfaceControl.Transaction] instance to use.
+ * @param surfaceControlBuilder the [SurfaceControl.Builder] instance to use.
+ * @param appCompat the [AppCompatConfiguration] to use.
+ * @return the created [WindowManagerService] instance.
+ */
+ @JvmStatic
+ @JvmName("setUpService")
+ internal fun setUpService(
+ context: Context,
+ im: InputManagerService,
+ policy: WindowManagerPolicy,
+ atm: ActivityTaskManagerService,
+ displayWindowSettingsProvider: DisplayWindowSettingsProvider,
+ surfaceControlTransaction: SurfaceControl.Transaction,
+ surfaceControlBuilder: SurfaceControl.Builder,
+ appCompat: AppCompatConfiguration,
+ ): WindowManagerService {
+ // Suppress StrictMode violation (DisplayWindowSettings) to avoid log flood.
+ DisplayThread.getHandler().post { StrictMode.allowThreadDiskWritesMask() }
+
+ return WindowManagerService.main(
+ context,
+ im,
+ false, /* showBootMsgs */
+ policy,
+ atm,
+ displayWindowSettingsProvider,
+ { surfaceControlTransaction },
+ { surfaceControlBuilder },
+ appCompat,
+ )
+ }
+
+ /** Tears down the [WindowManagerService] and removes related local services. */
+ @JvmStatic
+ fun tearDownService() {
+ LocalServices.removeServiceForTest(WindowManagerPolicy::class.java)
+ LocalServices.removeServiceForTest(WindowManagerInternal::class.java)
+ LocalServices.removeServiceForTest(ImeTargetVisibilityPolicy::class.java)
+
+ if (Flags.condenseConfigurationChangeForSimpleMode()) {
+ LocalServices.removeServiceForTest(
+ ConfigurationChangeSetting.ConfigurationChangeSettingInternal::class.java,
+ )
+ }
+ }
+}