summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--TEST_MAPPING6
-rw-r--r--apex/jobscheduler/service/aconfig/job.aconfig12
-rw-r--r--apex/jobscheduler/service/java/com/android/server/job/controllers/QuotaController.java96
-rw-r--r--core/api/module-lib-current.txt1
-rw-r--r--core/api/system-current.txt1
-rw-r--r--core/api/test-current.txt2
-rw-r--r--core/java/android/app/AutomaticZenRule.java12
-rw-r--r--core/java/android/app/ContextImpl.java4
-rw-r--r--core/java/android/app/PropertyInvalidatedCache.java37
-rw-r--r--core/java/android/app/supervision/SupervisionManager.java2
-rw-r--r--core/java/android/content/pm/RegisteredServicesCache.java36
-rw-r--r--core/java/android/hardware/camera2/marshal/impl/MarshalQueryableParcelable.java13
-rw-r--r--core/java/android/hardware/display/DisplayManager.java5
-rw-r--r--core/java/android/os/IpcDataCache.java5
-rw-r--r--core/java/android/os/Parcel.java78
-rw-r--r--core/java/android/os/flags.aconfig8
-rw-r--r--core/java/android/permission/PermissionManager.java6
-rw-r--r--core/java/android/provider/Settings.java29
-rw-r--r--core/java/android/service/notification/NotificationRankingUpdate.java2
-rw-r--r--core/java/android/service/notification/TEST_MAPPING5
-rw-r--r--core/java/android/service/notification/ZenModeConfig.java24
-rw-r--r--core/java/android/view/Display.java14
-rw-r--r--core/java/android/view/DisplayInfo.java3
-rw-r--r--core/java/android/view/ViewRootImpl.java20
-rw-r--r--core/java/android/view/inputmethod/RemoteInputConnectionImpl.java188
-rw-r--r--core/java/android/view/inputmethod/flags.aconfig10
-rw-r--r--core/java/android/window/BackMotionEvent.java22
-rw-r--r--core/java/android/window/BackTouchTracker.java9
-rw-r--r--core/java/android/window/DesktopExperienceFlags.java3
-rw-r--r--core/java/android/window/IWindowOrganizerController.aidl11
-rw-r--r--core/java/android/window/ImeOnBackInvokedDispatcher.java6
-rw-r--r--core/java/android/window/WindowOrganizer.java21
-rw-r--r--core/java/android/window/flags/lse_desktop_experience.aconfig17
-rw-r--r--core/java/com/android/internal/app/MediaRouteChooserContentManager.java56
-rw-r--r--core/java/com/android/internal/app/MediaRouteChooserDialog.java37
-rw-r--r--core/jni/android_os_Parcel.cpp87
-rw-r--r--core/proto/android/os/incident.proto7
-rw-r--r--core/proto/android/providers/settings/system.proto11
-rw-r--r--core/proto/android/server/Android.bp28
-rw-r--r--core/proto/android/server/bluetooth_manager_service.proto49
-rw-r--r--core/tests/coretests/src/android/accessibilityservice/BrailleDisplayControllerImplTest.java4
-rw-r--r--core/tests/coretests/src/android/content/pm/RegisteredServicesCacheTest.java12
-rw-r--r--core/tests/coretests/src/android/content/pm/RegisteredServicesCacheUnitTest.java403
-rw-r--r--core/tests/coretests/src/android/hardware/display/DisplayManagerGlobalTest.java6
-rw-r--r--core/tests/coretests/src/android/os/ParcelTest.java61
-rw-r--r--core/tests/coretests/src/android/view/PendingInsetsControllerTest.java4
-rw-r--r--core/tests/coretests/src/android/view/accessibility/AccessibilityInteractionClientTest.java4
-rw-r--r--core/tests/coretests/src/android/view/contentcapture/MainContentCaptureSessionTest.java44
-rw-r--r--core/tests/coretests/src/android/view/contentprotection/ContentProtectionEventProcessorTest.java3
-rw-r--r--core/tests/coretests/src/android/widget/TextViewReceiveContentTest.java11
-rw-r--r--core/tests/coretests/src/android/window/BackTouchTrackerTest.kt2
-rw-r--r--core/tests/coretests/src/android/window/WindowOnBackInvokedDispatcherTest.java3
-rw-r--r--core/tests/coretests/src/com/android/internal/accessibility/AccessibilityShortcutControllerTest.java8
-rw-r--r--core/tests/devicestatetests/src/android/hardware/devicestate/DeviceStateManagerGlobalTest.java4
-rw-r--r--libs/WindowManager/Shell/res/layout/desktop_mode_window_decor_handle_menu.xml4
-rw-r--r--libs/WindowManager/Shell/res/values/dimen.xml16
-rw-r--r--libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/desktopmode/DesktopModeCompatPolicy.kt28
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationController.java6
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/common/SyncTransactionQueue.java53
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java11
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java2
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopDisplayModeController.kt20
-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/DesktopTasksController.kt26
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksTransitionObserver.kt37
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/IDesktopMode.aidl13
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/common/DefaultHomePackageSupplier.kt65
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/freeform/FreeformTaskTransitionObserver.java6
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java4
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/transition/LegacyTransitions.java139
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecorViewModel.java4
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CarWindowDecorViewModel.java4
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java30
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecoration.java103
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/FluidResizeTaskPositioner.java3
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/HandleMenu.kt8
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/MultiDisplayVeiledResizeTaskPositioner.kt4
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/TaskDragResizer.java6
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/VeiledResizeTaskPositioner.java3
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/WindowDecorViewModel.java5
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/WindowDecoration.java3
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/tiling/DesktopTilingWindowDecoration.kt2
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/viewholder/AppHandleViewHolder.kt32
-rw-r--r--libs/WindowManager/Shell/tests/e2e/utils/src/com/android/wm/shell/SimulatedConnectedDisplayTestRule.kt168
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/BackAnimationControllerTest.java2
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/BackProgressAnimatorTest.java3
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/CustomCrossActivityBackAnimationTest.kt3
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubbleDataTest.java3
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubblesNavBarMotionEventHandlerTest.java13
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/DevicePostureControllerTest.java4
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/DisplayImeControllerTest.java12
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/TabletopModeControllerTest.java4
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/pip/PipAppOpsListenerTest.java4
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopDisplayModeControllerTest.kt167
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeEventLoggerTest.kt32
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeLoggerTransitionObserverTest.kt18
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopRepositoryTest.kt12
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksControllerTest.kt10
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksTransitionObserverTest.kt44
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DragToDesktopTransitionHandlerTest.kt6
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/VisualIndicatorViewContainerTest.kt4
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/compatui/SystemModalsTransitionHandlerTest.kt12
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip2/animation/PipAlphaAnimatorTest.java4
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip2/animation/PipEnterAnimatorTest.java4
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip2/animation/PipExpandAnimatorTest.java4
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip2/animation/PipResizeAnimatorTest.java4
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip2/phone/PipTaskListenerTest.java12
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip2/phone/PipUiStateChangeControllerTests.java4
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/shared/desktopmode/DesktopModeCompatPolicyTest.kt35
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModelAppHandleOnlyTest.kt5
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModelTests.kt21
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModelTestsBase.kt14
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorationTests.java132
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/HandleMenuTest.kt10
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/MultiDisplayVeiledResizeTaskPositionerTest.kt11
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/ResizeVeilTest.kt4
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/WindowDecorationTests.java12
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/common/WindowDecorTaskResourceLoaderTest.kt8
-rw-r--r--media/java/android/media/MediaRouter.java12
-rw-r--r--media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/AudioManagerUnitTest.java4
-rw-r--r--packages/CompanionDeviceManager/src/com/android/companiondevicemanager/CompanionAssociationActivity.java7
-rw-r--r--packages/SettingsLib/DataStore/src/com/android/settingslib/datastore/KeyValueStore.kt28
-rw-r--r--packages/SettingsLib/DataStore/src/com/android/settingslib/datastore/KeyedObserver.kt24
-rw-r--r--packages/SettingsLib/DeviceStateRotationLock/src/com.android.settingslib.devicestate/DeviceStateAutoRotateSettingManager.kt69
-rw-r--r--packages/SettingsLib/DeviceStateRotationLock/src/com.android.settingslib.devicestate/DeviceStateAutoRotateSettingManagerImpl.kt191
-rw-r--r--packages/SettingsLib/DeviceStateRotationLock/src/com.android.settingslib.devicestate/DeviceStateAutoRotateSettingUtils.kt31
-rw-r--r--packages/SettingsLib/DeviceStateRotationLock/src/com.android.settingslib.devicestate/DeviceStateRotationLockSettingsManager.java65
-rw-r--r--packages/SettingsLib/DeviceStateRotationLock/src/com.android.settingslib.devicestate/SecureSettings.java2
-rw-r--r--packages/SettingsLib/MainSwitchPreference/src/com/android/settingslib/widget/MainSwitchBar.java20
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/RestrictedPreferenceHelper.java12
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothUtils.java12
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothLeBroadcast.java6
-rw-r--r--packages/SettingsLib/tests/integ/src/com/android/settingslib/devicestate/DeviceStateAutoRotateSettingManagerImplTest.kt300
-rw-r--r--packages/SettingsLib/tests/integ/src/com/android/settingslib/devicestate/DeviceStateRotationLockSettingsManagerTest.java1
-rw-r--r--packages/SettingsProvider/src/android/provider/settings/backup/GlobalSettings.java2
-rw-r--r--packages/SettingsProvider/src/android/provider/settings/backup/SystemSettings.java3
-rw-r--r--packages/SettingsProvider/src/android/provider/settings/validators/GlobalSettingsValidators.java2
-rw-r--r--packages/SettingsProvider/src/android/provider/settings/validators/SystemSettingsValidators.java2
-rw-r--r--packages/SettingsProvider/src/com/android/providers/settings/OWNERS4
-rw-r--r--packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java6
-rw-r--r--packages/Shell/src/com/android/shell/BugreportProgressService.java24
-rw-r--r--packages/SystemUI/aconfig/systemui.aconfig12
-rw-r--r--packages/SystemUI/animation/src/com/android/systemui/animation/RemoteAnimationRunnerCompat.java4
-rw-r--r--packages/SystemUI/compose/core/src/com/android/compose/animation/Expandable.kt45
-rw-r--r--packages/SystemUI/compose/core/src/com/android/compose/animation/ExpandableController.kt21
-rw-r--r--packages/SystemUI/compose/core/src/com/android/compose/gesture/NestedDraggable.kt64
-rw-r--r--packages/SystemUI/compose/core/src/com/android/compose/modifiers/AnimatedBackground.kt159
-rw-r--r--packages/SystemUI/compose/core/src/com/android/compose/modifiers/FadingBackground.kt113
-rw-r--r--packages/SystemUI/compose/core/tests/src/com/android/compose/gesture/NestedDraggableTest.kt40
-rw-r--r--packages/SystemUI/compose/features/src/com/android/systemui/qs/footer/ui/compose/FooterActions.kt6
-rw-r--r--packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/SceneContainer.kt2
-rw-r--r--packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/SceneContainerTransitions.kt4
-rw-r--r--packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/SceneContainerTransitionsBuilder.kt6
-rw-r--r--packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/transitions/ToNotificationsShadeTransition.kt4
-rw-r--r--packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/transitions/ToQuickSettingsShadeTransition.kt4
-rw-r--r--packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/OverlayShade.kt16
-rw-r--r--packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/DraggableHandler.kt2
-rw-r--r--packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/reveal/ContainerReveal.kt4
-rw-r--r--packages/SystemUI/compose/scene/tests/Android.bp1
-rw-r--r--packages/SystemUI/compose/scene/tests/goldens/edge_verticalReveal_gesture_dragFullyClose.json (renamed from packages/SystemUI/compose/scene/tests/goldens/verticalReveal_gesture_dragFullyClose.json)0
-rw-r--r--packages/SystemUI/compose/scene/tests/goldens/edge_verticalReveal_gesture_dragHalfClose.json (renamed from packages/SystemUI/compose/scene/tests/goldens/verticalReveal_gesture_dragHalfClose.json)0
-rw-r--r--packages/SystemUI/compose/scene/tests/goldens/edge_verticalReveal_gesture_dragOpen.json (renamed from packages/SystemUI/compose/scene/tests/goldens/verticalReveal_gesture_dragOpen.json)0
-rw-r--r--packages/SystemUI/compose/scene/tests/goldens/edge_verticalReveal_gesture_flingClose.json (renamed from packages/SystemUI/compose/scene/tests/goldens/verticalReveal_gesture_flingClose.json)0
-rw-r--r--packages/SystemUI/compose/scene/tests/goldens/edge_verticalReveal_gesture_flingOpen.json (renamed from packages/SystemUI/compose/scene/tests/goldens/verticalReveal_gesture_flingOpen.json)0
-rw-r--r--packages/SystemUI/compose/scene/tests/goldens/edge_verticalReveal_gesture_magneticDetachAndReattach.json (renamed from packages/SystemUI/compose/scene/tests/goldens/verticalReveal_gesture_magneticDetachAndReattach.json)0
-rw-r--r--packages/SystemUI/compose/scene/tests/goldens/edge_verticalReveal_triggeredRevealCloseTransition.json (renamed from packages/SystemUI/compose/scene/tests/goldens/verticalReveal_triggeredRevealCloseTransition.json)0
-rw-r--r--packages/SystemUI/compose/scene/tests/goldens/edge_verticalReveal_triggeredRevealOpenTransition.json (renamed from packages/SystemUI/compose/scene/tests/goldens/verticalReveal_triggeredRevealOpenTransition.json)0
-rw-r--r--packages/SystemUI/compose/scene/tests/goldens/floating_verticalReveal_gesture_dragFullyClose.json634
-rw-r--r--packages/SystemUI/compose/scene/tests/goldens/floating_verticalReveal_gesture_dragHalfClose.json614
-rw-r--r--packages/SystemUI/compose/scene/tests/goldens/floating_verticalReveal_gesture_dragOpen.json544
-rw-r--r--packages/SystemUI/compose/scene/tests/goldens/floating_verticalReveal_gesture_flingClose.json444
-rw-r--r--packages/SystemUI/compose/scene/tests/goldens/floating_verticalReveal_gesture_flingOpen.json374
-rw-r--r--packages/SystemUI/compose/scene/tests/goldens/floating_verticalReveal_gesture_magneticDetachAndReattach.json714
-rw-r--r--packages/SystemUI/compose/scene/tests/goldens/floating_verticalReveal_triggeredRevealCloseTransition.json314
-rw-r--r--packages/SystemUI/compose/scene/tests/goldens/floating_verticalReveal_triggeredRevealOpenTransition.json244
-rw-r--r--packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/reveal/ContentRevealTest.kt69
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/clipboardoverlay/ActionIntentCreatorTest.kt12
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/clipboardoverlay/DefaultIntentCreatorTest.java16
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/display/data/repository/DisplayRepositoryTest.kt21
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/display/data/repository/PerDisplayInstanceRepositoryImplTest.kt4
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/binder/WindowManagerLockscreenVisibilityManagerTest.kt11
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/NotificationShadeDepthControllerTest.kt23
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerTest.kt18
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/NotificationInfoTest.kt32
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/NotificationSnoozeTest.java3
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/PromotedNotificationInfoTest.java5
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/FakeHomeStatusBarViewModel.kt4
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/HomeStatusBarViewModelImplTest.kt41
-rw-r--r--packages/SystemUI/res/layout/notification_2025_info.xml37
-rw-r--r--packages/SystemUI/res/layout/notification_info.xml15
-rw-r--r--packages/SystemUI/res/layout/partial_conversation_info.xml15
-rw-r--r--packages/SystemUI/res/layout/promoted_notification_info.xml15
-rw-r--r--packages/SystemUI/res/values/strings.xml2
-rw-r--r--packages/SystemUI/src/com/android/keyguard/ClockEventController.kt4
-rw-r--r--packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java9
-rw-r--r--packages/SystemUI/src/com/android/systemui/clipboardoverlay/ActionIntentCreator.kt18
-rw-r--r--packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardOverlayController.java12
-rw-r--r--packages/SystemUI/src/com/android/systemui/clipboardoverlay/DefaultIntentCreator.java6
-rw-r--r--packages/SystemUI/src/com/android/systemui/clipboardoverlay/IntentCreator.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/display/DisplayModule.kt42
-rw-r--r--packages/SystemUI/src/com/android/systemui/display/data/repository/DisplayRepository.kt394
-rw-r--r--packages/SystemUI/src/com/android/systemui/display/data/repository/PerDisplayRepoDumpHelper.kt39
-rw-r--r--packages/SystemUI/src/com/android/systemui/display/data/repository/PerDisplayRepository.kt29
-rw-r--r--packages/SystemUI/src/com/android/systemui/display/domain/interactor/ConnectedDisplayInteractor.kt4
-rw-r--r--packages/SystemUI/src/com/android/systemui/dump/DumpableFromToString.kt27
-rw-r--r--packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialogLite.java62
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java3
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/WindowManagerLockscreenVisibilityManager.kt56
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardServiceShowLockscreenRepository.kt46
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardClockInteractor.kt4
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardServiceShowLockscreenInteractor.kt98
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardShowWhileAwakeInteractor.kt28
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionCoreStartable.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/controls/ui/binder/SeekBarObserver.kt21
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaControlPanel.java8
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaViewController.kt16
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/controls/ui/viewmodel/SeekBarViewModel.kt33
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/remedia/ui/compose/Media.kt47
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/composefragment/QSFragmentCompose.kt66
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/PaginatedGridLayout.kt6
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/infinitegrid/EditTile.kt135
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/infinitegrid/Tile.kt48
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/selection/Selection.kt9
-rw-r--r--packages/SystemUI/src/com/android/systemui/reardisplay/RearDisplayCoreStartable.kt67
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/BlurUtils.kt17
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/NotificationShadeDepthController.kt25
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/OWNERS4
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/BundleEntryAdapter.kt4
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/EntryAdapter.java5
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntryAdapter.kt4
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ShadeListBuilder.java3
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/BundleCoordinator.kt12
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/ViewConfigCoordinator.kt16
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/VisualStabilityCoordinator.java12
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/ShadeViewDiffer.kt6
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/promoted/AODPromotedNotification.kt142
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/promoted/domain/interactor/PackageDemotionInteractor.kt39
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationView.java29
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java60
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationBackgroundView.java59
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java1
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGutsManager.java22
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInfo.java12
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationSnooze.java3
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/PromotedNotificationInfo.java12
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java5
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java1
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/view/NotificationScrollView.kt3
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/NotificationScrollViewBinder.kt5
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationScrollViewModel.kt38
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/composable/StatusBarRoot.kt3
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/HomeStatusBarViewModel.kt41
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/policy/DeviceStateRotationLockSettingController.java13
-rw-r--r--packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java2
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/clipboardoverlay/ClipboardOverlayControllerTest.java29
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsDialogLiteTest.java28
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/viewmodel/SeekBarViewModelTest.kt22
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/qs/panels/ui/compose/DragAndDropTest.kt29
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/qs/panels/ui/compose/EditModeTest.kt30
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/qs/panels/ui/compose/ResizingTest.kt29
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/reardisplay/RearDisplayCoreStartableTest.kt23
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/ViewConfigCoordinatorTest.kt12
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowTest.java49
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerWithScenesTest.kt57
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/wallpapers/GradientColorWallpaperTest.kt4
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/display/data/repository/FakeDisplayRepository.kt11
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/display/data/repository/PerDisplayStoreKosmos.kt3
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/KeyguardServiceShowLockscreenRepositoryKosmos.kt (renamed from packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardServiceShowLockscreenInteractor.kt)9
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardServiceShowLockscreenInteractorKosmos.kt35
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardShowWhileAwakeInteractorKosmos.kt2
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardWakeDirectlyToGoneInteractorKosmos.kt2
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/promoted/domain/interactor/PackageDemotionInteractorKosmos.kt (renamed from packages/SystemUI/src/com/android/systemui/display/data/DisplayEvent.kt)13
-rw-r--r--ravenwood/TEST_MAPPING10
-rw-r--r--services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java42
-rw-r--r--services/accessibility/java/com/android/server/accessibility/HearingDevicePhoneCallNotificationController.java3
-rw-r--r--services/core/java/com/android/server/TelephonyRegistry.java4
-rw-r--r--services/core/java/com/android/server/am/OWNERS2
-rw-r--r--services/core/java/com/android/server/appop/DiscreteOpsDbHelper.java16
-rw-r--r--services/core/java/com/android/server/audio/AudioService.java81
-rw-r--r--services/core/java/com/android/server/display/DisplayDeviceInfo.java17
-rw-r--r--services/core/java/com/android/server/display/DisplayGroup.java12
-rw-r--r--services/core/java/com/android/server/display/LocalDisplayAdapter.java17
-rw-r--r--services/core/java/com/android/server/display/LogicalDisplay.java6
-rw-r--r--services/core/java/com/android/server/display/LogicalDisplayMapper.java15
-rw-r--r--services/core/java/com/android/server/display/OverlayDisplayAdapter.java37
-rw-r--r--services/core/java/com/android/server/display/WifiDisplayAdapter.java6
-rw-r--r--services/core/java/com/android/server/notification/ConditionProviders.java13
-rw-r--r--services/core/java/com/android/server/notification/TEST_MAPPING5
-rw-r--r--services/core/java/com/android/server/notification/ZenConfigTrimmer.java109
-rw-r--r--services/core/java/com/android/server/notification/ZenModeHelper.java15
-rw-r--r--services/core/java/com/android/server/notification/flags.aconfig10
-rw-r--r--services/core/java/com/android/server/power/Notifier.java52
-rw-r--r--services/core/java/com/android/server/power/PowerManagerService.java11
-rw-r--r--services/core/java/com/android/server/power/WakeLockLog.java42
-rw-r--r--services/core/java/com/android/server/wallpaper/WallpaperCropper.java10
-rw-r--r--services/core/java/com/android/server/wm/DesktopModeBoundsCalculator.java7
-rw-r--r--services/core/java/com/android/server/wm/DisplayContent.java83
-rw-r--r--services/core/java/com/android/server/wm/DisplayRotation.java87
-rw-r--r--services/core/java/com/android/server/wm/PinnedTaskController.java102
-rw-r--r--services/core/java/com/android/server/wm/RootWindowContainer.java17
-rw-r--r--services/core/java/com/android/server/wm/Transition.java2
-rw-r--r--services/core/java/com/android/server/wm/WindowAnimator.java24
-rw-r--r--services/core/java/com/android/server/wm/WindowManagerService.java33
-rw-r--r--services/core/java/com/android/server/wm/WindowOrganizerController.java29
-rw-r--r--services/core/java/com/android/server/wm/WindowState.java106
-rw-r--r--services/core/java/com/android/server/wm/WindowSurfacePlacer.java3
-rw-r--r--services/core/jni/com_android_server_input_InputManagerService.cpp6
-rw-r--r--services/foldables/devicestateprovider/src/com/android/server/policy/FoldableDeviceStateProvider.java3
-rw-r--r--services/tests/displayservicetests/src/com/android/server/display/DisplayPowerProximityStateControllerTest.java4
-rw-r--r--services/tests/displayservicetests/src/com/android/server/display/LocalDisplayAdapterTest.java98
-rw-r--r--services/tests/displayservicetests/src/com/android/server/display/WakelockControllerTest.java8
-rw-r--r--services/tests/displayservicetests/src/com/android/server/display/brightness/DisplayBrightnessControllerTest.java3
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/alarm/AlarmManagerServiceTest.java6
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/alarm/AlarmStoreTest.java4
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/am/ActivityManagerServiceTest.java3
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/backup/utils/FullBackupUtilsTest.java8
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/backup/utils/TarBackupReaderTest.java3
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/job/controllers/QuotaControllerTest.java378
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/pm/BackgroundUserSoundNotifierTest.java6
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/sensorprivacy/CameraPrivacyLightControllerTest.java8
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/wallpaper/WallpaperCropperTest.java80
-rw-r--r--services/tests/powerservicetests/src/com/android/server/power/NotifierTest.java78
-rw-r--r--services/tests/powerservicetests/src/com/android/server/power/WakeLockLogTest.java85
-rw-r--r--services/tests/servicestests/src/com/android/server/NetworkScoreServiceTest.java17
-rw-r--r--services/tests/servicestests/src/com/android/server/accessibility/AbstractAccessibilityServiceConnectionTest.java7
-rw-r--r--services/tests/servicestests/src/com/android/server/accessibility/AccessibilityManagerServiceTest.java153
-rw-r--r--services/tests/servicestests/src/com/android/server/accessibility/FingerprintGestureControllerTest.java10
-rw-r--r--services/tests/servicestests/src/com/android/server/accessibility/HearingDevicePhoneCallNotificationControllerTest.java52
-rw-r--r--services/tests/servicestests/src/com/android/server/accessibility/KeyEventDispatcherTest.java16
-rw-r--r--services/tests/servicestests/src/com/android/server/accessibility/MotionEventInjectorTest.java3
-rw-r--r--services/tests/servicestests/src/com/android/server/attention/AttentionManagerServiceTest.java3
-rw-r--r--services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/FaceDetectClientTest.java4
-rw-r--r--services/tests/servicestests/src/com/android/server/contentcapture/ContentCaptureManagerServiceTest.java34
-rw-r--r--services/tests/servicestests/src/com/android/server/contentprotection/ContentProtectionAllowlistManagerTest.java86
-rw-r--r--services/tests/servicestests/src/com/android/server/contentprotection/ContentProtectionConsentManagerTest.java40
-rw-r--r--services/tests/servicestests/src/com/android/server/contentprotection/RemoteContentProtectionServiceTest.java8
-rw-r--r--services/tests/servicestests/src/com/android/server/credentials/ProviderRegistryGetSessionTest.java6
-rw-r--r--services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java17
-rw-r--r--services/tests/servicestests/src/com/android/server/locales/SystemAppUpdateTrackerTest.java6
-rw-r--r--services/tests/servicestests/src/com/android/server/media/projection/MediaProjectionManagerServiceTest.java4
-rw-r--r--services/tests/servicestests/src/com/android/server/pm/dex/DynamicCodeLoggerTests.java4
-rw-r--r--services/tests/timetests/src/com/android/server/timedetector/GnssTimeUpdateServiceTest.java10
-rw-r--r--services/tests/uiservicestests/Android.bp83
-rw-r--r--services/tests/uiservicestests/AndroidTest.xml1
-rw-r--r--services/tests/uiservicestests/notification-tests.xml30
-rw-r--r--services/tests/uiservicestests/notification-zen-tests.xml30
-rw-r--r--services/tests/uiservicestests/src/com/android/server/UiModeManagerServiceTest.java5
-rw-r--r--services/tests/uiservicestests/src/com/android/server/notification/ConditionProvidersTest.java14
-rw-r--r--services/tests/uiservicestests/src/com/android/server/notification/DefaultDeviceEffectsApplierTest.java11
-rw-r--r--services/tests/uiservicestests/src/com/android/server/notification/GroupHelperTest.java40
-rw-r--r--services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java8
-rw-r--r--services/tests/uiservicestests/src/com/android/server/notification/ZenConfigTrimmerTest.java124
-rw-r--r--services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java41
-rw-r--r--services/tests/vibrator/src/com/android/server/vibrator/VibratorControlServiceTest.java9
-rw-r--r--services/tests/voiceinteractiontests/src/com/android/server/soundtrigger_middleware/SoundTriggerHalConcurrentCaptureHandlerTest.java3
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java2
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/DesktopModeLaunchParamsModifierTests.java30
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/DisplayAreaTest.java6
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java79
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java4
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java54
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/WindowTokenTests.java30
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/WindowTracingLegacyTest.java4
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/WindowTracingPerfettoTest.java4
-rw-r--r--telephony/java/android/telephony/TelephonyManager.java4
-rw-r--r--tests/TrustTests/src/android/trust/test/GrantAndRevokeTrustTest.kt4
-rw-r--r--tests/UsbTests/src/com/android/server/usb/UsbServiceTest.java10
367 files changed, 10188 insertions, 3614 deletions
diff --git a/TEST_MAPPING b/TEST_MAPPING
index e469f167d32f..ce0da7e21071 100644
--- a/TEST_MAPPING
+++ b/TEST_MAPPING
@@ -25,6 +25,12 @@
"name": "FrameworksUiServicesTests"
},
{
+ "name": "FrameworksUiServicesNotificationTests"
+ },
+ {
+ "name": "FrameworksUiServicesZenTests"
+ },
+ {
"name": "FrameworksInputMethodSystemServerTests_server_inputmethod"
},
{
diff --git a/apex/jobscheduler/service/aconfig/job.aconfig b/apex/jobscheduler/service/aconfig/job.aconfig
index 876274ecc32e..aae5bb31273b 100644
--- a/apex/jobscheduler/service/aconfig/job.aconfig
+++ b/apex/jobscheduler/service/aconfig/job.aconfig
@@ -126,3 +126,15 @@ flag {
purpose: PURPOSE_BUGFIX
}
}
+
+flag {
+ name: "tune_quota_window_default_parameters"
+ namespace: "backstage_power"
+ description: "Tune default active/exempted bucket quota parameters"
+ bug: "401767691"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
+
+
diff --git a/apex/jobscheduler/service/java/com/android/server/job/controllers/QuotaController.java b/apex/jobscheduler/service/java/com/android/server/job/controllers/QuotaController.java
index 54d337eded7d..a9c4a1501dd8 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/controllers/QuotaController.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/controllers/QuotaController.java
@@ -360,13 +360,13 @@ public final class QuotaController extends StateController {
/** How much time each app will have to run jobs within their standby bucket window. */
private final long[] mAllowedTimePerPeriodMs = new long[]{
- QcConstants.DEFAULT_ALLOWED_TIME_PER_PERIOD_ACTIVE_MS,
+ QcConstants.DEFAULT_LEGACY_ALLOWED_TIME_PER_PERIOD_ACTIVE_MS,
QcConstants.DEFAULT_ALLOWED_TIME_PER_PERIOD_WORKING_MS,
QcConstants.DEFAULT_ALLOWED_TIME_PER_PERIOD_FREQUENT_MS,
QcConstants.DEFAULT_ALLOWED_TIME_PER_PERIOD_RARE_MS,
0, // NEVER
QcConstants.DEFAULT_ALLOWED_TIME_PER_PERIOD_RESTRICTED_MS,
- QcConstants.DEFAULT_ALLOWED_TIME_PER_PERIOD_EXEMPTED_MS
+ QcConstants.DEFAULT_LEGACY_ALLOWED_TIME_PER_PERIOD_EXEMPTED_MS
};
/**
@@ -3178,9 +3178,11 @@ public final class QuotaController extends StateController {
static final String KEY_EJ_GRACE_PERIOD_TOP_APP_MS =
QC_CONSTANT_PREFIX + "ej_grace_period_top_app_ms";
- private static final long DEFAULT_ALLOWED_TIME_PER_PERIOD_EXEMPTED_MS =
+ // Legacy default time each app will have to run jobs within EXEMPTED bucket
+ private static final long DEFAULT_LEGACY_ALLOWED_TIME_PER_PERIOD_EXEMPTED_MS =
10 * 60 * 1000L; // 10 minutes
- private static final long DEFAULT_ALLOWED_TIME_PER_PERIOD_ACTIVE_MS =
+ // Legacy default time each app will have to run jobs within ACTIVE bucket
+ private static final long DEFAULT_LEGACY_ALLOWED_TIME_PER_PERIOD_ACTIVE_MS =
10 * 60 * 1000L; // 10 minutes
private static final long DEFAULT_ALLOWED_TIME_PER_PERIOD_WORKING_MS =
10 * 60 * 1000L; // 10 minutes
@@ -3192,14 +3194,26 @@ public final class QuotaController extends StateController {
10 * 60 * 1000L; // 10 minutes
private static final long DEFAULT_ALLOWED_TIME_PER_PERIOD_ADDITION_INSTALLER_MS =
10 * 60 * 1000L; // 10 minutes
+
+ // Current default time each app will have to run jobs within EXEMPTED bucket
+ private static final long DEFAULT_CURRENT_ALLOWED_TIME_PER_PERIOD_EXEMPTED_MS =
+ 20 * 60 * 1000L; // 20 minutes
+ // Current default time each app will have to run jobs within ACTIVE bucket
+ private static final long DEFAULT_CURRENT_ALLOWED_TIME_PER_PERIOD_ACTIVE_MS =
+ 20 * 60 * 1000L; // 20 minutes
+ private static final long DEFAULT_CURRENT_ALLOWED_TIME_PER_PERIOD_ADDITION_INSTALLER_MS =
+ 20 * 60 * 1000L; // 20 minutes
+
private static final long DEFAULT_IN_QUOTA_BUFFER_MS =
30 * 1000L; // 30 seconds
// Legacy default window size for EXEMPTED bucket
+ // EXEMPT apps can run jobs at any time
private static final long DEFAULT_LEGACY_WINDOW_SIZE_EXEMPTED_MS =
- DEFAULT_ALLOWED_TIME_PER_PERIOD_EXEMPTED_MS; // EXEMPT apps can run jobs at any time
+ DEFAULT_LEGACY_ALLOWED_TIME_PER_PERIOD_EXEMPTED_MS;
// Legacy default window size for ACTIVE bucket
+ // ACTIVE apps can run jobs at any time
private static final long DEFAULT_LEGACY_WINDOW_SIZE_ACTIVE_MS =
- DEFAULT_ALLOWED_TIME_PER_PERIOD_ACTIVE_MS; // ACTIVE apps can run jobs at any time
+ DEFAULT_LEGACY_ALLOWED_TIME_PER_PERIOD_ACTIVE_MS;
// Legacy default window size for WORKING bucket
private static final long DEFAULT_LEGACY_WINDOW_SIZE_WORKING_MS =
2 * 60 * 60 * 1000L; // 2 hours
@@ -3216,6 +3230,13 @@ public final class QuotaController extends StateController {
private static final long DEFAULT_CURRENT_WINDOW_SIZE_FREQUENT_MS =
12 * 60 * 60 * 1000L; // 12 hours
+ // Latest default window size for EXEMPTED bucket.
+ private static final long DEFAULT_LATEST_WINDOW_SIZE_EXEMPTED_MS =
+ 40 * 60 * 1000L; // 40 minutes.
+ // Latest default window size for ACTIVE bucket.
+ private static final long DEFAULT_LATEST_WINDOW_SIZE_ACTIVE_MS =
+ 60 * 60 * 1000L; // 60 minutes.
+
private static final long DEFAULT_WINDOW_SIZE_RARE_MS =
24 * 60 * 60 * 1000L; // 24 hours
private static final long DEFAULT_WINDOW_SIZE_RESTRICTED_MS =
@@ -3276,12 +3297,13 @@ public final class QuotaController extends StateController {
* bucket window.
*/
public long ALLOWED_TIME_PER_PERIOD_EXEMPTED_MS =
- DEFAULT_ALLOWED_TIME_PER_PERIOD_EXEMPTED_MS;
+ DEFAULT_LEGACY_ALLOWED_TIME_PER_PERIOD_EXEMPTED_MS;
/**
* How much time each app in the active bucket will have to run jobs within their standby
* bucket window.
*/
- public long ALLOWED_TIME_PER_PERIOD_ACTIVE_MS = DEFAULT_ALLOWED_TIME_PER_PERIOD_ACTIVE_MS;
+ public long ALLOWED_TIME_PER_PERIOD_ACTIVE_MS =
+ DEFAULT_LEGACY_ALLOWED_TIME_PER_PERIOD_ACTIVE_MS;
/**
* How much time each app in the working set bucket will have to run jobs within their
* standby bucket window.
@@ -3575,11 +3597,30 @@ public final class QuotaController extends StateController {
public long EJ_GRACE_PERIOD_TOP_APP_MS = DEFAULT_EJ_GRACE_PERIOD_TOP_APP_MS;
void adjustDefaultBucketWindowSizes() {
- WINDOW_SIZE_EXEMPTED_MS = DEFAULT_CURRENT_WINDOW_SIZE_EXEMPTED_MS;
- WINDOW_SIZE_ACTIVE_MS = DEFAULT_CURRENT_WINDOW_SIZE_ACTIVE_MS;
+ ALLOWED_TIME_PER_PERIOD_EXEMPTED_MS = Flags.tuneQuotaWindowDefaultParameters()
+ ? DEFAULT_CURRENT_ALLOWED_TIME_PER_PERIOD_EXEMPTED_MS :
+ DEFAULT_LEGACY_ALLOWED_TIME_PER_PERIOD_EXEMPTED_MS;
+ ALLOWED_TIME_PER_PERIOD_ACTIVE_MS = Flags.tuneQuotaWindowDefaultParameters()
+ ? DEFAULT_CURRENT_ALLOWED_TIME_PER_PERIOD_ACTIVE_MS :
+ DEFAULT_LEGACY_ALLOWED_TIME_PER_PERIOD_ACTIVE_MS;
+ ALLOWED_TIME_PER_PERIOD_ADDITION_INSTALLER_MS = Flags.tuneQuotaWindowDefaultParameters()
+ ? DEFAULT_CURRENT_ALLOWED_TIME_PER_PERIOD_ADDITION_INSTALLER_MS :
+ DEFAULT_ALLOWED_TIME_PER_PERIOD_ADDITION_INSTALLER_MS;
+
+ WINDOW_SIZE_EXEMPTED_MS = Flags.tuneQuotaWindowDefaultParameters()
+ ? DEFAULT_LATEST_WINDOW_SIZE_EXEMPTED_MS :
+ DEFAULT_CURRENT_WINDOW_SIZE_EXEMPTED_MS;
+ WINDOW_SIZE_ACTIVE_MS = Flags.tuneQuotaWindowDefaultParameters()
+ ? DEFAULT_LATEST_WINDOW_SIZE_ACTIVE_MS :
+ DEFAULT_CURRENT_WINDOW_SIZE_ACTIVE_MS;
WINDOW_SIZE_WORKING_MS = DEFAULT_CURRENT_WINDOW_SIZE_WORKING_MS;
WINDOW_SIZE_FREQUENT_MS = DEFAULT_CURRENT_WINDOW_SIZE_FREQUENT_MS;
+ mAllowedTimePerPeriodMs[EXEMPTED_INDEX] = Math.min(MAX_PERIOD_MS,
+ Math.max(MINUTE_IN_MILLIS, ALLOWED_TIME_PER_PERIOD_EXEMPTED_MS));
+ mAllowedTimePerPeriodMs[ACTIVE_INDEX] = Math.min(MAX_PERIOD_MS,
+ Math.max(MINUTE_IN_MILLIS, ALLOWED_TIME_PER_PERIOD_ACTIVE_MS));
+
mBucketPeriodsMs[EXEMPTED_INDEX] = Math.max(
mAllowedTimePerPeriodMs[EXEMPTED_INDEX],
Math.min(MAX_PERIOD_MS, WINDOW_SIZE_EXEMPTED_MS));
@@ -3592,6 +3633,11 @@ public final class QuotaController extends StateController {
mBucketPeriodsMs[FREQUENT_INDEX] = Math.max(
mAllowedTimePerPeriodMs[FREQUENT_INDEX],
Math.min(MAX_PERIOD_MS, WINDOW_SIZE_FREQUENT_MS));
+
+ mAllowedTimePeriodAdditionaInstallerMs =
+ Math.min(mBucketPeriodsMs[EXEMPTED_INDEX]
+ - mAllowedTimePerPeriodMs[EXEMPTED_INDEX],
+ ALLOWED_TIME_PER_PERIOD_ADDITION_INSTALLER_MS);
}
void adjustDefaultEjLimits() {
@@ -3882,10 +3928,14 @@ public final class QuotaController extends StateController {
KEY_WINDOW_SIZE_RESTRICTED_MS);
ALLOWED_TIME_PER_PERIOD_EXEMPTED_MS =
properties.getLong(KEY_ALLOWED_TIME_PER_PERIOD_EXEMPTED_MS,
- DEFAULT_ALLOWED_TIME_PER_PERIOD_EXEMPTED_MS);
+ Flags.tuneQuotaWindowDefaultParameters()
+ ? DEFAULT_CURRENT_ALLOWED_TIME_PER_PERIOD_EXEMPTED_MS :
+ DEFAULT_LEGACY_ALLOWED_TIME_PER_PERIOD_EXEMPTED_MS);
ALLOWED_TIME_PER_PERIOD_ACTIVE_MS =
properties.getLong(KEY_ALLOWED_TIME_PER_PERIOD_ACTIVE_MS,
- DEFAULT_ALLOWED_TIME_PER_PERIOD_ACTIVE_MS);
+ Flags.tuneQuotaWindowDefaultParameters()
+ ? DEFAULT_CURRENT_ALLOWED_TIME_PER_PERIOD_ACTIVE_MS :
+ DEFAULT_LEGACY_ALLOWED_TIME_PER_PERIOD_ACTIVE_MS);
ALLOWED_TIME_PER_PERIOD_WORKING_MS =
properties.getLong(KEY_ALLOWED_TIME_PER_PERIOD_WORKING_MS,
DEFAULT_ALLOWED_TIME_PER_PERIOD_WORKING_MS);
@@ -3900,19 +3950,27 @@ public final class QuotaController extends StateController {
DEFAULT_ALLOWED_TIME_PER_PERIOD_RESTRICTED_MS);
ALLOWED_TIME_PER_PERIOD_ADDITION_INSTALLER_MS =
properties.getLong(KEY_ALLOWED_TIME_PER_PERIOD_ADDITION_INSTALLER_MS,
- DEFAULT_ALLOWED_TIME_PER_PERIOD_ADDITION_INSTALLER_MS);
+ Flags.tuneQuotaWindowDefaultParameters()
+ ? DEFAULT_CURRENT_ALLOWED_TIME_PER_PERIOD_ADDITION_INSTALLER_MS
+ : DEFAULT_ALLOWED_TIME_PER_PERIOD_ADDITION_INSTALLER_MS);
IN_QUOTA_BUFFER_MS = properties.getLong(KEY_IN_QUOTA_BUFFER_MS,
DEFAULT_IN_QUOTA_BUFFER_MS);
MAX_EXECUTION_TIME_MS = properties.getLong(KEY_MAX_EXECUTION_TIME_MS,
DEFAULT_MAX_EXECUTION_TIME_MS);
WINDOW_SIZE_EXEMPTED_MS = properties.getLong(KEY_WINDOW_SIZE_EXEMPTED_MS,
- Flags.adjustQuotaDefaultConstants()
- ? DEFAULT_CURRENT_WINDOW_SIZE_EXEMPTED_MS :
- DEFAULT_LEGACY_WINDOW_SIZE_EXEMPTED_MS);
+ (Flags.adjustQuotaDefaultConstants()
+ && Flags.tuneQuotaWindowDefaultParameters())
+ ? DEFAULT_LATEST_WINDOW_SIZE_EXEMPTED_MS :
+ (Flags.adjustQuotaDefaultConstants()
+ ? DEFAULT_CURRENT_WINDOW_SIZE_EXEMPTED_MS :
+ DEFAULT_LEGACY_WINDOW_SIZE_EXEMPTED_MS));
WINDOW_SIZE_ACTIVE_MS = properties.getLong(KEY_WINDOW_SIZE_ACTIVE_MS,
- Flags.adjustQuotaDefaultConstants()
- ? DEFAULT_CURRENT_WINDOW_SIZE_ACTIVE_MS :
- DEFAULT_LEGACY_WINDOW_SIZE_ACTIVE_MS);
+ (Flags.adjustQuotaDefaultConstants()
+ && Flags.tuneQuotaWindowDefaultParameters())
+ ? DEFAULT_LATEST_WINDOW_SIZE_ACTIVE_MS :
+ (Flags.adjustQuotaDefaultConstants()
+ ? DEFAULT_CURRENT_WINDOW_SIZE_ACTIVE_MS :
+ DEFAULT_LEGACY_WINDOW_SIZE_ACTIVE_MS));
WINDOW_SIZE_WORKING_MS =
properties.getLong(KEY_WINDOW_SIZE_WORKING_MS,
Flags.adjustQuotaDefaultConstants()
diff --git a/core/api/module-lib-current.txt b/core/api/module-lib-current.txt
index 132c65cc26ee..526a213a6003 100644
--- a/core/api/module-lib-current.txt
+++ b/core/api/module-lib-current.txt
@@ -410,7 +410,6 @@ package android.os {
method public void invalidateCache();
method public static void invalidateCache(@NonNull String, @NonNull String);
method @Nullable public Result query(@NonNull Query);
- method @FlaggedApi("android.os.ipc_data_cache_test_apis") public static void setTestMode(boolean);
field public static final String MODULE_BLUETOOTH = "bluetooth";
}
diff --git a/core/api/system-current.txt b/core/api/system-current.txt
index 32b170a6286b..cd7e434d30d9 100644
--- a/core/api/system-current.txt
+++ b/core/api/system-current.txt
@@ -2941,6 +2941,7 @@ package android.app.smartspace.uitemplatedata {
package android.app.supervision {
@FlaggedApi("android.app.supervision.flags.supervision_manager_apis") public class SupervisionManager {
+ method @FlaggedApi("android.app.supervision.flags.supervision_manager_apis") @Nullable @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.QUERY_USERS}) public android.content.Intent createConfirmSupervisionCredentialsIntent();
method @FlaggedApi("android.app.supervision.flags.supervision_manager_apis") @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.QUERY_USERS}) public boolean isSupervisionEnabled();
}
diff --git a/core/api/test-current.txt b/core/api/test-current.txt
index 12bfccf2172c..4c8283907712 100644
--- a/core/api/test-current.txt
+++ b/core/api/test-current.txt
@@ -2460,7 +2460,7 @@ package android.os {
method public static void invalidateCache(@NonNull String, @NonNull String);
method public final boolean isDisabled();
method @Nullable public Result query(@NonNull Query);
- method @FlaggedApi("android.os.ipc_data_cache_test_apis") public static void setTestMode(boolean);
+ method public static void setTestMode(boolean);
field public static final String MODULE_BLUETOOTH = "bluetooth";
field public static final String MODULE_SYSTEM = "system_server";
field public static final String MODULE_TEST = "test";
diff --git a/core/java/android/app/AutomaticZenRule.java b/core/java/android/app/AutomaticZenRule.java
index 2daa52b47102..fa977c93113a 100644
--- a/core/java/android/app/AutomaticZenRule.java
+++ b/core/java/android/app/AutomaticZenRule.java
@@ -228,7 +228,7 @@ public final class AutomaticZenRule implements Parcelable {
public AutomaticZenRule(Parcel source) {
enabled = source.readInt() == ENABLED;
if (source.readInt() == ENABLED) {
- name = getTrimmedString(source.readString8());
+ name = getTrimmedString(source.readString());
}
interruptionFilter = source.readInt();
conditionId = getTrimmedUri(source.readParcelable(null, android.net.Uri.class));
@@ -238,11 +238,11 @@ public final class AutomaticZenRule implements Parcelable {
source.readParcelable(null, android.content.ComponentName.class));
creationTime = source.readLong();
mZenPolicy = source.readParcelable(null, ZenPolicy.class);
- mPkg = source.readString8();
+ mPkg = source.readString();
mDeviceEffects = source.readParcelable(null, ZenDeviceEffects.class);
mAllowManualInvocation = source.readBoolean();
mIconResId = source.readInt();
- mTriggerDescription = getTrimmedString(source.readString8(), MAX_DESC_LENGTH);
+ mTriggerDescription = getTrimmedString(source.readString(), MAX_DESC_LENGTH);
mType = source.readInt();
}
@@ -514,7 +514,7 @@ public final class AutomaticZenRule implements Parcelable {
dest.writeInt(enabled ? ENABLED : DISABLED);
if (name != null) {
dest.writeInt(1);
- dest.writeString8(name);
+ dest.writeString(name);
} else {
dest.writeInt(0);
}
@@ -524,11 +524,11 @@ public final class AutomaticZenRule implements Parcelable {
dest.writeParcelable(configurationActivity, 0);
dest.writeLong(creationTime);
dest.writeParcelable(mZenPolicy, 0);
- dest.writeString8(mPkg);
+ dest.writeString(mPkg);
dest.writeParcelable(mDeviceEffects, 0);
dest.writeBoolean(mAllowManualInvocation);
dest.writeInt(mIconResId);
- dest.writeString8(mTriggerDescription);
+ dest.writeString(mTriggerDescription);
dest.writeInt(mType);
}
diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java
index 7e5c0fbe1ee1..99a2763650cd 100644
--- a/core/java/android/app/ContextImpl.java
+++ b/core/java/android/app/ContextImpl.java
@@ -2473,11 +2473,9 @@ class ContextImpl extends Context {
@Override
public int getPermissionRequestState(String permission) {
Objects.requireNonNull(permission, "Permission name can't be null");
- int deviceId = PermissionManager.resolveDeviceIdForPermissionCheck(this, getDeviceId(),
- permission);
PermissionManager permissionManager = getSystemService(PermissionManager.class);
return permissionManager.getPermissionRequestState(getOpPackageName(), permission,
- deviceId);
+ getDeviceId());
}
@Override
diff --git a/core/java/android/app/PropertyInvalidatedCache.java b/core/java/android/app/PropertyInvalidatedCache.java
index 6e495768bfd4..38141cf20ce3 100644
--- a/core/java/android/app/PropertyInvalidatedCache.java
+++ b/core/java/android/app/PropertyInvalidatedCache.java
@@ -1417,36 +1417,7 @@ public class PropertyInvalidatedCache<Query, Result> {
}
/**
- * Throw if the current process is not allowed to use test APIs.
- */
- @android.ravenwood.annotation.RavenwoodReplace
- private static void throwIfNotTest() {
- final ActivityThread activityThread = ActivityThread.currentActivityThread();
- if (activityThread == null) {
- // Only tests can reach here.
- return;
- }
- final Instrumentation instrumentation = activityThread.getInstrumentation();
- if (instrumentation == null) {
- // Only tests can reach here.
- return;
- }
- if (instrumentation.isInstrumenting()) {
- return;
- }
- if (Flags.enforcePicTestmodeProtocol()) {
- throw new IllegalStateException("Test-only API called not from a test.");
- }
- }
-
- /**
- * Do not throw if running under ravenwood.
- */
- private static void throwIfNotTest$ravenwood() {
- }
-
- /**
- * Enable or disable test mode. The protocol requires that the mode toggle: for instance, it is
+ * Enable or disable testing. The protocol requires that the mode toggle: for instance, it is
* illegal to clear the test mode if the test mode is already off. Enabling test mode puts
* all caches in the process into test mode; all nonces are initialized to UNSET and
* subsequent reads and writes are to process memory. This has the effect of disabling all
@@ -1454,12 +1425,10 @@ public class PropertyInvalidatedCache<Query, Result> {
* operation.
* @param mode The desired test mode.
* @throws IllegalStateException if the supplied mode is already set.
- * @throws IllegalStateException if the process is not running an instrumentation test.
* @hide
*/
@VisibleForTesting
public static void setTestMode(boolean mode) {
- throwIfNotTest();
synchronized (sGlobalLock) {
if (sTestMode == mode) {
final String msg = "cannot set test mode redundantly: mode=" + mode;
@@ -1495,11 +1464,9 @@ public class PropertyInvalidatedCache<Query, Result> {
* for which it would not otherwise have permission. Caches in test mode do NOT write their
* values to the system properties. The effect is local to the current process. Test mode
* must be true when this method is called.
- * @throws IllegalStateException if the process is not running an instrumentation test.
* @hide
*/
public void testPropertyName() {
- throwIfNotTest();
synchronized (sGlobalLock) {
if (sTestMode == false) {
throw new IllegalStateException("cannot test property name with test mode off");
@@ -1810,12 +1777,10 @@ public class PropertyInvalidatedCache<Query, Result> {
* When multiple caches share a single property value, using an instance method on one of
* the cache objects to invalidate all of the cache objects becomes confusing and you should
* just use the static version of this function.
- * @throws IllegalStateException if the process is not running an instrumentation test.
* @hide
*/
@VisibleForTesting
public void disableSystemWide() {
- throwIfNotTest();
disableSystemWide(mPropertyName);
}
diff --git a/core/java/android/app/supervision/SupervisionManager.java b/core/java/android/app/supervision/SupervisionManager.java
index 8217ca1953ab..172ed2358a5d 100644
--- a/core/java/android/app/supervision/SupervisionManager.java
+++ b/core/java/android/app/supervision/SupervisionManager.java
@@ -97,6 +97,8 @@ public class SupervisionManager {
*
* @hide
*/
+ @SystemApi
+ @FlaggedApi(Flags.FLAG_SUPERVISION_MANAGER_APIS)
@RequiresPermission(anyOf = {MANAGE_USERS, QUERY_USERS})
@Nullable
public Intent createConfirmSupervisionCredentialsIntent() {
diff --git a/core/java/android/content/pm/RegisteredServicesCache.java b/core/java/android/content/pm/RegisteredServicesCache.java
index 10d3051cff6f..ded35b23608d 100644
--- a/core/java/android/content/pm/RegisteredServicesCache.java
+++ b/core/java/android/content/pm/RegisteredServicesCache.java
@@ -31,13 +31,13 @@ import android.os.Environment;
import android.os.Handler;
import android.os.UserHandle;
import android.os.UserManager;
-import android.util.ArrayMap;
import android.util.AtomicFile;
import android.util.AttributeSet;
import android.util.IntArray;
import android.util.Log;
import android.util.Slog;
import android.util.SparseArray;
+import android.util.SparseArrayMap;
import android.util.Xml;
import com.android.internal.annotations.GuardedBy;
@@ -98,15 +98,16 @@ public abstract class RegisteredServicesCache<V> {
@GuardedBy("mServicesLock")
private final SparseArray<UserServices<V>> mUserServices = new SparseArray<UserServices<V>>(2);
- @GuardedBy("mServiceInfoCaches")
- private final ArrayMap<ComponentName, ServiceInfo<V>> mServiceInfoCaches = new ArrayMap<>();
+ @GuardedBy("mUserIdToServiceInfoCaches")
+ private final SparseArrayMap<ComponentName, ServiceInfo<V>> mUserIdToServiceInfoCaches =
+ new SparseArrayMap<>();
private final Handler mBackgroundHandler;
private final Runnable mClearServiceInfoCachesRunnable = new Runnable() {
public void run() {
- synchronized (mServiceInfoCaches) {
- mServiceInfoCaches.clear();
+ synchronized (mUserIdToServiceInfoCaches) {
+ mUserIdToServiceInfoCaches.clear();
}
}
};
@@ -537,8 +538,8 @@ public abstract class RegisteredServicesCache<V> {
Slog.d(TAG, "Fail to get the PackageInfo in generateServicesMap: " + e);
}
if (lastUpdateTime >= 0) {
- ServiceInfo<V> serviceInfo = getServiceInfoFromServiceCache(componentName,
- lastUpdateTime);
+ ServiceInfo<V> serviceInfo = getServiceInfoFromServiceCache(userId,
+ componentName, lastUpdateTime);
if (serviceInfo != null) {
serviceInfos.add(serviceInfo);
continue;
@@ -553,8 +554,8 @@ public abstract class RegisteredServicesCache<V> {
}
serviceInfos.add(info);
if (Flags.optimizeParsingInRegisteredServicesCache()) {
- synchronized (mServiceInfoCaches) {
- mServiceInfoCaches.put(componentName, info);
+ synchronized (mUserIdToServiceInfoCaches) {
+ mUserIdToServiceInfoCaches.add(userId, componentName, info);
}
}
} catch (XmlPullParserException | IOException e) {
@@ -563,8 +564,8 @@ public abstract class RegisteredServicesCache<V> {
}
if (Flags.optimizeParsingInRegisteredServicesCache()) {
- synchronized (mServiceInfoCaches) {
- if (!mServiceInfoCaches.isEmpty()) {
+ synchronized (mUserIdToServiceInfoCaches) {
+ if (mUserIdToServiceInfoCaches.numMaps() > 0) {
mBackgroundHandler.removeCallbacks(mClearServiceInfoCachesRunnable);
mBackgroundHandler.postDelayed(mClearServiceInfoCachesRunnable,
SERVICE_INFO_CACHES_TIMEOUT_MILLIS);
@@ -873,6 +874,11 @@ public abstract class RegisteredServicesCache<V> {
synchronized (mServicesLock) {
mUserServices.remove(userId);
}
+ if (Flags.optimizeParsingInRegisteredServicesCache()) {
+ synchronized (mUserIdToServiceInfoCaches) {
+ mUserIdToServiceInfoCaches.delete(userId);
+ }
+ }
}
@VisibleForTesting
@@ -916,10 +922,10 @@ public abstract class RegisteredServicesCache<V> {
mContext.unregisterReceiver(mUserRemovedReceiver);
}
- private ServiceInfo<V> getServiceInfoFromServiceCache(@NonNull ComponentName componentName,
- long lastUpdateTime) {
- synchronized (mServiceInfoCaches) {
- ServiceInfo<V> serviceCache = mServiceInfoCaches.get(componentName);
+ private ServiceInfo<V> getServiceInfoFromServiceCache(int userId,
+ @NonNull ComponentName componentName, long lastUpdateTime) {
+ synchronized (mUserIdToServiceInfoCaches) {
+ ServiceInfo<V> serviceCache = mUserIdToServiceInfoCaches.get(userId, componentName);
if (serviceCache != null && serviceCache.lastUpdateTime == lastUpdateTime) {
return serviceCache;
}
diff --git a/core/java/android/hardware/camera2/marshal/impl/MarshalQueryableParcelable.java b/core/java/android/hardware/camera2/marshal/impl/MarshalQueryableParcelable.java
index fdde2057a1a0..de3bafb4bb56 100644
--- a/core/java/android/hardware/camera2/marshal/impl/MarshalQueryableParcelable.java
+++ b/core/java/android/hardware/camera2/marshal/impl/MarshalQueryableParcelable.java
@@ -75,7 +75,6 @@ public class MarshalQueryableParcelable<T extends Parcelable>
}
Parcel parcel = Parcel.obtain();
- byte[] parcelContents;
try {
value.writeToParcel(parcel, /*flags*/0);
@@ -85,17 +84,15 @@ public class MarshalQueryableParcelable<T extends Parcelable>
"Parcelable " + value + " must not have file descriptors");
}
- parcelContents = parcel.marshall();
+ final int position = buffer.position();
+ parcel.marshall(buffer);
+ if (buffer.position() == position) {
+ throw new AssertionError("No data marshaled for " + value);
+ }
}
finally {
parcel.recycle();
}
-
- if (parcelContents.length == 0) {
- throw new AssertionError("No data marshaled for " + value);
- }
-
- buffer.put(parcelContents);
}
@Override
diff --git a/core/java/android/hardware/display/DisplayManager.java b/core/java/android/hardware/display/DisplayManager.java
index 92a56fc575e3..8216130e3731 100644
--- a/core/java/android/hardware/display/DisplayManager.java
+++ b/core/java/android/hardware/display/DisplayManager.java
@@ -433,8 +433,9 @@ public final class DisplayManager {
public static final int VIRTUAL_DISPLAY_FLAG_DESTROY_CONTENT_ON_REMOVAL = 1 << 8;
/**
- * Virtual display flag: Indicates that the display should support system decorations. Virtual
- * displays without this flag shouldn't show home, navigation bar or wallpaper.
+ * Virtual display flag: Indicates that the display supports and should always show system
+ * decorations. Virtual displays without this flag shouldn't show home, navigation bar or
+ * wallpaper.
* <p>This flag doesn't work without {@link #VIRTUAL_DISPLAY_FLAG_TRUSTED}</p>
*
* @see #createVirtualDisplay
diff --git a/core/java/android/os/IpcDataCache.java b/core/java/android/os/IpcDataCache.java
index e888f520b842..2e7c3be53d90 100644
--- a/core/java/android/os/IpcDataCache.java
+++ b/core/java/android/os/IpcDataCache.java
@@ -718,7 +718,7 @@ public class IpcDataCache<Query, Result> extends PropertyInvalidatedCache<Query,
}
/**
- * Enable or disable test mode. The protocol requires that the mode toggle: for instance, it is
+ * Enable or disable testing. The protocol requires that the mode toggle: for instance, it is
* illegal to clear the test mode if the test mode is already off. Enabling test mode puts
* all caches in the process into test mode; all nonces are initialized to UNSET and
* subsequent reads and writes are to process memory. This has the effect of disabling all
@@ -726,11 +726,8 @@ public class IpcDataCache<Query, Result> extends PropertyInvalidatedCache<Query,
* operation.
* @param mode The desired test mode.
* @throws IllegalStateException if the supplied mode is already set.
- * @throws IllegalStateException if the process is not running an instrumentation test.
* @hide
*/
- @FlaggedApi(android.os.Flags.FLAG_IPC_DATA_CACHE_TEST_APIS)
- @SystemApi(client=SystemApi.Client.MODULE_LIBRARIES)
@TestApi
public static void setTestMode(boolean mode) {
PropertyInvalidatedCache.setTestMode(mode);
diff --git a/core/java/android/os/Parcel.java b/core/java/android/os/Parcel.java
index 49d3f06eb80f..6cb49b3ea166 100644
--- a/core/java/android/os/Parcel.java
+++ b/core/java/android/os/Parcel.java
@@ -52,6 +52,8 @@ import com.android.internal.util.ArrayUtils;
import dalvik.annotation.optimization.CriticalNative;
import dalvik.annotation.optimization.FastNative;
+import java.nio.BufferOverflowException;
+import java.nio.ReadOnlyBufferException;
import libcore.util.SneakyThrow;
import java.io.ByteArrayInputStream;
@@ -62,6 +64,7 @@ import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.ObjectStreamClass;
import java.io.Serializable;
+import java.nio.ByteBuffer;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.reflect.Array;
@@ -457,8 +460,15 @@ public final class Parcel {
private static native void nativeDestroy(long nativePtr);
private static native byte[] nativeMarshall(long nativePtr);
+ private static native int nativeMarshallArray(
+ long nativePtr, byte[] data, int offset, int length);
+ private static native int nativeMarshallBuffer(
+ long nativePtr, ByteBuffer buffer, int offset, int length);
private static native void nativeUnmarshall(
long nativePtr, byte[] data, int offset, int length);
+ private static native void nativeUnmarshallBuffer(
+ long nativePtr, ByteBuffer buffer, int offset, int length);
+
private static native int nativeCompareData(long thisNativePtr, long otherNativePtr);
private static native boolean nativeCompareDataInRange(
long ptrA, int offsetA, long ptrB, int offsetB, int length);
@@ -814,12 +824,80 @@ public final class Parcel {
}
/**
+ * Writes the raw bytes of the parcel to a buffer.
+ *
+ * <p class="note">The data you retrieve here <strong>must not</strong>
+ * be placed in any kind of persistent storage (on local disk, across
+ * a network, etc). For that, you should use standard serialization
+ * or another kind of general serialization mechanism. The Parcel
+ * marshalled representation is highly optimized for local IPC, and as
+ * such does not attempt to maintain compatibility with data created
+ * in different versions of the platform.
+ *
+ * @param buffer The ByteBuffer to write the data to.
+ * @throws ReadOnlyBufferException if the buffer is read-only.
+ * @throws BufferOverflowException if the buffer is too small.
+ *
+ * @hide
+ */
+ public final void marshall(@NonNull ByteBuffer buffer) {
+ if (buffer == null) {
+ throw new NullPointerException();
+ }
+ if (buffer.isReadOnly()) {
+ throw new ReadOnlyBufferException();
+ }
+
+ final int position = buffer.position();
+ final int remaining = buffer.remaining();
+
+ int marshalledSize = 0;
+ if (buffer.isDirect()) {
+ marshalledSize = nativeMarshallBuffer(mNativePtr, buffer, position, remaining);
+ } else if (buffer.hasArray()) {
+ marshalledSize = nativeMarshallArray(
+ mNativePtr, buffer.array(), buffer.arrayOffset() + position, remaining);
+ } else {
+ throw new IllegalArgumentException();
+ }
+
+ buffer.position(position + marshalledSize);
+ }
+
+ /**
* Fills the raw bytes of this Parcel with the supplied data.
*/
public final void unmarshall(@NonNull byte[] data, int offset, int length) {
nativeUnmarshall(mNativePtr, data, offset, length);
}
+ /**
+ * Fills the raw bytes of this Parcel with data from the supplied buffer.
+ *
+ * @param buffer will read buffer.remaining() bytes from the buffer.
+ *
+ * @hide
+ */
+ public final void unmarshall(@NonNull ByteBuffer buffer) {
+ if (buffer == null) {
+ throw new NullPointerException();
+ }
+
+ final int position = buffer.position();
+ final int remaining = buffer.remaining();
+
+ if (buffer.isDirect()) {
+ nativeUnmarshallBuffer(mNativePtr, buffer, position, remaining);
+ } else if (buffer.hasArray()) {
+ nativeUnmarshall(
+ mNativePtr, buffer.array(), buffer.arrayOffset() + position, remaining);
+ } else {
+ throw new IllegalArgumentException();
+ }
+
+ buffer.position(position + remaining);
+ }
+
public final void appendFrom(Parcel parcel, int offset, int length) {
nativeAppendFrom(mNativePtr, parcel.mNativePtr, offset, length);
}
diff --git a/core/java/android/os/flags.aconfig b/core/java/android/os/flags.aconfig
index 5d80119410e1..86acb2b21cfa 100644
--- a/core/java/android/os/flags.aconfig
+++ b/core/java/android/os/flags.aconfig
@@ -227,14 +227,6 @@ flag {
}
flag {
- name: "ipc_data_cache_test_apis"
- namespace: "system_performance"
- description: "Expose IpcDataCache test apis to mainline modules."
- bug: "396173886"
- is_exported: true
-}
-
-flag {
name: "mainline_vcn_platform_api"
namespace: "vcn"
description: "Expose platform APIs to mainline VCN"
diff --git a/core/java/android/permission/PermissionManager.java b/core/java/android/permission/PermissionManager.java
index 561a2c96e6a7..0433c76fbbf4 100644
--- a/core/java/android/permission/PermissionManager.java
+++ b/core/java/android/permission/PermissionManager.java
@@ -1907,8 +1907,9 @@ public final class PermissionManager {
@Context.PermissionRequestState
public int getPermissionRequestState(@NonNull String packageName, @NonNull String permission,
int deviceId) {
+ int actualDeviceId = resolveDeviceIdForPermissionCheck(mContext, deviceId, permission);
return sPermissionRequestStateCache.query(
- new PermissionRequestStateQuery(packageName, permission, deviceId));
+ new PermissionRequestStateQuery(packageName, permission, actualDeviceId));
}
/**
@@ -2035,7 +2036,8 @@ public final class PermissionManager {
*/
public int checkPackageNamePermission(String permName, String pkgName,
int deviceId, @UserIdInt int userId) {
- String persistentDeviceId = getPersistentDeviceId(deviceId);
+ int actualDeviceId = resolveDeviceIdForPermissionCheck(mContext, deviceId, permName);
+ String persistentDeviceId = getPersistentDeviceId(actualDeviceId);
return sPackageNamePermissionCache.query(
new PackageNamePermissionQuery(permName, pkgName, persistentDeviceId, userId));
}
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 5b708b382a96..85f38c984f5c 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -6484,6 +6484,14 @@ public final class Settings {
public static final String SCREEN_FLASH_NOTIFICATION = "screen_flash_notification";
/**
+ * Setting to enable CV (proprietary)
+ *
+ * @hide
+ */
+ public static final String CV_ENABLED =
+ "cv_enabled";
+
+ /**
* Integer property that specifes the color for screen flash notification as a
* packed 32-bit color.
*
@@ -15514,7 +15522,8 @@ public final class Settings {
* <ul>
* <li><pre>secure</pre>: creates a secure display</li>
* <li><pre>own_content_only</pre>: only shows this display's own content</li>
- * <li><pre>should_show_system_decorations</pre>: supports system decorations</li>
+ * <li><pre>should_show_system_decorations</pre>: always shows system decorations</li>
+ * <li><pre>fixed_content_mode</pre>: does not allow the content mode switch</li>
* </ul>
* </p><p>
* Example:
@@ -20815,6 +20824,24 @@ public final class Settings {
@Readable
public static final String WEAR_LAUNCHER_UI_MODE = "wear_launcher_ui_mode";
+ /**
+ * Setting indicating whether the primary gesture input action has been enabled by the
+ * user.
+ *
+ * @hide
+ */
+ public static final String GESTURE_PRIMARY_ACTION_USER_PREFERENCE =
+ "gesture_primary_action_user_preference";
+
+ /**
+ * Setting indicating whether the dismiss gesture input action has been enabled by the
+ * user.
+ *
+ * @hide
+ */
+ public static final String GESTURE_DISMISS_ACTION_USER_PREFERENCE =
+ "gesture_dismiss_action_user_preference";
+
/** Whether Wear Power Anomaly Service is enabled.
*
* (0 = false, 1 = true)
diff --git a/core/java/android/service/notification/NotificationRankingUpdate.java b/core/java/android/service/notification/NotificationRankingUpdate.java
index 7660ed96d30e..815444d195c9 100644
--- a/core/java/android/service/notification/NotificationRankingUpdate.java
+++ b/core/java/android/service/notification/NotificationRankingUpdate.java
@@ -219,7 +219,7 @@ public class NotificationRankingUpdate implements Parcelable {
// Gets a read/write buffer mapping the entire shared memory region.
buffer = mRankingMapFd.mapReadWrite();
// Puts the ranking map into the shared memory region buffer.
- buffer.put(mapParcel.marshall(), 0, mapSize);
+ mapParcel.marshall(buffer);
// Protects the region from being written to, by setting it to be read-only.
mRankingMapFd.setProtect(OsConstants.PROT_READ);
// Puts the SharedMemory object in the parcel.
diff --git a/core/java/android/service/notification/TEST_MAPPING b/core/java/android/service/notification/TEST_MAPPING
index dc7129cde5e5..ea7ee4addbe6 100644
--- a/core/java/android/service/notification/TEST_MAPPING
+++ b/core/java/android/service/notification/TEST_MAPPING
@@ -4,7 +4,10 @@
"name": "CtsNotificationTestCases_notification"
},
{
- "name": "FrameworksUiServicesTests_notification"
+ "name": "FrameworksUiServicesNotificationTests"
+ },
+ {
+ "name": "FrameworksUiServicesZenTests"
}
],
"postsubmit": [
diff --git a/core/java/android/service/notification/ZenModeConfig.java b/core/java/android/service/notification/ZenModeConfig.java
index 1cf43d455be8..4cbd5beb3a8c 100644
--- a/core/java/android/service/notification/ZenModeConfig.java
+++ b/core/java/android/service/notification/ZenModeConfig.java
@@ -2636,7 +2636,7 @@ public class ZenModeConfig implements Parcelable {
enabled = source.readInt() == 1;
snoozing = source.readInt() == 1;
if (source.readInt() == 1) {
- name = source.readString8();
+ name = source.readString();
}
zenMode = source.readInt();
conditionId = source.readParcelable(null, android.net.Uri.class);
@@ -2644,18 +2644,18 @@ public class ZenModeConfig implements Parcelable {
component = source.readParcelable(null, android.content.ComponentName.class);
configurationActivity = source.readParcelable(null, android.content.ComponentName.class);
if (source.readInt() == 1) {
- id = source.readString8();
+ id = source.readString();
}
creationTime = source.readLong();
if (source.readInt() == 1) {
- enabler = source.readString8();
+ enabler = source.readString();
}
zenPolicy = source.readParcelable(null, android.service.notification.ZenPolicy.class);
zenDeviceEffects = source.readParcelable(null, ZenDeviceEffects.class);
- pkg = source.readString8();
+ pkg = source.readString();
allowManualInvocation = source.readBoolean();
- iconResName = source.readString8();
- triggerDescription = source.readString8();
+ iconResName = source.readString();
+ triggerDescription = source.readString();
type = source.readInt();
userModifiedFields = source.readInt();
zenPolicyUserModifiedFields = source.readInt();
@@ -2703,7 +2703,7 @@ public class ZenModeConfig implements Parcelable {
dest.writeInt(snoozing ? 1 : 0);
if (name != null) {
dest.writeInt(1);
- dest.writeString8(name);
+ dest.writeString(name);
} else {
dest.writeInt(0);
}
@@ -2714,23 +2714,23 @@ public class ZenModeConfig implements Parcelable {
dest.writeParcelable(configurationActivity, 0);
if (id != null) {
dest.writeInt(1);
- dest.writeString8(id);
+ dest.writeString(id);
} else {
dest.writeInt(0);
}
dest.writeLong(creationTime);
if (enabler != null) {
dest.writeInt(1);
- dest.writeString8(enabler);
+ dest.writeString(enabler);
} else {
dest.writeInt(0);
}
dest.writeParcelable(zenPolicy, 0);
dest.writeParcelable(zenDeviceEffects, 0);
- dest.writeString8(pkg);
+ dest.writeString(pkg);
dest.writeBoolean(allowManualInvocation);
- dest.writeString8(iconResName);
- dest.writeString8(triggerDescription);
+ dest.writeString(iconResName);
+ dest.writeString(triggerDescription);
dest.writeInt(type);
dest.writeInt(userModifiedFields);
dest.writeInt(zenPolicyUserModifiedFields);
diff --git a/core/java/android/view/Display.java b/core/java/android/view/Display.java
index 3f45e29f2d43..5c816543e191 100644
--- a/core/java/android/view/Display.java
+++ b/core/java/android/view/Display.java
@@ -286,7 +286,7 @@ public final class Display {
/**
* Display flag: Indicates that the display should show system decorations.
* <p>
- * This flag identifies secondary displays that should show system decorations, such as
+ * This flag identifies secondary displays that should always show system decorations, such as
* navigation bar, home activity or wallpaper.
* </p>
* <p>Note that this flag doesn't work without {@link #FLAG_TRUSTED}</p>
@@ -401,6 +401,18 @@ public final class Display {
public static final int FLAG_ROTATES_WITH_CONTENT = 1 << 14;
/**
+ * Display flag: Indicates that the display is allowed to switch the content mode between
+ * projected/extended and mirroring. This allows the display to dynamically add or remove the
+ * home and system decorations.
+ *
+ * Note that this flag shouldn't be enabled with {@link #FLAG_PRIVATE} or
+ * {@link #FLAG_SHOULD_SHOW_SYSTEM_DECORATIONS} at the same time; otherwise it will be ignored.
+ *
+ * @hide
+ */
+ public static final int FLAG_ALLOWS_CONTENT_MODE_SWITCH = 1 << 15;
+
+ /**
* Display flag: Indicates that the contents of the display should not be scaled
* to fit the physical screen dimensions. Used for development only to emulate
* devices with smaller physicals screens while preserving density.
diff --git a/core/java/android/view/DisplayInfo.java b/core/java/android/view/DisplayInfo.java
index d880072aa404..bf000d5fa39a 100644
--- a/core/java/android/view/DisplayInfo.java
+++ b/core/java/android/view/DisplayInfo.java
@@ -1125,6 +1125,9 @@ public final class DisplayInfo implements Parcelable {
if ((flags & Display.FLAG_REAR) != 0) {
result.append(", FLAG_REAR_DISPLAY");
}
+ if ((flags & Display.FLAG_ALLOWS_CONTENT_MODE_SWITCH) != 0) {
+ result.append(", FLAG_ALLOWS_CONTENT_MODE_SWITCH");
+ }
return result.toString();
}
}
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index da91e8d2d256..2edce5de7ace 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -555,8 +555,6 @@ public final class ViewRootImpl implements ViewParent,
@UiContext
public final Context mContext;
- private UiModeManager mUiModeManager;
-
@UnsupportedAppUsage
final IWindowSession mWindowSession;
@NonNull Display mDisplay;
@@ -2079,8 +2077,7 @@ public final class ViewRootImpl implements ViewParent,
// We also ignore dark theme, since the app developer can override the user's
// preference for dark mode in configuration.uiMode. Instead, we assume that both
// force invert and the system's dark theme are enabled.
- if (getUiModeManager().getForceInvertState() ==
- UiModeManager.FORCE_INVERT_TYPE_DARK) {
+ if (shouldApplyForceInvertDark()) {
final boolean isLightTheme =
a.getBoolean(R.styleable.Theme_isLightTheme, false);
// TODO: b/372558459 - Also check the background ColorDrawable color lightness
@@ -2108,6 +2105,14 @@ public final class ViewRootImpl implements ViewParent,
}
}
+ private boolean shouldApplyForceInvertDark() {
+ final UiModeManager uiModeManager = mContext.getSystemService(UiModeManager.class);
+ if (uiModeManager == null) {
+ return false;
+ }
+ return uiModeManager.getForceInvertState() == UiModeManager.FORCE_INVERT_TYPE_DARK;
+ }
+
private void updateForceDarkMode() {
if (mAttachInfo.mThreadedRenderer == null) return;
if (mAttachInfo.mThreadedRenderer.setForceDark(determineForceDarkType())) {
@@ -9413,13 +9418,6 @@ public final class ViewRootImpl implements ViewParent,
return mAudioManager;
}
- private UiModeManager getUiModeManager() {
- if (mUiModeManager == null) {
- mUiModeManager = mContext.getSystemService(UiModeManager.class);
- }
- return mUiModeManager;
- }
-
private Vibrator getSystemVibrator() {
if (mVibrator == null) {
mVibrator = mContext.getSystemService(Vibrator.class);
diff --git a/core/java/android/view/inputmethod/RemoteInputConnectionImpl.java b/core/java/android/view/inputmethod/RemoteInputConnectionImpl.java
index 3557f16a6dc8..ced27d6d4886 100644
--- a/core/java/android/view/inputmethod/RemoteInputConnectionImpl.java
+++ b/core/java/android/view/inputmethod/RemoteInputConnectionImpl.java
@@ -28,7 +28,6 @@ import static java.lang.annotation.RetentionPolicy.SOURCE;
import android.annotation.AnyThread;
import android.annotation.NonNull;
import android.annotation.Nullable;
-import android.annotation.UiThread;
import android.app.UriGrantsManager;
import android.content.ContentProvider;
import android.content.Intent;
@@ -38,6 +37,7 @@ import android.os.Binder;
import android.os.Bundle;
import android.os.CancellationSignal;
import android.os.CancellationSignalBeamer;
+import android.os.Debug;
import android.os.Handler;
import android.os.IBinder;
import android.os.Looper;
@@ -468,13 +468,27 @@ final class RemoteInputConnectionImpl extends IRemoteInputConnection.Stub {
});
}
+ /**
+ * Returns {@code false} if there is a sessionId mismatch and logs the event.
+ */
+ private boolean checkSessionId(@NonNull InputConnectionCommandHeader header) {
+ if (header.mSessionId != mCurrentSessionId.get()) {
+ Log.w(TAG, "Session id mismatch header.sessionId: " + header.mSessionId
+ + " currentSessionId: " + mCurrentSessionId.get() + " while calling "
+ + Debug.getCaller());
+ //TODO(b/396066692): log metrics.
+ return false; // cancelled
+ }
+ return true;
+ }
+
@Dispatching(cancellable = true)
@Override
public void getTextAfterCursor(InputConnectionCommandHeader header, int length, int flags,
AndroidFuture future /* T=CharSequence */) {
dispatchWithTracing("getTextAfterCursor", future, () -> {
- if (header.mSessionId != mCurrentSessionId.get()) {
- return null; // cancelled
+ if (!checkSessionId(header)) {
+ return null;
}
final InputConnection ic = getInputConnection();
if (ic == null || mDeactivateRequested.get()) {
@@ -495,8 +509,8 @@ final class RemoteInputConnectionImpl extends IRemoteInputConnection.Stub {
public void getTextBeforeCursor(InputConnectionCommandHeader header, int length, int flags,
AndroidFuture future /* T=CharSequence */) {
dispatchWithTracing("getTextBeforeCursor", future, () -> {
- if (header.mSessionId != mCurrentSessionId.get()) {
- return null; // cancelled
+ if (!checkSessionId(header)) {
+ return null;
}
final InputConnection ic = getInputConnection();
if (ic == null || mDeactivateRequested.get()) {
@@ -517,8 +531,8 @@ final class RemoteInputConnectionImpl extends IRemoteInputConnection.Stub {
public void getSelectedText(InputConnectionCommandHeader header, int flags,
AndroidFuture future /* T=CharSequence */) {
dispatchWithTracing("getSelectedText", future, () -> {
- if (header.mSessionId != mCurrentSessionId.get()) {
- return null; // cancelled
+ if (!checkSessionId(header)) {
+ return null;
}
final InputConnection ic = getInputConnection();
if (ic == null || mDeactivateRequested.get()) {
@@ -539,8 +553,8 @@ final class RemoteInputConnectionImpl extends IRemoteInputConnection.Stub {
public void getSurroundingText(InputConnectionCommandHeader header, int beforeLength,
int afterLength, int flags, AndroidFuture future /* T=SurroundingText */) {
dispatchWithTracing("getSurroundingText", future, () -> {
- if (header.mSessionId != mCurrentSessionId.get()) {
- return null; // cancelled
+ if (!checkSessionId(header)) {
+ return null;
}
final InputConnection ic = getInputConnection();
if (ic == null || mDeactivateRequested.get()) {
@@ -567,8 +581,8 @@ final class RemoteInputConnectionImpl extends IRemoteInputConnection.Stub {
public void getCursorCapsMode(InputConnectionCommandHeader header, int reqModes,
AndroidFuture future /* T=Integer */) {
dispatchWithTracing("getCursorCapsMode", future, () -> {
- if (header.mSessionId != mCurrentSessionId.get()) {
- return 0; // cancelled
+ if (!checkSessionId(header)) {
+ return 0;
}
final InputConnection ic = getInputConnection();
if (ic == null || mDeactivateRequested.get()) {
@@ -584,8 +598,8 @@ final class RemoteInputConnectionImpl extends IRemoteInputConnection.Stub {
public void getExtractedText(InputConnectionCommandHeader header, ExtractedTextRequest request,
int flags, AndroidFuture future /* T=ExtractedText */) {
dispatchWithTracing("getExtractedText", future, () -> {
- if (header.mSessionId != mCurrentSessionId.get()) {
- return null; // cancelled
+ if (!checkSessionId(header)) {
+ return null;
}
final InputConnection ic = getInputConnection();
if (ic == null || mDeactivateRequested.get()) {
@@ -601,8 +615,8 @@ final class RemoteInputConnectionImpl extends IRemoteInputConnection.Stub {
public void commitText(InputConnectionCommandHeader header, CharSequence text,
int newCursorPosition) {
dispatchWithTracing("commitText", () -> {
- if (header.mSessionId != mCurrentSessionId.get()) {
- return; // cancelled
+ if (!checkSessionId(header)) {
+ return;
}
InputConnection ic = getInputConnection();
if (ic == null || mDeactivateRequested.get()) {
@@ -618,8 +632,8 @@ final class RemoteInputConnectionImpl extends IRemoteInputConnection.Stub {
public void commitTextWithTextAttribute(InputConnectionCommandHeader header, CharSequence text,
int newCursorPosition, @Nullable TextAttribute textAttribute) {
dispatchWithTracing("commitTextWithTextAttribute", () -> {
- if (header.mSessionId != mCurrentSessionId.get()) {
- return; // cancelled
+ if (!checkSessionId(header)) {
+ return; // cancelled
}
InputConnection ic = getInputConnection();
if (ic == null || mDeactivateRequested.get()) {
@@ -634,8 +648,8 @@ final class RemoteInputConnectionImpl extends IRemoteInputConnection.Stub {
@Override
public void commitCompletion(InputConnectionCommandHeader header, CompletionInfo text) {
dispatchWithTracing("commitCompletion", () -> {
- if (header.mSessionId != mCurrentSessionId.get()) {
- return; // cancelled
+ if (!checkSessionId(header)) {
+ return; // cancelled
}
InputConnection ic = getInputConnection();
if (ic == null || mDeactivateRequested.get()) {
@@ -650,8 +664,8 @@ final class RemoteInputConnectionImpl extends IRemoteInputConnection.Stub {
@Override
public void commitCorrection(InputConnectionCommandHeader header, CorrectionInfo info) {
dispatchWithTracing("commitCorrection", () -> {
- if (header.mSessionId != mCurrentSessionId.get()) {
- return; // cancelled
+ if (!checkSessionId(header)) {
+ return; // cancelled
}
InputConnection ic = getInputConnection();
if (ic == null || mDeactivateRequested.get()) {
@@ -670,8 +684,8 @@ final class RemoteInputConnectionImpl extends IRemoteInputConnection.Stub {
@Override
public void setSelection(InputConnectionCommandHeader header, int start, int end) {
dispatchWithTracing("setSelection", () -> {
- if (header.mSessionId != mCurrentSessionId.get()) {
- return; // cancelled
+ if (!checkSessionId(header)) {
+ return; // cancelled.
}
InputConnection ic = getInputConnection();
if (ic == null || mDeactivateRequested.get()) {
@@ -686,8 +700,8 @@ final class RemoteInputConnectionImpl extends IRemoteInputConnection.Stub {
@Override
public void performEditorAction(InputConnectionCommandHeader header, int id) {
dispatchWithTracing("performEditorAction", () -> {
- if (header.mSessionId != mCurrentSessionId.get()) {
- return; // cancelled
+ if (!checkSessionId(header)) {
+ return; // cancelled.
}
InputConnection ic = getInputConnection();
if (ic == null || mDeactivateRequested.get()) {
@@ -702,8 +716,8 @@ final class RemoteInputConnectionImpl extends IRemoteInputConnection.Stub {
@Override
public void performContextMenuAction(InputConnectionCommandHeader header, int id) {
dispatchWithTracing("performContextMenuAction", () -> {
- if (header.mSessionId != mCurrentSessionId.get()) {
- return; // cancelled
+ if (!checkSessionId(header)) {
+ return; // cancelled.
}
InputConnection ic = getInputConnection();
if (ic == null || mDeactivateRequested.get()) {
@@ -718,8 +732,8 @@ final class RemoteInputConnectionImpl extends IRemoteInputConnection.Stub {
@Override
public void setComposingRegion(InputConnectionCommandHeader header, int start, int end) {
dispatchWithTracing("setComposingRegion", () -> {
- if (header.mSessionId != mCurrentSessionId.get()) {
- return; // cancelled
+ if (!checkSessionId(header)) {
+ return; // cancelled.
}
InputConnection ic = getInputConnection();
if (ic == null || mDeactivateRequested.get()) {
@@ -739,8 +753,8 @@ final class RemoteInputConnectionImpl extends IRemoteInputConnection.Stub {
public void setComposingRegionWithTextAttribute(InputConnectionCommandHeader header, int start,
int end, @Nullable TextAttribute textAttribute) {
dispatchWithTracing("setComposingRegionWithTextAttribute", () -> {
- if (header.mSessionId != mCurrentSessionId.get()) {
- return; // cancelled
+ if (!checkSessionId(header)) {
+ return; // cancelled.
}
InputConnection ic = getInputConnection();
if (ic == null || mDeactivateRequested.get()) {
@@ -756,8 +770,8 @@ final class RemoteInputConnectionImpl extends IRemoteInputConnection.Stub {
public void setComposingText(InputConnectionCommandHeader header, CharSequence text,
int newCursorPosition) {
dispatchWithTracing("setComposingText", () -> {
- if (header.mSessionId != mCurrentSessionId.get()) {
- return; // cancelled
+ if (!checkSessionId(header)) {
+ return; // cancelled.
}
InputConnection ic = getInputConnection();
if (ic == null || mDeactivateRequested.get()) {
@@ -773,8 +787,8 @@ final class RemoteInputConnectionImpl extends IRemoteInputConnection.Stub {
public void setComposingTextWithTextAttribute(InputConnectionCommandHeader header,
CharSequence text, int newCursorPosition, @Nullable TextAttribute textAttribute) {
dispatchWithTracing("setComposingTextWithTextAttribute", () -> {
- if (header.mSessionId != mCurrentSessionId.get()) {
- return; // cancelled
+ if (!checkSessionId(header)) {
+ return; // cancelled.
}
InputConnection ic = getInputConnection();
if (ic == null || mDeactivateRequested.get()) {
@@ -826,8 +840,8 @@ final class RemoteInputConnectionImpl extends IRemoteInputConnection.Stub {
}
return;
}
- if (header.mSessionId != mCurrentSessionId.get()) {
- return; // cancelled
+ if (!checkSessionId(header)) {
+ return; // cancelled.
}
InputConnection ic = getInputConnection();
if (ic == null && mDeactivateRequested.get()) {
@@ -842,8 +856,8 @@ final class RemoteInputConnectionImpl extends IRemoteInputConnection.Stub {
@Override
public void sendKeyEvent(InputConnectionCommandHeader header, KeyEvent event) {
dispatchWithTracing("sendKeyEvent", () -> {
- if (header.mSessionId != mCurrentSessionId.get()) {
- return; // cancelled
+ if (!checkSessionId(header)) {
+ return; // cancelled.
}
InputConnection ic = getInputConnection();
if (ic == null || mDeactivateRequested.get()) {
@@ -858,8 +872,8 @@ final class RemoteInputConnectionImpl extends IRemoteInputConnection.Stub {
@Override
public void clearMetaKeyStates(InputConnectionCommandHeader header, int states) {
dispatchWithTracing("clearMetaKeyStates", () -> {
- if (header.mSessionId != mCurrentSessionId.get()) {
- return; // cancelled
+ if (!checkSessionId(header)) {
+ return; // cancelled.
}
InputConnection ic = getInputConnection();
if (ic == null || mDeactivateRequested.get()) {
@@ -875,8 +889,8 @@ final class RemoteInputConnectionImpl extends IRemoteInputConnection.Stub {
public void deleteSurroundingText(InputConnectionCommandHeader header, int beforeLength,
int afterLength) {
dispatchWithTracing("deleteSurroundingText", () -> {
- if (header.mSessionId != mCurrentSessionId.get()) {
- return; // cancelled
+ if (!checkSessionId(header)) {
+ return; // cancelled.
}
InputConnection ic = getInputConnection();
if (ic == null || mDeactivateRequested.get()) {
@@ -892,8 +906,8 @@ final class RemoteInputConnectionImpl extends IRemoteInputConnection.Stub {
public void deleteSurroundingTextInCodePoints(InputConnectionCommandHeader header,
int beforeLength, int afterLength) {
dispatchWithTracing("deleteSurroundingTextInCodePoints", () -> {
- if (header.mSessionId != mCurrentSessionId.get()) {
- return; // cancelled
+ if (!checkSessionId(header)) {
+ return; // cancelled.
}
InputConnection ic = getInputConnection();
if (ic == null || mDeactivateRequested.get()) {
@@ -912,8 +926,8 @@ final class RemoteInputConnectionImpl extends IRemoteInputConnection.Stub {
@Override
public void beginBatchEdit(InputConnectionCommandHeader header) {
dispatchWithTracing("beginBatchEdit", () -> {
- if (header.mSessionId != mCurrentSessionId.get()) {
- return; // cancelled
+ if (!checkSessionId(header)) {
+ return; // cancelled.
}
InputConnection ic = getInputConnection();
if (ic == null || mDeactivateRequested.get()) {
@@ -928,8 +942,8 @@ final class RemoteInputConnectionImpl extends IRemoteInputConnection.Stub {
@Override
public void endBatchEdit(InputConnectionCommandHeader header) {
dispatchWithTracing("endBatchEdit", () -> {
- if (header.mSessionId != mCurrentSessionId.get()) {
- return; // cancelled
+ if (!checkSessionId(header)) {
+ return; // cancelled.
}
InputConnection ic = getInputConnection();
if (ic == null || mDeactivateRequested.get()) {
@@ -944,8 +958,8 @@ final class RemoteInputConnectionImpl extends IRemoteInputConnection.Stub {
@Override
public void performSpellCheck(InputConnectionCommandHeader header) {
dispatchWithTracing("performSpellCheck", () -> {
- if (header.mSessionId != mCurrentSessionId.get()) {
- return; // cancelled
+ if (!checkSessionId(header)) {
+ return; // cancelled.
}
InputConnection ic = getInputConnection();
if (ic == null || mDeactivateRequested.get()) {
@@ -961,8 +975,8 @@ final class RemoteInputConnectionImpl extends IRemoteInputConnection.Stub {
public void performPrivateCommand(InputConnectionCommandHeader header, String action,
Bundle data) {
dispatchWithTracing("performPrivateCommand", () -> {
- if (header.mSessionId != mCurrentSessionId.get()) {
- return; // cancelled
+ if (!checkSessionId(header)) {
+ return; // cancelled.
}
InputConnection ic = getInputConnection();
if (ic == null || mDeactivateRequested.get()) {
@@ -995,12 +1009,12 @@ final class RemoteInputConnectionImpl extends IRemoteInputConnection.Stub {
}
}
dispatchWithTracing("performHandwritingGesture", () -> {
- if (header.mSessionId != mCurrentSessionId.get()) {
+ if (!checkSessionId(header)) {
if (resultReceiver != null) {
resultReceiver.send(
InputConnection.HANDWRITING_GESTURE_RESULT_CANCELLED, null);
}
- return; // cancelled
+ return; // cancelled
}
InputConnection ic = getInputConnection();
if (ic == null || mDeactivateRequested.get()) {
@@ -1038,9 +1052,9 @@ final class RemoteInputConnectionImpl extends IRemoteInputConnection.Stub {
(PreviewableHandwritingGesture) gestureContainer.get();
dispatchWithTracing("previewHandwritingGesture", () -> {
- if (header.mSessionId != mCurrentSessionId.get()
+ if (!checkSessionId(header)
|| (cancellationSignal != null && cancellationSignal.isCanceled())) {
- return; // cancelled
+ return; // cancelled
}
InputConnection ic = getInputConnection();
if (ic == null || mDeactivateRequested.get()) {
@@ -1065,8 +1079,8 @@ final class RemoteInputConnectionImpl extends IRemoteInputConnection.Stub {
public void requestCursorUpdates(InputConnectionCommandHeader header, int cursorUpdateMode,
int imeDisplayId, AndroidFuture future /* T=Boolean */) {
dispatchWithTracing("requestCursorUpdates", future, () -> {
- if (header.mSessionId != mCurrentSessionId.get()) {
- return false; // cancelled
+ if (!checkSessionId(header)) {
+ return false; // cancelled.
}
return requestCursorUpdatesInternal(
cursorUpdateMode, 0 /* cursorUpdateFilter */, imeDisplayId);
@@ -1079,8 +1093,8 @@ final class RemoteInputConnectionImpl extends IRemoteInputConnection.Stub {
int cursorUpdateMode, int cursorUpdateFilter, int imeDisplayId,
AndroidFuture future /* T=Boolean */) {
dispatchWithTracing("requestCursorUpdates", future, () -> {
- if (header.mSessionId != mCurrentSessionId.get()) {
- return false; // cancelled
+ if (!checkSessionId(header)) {
+ return false; // cancelled.
}
return requestCursorUpdatesInternal(
cursorUpdateMode, cursorUpdateFilter, imeDisplayId);
@@ -1123,9 +1137,9 @@ final class RemoteInputConnectionImpl extends IRemoteInputConnection.Stub {
InputConnectionCommandHeader header, RectF bounds,
@NonNull ResultReceiver resultReceiver) {
dispatchWithTracing("requestTextBoundsInfo", () -> {
- if (header.mSessionId != mCurrentSessionId.get()) {
+ if (!checkSessionId(header)) {
resultReceiver.send(TextBoundsInfoResult.CODE_CANCELLED, null);
- return; // cancelled
+ return; // cancelled
}
InputConnection ic = getInputConnection();
if (ic == null || mDeactivateRequested.get()) {
@@ -1168,8 +1182,8 @@ final class RemoteInputConnectionImpl extends IRemoteInputConnection.Stub {
return false;
}
- if (header.mSessionId != mCurrentSessionId.get()) {
- return false; // cancelled
+ if (!checkSessionId(header)) {
+ return false; // cancelled.
}
final InputConnection ic = getInputConnection();
if (ic == null || mDeactivateRequested.get()) {
@@ -1193,8 +1207,8 @@ final class RemoteInputConnectionImpl extends IRemoteInputConnection.Stub {
@Override
public void setImeConsumesInput(InputConnectionCommandHeader header, boolean imeConsumesInput) {
dispatchWithTracing("setImeConsumesInput", () -> {
- if (header.mSessionId != mCurrentSessionId.get()) {
- return; // cancelled
+ if (!checkSessionId(header)) {
+ return; // cancelled.
}
InputConnection ic = getInputConnection();
if (ic == null || mDeactivateRequested.get()) {
@@ -1217,8 +1231,8 @@ final class RemoteInputConnectionImpl extends IRemoteInputConnection.Stub {
dispatchWithTracing(
"replaceText",
() -> {
- if (header.mSessionId != mCurrentSessionId.get()) {
- return; // cancelled
+ if (!checkSessionId(header)) {
+ return; // cancelled.
}
InputConnection ic = getInputConnection();
if (ic == null || mDeactivateRequested.get()) {
@@ -1236,8 +1250,8 @@ final class RemoteInputConnectionImpl extends IRemoteInputConnection.Stub {
public void commitText(InputConnectionCommandHeader header, CharSequence text,
int newCursorPosition, @Nullable TextAttribute textAttribute) {
dispatchWithTracing("commitTextFromA11yIme", () -> {
- if (header.mSessionId != mCurrentSessionId.get()) {
- return; // cancelled
+ if (!checkSessionId(header)) {
+ return; // cancelled.
}
InputConnection ic = getInputConnection();
if (ic == null || mDeactivateRequested.get()) {
@@ -1256,8 +1270,8 @@ final class RemoteInputConnectionImpl extends IRemoteInputConnection.Stub {
@Override
public void setSelection(InputConnectionCommandHeader header, int start, int end) {
dispatchWithTracing("setSelectionFromA11yIme", () -> {
- if (header.mSessionId != mCurrentSessionId.get()) {
- return; // cancelled
+ if (!checkSessionId(header)) {
+ return; // cancelled.
}
InputConnection ic = getInputConnection();
if (ic == null || mDeactivateRequested.get()) {
@@ -1273,8 +1287,8 @@ final class RemoteInputConnectionImpl extends IRemoteInputConnection.Stub {
public void getSurroundingText(InputConnectionCommandHeader header, int beforeLength,
int afterLength, int flags, AndroidFuture future /* T=SurroundingText */) {
dispatchWithTracing("getSurroundingTextFromA11yIme", future, () -> {
- if (header.mSessionId != mCurrentSessionId.get()) {
- return null; // cancelled
+ if (!checkSessionId(header)) {
+ return null; // cancelled.
}
final InputConnection ic = getInputConnection();
if (ic == null || mDeactivateRequested.get()) {
@@ -1301,8 +1315,8 @@ final class RemoteInputConnectionImpl extends IRemoteInputConnection.Stub {
public void deleteSurroundingText(InputConnectionCommandHeader header, int beforeLength,
int afterLength) {
dispatchWithTracing("deleteSurroundingTextFromA11yIme", () -> {
- if (header.mSessionId != mCurrentSessionId.get()) {
- return; // cancelled
+ if (!checkSessionId(header)) {
+ return; // cancelled.
}
InputConnection ic = getInputConnection();
if (ic == null || mDeactivateRequested.get()) {
@@ -1317,8 +1331,8 @@ final class RemoteInputConnectionImpl extends IRemoteInputConnection.Stub {
@Override
public void sendKeyEvent(InputConnectionCommandHeader header, KeyEvent event) {
dispatchWithTracing("sendKeyEventFromA11yIme", () -> {
- if (header.mSessionId != mCurrentSessionId.get()) {
- return; // cancelled
+ if (!checkSessionId(header)) {
+ return; // cancelled.
}
InputConnection ic = getInputConnection();
if (ic == null || mDeactivateRequested.get()) {
@@ -1333,8 +1347,8 @@ final class RemoteInputConnectionImpl extends IRemoteInputConnection.Stub {
@Override
public void performEditorAction(InputConnectionCommandHeader header, int id) {
dispatchWithTracing("performEditorActionFromA11yIme", () -> {
- if (header.mSessionId != mCurrentSessionId.get()) {
- return; // cancelled
+ if (!checkSessionId(header)) {
+ return; // cancelled.
}
InputConnection ic = getInputConnection();
if (ic == null || mDeactivateRequested.get()) {
@@ -1349,8 +1363,8 @@ final class RemoteInputConnectionImpl extends IRemoteInputConnection.Stub {
@Override
public void performContextMenuAction(InputConnectionCommandHeader header, int id) {
dispatchWithTracing("performContextMenuActionFromA11yIme", () -> {
- if (header.mSessionId != mCurrentSessionId.get()) {
- return; // cancelled
+ if (!checkSessionId(header)) {
+ return; // cancelled.
}
InputConnection ic = getInputConnection();
if (ic == null || mDeactivateRequested.get()) {
@@ -1366,8 +1380,8 @@ final class RemoteInputConnectionImpl extends IRemoteInputConnection.Stub {
public void getCursorCapsMode(InputConnectionCommandHeader header, int reqModes,
AndroidFuture future /* T=Integer */) {
dispatchWithTracing("getCursorCapsModeFromA11yIme", future, () -> {
- if (header.mSessionId != mCurrentSessionId.get()) {
- return 0; // cancelled
+ if (!checkSessionId(header)) {
+ return 0; // cancelled.
}
final InputConnection ic = getInputConnection();
if (ic == null || mDeactivateRequested.get()) {
@@ -1382,8 +1396,8 @@ final class RemoteInputConnectionImpl extends IRemoteInputConnection.Stub {
@Override
public void clearMetaKeyStates(InputConnectionCommandHeader header, int states) {
dispatchWithTracing("clearMetaKeyStatesFromA11yIme", () -> {
- if (header.mSessionId != mCurrentSessionId.get()) {
- return; // cancelled
+ if (!checkSessionId(header)) {
+ return; // cancelled.
}
InputConnection ic = getInputConnection();
if (ic == null || mDeactivateRequested.get()) {
diff --git a/core/java/android/view/inputmethod/flags.aconfig b/core/java/android/view/inputmethod/flags.aconfig
index a4ea64e5811e..67e54423414c 100644
--- a/core/java/android/view/inputmethod/flags.aconfig
+++ b/core/java/android/view/inputmethod/flags.aconfig
@@ -214,3 +214,13 @@ flag {
purpose: PURPOSE_BUGFIX
}
}
+
+flag {
+ name: "invalidate_input_calls_restart"
+ namespace: "input_method"
+ description: "Feature flag to fix the race between invalidateInput and restartInput"
+ bug: "396066692"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
diff --git a/core/java/android/window/BackMotionEvent.java b/core/java/android/window/BackMotionEvent.java
index cc2afbc6aaa3..d53c787749d9 100644
--- a/core/java/android/window/BackMotionEvent.java
+++ b/core/java/android/window/BackMotionEvent.java
@@ -18,7 +18,6 @@ package android.window;
import android.annotation.FloatRange;
import android.annotation.NonNull;
-import android.annotation.Nullable;
import android.os.Parcel;
import android.os.Parcelable;
import android.view.RemoteAnimationTarget;
@@ -39,8 +38,6 @@ public final class BackMotionEvent implements Parcelable {
@BackEvent.SwipeEdge
private final int mSwipeEdge;
- @Nullable
- private final RemoteAnimationTarget mDepartingAnimationTarget;
/**
* Creates a new {@link BackMotionEvent} instance.
@@ -53,8 +50,6 @@ public final class BackMotionEvent implements Parcelable {
* @param progress Value between 0 and 1 on how far along the back gesture is.
* @param triggerBack Indicates whether the back arrow is in the triggered state or not
* @param swipeEdge Indicates which edge the swipe starts from.
- * @param departingAnimationTarget The remote animation target of the departing
- * application window.
*/
public BackMotionEvent(
float touchX,
@@ -62,15 +57,13 @@ public final class BackMotionEvent implements Parcelable {
long frameTimeMillis,
float progress,
boolean triggerBack,
- @BackEvent.SwipeEdge int swipeEdge,
- @Nullable RemoteAnimationTarget departingAnimationTarget) {
+ @BackEvent.SwipeEdge int swipeEdge) {
mTouchX = touchX;
mTouchY = touchY;
mFrameTimeMillis = frameTimeMillis;
mProgress = progress;
mTriggerBack = triggerBack;
mSwipeEdge = swipeEdge;
- mDepartingAnimationTarget = departingAnimationTarget;
}
private BackMotionEvent(@NonNull Parcel in) {
@@ -79,7 +72,6 @@ public final class BackMotionEvent implements Parcelable {
mProgress = in.readFloat();
mTriggerBack = in.readBoolean();
mSwipeEdge = in.readInt();
- mDepartingAnimationTarget = in.readTypedObject(RemoteAnimationTarget.CREATOR);
mFrameTimeMillis = in.readLong();
}
@@ -108,7 +100,6 @@ public final class BackMotionEvent implements Parcelable {
dest.writeFloat(mProgress);
dest.writeBoolean(mTriggerBack);
dest.writeInt(mSwipeEdge);
- dest.writeTypedObject(mDepartingAnimationTarget, flags);
dest.writeLong(mFrameTimeMillis);
}
@@ -160,16 +151,6 @@ public final class BackMotionEvent implements Parcelable {
return mFrameTimeMillis;
}
- /**
- * Returns the {@link RemoteAnimationTarget} of the top departing application window,
- * or {@code null} if the top window should not be moved for the current type of back
- * destination.
- */
- @Nullable
- public RemoteAnimationTarget getDepartingAnimationTarget() {
- return mDepartingAnimationTarget;
- }
-
@Override
public String toString() {
return "BackMotionEvent{"
@@ -179,7 +160,6 @@ public final class BackMotionEvent implements Parcelable {
+ ", mProgress=" + mProgress
+ ", mTriggerBack=" + mTriggerBack
+ ", mSwipeEdge=" + mSwipeEdge
- + ", mDepartingAnimationTarget=" + mDepartingAnimationTarget
+ "}";
}
}
diff --git a/core/java/android/window/BackTouchTracker.java b/core/java/android/window/BackTouchTracker.java
index 4908068d51e4..ea1b64066cfe 100644
--- a/core/java/android/window/BackTouchTracker.java
+++ b/core/java/android/window/BackTouchTracker.java
@@ -20,7 +20,6 @@ import android.annotation.FloatRange;
import android.os.SystemProperties;
import android.util.MathUtils;
import android.view.MotionEvent;
-import android.view.RemoteAnimationTarget;
import java.io.PrintWriter;
@@ -147,15 +146,14 @@ public class BackTouchTracker {
}
/** Creates a start {@link BackMotionEvent}. */
- public BackMotionEvent createStartEvent(RemoteAnimationTarget target) {
+ public BackMotionEvent createStartEvent() {
return new BackMotionEvent(
/* touchX = */ mInitTouchX,
/* touchY = */ mInitTouchY,
/* frameTimeMillis = */ 0,
/* progress = */ 0,
/* triggerBack = */ mTriggerBack,
- /* swipeEdge = */ mSwipeEdge,
- /* departingAnimationTarget = */ target);
+ /* swipeEdge = */ mSwipeEdge);
}
/** Creates a progress {@link BackMotionEvent}. */
@@ -239,8 +237,7 @@ public class BackTouchTracker {
/* frameTimeMillis = */ 0,
/* progress = */ progress,
/* triggerBack = */ mTriggerBack,
- /* swipeEdge = */ mSwipeEdge,
- /* departingAnimationTarget = */ null);
+ /* swipeEdge = */ mSwipeEdge);
}
/** Sets the thresholds for computing progress. */
diff --git a/core/java/android/window/DesktopExperienceFlags.java b/core/java/android/window/DesktopExperienceFlags.java
index 866c16cb566d..50b8bd22350c 100644
--- a/core/java/android/window/DesktopExperienceFlags.java
+++ b/core/java/android/window/DesktopExperienceFlags.java
@@ -56,9 +56,12 @@ public enum DesktopExperienceFlags {
ENABLE_DISPLAY_CONTENT_MODE_MANAGEMENT(
com.android.server.display.feature.flags.Flags::enableDisplayContentModeManagement,
true),
+ ENABLE_DISPLAY_DISCONNECT_INTERACTION(Flags::enableDisplayDisconnectInteraction, false),
ENABLE_DISPLAY_FOCUS_IN_SHELL_TRANSITIONS(Flags::enableDisplayFocusInShellTransitions, true),
+ ENABLE_DISPLAY_RECONNECT_INTERACTION(Flags::enableDisplayReconnectInteraction, false),
ENABLE_DISPLAY_WINDOWING_MODE_SWITCHING(Flags::enableDisplayWindowingModeSwitching, true),
ENABLE_DRAG_TO_MAXIMIZE(Flags::enableDragToMaximize, true),
+ ENABLE_KEYBOARD_SHORTCUTS_TO_SWITCH_DESKS(Flags::keyboardShortcutsToSwitchDesks, false),
ENABLE_MOVE_TO_NEXT_DISPLAY_SHORTCUT(Flags::enableMoveToNextDisplayShortcut, true),
ENABLE_MULTIPLE_DESKTOPS_BACKEND(Flags::enableMultipleDesktopsBackend, false),
ENABLE_MULTIPLE_DESKTOPS_FRONTEND(Flags::enableMultipleDesktopsFrontend, false),
diff --git a/core/java/android/window/IWindowOrganizerController.aidl b/core/java/android/window/IWindowOrganizerController.aidl
index 6f4dd4e3c5ed..0b84070c8d26 100644
--- a/core/java/android/window/IWindowOrganizerController.aidl
+++ b/core/java/android/window/IWindowOrganizerController.aidl
@@ -66,17 +66,6 @@ interface IWindowOrganizerController {
void startTransition(IBinder transitionToken, in @nullable WindowContainerTransaction t);
/**
- * Starts a legacy transition.
- * @param type The transition type.
- * @param adapter The animation to use.
- * @param syncCallback A sync callback for the contents of `t`
- * @param t Operations that are part of the transition.
- * @return sync-id or -1 if this no-op'd because a transition is already running.
- */
- int startLegacyTransition(int type, in RemoteAnimationAdapter adapter,
- in IWindowContainerTransactionCallback syncCallback, in WindowContainerTransaction t);
-
- /**
* Finishes a transition. This must be called for all created transitions.
* @param transitionToken Which transition to finish
* @param t Changes to make before finishing but in the same SF Transaction. Can be null.
diff --git a/core/java/android/window/ImeOnBackInvokedDispatcher.java b/core/java/android/window/ImeOnBackInvokedDispatcher.java
index d478108d928a..69613a748884 100644
--- a/core/java/android/window/ImeOnBackInvokedDispatcher.java
+++ b/core/java/android/window/ImeOnBackInvokedDispatcher.java
@@ -270,8 +270,7 @@ public class ImeOnBackInvokedDispatcher implements OnBackInvokedDispatcher, Parc
}
mIOnBackInvokedCallback.onBackStarted(
new BackMotionEvent(backEvent.getTouchX(), backEvent.getTouchY(), frameTime,
- backEvent.getProgress(), false, backEvent.getSwipeEdge(),
- null));
+ backEvent.getProgress(), false, backEvent.getSwipeEdge()));
} catch (RemoteException e) {
Log.e(TAG, "Exception when invoking forwarded callback. e: ", e);
}
@@ -286,8 +285,7 @@ public class ImeOnBackInvokedDispatcher implements OnBackInvokedDispatcher, Parc
}
mIOnBackInvokedCallback.onBackProgressed(
new BackMotionEvent(backEvent.getTouchX(), backEvent.getTouchY(), frameTime,
- backEvent.getProgress(), false, backEvent.getSwipeEdge(),
- null));
+ backEvent.getProgress(), false, backEvent.getSwipeEdge()));
} catch (RemoteException e) {
Log.e(TAG, "Exception when invoking forwarded callback. e: ", e);
}
diff --git a/core/java/android/window/WindowOrganizer.java b/core/java/android/window/WindowOrganizer.java
index 5c5da49fe543..6e56b6318727 100644
--- a/core/java/android/window/WindowOrganizer.java
+++ b/core/java/android/window/WindowOrganizer.java
@@ -130,27 +130,6 @@ public class WindowOrganizer {
}
/**
- * Start a legacy transition.
- * @param type The type of the transition. This is ignored if a transitionToken is provided.
- * @param adapter An existing transition to start. If null, a new transition is created.
- * @param t The set of window operations that are part of this transition.
- * @return true on success, false if a transition was already running.
- * @hide
- */
- @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_TASKS)
- @NonNull
- public int startLegacyTransition(int type, @NonNull RemoteAnimationAdapter adapter,
- @NonNull WindowContainerTransactionCallback syncCallback,
- @NonNull WindowContainerTransaction t) {
- try {
- return getWindowOrganizerController().startLegacyTransition(
- type, adapter, syncCallback.mInterface, t);
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- }
- }
-
- /**
* Register an ITransitionPlayer to handle transition animations.
* @hide
*/
diff --git a/core/java/android/window/flags/lse_desktop_experience.aconfig b/core/java/android/window/flags/lse_desktop_experience.aconfig
index e706af999117..53db2b1178cf 100644
--- a/core/java/android/window/flags/lse_desktop_experience.aconfig
+++ b/core/java/android/window/flags/lse_desktop_experience.aconfig
@@ -602,6 +602,13 @@ flag {
}
flag {
+ name: "keyboard_shortcuts_to_switch_desks"
+ namespace: "lse_desktop_experience"
+ description: "Enable switching the active desk with keyboard shortcuts"
+ bug: "389957556"
+}
+
+flag {
name: "enable_connected_displays_dnd"
namespace: "lse_desktop_experience"
description: "Enable drag-and-drop between connected displays."
@@ -945,3 +952,13 @@ flag {
description: "Enable restart menu UI, which is shown when an app moves between displays."
bug: "397804287"
}
+
+flag {
+ name: "enable_dynamic_radius_computation_bugfix"
+ namespace: "lse_desktop_experience"
+ description: "Enables bugfix to compute the corner/shadow radius of desktop windows dynamically with the current window context."
+ bug: "399630464"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
diff --git a/core/java/com/android/internal/app/MediaRouteChooserContentManager.java b/core/java/com/android/internal/app/MediaRouteChooserContentManager.java
new file mode 100644
index 000000000000..09c6f5e6caaa
--- /dev/null
+++ b/core/java/com/android/internal/app/MediaRouteChooserContentManager.java
@@ -0,0 +1,56 @@
+/*
+ * 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.internal.app;
+
+import android.content.Context;
+import android.view.Gravity;
+import android.view.View;
+import android.widget.LinearLayout;
+import android.widget.ListView;
+
+import com.android.internal.R;
+
+public class MediaRouteChooserContentManager {
+ Context mContext;
+
+ private final boolean mShowProgressBarWhenEmpty;
+
+ public MediaRouteChooserContentManager(Context context, boolean showProgressBarWhenEmpty) {
+ mContext = context;
+ mShowProgressBarWhenEmpty = showProgressBarWhenEmpty;
+ }
+
+ /**
+ * Starts binding all the views (list view, empty view, etc.) using the
+ * given container view.
+ */
+ public void bindViews(View containerView) {
+ View emptyView = containerView.findViewById(android.R.id.empty);
+ ListView listView = containerView.findViewById(R.id.media_route_list);
+ listView.setEmptyView(emptyView);
+
+ if (!mShowProgressBarWhenEmpty) {
+ containerView.findViewById(R.id.media_route_progress_bar).setVisibility(View.GONE);
+
+ // Center the empty view when the progress bar is not shown.
+ LinearLayout.LayoutParams params =
+ (LinearLayout.LayoutParams) emptyView.getLayoutParams();
+ params.gravity = Gravity.CENTER;
+ emptyView.setLayoutParams(params);
+ }
+ }
+}
diff --git a/core/java/com/android/internal/app/MediaRouteChooserDialog.java b/core/java/com/android/internal/app/MediaRouteChooserDialog.java
index 23d966fdbc0d..5030a143ea94 100644
--- a/core/java/com/android/internal/app/MediaRouteChooserDialog.java
+++ b/core/java/com/android/internal/app/MediaRouteChooserDialog.java
@@ -23,14 +23,12 @@ import android.media.MediaRouter.RouteInfo;
import android.os.Bundle;
import android.text.TextUtils;
import android.util.TypedValue;
-import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import android.widget.Button;
-import android.widget.LinearLayout;
import android.widget.ListView;
import android.widget.TextView;
@@ -52,15 +50,15 @@ import java.util.Comparator;
public class MediaRouteChooserDialog extends AlertDialog {
private final MediaRouter mRouter;
private final MediaRouterCallback mCallback;
- private final boolean mShowProgressBarWhenEmpty;
private int mRouteTypes;
private View.OnClickListener mExtendedSettingsClickListener;
private RouteAdapter mAdapter;
- private ListView mListView;
private Button mExtendedSettingsButton;
private boolean mAttachedToWindow;
+ private final MediaRouteChooserContentManager mContentManager;
+
public MediaRouteChooserDialog(Context context, int theme) {
this(context, theme, true);
}
@@ -70,7 +68,7 @@ public class MediaRouteChooserDialog extends AlertDialog {
mRouter = (MediaRouter) context.getSystemService(Context.MEDIA_ROUTER_SERVICE);
mCallback = new MediaRouterCallback();
- mShowProgressBarWhenEmpty = showProgressBarWhenEmpty;
+ mContentManager = new MediaRouteChooserContentManager(context, showProgressBarWhenEmpty);
}
/**
@@ -128,8 +126,9 @@ public class MediaRouteChooserDialog extends AlertDialog {
@Override
protected void onCreate(Bundle savedInstanceState) {
// Note: setView must be called before super.onCreate().
- setView(LayoutInflater.from(getContext()).inflate(R.layout.media_route_chooser_dialog,
- null));
+ View containerView = LayoutInflater.from(getContext()).inflate(
+ R.layout.media_route_chooser_dialog, null);
+ setView(containerView);
setTitle(mRouteTypes == MediaRouter.ROUTE_TYPE_REMOTE_DISPLAY
? R.string.media_route_chooser_title_for_remote_display
@@ -140,25 +139,15 @@ public class MediaRouteChooserDialog extends AlertDialog {
super.onCreate(savedInstanceState);
- View emptyView = findViewById(android.R.id.empty);
mAdapter = new RouteAdapter(getContext());
- mListView = (ListView) findViewById(R.id.media_route_list);
- mListView.setAdapter(mAdapter);
- mListView.setOnItemClickListener(mAdapter);
- mListView.setEmptyView(emptyView);
+ ListView listView = findViewById(R.id.media_route_list);
+ listView.setAdapter(mAdapter);
+ listView.setOnItemClickListener(mAdapter);
- mExtendedSettingsButton = (Button) findViewById(R.id.media_route_extended_settings_button);
+ mExtendedSettingsButton = findViewById(R.id.media_route_extended_settings_button);
updateExtendedSettingsButton();
- if (!mShowProgressBarWhenEmpty) {
- findViewById(R.id.media_route_progress_bar).setVisibility(View.GONE);
-
- // Center the empty view when the progress bar is not shown.
- LinearLayout.LayoutParams params =
- (LinearLayout.LayoutParams) emptyView.getLayoutParams();
- params.gravity = Gravity.CENTER;
- emptyView.setLayoutParams(params);
- }
+ mContentManager.bindViews(containerView);
}
private void updateExtendedSettingsButton() {
@@ -240,8 +229,8 @@ public class MediaRouteChooserDialog extends AlertDialog {
view = mInflater.inflate(R.layout.media_route_list_item, parent, false);
}
MediaRouter.RouteInfo route = getItem(position);
- TextView text1 = (TextView)view.findViewById(android.R.id.text1);
- TextView text2 = (TextView)view.findViewById(android.R.id.text2);
+ TextView text1 = view.findViewById(android.R.id.text1);
+ TextView text2 = view.findViewById(android.R.id.text2);
text1.setText(route.getName());
CharSequence description = route.getDescription();
if (TextUtils.isEmpty(description)) {
diff --git a/core/jni/android_os_Parcel.cpp b/core/jni/android_os_Parcel.cpp
index dec724b6a7ff..b4c58b9b246a 100644
--- a/core/jni/android_os_Parcel.cpp
+++ b/core/jni/android_os_Parcel.cpp
@@ -558,8 +558,7 @@ static void android_os_Parcel_destroy(JNIEnv* env, jclass clazz, jlong nativePtr
delete parcel;
}
-static jbyteArray android_os_Parcel_marshall(JNIEnv* env, jclass clazz, jlong nativePtr)
-{
+static Parcel* parcel_for_marshall(JNIEnv* env, jlong nativePtr) {
Parcel* parcel = reinterpret_cast<Parcel*>(nativePtr);
if (parcel == NULL) {
return NULL;
@@ -577,6 +576,16 @@ static jbyteArray android_os_Parcel_marshall(JNIEnv* env, jclass clazz, jlong na
return NULL;
}
+ return parcel;
+}
+
+static jbyteArray android_os_Parcel_marshall(JNIEnv* env, jclass clazz, jlong nativePtr)
+{
+ Parcel* parcel = parcel_for_marshall(env, nativePtr);
+ if (parcel == NULL) {
+ return NULL;
+ }
+
jbyteArray ret = env->NewByteArray(parcel->dataSize());
if (ret != NULL)
@@ -592,6 +601,58 @@ static jbyteArray android_os_Parcel_marshall(JNIEnv* env, jclass clazz, jlong na
return ret;
}
+static long ensure_capacity(JNIEnv* env, Parcel* parcel, jint remaining) {
+ long dataSize = parcel->dataSize();
+ if (remaining < dataSize) {
+ jniThrowExceptionFmt(env, "java/nio/BufferOverflowException",
+ "Destination buffer remaining capacity %d is less than the Parcel data size %d.",
+ remaining, dataSize);
+ return -1;
+ }
+ return dataSize;
+}
+
+static int android_os_Parcel_marshall_array(JNIEnv* env, jclass clazz, jlong nativePtr,
+ jbyteArray data, jint offset, jint remaining)
+{
+ Parcel* parcel = parcel_for_marshall(env, nativePtr);
+ if (parcel == NULL) {
+ return 0;
+ }
+
+ long data_size = ensure_capacity(env, parcel, remaining);
+ if (data_size < 0) {
+ return 0;
+ }
+
+ jbyte* array = (jbyte*)env->GetPrimitiveArrayCritical(data, 0);
+ if (array != NULL)
+ {
+ memcpy(array + offset, parcel->data(), data_size);
+ env->ReleasePrimitiveArrayCritical(data, array, 0);
+ }
+ return data_size;
+}
+
+static int android_os_Parcel_marshall_buffer(JNIEnv* env, jclass clazz, jlong nativePtr,
+ jobject javaBuffer, jint offset, jint remaining) {
+ Parcel* parcel = parcel_for_marshall(env, nativePtr);
+ if (parcel == NULL) {
+ return 0;
+ }
+
+ long data_size = ensure_capacity(env, parcel, remaining);
+ if (data_size < 0) {
+ return 0;
+ }
+
+ jbyte* buffer = (jbyte*)env->GetDirectBufferAddress(javaBuffer);
+ if (buffer != NULL) {
+ memcpy(buffer + offset, parcel->data(), data_size);
+ }
+ return data_size;
+}
+
static void android_os_Parcel_unmarshall(JNIEnv* env, jclass clazz, jlong nativePtr,
jbyteArray data, jint offset, jint length)
{
@@ -613,6 +674,25 @@ static void android_os_Parcel_unmarshall(JNIEnv* env, jclass clazz, jlong native
}
}
+static void android_os_Parcel_unmarshall_buffer(JNIEnv* env, jclass clazz, jlong nativePtr,
+ jobject javaBuffer, jint offset, jint length)
+{
+ Parcel* parcel = reinterpret_cast<Parcel*>(nativePtr);
+ if (parcel == NULL || length < 0) {
+ return;
+ }
+
+ jbyte* buffer = (jbyte*)env->GetDirectBufferAddress(javaBuffer);
+ if (buffer)
+ {
+ parcel->setDataSize(length);
+ parcel->setDataPosition(0);
+
+ void* raw = parcel->writeInplace(length);
+ memcpy(raw, (buffer + offset), length);
+ }
+}
+
static jint android_os_Parcel_compareData(JNIEnv* env, jclass clazz, jlong thisNativePtr,
jlong otherNativePtr)
{
@@ -911,7 +991,10 @@ static const JNINativeMethod gParcelMethods[] = {
{"nativeDestroy", "(J)V", (void*)android_os_Parcel_destroy},
{"nativeMarshall", "(J)[B", (void*)android_os_Parcel_marshall},
+ {"nativeMarshallArray", "(J[BII)I", (void*)android_os_Parcel_marshall_array},
+ {"nativeMarshallBuffer", "(JLjava/nio/ByteBuffer;II)I", (void*)android_os_Parcel_marshall_buffer},
{"nativeUnmarshall", "(J[BII)V", (void*)android_os_Parcel_unmarshall},
+ {"nativeUnmarshallBuffer", "(JLjava/nio/ByteBuffer;II)V", (void*)android_os_Parcel_unmarshall_buffer},
{"nativeCompareData", "(JJ)I", (void*)android_os_Parcel_compareData},
{"nativeCompareDataInRange", "(JIJII)Z", (void*)android_os_Parcel_compareDataInRange},
{"nativeAppendFrom", "(JJII)V", (void*)android_os_Parcel_appendFrom},
diff --git a/core/proto/android/os/incident.proto b/core/proto/android/os/incident.proto
index 59e01bfa0c4b..9df351fa39c4 100644
--- a/core/proto/android/os/incident.proto
+++ b/core/proto/android/os/incident.proto
@@ -35,7 +35,6 @@ import "frameworks/base/core/proto/android/os/system_properties.proto";
import "frameworks/base/core/proto/android/providers/settings.proto";
import "frameworks/base/core/proto/android/server/activitymanagerservice.proto";
import "frameworks/base/core/proto/android/server/alarm/alarmmanagerservice.proto";
-import "frameworks/base/core/proto/android/server/bluetooth_manager_service.proto";
import "frameworks/base/core/proto/android/server/fingerprint.proto";
import "frameworks/base/core/proto/android/server/jobscheduler.proto";
import "frameworks/base/core/proto/android/server/location/context_hub.proto";
@@ -483,10 +482,8 @@ message IncidentProto {
(section).args = "connmetrics --proto"
];
- optional com.android.server.BluetoothManagerServiceDumpProto bluetooth_manager = 3050 [
- (section).type = SECTION_DUMPSYS,
- (section).args = "bluetooth_manager --proto"
- ];
+ // Deprecated BluetoothManagerServiceDumpProto
+ reserved 3050;
optional com.android.server.location.ContextHubServiceProto context_hub = 3051 [
(section).type = SECTION_DUMPSYS,
diff --git a/core/proto/android/providers/settings/system.proto b/core/proto/android/providers/settings/system.proto
index 325790c22fce..8393f8b4db61 100644
--- a/core/proto/android/providers/settings/system.proto
+++ b/core/proto/android/providers/settings/system.proto
@@ -290,7 +290,16 @@ message SystemSettingsProto {
optional SettingProto apply_ramping_ringer = 35 [ (android.privacy).dest = DEST_AUTOMATIC ];
+ message Display {
+ option (android.msg_privacy).dest = DEST_EXPLICIT;
+
+ optional SettingProto cv_enabled = 1 [ (android.privacy).dest = DEST_AUTOMATIC ];
+ }
+ optional Display display = 39;
+
+
+
// Please insert fields in alphabetical order and group them into messages
// if possible (to avoid reaching the method limit).
- // Next tag = 39;
+ // Next tag = 40;
}
diff --git a/core/proto/android/server/Android.bp b/core/proto/android/server/Android.bp
deleted file mode 100644
index 362daa73ff14..000000000000
--- a/core/proto/android/server/Android.bp
+++ /dev/null
@@ -1,28 +0,0 @@
-//
-// Copyright (C) 2021 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-//
-
-package {
- default_applicable_licenses: ["Android-Apache-2.0"],
-}
-
-filegroup {
- name: "srcs_bluetooth_manager_service_proto",
- srcs: [
- "bluetooth_manager_service.proto",
- ],
- visibility: ["//packages/modules/Bluetooth:__subpackages__"],
-}
-
diff --git a/core/proto/android/server/bluetooth_manager_service.proto b/core/proto/android/server/bluetooth_manager_service.proto
deleted file mode 100644
index c33f66a9aeca..000000000000
--- a/core/proto/android/server/bluetooth_manager_service.proto
+++ /dev/null
@@ -1,49 +0,0 @@
-/*
- * Copyright (C) 2020 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.
- */
-
-syntax = "proto2";
-package com.android.server;
-
-import "frameworks/base/core/proto/android/privacy.proto";
-import "frameworks/proto_logging/stats/enums/bluetooth/enums.proto";
-
-option java_multiple_files = true;
-
-message BluetoothManagerServiceDumpProto {
- option (.android.msg_privacy).dest = DEST_AUTOMATIC;
-
- message ActiveLog {
- option (.android.msg_privacy).dest = DEST_AUTOMATIC;
- optional int64 timestamp_ms = 1;
- optional bool enable = 2;
- optional string package_name = 3;
- optional .android.bluetooth.EnableDisableReasonEnum reason = 4;
- }
-
- optional bool enabled = 1;
- optional int32 state = 2;
- optional string state_name = 3;
- optional string address = 4 [(.android.privacy).dest = DEST_EXPLICIT];
- optional string name = 5 [(.android.privacy).dest = DEST_EXPLICIT];
- optional int64 last_enabled_time_ms = 6;
- optional int64 curr_timestamp_ms = 7;
- repeated ActiveLog active_logs = 8;
- optional int32 num_crashes = 9;
- optional bool crash_log_maxed = 10;
- repeated int64 crash_timestamps_ms = 11;
- optional int32 num_ble_apps = 12;
- repeated string ble_app_package_names = 13;
-} \ No newline at end of file
diff --git a/core/tests/coretests/src/android/accessibilityservice/BrailleDisplayControllerImplTest.java b/core/tests/coretests/src/android/accessibilityservice/BrailleDisplayControllerImplTest.java
index e8b295bd5fdb..0287e6c086aa 100644
--- a/core/tests/coretests/src/android/accessibilityservice/BrailleDisplayControllerImplTest.java
+++ b/core/tests/coretests/src/android/accessibilityservice/BrailleDisplayControllerImplTest.java
@@ -22,7 +22,7 @@ import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.verifyZeroInteractions;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
import android.hardware.usb.UsbDevice;
import android.platform.test.annotations.RequiresFlagsEnabled;
@@ -118,6 +118,6 @@ public class BrailleDisplayControllerImplTest {
verify(mBrailleDisplayCallback).onConnectionFailed(
BrailleDisplayController.BrailleDisplayCallback.FLAG_ERROR_CANNOT_ACCESS);
- verifyZeroInteractions(mAccessibilityServiceConnection);
+ verifyNoMoreInteractions(mAccessibilityServiceConnection);
}
}
diff --git a/core/tests/coretests/src/android/content/pm/RegisteredServicesCacheTest.java b/core/tests/coretests/src/android/content/pm/RegisteredServicesCacheTest.java
index dccbf4036b3e..70b4150115fe 100644
--- a/core/tests/coretests/src/android/content/pm/RegisteredServicesCacheTest.java
+++ b/core/tests/coretests/src/android/content/pm/RegisteredServicesCacheTest.java
@@ -233,7 +233,7 @@ public class RegisteredServicesCacheTest extends AndroidTestCase {
/**
* Mock implementation of {@link android.content.pm.RegisteredServicesCache} for testing
*/
- private class TestServicesCache extends RegisteredServicesCache<TestServiceType> {
+ public class TestServicesCache extends RegisteredServicesCache<TestServiceType> {
static final String SERVICE_INTERFACE = "RegisteredServicesCacheTest";
static final String SERVICE_META_DATA = "RegisteredServicesCacheTest";
static final String ATTRIBUTES_NAME = "test";
@@ -245,12 +245,6 @@ public class RegisteredServicesCacheTest extends AndroidTestCase {
SERVICE_INTERFACE, SERVICE_META_DATA, ATTRIBUTES_NAME, new TestSerializer());
}
- TestServicesCache(Injector<TestServiceType> injector,
- XmlSerializerAndParser<TestServiceType> serializerAndParser) {
- super(injector, SERVICE_INTERFACE, SERVICE_META_DATA, ATTRIBUTES_NAME,
- serializerAndParser);
- }
-
@Override
public TestServiceType parseServiceAttributes(Resources res, String packageName,
AttributeSet attrs) {
@@ -338,7 +332,7 @@ public class RegisteredServicesCacheTest extends AndroidTestCase {
}
}
- static class TestSerializer implements XmlSerializerAndParser<TestServiceType> {
+ public static class TestSerializer implements XmlSerializerAndParser<TestServiceType> {
public void writeAsXml(TestServiceType item, TypedXmlSerializer out) throws IOException {
out.attribute(null, "type", item.type);
@@ -353,7 +347,7 @@ public class RegisteredServicesCacheTest extends AndroidTestCase {
}
}
- static class TestServiceType implements Parcelable {
+ public static class TestServiceType implements Parcelable {
final String type;
final String value;
diff --git a/core/tests/coretests/src/android/content/pm/RegisteredServicesCacheUnitTest.java b/core/tests/coretests/src/android/content/pm/RegisteredServicesCacheUnitTest.java
new file mode 100644
index 000000000000..8349659517c5
--- /dev/null
+++ b/core/tests/coretests/src/android/content/pm/RegisteredServicesCacheUnitTest.java
@@ -0,0 +1,403 @@
+/*
+ * 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 android.content.pm;
+
+import static android.content.pm.Flags.FLAG_OPTIMIZE_PARSING_IN_REGISTERED_SERVICES_CACHE;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyLong;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.doThrow;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.reset;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.RegisteredServicesCacheTest.TestServiceType;
+import android.content.res.Resources;
+import android.os.Handler;
+import android.os.Message;
+import android.os.UserHandle;
+import android.platform.test.annotations.Presubmit;
+import android.platform.test.annotations.RequiresFlagsEnabled;
+import android.platform.test.flag.junit.CheckFlagsRule;
+import android.platform.test.flag.junit.DeviceFlagsValueProvider;
+import android.util.AttributeSet;
+import android.util.SparseArray;
+
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+
+import com.android.internal.os.BackgroundThread;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.xmlpull.v1.XmlPullParserException;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Unit tests for {@link android.content.pm.RegisteredServicesCache}
+ */
+@Presubmit
+@RunWith(AndroidJUnit4.class)
+@RequiresFlagsEnabled(FLAG_OPTIMIZE_PARSING_IN_REGISTERED_SERVICES_CACHE)
+public class RegisteredServicesCacheUnitTest {
+ private static final String TAG = "RegisteredServicesCacheUnitTest";
+ private static final int U0 = 0;
+ private static final int U1 = 1;
+ private static final int UID1 = 1;
+
+ @Rule
+ public final CheckFlagsRule mCheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule();
+
+ private final ResolveInfo mResolveInfo1 = new ResolveInfo();
+ private final ResolveInfo mResolveInfo2 = new ResolveInfo();
+ private final TestServiceType mTestServiceType1 = new TestServiceType("t1", "value1");
+ private final TestServiceType mTestServiceType2 = new TestServiceType("t2", "value2");
+ @Mock
+ RegisteredServicesCache.Injector<TestServiceType> mMockInjector;
+ @Mock
+ Context mMockContext;
+ Handler mMockBackgroundHandler;
+ @Mock
+ PackageManager mMockPackageManager;
+
+ @Before
+ public void setup() throws Exception {
+ MockitoAnnotations.initMocks(this);
+
+ when(mMockInjector.getContext()).thenReturn(mMockContext);
+ mMockBackgroundHandler = spy(BackgroundThread.getHandler());
+ when(mMockInjector.getBackgroundHandler()).thenReturn(mMockBackgroundHandler);
+ doReturn(mock(Intent.class)).when(mMockContext).registerReceiverAsUser(any(), any(), any(),
+ any(), any());
+ doReturn(mock(Intent.class)).when(mMockContext).registerReceiver(any(), any(), any(),
+ any());
+ when(mMockContext.getPackageManager()).thenReturn(mMockPackageManager);
+
+ addServiceInfoIntoResolveInfo(mResolveInfo1, "r1.package.name" /* packageName */,
+ "r1.service.name" /* serviceName */);
+ addServiceInfoIntoResolveInfo(mResolveInfo2, "r2.package.name" /* packageName */,
+ "r2.service.name" /* serviceName */);
+ }
+
+ @Test
+ public void testSaveServiceInfoIntoCaches() throws Exception {
+ PackageInfo packageInfo1 = createPackageInfo(1000L /* lastUpdateTime */);
+ when(mMockPackageManager.getPackageInfoAsUser(eq(mResolveInfo1.serviceInfo.packageName),
+ anyInt(), eq(U0))).thenReturn(packageInfo1);
+ PackageInfo packageInfo2 = createPackageInfo(2000L /* lastUpdateTime */);
+ when(mMockPackageManager.getPackageInfoAsUser(eq(mResolveInfo2.serviceInfo.packageName),
+ anyInt(), eq(U1))).thenReturn(packageInfo2);
+
+ TestRegisteredServicesCache testServicesCache = spy(
+ new TestRegisteredServicesCache(mMockInjector, null /* serializerAndParser */));
+ final RegisteredServicesCache.ServiceInfo<TestServiceType> serviceInfo1 = newServiceInfo(
+ mTestServiceType1, UID1, mResolveInfo1.serviceInfo.getComponentName(),
+ 1000L /* lastUpdateTime */);
+ testServicesCache.addServiceForQuerying(U0, mResolveInfo1, serviceInfo1);
+
+ int u1uid = UserHandle.getUid(U1, UID1);
+ final RegisteredServicesCache.ServiceInfo<TestServiceType> serviceInfo2 = newServiceInfo(
+ mTestServiceType2, u1uid, mResolveInfo2.serviceInfo.getComponentName(),
+ 2000L /* lastUpdateTime */);
+ testServicesCache.addServiceForQuerying(U1, mResolveInfo2, serviceInfo2);
+
+ testServicesCache.getAllServices(U0);
+ verify(testServicesCache, times(1)).parseServiceInfo(eq(mResolveInfo1), eq(1000L));
+ testServicesCache.getAllServices(U1);
+ verify(testServicesCache, times(1)).parseServiceInfo(eq(mResolveInfo2), eq(2000L));
+
+ reset(testServicesCache);
+
+ testServicesCache.invalidateCache(U0);
+ testServicesCache.invalidateCache(U1);
+ testServicesCache.getAllServices(U0);
+ verify(testServicesCache, never()).parseServiceInfo(eq(mResolveInfo1), eq(1000L));
+ testServicesCache.getAllServices(U1);
+ verify(testServicesCache, never()).parseServiceInfo(eq(mResolveInfo2), eq(2000L));
+ }
+
+ @Test
+ public void testClearServiceInfoCachesAfterRemoveUserId() throws Exception {
+ PackageInfo packageInfo1 = createPackageInfo(1000L /* lastUpdateTime */);
+ when(mMockPackageManager.getPackageInfoAsUser(eq(mResolveInfo1.serviceInfo.packageName),
+ anyInt(), eq(U0))).thenReturn(packageInfo1);
+
+ TestRegisteredServicesCache testServicesCache = spy(
+ new TestRegisteredServicesCache(mMockInjector, null /* serializerAndParser */));
+ final RegisteredServicesCache.ServiceInfo<TestServiceType> serviceInfo1 = newServiceInfo(
+ mTestServiceType1, UID1, mResolveInfo1.serviceInfo.getComponentName(),
+ 1000L /* lastUpdateTime */);
+ testServicesCache.addServiceForQuerying(U0, mResolveInfo1, serviceInfo1);
+
+ testServicesCache.getAllServices(U0);
+ verify(testServicesCache, times(1)).parseServiceInfo(eq(mResolveInfo1), eq(1000L));
+
+ reset(testServicesCache);
+
+ testServicesCache.onUserRemoved(U0);
+ testServicesCache.getAllServices(U0);
+ verify(testServicesCache, times(1)).parseServiceInfo(eq(mResolveInfo1), eq(1000L));
+ }
+
+ @Test
+ public void testGetServiceInfoCachesForMultiUser() throws Exception {
+ PackageInfo packageInfo1 = createPackageInfo(1000L /* lastUpdateTime */);
+ when(mMockPackageManager.getPackageInfoAsUser(eq(mResolveInfo1.serviceInfo.packageName),
+ anyInt(), eq(U0))).thenReturn(packageInfo1);
+ when(mMockPackageManager.getPackageInfoAsUser(eq(mResolveInfo1.serviceInfo.packageName),
+ anyInt(), eq(U1))).thenReturn(packageInfo1);
+
+ TestRegisteredServicesCache testServicesCache = spy(
+ new TestRegisteredServicesCache(mMockInjector, null /* serializerAndParser */));
+ final RegisteredServicesCache.ServiceInfo<TestServiceType> serviceInfo1 = newServiceInfo(
+ mTestServiceType1, UID1, mResolveInfo1.serviceInfo.getComponentName(),
+ 1000L /* lastUpdateTime */);
+ testServicesCache.addServiceForQuerying(U0, mResolveInfo1, serviceInfo1);
+
+ testServicesCache.getAllServices(U0);
+ verify(testServicesCache, times(1)).parseServiceInfo(eq(mResolveInfo1), eq(1000L));
+
+ reset(testServicesCache);
+
+ testServicesCache.clearServicesForQuerying();
+ int u1uid = UserHandle.getUid(U1, UID1);
+ assertThat(u1uid).isNotEqualTo(UID1);
+
+ final RegisteredServicesCache.ServiceInfo<TestServiceType> serviceInfo2 = newServiceInfo(
+ mTestServiceType1, u1uid, mResolveInfo1.serviceInfo.getComponentName(),
+ 1000L /* lastUpdateTime */);
+ testServicesCache.addServiceForQuerying(U1, mResolveInfo1, serviceInfo2);
+
+ testServicesCache.getAllServices(U1);
+ verify(testServicesCache, times(1)).parseServiceInfo(eq(mResolveInfo1), eq(1000L));
+
+ reset(testServicesCache);
+
+ testServicesCache.invalidateCache(U0);
+ testServicesCache.invalidateCache(U1);
+
+ // There is a bug to return the same info from the cache for different users. Make sure it
+ // will return the different info from the cache for different users.
+ Collection<RegisteredServicesCache.ServiceInfo<TestServiceType>> serviceInfos;
+ serviceInfos = testServicesCache.getAllServices(U0);
+ // Make sure the service info is retrieved from the cache for U0.
+ verify(testServicesCache, never()).parseServiceInfo(eq(mResolveInfo1), eq(1000L));
+ for (RegisteredServicesCache.ServiceInfo<TestServiceType> serviceInfo : serviceInfos) {
+ assertThat(serviceInfo.componentInfo.applicationInfo.uid).isEqualTo(UID1);
+ }
+
+ serviceInfos = testServicesCache.getAllServices(U1);
+ // Make sure the service info is retrieved from the cache for U1.
+ verify(testServicesCache, never()).parseServiceInfo(eq(mResolveInfo2), eq(2000L));
+ for (RegisteredServicesCache.ServiceInfo<TestServiceType> serviceInfo : serviceInfos) {
+ assertThat(serviceInfo.componentInfo.applicationInfo.uid).isEqualTo(u1uid);
+ }
+ }
+
+ @Test
+ public void testUpdateServiceInfoIntoCachesWhenPackageInfoNotFound() throws Exception {
+ PackageInfo packageInfo1 = createPackageInfo(1000L /* lastUpdateTime */);
+ when(mMockPackageManager.getPackageInfoAsUser(eq(mResolveInfo1.serviceInfo.packageName),
+ anyInt(), eq(U0))).thenReturn(packageInfo1);
+
+ TestRegisteredServicesCache testServicesCache = spy(
+ new TestRegisteredServicesCache(mMockInjector, null /* serializerAndParser */));
+ final RegisteredServicesCache.ServiceInfo<TestServiceType> serviceInfo1 = newServiceInfo(
+ mTestServiceType1, UID1, mResolveInfo1.serviceInfo.getComponentName(),
+ 1000L /* lastUpdateTime */);
+ testServicesCache.addServiceForQuerying(U0, mResolveInfo1, serviceInfo1);
+
+ testServicesCache.getAllServices(U0);
+ verify(testServicesCache, times(1)).parseServiceInfo(eq(mResolveInfo1), eq(1000L));
+
+ reset(testServicesCache);
+ reset(mMockPackageManager);
+
+ doThrow(new SecurityException("")).when(mMockPackageManager).getPackageInfoAsUser(
+ eq(mResolveInfo1.serviceInfo.packageName), anyInt(), eq(U0));
+
+ testServicesCache.invalidateCache(U0);
+ testServicesCache.getAllServices(U0);
+ verify(testServicesCache, times(1)).parseServiceInfo(eq(mResolveInfo1), anyLong());
+ }
+
+ @Test
+ public void testUpdateServiceInfoIntoCachesWhenTheApplicationHasBeenUpdated() throws Exception {
+ PackageInfo packageInfo1 = createPackageInfo(1000L /* lastUpdateTime */);
+ when(mMockPackageManager.getPackageInfoAsUser(eq(mResolveInfo1.serviceInfo.packageName),
+ anyInt(), eq(U0))).thenReturn(packageInfo1);
+
+ TestRegisteredServicesCache testServicesCache = spy(
+ new TestRegisteredServicesCache(mMockInjector, null /* serializerAndParser */));
+ final RegisteredServicesCache.ServiceInfo<TestServiceType> serviceInfo1 = newServiceInfo(
+ mTestServiceType1, UID1, mResolveInfo1.serviceInfo.getComponentName(),
+ 1000L /* lastUpdateTime */);
+ testServicesCache.addServiceForQuerying(U0, mResolveInfo1, serviceInfo1);
+
+ testServicesCache.getAllServices(U0);
+ verify(testServicesCache, times(1)).parseServiceInfo(eq(mResolveInfo1), eq(1000L));
+
+ reset(testServicesCache);
+ reset(mMockPackageManager);
+
+ PackageInfo packageInfo2 = createPackageInfo(2000L /* lastUpdateTime */);
+ when(mMockPackageManager.getPackageInfoAsUser(eq(mResolveInfo1.serviceInfo.packageName),
+ anyInt(), eq(U0))).thenReturn(packageInfo2);
+
+ testServicesCache.invalidateCache(U0);
+ testServicesCache.getAllServices(U0);
+ verify(testServicesCache, times(1)).parseServiceInfo(eq(mResolveInfo1), eq(2000L));
+ }
+
+ @Test
+ public void testClearServiceInfoCachesAfterTimeout() throws Exception {
+ PackageInfo packageInfo1 = createPackageInfo(1000L /* lastUpdateTime */);
+ when(mMockPackageManager.getPackageInfoAsUser(eq(mResolveInfo1.serviceInfo.packageName),
+ anyInt(), eq(U0))).thenReturn(packageInfo1);
+
+ TestRegisteredServicesCache testServicesCache = spy(
+ new TestRegisteredServicesCache(mMockInjector, null /* serializerAndParser */));
+ final RegisteredServicesCache.ServiceInfo<TestServiceType> serviceInfo1 = newServiceInfo(
+ mTestServiceType1, UID1, mResolveInfo1.serviceInfo.getComponentName(),
+ 1000L /* lastUpdateTime */);
+ testServicesCache.addServiceForQuerying(U0, mResolveInfo1, serviceInfo1);
+
+ // Immediately invoke run on the Runnable posted to the handler
+ doAnswer(invocation -> {
+ Message message = invocation.getArgument(0);
+ message.getCallback().run();
+ return true;
+ }).when(mMockBackgroundHandler).sendMessageAtTime(any(Message.class), anyLong());
+
+ testServicesCache.getAllServices(U0);
+ verify(testServicesCache, times(1)).parseServiceInfo(eq(mResolveInfo1), eq(1000L));
+ verify(mMockBackgroundHandler, times(1)).sendMessageAtTime(any(Message.class), anyLong());
+
+ reset(testServicesCache);
+
+ testServicesCache.invalidateCache(U0);
+ testServicesCache.getAllServices(U0);
+ verify(testServicesCache, times(1)).parseServiceInfo(eq(mResolveInfo1), eq(1000L));
+ }
+
+ private static RegisteredServicesCache.ServiceInfo<TestServiceType> newServiceInfo(
+ TestServiceType type, int uid, ComponentName componentName, long lastUpdateTime) {
+ final ComponentInfo info = new ComponentInfo();
+ info.applicationInfo = new ApplicationInfo();
+ info.applicationInfo.uid = uid;
+ return new RegisteredServicesCache.ServiceInfo<>(type, info, componentName, lastUpdateTime);
+ }
+
+ private void addServiceInfoIntoResolveInfo(ResolveInfo resolveInfo, String packageName,
+ String serviceName) {
+ final ServiceInfo serviceInfo = new ServiceInfo();
+ serviceInfo.packageName = packageName;
+ serviceInfo.name = serviceName;
+ resolveInfo.serviceInfo = serviceInfo;
+ }
+
+ private PackageInfo createPackageInfo(long lastUpdateTime) {
+ PackageInfo packageInfo = new PackageInfo();
+ packageInfo.lastUpdateTime = lastUpdateTime;
+ return packageInfo;
+ }
+
+ /**
+ * Mock implementation of {@link android.content.pm.RegisteredServicesCache} for testing
+ */
+ public class TestRegisteredServicesCache extends RegisteredServicesCache<TestServiceType> {
+ static final String SERVICE_INTERFACE = "RegisteredServicesCacheUnitTest";
+ static final String SERVICE_META_DATA = "RegisteredServicesCacheUnitTest";
+ static final String ATTRIBUTES_NAME = "test";
+ private SparseArray<Map<ResolveInfo, ServiceInfo<TestServiceType>>> mServices =
+ new SparseArray<>();
+
+ public TestRegisteredServicesCache(Injector<TestServiceType> injector,
+ XmlSerializerAndParser<TestServiceType> serializerAndParser) {
+ super(injector, SERVICE_INTERFACE, SERVICE_META_DATA, ATTRIBUTES_NAME,
+ serializerAndParser);
+ }
+
+ @Override
+ public TestServiceType parseServiceAttributes(Resources res, String packageName,
+ AttributeSet attrs) {
+ return null;
+ }
+
+ @Override
+ protected List<ResolveInfo> queryIntentServices(int userId) {
+ Map<ResolveInfo, ServiceInfo<TestServiceType>> map = mServices.get(userId,
+ new HashMap<ResolveInfo, ServiceInfo<TestServiceType>>());
+ return new ArrayList<>(map.keySet());
+ }
+
+ void addServiceForQuerying(int userId, ResolveInfo resolveInfo,
+ ServiceInfo<TestServiceType> serviceInfo) {
+ Map<ResolveInfo, ServiceInfo<TestServiceType>> map = mServices.get(userId);
+ if (map == null) {
+ map = new HashMap<>();
+ mServices.put(userId, map);
+ }
+ map.put(resolveInfo, serviceInfo);
+ }
+
+ void clearServicesForQuerying() {
+ mServices.clear();
+ }
+
+ @Override
+ protected ServiceInfo<TestServiceType> parseServiceInfo(ResolveInfo resolveInfo,
+ long lastUpdateTime) throws XmlPullParserException, IOException {
+ int size = mServices.size();
+ for (int i = 0; i < size; i++) {
+ Map<ResolveInfo, ServiceInfo<TestServiceType>> map = mServices.valueAt(i);
+ ServiceInfo<TestServiceType> serviceInfo = map.get(resolveInfo);
+ if (serviceInfo != null) {
+ return serviceInfo;
+ }
+ }
+ throw new IllegalArgumentException("Unexpected service " + resolveInfo);
+ }
+
+ @Override
+ public void onUserRemoved(int userId) {
+ super.onUserRemoved(userId);
+ }
+ }
+}
diff --git a/core/tests/coretests/src/android/hardware/display/DisplayManagerGlobalTest.java b/core/tests/coretests/src/android/hardware/display/DisplayManagerGlobalTest.java
index de5f0ffbe23f..34650be331d3 100644
--- a/core/tests/coretests/src/android/hardware/display/DisplayManagerGlobalTest.java
+++ b/core/tests/coretests/src/android/hardware/display/DisplayManagerGlobalTest.java
@@ -264,7 +264,7 @@ public class DisplayManagerGlobalTest {
/* isEventFilterExplicit */ true);
callback.onDisplayEvent(displayId, DisplayManagerGlobal.EVENT_DISPLAY_ADDED);
waitForHandler();
- Mockito.verifyZeroInteractions(mDisplayListener);
+ Mockito.verifyNoMoreInteractions(mDisplayListener);
mDisplayManagerGlobal.registerDisplayListener(mDisplayListener, mHandler,
ALL_DISPLAY_EVENTS
@@ -272,7 +272,7 @@ public class DisplayManagerGlobalTest {
/* isEventFilterExplicit */ true);
callback.onDisplayEvent(displayId, DisplayManagerGlobal.EVENT_DISPLAY_BASIC_CHANGED);
waitForHandler();
- Mockito.verifyZeroInteractions(mDisplayListener);
+ Mockito.verifyNoMoreInteractions(mDisplayListener);
mDisplayManagerGlobal.registerDisplayListener(mDisplayListener, mHandler,
ALL_DISPLAY_EVENTS
@@ -280,7 +280,7 @@ public class DisplayManagerGlobalTest {
/* isEventFilterExplicit */ true);
callback.onDisplayEvent(displayId, DisplayManagerGlobal.EVENT_DISPLAY_REMOVED);
waitForHandler();
- Mockito.verifyZeroInteractions(mDisplayListener);
+ Mockito.verifyNoMoreInteractions(mDisplayListener);
}
@Test
diff --git a/core/tests/coretests/src/android/os/ParcelTest.java b/core/tests/coretests/src/android/os/ParcelTest.java
index 3e6520106ab0..bb059108d4b6 100644
--- a/core/tests/coretests/src/android/os/ParcelTest.java
+++ b/core/tests/coretests/src/android/os/ParcelTest.java
@@ -29,6 +29,8 @@ import android.util.Log;
import androidx.test.ext.junit.runners.AndroidJUnit4;
+import java.nio.BufferOverflowException;
+import java.nio.ByteBuffer;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -416,4 +418,63 @@ public class ParcelTest {
int binderEndPos = pA.dataPosition();
assertTrue(pA.hasBinders(binderStartPos, binderEndPos - binderStartPos));
}
+
+ private static final byte[] TEST_DATA = new byte[] {4, 8, 15, 16, 23, 42};
+
+ // Allow for some Parcel overhead
+ private static final int TEST_DATA_LENGTH = TEST_DATA.length + 100;
+
+ @Test
+ public void testMarshall_ByteBuffer_wrapped() {
+ ByteBuffer bb = ByteBuffer.allocate(TEST_DATA_LENGTH);
+ testMarshall_ByteBuffer(bb);
+ }
+
+ @Test
+ public void testMarshall_DirectByteBuffer() {
+ ByteBuffer bb = ByteBuffer.allocateDirect(TEST_DATA_LENGTH);
+ testMarshall_ByteBuffer(bb);
+ }
+
+ private void testMarshall_ByteBuffer(ByteBuffer bb) {
+ // Ensure that Parcel respects the starting offset by not starting at 0
+ bb.position(1);
+ bb.mark();
+
+ // Parcel test data, then marshall into the ByteBuffer
+ Parcel p1 = Parcel.obtain();
+ p1.writeByteArray(TEST_DATA);
+ p1.marshall(bb);
+ p1.recycle();
+
+ assertTrue(bb.position() > 1);
+ bb.reset();
+
+ // Unmarshall test data into a new Parcel
+ Parcel p2 = Parcel.obtain();
+ bb.reset();
+ p2.unmarshall(bb);
+ assertTrue(bb.position() > 1);
+ p2.setDataPosition(0);
+ byte[] marshalled = p2.marshall();
+
+ bb.reset();
+ for (int i = 0; i < TEST_DATA.length; i++) {
+ assertEquals(bb.get(), marshalled[i]);
+ }
+
+ byte[] testDataCopy = new byte[TEST_DATA.length];
+ p2.setDataPosition(0);
+ p2.readByteArray(testDataCopy);
+ for (int i = 0; i < TEST_DATA.length; i++) {
+ assertEquals(TEST_DATA[i], testDataCopy[i]);
+ }
+
+ // Test that overflowing the buffer throws an exception
+ bb.reset();
+ // Leave certainly not enough room for the test data
+ bb.limit(bb.position() + TEST_DATA.length - 1);
+ assertThrows(BufferOverflowException.class, () -> p2.marshall(bb));
+ p2.recycle();
+ }
}
diff --git a/core/tests/coretests/src/android/view/PendingInsetsControllerTest.java b/core/tests/coretests/src/android/view/PendingInsetsControllerTest.java
index 8ac9292390b0..50cd4c00c2ce 100644
--- a/core/tests/coretests/src/android/view/PendingInsetsControllerTest.java
+++ b/core/tests/coretests/src/android/view/PendingInsetsControllerTest.java
@@ -29,7 +29,7 @@ import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.verifyZeroInteractions;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
import static org.mockito.Mockito.when;
import android.os.CancellationSignal;
@@ -230,7 +230,7 @@ public class PendingInsetsControllerTest {
InsetsController secondController = mock(InsetsController.class);
mPendingInsetsController.replayAndAttach(secondController);
verify(mReplayedController).show(eq(systemBars()));
- verifyZeroInteractions(secondController);
+ verifyNoMoreInteractions(secondController);
}
@Test
diff --git a/core/tests/coretests/src/android/view/accessibility/AccessibilityInteractionClientTest.java b/core/tests/coretests/src/android/view/accessibility/AccessibilityInteractionClientTest.java
index eb482f2e0aa5..f811d8efedeb 100644
--- a/core/tests/coretests/src/android/view/accessibility/AccessibilityInteractionClientTest.java
+++ b/core/tests/coretests/src/android/view/accessibility/AccessibilityInteractionClientTest.java
@@ -20,7 +20,7 @@ import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
-import static org.mockito.Mockito.verifyZeroInteractions;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
import static org.mockito.MockitoAnnotations.initMocks;
import android.os.Bundle;
@@ -74,7 +74,7 @@ public class AccessibilityInteractionClientTest {
MOCK_CONNECTION_ID, windowId, accessibilityNodeId, true, 0, null);
assertEquals("Node got lost along the way", nodeFromConnection, node);
- verifyZeroInteractions(mMockCache);
+ verifyNoMoreInteractions(mMockCache);
}
@Test
diff --git a/core/tests/coretests/src/android/view/contentcapture/MainContentCaptureSessionTest.java b/core/tests/coretests/src/android/view/contentcapture/MainContentCaptureSessionTest.java
index 5f89f9c14793..8bbe81da512b 100644
--- a/core/tests/coretests/src/android/view/contentcapture/MainContentCaptureSessionTest.java
+++ b/core/tests/coretests/src/android/view/contentcapture/MainContentCaptureSessionTest.java
@@ -27,7 +27,7 @@ import static org.mockito.Mockito.anyInt;
import static org.mockito.Mockito.eq;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.verifyZeroInteractions;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
import android.content.ComponentName;
import android.content.ContentCaptureOptions;
@@ -130,7 +130,7 @@ public class MainContentCaptureSessionTest {
mTestableLooper.processAllMessages();
assertThat(session.mContentProtectionEventProcessor).isNull();
- verifyZeroInteractions(mMockContentProtectionEventProcessor);
+ verifyNoMoreInteractions(mMockContentProtectionEventProcessor);
}
@Test
@@ -151,7 +151,7 @@ public class MainContentCaptureSessionTest {
mTestableLooper.processAllMessages();
assertThat(session.mContentProtectionEventProcessor).isNull();
- verifyZeroInteractions(mMockContentProtectionEventProcessor);
+ verifyNoMoreInteractions(mMockContentProtectionEventProcessor);
}
@Test
@@ -172,7 +172,7 @@ public class MainContentCaptureSessionTest {
mTestableLooper.processAllMessages();
assertThat(session.mContentProtectionEventProcessor).isNull();
- verifyZeroInteractions(mMockContentProtectionEventProcessor);
+ verifyNoMoreInteractions(mMockContentProtectionEventProcessor);
}
@Test
@@ -197,7 +197,7 @@ public class MainContentCaptureSessionTest {
session.sendEvent(EVENT);
mTestableLooper.processAllMessages();
- verifyZeroInteractions(mMockContentProtectionEventProcessor);
+ verifyNoMoreInteractions(mMockContentProtectionEventProcessor);
assertThat(session.mEvents).isNull();
}
@@ -227,7 +227,7 @@ public class MainContentCaptureSessionTest {
session.sendEvent(EVENT);
mTestableLooper.processAllMessages();
- verifyZeroInteractions(mMockContentProtectionEventProcessor);
+ verifyNoMoreInteractions(mMockContentProtectionEventProcessor);
assertThat(session.mEvents).isNotNull();
assertThat(session.mEvents).containsExactly(EVENT);
}
@@ -255,7 +255,7 @@ public class MainContentCaptureSessionTest {
session.sendEvent(EVENT);
mTestableLooper.processAllMessages();
- verifyZeroInteractions(mMockContentProtectionEventProcessor);
+ verifyNoMoreInteractions(mMockContentProtectionEventProcessor);
assertThat(session.mEvents).isNull();
}
@@ -272,8 +272,8 @@ public class MainContentCaptureSessionTest {
session.flush(REASON);
mTestableLooper.processAllMessages();
- verifyZeroInteractions(mMockContentProtectionEventProcessor);
- verifyZeroInteractions(mMockContentCaptureDirectManager);
+ verifyNoMoreInteractions(mMockContentProtectionEventProcessor);
+ verifyNoMoreInteractions(mMockContentCaptureDirectManager);
assertThat(session.mEvents).containsExactly(EVENT);
}
@@ -289,8 +289,8 @@ public class MainContentCaptureSessionTest {
session.flush(REASON);
mTestableLooper.processAllMessages();
- verifyZeroInteractions(mMockContentProtectionEventProcessor);
- verifyZeroInteractions(mMockContentCaptureDirectManager);
+ verifyNoMoreInteractions(mMockContentProtectionEventProcessor);
+ verifyNoMoreInteractions(mMockContentCaptureDirectManager);
assertThat(session.mEvents).containsExactly(EVENT);
}
@@ -307,7 +307,7 @@ public class MainContentCaptureSessionTest {
session.flush(REASON);
mTestableLooper.processAllMessages();
- verifyZeroInteractions(mMockContentProtectionEventProcessor);
+ verifyNoMoreInteractions(mMockContentProtectionEventProcessor);
assertThat(session.mEvents).isEmpty();
assertEventFlushedContentCapture(options);
}
@@ -325,7 +325,7 @@ public class MainContentCaptureSessionTest {
session.flush(REASON);
mTestableLooper.processAllMessages();
- verifyZeroInteractions(mMockContentProtectionEventProcessor);
+ verifyNoMoreInteractions(mMockContentProtectionEventProcessor);
assertThat(session.mEvents).isEmpty();
assertEventFlushedContentCapture(options);
}
@@ -339,7 +339,7 @@ public class MainContentCaptureSessionTest {
mTestableLooper.processAllMessages();
verify(mMockSystemServerInterface).finishSession(anyInt());
- verifyZeroInteractions(mMockContentProtectionEventProcessor);
+ verifyNoMoreInteractions(mMockContentProtectionEventProcessor);
assertThat(session.mDirectServiceInterface).isNull();
assertThat(session.mContentProtectionEventProcessor).isNull();
}
@@ -352,8 +352,8 @@ public class MainContentCaptureSessionTest {
session.resetSession(/* newState= */ 0);
mTestableLooper.processAllMessages();
- verifyZeroInteractions(mMockSystemServerInterface);
- verifyZeroInteractions(mMockContentProtectionEventProcessor);
+ verifyNoMoreInteractions(mMockSystemServerInterface);
+ verifyNoMoreInteractions(mMockContentProtectionEventProcessor);
assertThat(session.mDirectServiceInterface).isNull();
assertThat(session.mContentProtectionEventProcessor).isNull();
}
@@ -370,8 +370,8 @@ public class MainContentCaptureSessionTest {
notifyContentCaptureEvents(session);
mTestableLooper.processAllMessages();
- verifyZeroInteractions(mMockContentCaptureDirectManager);
- verifyZeroInteractions(mMockContentProtectionEventProcessor);
+ verifyNoMoreInteractions(mMockContentCaptureDirectManager);
+ verifyNoMoreInteractions(mMockContentProtectionEventProcessor);
assertThat(session.mEvents).isNull();
}
@@ -388,8 +388,8 @@ public class MainContentCaptureSessionTest {
notifyContentCaptureEvents(session);
mTestableLooper.processAllMessages();
- verifyZeroInteractions(mMockContentCaptureDirectManager);
- verifyZeroInteractions(mMockContentProtectionEventProcessor);
+ verifyNoMoreInteractions(mMockContentCaptureDirectManager);
+ verifyNoMoreInteractions(mMockContentProtectionEventProcessor);
assertThat(session.mEvents).isNull();
}
@@ -407,8 +407,8 @@ public class MainContentCaptureSessionTest {
notifyContentCaptureEvents(session);
mTestableLooper.processAllMessages();
- verifyZeroInteractions(mMockContentCaptureDirectManager);
- verifyZeroInteractions(mMockContentProtectionEventProcessor);
+ verifyNoMoreInteractions(mMockContentCaptureDirectManager);
+ verifyNoMoreInteractions(mMockContentProtectionEventProcessor);
assertThat(session.mEvents).isNull();
}
diff --git a/core/tests/coretests/src/android/view/contentprotection/ContentProtectionEventProcessorTest.java b/core/tests/coretests/src/android/view/contentprotection/ContentProtectionEventProcessorTest.java
index ba0dbf454ad2..e75452cafdab 100644
--- a/core/tests/coretests/src/android/view/contentprotection/ContentProtectionEventProcessorTest.java
+++ b/core/tests/coretests/src/android/view/contentprotection/ContentProtectionEventProcessorTest.java
@@ -26,7 +26,6 @@ import static org.mockito.Mockito.never;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoMoreInteractions;
-import static org.mockito.Mockito.verifyZeroInteractions;
import static org.mockito.Mockito.when;
import android.annotation.NonNull;
@@ -443,7 +442,7 @@ public class ContentProtectionEventProcessorTest {
mTestLooper.dispatchAll();
verify(mMockEventBuffer, never()).clear();
verify(mMockEventBuffer, never()).toArray();
- verifyZeroInteractions(mMockContentCaptureManager);
+ verifyNoMoreInteractions(mMockContentCaptureManager);
}
private void assertLoginDetected() throws Exception {
diff --git a/core/tests/coretests/src/android/widget/TextViewReceiveContentTest.java b/core/tests/coretests/src/android/widget/TextViewReceiveContentTest.java
index b61d86819c17..3570c2e0ace0 100644
--- a/core/tests/coretests/src/android/widget/TextViewReceiveContentTest.java
+++ b/core/tests/coretests/src/android/widget/TextViewReceiveContentTest.java
@@ -33,7 +33,6 @@ import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoMoreInteractions;
-import static org.mockito.Mockito.verifyZeroInteractions;
import android.app.Activity;
import android.app.Instrumentation;
@@ -159,7 +158,7 @@ public class TextViewReceiveContentTest {
ContentInfo payload =
new ContentInfo.Builder(clip, SOURCE_AUTOFILL).build();
mDefaultReceiver.onReceiveContent(mEditText, payload);
- verifyZeroInteractions(ic.mMock);
+ verifyNoMoreInteractions(ic.mMock);
}
@Test
@@ -180,19 +179,19 @@ public class TextViewReceiveContentTest {
ContentInfo payload =
new ContentInfo.Builder(clip, SOURCE_CLIPBOARD).build();
mDefaultReceiver.onReceiveContent(mEditText, payload);
- verifyZeroInteractions(ic.mMock);
+ verifyNoMoreInteractions(ic.mMock);
payload = new ContentInfo.Builder(clip, SOURCE_INPUT_METHOD).build();
mDefaultReceiver.onReceiveContent(mEditText, payload);
- verifyZeroInteractions(ic.mMock);
+ verifyNoMoreInteractions(ic.mMock);
payload = new ContentInfo.Builder(clip, SOURCE_DRAG_AND_DROP).build();
mDefaultReceiver.onReceiveContent(mEditText, payload);
- verifyZeroInteractions(ic.mMock);
+ verifyNoMoreInteractions(ic.mMock);
payload = new ContentInfo.Builder(clip, SOURCE_PROCESS_TEXT).build();
mDefaultReceiver.onReceiveContent(mEditText, payload);
- verifyZeroInteractions(ic.mMock);
+ verifyNoMoreInteractions(ic.mMock);
}
private static class MyInputConnection extends InputConnectionWrapper {
diff --git a/core/tests/coretests/src/android/window/BackTouchTrackerTest.kt b/core/tests/coretests/src/android/window/BackTouchTrackerTest.kt
index 381b566018c7..ad68e385459e 100644
--- a/core/tests/coretests/src/android/window/BackTouchTrackerTest.kt
+++ b/core/tests/coretests/src/android/window/BackTouchTrackerTest.kt
@@ -37,7 +37,7 @@ class BackTouchTrackerTest {
fun generatesProgress_onStart() {
val linearTracker = linearTouchTracker()
linearTracker.setGestureStartLocation(INITIAL_X_LEFT_EDGE, 0f, BackEvent.EDGE_LEFT)
- val event = linearTracker.createStartEvent(null)
+ val event = linearTracker.createStartEvent()
assertEquals(0f, event.progress, 0f)
}
diff --git a/core/tests/coretests/src/android/window/WindowOnBackInvokedDispatcherTest.java b/core/tests/coretests/src/android/window/WindowOnBackInvokedDispatcherTest.java
index 215c1623a530..66524d1c1d2a 100644
--- a/core/tests/coretests/src/android/window/WindowOnBackInvokedDispatcherTest.java
+++ b/core/tests/coretests/src/android/window/WindowOnBackInvokedDispatcherTest.java
@@ -695,8 +695,7 @@ public class WindowOnBackInvokedDispatcherTest {
/* frameTimeMillis = */ 0,
/* progress = */ progress,
/* triggerBack = */ false,
- /* swipeEdge = */ BackEvent.EDGE_LEFT,
- /* departingAnimationTarget = */ null);
+ /* swipeEdge = */ BackEvent.EDGE_LEFT);
}
private void verifyImeCallackRegistrations() throws RemoteException {
diff --git a/core/tests/coretests/src/com/android/internal/accessibility/AccessibilityShortcutControllerTest.java b/core/tests/coretests/src/com/android/internal/accessibility/AccessibilityShortcutControllerTest.java
index 890074552284..1977ff52c7c5 100644
--- a/core/tests/coretests/src/com/android/internal/accessibility/AccessibilityShortcutControllerTest.java
+++ b/core/tests/coretests/src/com/android/internal/accessibility/AccessibilityShortcutControllerTest.java
@@ -44,7 +44,7 @@ import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.verifyZeroInteractions;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
import static org.mockito.Mockito.when;
import android.accessibilityservice.AccessibilityServiceInfo;
@@ -522,7 +522,7 @@ public class AccessibilityShortcutControllerTest {
AccessibilityShortcutController.DialogStatus.SHOWN);
getController().performAccessibilityShortcut();
- verifyZeroInteractions(mAlertDialogBuilder, mAlertDialog);
+ verifyNoMoreInteractions(mAlertDialogBuilder, mAlertDialog);
verify(mToast).show();
verify(mAccessibilityManagerService).performAccessibilityShortcut(
Display.DEFAULT_DISPLAY, HARDWARE, null);
@@ -615,7 +615,7 @@ public class AccessibilityShortcutControllerTest {
AccessibilityShortcutController.DialogStatus.SHOWN);
getController().performAccessibilityShortcut();
- verifyZeroInteractions(mToast);
+ verifyNoMoreInteractions(mToast);
verify(mAccessibilityManagerService).performAccessibilityShortcut(
Display.DEFAULT_DISPLAY, HARDWARE, null);
}
@@ -632,7 +632,7 @@ public class AccessibilityShortcutControllerTest {
AccessibilityShortcutController.DialogStatus.SHOWN);
getController().performAccessibilityShortcut();
- verifyZeroInteractions(mToast);
+ verifyNoMoreInteractions(mToast);
verify(mAccessibilityManagerService).performAccessibilityShortcut(
Display.DEFAULT_DISPLAY, HARDWARE, null);
}
diff --git a/core/tests/devicestatetests/src/android/hardware/devicestate/DeviceStateManagerGlobalTest.java b/core/tests/devicestatetests/src/android/hardware/devicestate/DeviceStateManagerGlobalTest.java
index 17fe15c94294..21ef391ee9de 100644
--- a/core/tests/devicestatetests/src/android/hardware/devicestate/DeviceStateManagerGlobalTest.java
+++ b/core/tests/devicestatetests/src/android/hardware/devicestate/DeviceStateManagerGlobalTest.java
@@ -28,7 +28,7 @@ import static org.mockito.Mockito.never;
import static org.mockito.Mockito.reset;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.verifyZeroInteractions;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
import android.annotation.EnforcePermission;
import android.hardware.devicestate.DeviceStateManager.DeviceStateCallback;
@@ -207,7 +207,7 @@ public final class DeviceStateManagerGlobalTest {
mService.setSupportedStates(List.of(OTHER_DEVICE_STATE));
mService.setBaseState(OTHER_DEVICE_STATE);
- verifyZeroInteractions(callback);
+ verifyNoMoreInteractions(callback);
}
@Test
diff --git a/libs/WindowManager/Shell/res/layout/desktop_mode_window_decor_handle_menu.xml b/libs/WindowManager/Shell/res/layout/desktop_mode_window_decor_handle_menu.xml
index e11babe5cb0e..ed8b54397076 100644
--- a/libs/WindowManager/Shell/res/layout/desktop_mode_window_decor_handle_menu.xml
+++ b/libs/WindowManager/Shell/res/layout/desktop_mode_window_decor_handle_menu.xml
@@ -22,8 +22,8 @@
android:layout_height="wrap_content"
android:clipChildren="false"
android:clipToPadding="false"
- android:paddingBottom="@dimen/desktop_mode_handle_menu_pill_elevation"
- android:paddingEnd="@dimen/desktop_mode_handle_menu_pill_elevation"
+ android:paddingBottom="@dimen/desktop_mode_handle_menu_pill_elevation_padding"
+ android:paddingEnd="@dimen/desktop_mode_handle_menu_pill_elevation_padding"
android:orientation="vertical">
<LinearLayout
diff --git a/libs/WindowManager/Shell/res/values/dimen.xml b/libs/WindowManager/Shell/res/values/dimen.xml
index 9ebbf71138b0..2a8c88ed3a40 100644
--- a/libs/WindowManager/Shell/res/values/dimen.xml
+++ b/libs/WindowManager/Shell/res/values/dimen.xml
@@ -525,17 +525,21 @@
<!-- The radius of the Maximize menu shadow. -->
<dimen name="desktop_mode_maximize_menu_shadow_radius">8dp</dimen>
- <!-- The width of the handle menu in desktop mode. -->
- <dimen name="desktop_mode_handle_menu_width">216dp</dimen>
+ <!-- The width of the handle menu in desktop mode plus the 2dp added for padding to account for
+ pill elevation. -->
+ <dimen name="desktop_mode_handle_menu_width">218dp</dimen>
- <!-- The maximum height of the handle menu in desktop mode. Three pills at 52dp each,
- additional actions pill 208dp, plus 2dp spacing between them plus 4dp top padding.
- 52*3 + 52*4 + (4-1)*2 + 4 = 374 -->
- <dimen name="desktop_mode_handle_menu_height">374dp</dimen>
+ <!-- The maximum height of the handle menu in desktop mode. Three pills at 52dp each plus
+ additional actions pill 208dp plus 2dp spacing between them plus 4dp top padding
+ plus 2dp bottom padding: 52*3 + 52*4 + (4-1)*2 + 4 + 2 = 376 -->
+ <dimen name="desktop_mode_handle_menu_height">376dp</dimen>
<!-- The elevation set on the handle menu pills. -->
<dimen name="desktop_mode_handle_menu_pill_elevation">1dp</dimen>
+ <!-- The padding added to account for the handle menu's pills' elevation. -->
+ <dimen name="desktop_mode_handle_menu_pill_elevation_padding">2dp</dimen>
+
<!-- The height of the handle menu's "App Info" pill in desktop mode. -->
<dimen name="desktop_mode_handle_menu_app_info_pill_height">52dp</dimen>
diff --git a/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/desktopmode/DesktopModeCompatPolicy.kt b/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/desktopmode/DesktopModeCompatPolicy.kt
index b87c2054bea6..529203f7ded2 100644
--- a/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/desktopmode/DesktopModeCompatPolicy.kt
+++ b/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/desktopmode/DesktopModeCompatPolicy.kt
@@ -23,6 +23,7 @@ import android.content.pm.PackageManager
import android.window.DesktopModeFlags
import com.android.internal.R
import com.android.internal.policy.DesktopModeCompatUtils
+import java.util.function.Supplier
/**
* Class to decide whether to apply app compat policies in desktop mode.
@@ -34,9 +35,11 @@ class DesktopModeCompatPolicy(private val context: Context) {
private val pkgManager: PackageManager
get() = context.getPackageManager()
private val defaultHomePackage: String?
- get() = pkgManager.getHomeActivities(ArrayList())?.packageName
+ get() = defaultHomePackageSupplier?.get()
+ ?: pkgManager.getHomeActivities(ArrayList())?.packageName
private val packageInfoCache = mutableMapOf<String, Boolean>()
+ var defaultHomePackageSupplier: Supplier<String?>? = null
/**
* If the top activity should be exempt from desktop windowing and forced back to fullscreen.
@@ -46,15 +49,21 @@ class DesktopModeCompatPolicy(private val context: Context) {
*/
fun isTopActivityExemptFromDesktopWindowing(task: TaskInfo) =
isTopActivityExemptFromDesktopWindowing(task.baseActivity?.packageName,
- task.numActivities, task.isTopActivityNoDisplay, task.isActivityStackTransparent)
+ task.numActivities, task.isTopActivityNoDisplay, task.isActivityStackTransparent,
+ task.userId)
- fun isTopActivityExemptFromDesktopWindowing(packageName: String?,
- numActivities: Int, isTopActivityNoDisplay: Boolean, isActivityStackTransparent: Boolean) =
+ fun isTopActivityExemptFromDesktopWindowing(
+ packageName: String?,
+ numActivities: Int,
+ isTopActivityNoDisplay: Boolean,
+ isActivityStackTransparent: Boolean,
+ userId: Int
+ ) =
DesktopModeFlags.ENABLE_DESKTOP_WINDOWING_MODALS_POLICY.isTrue &&
((isSystemUiTask(packageName) ||
isPartOfDefaultHomePackageOrNoHomeAvailable(packageName) ||
(isTransparentTask(isActivityStackTransparent, numActivities) &&
- hasFullscreenTransparentPermission(packageName))) &&
+ hasFullscreenTransparentPermission(packageName, userId))) &&
!isTopActivityNoDisplay)
/** @see DesktopModeCompatUtils.shouldExcludeCaptionFromAppBounds */
@@ -77,16 +86,17 @@ class DesktopModeCompatPolicy(private val context: Context) {
private fun isSystemUiTask(packageName: String?) = packageName == systemUiPackage
// Checks if the app for the given package has the SYSTEM_ALERT_WINDOW permission.
- private fun hasFullscreenTransparentPermission(packageName: String?): Boolean {
+ private fun hasFullscreenTransparentPermission(packageName: String?, userId: Int): Boolean {
if (DesktopModeFlags.ENABLE_MODALS_FULLSCREEN_WITH_PERMISSIONS.isTrue) {
if (packageName == null) {
return false
}
- return packageInfoCache.getOrPut(packageName) {
+ return packageInfoCache.getOrPut("$userId@$packageName") {
try {
- val packageInfo = pkgManager.getPackageInfo(
+ val packageInfo = pkgManager.getPackageInfoAsUser(
packageName,
- PackageManager.GET_PERMISSIONS
+ PackageManager.GET_PERMISSIONS,
+ userId
)
packageInfo?.requestedPermissions?.contains(SYSTEM_ALERT_WINDOW) == true
} catch (e: PackageManager.NameNotFoundException) {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationController.java
index 53dede6bd227..7f8cfaeb9c03 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationController.java
@@ -448,7 +448,7 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont
final boolean shouldDispatchToAnimator = shouldDispatchToAnimator();
if (!shouldDispatchToAnimator && mActiveCallback != null) {
mCurrentTracker.updateStartLocation();
- tryDispatchOnBackStarted(mActiveCallback, mCurrentTracker.createStartEvent(null));
+ tryDispatchOnBackStarted(mActiveCallback, mCurrentTracker.createStartEvent());
if (mBackNavigationInfo != null && !isAppProgressGenerationAllowed()) {
tryPilferPointers();
}
@@ -604,7 +604,7 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont
mActiveCallback = mBackNavigationInfo.getOnBackInvokedCallback();
// App is handling back animation. Cancel system animation latency tracking.
cancelLatencyTracking();
- tryDispatchOnBackStarted(mActiveCallback, touchTracker.createStartEvent(null));
+ tryDispatchOnBackStarted(mActiveCallback, touchTracker.createStartEvent());
if (!isAppProgressGenerationAllowed()) {
tryPilferPointers();
}
@@ -1041,7 +1041,7 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont
() -> mShellExecutor.execute(this::onBackAnimationFinished));
if (mApps.length >= 1) {
- BackMotionEvent startEvent = mCurrentTracker.createStartEvent(mApps[0]);
+ BackMotionEvent startEvent = mCurrentTracker.createStartEvent();
dispatchOnBackStarted(mActiveCallback, startEvent);
if (startEvent.getSwipeEdge() == EDGE_NONE) {
// TODO(b/373544911): onBackStarted is dispatched here so that
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/SyncTransactionQueue.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/SyncTransactionQueue.java
index c4696d5f44f4..a8e6b593f20d 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/SyncTransactionQueue.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/SyncTransactionQueue.java
@@ -20,17 +20,14 @@ import static com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL;
import android.annotation.BinderThread;
import android.annotation.NonNull;
-import android.os.RemoteException;
import android.util.Slog;
import android.view.SurfaceControl;
-import android.view.WindowManager;
import android.window.WindowContainerTransaction;
import android.window.WindowContainerTransactionCallback;
import android.window.WindowOrganizer;
import com.android.internal.protolog.ProtoLog;
import com.android.wm.shell.shared.TransactionPool;
-import com.android.wm.shell.transition.LegacyTransitions;
import java.util.ArrayList;
@@ -87,25 +84,6 @@ public final class SyncTransactionQueue {
}
/**
- * Queues a legacy transition to be sent serially to WM
- */
- public void queue(LegacyTransitions.ILegacyTransition transition,
- @WindowManager.TransitionType int type, WindowContainerTransaction wct) {
- if (wct.isEmpty()) {
- if (DEBUG) Slog.d(TAG, "Skip queue due to transaction change is empty");
- return;
- }
- SyncCallback cb = new SyncCallback(transition, type, wct);
- synchronized (mQueue) {
- if (DEBUG) Slog.d(TAG, "Queueing up legacy transition " + wct);
- mQueue.add(cb);
- if (mQueue.size() == 1) {
- cb.send();
- }
- }
- }
-
- /**
* Queues a sync transaction only if there are already sync transaction(s) queued or in flight.
* Otherwise just returns without queueing.
* @return {@code true} if queued, {@code false} if not.
@@ -168,17 +146,9 @@ public final class SyncTransactionQueue {
private class SyncCallback extends WindowContainerTransactionCallback {
int mId = -1;
final WindowContainerTransaction mWCT;
- final LegacyTransitions.LegacyTransition mLegacyTransition;
SyncCallback(WindowContainerTransaction wct) {
mWCT = wct;
- mLegacyTransition = null;
- }
-
- SyncCallback(LegacyTransitions.ILegacyTransition legacyTransition,
- @WindowManager.TransitionType int type, WindowContainerTransaction wct) {
- mWCT = wct;
- mLegacyTransition = new LegacyTransitions.LegacyTransition(type, legacyTransition);
}
// Must be sychronized on mQueue
@@ -194,12 +164,7 @@ public final class SyncTransactionQueue {
}
if (DEBUG) Slog.d(TAG, "Sending sync transaction: " + mWCT);
try {
- if (mLegacyTransition != null) {
- mId = new WindowOrganizer().startLegacyTransition(mLegacyTransition.getType(),
- mLegacyTransition.getAdapter(), this, mWCT);
- } else {
- mId = new WindowOrganizer().applySyncTransaction(mWCT, this);
- }
+ mId = new WindowOrganizer().applySyncTransaction(mWCT, this);
} catch (RuntimeException e) {
Slog.e(TAG, "Send failed", e);
// Finish current sync callback immediately.
@@ -228,18 +193,10 @@ public final class SyncTransactionQueue {
if (DEBUG) Slog.d(TAG, "onTransactionReady id=" + mId);
mQueue.remove(this);
onTransactionReceived(t);
- if (mLegacyTransition != null) {
- try {
- mLegacyTransition.getSyncCallback().onTransactionReady(mId, t);
- } catch (RemoteException e) {
- Slog.e(TAG, "Error sending callback to legacy transition: " + mId, e);
- }
- } else {
- ProtoLog.v(WM_SHELL,
- "SyncTransactionQueue.onTransactionReady(): syncId=%d apply", id);
- t.apply();
- t.close();
- }
+ ProtoLog.v(WM_SHELL,
+ "SyncTransactionQueue.onTransactionReady(): syncId=%d apply", id);
+ t.apply();
+ t.close();
if (!mQueue.isEmpty()) {
mQueue.get(0).send();
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java
index 318cdeec5bc1..f62fd819319e 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java
@@ -95,6 +95,7 @@ import com.android.wm.shell.compatui.impl.DefaultComponentIdGenerator;
import com.android.wm.shell.desktopmode.DesktopMode;
import com.android.wm.shell.desktopmode.DesktopTasksController;
import com.android.wm.shell.desktopmode.DesktopUserRepositories;
+import com.android.wm.shell.desktopmode.common.DefaultHomePackageSupplier;
import com.android.wm.shell.desktopmode.desktopwallpaperactivity.DesktopWallpaperActivityTokenProvider;
import com.android.wm.shell.displayareahelper.DisplayAreaHelper;
import com.android.wm.shell.displayareahelper.DisplayAreaHelperController;
@@ -260,8 +261,14 @@ public abstract class WMShellBaseModule {
@WMSingleton
@Provides
- static DesktopModeCompatPolicy provideDesktopModeCompatPolicy(Context context) {
- return new DesktopModeCompatPolicy(context);
+ static DesktopModeCompatPolicy provideDesktopModeCompatPolicy(
+ Context context,
+ ShellInit shellInit,
+ @ShellMainThread Handler mainHandler) {
+ final DesktopModeCompatPolicy policy = new DesktopModeCompatPolicy(context);
+ policy.setDefaultHomePackageSupplier(new DefaultHomePackageSupplier(
+ context, shellInit, mainHandler));
+ return policy;
}
@WMSingleton
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 bc2ed3f35b45..257e27a338be 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
@@ -1474,6 +1474,7 @@ public abstract class WMShellModule {
ShellTaskOrganizer shellTaskOrganizer,
DesktopWallpaperActivityTokenProvider desktopWallpaperActivityTokenProvider,
InputManager inputManager,
+ DisplayController displayController,
@ShellMainThread Handler mainHandler
) {
if (!DesktopModeStatus.canEnterDesktopMode(context)) {
@@ -1488,6 +1489,7 @@ public abstract class WMShellModule {
shellTaskOrganizer,
desktopWallpaperActivityTokenProvider,
inputManager,
+ displayController,
mainHandler));
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopDisplayModeController.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopDisplayModeController.kt
index 904d86282c39..6dcc0deb1da1 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopDisplayModeController.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopDisplayModeController.kt
@@ -35,9 +35,11 @@ import com.android.internal.annotations.VisibleForTesting
import com.android.internal.protolog.ProtoLog
import com.android.wm.shell.RootTaskDisplayAreaOrganizer
import com.android.wm.shell.ShellTaskOrganizer
+import com.android.wm.shell.common.DisplayController
import com.android.wm.shell.desktopmode.desktopwallpaperactivity.DesktopWallpaperActivityTokenProvider
import com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_DESKTOP_MODE
import com.android.wm.shell.shared.annotations.ShellMainThread
+import com.android.wm.shell.shared.desktopmode.DesktopModeStatus
import com.android.wm.shell.transition.Transitions
/** Controls the display windowing mode in desktop mode */
@@ -49,6 +51,7 @@ class DesktopDisplayModeController(
private val shellTaskOrganizer: ShellTaskOrganizer,
private val desktopWallpaperActivityTokenProvider: DesktopWallpaperActivityTokenProvider,
private val inputManager: InputManager,
+ private val displayController: DisplayController,
@ShellMainThread private val mainHandler: Handler,
) {
@@ -128,14 +131,25 @@ class DesktopDisplayModeController(
return windowManager.getWindowingMode(DEFAULT_DISPLAY)
}
- // TODO: b/375319538 - Replace the check with a DisplayManager API once it's available.
- private fun isExtendedDisplayEnabled() =
- 0 !=
+ private fun isExtendedDisplayEnabled(): Boolean {
+ if (DesktopExperienceFlags.ENABLE_DISPLAY_CONTENT_MODE_MANAGEMENT.isTrue) {
+ return rootTaskDisplayAreaOrganizer
+ .getDisplayIds()
+ .filter { it != DEFAULT_DISPLAY }
+ .any { displayId ->
+ displayController.getDisplay(displayId)?.let { display ->
+ DesktopModeStatus.isDesktopModeSupportedOnDisplay(context, display)
+ } ?: false
+ }
+ }
+
+ return 0 !=
Settings.Global.getInt(
context.contentResolver,
DEVELOPMENT_FORCE_DESKTOP_MODE_ON_EXTERNAL_DISPLAYS,
0,
)
+ }
private fun hasExternalDisplay() =
rootTaskDisplayAreaOrganizer.getDisplayIds().any { it != DEFAULT_DISPLAY }
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 8636bc1f56c2..a4e9c52ac9d9 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
@@ -339,7 +339,7 @@ class DesktopRepository(
val affectedDisplays = mutableSetOf<Int>()
desktopData
.desksSequence()
- .filter { desk -> desk.displayId != excludedDeskId }
+ .filter { desk -> desk.deskId != excludedDeskId }
.forEach { desk ->
val removed = removeActiveTaskFromDesk(desk.deskId, taskId, notifyListeners = false)
if (removed) {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt
index b2df4978e702..5e9cd9016d92 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt
@@ -43,6 +43,7 @@ import android.os.IBinder
import android.os.SystemProperties
import android.os.UserHandle
import android.util.Slog
+import android.view.Display
import android.view.Display.DEFAULT_DISPLAY
import android.view.DragEvent
import android.view.MotionEvent
@@ -463,6 +464,10 @@ class DesktopTasksController(
/** Creates a new desk in the given display. */
fun createDesk(displayId: Int) {
+ if (displayId == Display.INVALID_DISPLAY) {
+ logW("createDesk attempt with invalid displayId", displayId)
+ return
+ }
if (DesktopExperienceFlags.ENABLE_MULTIPLE_DESKTOPS_BACKEND.isTrue) {
desksOrganizer.createDesk(displayId) { deskId ->
taskRepository.addDesk(displayId = displayId, deskId = deskId)
@@ -2943,6 +2948,11 @@ class DesktopTasksController(
removeDesk(displayId = displayId, deskId = deskId)
}
+ /** Removes all the available desks on all displays. */
+ fun removeAllDesks() {
+ taskRepository.getAllDeskIds().forEach { deskId -> removeDesk(deskId) }
+ }
+
private fun removeDesk(displayId: Int, deskId: Int) {
if (!DesktopModeFlags.ENABLE_DESKTOP_WINDOWING_BACK_NAVIGATION.isTrue()) return
logV("removeDesk deskId=%d from displayId=%d", deskId, displayId)
@@ -3709,6 +3719,18 @@ class DesktopTasksController(
}
}
+ override fun removeDesk(deskId: Int) {
+ executeRemoteCallWithTaskPermission(controller, "removeDesk") { c ->
+ c.removeDesk(deskId)
+ }
+ }
+
+ override fun removeAllDesks() {
+ executeRemoteCallWithTaskPermission(controller, "removeAllDesks") { c ->
+ c.removeAllDesks()
+ }
+ }
+
override fun activateDesk(deskId: Int, remoteTransition: RemoteTransition?) {
executeRemoteCallWithTaskPermission(controller, "activateDesk") { c ->
c.activateDesk(deskId, remoteTransition)
@@ -3772,8 +3794,8 @@ class DesktopTasksController(
}
}
- override fun removeDesktop(displayId: Int) {
- executeRemoteCallWithTaskPermission(controller, "removeDesktop") { c ->
+ override fun removeDefaultDeskInDisplay(displayId: Int) {
+ executeRemoteCallWithTaskPermission(controller, "removeDefaultDeskInDisplay") { c ->
c.removeDefaultDeskInDisplay(displayId)
}
}
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 7dabeb7c9d15..2ec6105e5af9 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
@@ -38,6 +38,8 @@ import com.android.wm.shell.desktopmode.DesktopModeTransitionTypes.isExitDesktop
import com.android.wm.shell.desktopmode.desktopwallpaperactivity.DesktopWallpaperActivityTokenProvider
import com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_DESKTOP_MODE
import com.android.wm.shell.shared.TransitionUtil
+import com.android.wm.shell.shared.TransitionUtil.isClosingMode
+import com.android.wm.shell.shared.TransitionUtil.isOpeningMode
import com.android.wm.shell.shared.desktopmode.DesktopModeStatus
import com.android.wm.shell.sysui.ShellInit
import com.android.wm.shell.transition.Transitions
@@ -345,18 +347,29 @@ class DesktopTasksTransitionObserver(
}
private fun updateTopTransparentFullscreenTaskId(info: TransitionInfo) {
- info.changes.forEach { change ->
- change.taskInfo?.let { task ->
- val desktopRepository = desktopUserRepositories.getProfile(task.userId)
- val displayId = task.displayId
- // Clear `topTransparentFullscreenTask` information from repository if task
- // is closed or sent to back.
- if (
- TransitionUtil.isClosingMode(change.mode) &&
- task.taskId ==
- desktopRepository.getTopTransparentFullscreenTaskId(displayId)
- ) {
- desktopRepository.clearTopTransparentFullscreenTaskId(displayId)
+ run forEachLoop@{
+ info.changes.forEach { change ->
+ change.taskInfo?.let { task ->
+ val desktopRepository = desktopUserRepositories.getProfile(task.userId)
+ val displayId = task.displayId
+ val transparentTaskId =
+ desktopRepository.getTopTransparentFullscreenTaskId(displayId)
+ if (transparentTaskId == null) return@forEachLoop
+ val changeMode = change.mode
+ val taskId = task.taskId
+ val isTopTransparentFullscreenTaskClosing =
+ taskId == transparentTaskId && isClosingMode(changeMode)
+ val isNonTopTransparentFullscreenTaskOpening =
+ taskId != transparentTaskId && isOpeningMode(changeMode)
+ // Clear `topTransparentFullscreenTask` information from repository if task
+ // is closed, sent to back or if a different task is opened, brought to front.
+ if (
+ isTopTransparentFullscreenTaskClosing ||
+ isNonTopTransparentFullscreenTaskOpening
+ ) {
+ desktopRepository.clearTopTransparentFullscreenTaskId(displayId)
+ return@forEachLoop
+ }
}
}
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/IDesktopMode.aidl b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/IDesktopMode.aidl
index 44f7e16e98c3..5f7fbd9843d4 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/IDesktopMode.aidl
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/IDesktopMode.aidl
@@ -35,6 +35,12 @@ interface IDesktopMode {
/** Activates the desk whose ID is `deskId` on whatever display it currently exists on. */
oneway void activateDesk(int deskId, in RemoteTransition remoteTransition);
+ /** Removes the desk with the given `deskId`. */
+ oneway void removeDesk(int deskId);
+
+ /** Removes all the available desks on all displays. */
+ oneway void removeAllDesks();
+
/** Show apps on the desktop on the given display */
void showDesktopApps(int displayId, in RemoteTransition remoteTransition);
@@ -64,8 +70,11 @@ interface IDesktopMode {
in @nullable RemoteTransition remoteTransition,
in @nullable IMoveToDesktopCallback callback);
- /** Remove desktop on the given display */
- oneway void removeDesktop(int displayId);
+ /**
+ * Removes the default desktop on the given display.
+ * @deprecated with multi-desks, we should use `removeDesk()`.
+ */
+ oneway void removeDefaultDeskInDisplay(int displayId);
/** Move a task with given `taskId` to external display */
void moveToExternalDisplay(int taskId);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/common/DefaultHomePackageSupplier.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/common/DefaultHomePackageSupplier.kt
new file mode 100644
index 000000000000..8ce624e103ef
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/common/DefaultHomePackageSupplier.kt
@@ -0,0 +1,65 @@
+/*
+ * 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.desktopmode.common
+
+import android.content.BroadcastReceiver
+import android.content.Context
+import android.content.Intent
+import android.content.IntentFilter
+import android.os.Handler
+import com.android.wm.shell.shared.annotations.ShellMainThread
+import com.android.wm.shell.sysui.ShellInit
+import java.util.function.Supplier
+
+/**
+ * This supplies the package name of default home in an efficient way. The query to package manager
+ * only executes on initialization and when the preferred activity (e.g. default home) is changed.
+ */
+class DefaultHomePackageSupplier(
+ private val context: Context,
+ shellInit: ShellInit,
+ @ShellMainThread private val mainHandler: Handler,
+) : BroadcastReceiver(), Supplier<String?> {
+
+ private var defaultHomePackage: String? = null
+
+ init {
+ shellInit.addInitCallback({ onInit() }, this)
+ }
+
+ private fun onInit() {
+ context.registerReceiver(
+ this,
+ IntentFilter(Intent.ACTION_PREFERRED_ACTIVITY_CHANGED),
+ null /* broadcastPermission */,
+ mainHandler,
+ )
+ }
+
+ private fun updateDefaultHomePackage(): String? {
+ defaultHomePackage = context.packageManager.getHomeActivities(ArrayList())?.packageName
+ return defaultHomePackage
+ }
+
+ override fun onReceive(contxt: Context?, intent: Intent?) {
+ updateDefaultHomePackage()
+ }
+
+ override fun get(): String? {
+ return defaultHomePackage ?: updateDefaultHomePackage()
+ }
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/freeform/FreeformTaskTransitionObserver.java b/libs/WindowManager/Shell/src/com/android/wm/shell/freeform/FreeformTaskTransitionObserver.java
index f89ba0a168d1..0bf2ea61b0a4 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/freeform/FreeformTaskTransitionObserver.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/freeform/FreeformTaskTransitionObserver.java
@@ -174,7 +174,7 @@ public class FreeformTaskTransitionObserver implements Transitions.TransitionObs
SurfaceControl.Transaction finishT) {
mTaskChangeListener.ifPresent(listener -> listener.onTaskChanging(change.getTaskInfo()));
mWindowDecorViewModel.onTaskChanging(
- change.getTaskInfo(), change.getLeash(), startT, finishT);
+ change.getTaskInfo(), change.getLeash(), startT, finishT, change.getMode());
}
private void onToFrontTransitionReady(
@@ -184,7 +184,7 @@ public class FreeformTaskTransitionObserver implements Transitions.TransitionObs
mTaskChangeListener.ifPresent(
listener -> listener.onTaskMovingToFront(change.getTaskInfo()));
mWindowDecorViewModel.onTaskChanging(
- change.getTaskInfo(), change.getLeash(), startT, finishT);
+ change.getTaskInfo(), change.getLeash(), startT, finishT, change.getMode());
}
private void onToBackTransitionReady(
@@ -194,7 +194,7 @@ public class FreeformTaskTransitionObserver implements Transitions.TransitionObs
mTaskChangeListener.ifPresent(
listener -> listener.onTaskMovingToBack(change.getTaskInfo()));
mWindowDecorViewModel.onTaskChanging(
- change.getTaskInfo(), change.getLeash(), startT, finishT);
+ change.getTaskInfo(), change.getLeash(), startT, finishT, change.getMode());
}
@Override
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 75c09829e551..a7cba76ea91f 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
@@ -123,6 +123,7 @@ import android.view.SurfaceControl;
import android.view.WindowManager;
import android.widget.Toast;
import android.window.DesktopExperienceFlags;
+import android.window.DesktopModeFlags;
import android.window.DisplayAreaInfo;
import android.window.RemoteTransition;
import android.window.TransitionInfo;
@@ -675,7 +676,8 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
if (!enteredSplitSelect) {
return null;
}
- if (!DesktopExperienceFlags.ENABLE_MULTIPLE_DESKTOPS_BACKEND.isTrue()) {
+ if (!DesktopExperienceFlags.ENABLE_MULTIPLE_DESKTOPS_BACKEND.isTrue()
+ && !DesktopModeFlags.ENABLE_INPUT_LAYER_TRANSITION_FIX.isTrue()) {
mTaskOrganizer.applyTransaction(wct);
return null;
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/LegacyTransitions.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/LegacyTransitions.java
deleted file mode 100644
index 978b8da2eb6d..000000000000
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/LegacyTransitions.java
+++ /dev/null
@@ -1,139 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.wm.shell.transition;
-
-import static com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_TRANSITIONS;
-
-import android.annotation.NonNull;
-import android.os.RemoteException;
-import android.view.IRemoteAnimationFinishedCallback;
-import android.view.IRemoteAnimationRunner;
-import android.view.RemoteAnimationAdapter;
-import android.view.RemoteAnimationTarget;
-import android.view.SurfaceControl;
-import android.view.WindowManager;
-import android.window.IWindowContainerTransactionCallback;
-
-import com.android.internal.protolog.ProtoLog;
-
-/**
- * Utilities and interfaces for transition-like usage on top of the legacy app-transition and
- * synctransaction tools.
- */
-public class LegacyTransitions {
-
- /**
- * Interface for a "legacy" transition. Effectively wraps a sync callback + remoteAnimation
- * into one callback.
- */
- public interface ILegacyTransition {
- /**
- * Called when both the associated sync transaction finishes and the remote animation is
- * ready.
- */
- void onAnimationStart(int transit, RemoteAnimationTarget[] apps,
- RemoteAnimationTarget[] wallpapers, RemoteAnimationTarget[] nonApps,
- IRemoteAnimationFinishedCallback finishedCallback, SurfaceControl.Transaction t);
- }
-
- /**
- * Makes sure that a remote animation and corresponding sync callback are called together
- * such that the sync callback is called first. This assumes that both the callback receiver
- * and the remoteanimation are in the same process so that order is preserved on both ends.
- */
- public static class LegacyTransition {
- private final ILegacyTransition mLegacyTransition;
- private int mSyncId = -1;
- private SurfaceControl.Transaction mTransaction;
- private int mTransit;
- private RemoteAnimationTarget[] mApps;
- private RemoteAnimationTarget[] mWallpapers;
- private RemoteAnimationTarget[] mNonApps;
- private IRemoteAnimationFinishedCallback mFinishCallback = null;
- private boolean mCancelled = false;
- private final SyncCallback mSyncCallback = new SyncCallback();
- private final RemoteAnimationAdapter mAdapter =
- new RemoteAnimationAdapter(new RemoteAnimationWrapper(), 0, 0);
-
- public LegacyTransition(@WindowManager.TransitionType int type,
- @NonNull ILegacyTransition legacyTransition) {
- mLegacyTransition = legacyTransition;
- mTransit = type;
- }
-
- public @WindowManager.TransitionType int getType() {
- return mTransit;
- }
-
- public IWindowContainerTransactionCallback getSyncCallback() {
- return mSyncCallback;
- }
-
- public RemoteAnimationAdapter getAdapter() {
- return mAdapter;
- }
-
- private class SyncCallback extends IWindowContainerTransactionCallback.Stub {
- @Override
- public void onTransactionReady(int id, SurfaceControl.Transaction t)
- throws RemoteException {
- ProtoLog.v(WM_SHELL_TRANSITIONS,
- "LegacyTransitions.onTransactionReady(): syncId=%d", id);
- mSyncId = id;
- mTransaction = t;
- checkApply(true /* log */);
- }
- }
-
- private class RemoteAnimationWrapper extends IRemoteAnimationRunner.Stub {
- @Override
- public void onAnimationStart(int transit, RemoteAnimationTarget[] apps,
- RemoteAnimationTarget[] wallpapers, RemoteAnimationTarget[] nonApps,
- IRemoteAnimationFinishedCallback finishedCallback) throws RemoteException {
- mTransit = transit;
- mApps = apps;
- mWallpapers = wallpapers;
- mNonApps = nonApps;
- mFinishCallback = finishedCallback;
- checkApply(false /* log */);
- }
-
- @Override
- public void onAnimationCancelled() throws RemoteException {
- mCancelled = true;
- mApps = mWallpapers = mNonApps = null;
- checkApply(false /* log */);
- }
- }
-
-
- private void checkApply(boolean log) throws RemoteException {
- if (mSyncId < 0 || (mFinishCallback == null && !mCancelled)) {
- if (log) {
- ProtoLog.v(WM_SHELL_TRANSITIONS, "\tSkipping hasFinishedCb=%b canceled=%b",
- mFinishCallback != null, mCancelled);
- }
- return;
- }
- if (log) {
- ProtoLog.v(WM_SHELL_TRANSITIONS, "\tapply");
- }
- mLegacyTransition.onAnimationStart(mTransit, mApps, mWallpapers,
- mNonApps, mFinishCallback, mTransaction);
- }
- }
-}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecorViewModel.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecorViewModel.java
index 7871179a50de..42321e56e72b 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecorViewModel.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecorViewModel.java
@@ -49,6 +49,7 @@ import android.view.SurfaceControl;
import android.view.View;
import android.view.ViewConfiguration;
import android.window.DisplayAreaInfo;
+import android.window.TransitionInfo;
import android.window.WindowContainerToken;
import android.window.WindowContainerTransaction;
@@ -233,7 +234,8 @@ public class CaptionWindowDecorViewModel implements WindowDecorViewModel, FocusT
RunningTaskInfo taskInfo,
SurfaceControl taskSurface,
SurfaceControl.Transaction startT,
- SurfaceControl.Transaction finishT) {
+ SurfaceControl.Transaction finishT,
+ @TransitionInfo.TransitionMode int changeMode) {
final CaptionWindowDecoration decoration = mWindowDecorByTaskId.get(taskInfo.taskId);
if (!shouldShowWindowDecor(taskInfo)) {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CarWindowDecorViewModel.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CarWindowDecorViewModel.java
index 2b2cdf84005c..4511fbe10764 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CarWindowDecorViewModel.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CarWindowDecorViewModel.java
@@ -31,6 +31,7 @@ import android.view.KeyCharacterMap;
import android.view.KeyEvent;
import android.view.SurfaceControl;
import android.view.View;
+import android.window.TransitionInfo;
import android.window.WindowContainerToken;
import android.window.WindowContainerTransaction;
@@ -159,7 +160,8 @@ public abstract class CarWindowDecorViewModel
RunningTaskInfo taskInfo,
SurfaceControl taskSurface,
SurfaceControl.Transaction startT,
- SurfaceControl.Transaction finishT) {
+ SurfaceControl.Transaction finishT,
+ @TransitionInfo.TransitionMode int changeMode) {
final CarWindowDecoration decoration = mWindowDecorByTaskId.get(taskInfo.taskId);
if (!shouldShowWindowDecor(taskInfo)) {
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 febb8850183d..f2ff39627362 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
@@ -27,6 +27,7 @@ import static android.view.MotionEvent.ACTION_HOVER_EXIT;
import static android.view.MotionEvent.ACTION_MOVE;
import static android.view.MotionEvent.ACTION_UP;
import static android.view.WindowInsets.Type.statusBars;
+import static android.view.WindowManager.TRANSIT_TO_BACK;
import static com.android.internal.jank.Cuj.CUJ_DESKTOP_MODE_ENTER_MODE_APP_HANDLE_MENU;
import static com.android.window.flags.Flags.enableDisplayFocusInShellTransitions;
@@ -79,6 +80,7 @@ import android.view.ViewConfiguration;
import android.view.ViewRootImpl;
import android.window.DesktopModeFlags;
import android.window.TaskSnapshot;
+import android.window.TransitionInfo;
import android.window.WindowContainerToken;
import android.window.WindowContainerTransaction;
@@ -150,18 +152,19 @@ import com.android.wm.shell.windowdecor.extension.InsetsStateKt;
import com.android.wm.shell.windowdecor.extension.TaskInfoKt;
import com.android.wm.shell.windowdecor.tiling.DesktopTilingDecorViewModel;
import com.android.wm.shell.windowdecor.tiling.SnapEventHandler;
+import com.android.wm.shell.windowdecor.viewholder.AppHandleViewHolder;
import com.android.wm.shell.windowdecor.viewholder.AppHeaderViewHolder;
import kotlin.Pair;
import kotlin.Unit;
import kotlin.jvm.functions.Function1;
-import org.jetbrains.annotations.NotNull;
-
import kotlinx.coroutines.CoroutineScope;
import kotlinx.coroutines.ExperimentalCoroutinesApi;
import kotlinx.coroutines.MainCoroutineDispatcher;
+import org.jetbrains.annotations.NotNull;
+
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.HashSet;
@@ -206,6 +209,7 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel,
private final AppToWebEducationController mAppToWebEducationController;
private final AppHandleAndHeaderVisibilityHelper mAppHandleAndHeaderVisibilityHelper;
private final AppHeaderViewHolder.Factory mAppHeaderViewHolderFactory;
+ private final AppHandleViewHolder.Factory mAppHandleViewHolderFactory;
private boolean mTransitionDragActive;
private SparseArray<EventReceiver> mEventReceiversByDisplay = new SparseArray<>();
@@ -331,6 +335,7 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel,
new InputMonitorFactory(),
SurfaceControl.Transaction::new,
new AppHeaderViewHolder.Factory(),
+ new AppHandleViewHolder.Factory(),
rootTaskDisplayAreaOrganizer,
new SparseArray<>(),
interactionJankMonitor,
@@ -380,6 +385,7 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel,
InputMonitorFactory inputMonitorFactory,
Supplier<SurfaceControl.Transaction> transactionFactory,
AppHeaderViewHolder.Factory appHeaderViewHolderFactory,
+ AppHandleViewHolder.Factory appHandleViewHolderFactory,
RootTaskDisplayAreaOrganizer rootTaskDisplayAreaOrganizer,
SparseArray<DesktopModeWindowDecoration> windowDecorByTaskId,
InteractionJankMonitor interactionJankMonitor,
@@ -422,6 +428,7 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel,
mInputMonitorFactory = inputMonitorFactory;
mTransactionFactory = transactionFactory;
mAppHeaderViewHolderFactory = appHeaderViewHolderFactory;
+ mAppHandleViewHolderFactory = appHandleViewHolderFactory;
mRootTaskDisplayAreaOrganizer = rootTaskDisplayAreaOrganizer;
mGenericLinksParser = genericLinksParser;
mInputManager = mContext.getSystemService(InputManager.class);
@@ -486,7 +493,8 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel,
new DesktopModeOnTaskResizeAnimationListener());
mDesktopTasksController.setOnTaskRepositionAnimationListener(
new DesktopModeOnTaskRepositionAnimationListener());
- if (DesktopModeFlags.ENABLE_DESKTOP_RECENTS_TRANSITIONS_CORNERS_BUGFIX.isTrue()) {
+ if (DesktopModeFlags.ENABLE_DESKTOP_RECENTS_TRANSITIONS_CORNERS_BUGFIX.isTrue()
+ || DesktopModeFlags.ENABLE_INPUT_LAYER_TRANSITION_FIX.isTrue()) {
mRecentsTransitionHandler.addTransitionStateListener(
new DesktopModeRecentsTransitionStateListener());
}
@@ -587,7 +595,8 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel,
RunningTaskInfo taskInfo,
SurfaceControl taskSurface,
SurfaceControl.Transaction startT,
- SurfaceControl.Transaction finishT) {
+ SurfaceControl.Transaction finishT,
+ @TransitionInfo.TransitionMode int changeMode) {
final DesktopModeWindowDecoration decoration = mWindowDecorByTaskId.get(taskInfo.taskId);
if (!shouldShowWindowDecor(taskInfo)) {
if (decoration != null) {
@@ -601,8 +610,8 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel,
} else {
decoration.relayout(taskInfo, startT, finishT, false /* applyStartTransactionOnDraw */,
false /* shouldSetTaskPositionAndCrop */,
- mFocusTransitionObserver.hasGlobalFocus(taskInfo),
- mExclusionRegion);
+ mFocusTransitionObserver.hasGlobalFocus(taskInfo), mExclusionRegion,
+ /*isMovingToBack= */ changeMode == TRANSIT_TO_BACK);
}
}
@@ -617,7 +626,7 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel,
decoration.relayout(taskInfo, startT, finishT, false /* applyStartTransactionOnDraw */,
false /* shouldSetTaskPositionAndCrop */,
mFocusTransitionObserver.hasGlobalFocus(taskInfo),
- mExclusionRegion);
+ mExclusionRegion, /* isMovingToBack= */ false);
}
@Override
@@ -817,9 +826,6 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel,
return;
}
decoration.closeHandleMenu();
- // When the app enters split-select, the handle will no longer be visible, meaning
- // we shouldn't receive input for it any longer.
- decoration.disposeStatusBarInputLayer();
mDesktopTasksController.requestSplit(decoration.mTaskInfo, false /* leftOrTop */);
mDesktopModeUiEventLogger.log(decoration.mTaskInfo,
DesktopUiEventEnum.DESKTOP_WINDOW_APP_HANDLE_MENU_TAP_TO_SPLIT_SCREEN);
@@ -1780,6 +1786,7 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel,
mMainChoreographer,
mSyncQueue,
mAppHeaderViewHolderFactory,
+ mAppHandleViewHolderFactory,
mRootTaskDisplayAreaOrganizer,
mGenericLinksParser,
mAssistContentRequester,
@@ -1871,12 +1878,13 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel,
windowDecoration.relayout(taskInfo, startT, finishT,
false /* applyStartTransactionOnDraw */, false /* shouldSetTaskPositionAndCrop */,
mFocusTransitionObserver.hasGlobalFocus(taskInfo),
- mExclusionRegion);
+ mExclusionRegion, /* isMovingToBack= */ false);
if (!DesktopModeFlags.ENABLE_HANDLE_INPUT_FIX.isTrue()) {
incrementEventReceiverTasks(taskInfo.displayId);
}
}
+ @Nullable
private RunningTaskInfo getOtherSplitTask(int taskId) {
@SplitPosition int remainingTaskPosition = mSplitScreenController
.getSplitPosition(taskId) == SPLIT_POSITION_BOTTOM_OR_RIGHT
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 5432f6f65afb..003baae29114 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
@@ -190,6 +190,7 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin
private ExclusionRegionListener mExclusionRegionListener;
private final AppHeaderViewHolder.Factory mAppHeaderViewHolderFactory;
+ private final AppHandleViewHolder.Factory mAppHandleViewHolderFactory;
private final RootTaskDisplayAreaOrganizer mRootTaskDisplayAreaOrganizer;
private final MaximizeMenuFactory mMaximizeMenuFactory;
private final HandleMenuFactory mHandleMenuFactory;
@@ -212,6 +213,7 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin
private boolean mIsDragging = false;
private Runnable mLoadAppInfoRunnable;
private Runnable mSetAppInfoRunnable;
+ private boolean mIsMovingToBack;
public DesktopModeWindowDecoration(
Context context,
@@ -231,6 +233,7 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin
Choreographer choreographer,
SyncTransactionQueue syncQueue,
AppHeaderViewHolder.Factory appHeaderViewHolderFactory,
+ AppHandleViewHolder.Factory appHandleViewHolderFactory,
RootTaskDisplayAreaOrganizer rootTaskDisplayAreaOrganizer,
AppToWebGenericLinksParser genericLinksParser,
AssistContentRequester assistContentRequester,
@@ -242,10 +245,10 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin
this (context, userContext, displayController, taskResourceLoader, splitScreenController,
desktopUserRepositories, taskOrganizer, taskInfo, taskSurface, handler,
mainExecutor, mainDispatcher, bgScope, bgExecutor, choreographer, syncQueue,
- appHeaderViewHolderFactory, rootTaskDisplayAreaOrganizer, genericLinksParser,
- assistContentRequester, SurfaceControl.Builder::new,
- SurfaceControl.Transaction::new, WindowContainerTransaction::new,
- SurfaceControl::new, new WindowManagerWrapper(
+ appHeaderViewHolderFactory, appHandleViewHolderFactory,
+ rootTaskDisplayAreaOrganizer, genericLinksParser, assistContentRequester,
+ SurfaceControl.Builder::new, SurfaceControl.Transaction::new,
+ WindowContainerTransaction::new, SurfaceControl::new, new WindowManagerWrapper(
context.getSystemService(WindowManager.class)),
new SurfaceControlViewHostFactory() {},
windowDecorViewHostSupplier,
@@ -273,6 +276,7 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin
Choreographer choreographer,
SyncTransactionQueue syncQueue,
AppHeaderViewHolder.Factory appHeaderViewHolderFactory,
+ AppHandleViewHolder.Factory appHandleViewHolderFactory,
RootTaskDisplayAreaOrganizer rootTaskDisplayAreaOrganizer,
AppToWebGenericLinksParser genericLinksParser,
AssistContentRequester assistContentRequester,
@@ -302,6 +306,7 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin
mChoreographer = choreographer;
mSyncQueue = syncQueue;
mAppHeaderViewHolderFactory = appHeaderViewHolderFactory;
+ mAppHandleViewHolderFactory = appHandleViewHolderFactory;
mRootTaskDisplayAreaOrganizer = rootTaskDisplayAreaOrganizer;
mGenericLinksParser = genericLinksParser;
mAssistContentRequester = assistContentRequester;
@@ -462,7 +467,7 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin
// causes flickering. See b/270202228.
final boolean applyTransactionOnDraw = taskInfo.isFreeform();
relayout(taskInfo, t, t, applyTransactionOnDraw, shouldSetTaskVisibilityPositionAndCrop,
- hasGlobalFocus, displayExclusionRegion);
+ hasGlobalFocus, displayExclusionRegion, mIsMovingToBack);
if (!applyTransactionOnDraw) {
t.apply();
}
@@ -489,7 +494,8 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin
void relayout(ActivityManager.RunningTaskInfo taskInfo,
SurfaceControl.Transaction startT, SurfaceControl.Transaction finishT,
boolean applyStartTransactionOnDraw, boolean shouldSetTaskVisibilityPositionAndCrop,
- boolean hasGlobalFocus, @NonNull Region displayExclusionRegion) {
+ boolean hasGlobalFocus, @NonNull Region displayExclusionRegion,
+ boolean isMovingToBack) {
Trace.beginSection("DesktopModeWindowDecoration#relayout");
if (DesktopModeFlags.ENABLE_DESKTOP_WINDOWING_APP_TO_WEB.isTrue()) {
@@ -512,12 +518,17 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin
final boolean inFullImmersive = mDesktopUserRepositories.getProfile(taskInfo.userId)
.isTaskInFullImmersiveState(taskInfo.taskId);
+ mIsMovingToBack = isMovingToBack;
updateRelayoutParams(mRelayoutParams, mContext, taskInfo, mSplitScreenController,
applyStartTransactionOnDraw, shouldSetTaskVisibilityPositionAndCrop,
mIsStatusBarVisible, mIsKeyguardVisibleAndOccluded, inFullImmersive,
mIsDragging, mDisplayController.getInsetsState(taskInfo.displayId), hasGlobalFocus,
- displayExclusionRegion, mIsRecentsTransitionRunning,
- mDesktopModeCompatPolicy.shouldExcludeCaptionFromAppBounds(taskInfo));
+ displayExclusionRegion,
+ /* shouldIgnoreCornerRadius= */ mIsRecentsTransitionRunning
+ && DesktopModeFlags
+ .ENABLE_DESKTOP_RECENTS_TRANSITIONS_CORNERS_BUGFIX.isTrue(),
+ mDesktopModeCompatPolicy.shouldExcludeCaptionFromAppBounds(taskInfo),
+ mIsRecentsTransitionRunning, mIsMovingToBack);
final WindowDecorLinearLayout oldRootView = mResult.mRootView;
final SurfaceControl oldDecorationSurface = mDecorationContainerSurface;
@@ -558,29 +569,15 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin
});
}
- final Point position = new Point();
- if (isAppHandle(mWindowDecorViewHolder)) {
- position.set(determineHandlePosition());
- }
if (canEnterDesktopMode(mContext) && isEducationEnabled()) {
notifyCaptionStateChanged();
}
Trace.beginSection("DesktopModeWindowDecoration#relayout-bindData");
if (isAppHandle(mWindowDecorViewHolder)) {
- mWindowDecorViewHolder.bindData(new AppHandleViewHolder.HandleData(
- mTaskInfo, position, mResult.mCaptionWidth, mResult.mCaptionHeight,
- isCaptionVisible()
- ));
+ updateAppHandleViewHolder();
} else {
- mWindowDecorViewHolder.bindData(new AppHeaderViewHolder.HeaderData(
- mTaskInfo,
- DesktopModeUtils.isTaskMaximized(mTaskInfo, mDisplayController),
- inFullImmersive,
- hasGlobalFocus,
- /* maximizeHoverEnabled= */ canOpenMaximizeMenu(
- /* animatingTaskResizeOrReposition= */ false)
- ));
+ updateAppHeaderViewHolder(inFullImmersive, hasGlobalFocus);
}
Trace.endSection();
@@ -857,9 +854,31 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin
asAppHandle(mWindowDecorViewHolder).disposeStatusBarInputLayer();
}
+ /** Update the view holder for app handle. */
+ private void updateAppHandleViewHolder() {
+ if (!isAppHandle(mWindowDecorViewHolder)) return;
+ asAppHandle(mWindowDecorViewHolder).bindData(new AppHandleViewHolder.HandleData(
+ mTaskInfo, determineHandlePosition(), mResult.mCaptionWidth,
+ mResult.mCaptionHeight, isCaptionVisible()
+ ));
+ }
+
+ /** Update the view holder for app header. */
+ private void updateAppHeaderViewHolder(boolean inFullImmersive, boolean hasGlobalFocus) {
+ if (!isAppHeader(mWindowDecorViewHolder)) return;
+ asAppHeader(mWindowDecorViewHolder).bindData(new AppHeaderViewHolder.HeaderData(
+ mTaskInfo,
+ DesktopModeUtils.isTaskMaximized(mTaskInfo, mDisplayController),
+ inFullImmersive,
+ hasGlobalFocus,
+ /* maximizeHoverEnabled= */ canOpenMaximizeMenu(
+ /* animatingTaskResizeOrReposition= */ false)
+ ));
+ }
+
private WindowDecorationViewHolder createViewHolder() {
if (mRelayoutParams.mLayoutResId == R.layout.desktop_mode_app_handle) {
- return new AppHandleViewHolder(
+ return mAppHandleViewHolderFactory.create(
mResult.mRootView,
mOnCaptionTouchListener,
mOnCaptionButtonClickListener,
@@ -886,6 +905,10 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin
return viewHolder instanceof AppHandleViewHolder;
}
+ private boolean isAppHeader(WindowDecorationViewHolder viewHolder) {
+ return viewHolder instanceof AppHeaderViewHolder;
+ }
+
@Nullable
private AppHandleViewHolder asAppHandle(WindowDecorationViewHolder viewHolder) {
if (viewHolder instanceof AppHandleViewHolder) {
@@ -918,7 +941,9 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin
boolean hasGlobalFocus,
@NonNull Region displayExclusionRegion,
boolean shouldIgnoreCornerRadius,
- boolean shouldExcludeCaptionFromAppBounds) {
+ boolean shouldExcludeCaptionFromAppBounds,
+ boolean isRecentsTransitionRunning,
+ boolean isMovingToBack) {
final int captionLayoutId = getDesktopModeWindowDecorLayoutId(taskInfo.getWindowingMode());
final boolean isAppHeader =
captionLayoutId == R.layout.desktop_mode_app_header;
@@ -935,11 +960,20 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin
// the first frame.
relayoutParams.mAsyncViewHost = isAppHandle;
- final boolean showCaption;
- if (DesktopModeFlags.ENABLE_DESKTOP_IMMERSIVE_DRAG_BUGFIX.isTrue() && isDragging) {
+ boolean showCaption;
+ // If this relayout is occurring from an observed TRANSIT_TO_BACK transition, do not
+ // show caption (this includes split select transition).
+ if (DesktopModeFlags.ENABLE_INPUT_LAYER_TRANSITION_FIX.isTrue()
+ && isMovingToBack && !isDragging) {
+ showCaption = false;
+ } else if (DesktopModeFlags.ENABLE_DESKTOP_IMMERSIVE_DRAG_BUGFIX.isTrue() && isDragging) {
// If the task is being dragged, the caption should not be hidden so that it continues
// receiving input
showCaption = true;
+ } else if (DesktopModeFlags.ENABLE_INPUT_LAYER_TRANSITION_FIX.isTrue()
+ && isRecentsTransitionRunning) {
+ // Caption should not be visible in recents.
+ showCaption = false;
} else if (DesktopModeFlags.ENABLE_FULLY_IMMERSIVE_IN_DESKTOP.isTrue()) {
if (inFullImmersiveMode) {
showCaption = (isStatusBarVisible && !isKeyguardVisibleAndOccluded);
@@ -1811,9 +1845,18 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin
* <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.
+ *
+ * We should not allow input to reach the input layer during a Recents transition, so
+ * update the handle view holder accordingly if transition status changes.
*/
void setIsRecentsTransitionRunning(boolean isRecentsTransitionRunning) {
- mIsRecentsTransitionRunning = isRecentsTransitionRunning;
+ if (mIsRecentsTransitionRunning != isRecentsTransitionRunning) {
+ mIsRecentsTransitionRunning = isRecentsTransitionRunning;
+ if (DesktopModeFlags.ENABLE_INPUT_LAYER_TRANSITION_FIX.isTrue()) {
+ // We don't relayout decor on recents transition, so we need to call it directly.
+ relayout(mTaskInfo, mHasGlobalFocus, mRelayoutParams.mDisplayExclusionRegion);
+ }
+ }
}
/**
@@ -1878,6 +1921,7 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin
Choreographer choreographer,
SyncTransactionQueue syncQueue,
AppHeaderViewHolder.Factory appHeaderViewHolderFactory,
+ AppHandleViewHolder.Factory appHandleViewHolderFactory,
RootTaskDisplayAreaOrganizer rootTaskDisplayAreaOrganizer,
AppToWebGenericLinksParser genericLinksParser,
AssistContentRequester assistContentRequester,
@@ -1905,6 +1949,7 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin
choreographer,
syncQueue,
appHeaderViewHolderFactory,
+ appHandleViewHolderFactory,
rootTaskDisplayAreaOrganizer,
genericLinksParser,
assistContentRequester,
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/FluidResizeTaskPositioner.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/FluidResizeTaskPositioner.java
index 2d6f7459e0ae..732f04259fd3 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/FluidResizeTaskPositioner.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/FluidResizeTaskPositioner.java
@@ -173,6 +173,9 @@ class FluidResizeTaskPositioner implements TaskPositioner, Transitions.Transitio
return new Rect(mRepositionTaskBounds);
}
+ @Override
+ public void close() {}
+
private boolean isResizing() {
return (mCtrlType & CTRL_TYPE_TOP) != 0 || (mCtrlType & CTRL_TYPE_BOTTOM) != 0
|| (mCtrlType & CTRL_TYPE_LEFT) != 0 || (mCtrlType & CTRL_TYPE_RIGHT) != 0;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/HandleMenu.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/HandleMenu.kt
index c544468f5191..a8a7032d0b86 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/HandleMenu.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/HandleMenu.kt
@@ -108,12 +108,9 @@ class HandleMenu(
private val isViewAboveStatusBar: Boolean
get() = (DesktopModeFlags.ENABLE_HANDLE_INPUT_FIX.isTrue() && !taskInfo.isFreeform)
- private val pillElevation: Int = loadDimensionPixelSize(
- R.dimen.desktop_mode_handle_menu_pill_elevation)
private val pillTopMargin: Int = loadDimensionPixelSize(
R.dimen.desktop_mode_handle_menu_pill_spacing_margin)
- private val menuWidth = loadDimensionPixelSize(
- R.dimen.desktop_mode_handle_menu_width) + pillElevation
+ private val menuWidth = loadDimensionPixelSize(R.dimen.desktop_mode_handle_menu_width)
private val menuHeight = getHandleMenuHeight()
private val marginMenuTop = loadDimensionPixelSize(R.dimen.desktop_mode_handle_menu_margin_top)
private val marginMenuStart = loadDimensionPixelSize(
@@ -398,8 +395,7 @@ class HandleMenu(
* Determines handle menu height based the max size and the visibility of pills.
*/
private fun getHandleMenuHeight(): Int {
- var menuHeight = loadDimensionPixelSize(
- R.dimen.desktop_mode_handle_menu_height) + pillElevation
+ var menuHeight = loadDimensionPixelSize(R.dimen.desktop_mode_handle_menu_height)
if (!shouldShowWindowingPill) {
menuHeight -= loadDimensionPixelSize(
R.dimen.desktop_mode_handle_menu_windowing_pill_height)
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/MultiDisplayVeiledResizeTaskPositioner.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/MultiDisplayVeiledResizeTaskPositioner.kt
index 1b0e0f70ed21..eb324f74ca82 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/MultiDisplayVeiledResizeTaskPositioner.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/MultiDisplayVeiledResizeTaskPositioner.kt
@@ -294,6 +294,10 @@ class MultiDisplayVeiledResizeTaskPositioner(
return Rect(repositionTaskBounds)
}
+ override fun close() {
+ displayController.removeDisplayWindowListener(this)
+ }
+
private fun resetVeilIfVisible() {
if (isResizingOrAnimatingResize) {
desktopWindowDecoration.hideResizeVeil()
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/TaskDragResizer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/TaskDragResizer.java
index 63b288d133dd..06e5380fa1de 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/TaskDragResizer.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/TaskDragResizer.java
@@ -41,4 +41,10 @@ public interface TaskDragResizer {
*/
void removeDragEventListener(
DragPositioningCallbackUtility.DragEventListener dragEventListener);
+
+ /**
+ * Releases any resources associated with this TaskDragResizer. This should be called when the
+ * associated window is closed.
+ */
+ void close();
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/VeiledResizeTaskPositioner.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/VeiledResizeTaskPositioner.java
index d2c79d76e6c1..7e941ec0d31a 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/VeiledResizeTaskPositioner.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/VeiledResizeTaskPositioner.java
@@ -205,6 +205,9 @@ public class VeiledResizeTaskPositioner implements TaskPositioner, Transitions.T
return new Rect(mRepositionTaskBounds);
}
+ @Override
+ public void close() {}
+
private boolean isResizing() {
return (mCtrlType & CTRL_TYPE_TOP) != 0 || (mCtrlType & CTRL_TYPE_BOTTOM) != 0
|| (mCtrlType & CTRL_TYPE_LEFT) != 0 || (mCtrlType & CTRL_TYPE_RIGHT) != 0;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/WindowDecorViewModel.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/WindowDecorViewModel.java
index 1563259f4a1a..5e4a0a5860f0 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/WindowDecorViewModel.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/WindowDecorViewModel.java
@@ -18,6 +18,7 @@ package com.android.wm.shell.windowdecor;
import android.app.ActivityManager;
import android.view.SurfaceControl;
+import android.window.TransitionInfo;
import com.android.wm.shell.freeform.FreeformTaskTransitionStarter;
import com.android.wm.shell.splitscreen.SplitScreenController;
@@ -83,12 +84,14 @@ public interface WindowDecorViewModel {
* @param taskSurface the surface of the task
* @param startT the start transaction to be applied before the transition
* @param finishT the finish transaction to restore states after the transition
+ * @param changeMode the type of change to the task
*/
void onTaskChanging(
ActivityManager.RunningTaskInfo taskInfo,
SurfaceControl taskSurface,
SurfaceControl.Transaction startT,
- SurfaceControl.Transaction finishT);
+ SurfaceControl.Transaction finishT,
+ @TransitionInfo.TransitionMode int changeMode);
/**
* Notifies that the given task is about to close to give the window decoration a chance to
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 bde46a1bc375..91a899c09407 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
@@ -699,6 +699,9 @@ public abstract class WindowDecoration<T extends View & TaskFocusStateConsumer>
public void close() {
Trace.beginSection("WindowDecoration#close");
mDisplayController.removeDisplayWindowListener(mOnDisplaysChangedListener);
+ if (mTaskDragResizer != null) {
+ mTaskDragResizer.close();
+ }
final WindowContainerTransaction wct = mWindowContainerTransactionSupplier.get();
releaseViews(wct);
mTaskOrganizer.applyTransaction(wct);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/tiling/DesktopTilingWindowDecoration.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/tiling/DesktopTilingWindowDecoration.kt
index 9c55f0ecda93..7c5f34f979cd 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/tiling/DesktopTilingWindowDecoration.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/tiling/DesktopTilingWindowDecoration.kt
@@ -152,6 +152,8 @@ class DesktopTilingWindowDecoration(
endBounds = destinationBounds,
callback,
)
+ } else {
+ callback.invoke()
}
}
return isTiled
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/viewholder/AppHandleViewHolder.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/viewholder/AppHandleViewHolder.kt
index d9df899f8b40..0985587a330e 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/viewholder/AppHandleViewHolder.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/viewholder/AppHandleViewHolder.kt
@@ -49,7 +49,7 @@ import com.android.wm.shell.windowdecor.additionalviewcontainer.AdditionalSystem
* A desktop mode window decoration used when the window is in full "focus" (i.e. fullscreen/split).
* It hosts a simple handle bar from which to initiate a drag motion to enter desktop mode.
*/
-internal class AppHandleViewHolder(
+class AppHandleViewHolder(
rootView: View,
onCaptionTouchListener: View.OnTouchListener,
onCaptionButtonClickListener: OnClickListener,
@@ -66,7 +66,7 @@ internal class AppHandleViewHolder(
val position: Point,
val width: Int,
val height: Int,
- val isCaptionVisible: Boolean
+ val showInputLayer: Boolean
) : Data()
private lateinit var taskInfo: RunningTaskInfo
@@ -101,7 +101,7 @@ internal class AppHandleViewHolder(
}
override fun bindData(data: HandleData) {
- bindData(data.taskInfo, data.position, data.width, data.height, data.isCaptionVisible)
+ bindData(data.taskInfo, data.position, data.width, data.height, data.showInputLayer)
}
private fun bindData(
@@ -109,14 +109,13 @@ internal class AppHandleViewHolder(
position: Point,
width: Int,
height: Int,
- isCaptionVisible: Boolean
+ showInputLayer: Boolean
) {
captionHandle.imageTintList = ColorStateList.valueOf(getCaptionHandleBarColor(taskInfo))
this.taskInfo = taskInfo
// If handle is not in status bar region(i.e., bottom stage in vertical split),
// do not create an input layer
- if (position.y >= SystemBarUtils.getStatusBarHeight(context)) return
- if (!isCaptionVisible) {
+ if (position.y >= SystemBarUtils.getStatusBarHeight(context) || !showInputLayer) {
disposeStatusBarInputLayer()
return
}
@@ -276,4 +275,25 @@ internal class AppHandleViewHolder(
}
override fun close() {}
+
+ /** Factory class for creating [AppHandleViewHolder] objects. */
+ class Factory {
+ /**
+ * Create a [AppHandleViewHolder] object to handle caption view and status bar
+ * input layer logic.
+ */
+ fun create(
+ rootView: View,
+ onCaptionTouchListener: View.OnTouchListener,
+ onCaptionButtonClickListener: OnClickListener,
+ windowManagerWrapper: WindowManagerWrapper,
+ handler: Handler,
+ ): AppHandleViewHolder = AppHandleViewHolder(
+ rootView,
+ onCaptionTouchListener,
+ onCaptionButtonClickListener,
+ windowManagerWrapper,
+ handler,
+ )
+ }
}
diff --git a/libs/WindowManager/Shell/tests/e2e/utils/src/com/android/wm/shell/SimulatedConnectedDisplayTestRule.kt b/libs/WindowManager/Shell/tests/e2e/utils/src/com/android/wm/shell/SimulatedConnectedDisplayTestRule.kt
new file mode 100644
index 000000000000..68f7ef09ee70
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/e2e/utils/src/com/android/wm/shell/SimulatedConnectedDisplayTestRule.kt
@@ -0,0 +1,168 @@
+/*
+ * 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.wm.shell
+
+import android.graphics.Point
+import android.hardware.display.DisplayManager
+import android.hardware.display.DisplayManager.DisplayListener
+import android.os.Handler
+import android.os.Looper
+import android.provider.Settings
+import android.util.Log
+import androidx.test.platform.app.InstrumentationRegistry
+import kotlin.time.Duration.Companion.seconds
+import kotlinx.coroutines.channels.awaitClose
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.callbackFlow
+import kotlinx.coroutines.flow.take
+import kotlinx.coroutines.runBlocking
+import kotlinx.coroutines.withTimeoutOrNull
+import org.junit.rules.TestRule
+import org.junit.runner.Description
+import org.junit.runners.model.Statement
+
+/**
+ * A TestRule to manage multiple simulated connected overlay displays.
+ */
+class SimulatedConnectedDisplayTestRule : TestRule {
+
+ private val context = InstrumentationRegistry.getInstrumentation().targetContext
+ private val uiAutomation = InstrumentationRegistry.getInstrumentation().uiAutomation
+ private val displayManager = context.getSystemService(DisplayManager::class.java)
+ private val addedDisplays = mutableListOf<Int>()
+
+ override fun apply(base: Statement, description: Description): Statement =
+ object : Statement() {
+ override fun evaluate() {
+ try {
+ base.evaluate()
+ } finally {
+ teardown()
+ }
+ }
+ }
+
+ private fun teardown() {
+ cleanupTestDisplays()
+ }
+
+ /**
+ * Adds multiple overlay displays with specified dimensions. Any existing overlay displays
+ * will be removed before adding the new ones.
+ *
+ * @param displays A list of [Point] objects, where each [Point] represents the
+ * width and height of a simulated display.
+ * @return List of displayIds of added displays.
+ */
+ fun setupTestDisplays(displays: List<Point>): List<Int> = runBlocking {
+ // Cleanup any existing overlay displays.
+ cleanupTestDisplays()
+
+ if (displays.isEmpty()) {
+ Log.w(TAG, "setupTestDisplays called with an empty list. No displays created.")
+ return@runBlocking emptyList()
+ }
+
+ val displayAddedFlow: Flow<Int> = callbackFlow {
+ val listener = object : DisplayListener {
+ override fun onDisplayAdded(displayId: Int) {
+ trySend(displayId)
+ }
+
+ override fun onDisplayRemoved(displayId: Int) {}
+ override fun onDisplayChanged(displayId: Int) {}
+ }
+
+ val handler = Handler(Looper.getMainLooper())
+ displayManager.registerDisplayListener(listener, handler)
+
+ awaitClose {
+ displayManager.unregisterDisplayListener(listener)
+ }
+ }
+
+ val displaySettings = displays.joinToString(separator = ";") { size ->
+ "${size.x}x${size.y}/$DEFAULT_DENSITY"
+ }
+
+ // Add the overlay displays
+ Settings.Global.putString(
+ InstrumentationRegistry.getInstrumentation().context.contentResolver,
+ Settings.Global.OVERLAY_DISPLAY_DEVICES, displaySettings
+ )
+ withTimeoutOrNull(TIMEOUT) {
+ displayAddedFlow.take(displays.size).collect { displayId ->
+ addedDisplays.add(displayId)
+ }
+ } ?: error("Timed out waiting for displays to be added.")
+ addedDisplays
+ }
+
+ /**
+ * Adds multiple overlay displays with default dimensions. Any existing overlay displays
+ * will be removed before adding the new ones.
+ *
+ * @param count number of displays to add.
+ * @return List of displayIds of added displays.
+ */
+ fun setupTestDisplays(count: Int): List<Int> {
+ val displays = List(count) { Point(DEFAULT_WIDTH, DEFAULT_HEIGHT) }
+ return setupTestDisplays(displays)
+ }
+
+ private fun cleanupTestDisplays() = runBlocking {
+ if (addedDisplays.isEmpty()) {
+ return@runBlocking
+ }
+
+ val displayRemovedFlow: Flow<Int> = callbackFlow {
+ val listener = object : DisplayListener {
+ override fun onDisplayAdded(displayId: Int) {}
+ override fun onDisplayRemoved(displayId: Int) {
+ trySend(displayId)
+ }
+
+ override fun onDisplayChanged(displayId: Int) {}
+ }
+ val handler = Handler(Looper.getMainLooper())
+ displayManager.registerDisplayListener(listener, handler)
+
+ awaitClose {
+ displayManager.unregisterDisplayListener(listener)
+ }
+ }
+
+ // Remove overlay displays
+ Settings.Global.putString(
+ InstrumentationRegistry.getInstrumentation().context.contentResolver,
+ Settings.Global.OVERLAY_DISPLAY_DEVICES, null)
+
+ withTimeoutOrNull(TIMEOUT) {
+ displayRemovedFlow.take(addedDisplays.size).collect { displayId ->
+ addedDisplays.remove(displayId)
+ }
+ } ?: error("Timed out waiting for displays to be removed.")
+ }
+
+ private companion object {
+ const val DEFAULT_WIDTH = 1280
+ const val DEFAULT_HEIGHT = 720
+ const val DEFAULT_DENSITY = 160
+ const val TAG = "SimulatedConnectedDisplayTestRule"
+ val TIMEOUT = 10.seconds
+ }
+}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/BackAnimationControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/BackAnimationControllerTest.java
index 05750a54f566..b139c000a1a9 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/BackAnimationControllerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/BackAnimationControllerTest.java
@@ -889,8 +889,8 @@ public class BackAnimationControllerTest extends ShellTestCase {
*/
private void doStartEvents(int startX, int moveX) {
doMotionEvent(MotionEvent.ACTION_DOWN, startX);
- mController.onThresholdCrossed();
doMotionEvent(MotionEvent.ACTION_MOVE, moveX);
+ mController.onThresholdCrossed();
}
private void simulateRemoteAnimationStart() throws RemoteException {
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/BackProgressAnimatorTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/BackProgressAnimatorTest.java
index 2ef6c558b0b5..43bcc3b61124 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/BackProgressAnimatorTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/BackProgressAnimatorTest.java
@@ -58,8 +58,7 @@ public class BackProgressAnimatorTest extends ShellTestCase {
/* frameTime = */ 0,
/* progress = */ progress,
/* triggerBack = */ false,
- /* swipeEdge = */ BackEvent.EDGE_LEFT,
- /* departingAnimationTarget = */ null);
+ /* swipeEdge = */ BackEvent.EDGE_LEFT);
}
@Before
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/CustomCrossActivityBackAnimationTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/CustomCrossActivityBackAnimationTest.kt
index 2cc52c5ab9ad..9d4cc49a7a65 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/CustomCrossActivityBackAnimationTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/CustomCrossActivityBackAnimationTest.kt
@@ -224,8 +224,7 @@ class CustomCrossActivityBackAnimationTest : ShellTestCase() {
/* frameTime = */ 0,
/* progress = */ progress,
/* triggerBack = */ false,
- /* swipeEdge = */ BackEvent.EDGE_LEFT,
- /* departingAnimationTarget = */ null
+ /* swipeEdge = */ BackEvent.EDGE_LEFT
)
private fun createAnimationTarget(open: Boolean): RemoteAnimationTarget {
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubbleDataTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubbleDataTest.java
index 7a7d88b80ce3..b3d2db6da6d5 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubbleDataTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubbleDataTest.java
@@ -28,7 +28,6 @@ import static org.mockito.Mockito.never;
import static org.mockito.Mockito.reset;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoMoreInteractions;
-import static org.mockito.Mockito.verifyZeroInteractions;
import static org.mockito.Mockito.when;
import android.app.Notification;
@@ -804,7 +803,7 @@ public class BubbleDataTest extends ShellTestCase {
mBubbleData.setListener(mListener);
changeExpandedStateAtTime(true, 2000L);
- verifyZeroInteractions(mListener);
+ verifyNoMoreInteractions(mListener);
}
/**
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubblesNavBarMotionEventHandlerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubblesNavBarMotionEventHandlerTest.java
index c4b9c9ba43f1..b4b96791298d 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubblesNavBarMotionEventHandlerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubblesNavBarMotionEventHandlerTest.java
@@ -25,7 +25,6 @@ import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.ArgumentMatchers.floatThat;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoMoreInteractions;
-import static org.mockito.Mockito.verifyZeroInteractions;
import android.os.SystemClock;
import android.testing.AndroidTestingRunner;
@@ -84,7 +83,7 @@ public class BubblesNavBarMotionEventHandlerTest extends ShellTestCase {
verify(mMotionEventListener).onMove(0, -600);
// Check that velocity up is about 5000
verify(mMotionEventListener).onUp(eq(0f), floatThat(f -> Math.round(f) == -5000));
- verifyZeroInteractions(mMotionEventListener);
+ verifyNoMoreInteractions(mMotionEventListener);
verify(mInterceptTouchRunnable).run();
}
@@ -94,8 +93,8 @@ public class BubblesNavBarMotionEventHandlerTest extends ShellTestCase {
mMotionEventHandler.onMotionEvent(newEvent(ACTION_MOVE, 0, 100));
mMotionEventHandler.onMotionEvent(newEvent(ACTION_UP, 0, 100));
- verifyZeroInteractions(mMotionEventListener);
- verifyZeroInteractions(mInterceptTouchRunnable);
+ verifyNoMoreInteractions(mMotionEventListener);
+ verifyNoMoreInteractions(mInterceptTouchRunnable);
}
@Test
@@ -107,7 +106,7 @@ public class BubblesNavBarMotionEventHandlerTest extends ShellTestCase {
verify(mMotionEventListener).onDown(0, 990);
verify(mMotionEventListener).onMove(100, 0);
verify(mMotionEventListener).onUp(0, 0);
- verifyZeroInteractions(mMotionEventListener);
+ verifyNoMoreInteractions(mMotionEventListener);
verify(mInterceptTouchRunnable).run();
}
@@ -119,7 +118,7 @@ public class BubblesNavBarMotionEventHandlerTest extends ShellTestCase {
verify(mMotionEventListener).onDown(0, 990);
verifyNoMoreInteractions(mMotionEventListener);
- verifyZeroInteractions(mInterceptTouchRunnable);
+ verifyNoMoreInteractions(mInterceptTouchRunnable);
}
@Test
@@ -129,7 +128,7 @@ public class BubblesNavBarMotionEventHandlerTest extends ShellTestCase {
verify(mMotionEventListener).onDown(0, 990);
verify(mMotionEventListener).onCancel();
verifyNoMoreInteractions(mMotionEventListener);
- verifyZeroInteractions(mInterceptTouchRunnable);
+ verifyNoMoreInteractions(mInterceptTouchRunnable);
}
private MotionEvent newEvent(int actionDown, float x, float y) {
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/DevicePostureControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/DevicePostureControllerTest.java
index 3323740697f3..1472464e8143 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/DevicePostureControllerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/DevicePostureControllerTest.java
@@ -22,7 +22,7 @@ import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.clearInvocations;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.verifyZeroInteractions;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
import android.content.Context;
@@ -105,6 +105,6 @@ public class DevicePostureControllerTest extends ShellTestCase {
int sameDevicePosture = mDevicePostureCaptor.getValue();
mDevicePostureController.onDevicePostureChanged(sameDevicePosture);
- verifyZeroInteractions(mOnDevicePostureChangedListener);
+ verifyNoMoreInteractions(mOnDevicePostureChangedListener);
}
}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/DisplayImeControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/DisplayImeControllerTest.java
index ee9d17706372..a53277a3764e 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/DisplayImeControllerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/DisplayImeControllerTest.java
@@ -32,7 +32,7 @@ import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.verifyZeroInteractions;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
import android.graphics.Insets;
import android.graphics.Point;
@@ -108,26 +108,26 @@ public class DisplayImeControllerTest extends ShellTestCase {
public void insetsControlChanged_schedulesNoWorkOnExecutor() {
Looper.prepare();
mPerDisplay.insetsControlChanged(insetsStateWithIme(false), insetsSourceControl());
- verifyZeroInteractions(mExecutor);
+ verifyNoMoreInteractions(mExecutor);
}
@Test
public void insetsChanged_schedulesNoWorkOnExecutor() {
Looper.prepare();
mPerDisplay.insetsChanged(insetsStateWithIme(false));
- verifyZeroInteractions(mExecutor);
+ verifyNoMoreInteractions(mExecutor);
}
@Test
public void showInsets_schedulesNoWorkOnExecutor() {
mPerDisplay.showInsets(ime(), true /* fromIme */, ImeTracker.Token.empty());
- verifyZeroInteractions(mExecutor);
+ verifyNoMoreInteractions(mExecutor);
}
@Test
public void hideInsets_schedulesNoWorkOnExecutor() {
mPerDisplay.hideInsets(ime(), true /* fromIme */, ImeTracker.Token.empty());
- verifyZeroInteractions(mExecutor);
+ verifyNoMoreInteractions(mExecutor);
}
// With the refactor, the control's isInitiallyVisible is used to apply to the IME, therefore
@@ -135,7 +135,7 @@ public class DisplayImeControllerTest extends ShellTestCase {
@Test
@RequiresFlagsDisabled(android.view.inputmethod.Flags.FLAG_REFACTOR_INSETS_CONTROLLER)
public void reappliesVisibilityToChangedLeash() {
- verifyZeroInteractions(mT);
+ verifyNoMoreInteractions(mT);
mPerDisplay.mImeShowing = false;
mPerDisplay.insetsControlChanged(insetsStateWithIme(false), insetsSourceControl());
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/TabletopModeControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/TabletopModeControllerTest.java
index 96d202ce3a85..7d1866975848 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/TabletopModeControllerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/TabletopModeControllerTest.java
@@ -29,7 +29,7 @@ import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.clearInvocations;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.verifyZeroInteractions;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
import static org.mockito.Mockito.when;
import android.content.Context;
@@ -145,7 +145,7 @@ public class TabletopModeControllerTest extends ShellTestCase {
mConfiguration.windowConfiguration.setDisplayRotation(Surface.ROTATION_0);
mPipTabletopController.onDisplayConfigurationChanged(DEFAULT_DISPLAY, mConfiguration);
- verifyZeroInteractions(mOnTabletopModeChangedListener);
+ verifyNoMoreInteractions(mOnTabletopModeChangedListener);
}
// Test cases starting from folded state (DEVICE_POSTURE_CLOSED)
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/pip/PipAppOpsListenerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/pip/PipAppOpsListenerTest.java
index e92e243172f7..a2066dbf7a5f 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/pip/PipAppOpsListenerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/pip/PipAppOpsListenerTest.java
@@ -21,7 +21,7 @@ import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.verifyZeroInteractions;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
import static org.mockito.Mockito.when;
import android.app.AppOpsManager;
@@ -157,7 +157,7 @@ public class PipAppOpsListenerTest {
opChangedListener.onOpChanged(String.valueOf(AppOpsManager.OP_PICTURE_IN_PICTURE),
packageName);
- verifyZeroInteractions(mMockExecutor);
+ verifyNoMoreInteractions(mMockExecutor);
}
@Test
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopDisplayModeControllerTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopDisplayModeControllerTest.kt
index 450989dd334d..105941079095 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopDisplayModeControllerTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopDisplayModeControllerTest.kt
@@ -26,28 +26,37 @@ import android.os.Binder
import android.os.Handler
import android.platform.test.annotations.DisableFlags
import android.platform.test.annotations.EnableFlags
+import android.platform.test.annotations.UsesFlags
import android.platform.test.flag.junit.FlagsParameterization
import android.provider.Settings
import android.provider.Settings.Global.DEVELOPMENT_FORCE_DESKTOP_MODE_ON_EXTERNAL_DISPLAYS
+import android.view.Display
import android.view.Display.DEFAULT_DISPLAY
import android.view.IWindowManager
import android.view.WindowManager.TRANSIT_CHANGE
import android.window.DisplayAreaInfo
import android.window.WindowContainerTransaction
import androidx.test.filters.SmallTest
+import com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn
+import com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession
import com.android.dx.mockito.inline.extended.ExtendedMockito.never
+import com.android.dx.mockito.inline.extended.StaticMockitoSession
+import com.android.server.display.feature.flags.Flags as DisplayFlags
import com.android.window.flags.Flags
import com.android.wm.shell.MockToken
import com.android.wm.shell.RootTaskDisplayAreaOrganizer
import com.android.wm.shell.ShellTaskOrganizer
import com.android.wm.shell.ShellTestCase
import com.android.wm.shell.TestRunningTaskInfoBuilder
+import com.android.wm.shell.common.DisplayController
import com.android.wm.shell.desktopmode.desktopwallpaperactivity.DesktopWallpaperActivityTokenProvider
+import com.android.wm.shell.shared.desktopmode.DesktopModeStatus
import com.android.wm.shell.transition.Transitions
import com.google.common.truth.Truth.assertThat
import com.google.testing.junit.testparameterinjector.TestParameter
import com.google.testing.junit.testparameterinjector.TestParameterInjector
import com.google.testing.junit.testparameterinjector.TestParameterValuesProvider
+import org.junit.After
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
@@ -60,6 +69,7 @@ import org.mockito.kotlin.argumentCaptor
import org.mockito.kotlin.eq
import org.mockito.kotlin.mock
import org.mockito.kotlin.whenever
+import org.mockito.quality.Strictness
/**
* Test class for [DesktopDisplayModeController]
@@ -68,6 +78,7 @@ import org.mockito.kotlin.whenever
*/
@SmallTest
@RunWith(TestParameterInjector::class)
+@UsesFlags(com.android.server.display.feature.flags.Flags::class)
class DesktopDisplayModeControllerTest(
@TestParameter(valuesProvider = FlagsParameterizationProvider::class)
flags: FlagsParameterization
@@ -79,6 +90,7 @@ class DesktopDisplayModeControllerTest(
private val desktopWallpaperActivityTokenProvider =
mock<DesktopWallpaperActivityTokenProvider>()
private val inputManager = mock<InputManager>()
+ private val displayController = mock<DisplayController>()
private val mainHandler = mock<Handler>()
private lateinit var controller: DesktopDisplayModeController
@@ -90,6 +102,12 @@ class DesktopDisplayModeControllerTest(
TestRunningTaskInfoBuilder().setWindowingMode(WINDOWING_MODE_FULLSCREEN).build()
private val defaultTDA = DisplayAreaInfo(MockToken().token(), DEFAULT_DISPLAY, 0)
private val wallpaperToken = MockToken().token()
+ private val externalDisplay = mock<Display>()
+
+ private lateinit var extendedDisplaySettingsRestoreSession:
+ ExtendedDisplaySettingsRestoreSession
+
+ private lateinit var mockitoSession: StaticMockitoSession
init {
mSetFlagsRule.setFlagsParameterization(flags)
@@ -97,6 +115,13 @@ class DesktopDisplayModeControllerTest(
@Before
fun setUp() {
+ mockitoSession =
+ mockitoSession()
+ .strictness(Strictness.LENIENT)
+ .spyStatic(DesktopModeStatus::class.java)
+ .startMocking()
+ extendedDisplaySettingsRestoreSession =
+ ExtendedDisplaySettingsRestoreSession(context.contentResolver)
whenever(transitions.startTransition(anyInt(), any(), isNull())).thenReturn(Binder())
whenever(rootTaskDisplayAreaOrganizer.getDisplayAreaInfo(DEFAULT_DISPLAY))
.thenReturn(defaultTDA)
@@ -109,42 +134,50 @@ class DesktopDisplayModeControllerTest(
shellTaskOrganizer,
desktopWallpaperActivityTokenProvider,
inputManager,
+ displayController,
mainHandler,
)
runningTasks.add(freeformTask)
runningTasks.add(fullscreenTask)
whenever(shellTaskOrganizer.getRunningTasks(anyInt())).thenReturn(ArrayList(runningTasks))
whenever(desktopWallpaperActivityTokenProvider.getToken()).thenReturn(wallpaperToken)
+ whenever(displayController.getDisplay(EXTERNAL_DISPLAY_ID)).thenReturn(externalDisplay)
setTabletModeStatus(SwitchState.UNKNOWN)
}
+ @After
+ fun tearDown() {
+ extendedDisplaySettingsRestoreSession.restore()
+ mockitoSession.finishMocking()
+ }
+
private fun testDisplayWindowingModeSwitchOnDisplayConnected(expectToSwitch: Boolean) {
defaultTDA.configuration.windowConfiguration.windowingMode = WINDOWING_MODE_FULLSCREEN
whenever(mockWindowManager.getWindowingMode(anyInt())).thenReturn(WINDOWING_MODE_FULLSCREEN)
- ExtendedDisplaySettingsSession(context.contentResolver, 1).use {
- connectExternalDisplay()
- if (expectToSwitch) {
- // Assumes [connectExternalDisplay] properly triggered the switching transition.
- // Will verify the transition later along with [disconnectExternalDisplay].
- defaultTDA.configuration.windowConfiguration.windowingMode = WINDOWING_MODE_FREEFORM
- }
- disconnectExternalDisplay()
+ setExtendedMode(true)
- if (expectToSwitch) {
- val arg = argumentCaptor<WindowContainerTransaction>()
- verify(transitions, times(2))
- .startTransition(eq(TRANSIT_CHANGE), arg.capture(), isNull())
- assertThat(arg.firstValue.changes[defaultTDA.token.asBinder()]?.windowingMode)
- .isEqualTo(WINDOWING_MODE_FREEFORM)
- assertThat(arg.firstValue.changes[wallpaperToken.asBinder()]?.windowingMode)
- .isEqualTo(WINDOWING_MODE_FULLSCREEN)
- assertThat(arg.secondValue.changes[defaultTDA.token.asBinder()]?.windowingMode)
- .isEqualTo(WINDOWING_MODE_FULLSCREEN)
- assertThat(arg.secondValue.changes[wallpaperToken.asBinder()]?.windowingMode)
- .isEqualTo(WINDOWING_MODE_FULLSCREEN)
- } else {
- verify(transitions, never()).startTransition(eq(TRANSIT_CHANGE), any(), isNull())
- }
+ connectExternalDisplay()
+ if (expectToSwitch) {
+ // Assumes [connectExternalDisplay] properly triggered the switching transition.
+ // Will verify the transition later along with [disconnectExternalDisplay].
+ defaultTDA.configuration.windowConfiguration.windowingMode = WINDOWING_MODE_FREEFORM
+ }
+ disconnectExternalDisplay()
+
+ if (expectToSwitch) {
+ val arg = argumentCaptor<WindowContainerTransaction>()
+ verify(transitions, times(2))
+ .startTransition(eq(TRANSIT_CHANGE), arg.capture(), isNull())
+ assertThat(arg.firstValue.changes[defaultTDA.token.asBinder()]?.windowingMode)
+ .isEqualTo(WINDOWING_MODE_FREEFORM)
+ assertThat(arg.firstValue.changes[wallpaperToken.asBinder()]?.windowingMode)
+ .isEqualTo(WINDOWING_MODE_FULLSCREEN)
+ assertThat(arg.secondValue.changes[defaultTDA.token.asBinder()]?.windowingMode)
+ .isEqualTo(WINDOWING_MODE_FULLSCREEN)
+ assertThat(arg.secondValue.changes[wallpaperToken.asBinder()]?.windowingMode)
+ .isEqualTo(WINDOWING_MODE_FULLSCREEN)
+ } else {
+ verify(transitions, never()).startTransition(eq(TRANSIT_CHANGE), any(), isNull())
}
}
@@ -176,15 +209,10 @@ class DesktopDisplayModeControllerTest(
disconnectExternalDisplay()
}
setTabletModeStatus(tabletModeStatus)
+ setExtendedMode(param.extendedDisplayEnabled)
- ExtendedDisplaySettingsSession(
- context.contentResolver,
- if (param.extendedDisplayEnabled) 1 else 0,
- )
- .use {
- assertThat(controller.getTargetWindowingModeForDefaultDisplay())
- .isEqualTo(param.expectedWindowingMode)
- }
+ assertThat(controller.getTargetWindowingModeForDefaultDisplay())
+ .isEqualTo(param.expectedWindowingMode)
}
@Test
@@ -199,15 +227,10 @@ class DesktopDisplayModeControllerTest(
disconnectExternalDisplay()
}
setTabletModeStatus(param.tabletModeStatus)
+ setExtendedMode(param.extendedDisplayEnabled)
- ExtendedDisplaySettingsSession(
- context.contentResolver,
- if (param.extendedDisplayEnabled) 1 else 0,
- )
- .use {
- assertThat(controller.getTargetWindowingModeForDefaultDisplay())
- .isEqualTo(param.expectedWindowingMode)
- }
+ assertThat(controller.getTargetWindowingModeForDefaultDisplay())
+ .isEqualTo(param.expectedWindowingMode)
}
@Test
@@ -215,18 +238,16 @@ class DesktopDisplayModeControllerTest(
fun displayWindowingModeSwitch_existingTasksOnConnected() {
defaultTDA.configuration.windowConfiguration.windowingMode = WINDOWING_MODE_FULLSCREEN
whenever(mockWindowManager.getWindowingMode(anyInt())).thenReturn(WINDOWING_MODE_FULLSCREEN)
+ setExtendedMode(true)
- ExtendedDisplaySettingsSession(context.contentResolver, 1).use {
- connectExternalDisplay()
+ connectExternalDisplay()
- val arg = argumentCaptor<WindowContainerTransaction>()
- verify(transitions, times(1))
- .startTransition(eq(TRANSIT_CHANGE), arg.capture(), isNull())
- assertThat(arg.firstValue.changes[freeformTask.token.asBinder()]?.windowingMode)
- .isEqualTo(WINDOWING_MODE_UNDEFINED)
- assertThat(arg.firstValue.changes[fullscreenTask.token.asBinder()]?.windowingMode)
- .isEqualTo(WINDOWING_MODE_FULLSCREEN)
- }
+ val arg = argumentCaptor<WindowContainerTransaction>()
+ verify(transitions, times(1)).startTransition(eq(TRANSIT_CHANGE), arg.capture(), isNull())
+ assertThat(arg.firstValue.changes[freeformTask.token.asBinder()]?.windowingMode)
+ .isEqualTo(WINDOWING_MODE_UNDEFINED)
+ assertThat(arg.firstValue.changes[fullscreenTask.token.asBinder()]?.windowingMode)
+ .isEqualTo(WINDOWING_MODE_FULLSCREEN)
}
@Test
@@ -236,18 +257,16 @@ class DesktopDisplayModeControllerTest(
whenever(mockWindowManager.getWindowingMode(anyInt())).thenAnswer {
WINDOWING_MODE_FULLSCREEN
}
+ setExtendedMode(true)
- ExtendedDisplaySettingsSession(context.contentResolver, 1).use {
- disconnectExternalDisplay()
+ disconnectExternalDisplay()
- val arg = argumentCaptor<WindowContainerTransaction>()
- verify(transitions, times(1))
- .startTransition(eq(TRANSIT_CHANGE), arg.capture(), isNull())
- assertThat(arg.firstValue.changes[freeformTask.token.asBinder()]?.windowingMode)
- .isEqualTo(WINDOWING_MODE_FREEFORM)
- assertThat(arg.firstValue.changes[fullscreenTask.token.asBinder()]?.windowingMode)
- .isEqualTo(WINDOWING_MODE_UNDEFINED)
- }
+ val arg = argumentCaptor<WindowContainerTransaction>()
+ verify(transitions, times(1)).startTransition(eq(TRANSIT_CHANGE), arg.capture(), isNull())
+ assertThat(arg.firstValue.changes[freeformTask.token.asBinder()]?.windowingMode)
+ .isEqualTo(WINDOWING_MODE_FREEFORM)
+ assertThat(arg.firstValue.changes[fullscreenTask.token.asBinder()]?.windowingMode)
+ .isEqualTo(WINDOWING_MODE_UNDEFINED)
}
private fun connectExternalDisplay() {
@@ -266,18 +285,27 @@ class DesktopDisplayModeControllerTest(
whenever(inputManager.isInTabletMode()).thenReturn(status.value)
}
- private class ExtendedDisplaySettingsSession(
- private val contentResolver: ContentResolver,
- private val overrideValue: Int,
- ) : AutoCloseable {
+ private fun setExtendedMode(enabled: Boolean) {
+ if (DisplayFlags.enableDisplayContentModeManagement()) {
+ doReturn(enabled).`when` {
+ DesktopModeStatus.isDesktopModeSupportedOnDisplay(context, externalDisplay)
+ }
+ } else {
+ Settings.Global.putInt(
+ context.contentResolver,
+ DEVELOPMENT_FORCE_DESKTOP_MODE_ON_EXTERNAL_DISPLAYS,
+ if (enabled) 1 else 0,
+ )
+ }
+ }
+
+ private class ExtendedDisplaySettingsRestoreSession(
+ private val contentResolver: ContentResolver
+ ) {
private val settingName = DEVELOPMENT_FORCE_DESKTOP_MODE_ON_EXTERNAL_DISPLAYS
private val initialValue = Settings.Global.getInt(contentResolver, settingName, 0)
- init {
- Settings.Global.putInt(contentResolver, settingName, overrideValue)
- }
-
- override fun close() {
+ fun restore() {
Settings.Global.putInt(contentResolver, settingName, initialValue)
}
}
@@ -287,7 +315,8 @@ class DesktopDisplayModeControllerTest(
context: TestParameterValuesProvider.Context
): List<FlagsParameterization> {
return FlagsParameterization.allCombinationsOf(
- Flags.FLAG_FORM_FACTOR_BASED_DESKTOP_FIRST_SWITCH
+ Flags.FLAG_FORM_FACTOR_BASED_DESKTOP_FIRST_SWITCH,
+ DisplayFlags.FLAG_ENABLE_DISPLAY_CONTENT_MODE_MANAGEMENT,
)
}
}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeEventLoggerTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeEventLoggerTest.kt
index 8a5acfa70f50..695cf600b359 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeEventLoggerTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeEventLoggerTest.kt
@@ -23,7 +23,7 @@ import com.android.dx.mockito.inline.extended.ExtendedMockito.clearInvocations
import com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn
import com.android.dx.mockito.inline.extended.ExtendedMockito.staticMockMarker
import com.android.dx.mockito.inline.extended.ExtendedMockito.verify
-import com.android.dx.mockito.inline.extended.ExtendedMockito.verifyZeroInteractions
+import com.android.dx.mockito.inline.extended.ExtendedMockito.verifyNoMoreInteractions
import com.android.internal.util.FrameworkStatsLog
import com.android.modules.utils.testing.ExtendedMockitoRule
import com.android.window.flags.Flags
@@ -102,7 +102,7 @@ class DesktopModeEventLoggerTest : ShellTestCase() {
eq(sessionId),
)
}
- verifyZeroInteractions(staticMockMarker(EventLogTags::class.java))
+ verifyNoMoreInteractions(staticMockMarker(EventLogTags::class.java))
}
@Test
@@ -127,7 +127,7 @@ class DesktopModeEventLoggerTest : ShellTestCase() {
eq(sessionId),
)
}
- verifyZeroInteractions(staticMockMarker(EventLogTags::class.java))
+ verifyNoMoreInteractions(staticMockMarker(EventLogTags::class.java))
}
@Test
@@ -135,7 +135,7 @@ class DesktopModeEventLoggerTest : ShellTestCase() {
desktopModeEventLogger.logSessionExit(ExitReason.DRAG_TO_EXIT)
verifyNoLogging()
- verifyZeroInteractions(staticMockMarker(EventLogTags::class.java))
+ verifyNoMoreInteractions(staticMockMarker(EventLogTags::class.java))
}
@Test
@@ -157,7 +157,7 @@ class DesktopModeEventLoggerTest : ShellTestCase() {
eq(sessionId),
)
}
- verifyZeroInteractions(staticMockMarker(EventLogTags::class.java))
+ verifyNoMoreInteractions(staticMockMarker(EventLogTags::class.java))
assertThat(desktopModeEventLogger.currentSessionId.get()).isEqualTo(NO_SESSION_ID)
}
@@ -166,7 +166,7 @@ class DesktopModeEventLoggerTest : ShellTestCase() {
desktopModeEventLogger.logTaskAdded(TASK_UPDATE)
verifyNoLogging()
- verifyZeroInteractions(staticMockMarker(EventLogTags::class.java))
+ verifyNoMoreInteractions(staticMockMarker(EventLogTags::class.java))
}
@Test
@@ -205,7 +205,7 @@ class DesktopModeEventLoggerTest : ShellTestCase() {
eq(UNSET_FOCUS_REASON),
)
}
- verifyZeroInteractions(staticMockMarker(EventLogTags::class.java))
+ verifyNoMoreInteractions(staticMockMarker(EventLogTags::class.java))
}
@Test
@@ -213,7 +213,7 @@ class DesktopModeEventLoggerTest : ShellTestCase() {
desktopModeEventLogger.logTaskRemoved(TASK_UPDATE)
verifyNoLogging()
- verifyZeroInteractions(staticMockMarker(EventLogTags::class.java))
+ verifyNoMoreInteractions(staticMockMarker(EventLogTags::class.java))
}
@Test
@@ -252,7 +252,7 @@ class DesktopModeEventLoggerTest : ShellTestCase() {
eq(UNSET_FOCUS_REASON),
)
}
- verifyZeroInteractions(staticMockMarker(EventLogTags::class.java))
+ verifyNoMoreInteractions(staticMockMarker(EventLogTags::class.java))
}
@Test
@@ -260,7 +260,7 @@ class DesktopModeEventLoggerTest : ShellTestCase() {
desktopModeEventLogger.logTaskInfoChanged(TASK_UPDATE)
verifyNoLogging()
- verifyZeroInteractions(staticMockMarker(EventLogTags::class.java))
+ verifyNoMoreInteractions(staticMockMarker(EventLogTags::class.java))
}
@Test
@@ -302,7 +302,7 @@ class DesktopModeEventLoggerTest : ShellTestCase() {
eq(UNSET_FOCUS_REASON),
)
}
- verifyZeroInteractions(staticMockMarker(EventLogTags::class.java))
+ verifyNoMoreInteractions(staticMockMarker(EventLogTags::class.java))
}
@Test
@@ -346,7 +346,7 @@ class DesktopModeEventLoggerTest : ShellTestCase() {
eq(UNSET_FOCUS_REASON),
)
}
- verifyZeroInteractions(staticMockMarker(EventLogTags::class.java))
+ verifyNoMoreInteractions(staticMockMarker(EventLogTags::class.java))
}
@Test
@@ -390,7 +390,7 @@ class DesktopModeEventLoggerTest : ShellTestCase() {
eq(UNSET_FOCUS_REASON),
)
}
- verifyZeroInteractions(staticMockMarker(EventLogTags::class.java))
+ verifyNoMoreInteractions(staticMockMarker(EventLogTags::class.java))
}
@Test
@@ -434,7 +434,7 @@ class DesktopModeEventLoggerTest : ShellTestCase() {
eq(FocusReason.UNKNOWN.reason),
)
}
- verifyZeroInteractions(staticMockMarker(EventLogTags::class.java))
+ verifyNoMoreInteractions(staticMockMarker(EventLogTags::class.java))
}
@Test
@@ -446,7 +446,7 @@ class DesktopModeEventLoggerTest : ShellTestCase() {
)
verifyNoLogging()
- verifyZeroInteractions(staticMockMarker(EventLogTags::class.java))
+ verifyNoMoreInteractions(staticMockMarker(EventLogTags::class.java))
}
@Test
@@ -485,7 +485,7 @@ class DesktopModeEventLoggerTest : ShellTestCase() {
)
verifyNoLogging()
- verifyZeroInteractions(staticMockMarker(EventLogTags::class.java))
+ verifyNoMoreInteractions(staticMockMarker(EventLogTags::class.java))
}
@Test
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeLoggerTransitionObserverTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeLoggerTransitionObserverTest.kt
index b7d25b5255f8..bd37610ae65b 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeLoggerTransitionObserverTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeLoggerTransitionObserverTest.kt
@@ -83,7 +83,7 @@ import org.mockito.kotlin.same
import org.mockito.kotlin.spy
import org.mockito.kotlin.times
import org.mockito.kotlin.verify
-import org.mockito.kotlin.verifyZeroInteractions
+import org.mockito.kotlin.verifyNoMoreInteractions
import org.mockito.kotlin.whenever
/**
@@ -596,7 +596,7 @@ class DesktopModeLoggerTransitionObserverTest : ShellTestCase() {
.logTaskRemoved(
eq(DEFAULT_TASK_UPDATE.copy(minimizeReason = MinimizeReason.MINIMIZE_BUTTON))
)
- verifyZeroInteractions(desktopModeEventLogger)
+ verifyNoMoreInteractions(desktopModeEventLogger)
}
@Test
@@ -668,7 +668,7 @@ class DesktopModeLoggerTransitionObserverTest : ShellTestCase() {
.logTaskInfoChanged(
eq(DEFAULT_TASK_UPDATE.copy(taskX = DEFAULT_TASK_X + 100, visibleTaskCount = 1))
)
- verifyZeroInteractions(desktopModeEventLogger)
+ verifyNoMoreInteractions(desktopModeEventLogger)
}
@Test
@@ -701,7 +701,7 @@ class DesktopModeLoggerTransitionObserverTest : ShellTestCase() {
)
)
)
- verifyZeroInteractions(desktopModeEventLogger)
+ verifyNoMoreInteractions(desktopModeEventLogger)
}
@Test
@@ -729,7 +729,7 @@ class DesktopModeLoggerTransitionObserverTest : ShellTestCase() {
)
)
)
- verifyZeroInteractions(desktopModeEventLogger)
+ verifyNoMoreInteractions(desktopModeEventLogger)
}
@Test
@@ -753,7 +753,7 @@ class DesktopModeLoggerTransitionObserverTest : ShellTestCase() {
.logTaskInfoChanged(
eq(DEFAULT_TASK_UPDATE.copy(taskX = DEFAULT_TASK_X + 100, visibleTaskCount = 2))
)
- verifyZeroInteractions(desktopModeEventLogger)
+ verifyNoMoreInteractions(desktopModeEventLogger)
// task 2 resize
val newTaskInfo2 =
@@ -781,7 +781,7 @@ class DesktopModeLoggerTransitionObserverTest : ShellTestCase() {
)
)
)
- verifyZeroInteractions(desktopModeEventLogger)
+ verifyNoMoreInteractions(desktopModeEventLogger)
}
@Test
@@ -892,14 +892,14 @@ class DesktopModeLoggerTransitionObserverTest : ShellTestCase() {
eq(taskUpdate.visibleTaskCount.toString()),
)
}
- verifyZeroInteractions(desktopModeEventLogger)
+ verifyNoMoreInteractions(desktopModeEventLogger)
}
private fun verifyTaskRemovedAndExitLogging(exitReason: ExitReason, taskUpdate: TaskUpdate) {
assertFalse(transitionObserver.isSessionActive)
verify(desktopModeEventLogger, times(1)).logTaskRemoved(eq(taskUpdate))
verify(desktopModeEventLogger, times(1)).logSessionExit(eq(exitReason))
- verifyZeroInteractions(desktopModeEventLogger)
+ verifyNoMoreInteractions(desktopModeEventLogger)
}
private companion object {
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 f84a1a38bdfc..c455205f6411 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
@@ -275,6 +275,18 @@ class DesktopRepositoryTest(flags: FlagsParameterization) : ShellTestCase() {
}
@Test
+ @EnableFlags(FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND)
+ fun removeActiveTask_excludingDesk_leavesTaskInDesk() {
+ repo.addDesk(displayId = 2, deskId = 11)
+ repo.addDesk(displayId = 3, deskId = 12)
+ repo.addTaskToDesk(displayId = 3, deskId = 12, taskId = 100, isVisible = true)
+
+ repo.removeActiveTask(taskId = 100, excludedDeskId = 12)
+
+ assertThat(repo.getActiveTaskIdsInDesk(12)).contains(100)
+ }
+
+ @Test
fun isActiveTask_nonExistingTask_returnsFalse() {
assertThat(repo.isActiveTask(99)).isFalse()
}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksControllerTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksControllerTest.kt
index 3835c02678e7..8d8140c47c1a 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksControllerTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksControllerTest.kt
@@ -57,6 +57,7 @@ import android.platform.test.flag.junit.FlagsParameterization
import android.testing.TestableContext
import android.view.Display
import android.view.Display.DEFAULT_DISPLAY
+import android.view.Display.INVALID_DISPLAY
import android.view.DragEvent
import android.view.Gravity
import android.view.MotionEvent
@@ -5014,6 +5015,7 @@ class DesktopTasksControllerTest(flags: FlagsParameterization) : ShellTestCase()
@Test
@EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY)
+ @DisableFlags(Flags.FLAG_ENABLE_MODALS_FULLSCREEN_WITH_PERMISSION)
fun handleRequest_closeTransition_singleTaskNoToken_secondaryDisplay_launchesHome() {
taskRepository.addDesk(displayId = SECOND_DISPLAY, deskId = SECOND_DISPLAY)
taskRepository.setActiveDesk(displayId = SECOND_DISPLAY, deskId = SECOND_DISPLAY)
@@ -7351,6 +7353,14 @@ class DesktopTasksControllerTest(flags: FlagsParameterization) : ShellTestCase()
}
@Test
+ @EnableFlags(Flags.FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND)
+ fun testCreateDesk_invalidDisplay_dropsRequest() {
+ controller.createDesk(INVALID_DISPLAY)
+
+ verify(desksOrganizer, never()).createDesk(any(), any())
+ }
+
+ @Test
@EnableFlags(
Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY,
Flags.FLAG_ENABLE_DESKTOP_WALLPAPER_ACTIVITY_FOR_SYSTEM_USER,
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 a7dc706eb6c9..ac9a509ac6cb 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
@@ -336,6 +336,50 @@ class DesktopTasksTransitionObserverTest {
}
@Test
+ @EnableFlags(
+ Flags.FLAG_ENABLE_DESKTOP_WINDOWING_MODALS_POLICY,
+ Flags.FLAG_INCLUDE_TOP_TRANSPARENT_FULLSCREEN_TASK_IN_DESKTOP_HEURISTIC,
+ )
+ fun nonTopTransparentTaskOpened_clearTopTransparentTaskIdFromRepository() {
+ val mockTransition = Mockito.mock(IBinder::class.java)
+ val topTransparentTask = createTaskInfo(1)
+ val nonTopTransparentTask = createTaskInfo(2)
+ whenever(taskRepository.getTopTransparentFullscreenTaskId(any()))
+ .thenReturn(topTransparentTask.taskId)
+
+ transitionObserver.onTransitionReady(
+ transition = mockTransition,
+ info = createOpenChangeTransition(nonTopTransparentTask),
+ startTransaction = mock(),
+ finishTransaction = mock(),
+ )
+
+ verify(taskRepository).clearTopTransparentFullscreenTaskId(topTransparentTask.displayId)
+ }
+
+ @Test
+ @EnableFlags(
+ Flags.FLAG_ENABLE_DESKTOP_WINDOWING_MODALS_POLICY,
+ Flags.FLAG_INCLUDE_TOP_TRANSPARENT_FULLSCREEN_TASK_IN_DESKTOP_HEURISTIC,
+ )
+ fun nonTopTransparentTaskSentToFront_clearTopTransparentTaskIdFromRepository() {
+ val mockTransition = Mockito.mock(IBinder::class.java)
+ val topTransparentTask = createTaskInfo(1)
+ val nonTopTransparentTask = createTaskInfo(2)
+ whenever(taskRepository.getTopTransparentFullscreenTaskId(any()))
+ .thenReturn(topTransparentTask.taskId)
+
+ transitionObserver.onTransitionReady(
+ transition = mockTransition,
+ info = createToFrontTransition(nonTopTransparentTask),
+ startTransaction = mock(),
+ finishTransaction = mock(),
+ )
+
+ verify(taskRepository).clearTopTransparentFullscreenTaskId(topTransparentTask.displayId)
+ }
+
+ @Test
fun transitCloseWallpaper_wallpaperActivityVisibilitySaved() {
val wallpaperTask = createWallpaperTaskInfo()
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DragToDesktopTransitionHandlerTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DragToDesktopTransitionHandlerTest.kt
index dc96694b814d..4e2994c27729 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DragToDesktopTransitionHandlerTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DragToDesktopTransitionHandlerTest.kt
@@ -66,7 +66,7 @@ import org.mockito.kotlin.mock
import org.mockito.kotlin.never
import org.mockito.kotlin.times
import org.mockito.kotlin.verify
-import org.mockito.kotlin.verifyZeroInteractions
+import org.mockito.kotlin.verifyNoMoreInteractions
import org.mockito.kotlin.whenever
import org.mockito.quality.Strictness
@@ -457,7 +457,7 @@ class DragToDesktopTransitionHandlerTest : ShellTestCase() {
)
// No need to animate the cancel since the start animation couldn't even start.
- verifyZeroInteractions(dragAnimator)
+ verifyNoMoreInteractions(dragAnimator)
}
@Test
@@ -508,7 +508,7 @@ class DragToDesktopTransitionHandlerTest : ShellTestCase() {
)
// Should NOT have any transaction changes
- verifyZeroInteractions(mergedStartTransaction)
+ verifyNoMoreInteractions(mergedStartTransaction)
// Should NOT merge animation
verify(finishCallback, never()).onTransitionFinished(any())
}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/VisualIndicatorViewContainerTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/VisualIndicatorViewContainerTest.kt
index c7518d5914b4..3983bfbb2080 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/VisualIndicatorViewContainerTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/VisualIndicatorViewContainerTest.kt
@@ -54,7 +54,7 @@ import org.mockito.kotlin.mock
import org.mockito.kotlin.never
import org.mockito.kotlin.spy
import org.mockito.kotlin.verify
-import org.mockito.kotlin.verifyZeroInteractions
+import org.mockito.kotlin.verifyNoMoreInteractions
import org.mockito.kotlin.whenever
/**
@@ -111,7 +111,7 @@ class VisualIndicatorViewContainerTest : ShellTestCase() {
eq(DesktopModeVisualIndicator.IndicatorType.TO_FULLSCREEN_INDICATOR),
)
// Assert fadeIn, fadeOut, and animateIndicatorType were not called.
- verifyZeroInteractions(spyViewContainer)
+ verifyNoMoreInteractions(spyViewContainer)
}
@Test
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/compatui/SystemModalsTransitionHandlerTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/compatui/SystemModalsTransitionHandlerTest.kt
index 7560945856ec..dc973d0fda77 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/compatui/SystemModalsTransitionHandlerTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/compatui/SystemModalsTransitionHandlerTest.kt
@@ -90,7 +90,7 @@ class SystemModalsTransitionHandlerTest : ShellTestCase() {
whenever(packageManager.getHomeActivities(ArrayList())).thenReturn(componentName)
desktopModeCompatPolicy = DesktopModeCompatPolicy(spyContext)
transitionHandler = createTransitionHandler()
- allowOverlayPermission(arrayOf(SYSTEM_ALERT_WINDOW))
+ allowOverlayPermissionForAllUsers(arrayOf(SYSTEM_ALERT_WINDOW))
}
private fun createTransitionHandler() =
@@ -200,10 +200,16 @@ class SystemModalsTransitionHandlerTest : ShellTestCase() {
.isTrue()
}
- fun allowOverlayPermission(permissions: Array<String>) {
+ fun allowOverlayPermissionForAllUsers(permissions: Array<String>) {
val packageInfo = mock<PackageInfo>()
packageInfo.requestedPermissions = permissions
- whenever(packageManager.getPackageInfo(anyString(), eq(PackageManager.GET_PERMISSIONS)))
+ whenever(
+ packageManager.getPackageInfoAsUser(
+ anyString(),
+ eq(PackageManager.GET_PERMISSIONS),
+ anyInt(),
+ )
+ )
.thenReturn(packageInfo)
}
}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip2/animation/PipAlphaAnimatorTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip2/animation/PipAlphaAnimatorTest.java
index 14f9ffc52a66..2bd9afcef1bb 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip2/animation/PipAlphaAnimatorTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip2/animation/PipAlphaAnimatorTest.java
@@ -24,7 +24,7 @@ import static org.mockito.Mockito.atLeastOnce;
import static org.mockito.Mockito.clearInvocations;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.verifyZeroInteractions;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
import static org.mockito.Mockito.when;
import android.content.Context;
@@ -107,7 +107,7 @@ public class PipAlphaAnimatorTest {
});
verify(mMockStartCallback).run();
- verifyZeroInteractions(mMockEndCallback);
+ verifyNoMoreInteractions(mMockEndCallback);
}
@Test
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip2/animation/PipEnterAnimatorTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip2/animation/PipEnterAnimatorTest.java
index 72c466663a56..fa7ab9521dac 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip2/animation/PipEnterAnimatorTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip2/animation/PipEnterAnimatorTest.java
@@ -24,7 +24,7 @@ import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.atLeastOnce;
import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.verifyZeroInteractions;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
import static org.mockito.Mockito.when;
import android.content.Context;
@@ -117,7 +117,7 @@ public class PipEnterAnimatorTest {
});
verify(mMockStartCallback).run();
- verifyZeroInteractions(mMockEndCallback);
+ verifyNoMoreInteractions(mMockEndCallback);
// Check corner and shadow radii were set
verify(mMockAnimateTransaction, atLeastOnce())
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip2/animation/PipExpandAnimatorTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip2/animation/PipExpandAnimatorTest.java
index b816f0ef041e..97133bedfa2d 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip2/animation/PipExpandAnimatorTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip2/animation/PipExpandAnimatorTest.java
@@ -21,7 +21,7 @@ import static org.mockito.ArgumentMatchers.anyFloat;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.Mockito.clearInvocations;
import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.verifyZeroInteractions;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
import static org.mockito.Mockito.when;
import android.content.Context;
@@ -143,7 +143,7 @@ public class PipExpandAnimatorTest {
});
verify(mMockStartCallback).run();
- verifyZeroInteractions(mMockEndCallback);
+ verifyNoMoreInteractions(mMockEndCallback);
}
@Test
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip2/animation/PipResizeAnimatorTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip2/animation/PipResizeAnimatorTest.java
index 23fbad05ec99..c99ca6dd7065 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip2/animation/PipResizeAnimatorTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip2/animation/PipResizeAnimatorTest.java
@@ -22,7 +22,7 @@ import static org.mockito.ArgumentMatchers.anyFloat;
import static org.mockito.Mockito.atLeastOnce;
import static org.mockito.Mockito.clearInvocations;
import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.verifyZeroInteractions;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
import static org.mockito.Mockito.when;
import static org.mockito.kotlin.MatchersKt.eq;
@@ -118,7 +118,7 @@ public class PipResizeAnimatorTest {
});
verify(mMockStartCallback).run();
- verifyZeroInteractions(mMockEndCallback);
+ verifyNoMoreInteractions(mMockEndCallback);
}
@Test
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip2/phone/PipTaskListenerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip2/phone/PipTaskListenerTest.java
index 5029371c3419..b6894fd0a9fa 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip2/phone/PipTaskListenerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip2/phone/PipTaskListenerTest.java
@@ -30,7 +30,7 @@ import static org.mockito.kotlin.MatchersKt.eq;
import static org.mockito.kotlin.VerificationKt.clearInvocations;
import static org.mockito.kotlin.VerificationKt.times;
import static org.mockito.kotlin.VerificationKt.verify;
-import static org.mockito.kotlin.VerificationKt.verifyZeroInteractions;
+import static org.mockito.kotlin.VerificationKt.verifyNoMoreInteractions;
import android.app.ActivityManager;
import android.app.PendingIntent;
@@ -176,7 +176,7 @@ public class PipTaskListenerTest {
mPipTaskListener.setPictureInPictureParams(getPictureInPictureParams(
aspectRatio, action1));
- verifyZeroInteractions(mMockPipParamsChangedCallback);
+ verifyNoMoreInteractions(mMockPipParamsChangedCallback);
}
@Test
@@ -193,7 +193,7 @@ public class PipTaskListenerTest {
clearInvocations(mMockPipParamsChangedCallback);
mPipTaskListener.onTaskInfoChanged(new ActivityManager.RunningTaskInfo());
- verifyZeroInteractions(mMockPipParamsChangedCallback);
+ verifyNoMoreInteractions(mMockPipParamsChangedCallback);
verify(mMockPipTransitionState, times(0))
.setOnIdlePipTransitionStateRunnable(any(Runnable.class));
}
@@ -245,7 +245,7 @@ public class PipTaskListenerTest {
mPipTaskListener.onTaskInfoChanged(getTaskInfo(aspectRatio, action1));
verify(mMockPipTransitionState).setOnIdlePipTransitionStateRunnable(any(Runnable.class));
- verifyZeroInteractions(mMockPipParamsChangedCallback);
+ verifyNoMoreInteractions(mMockPipParamsChangedCallback);
}
@Test
@@ -262,7 +262,7 @@ public class PipTaskListenerTest {
clearInvocations(mMockPipParamsChangedCallback);
mPipTaskListener.onTaskInfoChanged(getTaskInfo(aspectRatio, action1));
- verifyZeroInteractions(mMockPipParamsChangedCallback);
+ verifyNoMoreInteractions(mMockPipParamsChangedCallback);
verify(mMockPipTransitionState, times(0))
.setOnIdlePipTransitionStateRunnable(any(Runnable.class));
}
@@ -319,7 +319,7 @@ public class PipTaskListenerTest {
PipTransitionState.SCHEDULED_BOUNDS_CHANGE,
extras);
- verifyZeroInteractions(mMockPipScheduler);
+ verifyNoMoreInteractions(mMockPipScheduler);
}
@Test
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip2/phone/PipUiStateChangeControllerTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip2/phone/PipUiStateChangeControllerTests.java
index 82cdfd52d2db..51de50da6921 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip2/phone/PipUiStateChangeControllerTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip2/phone/PipUiStateChangeControllerTests.java
@@ -20,7 +20,7 @@ import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.verifyZeroInteractions;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
import static org.mockito.Mockito.when;
import android.app.Flags;
@@ -82,7 +82,7 @@ public class PipUiStateChangeControllerTests {
mPipUiStateChangeController.onPipTransitionStateChanged(
PipTransitionState.SWIPING_TO_PIP, PipTransitionState.ENTERING_PIP, Bundle.EMPTY);
- verifyZeroInteractions(mPictureInPictureUiStateConsumer);
+ verifyNoMoreInteractions(mPictureInPictureUiStateConsumer);
}
@Test
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/shared/desktopmode/DesktopModeCompatPolicyTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/shared/desktopmode/DesktopModeCompatPolicyTest.kt
index 5ac680048a7e..12785c03aa9f 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/shared/desktopmode/DesktopModeCompatPolicyTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/shared/desktopmode/DesktopModeCompatPolicyTest.kt
@@ -42,6 +42,7 @@ import org.junit.Before
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
+import org.mockito.ArgumentMatchers.anyInt
import org.mockito.ArgumentMatchers.anyString
import org.mockito.kotlin.any
import org.mockito.kotlin.eq
@@ -87,7 +88,7 @@ class DesktopModeCompatPolicyTest : ShellTestCase() {
@Test
@EnableFlags(Flags.FLAG_ENABLE_MODALS_FULLSCREEN_WITH_PERMISSION)
fun testIsTopActivityExemptWithPermission_onlyTransparentActivitiesInStack() {
- allowOverlayPermission(arrayOf(SYSTEM_ALERT_WINDOW))
+ allowOverlayPermissionForAllUsers(arrayOf(SYSTEM_ALERT_WINDOW))
assertTrue(desktopModeCompatPolicy.isTopActivityExemptFromDesktopWindowing(
createFreeformTask(/* displayId */ 0)
.apply {
@@ -101,7 +102,7 @@ class DesktopModeCompatPolicyTest : ShellTestCase() {
@Test
@EnableFlags(Flags.FLAG_ENABLE_MODALS_FULLSCREEN_WITH_PERMISSION)
fun testIsTopActivityExemptWithNoPermission_onlyTransparentActivitiesInStack() {
- allowOverlayPermission(arrayOf())
+ allowOverlayPermissionForAllUsers(arrayOf())
assertFalse(desktopModeCompatPolicy.isTopActivityExemptFromDesktopWindowing(
createFreeformTask(/* displayId */ 0)
.apply {
@@ -115,7 +116,7 @@ class DesktopModeCompatPolicyTest : ShellTestCase() {
@Test
@EnableFlags(Flags.FLAG_ENABLE_MODALS_FULLSCREEN_WITH_PERMISSION)
fun testIsTopActivityExemptCachedPermissionCheckIsUsed() {
- allowOverlayPermission(arrayOf())
+ allowOverlayPermissionForAllUsers(arrayOf())
assertFalse(desktopModeCompatPolicy.isTopActivityExemptFromDesktopWindowing(
createFreeformTask(/* displayId */ 0)
.apply {
@@ -123,6 +124,7 @@ class DesktopModeCompatPolicyTest : ShellTestCase() {
isTopActivityNoDisplay = false
numActivities = 1
baseActivity = baseActivityTest
+ userId = 10
}))
assertFalse(desktopModeCompatPolicy.isTopActivityExemptFromDesktopWindowing(
createFreeformTask(/* displayId */ 0)
@@ -131,10 +133,26 @@ class DesktopModeCompatPolicyTest : ShellTestCase() {
isTopActivityNoDisplay = false
numActivities = 1
baseActivity = baseActivityTest
+ userId = 10
}))
- verify(packageManager, times(1)).getPackageInfo(
+ assertFalse(desktopModeCompatPolicy.isTopActivityExemptFromDesktopWindowing(
+ createFreeformTask(/* displayId */ 0)
+ .apply {
+ isActivityStackTransparent = true
+ isTopActivityNoDisplay = false
+ numActivities = 1
+ baseActivity = baseActivityTest
+ userId = 0
+ }))
+ verify(packageManager, times(1)).getPackageInfoAsUser(
+ eq("com.test.dummypackage"),
+ eq(PackageManager.GET_PERMISSIONS),
+ eq(10)
+ )
+ verify(packageManager, times(1)).getPackageInfoAsUser(
eq("com.test.dummypackage"),
- eq(PackageManager.GET_PERMISSIONS)
+ eq(PackageManager.GET_PERMISSIONS),
+ eq(0)
)
}
@@ -284,13 +302,14 @@ class DesktopModeCompatPolicyTest : ShellTestCase() {
}
}
- fun allowOverlayPermission(permissions: Array<String>) {
+ fun allowOverlayPermissionForAllUsers(permissions: Array<String>) {
val packageInfo = mock<PackageInfo>()
packageInfo.requestedPermissions = permissions
whenever(
- packageManager.getPackageInfo(
+ packageManager.getPackageInfoAsUser(
anyString(),
- eq(PackageManager.GET_PERMISSIONS)
+ eq(PackageManager.GET_PERMISSIONS),
+ anyInt(),
)
).thenReturn(packageInfo)
}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModelAppHandleOnlyTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModelAppHandleOnlyTest.kt
index 067dcec5d65d..b1f92411c5a3 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModelAppHandleOnlyTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModelAppHandleOnlyTest.kt
@@ -28,6 +28,7 @@ import android.testing.TestableLooper.RunWithLooper
import android.view.Display
import android.view.Display.DEFAULT_DISPLAY
import android.view.SurfaceControl
+import android.view.WindowManager.TRANSIT_CHANGE
import androidx.test.filters.SmallTest
import com.android.dx.mockito.inline.extended.ExtendedMockito.anyBoolean
import com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn
@@ -109,7 +110,7 @@ class DesktopModeWindowDecorViewModelAppHandleOnlyTest :
onTaskOpening(task, taskSurface)
assertTrue(windowDecorByTaskIdSpy.contains(task.taskId))
task.setActivityType(ACTIVITY_TYPE_UNDEFINED)
- onTaskChanging(task, taskSurface)
+ onTaskChanging(task, taskSurface, TRANSIT_CHANGE)
assertFalse(windowDecorByTaskIdSpy.contains(task.taskId))
verify(decoration).close()
@@ -165,7 +166,7 @@ class DesktopModeWindowDecorViewModelAppHandleOnlyTest :
setLargeScreen(false)
setUpMockDecorationForTask(task)
- onTaskChanging(task, taskSurface)
+ onTaskChanging(task, taskSurface, TRANSIT_CHANGE)
assertFalse(windowDecorByTaskIdSpy.contains(task.taskId))
}
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 d69509faf4ec..ad3426e82805 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
@@ -51,6 +51,7 @@ import android.view.SurfaceView
import android.view.View
import android.view.ViewRootImpl
import android.view.WindowInsets.Type.statusBars
+import android.view.WindowManager.TRANSIT_CHANGE
import android.window.WindowContainerTransaction
import android.window.WindowContainerTransaction.HierarchyOp
import androidx.test.filters.SmallTest
@@ -134,7 +135,7 @@ class DesktopModeWindowDecorViewModelTests : DesktopModeWindowDecorViewModelTest
task.setWindowingMode(WINDOWING_MODE_UNDEFINED)
task.setActivityType(ACTIVITY_TYPE_UNDEFINED)
- onTaskChanging(task, taskSurface)
+ onTaskChanging(task, taskSurface, TRANSIT_CHANGE)
assertFalse(windowDecorByTaskIdSpy.contains(task.taskId))
verify(decoration).close()
@@ -149,12 +150,12 @@ class DesktopModeWindowDecorViewModelTests : DesktopModeWindowDecorViewModelTest
val taskSurface = SurfaceControl()
setUpMockDecorationForTask(task)
- onTaskChanging(task, taskSurface)
+ onTaskChanging(task, taskSurface, TRANSIT_CHANGE)
assertFalse(windowDecorByTaskIdSpy.contains(task.taskId))
task.setWindowingMode(WINDOWING_MODE_FREEFORM)
task.setActivityType(ACTIVITY_TYPE_STANDARD)
- onTaskChanging(task, taskSurface)
+ onTaskChanging(task, taskSurface, TRANSIT_CHANGE)
assertTrue(windowDecorByTaskIdSpy.contains(task.taskId))
}
@@ -758,20 +759,6 @@ class DesktopModeWindowDecorViewModelTests : DesktopModeWindowDecorViewModelTest
}
@Test
- fun testDecor_onClickToSplitScreen_disposesStatusBarInputLayer() {
- val toSplitScreenListenerCaptor = forClass(Function0::class.java)
- as ArgumentCaptor<Function0<Unit>>
- val decor = createOpenTaskDecoration(
- windowingMode = WINDOWING_MODE_MULTI_WINDOW,
- onToSplitScreenClickListenerCaptor = toSplitScreenListenerCaptor
- )
-
- toSplitScreenListenerCaptor.value.invoke()
-
- verify(decor).disposeStatusBarInputLayer()
- }
-
- @Test
fun testDecor_onClickToOpenBrowser_closeMenus() {
val openInBrowserListenerCaptor = forClass(Consumer::class.java)
as ArgumentCaptor<Consumer<Intent>>
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 a1f40fdefee9..4c9c2f14d805 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
@@ -84,6 +84,7 @@ import com.android.wm.shell.windowdecor.common.WindowDecorTaskResourceLoader
import com.android.wm.shell.windowdecor.common.viewhost.WindowDecorViewHost
import com.android.wm.shell.windowdecor.common.viewhost.WindowDecorViewHostSupplier
import com.android.wm.shell.windowdecor.tiling.DesktopTilingDecorViewModel
+import com.android.wm.shell.windowdecor.viewholder.AppHandleViewHolder
import com.android.wm.shell.windowdecor.viewholder.AppHeaderViewHolder
import org.junit.After
import org.mockito.Mockito
@@ -125,6 +126,7 @@ open class DesktopModeWindowDecorViewModelTestsBase : ShellTestCase() {
protected val mockShellController = mock<ShellController>()
protected val testShellExecutor = TestShellExecutor()
protected val mockAppHeaderViewHolderFactory = mock<AppHeaderViewHolder.Factory>()
+ protected val mockAppHandleViewHolderFactory = mock<AppHandleViewHolder.Factory>()
protected val mockRootTaskDisplayAreaOrganizer = mock<RootTaskDisplayAreaOrganizer>()
protected val mockShellCommandHandler = mock<ShellCommandHandler>()
protected val mockWindowManager = mock<IWindowManager>()
@@ -222,6 +224,7 @@ open class DesktopModeWindowDecorViewModelTestsBase : ShellTestCase() {
mockInputMonitorFactory,
transactionFactory,
mockAppHeaderViewHolderFactory,
+ mockAppHandleViewHolderFactory,
mockRootTaskDisplayAreaOrganizer,
windowDecorByTaskIdSpy,
mockInteractionJankMonitor,
@@ -331,7 +334,7 @@ open class DesktopModeWindowDecorViewModelTestsBase : ShellTestCase() {
mockDesktopModeWindowDecorFactory.create(
any(), any(), any(), any(), any(), any(), any(), eq(task), any(), any(), any(),
any(), any(), any(), any(), any(), any(), any(), any(), any(), any(), any(),
- any(), any(), any())
+ any(), any(), any(), any())
).thenReturn(decoration)
decoration.mTaskInfo = task
whenever(decoration.user).thenReturn(mockUserHandle)
@@ -353,12 +356,17 @@ open class DesktopModeWindowDecorViewModelTestsBase : ShellTestCase() {
)
}
- protected fun onTaskChanging(task: RunningTaskInfo, leash: SurfaceControl = SurfaceControl()) {
+ protected fun onTaskChanging(
+ task: RunningTaskInfo,
+ leash: SurfaceControl = SurfaceControl(),
+ changeMode: Int
+ ) {
desktopModeWindowDecorViewModel.onTaskChanging(
task,
leash,
StubTransaction(),
- StubTransaction()
+ StubTransaction(),
+ changeMode
)
}
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 878324937f1a..f37f2fb14bea 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
@@ -115,6 +115,7 @@ import com.android.wm.shell.windowdecor.WindowDecoration.RelayoutParams;
import com.android.wm.shell.windowdecor.common.WindowDecorTaskResourceLoader;
import com.android.wm.shell.windowdecor.common.viewhost.WindowDecorViewHost;
import com.android.wm.shell.windowdecor.common.viewhost.WindowDecorViewHostSupplier;
+import com.android.wm.shell.windowdecor.viewholder.AppHandleViewHolder;
import com.android.wm.shell.windowdecor.viewholder.AppHeaderViewHolder;
import kotlin.Unit;
@@ -171,6 +172,9 @@ public class DesktopModeWindowDecorationTests extends ShellTestCase {
private static final boolean DEFAULT_HAS_GLOBAL_FOCUS = true;
private static final boolean DEFAULT_SHOULD_IGNORE_CORNER_RADIUS = false;
private static final boolean DEFAULT_SHOULD_EXCLUDE_CAPTION_FROM_APP_BOUNDS = false;
+ private static final boolean DEFAULT_IS_RECENTS_TRANSITION_RUNNING = false;
+ private static final boolean DEFAULT_IS_MOVING_TO_BACK = false;
+
@Mock
private DisplayController mMockDisplayController;
@@ -191,8 +195,12 @@ public class DesktopModeWindowDecorationTests extends ShellTestCase {
@Mock
private AppHeaderViewHolder.Factory mMockAppHeaderViewHolderFactory;
@Mock
+ private AppHandleViewHolder.Factory mMockAppHandleViewHolderFactory;
+ @Mock
private AppHeaderViewHolder mMockAppHeaderViewHolder;
@Mock
+ private AppHandleViewHolder mMockAppHandleViewHolder;
+ @Mock
private RootTaskDisplayAreaOrganizer mMockRootTaskDisplayAreaOrganizer;
@Mock
private Supplier<SurfaceControl.Transaction> mMockTransactionSupplier;
@@ -301,6 +309,9 @@ public class DesktopModeWindowDecorationTests extends ShellTestCase {
when(mMockAppHeaderViewHolderFactory
.create(any(), any(), any(), any(), any(), any(), any(), any(), any()))
.thenReturn(mMockAppHeaderViewHolder);
+ when(mMockAppHandleViewHolderFactory
+ .create(any(), any(), any(), any(), any()))
+ .thenReturn(mMockAppHandleViewHolder);
when(mMockDesktopUserRepositories.getCurrent()).thenReturn(mDesktopRepository);
when(mMockDesktopUserRepositories.getProfile(anyInt())).thenReturn(mDesktopRepository);
when(mMockWindowDecorViewHostSupplier.acquire(any(), eq(defaultDisplay)))
@@ -421,7 +432,9 @@ public class DesktopModeWindowDecorationTests extends ShellTestCase {
DEFAULT_HAS_GLOBAL_FOCUS,
mExclusionRegion,
/* shouldIgnoreCornerRadius= */ true,
- DEFAULT_SHOULD_EXCLUDE_CAPTION_FROM_APP_BOUNDS);
+ DEFAULT_SHOULD_EXCLUDE_CAPTION_FROM_APP_BOUNDS,
+ DEFAULT_IS_RECENTS_TRANSITION_RUNNING,
+ DEFAULT_IS_MOVING_TO_BACK);
assertThat(relayoutParams.mCornerRadius).isEqualTo(INVALID_CORNER_RADIUS);
}
@@ -623,7 +636,9 @@ public class DesktopModeWindowDecorationTests extends ShellTestCase {
DEFAULT_HAS_GLOBAL_FOCUS,
mExclusionRegion,
DEFAULT_SHOULD_IGNORE_CORNER_RADIUS,
- /* shouldExcludeCaptionFromAppBounds */ true);
+ /* shouldExcludeCaptionFromAppBounds */ true,
+ DEFAULT_IS_RECENTS_TRANSITION_RUNNING,
+ DEFAULT_IS_MOVING_TO_BACK);
// Force consuming flags are disabled.
assertThat((relayoutParams.mInsetSourceFlags & FLAG_FORCE_CONSUMING) == 0).isTrue();
@@ -658,7 +673,9 @@ public class DesktopModeWindowDecorationTests extends ShellTestCase {
DEFAULT_HAS_GLOBAL_FOCUS,
mExclusionRegion,
DEFAULT_SHOULD_IGNORE_CORNER_RADIUS,
- DEFAULT_SHOULD_EXCLUDE_CAPTION_FROM_APP_BOUNDS);
+ DEFAULT_SHOULD_EXCLUDE_CAPTION_FROM_APP_BOUNDS,
+ DEFAULT_IS_RECENTS_TRANSITION_RUNNING,
+ DEFAULT_IS_MOVING_TO_BACK);
assertThat((relayoutParams.mInsetSourceFlags & FLAG_FORCE_CONSUMING) != 0).isTrue();
assertThat(
@@ -737,7 +754,9 @@ public class DesktopModeWindowDecorationTests extends ShellTestCase {
DEFAULT_HAS_GLOBAL_FOCUS,
mExclusionRegion,
DEFAULT_SHOULD_IGNORE_CORNER_RADIUS,
- DEFAULT_SHOULD_EXCLUDE_CAPTION_FROM_APP_BOUNDS);
+ DEFAULT_SHOULD_EXCLUDE_CAPTION_FROM_APP_BOUNDS,
+ DEFAULT_IS_RECENTS_TRANSITION_RUNNING,
+ DEFAULT_IS_MOVING_TO_BACK);
// Takes status bar inset as padding, ignores caption bar inset.
assertThat(relayoutParams.mCaptionTopPadding).isEqualTo(50);
@@ -765,7 +784,9 @@ public class DesktopModeWindowDecorationTests extends ShellTestCase {
DEFAULT_HAS_GLOBAL_FOCUS,
mExclusionRegion,
DEFAULT_SHOULD_IGNORE_CORNER_RADIUS,
- DEFAULT_SHOULD_EXCLUDE_CAPTION_FROM_APP_BOUNDS);
+ DEFAULT_SHOULD_EXCLUDE_CAPTION_FROM_APP_BOUNDS,
+ DEFAULT_IS_RECENTS_TRANSITION_RUNNING,
+ DEFAULT_IS_MOVING_TO_BACK);
assertThat(relayoutParams.mIsInsetSource).isFalse();
}
@@ -792,7 +813,9 @@ public class DesktopModeWindowDecorationTests extends ShellTestCase {
DEFAULT_HAS_GLOBAL_FOCUS,
mExclusionRegion,
DEFAULT_SHOULD_IGNORE_CORNER_RADIUS,
- DEFAULT_SHOULD_EXCLUDE_CAPTION_FROM_APP_BOUNDS);
+ DEFAULT_SHOULD_EXCLUDE_CAPTION_FROM_APP_BOUNDS,
+ DEFAULT_IS_RECENTS_TRANSITION_RUNNING,
+ DEFAULT_IS_MOVING_TO_BACK);
// Header is always shown because it's assumed the status bar is always visible.
assertThat(relayoutParams.mIsCaptionVisible).isTrue();
@@ -819,7 +842,9 @@ public class DesktopModeWindowDecorationTests extends ShellTestCase {
DEFAULT_HAS_GLOBAL_FOCUS,
mExclusionRegion,
DEFAULT_SHOULD_IGNORE_CORNER_RADIUS,
- DEFAULT_SHOULD_EXCLUDE_CAPTION_FROM_APP_BOUNDS);
+ DEFAULT_SHOULD_EXCLUDE_CAPTION_FROM_APP_BOUNDS,
+ DEFAULT_IS_RECENTS_TRANSITION_RUNNING,
+ DEFAULT_IS_MOVING_TO_BACK);
assertThat(relayoutParams.mIsCaptionVisible).isTrue();
}
@@ -845,7 +870,9 @@ public class DesktopModeWindowDecorationTests extends ShellTestCase {
DEFAULT_HAS_GLOBAL_FOCUS,
mExclusionRegion,
DEFAULT_SHOULD_IGNORE_CORNER_RADIUS,
- DEFAULT_SHOULD_EXCLUDE_CAPTION_FROM_APP_BOUNDS);
+ DEFAULT_SHOULD_EXCLUDE_CAPTION_FROM_APP_BOUNDS,
+ DEFAULT_IS_RECENTS_TRANSITION_RUNNING,
+ DEFAULT_IS_MOVING_TO_BACK);
assertThat(relayoutParams.mIsCaptionVisible).isFalse();
}
@@ -871,7 +898,9 @@ public class DesktopModeWindowDecorationTests extends ShellTestCase {
DEFAULT_HAS_GLOBAL_FOCUS,
mExclusionRegion,
DEFAULT_SHOULD_IGNORE_CORNER_RADIUS,
- DEFAULT_SHOULD_EXCLUDE_CAPTION_FROM_APP_BOUNDS);
+ DEFAULT_SHOULD_EXCLUDE_CAPTION_FROM_APP_BOUNDS,
+ DEFAULT_IS_RECENTS_TRANSITION_RUNNING,
+ DEFAULT_IS_MOVING_TO_BACK);
assertThat(relayoutParams.mIsCaptionVisible).isFalse();
}
@@ -898,7 +927,9 @@ public class DesktopModeWindowDecorationTests extends ShellTestCase {
DEFAULT_HAS_GLOBAL_FOCUS,
mExclusionRegion,
DEFAULT_SHOULD_IGNORE_CORNER_RADIUS,
- DEFAULT_SHOULD_EXCLUDE_CAPTION_FROM_APP_BOUNDS);
+ DEFAULT_SHOULD_EXCLUDE_CAPTION_FROM_APP_BOUNDS,
+ DEFAULT_IS_RECENTS_TRANSITION_RUNNING,
+ DEFAULT_IS_MOVING_TO_BACK);
assertThat(relayoutParams.mIsCaptionVisible).isTrue();
@@ -917,7 +948,9 @@ public class DesktopModeWindowDecorationTests extends ShellTestCase {
DEFAULT_HAS_GLOBAL_FOCUS,
mExclusionRegion,
DEFAULT_SHOULD_IGNORE_CORNER_RADIUS,
- DEFAULT_SHOULD_EXCLUDE_CAPTION_FROM_APP_BOUNDS);
+ DEFAULT_SHOULD_EXCLUDE_CAPTION_FROM_APP_BOUNDS,
+ DEFAULT_IS_RECENTS_TRANSITION_RUNNING,
+ DEFAULT_IS_MOVING_TO_BACK);
assertThat(relayoutParams.mIsCaptionVisible).isFalse();
}
@@ -944,7 +977,9 @@ public class DesktopModeWindowDecorationTests extends ShellTestCase {
DEFAULT_HAS_GLOBAL_FOCUS,
mExclusionRegion,
DEFAULT_SHOULD_IGNORE_CORNER_RADIUS,
- DEFAULT_SHOULD_EXCLUDE_CAPTION_FROM_APP_BOUNDS);
+ DEFAULT_SHOULD_EXCLUDE_CAPTION_FROM_APP_BOUNDS,
+ DEFAULT_IS_RECENTS_TRANSITION_RUNNING,
+ DEFAULT_IS_MOVING_TO_BACK);
assertThat(relayoutParams.mIsCaptionVisible).isTrue();
}
@@ -971,7 +1006,9 @@ public class DesktopModeWindowDecorationTests extends ShellTestCase {
DEFAULT_HAS_GLOBAL_FOCUS,
mExclusionRegion,
DEFAULT_SHOULD_IGNORE_CORNER_RADIUS,
- DEFAULT_SHOULD_EXCLUDE_CAPTION_FROM_APP_BOUNDS);
+ DEFAULT_SHOULD_EXCLUDE_CAPTION_FROM_APP_BOUNDS,
+ DEFAULT_IS_RECENTS_TRANSITION_RUNNING,
+ DEFAULT_IS_MOVING_TO_BACK);
assertThat(relayoutParams.mIsCaptionVisible).isFalse();
}
@@ -1002,6 +1039,65 @@ public class DesktopModeWindowDecorationTests extends ShellTestCase {
assertThat(relayoutParams.mAsyncViewHost).isFalse();
}
+
+ @Test
+ @EnableFlags(Flags.FLAG_ENABLE_INPUT_LAYER_TRANSITION_FIX)
+ public void updateRelayoutParams_handle_movingToBack_captionNotVisible() {
+ final ActivityManager.RunningTaskInfo taskInfo = createTaskInfo(/* visible= */ true);
+ taskInfo.configuration.windowConfiguration.setWindowingMode(WINDOWING_MODE_FULLSCREEN);
+ final 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,
+ DEFAULT_IS_DRAGGING,
+ new InsetsState(),
+ DEFAULT_HAS_GLOBAL_FOCUS,
+ mExclusionRegion,
+ DEFAULT_SHOULD_IGNORE_CORNER_RADIUS,
+ DEFAULT_SHOULD_EXCLUDE_CAPTION_FROM_APP_BOUNDS,
+ DEFAULT_IS_RECENTS_TRANSITION_RUNNING,
+ /* isMovingToBack= */ true);
+
+ assertThat(relayoutParams.mIsCaptionVisible).isFalse();
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_ENABLE_INPUT_LAYER_TRANSITION_FIX)
+ public void updateRelayoutParams_handle_inRecentsTransition_captionNotVisible() {
+ final ActivityManager.RunningTaskInfo taskInfo = createTaskInfo(/* visible= */ true);
+ taskInfo.configuration.windowConfiguration.setWindowingMode(WINDOWING_MODE_FULLSCREEN);
+ final 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,
+ DEFAULT_IS_DRAGGING,
+ new InsetsState(),
+ DEFAULT_HAS_GLOBAL_FOCUS,
+ mExclusionRegion,
+ DEFAULT_SHOULD_IGNORE_CORNER_RADIUS,
+ DEFAULT_SHOULD_EXCLUDE_CAPTION_FROM_APP_BOUNDS,
+ /* isRecentsTransitionRunning= */ true,
+ DEFAULT_IS_MOVING_TO_BACK);
+
+ assertThat(relayoutParams.mIsCaptionVisible).isFalse();
+ }
+
@Test
public void relayout_fullscreenTask_appliesTransactionImmediately() {
final ActivityManager.RunningTaskInfo taskInfo = createTaskInfo(/* visible= */ true);
@@ -1633,7 +1729,9 @@ public class DesktopModeWindowDecorationTests extends ShellTestCase {
DEFAULT_HAS_GLOBAL_FOCUS,
mExclusionRegion,
DEFAULT_SHOULD_IGNORE_CORNER_RADIUS,
- DEFAULT_SHOULD_EXCLUDE_CAPTION_FROM_APP_BOUNDS);
+ DEFAULT_SHOULD_EXCLUDE_CAPTION_FROM_APP_BOUNDS,
+ DEFAULT_IS_RECENTS_TRANSITION_RUNNING,
+ DEFAULT_IS_MOVING_TO_BACK);
}
private DesktopModeWindowDecoration createWindowDecoration(
@@ -1676,9 +1774,9 @@ public class DesktopModeWindowDecorationTests extends ShellTestCase {
taskInfo, mMockSurfaceControl, mMockHandler, mMainExecutor,
mMockMainCoroutineDispatcher, mMockBgCoroutineScope, mBgExecutor,
mMockChoreographer, mMockSyncQueue, mMockAppHeaderViewHolderFactory,
- mMockRootTaskDisplayAreaOrganizer, mMockGenericLinksParser,
- mMockAssistContentRequester, SurfaceControl.Builder::new, mMockTransactionSupplier,
- WindowContainerTransaction::new, SurfaceControl::new,
+ mMockAppHandleViewHolderFactory, mMockRootTaskDisplayAreaOrganizer,
+ mMockGenericLinksParser, mMockAssistContentRequester, SurfaceControl.Builder::new,
+ mMockTransactionSupplier, WindowContainerTransaction::new, SurfaceControl::new,
new WindowManagerWrapper(mMockWindowManager), mMockSurfaceControlViewHostFactory,
mMockWindowDecorViewHostSupplier, maximizeMenuFactory, mMockHandleMenuFactory,
mMockMultiInstanceHelper, mMockCaptionHandleRepository, mDesktopModeEventLogger,
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/HandleMenuTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/HandleMenuTest.kt
index f984f6db13fc..2e46f6312d03 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/HandleMenuTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/HandleMenuTest.kt
@@ -98,8 +98,6 @@ class HandleMenuTest : ShellTestCase() {
private lateinit var handleMenu: HandleMenu
- private val menuWidthWithElevation = MENU_WIDTH + MENU_PILL_ELEVATION
-
@Before
fun setUp() {
val mockAdditionalViewHostViewContainer = AdditionalViewHostViewContainer(
@@ -126,7 +124,6 @@ class HandleMenuTest : ShellTestCase() {
addOverride(R.dimen.desktop_mode_handle_menu_height, MENU_HEIGHT)
addOverride(R.dimen.desktop_mode_handle_menu_margin_top, MENU_TOP_MARGIN)
addOverride(R.dimen.desktop_mode_handle_menu_margin_start, MENU_START_MARGIN)
- addOverride(R.dimen.desktop_mode_handle_menu_pill_elevation, MENU_PILL_ELEVATION)
addOverride(
R.dimen.desktop_mode_handle_menu_pill_spacing_margin, MENU_PILL_SPACING_MARGIN)
}
@@ -141,7 +138,7 @@ class HandleMenuTest : ShellTestCase() {
assertTrue(handleMenu.handleMenuViewContainer is AdditionalSystemViewContainer)
// Verify menu is created at coordinates that, when added to WindowManager,
// show at the top-center of display.
- val expected = Point(DISPLAY_BOUNDS.centerX() - menuWidthWithElevation / 2, MENU_TOP_MARGIN)
+ val expected = Point(DISPLAY_BOUNDS.centerX() - MENU_WIDTH / 2, MENU_TOP_MARGIN)
assertEquals(expected.toPointF(), handleMenu.handleMenuPosition)
}
@@ -165,7 +162,7 @@ class HandleMenuTest : ShellTestCase() {
// Verify menu is created at coordinates that, when added to WindowManager,
// show at the top-center of split left task.
val expected = Point(
- SPLIT_LEFT_BOUNDS.centerX() - menuWidthWithElevation / 2,
+ SPLIT_LEFT_BOUNDS.centerX() - MENU_WIDTH / 2,
MENU_TOP_MARGIN
)
assertEquals(expected.toPointF(), handleMenu.handleMenuPosition)
@@ -180,7 +177,7 @@ class HandleMenuTest : ShellTestCase() {
// Verify menu is created at coordinates that, when added to WindowManager,
// show at the top-center of split right task.
val expected = Point(
- SPLIT_RIGHT_BOUNDS.centerX() - menuWidthWithElevation / 2,
+ SPLIT_RIGHT_BOUNDS.centerX() - MENU_WIDTH / 2,
MENU_TOP_MARGIN
)
assertEquals(expected.toPointF(), handleMenu.handleMenuPosition)
@@ -323,7 +320,6 @@ class HandleMenuTest : ShellTestCase() {
private const val MENU_HEIGHT = 400
private const val MENU_TOP_MARGIN = 10
private const val MENU_START_MARGIN = 20
- private const val MENU_PILL_ELEVATION = 2
private const val MENU_PILL_SPACING_MARGIN = 4
private const val HANDLE_WIDTH = 80
private const val APP_NAME = "Test App"
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/MultiDisplayVeiledResizeTaskPositionerTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/MultiDisplayVeiledResizeTaskPositionerTest.kt
index a6b077037b86..0798613ed632 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/MultiDisplayVeiledResizeTaskPositionerTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/MultiDisplayVeiledResizeTaskPositionerTest.kt
@@ -559,6 +559,17 @@ class MultiDisplayVeiledResizeTaskPositionerTest : ShellTestCase() {
}
@Test
+ fun testClose() = runOnUiThread {
+ verify(mockDisplayController, times(1))
+ .addDisplayWindowListener(eq(taskPositioner))
+
+ taskPositioner.close()
+
+ verify(mockDisplayController, times(1))
+ .removeDisplayWindowListener(eq(taskPositioner))
+ }
+
+ @Test
fun testIsResizingOrAnimatingResizeSet() = runOnUiThread {
Assert.assertFalse(taskPositioner.isResizingOrAnimating)
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/ResizeVeilTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/ResizeVeilTest.kt
index fa3d3e4016e9..011c8f0ae17e 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/ResizeVeilTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/ResizeVeilTest.kt
@@ -52,7 +52,7 @@ import org.mockito.kotlin.mock
import org.mockito.kotlin.never
import org.mockito.kotlin.times
import org.mockito.kotlin.verify
-import org.mockito.kotlin.verifyZeroInteractions
+import org.mockito.kotlin.verifyNoMoreInteractions
import org.mockito.kotlin.whenever
@@ -216,7 +216,7 @@ class ResizeVeilTest : ShellTestCase() {
veil.hideVeil()
- verifyZeroInteractions(mockTransaction)
+ verifyNoMoreInteractions(mockTransaction)
}
@Test
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/WindowDecorationTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/WindowDecorationTests.java
index 9a2e2fad50be..2e95a979220c 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/WindowDecorationTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/WindowDecorationTests.java
@@ -826,6 +826,18 @@ public class WindowDecorationTests extends ShellTestCase {
}
@Test
+ public void testClose_withTaskDragResizerSet_callResizerClose() {
+ final TestWindowDecoration windowDecor = createWindowDecoration(
+ new TestRunningTaskInfoBuilder().build());
+ final TaskDragResizer taskDragResizer = mock(TaskDragResizer.class);
+ windowDecor.setTaskDragResizer(taskDragResizer);
+
+ windowDecor.close();
+
+ verify(taskDragResizer).close();
+ }
+
+ @Test
public void testRelayout_captionFrameChanged_insetsReapplied() {
final Display defaultDisplay = mock(Display.class);
doReturn(defaultDisplay).when(mMockDisplayController)
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/common/WindowDecorTaskResourceLoaderTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/common/WindowDecorTaskResourceLoaderTest.kt
index c8ccac35d4c4..714d06211044 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/common/WindowDecorTaskResourceLoaderTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/common/WindowDecorTaskResourceLoaderTest.kt
@@ -54,7 +54,7 @@ import org.mockito.kotlin.eq
import org.mockito.kotlin.mock
import org.mockito.kotlin.spy
import org.mockito.kotlin.verify
-import org.mockito.kotlin.verifyZeroInteractions
+import org.mockito.kotlin.verifyNoMoreInteractions
import org.mockito.kotlin.whenever
/**
@@ -125,7 +125,7 @@ class WindowDecorTaskResourceLoaderTest : ShellTestCase() {
loader.getName(task)
- verifyZeroInteractions(
+ verifyNoMoreInteractions(
mockPackageManager,
mockIconProvider,
mockHeaderIconFactory,
@@ -165,7 +165,7 @@ class WindowDecorTaskResourceLoaderTest : ShellTestCase() {
loader.getHeaderIcon(task)
- verifyZeroInteractions(mockPackageManager, mockIconProvider, mockHeaderIconFactory)
+ verifyNoMoreInteractions(mockPackageManager, mockIconProvider, mockHeaderIconFactory)
}
@Test
@@ -187,7 +187,7 @@ class WindowDecorTaskResourceLoaderTest : ShellTestCase() {
loader.getVeilIcon(task)
- verifyZeroInteractions(mockPackageManager, mockIconProvider, mockVeilIconFactory)
+ verifyNoMoreInteractions(mockPackageManager, mockIconProvider, mockVeilIconFactory)
}
@Test
diff --git a/media/java/android/media/MediaRouter.java b/media/java/android/media/MediaRouter.java
index b57476f4341f..6e0821f9f89b 100644
--- a/media/java/android/media/MediaRouter.java
+++ b/media/java/android/media/MediaRouter.java
@@ -183,7 +183,17 @@ public class MediaRouter {
appContext.registerReceiver(new VolumeChangeReceiver(),
new IntentFilter(AudioManager.VOLUME_CHANGED_ACTION));
- mDisplayService.registerDisplayListener(this, mHandler);
+ if (com.android.server.display.feature.flags.Flags
+ .displayListenerPerformanceImprovements()
+ && com.android.server.display.feature.flags.Flags
+ .delayImplicitRrRegistrationUntilRrAccessed()) {
+ mDisplayService.registerDisplayListener(this, mHandler,
+ DisplayManager.EVENT_TYPE_DISPLAY_ADDED
+ | DisplayManager.EVENT_TYPE_DISPLAY_CHANGED
+ | DisplayManager.EVENT_TYPE_DISPLAY_REMOVED);
+ } else {
+ mDisplayService.registerDisplayListener(this, mHandler);
+ }
AudioRoutesInfo newAudioRoutes = null;
try {
diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/AudioManagerUnitTest.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/AudioManagerUnitTest.java
index 6089f4291f3e..f65c7efa8ca7 100644
--- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/AudioManagerUnitTest.java
+++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/AudioManagerUnitTest.java
@@ -28,7 +28,7 @@ import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.verifyZeroInteractions;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
import static org.mockito.Mockito.when;
import android.companion.virtual.VirtualDeviceManager;
@@ -56,7 +56,7 @@ public class AudioManagerUnitTest {
audioManager.playSoundEffect(FX_KEY_CLICK);
// We expect no interactions with VDM when running on default device.
- verifyZeroInteractions(mockVdm);
+ verifyNoMoreInteractions(mockVdm);
}
@Test
diff --git a/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/CompanionAssociationActivity.java b/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/CompanionAssociationActivity.java
index b2c1e604db7e..964268e4ad14 100644
--- a/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/CompanionAssociationActivity.java
+++ b/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/CompanionAssociationActivity.java
@@ -65,6 +65,7 @@ import android.graphics.drawable.Icon;
import android.net.MacAddress;
import android.os.Bundle;
import android.os.Handler;
+import android.os.Looper;
import android.os.RemoteException;
import android.os.ResultReceiver;
import android.text.Spanned;
@@ -621,8 +622,10 @@ public class CompanionAssociationActivity extends FragmentActivity implements
Slog.w(TAG, "Already selected.");
return;
}
- // Notify the adapter to highlight the selected item.
- mDeviceAdapter.setSelectedPosition(position);
+ // Delay highlighting the selected item by posting to the main thread.
+ // This helps avoid flicker in the user consent dialog after device selection.
+ new Handler(
+ Looper.getMainLooper()).post(() -> mDeviceAdapter.setSelectedPosition(position));
mSelectedDevice = requireNonNull(selectedDevice);
diff --git a/packages/SettingsLib/DataStore/src/com/android/settingslib/datastore/KeyValueStore.kt b/packages/SettingsLib/DataStore/src/com/android/settingslib/datastore/KeyValueStore.kt
index 472ffa9289a7..6dec2f999630 100644
--- a/packages/SettingsLib/DataStore/src/com/android/settingslib/datastore/KeyValueStore.kt
+++ b/packages/SettingsLib/DataStore/src/com/android/settingslib/datastore/KeyValueStore.kt
@@ -17,6 +17,7 @@
package com.android.settingslib.datastore
import android.content.SharedPreferences
+import android.util.Log
/** Interface of key-value store. */
interface KeyValueStore : KeyedObservable<String> {
@@ -80,6 +81,27 @@ interface KeyValueStore : KeyedObservable<String> {
fun setString(key: String, value: String?) = setValue(key, String::class.javaObjectType, value)
}
+/** Delegation of [KeyValueStore]. */
+interface KeyValueStoreDelegate : KeyValueStore, KeyedObservableDelegate<String> {
+
+ /** [KeyValueStore] to delegate. */
+ val keyValueStoreDelegate: KeyValueStore
+
+ override val keyedObservableDelegate
+ get() = keyValueStoreDelegate
+
+ override fun contains(key: String) = keyValueStoreDelegate.contains(key)
+
+ override fun <T : Any> getDefaultValue(key: String, valueType: Class<T>) =
+ keyValueStoreDelegate.getDefaultValue(key, valueType)
+
+ override fun <T : Any> getValue(key: String, valueType: Class<T>) =
+ keyValueStoreDelegate.getValue(key, valueType) ?: getDefaultValue(key, valueType)
+
+ override fun <T : Any> setValue(key: String, valueType: Class<T>, value: T?) =
+ keyValueStoreDelegate.setValue(key, valueType, value)
+}
+
/** [SharedPreferences] based [KeyValueStore]. */
interface SharedPreferencesKeyValueStore : KeyValueStore {
@@ -103,11 +125,11 @@ interface SharedPreferencesKeyValueStore : KeyValueStore {
@Suppress("UNCHECKED_CAST")
override fun <T : Any> setValue(key: String, valueType: Class<T>, value: T?) {
+ val edit = sharedPreferences.edit()
if (value == null) {
- sharedPreferences.edit().remove(key).apply()
+ edit.remove(key).apply()
return
}
- val edit = sharedPreferences.edit()
when (valueType) {
Boolean::class.javaObjectType -> edit.putBoolean(key, value as Boolean)
Float::class.javaObjectType -> edit.putFloat(key, value as Float)
@@ -115,7 +137,7 @@ interface SharedPreferencesKeyValueStore : KeyValueStore {
Long::class.javaObjectType -> edit.putLong(key, value as Long)
String::class.javaObjectType -> edit.putString(key, value as String)
Set::class.javaObjectType -> edit.putStringSet(key, value as Set<String>)
- else -> {}
+ else -> Log.e(LOG_TAG, "Unsupported $valueType for $key: $value")
}
edit.apply()
}
diff --git a/packages/SettingsLib/DataStore/src/com/android/settingslib/datastore/KeyedObserver.kt b/packages/SettingsLib/DataStore/src/com/android/settingslib/datastore/KeyedObserver.kt
index 07b1c9e3385e..ff58bf7b8728 100644
--- a/packages/SettingsLib/DataStore/src/com/android/settingslib/datastore/KeyedObserver.kt
+++ b/packages/SettingsLib/DataStore/src/com/android/settingslib/datastore/KeyedObserver.kt
@@ -116,8 +116,28 @@ interface KeyedObservable<K> {
}
/** Delegation of [KeyedObservable]. */
-open class KeyedObservableDelegate<K>(delegate: KeyedObservable<K>) :
- KeyedObservable<K> by delegate
+interface KeyedObservableDelegate<K> : KeyedObservable<K> {
+
+ /** [KeyedObservable] to delegate. */
+ val keyedObservableDelegate: KeyedObservable<K>
+
+ override fun addObserver(observer: KeyedObserver<K?>, executor: Executor): Boolean =
+ keyedObservableDelegate.addObserver(observer, executor)
+
+ override fun addObserver(key: K, observer: KeyedObserver<K>, executor: Executor): Boolean =
+ keyedObservableDelegate.addObserver(key, observer, executor)
+
+ override fun removeObserver(observer: KeyedObserver<K?>): Boolean =
+ keyedObservableDelegate.removeObserver(observer)
+
+ override fun removeObserver(key: K, observer: KeyedObserver<K>): Boolean =
+ keyedObservableDelegate.removeObserver(key, observer)
+
+ override fun notifyChange(reason: Int): Unit = keyedObservableDelegate.notifyChange(reason)
+
+ override fun notifyChange(key: K, reason: Int): Unit =
+ keyedObservableDelegate.notifyChange(key, reason)
+}
/** A thread safe implementation of [KeyedObservable]. */
open class KeyedDataObservable<K> : KeyedObservable<K> {
diff --git a/packages/SettingsLib/DeviceStateRotationLock/src/com.android.settingslib.devicestate/DeviceStateAutoRotateSettingManager.kt b/packages/SettingsLib/DeviceStateRotationLock/src/com.android.settingslib.devicestate/DeviceStateAutoRotateSettingManager.kt
new file mode 100644
index 000000000000..fdde3d3f5669
--- /dev/null
+++ b/packages/SettingsLib/DeviceStateRotationLock/src/com.android.settingslib.devicestate/DeviceStateAutoRotateSettingManager.kt
@@ -0,0 +1,69 @@
+/*
+ * 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.devicestate
+
+import android.provider.Settings.Secure.DEVICE_STATE_ROTATION_LOCK
+
+/**
+ * Interface for managing [DEVICE_STATE_ROTATION_LOCK] setting.
+ *
+ * It provides methods to register/unregister listeners for setting changes, update the setting for
+ * specific device states, retrieve the setting value, and check if rotation is locked for specific
+ * or all device states.
+ */
+interface DeviceStateAutoRotateSettingManager {
+ // TODO: b/397928958 - Rename all terms from rotationLock to autoRotate in all apis.
+
+ /** Listener for changes in device-state based auto rotate setting. */
+ interface DeviceStateAutoRotateSettingListener {
+ /** Called whenever the setting has changed. */
+ fun onSettingsChanged()
+ }
+
+ /** Register listener for changes to [DEVICE_STATE_ROTATION_LOCK] setting. */
+ fun registerListener(settingListener: DeviceStateAutoRotateSettingListener)
+
+ /** Unregister listener for changes to [DEVICE_STATE_ROTATION_LOCK] setting. */
+ fun unregisterListener(settingListener: DeviceStateAutoRotateSettingListener)
+
+ /**
+ * Write [deviceState]'s setting value as [autoRotate], for [DEVICE_STATE_ROTATION_LOCK] setting.
+ */
+ fun updateSetting(deviceState: Int, autoRotate: Boolean)
+
+ /** Get [DEVICE_STATE_ROTATION_LOCK] setting value for [deviceState]. */
+ fun getRotationLockSetting(deviceState: Int): Int
+
+ /** Returns true if auto-rotate setting is OFF for [deviceState]. */
+ fun isRotationLocked(deviceState: Int): Boolean
+
+ /** Returns true if the auto-rotate setting value for all device states is OFF. */
+ fun isRotationLockedForAllStates(): Boolean
+
+ /** Returns a list of device states and their respective auto rotate setting availability. */
+ fun getSettableDeviceStates(): List<SettableDeviceState>
+}
+
+/** Represents a device state and whether it has an auto-rotation setting. */
+data class SettableDeviceState(
+ /** Returns the device state associated with this object. */
+ val deviceState: Int,
+ /** Returns whether there is an auto-rotation setting for this device state. */
+ val isSettable: Boolean
+)
+
+
diff --git a/packages/SettingsLib/DeviceStateRotationLock/src/com.android.settingslib.devicestate/DeviceStateAutoRotateSettingManagerImpl.kt b/packages/SettingsLib/DeviceStateRotationLock/src/com.android.settingslib.devicestate/DeviceStateAutoRotateSettingManagerImpl.kt
new file mode 100644
index 000000000000..0b6c6e238956
--- /dev/null
+++ b/packages/SettingsLib/DeviceStateRotationLock/src/com.android.settingslib.devicestate/DeviceStateAutoRotateSettingManagerImpl.kt
@@ -0,0 +1,191 @@
+/*
+ * 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.devicestate
+
+import android.content.Context
+import android.database.ContentObserver
+import android.os.Handler
+import android.os.UserHandle
+import android.provider.Settings.Secure.DEVICE_STATE_ROTATION_LOCK
+import android.provider.Settings.Secure.DEVICE_STATE_ROTATION_LOCK_IGNORED
+import android.provider.Settings.Secure.DEVICE_STATE_ROTATION_LOCK_LOCKED
+import android.util.Log
+import android.util.SparseIntArray
+import com.android.internal.R
+import com.android.settingslib.devicestate.DeviceStateAutoRotateSettingManager.DeviceStateAutoRotateSettingListener
+import com.android.window.flags.Flags
+import java.util.concurrent.Executor
+
+/**
+ * Implementation of [DeviceStateAutoRotateSettingManager]. This implementation is a part of
+ * refactoring, it should be used when [Flags.FLAG_ENABLE_DEVICE_STATE_AUTO_ROTATE_SETTING_REFACTOR]
+ * is enabled.
+ */
+class DeviceStateAutoRotateSettingManagerImpl(
+ context: Context,
+ backgroundExecutor: Executor,
+ private val secureSettings: SecureSettings,
+ private val mainHandler: Handler,
+ private val posturesHelper: PosturesHelper,
+) : DeviceStateAutoRotateSettingManager {
+ // TODO: b/397928958 rename the fields and apis from rotationLock to autoRotate.
+
+ private val settingListeners: MutableList<DeviceStateAutoRotateSettingListener> =
+ mutableListOf()
+ private val fallbackPostureMap = SparseIntArray()
+ private val settableDeviceState: MutableList<SettableDeviceState> = mutableListOf()
+
+ private val autoRotateSettingValue: String
+ get() = secureSettings.getStringForUser(DEVICE_STATE_ROTATION_LOCK, UserHandle.USER_CURRENT)
+
+ init {
+ loadAutoRotateDeviceStates(context)
+ val contentObserver =
+ object : ContentObserver(mainHandler) {
+ override fun onChange(selfChange: Boolean) = notifyListeners()
+ }
+ backgroundExecutor.execute {
+ secureSettings.registerContentObserver(
+ DEVICE_STATE_ROTATION_LOCK, false, contentObserver, UserHandle.USER_CURRENT
+ )
+ }
+ }
+
+ override fun registerListener(settingListener: DeviceStateAutoRotateSettingListener) {
+ settingListeners.add(settingListener)
+ }
+
+ override fun unregisterListener(settingListener: DeviceStateAutoRotateSettingListener) {
+ if (!settingListeners.remove(settingListener)) {
+ Log.w(TAG, "Attempting to unregister a listener hadn't been registered")
+ }
+ }
+
+ override fun getRotationLockSetting(deviceState: Int): Int {
+ val devicePosture = posturesHelper.deviceStateToPosture(deviceState)
+ val serializedSetting = autoRotateSettingValue
+ val autoRotateSetting = extractSettingForDevicePosture(devicePosture, serializedSetting)
+
+ // If the setting is ignored for this posture, check the fallback posture.
+ if (autoRotateSetting == DEVICE_STATE_ROTATION_LOCK_IGNORED) {
+ val fallbackPosture =
+ fallbackPostureMap.get(devicePosture, DEVICE_STATE_ROTATION_LOCK_IGNORED)
+ return extractSettingForDevicePosture(fallbackPosture, serializedSetting)
+ }
+
+ return autoRotateSetting
+ }
+
+ override fun isRotationLocked(deviceState: Int) =
+ getRotationLockSetting(deviceState) == DEVICE_STATE_ROTATION_LOCK_LOCKED
+
+ override fun isRotationLockedForAllStates(): Boolean =
+ convertSerializedSettingToMap(autoRotateSettingValue).all { (_, value) ->
+ value == DEVICE_STATE_ROTATION_LOCK_LOCKED
+ }
+
+ override fun getSettableDeviceStates(): List<SettableDeviceState> = settableDeviceState
+
+ override fun updateSetting(deviceState: Int, autoRotate: Boolean) {
+ // TODO: b/350946537 - Create IPC to update the setting, and call it here.
+ throw UnsupportedOperationException("API updateSetting is not implemented yet")
+ }
+
+ private fun notifyListeners() =
+ settingListeners.forEach { listener -> listener.onSettingsChanged() }
+
+ private fun loadAutoRotateDeviceStates(context: Context) {
+ val perDeviceStateAutoRotateDefaults =
+ context.resources.getStringArray(R.array.config_perDeviceStateRotationLockDefaults)
+ for (entry in perDeviceStateAutoRotateDefaults) {
+ entry.parsePostureEntry()?.let { (posture, autoRotate, fallbackPosture) ->
+ if (autoRotate == DEVICE_STATE_ROTATION_LOCK_IGNORED && fallbackPosture != null) {
+ fallbackPostureMap.put(posture, fallbackPosture)
+ }
+ settableDeviceState.add(
+ SettableDeviceState(posture, autoRotate != DEVICE_STATE_ROTATION_LOCK_IGNORED)
+ )
+ }
+ }
+ }
+
+ private fun convertSerializedSettingToMap(serializedSetting: String): Map<Int, Int> {
+ if (serializedSetting.isEmpty()) return emptyMap()
+ return try {
+ serializedSetting
+ .split(SEPARATOR_REGEX)
+ .hasEvenSize()
+ .chunked(2)
+ .mapNotNull(::parsePostureSettingPair)
+ .toMap()
+ } catch (e: Exception) {
+ Log.w(
+ TAG,
+ "Invalid format in serializedSetting=$serializedSetting: ${e.message}"
+ )
+ return emptyMap()
+ }
+ }
+
+ private fun List<String>.hasEvenSize(): List<String> {
+ if (this.size % 2 != 0) {
+ throw IllegalStateException("Odd number of elements in the list")
+ }
+ return this
+ }
+
+ private fun parsePostureSettingPair(settingPair: List<String>): Pair<Int, Int>? {
+ return settingPair.let { (keyStr, valueStr) ->
+ val key = keyStr.toIntOrNull()
+ val value = valueStr.toIntOrNull()
+ if (key != null && value != null && value in 0..2) {
+ key to value
+ } else {
+ Log.w(TAG, "Invalid key or value in pair: $keyStr, $valueStr")
+ null // Invalid pair, skip it
+ }
+ }
+ }
+
+ private fun extractSettingForDevicePosture(
+ devicePosture: Int,
+ serializedSetting: String
+ ): Int =
+ convertSerializedSettingToMap(serializedSetting)[devicePosture]
+ ?: DEVICE_STATE_ROTATION_LOCK_IGNORED
+
+ private fun String.parsePostureEntry(): Triple<Int, Int, Int?>? {
+ val values = split(SEPARATOR_REGEX)
+ if (values.size !in 2..3) { // It should contain 2 or 3 values.
+ Log.w(TAG, "Invalid number of values in entry: '$this'")
+ return null
+ }
+ return try {
+ val posture = values[0].toInt()
+ val rotationLockSetting = values[1].toInt()
+ val fallbackPosture = if (values.size == 3) values[2].toIntOrNull() else null
+ Triple(posture, rotationLockSetting, fallbackPosture)
+ } catch (e: NumberFormatException) {
+ Log.w(TAG, "Invalid number format in '$this': ${e.message}")
+ null
+ }
+ }
+
+ companion object {
+ private const val TAG = "DeviceStateAutoRotate"
+ private const val SEPARATOR_REGEX = ":"
+ }
+}
diff --git a/packages/SettingsLib/DeviceStateRotationLock/src/com.android.settingslib.devicestate/DeviceStateAutoRotateSettingUtils.kt b/packages/SettingsLib/DeviceStateRotationLock/src/com.android.settingslib.devicestate/DeviceStateAutoRotateSettingUtils.kt
new file mode 100644
index 000000000000..4d1d29242832
--- /dev/null
+++ b/packages/SettingsLib/DeviceStateRotationLock/src/com.android.settingslib.devicestate/DeviceStateAutoRotateSettingUtils.kt
@@ -0,0 +1,31 @@
+/*
+ * 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.
+ */
+@file:JvmName("DeviceStateAutoRotateSettingUtils")
+
+package com.android.settingslib.devicestate
+
+import android.content.Context
+import com.android.internal.R
+
+/** Returns true if device-state based rotation lock settings are enabled. */
+object DeviceStateAutoRotateSettingUtils {
+ @JvmStatic
+ fun isDeviceStateRotationLockEnabled(context: Context) =
+ context.resources
+ .getStringArray(R.array.config_perDeviceStateRotationLockDefaults)
+ .isNotEmpty()
+}
+
diff --git a/packages/SettingsLib/DeviceStateRotationLock/src/com.android.settingslib.devicestate/DeviceStateRotationLockSettingsManager.java b/packages/SettingsLib/DeviceStateRotationLock/src/com.android.settingslib.devicestate/DeviceStateRotationLockSettingsManager.java
index 635f6905e4f0..deeba574f2ad 100644
--- a/packages/SettingsLib/DeviceStateRotationLock/src/com.android.settingslib.devicestate/DeviceStateRotationLockSettingsManager.java
+++ b/packages/SettingsLib/DeviceStateRotationLock/src/com.android.settingslib.devicestate/DeviceStateRotationLockSettingsManager.java
@@ -20,6 +20,8 @@ import static android.provider.Settings.Secure.DEVICE_STATE_ROTATION_LOCK_IGNORE
import static android.provider.Settings.Secure.DEVICE_STATE_ROTATION_LOCK_LOCKED;
import static android.provider.Settings.Secure.DEVICE_STATE_ROTATION_LOCK_UNLOCKED;
+import static com.android.settingslib.devicestate.DeviceStateAutoRotateSettingManager.DeviceStateAutoRotateSettingListener;
+
import android.annotation.Nullable;
import android.content.ContentResolver;
import android.content.Context;
@@ -43,7 +45,6 @@ import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
-import java.util.Objects;
import java.util.Set;
/**
@@ -58,7 +59,7 @@ public final class DeviceStateRotationLockSettingsManager {
private static DeviceStateRotationLockSettingsManager sSingleton;
private final Handler mMainHandler = new Handler(Looper.getMainLooper());
- private final Set<DeviceStateRotationLockSettingsListener> mListeners = new HashSet<>();
+ private final Set<DeviceStateAutoRotateSettingListener> mListeners = new HashSet<>();
private final SecureSettings mSecureSettings;
private final PosturesHelper mPosturesHelper;
private String[] mPostureRotationLockDefaults;
@@ -127,20 +128,20 @@ public final class DeviceStateRotationLockSettingsManager {
}
/**
- * Registers a {@link DeviceStateRotationLockSettingsListener} to be notified when the settings
+ * Registers a {@link DeviceStateAutoRotateSettingListener} to be notified when the settings
* change. Can be called multiple times with different listeners.
*/
- public void registerListener(DeviceStateRotationLockSettingsListener runnable) {
+ public void registerListener(DeviceStateAutoRotateSettingListener runnable) {
mListeners.add(runnable);
}
/**
- * Unregisters a {@link DeviceStateRotationLockSettingsListener}. No-op if the given instance
+ * Unregisters a {@link DeviceStateAutoRotateSettingListener}. No-op if the given instance
* was never registered.
*/
public void unregisterListener(
- DeviceStateRotationLockSettingsListener deviceStateRotationLockSettingsListener) {
- if (!mListeners.remove(deviceStateRotationLockSettingsListener)) {
+ DeviceStateAutoRotateSettingListener deviceStateAutoRotateSettingListener) {
+ if (!mListeners.remove(deviceStateAutoRotateSettingListener)) {
Log.w(TAG, "Attempting to unregister a listener hadn't been registered");
}
}
@@ -379,56 +380,8 @@ public final class DeviceStateRotationLockSettingsManager {
}
private void notifyListeners() {
- for (DeviceStateRotationLockSettingsListener r : mListeners) {
+ for (DeviceStateAutoRotateSettingListener r : mListeners) {
r.onSettingsChanged();
}
}
-
- /** Listener for changes in device-state based rotation lock settings */
- public interface DeviceStateRotationLockSettingsListener {
- /** Called whenever the settings have changed. */
- void onSettingsChanged();
- }
-
- /** Represents a device state and whether it has an auto-rotation setting. */
- public static class SettableDeviceState {
- private final int mDeviceState;
- private final boolean mIsSettable;
-
- SettableDeviceState(int deviceState, boolean isSettable) {
- mDeviceState = deviceState;
- mIsSettable = isSettable;
- }
-
- /** Returns the device state associated with this object. */
- public int getDeviceState() {
- return mDeviceState;
- }
-
- /** Returns whether there is an auto-rotation setting for this device state. */
- public boolean isSettable() {
- return mIsSettable;
- }
-
- @Override
- public boolean equals(Object o) {
- if (this == o) return true;
- if (!(o instanceof SettableDeviceState)) return false;
- SettableDeviceState that = (SettableDeviceState) o;
- return mDeviceState == that.mDeviceState && mIsSettable == that.mIsSettable;
- }
-
- @Override
- public int hashCode() {
- return Objects.hash(mDeviceState, mIsSettable);
- }
-
- @Override
- public String toString() {
- return "SettableDeviceState{"
- + "mDeviceState=" + mDeviceState
- + ", mIsSettable=" + mIsSettable
- + '}';
- }
- }
}
diff --git a/packages/SettingsLib/DeviceStateRotationLock/src/com.android.settingslib.devicestate/SecureSettings.java b/packages/SettingsLib/DeviceStateRotationLock/src/com.android.settingslib.devicestate/SecureSettings.java
index 10528739b2b0..ea40e148aed6 100644
--- a/packages/SettingsLib/DeviceStateRotationLock/src/com.android.settingslib.devicestate/SecureSettings.java
+++ b/packages/SettingsLib/DeviceStateRotationLock/src/com.android.settingslib.devicestate/SecureSettings.java
@@ -19,7 +19,7 @@ package com.android.settingslib.devicestate;
import android.database.ContentObserver;
/** Minimal wrapper interface around {@link android.provider.Settings.Secure} for easier testing. */
-interface SecureSettings {
+public interface SecureSettings {
void putStringForUser(String name, String value, int userHandle);
diff --git a/packages/SettingsLib/MainSwitchPreference/src/com/android/settingslib/widget/MainSwitchBar.java b/packages/SettingsLib/MainSwitchPreference/src/com/android/settingslib/widget/MainSwitchBar.java
index 7bd4b3f771ab..ddd9d2acdab3 100644
--- a/packages/SettingsLib/MainSwitchPreference/src/com/android/settingslib/widget/MainSwitchBar.java
+++ b/packages/SettingsLib/MainSwitchPreference/src/com/android/settingslib/widget/MainSwitchBar.java
@@ -112,26 +112,6 @@ public class MainSwitchBar extends LinearLayout implements OnCheckedChangeListen
if (mSwitch.getVisibility() == VISIBLE) {
mSwitch.setOnCheckedChangeListener(this);
}
-
- setChecked(mSwitch.isChecked());
-
- if (attrs != null) {
- final TypedArray a = context.obtainStyledAttributes(attrs,
- androidx.preference.R.styleable.Preference, 0 /*defStyleAttr*/,
- 0 /*defStyleRes*/);
- final CharSequence title = a.getText(
- androidx.preference.R.styleable.Preference_android_title);
- setTitle(title);
- //TODO(b/369470034): update to next version
- if (isExpressive && Build.VERSION.SDK_INT >= VERSION_CODES.VANILLA_ICE_CREAM) {
- CharSequence summary = a.getText(
- androidx.preference.R.styleable.Preference_android_summary);
- setSummary(summary);
- }
- a.recycle();
- }
-
- setBackground(mSwitch.isChecked());
}
@Override
diff --git a/packages/SettingsLib/src/com/android/settingslib/RestrictedPreferenceHelper.java b/packages/SettingsLib/src/com/android/settingslib/RestrictedPreferenceHelper.java
index 9d979019be58..bf6006b1eddc 100644
--- a/packages/SettingsLib/src/com/android/settingslib/RestrictedPreferenceHelper.java
+++ b/packages/SettingsLib/src/com/android/settingslib/RestrictedPreferenceHelper.java
@@ -151,6 +151,18 @@ public class RestrictedPreferenceHelper {
UserHandle.myUserId());
}
+ /**
+ * Configures the user restriction that this preference will track. This is equivalent to
+ * specifying {@link R.styleable#RestrictedPreference_userRestriction} in XML and allows
+ * configuring user restriction at runtime.
+ */
+ public void setUserRestriction(@Nullable String userRestriction) {
+ mAttrUserRestriction = userRestriction == null ||
+ RestrictedLockUtilsInternal.hasBaseUserRestriction(mContext, userRestriction,
+ UserHandle.myUserId()) ? null : userRestriction;
+ setDisabledByAdmin(checkRestrictionEnforced());
+ }
+
public void useAdminDisabledSummary(boolean useSummary) {
mDisabledSummary = useSummary;
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothUtils.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothUtils.java
index 2fdf2ac0567b..ae9ad958b287 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothUtils.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothUtils.java
@@ -703,18 +703,18 @@ public class BluetoothUtils {
// However, app layer need to gate the feature based on whether the device has audio
// sharing capability regardless of the BT state.
// So here we check the BluetoothProperties when BT off.
- String mode = BluetoothProperties.le_audio_dynamic_switcher_mode().orElse("none");
- Set<String> disabledModes = ImmutableSet.of("disabled", "unicast");
+ //
+ // TODO: Also check SystemProperties "persist.bluetooth.leaudio_dynamic_switcher.mode"
+ // and return true if it is in broadcast mode.
+ // Now SystemUI don't have access to read the value.
int sourceSupportedCode = adapter.isLeAudioBroadcastSourceSupported();
int assistantSupportedCode = adapter.isLeAudioBroadcastAssistantSupported();
return (sourceSupportedCode == BluetoothStatusCodes.FEATURE_SUPPORTED
|| (sourceSupportedCode == BluetoothStatusCodes.ERROR_BLUETOOTH_NOT_ENABLED
- && BluetoothProperties.isProfileBapBroadcastSourceEnabled().orElse(false)
- && !disabledModes.contains(mode)))
+ && BluetoothProperties.isProfileBapBroadcastSourceEnabled().orElse(false)))
&& (assistantSupportedCode == BluetoothStatusCodes.FEATURE_SUPPORTED
|| (assistantSupportedCode == BluetoothStatusCodes.ERROR_BLUETOOTH_NOT_ENABLED
- && BluetoothProperties.isProfileBapBroadcastAssistEnabled().orElse(false)
- && !disabledModes.contains(mode)));
+ && BluetoothProperties.isProfileBapBroadcastAssistEnabled().orElse(false)));
} catch (IllegalStateException e) {
Log.d(TAG, "Fail to check isAudioSharingSupported, e = ", e);
return false;
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothLeBroadcast.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothLeBroadcast.java
index f22bdaf55ce4..01d8694256f3 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothLeBroadcast.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothLeBroadcast.java
@@ -724,7 +724,10 @@ public class LocalBluetoothLeBroadcast implements LocalBluetoothProfile {
Log.d(TAG, "The BluetoothLeBroadcast is null");
return null;
}
- if (mBluetoothLeBroadcastMetadata == null) {
+ if (mBluetoothLeBroadcastMetadata == null
+ // mBroadcastId is updated when onBroadcastStarted, which is always before
+ // onBroadcastMetadataChanged, so mBroadcastId is always the latest broadcast info
+ || mBluetoothLeBroadcastMetadata.getBroadcastId() != mBroadcastId) {
final List<BluetoothLeBroadcastMetadata> metadataList =
mServiceBroadcast.getAllBroadcastMetadata();
mBluetoothLeBroadcastMetadata =
@@ -732,6 +735,7 @@ public class LocalBluetoothLeBroadcast implements LocalBluetoothProfile {
.filter(i -> i.getBroadcastId() == mBroadcastId)
.findFirst()
.orElse(null);
+ Log.d(TAG, "getLatestBluetoothLeBroadcastMetadata for broadcast id " + mBroadcastId);
}
return mBluetoothLeBroadcastMetadata;
}
diff --git a/packages/SettingsLib/tests/integ/src/com/android/settingslib/devicestate/DeviceStateAutoRotateSettingManagerImplTest.kt b/packages/SettingsLib/tests/integ/src/com/android/settingslib/devicestate/DeviceStateAutoRotateSettingManagerImplTest.kt
new file mode 100644
index 000000000000..78dba57028ba
--- /dev/null
+++ b/packages/SettingsLib/tests/integ/src/com/android/settingslib/devicestate/DeviceStateAutoRotateSettingManagerImplTest.kt
@@ -0,0 +1,300 @@
+/*
+ * 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.devicestate
+
+import android.content.ContentResolver
+import android.content.Context
+import android.content.res.Resources
+import android.hardware.devicestate.DeviceStateManager
+import android.os.Handler
+import android.os.UserHandle
+import android.provider.Settings
+import android.provider.Settings.Secure.DEVICE_STATE_ROTATION_KEY_FOLDED
+import android.provider.Settings.Secure.DEVICE_STATE_ROTATION_KEY_HALF_FOLDED
+import android.provider.Settings.Secure.DEVICE_STATE_ROTATION_KEY_REAR_DISPLAY
+import android.provider.Settings.Secure.DEVICE_STATE_ROTATION_KEY_UNFOLDED
+import android.provider.Settings.Secure.DEVICE_STATE_ROTATION_LOCK_IGNORED
+import android.provider.Settings.Secure.DEVICE_STATE_ROTATION_LOCK_LOCKED
+import android.provider.Settings.Secure.DEVICE_STATE_ROTATION_LOCK_UNLOCKED
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.internal.R
+import com.android.settingslib.devicestate.DeviceStateAutoRotateSettingManager.DeviceStateAutoRotateSettingListener
+import com.google.common.truth.Truth.assertThat
+import org.junit.Before
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.ArgumentMatchers.any
+import org.mockito.Mock
+import org.mockito.Mockito.mock
+import org.mockito.Mockito.never
+import org.mockito.Mockito.verify
+import org.mockito.junit.MockitoJUnit
+import java.util.concurrent.Executor
+import org.mockito.Mockito.`when` as whenever
+
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class DeviceStateAutoRotateSettingManagerImplTest {
+ @get:Rule
+ val rule = MockitoJUnit.rule()
+
+ private val fakeSecureSettings = FakeSecureSettings()
+ private val executor: Executor = Executor { it.run() }
+ private val configPerDeviceStateRotationLockDefaults = arrayOf(
+ "$DEVICE_STATE_ROTATION_KEY_HALF_FOLDED:" +
+ "$DEVICE_STATE_ROTATION_LOCK_IGNORED:" +
+ "$DEVICE_STATE_ROTATION_KEY_UNFOLDED",
+ "$DEVICE_STATE_ROTATION_KEY_REAR_DISPLAY:" +
+ "$DEVICE_STATE_ROTATION_LOCK_IGNORED:" +
+ "$DEVICE_STATE_ROTATION_KEY_UNFOLDED",
+ "$DEVICE_STATE_ROTATION_KEY_UNFOLDED:$DEVICE_STATE_ROTATION_LOCK_LOCKED",
+ "$DEVICE_STATE_ROTATION_KEY_FOLDED:$DEVICE_STATE_ROTATION_LOCK_LOCKED",
+ )
+
+ @Mock
+ private lateinit var mockContext: Context
+
+ @Mock
+ private lateinit var mockContentResolver: ContentResolver
+
+ @Mock
+ private lateinit var mockPosturesHelper: PosturesHelper
+
+ @Mock
+ private lateinit var mockHandler: Handler
+
+ @Mock
+ private lateinit var mockDeviceStateManager: DeviceStateManager
+
+ @Mock
+ private lateinit var mockResources: Resources
+ private lateinit var settingManager: DeviceStateAutoRotateSettingManagerImpl
+
+ @Before
+ fun setUp() {
+ whenever(mockContext.contentResolver).thenReturn(mockContentResolver)
+ whenever(mockContext.resources).thenReturn(mockResources)
+ whenever(mockResources.getStringArray(R.array.config_perDeviceStateRotationLockDefaults))
+ .thenReturn(configPerDeviceStateRotationLockDefaults)
+ whenever(mockHandler.post(any(Runnable::class.java))).thenAnswer { invocation ->
+ val runnable = invocation.arguments[0] as Runnable
+ runnable.run()
+ null
+ }
+ whenever(mockContext.getSystemService(DeviceStateManager::class.java))
+ .thenReturn(mockDeviceStateManager)
+ whenever(mockPosturesHelper.deviceStateToPosture(DEVICE_STATE_UNFOLDED))
+ .thenReturn(DEVICE_STATE_ROTATION_KEY_UNFOLDED)
+ whenever(mockPosturesHelper.deviceStateToPosture(DEVICE_STATE_FOLDED))
+ .thenReturn(DEVICE_STATE_ROTATION_KEY_FOLDED)
+ whenever(mockPosturesHelper.deviceStateToPosture(DEVICE_STATE_HALF_FOLDED))
+ .thenReturn(DEVICE_STATE_ROTATION_KEY_HALF_FOLDED)
+ whenever(mockPosturesHelper.deviceStateToPosture(DEVICE_STATE_INVALID))
+ .thenReturn(DEVICE_STATE_ROTATION_LOCK_IGNORED)
+ whenever(mockPosturesHelper.deviceStateToPosture(DEVICE_STATE_REAR_DISPLAY))
+ .thenReturn(DEVICE_STATE_ROTATION_KEY_REAR_DISPLAY)
+
+ settingManager =
+ DeviceStateAutoRotateSettingManagerImpl(
+ mockContext,
+ executor,
+ fakeSecureSettings,
+ mockHandler,
+ mockPosturesHelper,
+ )
+ }
+
+ @Test
+ fun registerListener_onSettingsChanged_listenerNotified() {
+ val listener = mock(DeviceStateAutoRotateSettingListener::class.java)
+ settingManager.registerListener(listener)
+
+ persistSettings(DEVICE_STATE_ROTATION_KEY_UNFOLDED, DEVICE_STATE_ROTATION_LOCK_LOCKED)
+
+ verify(listener).onSettingsChanged()
+ }
+
+ @Test
+ fun registerMultipleListeners_onSettingsChanged_allListenersNotified() {
+ val listener1 = mock(DeviceStateAutoRotateSettingListener::class.java)
+ val listener2 = mock(DeviceStateAutoRotateSettingListener::class.java)
+ settingManager.registerListener(listener1)
+ settingManager.registerListener(listener2)
+
+ persistSettings(DEVICE_STATE_ROTATION_KEY_UNFOLDED, DEVICE_STATE_ROTATION_LOCK_LOCKED)
+
+ verify(listener1).onSettingsChanged()
+ verify(listener2).onSettingsChanged()
+ }
+
+ @Test
+ fun unregisterListener_onSettingsChanged_listenerNotNotified() {
+ val listener = mock(DeviceStateAutoRotateSettingListener::class.java)
+ settingManager.registerListener(listener)
+ settingManager.unregisterListener(listener)
+
+ persistSettings(DEVICE_STATE_ROTATION_KEY_UNFOLDED, DEVICE_STATE_ROTATION_LOCK_LOCKED)
+
+ verify(listener, never()).onSettingsChanged()
+ }
+
+ @Test
+ fun getAutoRotateSetting_offForUnfolded_returnsOff() {
+ persistSettings(DEVICE_STATE_ROTATION_KEY_UNFOLDED, DEVICE_STATE_ROTATION_LOCK_LOCKED)
+
+ val autoRotateSetting = settingManager.getRotationLockSetting(DEVICE_STATE_UNFOLDED)
+
+ assertThat(autoRotateSetting).isEqualTo(DEVICE_STATE_ROTATION_LOCK_LOCKED)
+ }
+
+ @Test
+ fun getAutoRotateSetting_onForFolded_returnsOn() {
+ persistSettings(DEVICE_STATE_ROTATION_KEY_FOLDED, DEVICE_STATE_ROTATION_LOCK_UNLOCKED)
+
+ val autoRotateSetting = settingManager.getRotationLockSetting(DEVICE_STATE_FOLDED)
+
+ assertThat(autoRotateSetting).isEqualTo(DEVICE_STATE_ROTATION_LOCK_UNLOCKED)
+ }
+
+ @Test
+ fun getAutoRotateSetting_forInvalidPostureWithNoFallback_returnsIgnored() {
+ val autoRotateSetting = settingManager.getRotationLockSetting(DEVICE_STATE_INVALID)
+
+ assertThat(autoRotateSetting).isEqualTo(DEVICE_STATE_ROTATION_LOCK_IGNORED)
+ }
+
+ @Test
+ fun getAutoRotateSetting_forInvalidPosture_returnsSettingForFallbackPosture() {
+ persistSettings(DEVICE_STATE_ROTATION_KEY_UNFOLDED, DEVICE_STATE_ROTATION_LOCK_UNLOCKED)
+ persistSettings(DEVICE_STATE_ROTATION_KEY_FOLDED, DEVICE_STATE_ROTATION_LOCK_LOCKED)
+
+ val autoRotateSetting = settingManager.getRotationLockSetting(DEVICE_STATE_HALF_FOLDED)
+
+ assertThat(autoRotateSetting).isEqualTo(DEVICE_STATE_ROTATION_LOCK_UNLOCKED)
+ }
+
+ @Test
+ fun getAutoRotateSetting_invalidFormat_returnsIgnored() {
+ persistSettings("invalid_format")
+
+ val autoRotateSetting = settingManager.getRotationLockSetting(DEVICE_STATE_FOLDED)
+
+ assertThat(autoRotateSetting).isEqualTo(DEVICE_STATE_ROTATION_LOCK_IGNORED)
+ }
+
+ @Test
+ fun getAutoRotateSetting_invalidNumberFormat_returnsIgnored() {
+ persistSettings("$DEVICE_STATE_ROTATION_KEY_FOLDED:4")
+
+ val autoRotateSetting = settingManager.getRotationLockSetting(DEVICE_STATE_FOLDED)
+
+ assertThat(autoRotateSetting).isEqualTo(DEVICE_STATE_ROTATION_LOCK_IGNORED)
+ }
+
+ @Test
+ fun getAutoRotateSetting_multipleSettings_returnsCorrectSetting() {
+ persistSettings(
+ "$DEVICE_STATE_ROTATION_KEY_FOLDED:$DEVICE_STATE_ROTATION_LOCK_LOCKED:" +
+ "$DEVICE_STATE_ROTATION_KEY_UNFOLDED:$DEVICE_STATE_ROTATION_LOCK_UNLOCKED"
+ )
+
+ val foldedSetting = settingManager.getRotationLockSetting(DEVICE_STATE_FOLDED)
+ val unfoldedSetting = settingManager.getRotationLockSetting(DEVICE_STATE_UNFOLDED)
+
+ assertThat(foldedSetting).isEqualTo(DEVICE_STATE_ROTATION_LOCK_LOCKED)
+ assertThat(unfoldedSetting).isEqualTo(DEVICE_STATE_ROTATION_LOCK_UNLOCKED)
+ }
+
+ @Test
+ fun isAutoRotateOff_offForUnfolded_returnsTrue() {
+ persistSettings(DEVICE_STATE_ROTATION_KEY_UNFOLDED, DEVICE_STATE_ROTATION_LOCK_LOCKED)
+
+ val isAutoRotateOff = settingManager.isRotationLocked(DEVICE_STATE_UNFOLDED)
+
+ assertThat(isAutoRotateOff).isTrue()
+ }
+
+ @Test
+ fun isRotationLockedForAllStates_allStatesLocked_returnsTrue() {
+ persistSettings(
+ "$DEVICE_STATE_ROTATION_KEY_FOLDED:$DEVICE_STATE_ROTATION_LOCK_LOCKED:" +
+ "$DEVICE_STATE_ROTATION_KEY_UNFOLDED:$DEVICE_STATE_ROTATION_LOCK_LOCKED"
+ )
+
+ val isRotationLockedForAllStates = settingManager.isRotationLockedForAllStates()
+
+ assertThat(isRotationLockedForAllStates).isTrue()
+ }
+
+ @Test
+ fun isRotationLockedForAllStates_someStatesLocked_returnsFalse() {
+ persistSettings(
+ "$DEVICE_STATE_ROTATION_KEY_FOLDED:$DEVICE_STATE_ROTATION_LOCK_UNLOCKED:" +
+ "$DEVICE_STATE_ROTATION_KEY_UNFOLDED:$DEVICE_STATE_ROTATION_LOCK_LOCKED"
+ )
+
+ val isRotationLockedForAllStates = settingManager.isRotationLockedForAllStates()
+
+ assertThat(isRotationLockedForAllStates).isFalse()
+ }
+
+ @Test
+ fun isRotationLockedForAllStates_noStatesLocked_returnsFalse() {
+ persistSettings(
+ "$DEVICE_STATE_ROTATION_KEY_FOLDED:$DEVICE_STATE_ROTATION_LOCK_UNLOCKED:" +
+ "$DEVICE_STATE_ROTATION_KEY_UNFOLDED:$DEVICE_STATE_ROTATION_LOCK_UNLOCKED"
+ )
+
+ val isRotationLockedForAllStates = settingManager.isRotationLockedForAllStates()
+
+ assertThat(isRotationLockedForAllStates).isFalse()
+ }
+
+ @Test
+ fun getSettableDeviceStates_returnsExpectedValuesInOriginalOrder() {
+ val settableDeviceStates = settingManager.getSettableDeviceStates()
+
+ assertThat(settableDeviceStates)
+ .containsExactly(
+ SettableDeviceState(DEVICE_STATE_ROTATION_KEY_UNFOLDED, isSettable = true),
+ SettableDeviceState(DEVICE_STATE_ROTATION_KEY_FOLDED, isSettable = true),
+ SettableDeviceState(DEVICE_STATE_ROTATION_KEY_HALF_FOLDED, isSettable = false),
+ SettableDeviceState(DEVICE_STATE_ROTATION_KEY_REAR_DISPLAY, isSettable = false),
+ SettableDeviceState(DEVICE_STATE_ROTATION_LOCK_IGNORED, isSettable = false),
+ )
+ }
+
+ private fun persistSettings(devicePosture: Int, autoRotateSetting: Int) {
+ persistSettings("$devicePosture:$autoRotateSetting")
+ }
+
+ private fun persistSettings(value: String) {
+ fakeSecureSettings.putStringForUser(
+ Settings.Secure.DEVICE_STATE_ROTATION_LOCK, value, UserHandle.USER_CURRENT
+ )
+ }
+
+ private companion object {
+ const val DEVICE_STATE_FOLDED = 0
+ const val DEVICE_STATE_HALF_FOLDED = 1
+ const val DEVICE_STATE_UNFOLDED = 2
+ const val DEVICE_STATE_REAR_DISPLAY = 3
+ const val DEVICE_STATE_INVALID = 4
+ }
+}
diff --git a/packages/SettingsLib/tests/integ/src/com/android/settingslib/devicestate/DeviceStateRotationLockSettingsManagerTest.java b/packages/SettingsLib/tests/integ/src/com/android/settingslib/devicestate/DeviceStateRotationLockSettingsManagerTest.java
index 9f9aaf5ff83a..baebaf7dfef0 100644
--- a/packages/SettingsLib/tests/integ/src/com/android/settingslib/devicestate/DeviceStateRotationLockSettingsManagerTest.java
+++ b/packages/SettingsLib/tests/integ/src/com/android/settingslib/devicestate/DeviceStateRotationLockSettingsManagerTest.java
@@ -40,7 +40,6 @@ import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
import com.android.internal.R;
-import com.android.settingslib.devicestate.DeviceStateRotationLockSettingsManager.SettableDeviceState;
import com.google.common.truth.Expect;
diff --git a/packages/SettingsProvider/src/android/provider/settings/backup/GlobalSettings.java b/packages/SettingsProvider/src/android/provider/settings/backup/GlobalSettings.java
index fc61b1e875f3..d3291b4bac17 100644
--- a/packages/SettingsProvider/src/android/provider/settings/backup/GlobalSettings.java
+++ b/packages/SettingsProvider/src/android/provider/settings/backup/GlobalSettings.java
@@ -118,6 +118,8 @@ public class GlobalSettings {
Settings.Global.Wearable.CHARGING_SOUNDS_ENABLED,
Settings.Global.Wearable.WRIST_DETECTION_AUTO_LOCKING_ENABLED,
Settings.Global.Wearable.AUTO_BEDTIME_MODE,
+ Settings.Global.Wearable.GESTURE_PRIMARY_ACTION_USER_PREFERENCE,
+ Settings.Global.Wearable.GESTURE_DISMISS_ACTION_USER_PREFERENCE,
Settings.Global.FORCE_ENABLE_PSS_PROFILING,
Settings.Global.Wearable.ACCESSIBILITY_VIBRATION_WATCH_ENABLED,
Settings.Global.Wearable.ACCESSIBILITY_VIBRATION_WATCH_TYPE,
diff --git a/packages/SettingsProvider/src/android/provider/settings/backup/SystemSettings.java b/packages/SettingsProvider/src/android/provider/settings/backup/SystemSettings.java
index cf0447f9fb3a..98f5face5e96 100644
--- a/packages/SettingsProvider/src/android/provider/settings/backup/SystemSettings.java
+++ b/packages/SettingsProvider/src/android/provider/settings/backup/SystemSettings.java
@@ -124,7 +124,8 @@ public class SystemSettings {
Settings.System.NOTIFICATION_COOLDOWN_ENABLED,
Settings.System.NOTIFICATION_COOLDOWN_ALL,
Settings.System.NOTIFICATION_COOLDOWN_VIBRATE_UNLOCKED,
- Settings.System.PREFERRED_REGION
+ Settings.System.PREFERRED_REGION,
+ Settings.System.CV_ENABLED
));
if (Flags.backUpSmoothDisplayAndForcePeakRefreshRate()) {
settings.add(Settings.System.PEAK_REFRESH_RATE);
diff --git a/packages/SettingsProvider/src/android/provider/settings/validators/GlobalSettingsValidators.java b/packages/SettingsProvider/src/android/provider/settings/validators/GlobalSettingsValidators.java
index 4c6a1ba7db0a..cd6521ff0dc5 100644
--- a/packages/SettingsProvider/src/android/provider/settings/validators/GlobalSettingsValidators.java
+++ b/packages/SettingsProvider/src/android/provider/settings/validators/GlobalSettingsValidators.java
@@ -474,5 +474,7 @@ public class GlobalSettingsValidators {
String.valueOf(
Global.Wearable.STATUS_TRAY_CONFIGURATION_SYSTEM_HIDDEN)
}));
+ VALIDATORS.put(Global.Wearable.GESTURE_PRIMARY_ACTION_USER_PREFERENCE, BOOLEAN_VALIDATOR);
+ VALIDATORS.put(Global.Wearable.GESTURE_DISMISS_ACTION_USER_PREFERENCE, BOOLEAN_VALIDATOR);
}
}
diff --git a/packages/SettingsProvider/src/android/provider/settings/validators/SystemSettingsValidators.java b/packages/SettingsProvider/src/android/provider/settings/validators/SystemSettingsValidators.java
index 4f649ed49be3..3a584401ed72 100644
--- a/packages/SettingsProvider/src/android/provider/settings/validators/SystemSettingsValidators.java
+++ b/packages/SettingsProvider/src/android/provider/settings/validators/SystemSettingsValidators.java
@@ -271,5 +271,7 @@ public class SystemSettingsValidators {
VALIDATORS.put(System.NOTIFICATION_COOLDOWN_ALL, BOOLEAN_VALIDATOR);
VALIDATORS.put(System.NOTIFICATION_COOLDOWN_VIBRATE_UNLOCKED, BOOLEAN_VALIDATOR);
VALIDATORS.put(System.PREFERRED_REGION, ANY_STRING_VALIDATOR);
+ VALIDATORS.put(System.CV_ENABLED,
+ new InclusiveIntegerRangeValidator(0, 1));
}
}
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/OWNERS b/packages/SettingsProvider/src/com/android/providers/settings/OWNERS
index b0086c180cbd..78c87b389dfc 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/OWNERS
+++ b/packages/SettingsProvider/src/com/android/providers/settings/OWNERS
@@ -1,2 +1,2 @@
-per-file WritableNamespacePrefixes.java = mpgroover@google.com,tedbauer@google.com
-per-file WritableNamespaces.java = mpgroover@google.com,tedbauer@google.com
+per-file WritableNamespacePrefixes.java = mpgroover@google.com
+per-file WritableNamespaces.java = mpgroover@google.com
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
index e7527dcde95c..584b21adbe77 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
@@ -3157,6 +3157,12 @@ class SettingsProtoDumpUtil {
SystemSettingsProto.Volume.MASTER_BALANCE);
p.end(volumeToken);
+ final long systemDisplayToken = p.start(SystemSettingsProto.DISPLAY);
+ dumpSetting(s, p,
+ Settings.System.CV_ENABLED,
+ SystemSettingsProto.Display.CV_ENABLED);
+ p.end(systemDisplayToken);
+
dumpSetting(s, p,
Settings.System.WHEN_TO_MAKE_WIFI_CALLS,
SystemSettingsProto.WHEN_TO_MAKE_WIFI_CALLS);
diff --git a/packages/Shell/src/com/android/shell/BugreportProgressService.java b/packages/Shell/src/com/android/shell/BugreportProgressService.java
index fb0678fedb56..5bba99f84d43 100644
--- a/packages/Shell/src/com/android/shell/BugreportProgressService.java
+++ b/packages/Shell/src/com/android/shell/BugreportProgressService.java
@@ -255,11 +255,11 @@ public class BugreportProgressService extends Service {
/** Always keep remote bugreport files created in the last day. */
private static final long REMOTE_MIN_KEEP_AGE = DateUtils.DAY_IN_MILLIS;
- /** Minimum delay for sending last update notification */
- private static final int DELAY_NOTIFICATION_MS = 250;
-
private final Object mLock = new Object();
+/** Minimum delay between percentage points before sending an update notification */
+ private static final int MIN_NOTIFICATION_GAP = 10;
+
/** Managed bugreport info (keyed by id) */
@GuardedBy("mLock")
private final SparseArray<BugreportInfo> mBugreportInfos = new SparseArray<>();
@@ -1460,17 +1460,6 @@ public class BugreportProgressService extends Service {
* Sends a notification indicating the bugreport has finished so use can share it.
*/
private void sendBugreportNotification(BugreportInfo info, boolean takingScreenshot) {
-
- final long lastUpdate = System.currentTimeMillis() - info.lastUpdate.longValue();
- if (lastUpdate < DELAY_NOTIFICATION_MS) {
- Log.d(TAG, "Delaying final notification for "
- + (DELAY_NOTIFICATION_MS - lastUpdate) + " ms ");
- mMainThreadHandler.postDelayed(() -> {
- sendBugreportNotification(info, takingScreenshot);
- }, DELAY_NOTIFICATION_MS - lastUpdate);
- return;
- }
-
// Since adding the details can take a while, do it before notifying user.
addDetailsToZipFile(info);
@@ -1523,7 +1512,7 @@ public class BugreportProgressService extends Service {
builder.setSubText(info.getName());
}
- Log.v(TAG, "Sending 'Share' notification for ID " + info.id + ": " + title);
+ Log.d(TAG, "Sending 'Share' notification for ID " + info.id + ": " + title);
NotificationManager.from(mContext).notify(info.id, builder.build());
}
@@ -2753,6 +2742,11 @@ public class BugreportProgressService extends Service {
if (progress > CAPPED_PROGRESS) {
progress = CAPPED_PROGRESS;
}
+
+ if ((progress - info.lastProgress.intValue()) < MIN_NOTIFICATION_GAP) {
+ return;
+ }
+
if (DEBUG) {
if (progress != info.progress.intValue()) {
Log.v(TAG, "Updating progress for name " + info.getName() + "(id: " + info.id
diff --git a/packages/SystemUI/aconfig/systemui.aconfig b/packages/SystemUI/aconfig/systemui.aconfig
index ab18612355f0..4693377654f8 100644
--- a/packages/SystemUI/aconfig/systemui.aconfig
+++ b/packages/SystemUI/aconfig/systemui.aconfig
@@ -213,18 +213,6 @@ flag {
}
flag {
- name: "notification_undo_guts_on_config_changed"
- namespace: "systemui"
- description: "Fixes a bug where a theme or font change while notification guts were open"
- " (e.g. the snooze options or notification info) would show an empty notification by"
- " closing the guts and undoing changes."
- bug: "379267630"
- metadata {
- purpose: PURPOSE_BUGFIX
- }
-}
-
-flag {
name: "pss_app_selector_recents_split_screen"
namespace: "systemui"
description: "Allows recent apps selected for partial screenshare to be launched in split screen mode"
diff --git a/packages/SystemUI/animation/src/com/android/systemui/animation/RemoteAnimationRunnerCompat.java b/packages/SystemUI/animation/src/com/android/systemui/animation/RemoteAnimationRunnerCompat.java
index 694fc8eb6ad7..ca94482b9c5a 100644
--- a/packages/SystemUI/animation/src/com/android/systemui/animation/RemoteAnimationRunnerCompat.java
+++ b/packages/SystemUI/animation/src/com/android/systemui/animation/RemoteAnimationRunnerCompat.java
@@ -22,6 +22,7 @@ import static android.view.WindowManager.TRANSIT_OLD_NONE;
import static android.view.WindowManager.TRANSIT_OPEN;
import static android.view.WindowManager.TRANSIT_TO_BACK;
import static android.view.WindowManager.TRANSIT_TO_FRONT;
+import static android.window.DesktopModeFlags.ENABLE_DESKTOP_WINDOWING_EXIT_BY_MINIMIZE_TRANSITION_BUGFIX;
import static android.window.DesktopModeFlags.ENABLE_DESKTOP_WINDOWING_EXIT_TRANSITIONS_BUGFIX;
import static android.window.TransitionInfo.FLAG_IS_WALLPAPER;
@@ -261,7 +262,8 @@ public abstract class RemoteAnimationRunnerCompat extends IRemoteAnimationRunner
SurfaceControl.Transaction startTransaction
) {
checkArgument(isOpeningMode(launcherChange.getMode()));
- if (!isClosingType(info.getType())) {
+ if (!isClosingType(info.getType())
+ && !ENABLE_DESKTOP_WINDOWING_EXIT_BY_MINIMIZE_TRANSITION_BUGFIX.isTrue()) {
return;
}
for (int i = info.getChanges().size() - 1; i >= 0; --i) {
diff --git a/packages/SystemUI/compose/core/src/com/android/compose/animation/Expandable.kt b/packages/SystemUI/compose/core/src/com/android/compose/animation/Expandable.kt
index a352b1eebb81..82e5f5bb6dc8 100644
--- a/packages/SystemUI/compose/core/src/com/android/compose/animation/Expandable.kt
+++ b/packages/SystemUI/compose/core/src/com/android/compose/animation/Expandable.kt
@@ -21,7 +21,6 @@ import android.view.View
import android.view.ViewGroup
import android.view.ViewGroupOverlay
import androidx.compose.foundation.BorderStroke
-import androidx.compose.foundation.background
import androidx.compose.foundation.border
import androidx.compose.foundation.clickable
import androidx.compose.foundation.interaction.MutableInteractionSource
@@ -62,6 +61,7 @@ import androidx.compose.ui.graphics.drawscope.ContentDrawScope
import androidx.compose.ui.graphics.drawscope.Stroke
import androidx.compose.ui.graphics.drawscope.scale
import androidx.compose.ui.graphics.drawscope.translate
+import androidx.compose.ui.graphics.isSpecified
import androidx.compose.ui.graphics.layer.GraphicsLayer
import androidx.compose.ui.graphics.layer.drawLayer
import androidx.compose.ui.graphics.rememberGraphicsLayer
@@ -82,6 +82,7 @@ import androidx.lifecycle.setViewTreeLifecycleOwner
import androidx.lifecycle.setViewTreeViewModelStoreOwner
import androidx.savedstate.findViewTreeSavedStateRegistryOwner
import androidx.savedstate.setViewTreeSavedStateRegistryOwner
+import com.android.compose.modifiers.animatedBackground
import com.android.compose.modifiers.thenIf
import com.android.compose.ui.graphics.FullScreenComposeViewInOverlay
import com.android.systemui.animation.ComposableControllerFactory
@@ -291,7 +292,7 @@ fun Expandable(
.updateExpandableSize()
.then(minInteractiveSizeModifier)
.then(clickModifier(controller, onClick, interactionSource))
- .background(color, shape)
+ .animatedBackground(color, shape = shape)
.border(controller)
.onGloballyPositioned { controller.boundsInComposeViewRoot = it.boundsInRoot() }
) {
@@ -307,19 +308,27 @@ private fun WrappedContent(
contentColor: Color,
content: @Composable (Expandable) -> Unit,
) {
- CompositionLocalProvider(LocalContentColor provides contentColor) {
- // We make sure that the content itself (wrapped by the background) is at least 40.dp, which
- // is the same as the M3 buttons. This applies even if onClick is null, to make it easier to
- // write expandables that are sometimes clickable and sometimes not. There shouldn't be any
- // Expandable smaller than 40dp because if the expandable is not clickable directly, then
- // something in its content should be (and with a size >= 40dp).
- val minSize = 40.dp
- Box(
- Modifier.defaultMinSize(minWidth = minSize, minHeight = minSize),
- contentAlignment = Alignment.Center,
- ) {
- content(expandable)
+ val minSizeContent =
+ @Composable {
+ // We make sure that the content itself (wrapped by the background) is at least 40.dp,
+ // which is the same as the M3 buttons. This applies even if onClick is null, to make it
+ // easier to write expandables that are sometimes clickable and sometimes not. There
+ // shouldn't be any Expandable smaller than 40dp because if the expandable is not
+ // clickable directly, then something in its content should be (and with a size >=
+ // 40dp).
+ val minSize = 40.dp
+ Box(
+ Modifier.defaultMinSize(minWidth = minSize, minHeight = minSize),
+ contentAlignment = Alignment.Center,
+ ) {
+ content(expandable)
+ }
}
+
+ if (contentColor.isSpecified) {
+ CompositionLocalProvider(LocalContentColor provides contentColor, content = minSizeContent)
+ } else {
+ minSizeContent()
}
}
@@ -345,7 +354,7 @@ private fun Modifier.expandable(
.thenIf(drawContent) {
Modifier.border(controller)
.then(clickModifier(controller, onClick, interactionSource))
- .background(controller.color, controller.shape)
+ .animatedBackground(controller.color, shape = controller.shape)
}
.onPlaced { controller.boundsInComposeViewRoot = it.boundsInRoot() }
.drawWithContent {
@@ -422,7 +431,7 @@ private class DrawExpandableInOverlayNode(
// Background.
this@draw.drawBackground(
state,
- controller.color,
+ controller.color(),
controller.borderStroke,
size = Size(state.width.toFloat(), state.height.toFloat()),
)
@@ -469,7 +478,7 @@ private fun clickModifier(
/** Draw [content] in [overlay] while respecting its screen position given by [animatorState]. */
@Composable
private fun AnimatedContentInOverlay(
- color: Color,
+ color: () -> Color,
sizeInOriginalLayout: Size,
overlay: ViewGroupOverlay,
controller: ExpandableControllerImpl,
@@ -523,7 +532,7 @@ private fun AnimatedContentInOverlay(
return@drawWithContent
}
- drawBackground(animatorState, color, controller.borderStroke)
+ drawBackground(animatorState, color(), controller.borderStroke)
drawContent()
},
// We center the content in the expanding container.
diff --git a/packages/SystemUI/compose/core/src/com/android/compose/animation/ExpandableController.kt b/packages/SystemUI/compose/core/src/com/android/compose/animation/ExpandableController.kt
index 72da175e26cf..d7d5a48e2b79 100644
--- a/packages/SystemUI/compose/core/src/com/android/compose/animation/ExpandableController.kt
+++ b/packages/SystemUI/compose/core/src/com/android/compose/animation/ExpandableController.kt
@@ -26,7 +26,6 @@ import androidx.compose.material3.contentColorFor
import androidx.compose.runtime.Composable
import androidx.compose.runtime.DisposableEffect
import androidx.compose.runtime.Stable
-import androidx.compose.runtime.State
import androidx.compose.runtime.derivedStateOf
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
@@ -80,6 +79,24 @@ fun rememberExpandableController(
borderStroke: BorderStroke? = null,
transitionControllerFactory: ComposableControllerFactory? = null,
): ExpandableController {
+ return rememberExpandableController(
+ color = { color },
+ shape = shape,
+ contentColor = contentColor,
+ borderStroke = borderStroke,
+ transitionControllerFactory = transitionControllerFactory,
+ )
+}
+
+/** Create an [ExpandableController] to control an [Expandable]. */
+@Composable
+fun rememberExpandableController(
+ color: () -> Color,
+ shape: Shape,
+ contentColor: Color = Color.Unspecified,
+ borderStroke: BorderStroke? = null,
+ transitionControllerFactory: ComposableControllerFactory? = null,
+): ExpandableController {
val composeViewRoot = LocalView.current
val density = LocalDensity.current
val layoutDirection = LocalLayoutDirection.current
@@ -125,7 +142,7 @@ fun rememberExpandableController(
}
internal class ExpandableControllerImpl(
- internal val color: Color,
+ internal val color: () -> Color,
internal val contentColor: Color,
internal val shape: Shape,
internal val borderStroke: BorderStroke?,
diff --git a/packages/SystemUI/compose/core/src/com/android/compose/gesture/NestedDraggable.kt b/packages/SystemUI/compose/core/src/com/android/compose/gesture/NestedDraggable.kt
index 959f28f2d99e..80cbbea7659f 100644
--- a/packages/SystemUI/compose/core/src/com/android/compose/gesture/NestedDraggable.kt
+++ b/packages/SystemUI/compose/core/src/com/android/compose/gesture/NestedDraggable.kt
@@ -95,10 +95,20 @@ interface NestedDraggable {
* nested scrollable.
*
* This is called whenever a nested scrollable does not consume some scroll amount. If this
- * returns `true`, then [onDragStarted] will be called and this draggable will have priority and
+ * returns `true`, then [onDragStarted] will be called, this draggable will have priority and
* consume all future events during preScroll until the nested scroll is finished.
*/
- fun shouldConsumeNestedScroll(sign: Float): Boolean
+ fun shouldConsumeNestedPostScroll(sign: Float): Boolean = true
+
+ /**
+ * Whether this draggable should consume any scroll amount with the given [sign] *before* it can
+ * be consumed by a nested scrollable.
+ *
+ * This is called before a nested scrollable is able to consume that scroll amount. If this
+ * returns `true`, then [onDragStarted] will be called, this draggable will have priority and
+ * consume all future scroll events during preScroll until the nested scroll is finished.
+ */
+ fun shouldConsumeNestedPreScroll(sign: Float): Boolean = false
interface Controller {
/**
@@ -540,6 +550,14 @@ private class NestedDraggableNode(
}
override fun onPreScroll(available: Offset, source: NestedScrollSource): Offset {
+ val sign = available.toFloat().sign
+ maybeCreateNewController(
+ sign = sign,
+ condition = {
+ source == NestedScrollSource.UserInput &&
+ draggable.shouldConsumeNestedPreScroll(sign)
+ },
+ )
val controller = nestedScrollController ?: return Offset.Zero
return scrollWithOverscroll(controller, available)
}
@@ -560,28 +578,34 @@ private class NestedDraggableNode(
}
val sign = offset.sign
+ maybeCreateNewController(
+ sign,
+ condition = { draggable.shouldConsumeNestedPostScroll(sign) },
+ )
+ val controller = nestedScrollController ?: return Offset.Zero
+ return scrollWithOverscroll(controller, available)
+ }
+
+ private fun maybeCreateNewController(sign: Float, condition: () -> Boolean) {
if (
- nestedDragsEnabled &&
- nestedScrollController == null &&
- // TODO(b/388231324): Remove this.
- !lastEventWasScrollWheel &&
- draggable.shouldConsumeNestedScroll(sign) &&
- lastFirstDown != null
+ !nestedDragsEnabled ||
+ nestedScrollController != null ||
+ lastEventWasScrollWheel ||
+ lastFirstDown == null ||
+ !condition()
) {
- val startedPosition = checkNotNull(lastFirstDown)
-
- // TODO(b/382665591): Ensure that there is at least one pointer down.
- val pointersDownCount = pointersDown.size.coerceAtLeast(1)
- val pointerType = pointersDown.entries.firstOrNull()?.value
- nestedScrollController =
- NestedScrollController(
- overscrollEffect,
- draggable.onDragStarted(startedPosition, sign, pointersDownCount, pointerType),
- )
+ return
}
- val controller = nestedScrollController ?: return Offset.Zero
- return scrollWithOverscroll(controller, available)
+ // TODO(b/382665591): Ensure that there is at least one pointer down.
+ val pointersDownCount = pointersDown.size.coerceAtLeast(1)
+ val pointerType = pointersDown.entries.firstOrNull()?.value
+ val startedPosition = checkNotNull(lastFirstDown)
+ nestedScrollController =
+ NestedScrollController(
+ overscrollEffect,
+ draggable.onDragStarted(startedPosition, sign, pointersDownCount, pointerType),
+ )
}
private fun scrollWithOverscroll(controller: NestedScrollController, offset: Offset): Offset {
diff --git a/packages/SystemUI/compose/core/src/com/android/compose/modifiers/AnimatedBackground.kt b/packages/SystemUI/compose/core/src/com/android/compose/modifiers/AnimatedBackground.kt
new file mode 100644
index 000000000000..5b1f0a7c6eb6
--- /dev/null
+++ b/packages/SystemUI/compose/core/src/com/android/compose/modifiers/AnimatedBackground.kt
@@ -0,0 +1,159 @@
+/*
+ * Copyright (C) 2022 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.modifiers
+
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.geometry.Size
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.graphics.Outline
+import androidx.compose.ui.graphics.RectangleShape
+import androidx.compose.ui.graphics.Shape
+import androidx.compose.ui.graphics.drawOutline
+import androidx.compose.ui.graphics.drawscope.ContentDrawScope
+import androidx.compose.ui.node.DrawModifierNode
+import androidx.compose.ui.node.ModifierNodeElement
+import androidx.compose.ui.node.ObserverModifierNode
+import androidx.compose.ui.node.invalidateDraw
+import androidx.compose.ui.node.observeReads
+import androidx.compose.ui.platform.InspectorInfo
+import androidx.compose.ui.platform.debugInspectorInfo
+import androidx.compose.ui.unit.LayoutDirection
+
+/**
+ * Draws a background in a given [shape] and with a [color] or [alpha] that can be animated.
+ *
+ * @param color color to paint background with
+ * @param alpha alpha of the background
+ * @param shape desired shape of the background
+ */
+fun Modifier.animatedBackground(
+ color: () -> Color,
+ alpha: () -> Float = DefaultAlpha,
+ shape: Shape = RectangleShape,
+) =
+ this.then(
+ BackgroundElement(
+ color = color,
+ alpha = alpha,
+ shape = shape,
+ inspectorInfo =
+ debugInspectorInfo {
+ name = "background"
+ value = color
+ properties["color"] = color
+ properties["alpha"] = alpha
+ properties["shape"] = shape
+ },
+ )
+ )
+
+private val DefaultAlpha = { 1f }
+
+private class BackgroundElement(
+ private val color: () -> Color,
+ private val alpha: () -> Float,
+ private val shape: Shape,
+ private val inspectorInfo: InspectorInfo.() -> Unit,
+) : ModifierNodeElement<BackgroundNode>() {
+ override fun create(): BackgroundNode {
+ return BackgroundNode(color, alpha, shape)
+ }
+
+ override fun update(node: BackgroundNode) {
+ node.color = color
+ node.alpha = alpha
+ node.shape = shape
+ }
+
+ override fun InspectorInfo.inspectableProperties() {
+ inspectorInfo()
+ }
+
+ override fun hashCode(): Int {
+ var result = color.hashCode()
+ result = 31 * result + alpha.hashCode()
+ result = 31 * result + shape.hashCode()
+ return result
+ }
+
+ override fun equals(other: Any?): Boolean {
+ val otherModifier = other as? BackgroundElement ?: return false
+ return color == otherModifier.color &&
+ alpha == otherModifier.alpha &&
+ shape == otherModifier.shape
+ }
+}
+
+private class BackgroundNode(var color: () -> Color, var alpha: () -> Float, var shape: Shape) :
+ DrawModifierNode, Modifier.Node(), ObserverModifierNode {
+
+ // Naively cache outline calculation if input parameters are the same, we manually observe
+ // reads inside shape#createOutline separately
+ private var lastSize: Size = Size.Unspecified
+ private var lastLayoutDirection: LayoutDirection? = null
+ private var lastOutline: Outline? = null
+ private var lastShape: Shape? = null
+ private var tmpOutline: Outline? = null
+
+ override fun ContentDrawScope.draw() {
+ if (shape === RectangleShape) {
+ // shortcut to avoid Outline calculation and allocation
+ drawRect()
+ } else {
+ drawOutline()
+ }
+ drawContent()
+ }
+
+ override fun onObservedReadsChanged() {
+ // Reset cached properties
+ lastSize = Size.Unspecified
+ lastLayoutDirection = null
+ lastOutline = null
+ lastShape = null
+ // Invalidate draw so we build the cache again - this is needed because observeReads within
+ // the draw scope obscures the state reads from the draw scope's observer
+ invalidateDraw()
+ }
+
+ private fun ContentDrawScope.drawRect() {
+ drawRect(color = color(), alpha = alpha())
+ }
+
+ private fun ContentDrawScope.drawOutline() {
+ val outline = getOutline()
+ drawOutline(outline, color = color(), alpha = alpha())
+ }
+
+ private fun ContentDrawScope.getOutline(): Outline {
+ val outline: Outline?
+ if (size == lastSize && layoutDirection == lastLayoutDirection && lastShape == shape) {
+ outline = lastOutline!!
+ } else {
+ // Manually observe reads so we can directly invalidate the outline when it changes
+ // Use tmpOutline to avoid creating an object reference to local var outline
+ observeReads { tmpOutline = shape.createOutline(size, layoutDirection, this) }
+ outline = tmpOutline
+ tmpOutline = null
+ }
+ lastOutline = outline
+ lastSize = size
+ lastLayoutDirection = layoutDirection
+ lastShape = shape
+ return outline!!
+ }
+}
diff --git a/packages/SystemUI/compose/core/src/com/android/compose/modifiers/FadingBackground.kt b/packages/SystemUI/compose/core/src/com/android/compose/modifiers/FadingBackground.kt
deleted file mode 100644
index 794b7a4a3d30..000000000000
--- a/packages/SystemUI/compose/core/src/com/android/compose/modifiers/FadingBackground.kt
+++ /dev/null
@@ -1,113 +0,0 @@
-/*
- * Copyright (C) 2022 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.modifiers
-
-import androidx.compose.ui.Modifier
-import androidx.compose.ui.draw.DrawModifier
-import androidx.compose.ui.geometry.Size
-import androidx.compose.ui.graphics.Brush
-import androidx.compose.ui.graphics.Color
-import androidx.compose.ui.graphics.Outline
-import androidx.compose.ui.graphics.RectangleShape
-import androidx.compose.ui.graphics.Shape
-import androidx.compose.ui.graphics.SolidColor
-import androidx.compose.ui.graphics.drawOutline
-import androidx.compose.ui.graphics.drawscope.ContentDrawScope
-import androidx.compose.ui.platform.InspectorInfo
-import androidx.compose.ui.platform.InspectorValueInfo
-import androidx.compose.ui.platform.debugInspectorInfo
-import androidx.compose.ui.unit.LayoutDirection
-
-/**
- * Draws a fading [shape] with a solid [color] and [alpha] behind the content.
- *
- * @param color color to paint background with
- * @param alpha alpha of the background
- * @param shape desired shape of the background
- */
-fun Modifier.fadingBackground(color: Color, alpha: () -> Float, shape: Shape = RectangleShape) =
- this.then(
- FadingBackground(
- brush = SolidColor(color),
- alpha = alpha,
- shape = shape,
- inspectorInfo =
- debugInspectorInfo {
- name = "background"
- value = color
- properties["color"] = color
- properties["alpha"] = alpha
- properties["shape"] = shape
- },
- )
- )
-
-private class FadingBackground
-constructor(
- private val brush: Brush,
- private val shape: Shape,
- private val alpha: () -> Float,
- inspectorInfo: InspectorInfo.() -> Unit,
-) : DrawModifier, InspectorValueInfo(inspectorInfo) {
- // naive cache outline calculation if size is the same
- private var lastSize: Size? = null
- private var lastLayoutDirection: LayoutDirection? = null
- private var lastOutline: Outline? = null
-
- override fun ContentDrawScope.draw() {
- if (shape === RectangleShape) {
- // shortcut to avoid Outline calculation and allocation
- drawRect()
- } else {
- drawOutline()
- }
- drawContent()
- }
-
- private fun ContentDrawScope.drawRect() {
- drawRect(brush, alpha = alpha())
- }
-
- private fun ContentDrawScope.drawOutline() {
- val outline =
- if (size == lastSize && layoutDirection == lastLayoutDirection) {
- lastOutline!!
- } else {
- shape.createOutline(size, layoutDirection, this)
- }
- drawOutline(outline, brush = brush, alpha = alpha())
- lastOutline = outline
- lastSize = size
- lastLayoutDirection = layoutDirection
- }
-
- override fun hashCode(): Int {
- var result = brush.hashCode()
- result = 31 * result + alpha.hashCode()
- result = 31 * result + shape.hashCode()
- return result
- }
-
- override fun equals(other: Any?): Boolean {
- val otherModifier = other as? FadingBackground ?: return false
- return brush == otherModifier.brush &&
- alpha == otherModifier.alpha &&
- shape == otherModifier.shape
- }
-
- override fun toString(): String = "FadingBackground(brush=$brush, alpha = $alpha, shape=$shape)"
-}
diff --git a/packages/SystemUI/compose/core/tests/src/com/android/compose/gesture/NestedDraggableTest.kt b/packages/SystemUI/compose/core/tests/src/com/android/compose/gesture/NestedDraggableTest.kt
index d8e46ad34c37..a03d769c9c36 100644
--- a/packages/SystemUI/compose/core/tests/src/com/android/compose/gesture/NestedDraggableTest.kt
+++ b/packages/SystemUI/compose/core/tests/src/com/android/compose/gesture/NestedDraggableTest.kt
@@ -971,6 +971,35 @@ class NestedDraggableTest(override val orientation: Orientation) : OrientationAw
assertThat(availableToEffectPostFling).isWithin(1f).of(100f)
}
+ @Test
+ fun consumeNestedPreScroll() {
+ var consumeNestedPreScroll by mutableStateOf(false)
+ val draggable = TestDraggable(shouldConsumeNestedPreScroll = { consumeNestedPreScroll })
+
+ val touchSlop =
+ rule.setContentWithTouchSlop {
+ Box(
+ Modifier.fillMaxSize()
+ .nestedDraggable(draggable, orientation)
+ // Always consume everything so that the only way to start the drag is to
+ // intercept preScroll events.
+ .scrollable(rememberScrollableState { it }, orientation)
+ )
+ }
+
+ rule.onRoot().performTouchInput {
+ down(center)
+ moveBy((touchSlop + 1f).toOffset())
+ }
+
+ assertThat(draggable.onDragStartedCalled).isFalse()
+
+ consumeNestedPreScroll = true
+ rule.onRoot().performTouchInput { moveBy(1f.toOffset()) }
+
+ assertThat(draggable.onDragStartedCalled).isTrue()
+ }
+
private fun ComposeContentTestRule.setContentWithTouchSlop(
content: @Composable () -> Unit
): Float {
@@ -996,7 +1025,8 @@ class NestedDraggableTest(override val orientation: Orientation) : OrientationAw
{ velocity, _ ->
velocity
},
- private val shouldConsumeNestedScroll: (Float) -> Boolean = { true },
+ private val shouldConsumeNestedPostScroll: (Float) -> Boolean = { true },
+ private val shouldConsumeNestedPreScroll: (Float) -> Boolean = { false },
) : NestedDraggable {
var shouldStartDrag = true
var onDragStartedCalled = false
@@ -1042,8 +1072,12 @@ class NestedDraggableTest(override val orientation: Orientation) : OrientationAw
}
}
- override fun shouldConsumeNestedScroll(sign: Float): Boolean {
- return shouldConsumeNestedScroll.invoke(sign)
+ override fun shouldConsumeNestedPostScroll(sign: Float): Boolean {
+ return shouldConsumeNestedPostScroll.invoke(sign)
+ }
+
+ override fun shouldConsumeNestedPreScroll(sign: Float): Boolean {
+ return shouldConsumeNestedPreScroll.invoke(sign)
}
}
}
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/qs/footer/ui/compose/FooterActions.kt b/packages/SystemUI/compose/features/src/com/android/systemui/qs/footer/ui/compose/FooterActions.kt
index 216f0a74e1c7..7782705d4c61 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/qs/footer/ui/compose/FooterActions.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/qs/footer/ui/compose/FooterActions.kt
@@ -73,7 +73,7 @@ import androidx.lifecycle.compose.collectAsStateWithLifecycle
import androidx.lifecycle.repeatOnLifecycle
import com.android.compose.animation.Expandable
import com.android.compose.animation.scene.ContentScope
-import com.android.compose.modifiers.fadingBackground
+import com.android.compose.modifiers.animatedBackground
import com.android.compose.theme.colorAttr
import com.android.systemui.Flags.notificationShadeBlur
import com.android.systemui.animation.Expandable
@@ -172,8 +172,8 @@ fun FooterActions(
val backgroundTopRadius = dimensionResource(R.dimen.qs_corner_radius)
val backgroundModifier =
remember(backgroundColor, backgroundAlphaValue, backgroundTopRadius) {
- Modifier.fadingBackground(
- backgroundColor,
+ Modifier.animatedBackground(
+ { backgroundColor },
backgroundAlphaValue,
RoundedCornerShape(topStart = backgroundTopRadius, topEnd = backgroundTopRadius),
)
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/SceneContainer.kt b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/SceneContainer.kt
index 7015f79e4a9f..0daef465bf1f 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/SceneContainer.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/SceneContainer.kt
@@ -101,7 +101,7 @@ fun SceneContainer(
rememberActivated(traceName = "sceneJankMonitor") { sceneJankMonitorFactory.create() }
val hapticFeedback = LocalHapticFeedback.current
- val shadeExpansionMotion = OverlayShade.rememberShadeExpansionMotion()
+ val shadeExpansionMotion = OverlayShade.rememberShadeExpansionMotion(isFullWidthShade())
val sceneTransitions =
remember(hapticFeedback, shadeExpansionMotion) {
transitionsBuilder.build(
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/SceneContainerTransitions.kt b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/SceneContainerTransitions.kt
index 9b45ef693ce6..2f5a0306c84f 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/SceneContainerTransitions.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/SceneContainerTransitions.kt
@@ -6,7 +6,7 @@ import com.android.compose.animation.scene.TransitionKey
import com.android.compose.animation.scene.reveal.ContainerRevealHaptics
import com.android.compose.animation.scene.transitions
import com.android.internal.jank.Cuj
-import com.android.mechanics.behavior.EdgeContainerExpansionSpec
+import com.android.mechanics.behavior.VerticalExpandContainerSpec
import com.android.systemui.notifications.ui.composable.Notifications
import com.android.systemui.scene.shared.model.Overlays
import com.android.systemui.scene.shared.model.Scenes
@@ -50,7 +50,7 @@ import com.android.systemui.shade.ui.composable.Shade
*/
class SceneContainerTransitions : SceneContainerTransitionsBuilder {
override fun build(
- shadeExpansionMotion: EdgeContainerExpansionSpec,
+ shadeExpansionMotion: VerticalExpandContainerSpec,
revealHaptics: ContainerRevealHaptics,
): SceneTransitions {
return transitions {
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/SceneContainerTransitionsBuilder.kt b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/SceneContainerTransitionsBuilder.kt
index eb5548d45247..4c9c23ad9f9f 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/SceneContainerTransitionsBuilder.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/SceneContainerTransitionsBuilder.kt
@@ -19,7 +19,7 @@ package com.android.systemui.scene.ui.composable
import com.android.compose.animation.scene.SceneTransitions
import com.android.compose.animation.scene.reveal.ContainerRevealHaptics
import com.android.compose.animation.scene.transitions
-import com.android.mechanics.behavior.EdgeContainerExpansionSpec
+import com.android.mechanics.behavior.VerticalExpandContainerSpec
/**
* Builder of the comprehensive definition of all transitions between scenes and overlays in the
@@ -29,7 +29,7 @@ interface SceneContainerTransitionsBuilder {
/** Build the [SceneContainer] transitions spec. */
fun build(
- shadeExpansionMotion: EdgeContainerExpansionSpec,
+ shadeExpansionMotion: VerticalExpandContainerSpec,
revealHaptics: ContainerRevealHaptics,
): SceneTransitions
}
@@ -42,7 +42,7 @@ class ConstantSceneContainerTransitionsBuilder(
private val transitions: SceneTransitions = transitions { /* No transitions */ }
) : SceneContainerTransitionsBuilder {
override fun build(
- shadeExpansionMotion: EdgeContainerExpansionSpec,
+ shadeExpansionMotion: VerticalExpandContainerSpec,
revealHaptics: ContainerRevealHaptics,
): SceneTransitions = transitions
}
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/transitions/ToNotificationsShadeTransition.kt b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/transitions/ToNotificationsShadeTransition.kt
index 9b4b91eb23c1..85aad9b087f1 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/transitions/ToNotificationsShadeTransition.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/transitions/ToNotificationsShadeTransition.kt
@@ -20,7 +20,7 @@ import androidx.compose.animation.core.tween
import com.android.compose.animation.scene.TransitionBuilder
import com.android.compose.animation.scene.reveal.ContainerRevealHaptics
import com.android.compose.animation.scene.reveal.verticalContainerReveal
-import com.android.mechanics.behavior.EdgeContainerExpansionSpec
+import com.android.mechanics.behavior.VerticalExpandContainerSpec
import com.android.systemui.keyguard.ui.composable.blueprint.ClockElementKeys
import com.android.systemui.notifications.ui.composable.NotificationsShade
import com.android.systemui.scene.shared.model.Overlays
@@ -29,7 +29,7 @@ import kotlin.time.Duration.Companion.milliseconds
fun TransitionBuilder.toNotificationsShadeTransition(
durationScale: Double = 1.0,
- shadeExpansionMotion: EdgeContainerExpansionSpec,
+ shadeExpansionMotion: VerticalExpandContainerSpec,
revealHaptics: ContainerRevealHaptics,
) {
spec = tween(durationMillis = (DefaultDuration * durationScale).inWholeMilliseconds.toInt())
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/transitions/ToQuickSettingsShadeTransition.kt b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/transitions/ToQuickSettingsShadeTransition.kt
index 47dd85f17cee..8f0447d05036 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/transitions/ToQuickSettingsShadeTransition.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/transitions/ToQuickSettingsShadeTransition.kt
@@ -20,14 +20,14 @@ import androidx.compose.animation.core.tween
import com.android.compose.animation.scene.TransitionBuilder
import com.android.compose.animation.scene.reveal.ContainerRevealHaptics
import com.android.compose.animation.scene.reveal.verticalContainerReveal
-import com.android.mechanics.behavior.EdgeContainerExpansionSpec
+import com.android.mechanics.behavior.VerticalExpandContainerSpec
import com.android.systemui.qs.ui.composable.QuickSettingsShade
import com.android.systemui.shade.ui.composable.OverlayShade
import kotlin.time.Duration.Companion.milliseconds
fun TransitionBuilder.toQuickSettingsShadeTransition(
durationScale: Double = 1.0,
- shadeExpansionMotion: EdgeContainerExpansionSpec,
+ shadeExpansionMotion: VerticalExpandContainerSpec,
revealHaptics: ContainerRevealHaptics,
) {
spec = tween(durationMillis = (DefaultDuration * durationScale).inWholeMilliseconds.toInt())
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/OverlayShade.kt b/packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/OverlayShade.kt
index 3446081fb123..068218a0053a 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/OverlayShade.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/OverlayShade.kt
@@ -53,8 +53,8 @@ import com.android.compose.animation.scene.ContentScope
import com.android.compose.animation.scene.ElementKey
import com.android.compose.animation.scene.LowestZIndexContentPicker
import com.android.compose.windowsizeclass.LocalWindowSizeClass
-import com.android.mechanics.behavior.EdgeContainerExpansionSpec
-import com.android.mechanics.behavior.edgeContainerExpansionBackground
+import com.android.mechanics.behavior.VerticalExpandContainerSpec
+import com.android.mechanics.behavior.verticalExpandContainerBackground
import com.android.systemui.res.R
import com.android.systemui.shade.ui.composable.OverlayShade.rememberShadeExpansionMotion
@@ -114,9 +114,9 @@ private fun ContentScope.Panel(
modifier =
modifier
.disableSwipesWhenScrolling()
- .edgeContainerExpansionBackground(
- OverlayShade.Colors.PanelBackground,
- rememberShadeExpansionMotion(),
+ .verticalExpandContainerBackground(
+ backgroundColor = OverlayShade.Colors.PanelBackground,
+ spec = rememberShadeExpansionMotion(isFullWidthShade()),
)
) {
Column {
@@ -202,8 +202,10 @@ object OverlayShade {
}
@Composable
- fun rememberShadeExpansionMotion(): EdgeContainerExpansionSpec {
+ fun rememberShadeExpansionMotion(isFullWidth: Boolean): VerticalExpandContainerSpec {
val radius = Dimensions.PanelCornerRadius
- return remember(radius) { EdgeContainerExpansionSpec(radius = radius) }
+ return remember(radius, isFullWidth) {
+ VerticalExpandContainerSpec(isFloating = !isFullWidth, radius = radius)
+ }
}
}
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 1360611ed814..024ca22069ae 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
@@ -68,7 +68,7 @@ internal class DraggableHandler(
return layoutImpl.swipeDetector.detectSwipe(change)
}
- override fun shouldConsumeNestedScroll(sign: Float): Boolean {
+ override fun shouldConsumeNestedPostScroll(sign: Float): Boolean {
return this.enabled()
}
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/reveal/ContainerReveal.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/reveal/ContainerReveal.kt
index 72f9bd5da742..734de34dcd2f 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/reveal/ContainerReveal.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/reveal/ContainerReveal.kt
@@ -29,7 +29,7 @@ import com.android.compose.animation.scene.transformation.CustomPropertyTransfor
import com.android.compose.animation.scene.transformation.PropertyTransformation
import com.android.compose.animation.scene.transformation.PropertyTransformationScope
import com.android.mechanics.MotionValue
-import com.android.mechanics.behavior.EdgeContainerExpansionSpec
+import com.android.mechanics.behavior.VerticalExpandContainerSpec
import kotlinx.coroutines.CoroutineScope
interface ContainerRevealHaptics {
@@ -53,7 +53,7 @@ interface ContainerRevealHaptics {
@OptIn(ExperimentalMaterial3ExpressiveApi::class)
fun TransitionBuilder.verticalContainerReveal(
container: ElementKey,
- motionSpec: EdgeContainerExpansionSpec,
+ motionSpec: VerticalExpandContainerSpec,
haptics: ContainerRevealHaptics,
) {
// Make the swipe distance be exactly the target height of the container.
diff --git a/packages/SystemUI/compose/scene/tests/Android.bp b/packages/SystemUI/compose/scene/tests/Android.bp
index 2ab27af37700..d63450b27390 100644
--- a/packages/SystemUI/compose/scene/tests/Android.bp
+++ b/packages/SystemUI/compose/scene/tests/Android.bp
@@ -42,6 +42,7 @@ android_test {
"PlatformMotionTestingCompose",
"androidx.test.runner",
"androidx.test.ext.junit",
+ "platform-parametric-runner-lib",
"androidx.compose.runtime_runtime",
"androidx.compose.ui_ui-test-junit4",
diff --git a/packages/SystemUI/compose/scene/tests/goldens/verticalReveal_gesture_dragFullyClose.json b/packages/SystemUI/compose/scene/tests/goldens/edge_verticalReveal_gesture_dragFullyClose.json
index 57f67665242c..57f67665242c 100644
--- a/packages/SystemUI/compose/scene/tests/goldens/verticalReveal_gesture_dragFullyClose.json
+++ b/packages/SystemUI/compose/scene/tests/goldens/edge_verticalReveal_gesture_dragFullyClose.json
diff --git a/packages/SystemUI/compose/scene/tests/goldens/verticalReveal_gesture_dragHalfClose.json b/packages/SystemUI/compose/scene/tests/goldens/edge_verticalReveal_gesture_dragHalfClose.json
index 01bc852cf7f4..01bc852cf7f4 100644
--- a/packages/SystemUI/compose/scene/tests/goldens/verticalReveal_gesture_dragHalfClose.json
+++ b/packages/SystemUI/compose/scene/tests/goldens/edge_verticalReveal_gesture_dragHalfClose.json
diff --git a/packages/SystemUI/compose/scene/tests/goldens/verticalReveal_gesture_dragOpen.json b/packages/SystemUI/compose/scene/tests/goldens/edge_verticalReveal_gesture_dragOpen.json
index b6e423afc6c4..b6e423afc6c4 100644
--- a/packages/SystemUI/compose/scene/tests/goldens/verticalReveal_gesture_dragOpen.json
+++ b/packages/SystemUI/compose/scene/tests/goldens/edge_verticalReveal_gesture_dragOpen.json
diff --git a/packages/SystemUI/compose/scene/tests/goldens/verticalReveal_gesture_flingClose.json b/packages/SystemUI/compose/scene/tests/goldens/edge_verticalReveal_gesture_flingClose.json
index a82db346ed58..a82db346ed58 100644
--- a/packages/SystemUI/compose/scene/tests/goldens/verticalReveal_gesture_flingClose.json
+++ b/packages/SystemUI/compose/scene/tests/goldens/edge_verticalReveal_gesture_flingClose.json
diff --git a/packages/SystemUI/compose/scene/tests/goldens/verticalReveal_gesture_flingOpen.json b/packages/SystemUI/compose/scene/tests/goldens/edge_verticalReveal_gesture_flingOpen.json
index 6dc5a0e79e81..6dc5a0e79e81 100644
--- a/packages/SystemUI/compose/scene/tests/goldens/verticalReveal_gesture_flingOpen.json
+++ b/packages/SystemUI/compose/scene/tests/goldens/edge_verticalReveal_gesture_flingOpen.json
diff --git a/packages/SystemUI/compose/scene/tests/goldens/verticalReveal_gesture_magneticDetachAndReattach.json b/packages/SystemUI/compose/scene/tests/goldens/edge_verticalReveal_gesture_magneticDetachAndReattach.json
index 1cd971aa2898..1cd971aa2898 100644
--- a/packages/SystemUI/compose/scene/tests/goldens/verticalReveal_gesture_magneticDetachAndReattach.json
+++ b/packages/SystemUI/compose/scene/tests/goldens/edge_verticalReveal_gesture_magneticDetachAndReattach.json
diff --git a/packages/SystemUI/compose/scene/tests/goldens/verticalReveal_triggeredRevealCloseTransition.json b/packages/SystemUI/compose/scene/tests/goldens/edge_verticalReveal_triggeredRevealCloseTransition.json
index 1030455e873f..1030455e873f 100644
--- a/packages/SystemUI/compose/scene/tests/goldens/verticalReveal_triggeredRevealCloseTransition.json
+++ b/packages/SystemUI/compose/scene/tests/goldens/edge_verticalReveal_triggeredRevealCloseTransition.json
diff --git a/packages/SystemUI/compose/scene/tests/goldens/verticalReveal_triggeredRevealOpenTransition.json b/packages/SystemUI/compose/scene/tests/goldens/edge_verticalReveal_triggeredRevealOpenTransition.json
index 622c29eebfb4..622c29eebfb4 100644
--- a/packages/SystemUI/compose/scene/tests/goldens/verticalReveal_triggeredRevealOpenTransition.json
+++ b/packages/SystemUI/compose/scene/tests/goldens/edge_verticalReveal_triggeredRevealOpenTransition.json
diff --git a/packages/SystemUI/compose/scene/tests/goldens/floating_verticalReveal_gesture_dragFullyClose.json b/packages/SystemUI/compose/scene/tests/goldens/floating_verticalReveal_gesture_dragFullyClose.json
new file mode 100644
index 000000000000..59e8b51412b8
--- /dev/null
+++ b/packages/SystemUI/compose/scene/tests/goldens/floating_verticalReveal_gesture_dragFullyClose.json
@@ -0,0 +1,634 @@
+{
+ "frame_ids": [
+ 0,
+ 16,
+ 32,
+ 48,
+ 64,
+ 80,
+ 96,
+ 112,
+ 128,
+ 144,
+ 160,
+ 176,
+ 192,
+ 208,
+ 224,
+ 240,
+ 256,
+ 272,
+ 288,
+ 304,
+ 320,
+ 336,
+ 352,
+ 368,
+ 384,
+ 400,
+ 416,
+ 432,
+ 448,
+ 464,
+ 480,
+ 496,
+ 512,
+ 528,
+ 544,
+ 560,
+ 576,
+ 592,
+ 608,
+ 624,
+ 640,
+ 656,
+ 672,
+ 688,
+ 704,
+ 720,
+ 736,
+ 752,
+ 768,
+ 784,
+ 800,
+ 816,
+ 832,
+ 848,
+ 864,
+ 880,
+ 896,
+ 912,
+ 928,
+ 944,
+ 960
+ ],
+ "features": [
+ {
+ "name": "RevealElement_position",
+ "type": "dpOffset",
+ "data_points": [
+ {
+ "x": 50,
+ "y": 50
+ },
+ {
+ "x": 50,
+ "y": 50
+ },
+ {
+ "x": 50,
+ "y": 50.4
+ },
+ {
+ "x": 50,
+ "y": 50
+ },
+ {
+ "x": 50,
+ "y": 50
+ },
+ {
+ "x": 50,
+ "y": 50
+ },
+ {
+ "x": 50,
+ "y": 50
+ },
+ {
+ "x": 50,
+ "y": 50
+ },
+ {
+ "x": 50,
+ "y": 50
+ },
+ {
+ "x": 50,
+ "y": 50
+ },
+ {
+ "x": 50,
+ "y": 50
+ },
+ {
+ "x": 50,
+ "y": 50
+ },
+ {
+ "x": 50,
+ "y": 50
+ },
+ {
+ "x": 50,
+ "y": 50
+ },
+ {
+ "x": 50,
+ "y": 50
+ },
+ {
+ "x": 50,
+ "y": 50
+ },
+ {
+ "x": 50,
+ "y": 50
+ },
+ {
+ "x": 50,
+ "y": 50
+ },
+ {
+ "x": 50,
+ "y": 50
+ },
+ {
+ "x": 50,
+ "y": 50
+ },
+ {
+ "x": 50,
+ "y": 50
+ },
+ {
+ "x": 50,
+ "y": 50
+ },
+ {
+ "x": 50,
+ "y": 50
+ },
+ {
+ "x": 50,
+ "y": 50
+ },
+ {
+ "x": 50,
+ "y": 50
+ },
+ {
+ "x": 50,
+ "y": 50
+ },
+ {
+ "x": 50,
+ "y": 50
+ },
+ {
+ "x": 50,
+ "y": 50
+ },
+ {
+ "x": 50,
+ "y": 50
+ },
+ {
+ "x": 50,
+ "y": 50
+ },
+ {
+ "x": 50,
+ "y": 50
+ },
+ {
+ "x": 50,
+ "y": 50
+ },
+ {
+ "x": 50,
+ "y": 50
+ },
+ {
+ "x": 50,
+ "y": 50
+ },
+ {
+ "x": 50,
+ "y": 50
+ },
+ {
+ "x": 50,
+ "y": 50
+ },
+ {
+ "x": 50,
+ "y": 50
+ },
+ {
+ "x": 50,
+ "y": 50
+ },
+ {
+ "x": 50,
+ "y": 50
+ },
+ {
+ "x": 50,
+ "y": 50
+ },
+ {
+ "x": 50,
+ "y": 50
+ },
+ {
+ "x": 50,
+ "y": 50
+ },
+ {
+ "x": 50,
+ "y": 50
+ },
+ {
+ "x": 50,
+ "y": 50
+ },
+ {
+ "x": 50,
+ "y": 50
+ },
+ {
+ "x": 50,
+ "y": 50
+ },
+ {
+ "x": 50,
+ "y": 50
+ },
+ {
+ "x": 50,
+ "y": 50
+ },
+ {
+ "x": 50,
+ "y": 50
+ },
+ {
+ "x": 50,
+ "y": 50
+ },
+ {
+ "x": 50,
+ "y": 50
+ },
+ {
+ "x": 50,
+ "y": 50
+ },
+ {
+ "x": 50,
+ "y": 50
+ },
+ {
+ "x": 50,
+ "y": 50
+ },
+ {
+ "x": 50,
+ "y": 50
+ },
+ {
+ "x": 50,
+ "y": 50
+ },
+ {
+ "x": 50,
+ "y": 50
+ },
+ {
+ "x": 50,
+ "y": 50
+ },
+ {
+ "x": 50,
+ "y": 50
+ },
+ {
+ "x": 50,
+ "y": 50
+ },
+ {
+ "x": 50,
+ "y": 50
+ }
+ ]
+ },
+ {
+ "name": "RevealElement_size",
+ "type": "dpSize",
+ "data_points": [
+ {
+ "width": 188,
+ "height": 400
+ },
+ {
+ "width": 188,
+ "height": 400
+ },
+ {
+ "width": 188,
+ "height": 400
+ },
+ {
+ "width": 188,
+ "height": 393.2
+ },
+ {
+ "width": 188,
+ "height": 393.2
+ },
+ {
+ "width": 188,
+ "height": 386
+ },
+ {
+ "width": 188,
+ "height": 379.6
+ },
+ {
+ "width": 188,
+ "height": 372.8
+ },
+ {
+ "width": 188,
+ "height": 366.8
+ },
+ {
+ "width": 188,
+ "height": 360.4
+ },
+ {
+ "width": 188,
+ "height": 354
+ },
+ {
+ "width": 188,
+ "height": 347.6
+ },
+ {
+ "width": 188,
+ "height": 341.2
+ },
+ {
+ "width": 188,
+ "height": 341.2
+ },
+ {
+ "width": 188,
+ "height": 334
+ },
+ {
+ "width": 188,
+ "height": 328
+ },
+ {
+ "width": 188,
+ "height": 321.6
+ },
+ {
+ "width": 188,
+ "height": 315.2
+ },
+ {
+ "width": 188,
+ "height": 308.8
+ },
+ {
+ "width": 188,
+ "height": 302.4
+ },
+ {
+ "width": 188,
+ "height": 296
+ },
+ {
+ "width": 188,
+ "height": 289.6
+ },
+ {
+ "width": 188,
+ "height": 289.6
+ },
+ {
+ "width": 188,
+ "height": 282.8
+ },
+ {
+ "width": 188,
+ "height": 276.4
+ },
+ {
+ "width": 188,
+ "height": 270
+ },
+ {
+ "width": 188,
+ "height": 263.6
+ },
+ {
+ "width": 188,
+ "height": 257.2
+ },
+ {
+ "width": 188,
+ "height": 250.8
+ },
+ {
+ "width": 188,
+ "height": 244.4
+ },
+ {
+ "width": 188,
+ "height": 238
+ },
+ {
+ "width": 188,
+ "height": 238
+ },
+ {
+ "width": 188,
+ "height": 231.2
+ },
+ {
+ "width": 188,
+ "height": 224.8
+ },
+ {
+ "width": 188,
+ "height": 218.4
+ },
+ {
+ "width": 188,
+ "height": 212
+ },
+ {
+ "width": 188,
+ "height": 212
+ },
+ {
+ "width": 188,
+ "height": 192.4
+ },
+ {
+ "width": 188,
+ "height": 159.6
+ },
+ {
+ "width": 188,
+ "height": 124.4
+ },
+ {
+ "width": 188,
+ "height": 92.8
+ },
+ {
+ "width": 188,
+ "height": 64.8
+ },
+ {
+ "width": 188,
+ "height": 44.4
+ },
+ {
+ "width": 188,
+ "height": 29.2
+ },
+ {
+ "width": 188,
+ "height": 18.4
+ },
+ {
+ "width": 188,
+ "height": 10.8
+ },
+ {
+ "width": 188,
+ "height": 5.6
+ },
+ {
+ "width": 188,
+ "height": 2.4
+ },
+ {
+ "width": 188,
+ "height": 0.4
+ },
+ {
+ "width": 188,
+ "height": 0
+ },
+ {
+ "width": 188,
+ "height": 0
+ },
+ {
+ "width": 188,
+ "height": 0
+ },
+ {
+ "width": 188,
+ "height": 0
+ },
+ {
+ "width": 188,
+ "height": 0
+ },
+ {
+ "width": 188,
+ "height": 0
+ },
+ {
+ "width": 188,
+ "height": 0
+ },
+ {
+ "width": 188,
+ "height": 0
+ },
+ {
+ "width": 188,
+ "height": 0
+ },
+ {
+ "width": 188,
+ "height": 0
+ },
+ {
+ "width": 188,
+ "height": 0
+ },
+ {
+ "width": 188,
+ "height": 0
+ }
+ ]
+ },
+ {
+ "name": "RevealElement_alpha",
+ "type": "float",
+ "data_points": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 0.9808927,
+ 0.8211168,
+ 0.61845565,
+ 0.43834114,
+ 0.29850912,
+ 0.19755232,
+ 0.12793064,
+ 0.08142871,
+ 0.051099956,
+ 0.031684637,
+ 0.019442618,
+ 0.011821032,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0
+ ]
+ }
+ ]
+} \ No newline at end of file
diff --git a/packages/SystemUI/compose/scene/tests/goldens/floating_verticalReveal_gesture_dragHalfClose.json b/packages/SystemUI/compose/scene/tests/goldens/floating_verticalReveal_gesture_dragHalfClose.json
new file mode 100644
index 000000000000..210ff0985e78
--- /dev/null
+++ b/packages/SystemUI/compose/scene/tests/goldens/floating_verticalReveal_gesture_dragHalfClose.json
@@ -0,0 +1,614 @@
+{
+ "frame_ids": [
+ 0,
+ 16,
+ 32,
+ 48,
+ 64,
+ 80,
+ 96,
+ 112,
+ 128,
+ 144,
+ 160,
+ 176,
+ 192,
+ 208,
+ 224,
+ 240,
+ 256,
+ 272,
+ 288,
+ 304,
+ 320,
+ 336,
+ 352,
+ 368,
+ 384,
+ 400,
+ 416,
+ 432,
+ 448,
+ 464,
+ 480,
+ 496,
+ 512,
+ 528,
+ 544,
+ 560,
+ 576,
+ 592,
+ 608,
+ 624,
+ 640,
+ 656,
+ 672,
+ 688,
+ 704,
+ 720,
+ 736,
+ 752,
+ 768,
+ 784,
+ 800,
+ 816,
+ 832,
+ 848,
+ 864,
+ 880,
+ 896,
+ 912,
+ 928
+ ],
+ "features": [
+ {
+ "name": "RevealElement_position",
+ "type": "dpOffset",
+ "data_points": [
+ {
+ "x": 50,
+ "y": 50
+ },
+ {
+ "x": 50,
+ "y": 50
+ },
+ {
+ "x": 50.8,
+ "y": 52
+ },
+ {
+ "x": 50,
+ "y": 50
+ },
+ {
+ "x": 50,
+ "y": 50
+ },
+ {
+ "x": 50,
+ "y": 50
+ },
+ {
+ "x": 50,
+ "y": 50
+ },
+ {
+ "x": 50,
+ "y": 50
+ },
+ {
+ "x": 50,
+ "y": 50
+ },
+ {
+ "x": 50,
+ "y": 50
+ },
+ {
+ "x": 50,
+ "y": 50
+ },
+ {
+ "x": 50,
+ "y": 50
+ },
+ {
+ "x": 50,
+ "y": 50
+ },
+ {
+ "x": 50,
+ "y": 50
+ },
+ {
+ "x": 50,
+ "y": 50
+ },
+ {
+ "x": 50,
+ "y": 50
+ },
+ {
+ "x": 50,
+ "y": 50
+ },
+ {
+ "x": 50,
+ "y": 50
+ },
+ {
+ "x": 50,
+ "y": 50
+ },
+ {
+ "x": 50,
+ "y": 50
+ },
+ {
+ "x": 50,
+ "y": 50
+ },
+ {
+ "x": 50,
+ "y": 50
+ },
+ {
+ "x": 50,
+ "y": 50
+ },
+ {
+ "x": 50,
+ "y": 50
+ },
+ {
+ "x": 50,
+ "y": 50
+ },
+ {
+ "x": 50,
+ "y": 50
+ },
+ {
+ "x": 50,
+ "y": 50
+ },
+ {
+ "x": 50,
+ "y": 50
+ },
+ {
+ "x": 50,
+ "y": 50
+ },
+ {
+ "x": 50,
+ "y": 50
+ },
+ {
+ "x": 50,
+ "y": 50
+ },
+ {
+ "x": 50,
+ "y": 50
+ },
+ {
+ "x": 50,
+ "y": 50
+ },
+ {
+ "x": 50,
+ "y": 50
+ },
+ {
+ "x": 50,
+ "y": 50
+ },
+ {
+ "x": 50,
+ "y": 50
+ },
+ {
+ "x": 50,
+ "y": 50
+ },
+ {
+ "x": 50,
+ "y": 50
+ },
+ {
+ "x": 50,
+ "y": 50
+ },
+ {
+ "x": 50,
+ "y": 50
+ },
+ {
+ "x": 50,
+ "y": 50
+ },
+ {
+ "x": 50,
+ "y": 50
+ },
+ {
+ "x": 50,
+ "y": 50
+ },
+ {
+ "x": 50,
+ "y": 50
+ },
+ {
+ "x": 50,
+ "y": 50
+ },
+ {
+ "x": 50,
+ "y": 50
+ },
+ {
+ "x": 50,
+ "y": 50
+ },
+ {
+ "x": 50,
+ "y": 50
+ },
+ {
+ "x": 50,
+ "y": 50
+ },
+ {
+ "x": 50,
+ "y": 50
+ },
+ {
+ "x": 50,
+ "y": 50
+ },
+ {
+ "x": 50,
+ "y": 50
+ },
+ {
+ "x": 50,
+ "y": 50
+ },
+ {
+ "x": 50,
+ "y": 50
+ },
+ {
+ "x": 50,
+ "y": 50
+ },
+ {
+ "x": 50,
+ "y": 50
+ },
+ {
+ "x": 50,
+ "y": 50
+ },
+ {
+ "x": 50,
+ "y": 50
+ },
+ {
+ "x": 50,
+ "y": 50
+ }
+ ]
+ },
+ {
+ "name": "RevealElement_size",
+ "type": "dpSize",
+ "data_points": [
+ {
+ "width": 188,
+ "height": 400
+ },
+ {
+ "width": 188,
+ "height": 400
+ },
+ {
+ "width": 188,
+ "height": 400
+ },
+ {
+ "width": 188,
+ "height": 390
+ },
+ {
+ "width": 188,
+ "height": 390
+ },
+ {
+ "width": 188,
+ "height": 379.2
+ },
+ {
+ "width": 188,
+ "height": 371.2
+ },
+ {
+ "width": 188,
+ "height": 363.2
+ },
+ {
+ "width": 188,
+ "height": 355.2
+ },
+ {
+ "width": 188,
+ "height": 347.2
+ },
+ {
+ "width": 188,
+ "height": 339.2
+ },
+ {
+ "width": 188,
+ "height": 331.2
+ },
+ {
+ "width": 188,
+ "height": 323.2
+ },
+ {
+ "width": 188,
+ "height": 323.2
+ },
+ {
+ "width": 188,
+ "height": 314.8
+ },
+ {
+ "width": 188,
+ "height": 306.8
+ },
+ {
+ "width": 188,
+ "height": 298.8
+ },
+ {
+ "width": 188,
+ "height": 290.8
+ },
+ {
+ "width": 188,
+ "height": 282.8
+ },
+ {
+ "width": 188,
+ "height": 274.8
+ },
+ {
+ "width": 188,
+ "height": 266.8
+ },
+ {
+ "width": 188,
+ "height": 258.8
+ },
+ {
+ "width": 188,
+ "height": 258.8
+ },
+ {
+ "width": 188,
+ "height": 250.4
+ },
+ {
+ "width": 188,
+ "height": 242.4
+ },
+ {
+ "width": 188,
+ "height": 234.4
+ },
+ {
+ "width": 188,
+ "height": 226.4
+ },
+ {
+ "width": 188,
+ "height": 218.4
+ },
+ {
+ "width": 188,
+ "height": 210.4
+ },
+ {
+ "width": 188,
+ "height": 202.4
+ },
+ {
+ "width": 188,
+ "height": 194.4
+ },
+ {
+ "width": 188,
+ "height": 194.4
+ },
+ {
+ "width": 188,
+ "height": 185.6
+ },
+ {
+ "width": 188,
+ "height": 178
+ },
+ {
+ "width": 188,
+ "height": 170
+ },
+ {
+ "width": 188,
+ "height": 161.6
+ },
+ {
+ "width": 188,
+ "height": 161.6
+ },
+ {
+ "width": 188,
+ "height": 144.8
+ },
+ {
+ "width": 188,
+ "height": 118.8
+ },
+ {
+ "width": 188,
+ "height": 92
+ },
+ {
+ "width": 188,
+ "height": 68
+ },
+ {
+ "width": 188,
+ "height": 49.6
+ },
+ {
+ "width": 188,
+ "height": 35.2
+ },
+ {
+ "width": 188,
+ "height": 24.4
+ },
+ {
+ "width": 188,
+ "height": 16.4
+ },
+ {
+ "width": 188,
+ "height": 10.4
+ },
+ {
+ "width": 188,
+ "height": 6.4
+ },
+ {
+ "width": 188,
+ "height": 3.6
+ },
+ {
+ "width": 188,
+ "height": 1.6
+ },
+ {
+ "width": 188,
+ "height": 0.4
+ },
+ {
+ "width": 188,
+ "height": 0
+ },
+ {
+ "width": 188,
+ "height": 0
+ },
+ {
+ "width": 188,
+ "height": 0
+ },
+ {
+ "width": 188,
+ "height": 0
+ },
+ {
+ "width": 188,
+ "height": 0
+ },
+ {
+ "width": 188,
+ "height": 0
+ },
+ {
+ "width": 188,
+ "height": 0
+ },
+ {
+ "width": 188,
+ "height": 0
+ },
+ {
+ "width": 188,
+ "height": 0
+ }
+ ]
+ },
+ {
+ "name": "RevealElement_alpha",
+ "type": "float",
+ "data_points": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 0.9967737,
+ 0.86538374,
+ 0.66414475,
+ 0.47619528,
+ 0.32686388,
+ 0.21757984,
+ 0.14153665,
+ 0.09041709,
+ 0.05691254,
+ 0.035380244,
+ 0.02175957,
+ 0.01325649,
+ 0.008007765,
+ 0,
+ 0,
+ 0
+ ]
+ }
+ ]
+} \ No newline at end of file
diff --git a/packages/SystemUI/compose/scene/tests/goldens/floating_verticalReveal_gesture_dragOpen.json b/packages/SystemUI/compose/scene/tests/goldens/floating_verticalReveal_gesture_dragOpen.json
new file mode 100644
index 000000000000..d186df22dda0
--- /dev/null
+++ b/packages/SystemUI/compose/scene/tests/goldens/floating_verticalReveal_gesture_dragOpen.json
@@ -0,0 +1,544 @@
+{
+ "frame_ids": [
+ 0,
+ 16,
+ 32,
+ 48,
+ 64,
+ 80,
+ 96,
+ 112,
+ 128,
+ 144,
+ 160,
+ 176,
+ 192,
+ 208,
+ 224,
+ 240,
+ 256,
+ 272,
+ 288,
+ 304,
+ 320,
+ 336,
+ 352,
+ 368,
+ 384,
+ 400,
+ 416,
+ 432,
+ 448,
+ 464,
+ 480,
+ 496,
+ 512,
+ 528,
+ 544,
+ 560,
+ 576,
+ 592,
+ 608,
+ 624,
+ 640,
+ 656,
+ 672,
+ 688,
+ 704,
+ 720,
+ 736,
+ 752,
+ 768,
+ 784,
+ 800,
+ 816
+ ],
+ "features": [
+ {
+ "name": "RevealElement_position",
+ "type": "dpOffset",
+ "data_points": [
+ {
+ "type": "not_found"
+ },
+ {
+ "type": "not_found"
+ },
+ {
+ "type": "not_found"
+ },
+ {
+ "x": 50,
+ "y": 50
+ },
+ {
+ "x": 50,
+ "y": 50
+ },
+ {
+ "x": 50,
+ "y": 50
+ },
+ {
+ "x": 50,
+ "y": 50
+ },
+ {
+ "x": 50,
+ "y": 50
+ },
+ {
+ "x": 50,
+ "y": 50
+ },
+ {
+ "x": 50,
+ "y": 50
+ },
+ {
+ "x": 50,
+ "y": 50
+ },
+ {
+ "x": 50,
+ "y": 50
+ },
+ {
+ "x": 50,
+ "y": 50
+ },
+ {
+ "x": 50,
+ "y": 50
+ },
+ {
+ "x": 50,
+ "y": 50
+ },
+ {
+ "x": 50,
+ "y": 50
+ },
+ {
+ "x": 50,
+ "y": 50
+ },
+ {
+ "x": 50,
+ "y": 50
+ },
+ {
+ "x": 50,
+ "y": 50
+ },
+ {
+ "x": 50,
+ "y": 50
+ },
+ {
+ "x": 50,
+ "y": 50
+ },
+ {
+ "x": 50,
+ "y": 50
+ },
+ {
+ "x": 50,
+ "y": 50
+ },
+ {
+ "x": 50,
+ "y": 50
+ },
+ {
+ "x": 50,
+ "y": 50
+ },
+ {
+ "x": 50,
+ "y": 50
+ },
+ {
+ "x": 50,
+ "y": 50
+ },
+ {
+ "x": 50,
+ "y": 50
+ },
+ {
+ "x": 50,
+ "y": 50
+ },
+ {
+ "x": 50,
+ "y": 50
+ },
+ {
+ "x": 50,
+ "y": 50
+ },
+ {
+ "x": 50,
+ "y": 50
+ },
+ {
+ "x": 50,
+ "y": 50
+ },
+ {
+ "x": 50,
+ "y": 50
+ },
+ {
+ "x": 50,
+ "y": 50
+ },
+ {
+ "x": 50,
+ "y": 50
+ },
+ {
+ "x": 50,
+ "y": 50
+ },
+ {
+ "x": 50,
+ "y": 50
+ },
+ {
+ "x": 50,
+ "y": 50
+ },
+ {
+ "x": 50,
+ "y": 50
+ },
+ {
+ "x": 50,
+ "y": 50
+ },
+ {
+ "x": 50,
+ "y": 50
+ },
+ {
+ "x": 50,
+ "y": 50
+ },
+ {
+ "x": 50,
+ "y": 50
+ },
+ {
+ "x": 50,
+ "y": 50
+ },
+ {
+ "x": 50,
+ "y": 50
+ },
+ {
+ "x": 50,
+ "y": 50
+ },
+ {
+ "x": 50,
+ "y": 50
+ },
+ {
+ "x": 50,
+ "y": 50
+ },
+ {
+ "x": 50,
+ "y": 50
+ },
+ {
+ "x": 50,
+ "y": 50
+ },
+ {
+ "x": 50,
+ "y": 50
+ }
+ ]
+ },
+ {
+ "name": "RevealElement_size",
+ "type": "dpSize",
+ "data_points": [
+ {
+ "type": "not_found"
+ },
+ {
+ "type": "not_found"
+ },
+ {
+ "type": "not_found"
+ },
+ {
+ "width": 188,
+ "height": 1.6
+ },
+ {
+ "width": 188,
+ "height": 1.6
+ },
+ {
+ "width": 188,
+ "height": 3.2
+ },
+ {
+ "width": 188,
+ "height": 4.8
+ },
+ {
+ "width": 188,
+ "height": 6.4
+ },
+ {
+ "width": 188,
+ "height": 8
+ },
+ {
+ "width": 188,
+ "height": 9.6
+ },
+ {
+ "width": 188,
+ "height": 11.2
+ },
+ {
+ "width": 188,
+ "height": 12.8
+ },
+ {
+ "width": 188,
+ "height": 14.4
+ },
+ {
+ "width": 188,
+ "height": 14.4
+ },
+ {
+ "width": 188,
+ "height": 16.4
+ },
+ {
+ "width": 188,
+ "height": 18
+ },
+ {
+ "width": 188,
+ "height": 19.6
+ },
+ {
+ "width": 188,
+ "height": 21.2
+ },
+ {
+ "width": 188,
+ "height": 22.8
+ },
+ {
+ "width": 188,
+ "height": 24.4
+ },
+ {
+ "width": 188,
+ "height": 26
+ },
+ {
+ "width": 188,
+ "height": 27.6
+ },
+ {
+ "width": 188,
+ "height": 27.6
+ },
+ {
+ "width": 188,
+ "height": 29.2
+ },
+ {
+ "width": 188,
+ "height": 30.8
+ },
+ {
+ "width": 188,
+ "height": 32.4
+ },
+ {
+ "width": 188,
+ "height": 34
+ },
+ {
+ "width": 188,
+ "height": 40.4
+ },
+ {
+ "width": 188,
+ "height": 52.4
+ },
+ {
+ "width": 188,
+ "height": 64.8
+ },
+ {
+ "width": 188,
+ "height": 83.2
+ },
+ {
+ "width": 188,
+ "height": 96
+ },
+ {
+ "width": 188,
+ "height": 114.8
+ },
+ {
+ "width": 188,
+ "height": 132
+ },
+ {
+ "width": 188,
+ "height": 148
+ },
+ {
+ "width": 188,
+ "height": 162
+ },
+ {
+ "width": 188,
+ "height": 168.4
+ },
+ {
+ "width": 188,
+ "height": 192.8
+ },
+ {
+ "width": 188,
+ "height": 229.6
+ },
+ {
+ "width": 188,
+ "height": 268
+ },
+ {
+ "width": 188,
+ "height": 302
+ },
+ {
+ "width": 188,
+ "height": 330
+ },
+ {
+ "width": 188,
+ "height": 351.6
+ },
+ {
+ "width": 188,
+ "height": 367.6
+ },
+ {
+ "width": 188,
+ "height": 379.2
+ },
+ {
+ "width": 188,
+ "height": 387.2
+ },
+ {
+ "width": 188,
+ "height": 392.4
+ },
+ {
+ "width": 188,
+ "height": 395.6
+ },
+ {
+ "width": 188,
+ "height": 398
+ },
+ {
+ "width": 188,
+ "height": 398.8
+ },
+ {
+ "width": 188,
+ "height": 399.6
+ },
+ {
+ "width": 188,
+ "height": 400
+ }
+ ]
+ },
+ {
+ "name": "RevealElement_alpha",
+ "type": "float",
+ "data_points": [
+ {
+ "type": "not_found"
+ },
+ {
+ "type": "not_found"
+ },
+ {
+ "type": "not_found"
+ },
+ 0,
+ 0,
+ 0,
+ 0,
+ 0.0067873597,
+ 0.0612576,
+ 0.19080025,
+ 0.39327443,
+ 0.5711931,
+ 0.70855826,
+ 0.8074064,
+ 0.8754226,
+ 0.9207788,
+ 0.95032376,
+ 0.9692185,
+ 0.98112255,
+ 0.9885286,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ }
+ ]
+} \ No newline at end of file
diff --git a/packages/SystemUI/compose/scene/tests/goldens/floating_verticalReveal_gesture_flingClose.json b/packages/SystemUI/compose/scene/tests/goldens/floating_verticalReveal_gesture_flingClose.json
new file mode 100644
index 000000000000..a9c24fa87089
--- /dev/null
+++ b/packages/SystemUI/compose/scene/tests/goldens/floating_verticalReveal_gesture_flingClose.json
@@ -0,0 +1,444 @@
+{
+ "frame_ids": [
+ 0,
+ 16,
+ 32,
+ 48,
+ 64,
+ 80,
+ 96,
+ 112,
+ 128,
+ 144,
+ 160,
+ 176,
+ 192,
+ 208,
+ 224,
+ 240,
+ 256,
+ 272,
+ 288,
+ 304,
+ 320,
+ 336,
+ 352,
+ 368,
+ 384,
+ 400,
+ 416,
+ 432,
+ 448,
+ 464,
+ 480,
+ 496,
+ 512,
+ 528,
+ 544,
+ 560,
+ 576,
+ 592,
+ 608,
+ 624,
+ 640,
+ 656
+ ],
+ "features": [
+ {
+ "name": "RevealElement_position",
+ "type": "dpOffset",
+ "data_points": [
+ {
+ "x": 50,
+ "y": 50
+ },
+ {
+ "x": 50,
+ "y": 50
+ },
+ {
+ "x": 50,
+ "y": 50
+ },
+ {
+ "x": 50,
+ "y": 50
+ },
+ {
+ "x": 50,
+ "y": 50
+ },
+ {
+ "x": 50,
+ "y": 50
+ },
+ {
+ "x": 50,
+ "y": 50
+ },
+ {
+ "x": 50,
+ "y": 50
+ },
+ {
+ "x": 50,
+ "y": 50
+ },
+ {
+ "x": 50.4,
+ "y": 50.8
+ },
+ {
+ "x": 50,
+ "y": 50
+ },
+ {
+ "x": 50,
+ "y": 50
+ },
+ {
+ "x": 50,
+ "y": 50
+ },
+ {
+ "x": 50,
+ "y": 50
+ },
+ {
+ "x": 50,
+ "y": 50
+ },
+ {
+ "x": 50,
+ "y": 50
+ },
+ {
+ "x": 50,
+ "y": 50
+ },
+ {
+ "x": 50,
+ "y": 50
+ },
+ {
+ "x": 50,
+ "y": 50
+ },
+ {
+ "x": 50,
+ "y": 50
+ },
+ {
+ "x": 50,
+ "y": 50
+ },
+ {
+ "x": 50,
+ "y": 50
+ },
+ {
+ "x": 50,
+ "y": 50
+ },
+ {
+ "x": 50,
+ "y": 50
+ },
+ {
+ "x": 50,
+ "y": 50
+ },
+ {
+ "x": 50,
+ "y": 50
+ },
+ {
+ "x": 50,
+ "y": 50
+ },
+ {
+ "x": 50,
+ "y": 50
+ },
+ {
+ "x": 50,
+ "y": 50
+ },
+ {
+ "x": 50,
+ "y": 50
+ },
+ {
+ "x": 50,
+ "y": 50
+ },
+ {
+ "x": 50,
+ "y": 50
+ },
+ {
+ "x": 50,
+ "y": 50
+ },
+ {
+ "x": 50,
+ "y": 50
+ },
+ {
+ "x": 50,
+ "y": 50
+ },
+ {
+ "x": 50,
+ "y": 50
+ },
+ {
+ "x": 50,
+ "y": 50
+ },
+ {
+ "x": 50,
+ "y": 50
+ },
+ {
+ "x": 50,
+ "y": 50
+ },
+ {
+ "x": 50,
+ "y": 50
+ },
+ {
+ "x": 50,
+ "y": 50
+ },
+ {
+ "x": 50,
+ "y": 50
+ }
+ ]
+ },
+ {
+ "name": "RevealElement_size",
+ "type": "dpSize",
+ "data_points": [
+ {
+ "width": 188,
+ "height": 400
+ },
+ {
+ "width": 188,
+ "height": 400
+ },
+ {
+ "width": 188,
+ "height": 400
+ },
+ {
+ "width": 188,
+ "height": 400
+ },
+ {
+ "width": 188,
+ "height": 400
+ },
+ {
+ "width": 188,
+ "height": 400
+ },
+ {
+ "width": 188,
+ "height": 400
+ },
+ {
+ "width": 188,
+ "height": 400
+ },
+ {
+ "width": 188,
+ "height": 400
+ },
+ {
+ "width": 188,
+ "height": 400
+ },
+ {
+ "width": 188,
+ "height": 389.6
+ },
+ {
+ "width": 188,
+ "height": 378.8
+ },
+ {
+ "width": 188,
+ "height": 366
+ },
+ {
+ "width": 188,
+ "height": 352
+ },
+ {
+ "width": 188,
+ "height": 352
+ },
+ {
+ "width": 188,
+ "height": 316.8
+ },
+ {
+ "width": 188,
+ "height": 261.2
+ },
+ {
+ "width": 188,
+ "height": 202.8
+ },
+ {
+ "width": 188,
+ "height": 150.8
+ },
+ {
+ "width": 188,
+ "height": 107.6
+ },
+ {
+ "width": 188,
+ "height": 71.2
+ },
+ {
+ "width": 188,
+ "height": 41.6
+ },
+ {
+ "width": 188,
+ "height": 21.6
+ },
+ {
+ "width": 188,
+ "height": 8.4
+ },
+ {
+ "width": 188,
+ "height": 0.4
+ },
+ {
+ "width": 188,
+ "height": 0
+ },
+ {
+ "width": 188,
+ "height": 0
+ },
+ {
+ "width": 188,
+ "height": 0
+ },
+ {
+ "width": 188,
+ "height": 0
+ },
+ {
+ "width": 188,
+ "height": 0
+ },
+ {
+ "width": 188,
+ "height": 0
+ },
+ {
+ "width": 188,
+ "height": 0
+ },
+ {
+ "width": 188,
+ "height": 0
+ },
+ {
+ "width": 188,
+ "height": 0
+ },
+ {
+ "width": 188,
+ "height": 0
+ },
+ {
+ "width": 188,
+ "height": 0
+ },
+ {
+ "width": 188,
+ "height": 0
+ },
+ {
+ "width": 188,
+ "height": 0
+ },
+ {
+ "width": 188,
+ "height": 0
+ },
+ {
+ "width": 188,
+ "height": 0
+ },
+ {
+ "width": 188,
+ "height": 0
+ },
+ {
+ "width": 188,
+ "height": 0
+ }
+ ]
+ },
+ {
+ "name": "RevealElement_alpha",
+ "type": "float",
+ "data_points": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 0.9833227,
+ 0.8263634,
+ 0.623688,
+ 0.44261706,
+ 0.3016883,
+ 0.1997872,
+ 0.12944388,
+ 0.08242595,
+ 0.051743627,
+ 0.032093227,
+ 0.019698441,
+ 0.0119793415,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0
+ ]
+ }
+ ]
+} \ No newline at end of file
diff --git a/packages/SystemUI/compose/scene/tests/goldens/floating_verticalReveal_gesture_flingOpen.json b/packages/SystemUI/compose/scene/tests/goldens/floating_verticalReveal_gesture_flingOpen.json
new file mode 100644
index 000000000000..f9279f1fae5c
--- /dev/null
+++ b/packages/SystemUI/compose/scene/tests/goldens/floating_verticalReveal_gesture_flingOpen.json
@@ -0,0 +1,374 @@
+{
+ "frame_ids": [
+ 0,
+ 16,
+ 32,
+ 48,
+ 64,
+ 80,
+ 96,
+ 112,
+ 128,
+ 144,
+ 160,
+ 176,
+ 192,
+ 208,
+ 224,
+ 240,
+ 256,
+ 272,
+ 288,
+ 304,
+ 320,
+ 336,
+ 352,
+ 368,
+ 384,
+ 400,
+ 416,
+ 432,
+ 448,
+ 464,
+ 480,
+ 496,
+ 512,
+ 528,
+ 544
+ ],
+ "features": [
+ {
+ "name": "RevealElement_position",
+ "type": "dpOffset",
+ "data_points": [
+ {
+ "type": "not_found"
+ },
+ {
+ "type": "not_found"
+ },
+ {
+ "type": "not_found"
+ },
+ {
+ "type": "not_found"
+ },
+ {
+ "type": "not_found"
+ },
+ {
+ "type": "not_found"
+ },
+ {
+ "type": "not_found"
+ },
+ {
+ "type": "not_found"
+ },
+ {
+ "x": 50,
+ "y": 50
+ },
+ {
+ "x": 50,
+ "y": 50
+ },
+ {
+ "x": 50,
+ "y": 50
+ },
+ {
+ "x": 50,
+ "y": 50
+ },
+ {
+ "x": 50,
+ "y": 50
+ },
+ {
+ "x": 50,
+ "y": 50
+ },
+ {
+ "x": 50,
+ "y": 50
+ },
+ {
+ "x": 50,
+ "y": 50
+ },
+ {
+ "x": 50,
+ "y": 50
+ },
+ {
+ "x": 50,
+ "y": 50
+ },
+ {
+ "x": 50,
+ "y": 50
+ },
+ {
+ "x": 50,
+ "y": 50
+ },
+ {
+ "x": 50,
+ "y": 50
+ },
+ {
+ "x": 50,
+ "y": 50
+ },
+ {
+ "x": 50,
+ "y": 50
+ },
+ {
+ "x": 50,
+ "y": 50
+ },
+ {
+ "x": 50,
+ "y": 50
+ },
+ {
+ "x": 50,
+ "y": 50
+ },
+ {
+ "x": 50,
+ "y": 50
+ },
+ {
+ "x": 50,
+ "y": 50
+ },
+ {
+ "x": 50,
+ "y": 50
+ },
+ {
+ "x": 50,
+ "y": 50
+ },
+ {
+ "x": 50,
+ "y": 50
+ },
+ {
+ "x": 50,
+ "y": 50
+ },
+ {
+ "x": 50,
+ "y": 50
+ },
+ {
+ "x": 50,
+ "y": 50
+ },
+ {
+ "x": 50,
+ "y": 50
+ }
+ ]
+ },
+ {
+ "name": "RevealElement_size",
+ "type": "dpSize",
+ "data_points": [
+ {
+ "type": "not_found"
+ },
+ {
+ "type": "not_found"
+ },
+ {
+ "type": "not_found"
+ },
+ {
+ "type": "not_found"
+ },
+ {
+ "type": "not_found"
+ },
+ {
+ "type": "not_found"
+ },
+ {
+ "type": "not_found"
+ },
+ {
+ "type": "not_found"
+ },
+ {
+ "width": 188,
+ "height": 2
+ },
+ {
+ "width": 188,
+ "height": 4.4
+ },
+ {
+ "width": 188,
+ "height": 6.8
+ },
+ {
+ "width": 188,
+ "height": 10
+ },
+ {
+ "width": 188,
+ "height": 13.2
+ },
+ {
+ "width": 188,
+ "height": 16.8
+ },
+ {
+ "width": 188,
+ "height": 16.8
+ },
+ {
+ "width": 188,
+ "height": 25.2
+ },
+ {
+ "width": 188,
+ "height": 53.2
+ },
+ {
+ "width": 188,
+ "height": 119.6
+ },
+ {
+ "width": 188,
+ "height": 182
+ },
+ {
+ "width": 188,
+ "height": 235.6
+ },
+ {
+ "width": 188,
+ "height": 279.2
+ },
+ {
+ "width": 188,
+ "height": 313.2
+ },
+ {
+ "width": 188,
+ "height": 338.8
+ },
+ {
+ "width": 188,
+ "height": 357.6
+ },
+ {
+ "width": 188,
+ "height": 371.2
+ },
+ {
+ "width": 188,
+ "height": 380.8
+ },
+ {
+ "width": 188,
+ "height": 387.6
+ },
+ {
+ "width": 188,
+ "height": 392
+ },
+ {
+ "width": 188,
+ "height": 395.2
+ },
+ {
+ "width": 188,
+ "height": 396.8
+ },
+ {
+ "width": 188,
+ "height": 398
+ },
+ {
+ "width": 188,
+ "height": 398.8
+ },
+ {
+ "width": 188,
+ "height": 399.2
+ },
+ {
+ "width": 188,
+ "height": 399.6
+ },
+ {
+ "width": 188,
+ "height": 399.6
+ }
+ ]
+ },
+ {
+ "name": "RevealElement_alpha",
+ "type": "float",
+ "data_points": [
+ {
+ "type": "not_found"
+ },
+ {
+ "type": "not_found"
+ },
+ {
+ "type": "not_found"
+ },
+ {
+ "type": "not_found"
+ },
+ {
+ "type": "not_found"
+ },
+ {
+ "type": "not_found"
+ },
+ {
+ "type": "not_found"
+ },
+ {
+ "type": "not_found"
+ },
+ 0,
+ 0,
+ 0.008216977,
+ 0.06259775,
+ 0.19032806,
+ 0.39281356,
+ 0.57081985,
+ 0.7082821,
+ 0.80721295,
+ 0.8752918,
+ 0.9206928,
+ 0.95026827,
+ 0.9691833,
+ 0.98110056,
+ 0.988515,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ }
+ ]
+} \ No newline at end of file
diff --git a/packages/SystemUI/compose/scene/tests/goldens/floating_verticalReveal_gesture_magneticDetachAndReattach.json b/packages/SystemUI/compose/scene/tests/goldens/floating_verticalReveal_gesture_magneticDetachAndReattach.json
new file mode 100644
index 000000000000..2504e57a927b
--- /dev/null
+++ b/packages/SystemUI/compose/scene/tests/goldens/floating_verticalReveal_gesture_magneticDetachAndReattach.json
@@ -0,0 +1,714 @@
+{
+ "frame_ids": [
+ 0,
+ 16,
+ 32,
+ 48,
+ 64,
+ 80,
+ 96,
+ 112,
+ 128,
+ 144,
+ 160,
+ 176,
+ 192,
+ 208,
+ 224,
+ 240,
+ 256,
+ 272,
+ 288,
+ 304,
+ 320,
+ 336,
+ 352,
+ 368,
+ 384,
+ 400,
+ 416,
+ 432,
+ 448,
+ 464,
+ 480,
+ 496,
+ 512,
+ 528,
+ 544,
+ 560,
+ 576,
+ 592,
+ 608,
+ 624,
+ 640,
+ 656,
+ 672,
+ 688,
+ 704,
+ 720,
+ 736,
+ 752,
+ 768,
+ 784,
+ 800,
+ 816,
+ 832,
+ 848,
+ 864,
+ 880,
+ 896,
+ 912,
+ 928,
+ 944,
+ 960,
+ 976,
+ 992,
+ 1008,
+ 1024,
+ 1040,
+ 1056,
+ 1072,
+ 1088
+ ],
+ "features": [
+ {
+ "name": "RevealElement_position",
+ "type": "dpOffset",
+ "data_points": [
+ {
+ "type": "not_found"
+ },
+ {
+ "type": "not_found"
+ },
+ {
+ "type": "not_found"
+ },
+ {
+ "type": "not_found"
+ },
+ {
+ "x": 50,
+ "y": 50
+ },
+ {
+ "x": 50,
+ "y": 50
+ },
+ {
+ "x": 50,
+ "y": 50
+ },
+ {
+ "x": 50,
+ "y": 50
+ },
+ {
+ "x": 50,
+ "y": 50
+ },
+ {
+ "x": 50,
+ "y": 50
+ },
+ {
+ "x": 50,
+ "y": 50
+ },
+ {
+ "x": 50,
+ "y": 50
+ },
+ {
+ "x": 50,
+ "y": 50
+ },
+ {
+ "x": 50,
+ "y": 50
+ },
+ {
+ "x": 50,
+ "y": 50
+ },
+ {
+ "x": 50,
+ "y": 50
+ },
+ {
+ "x": 50,
+ "y": 50
+ },
+ {
+ "x": 50,
+ "y": 50
+ },
+ {
+ "x": 50,
+ "y": 50
+ },
+ {
+ "x": 50,
+ "y": 50
+ },
+ {
+ "x": 50,
+ "y": 50
+ },
+ {
+ "x": 50,
+ "y": 50
+ },
+ {
+ "x": 50,
+ "y": 50
+ },
+ {
+ "x": 50,
+ "y": 50
+ },
+ {
+ "x": 50,
+ "y": 50
+ },
+ {
+ "x": 50,
+ "y": 50
+ },
+ {
+ "x": 50,
+ "y": 50
+ },
+ {
+ "x": 50,
+ "y": 50
+ },
+ {
+ "x": 50,
+ "y": 50
+ },
+ {
+ "x": 50,
+ "y": 50
+ },
+ {
+ "x": 50,
+ "y": 50
+ },
+ {
+ "x": 50,
+ "y": 50
+ },
+ {
+ "x": 50,
+ "y": 50
+ },
+ {
+ "x": 50,
+ "y": 50
+ },
+ {
+ "x": 50,
+ "y": 50
+ },
+ {
+ "x": 50,
+ "y": 50
+ },
+ {
+ "x": 50,
+ "y": 50
+ },
+ {
+ "x": 50,
+ "y": 50
+ },
+ {
+ "x": 50,
+ "y": 50
+ },
+ {
+ "x": 50,
+ "y": 50
+ },
+ {
+ "x": 50,
+ "y": 50
+ },
+ {
+ "x": 50,
+ "y": 50
+ },
+ {
+ "x": 50,
+ "y": 50
+ },
+ {
+ "x": 50,
+ "y": 50
+ },
+ {
+ "x": 50,
+ "y": 50
+ },
+ {
+ "x": 50,
+ "y": 50
+ },
+ {
+ "x": 50,
+ "y": 50
+ },
+ {
+ "x": 50,
+ "y": 50
+ },
+ {
+ "x": 50,
+ "y": 50
+ },
+ {
+ "x": 50,
+ "y": 50
+ },
+ {
+ "x": 50,
+ "y": 50
+ },
+ {
+ "x": 50,
+ "y": 50
+ },
+ {
+ "x": 50,
+ "y": 50
+ },
+ {
+ "x": 50,
+ "y": 50
+ },
+ {
+ "x": 50,
+ "y": 50
+ },
+ {
+ "x": 50,
+ "y": 50
+ },
+ {
+ "x": 50,
+ "y": 50
+ },
+ {
+ "x": 50,
+ "y": 50
+ },
+ {
+ "x": 50,
+ "y": 50
+ },
+ {
+ "x": 50,
+ "y": 50
+ },
+ {
+ "x": 50,
+ "y": 50
+ },
+ {
+ "x": 50,
+ "y": 50
+ },
+ {
+ "x": 50,
+ "y": 50
+ },
+ {
+ "x": 50,
+ "y": 50
+ },
+ {
+ "x": 50,
+ "y": 50
+ },
+ {
+ "x": 50,
+ "y": 50
+ },
+ {
+ "x": 50,
+ "y": 50
+ },
+ {
+ "x": 50,
+ "y": 50
+ },
+ {
+ "x": 50,
+ "y": 50
+ }
+ ]
+ },
+ {
+ "name": "RevealElement_size",
+ "type": "dpSize",
+ "data_points": [
+ {
+ "type": "not_found"
+ },
+ {
+ "type": "not_found"
+ },
+ {
+ "type": "not_found"
+ },
+ {
+ "type": "not_found"
+ },
+ {
+ "width": 188,
+ "height": 1.6
+ },
+ {
+ "width": 188,
+ "height": 2.8
+ },
+ {
+ "width": 188,
+ "height": 4
+ },
+ {
+ "width": 188,
+ "height": 5.2
+ },
+ {
+ "width": 188,
+ "height": 6.4
+ },
+ {
+ "width": 188,
+ "height": 7.6
+ },
+ {
+ "width": 188,
+ "height": 8.8
+ },
+ {
+ "width": 188,
+ "height": 10
+ },
+ {
+ "width": 188,
+ "height": 10.8
+ },
+ {
+ "width": 188,
+ "height": 12
+ },
+ {
+ "width": 188,
+ "height": 12.8
+ },
+ {
+ "width": 188,
+ "height": 13.6
+ },
+ {
+ "width": 188,
+ "height": 14.8
+ },
+ {
+ "width": 188,
+ "height": 15.6
+ },
+ {
+ "width": 188,
+ "height": 16.4
+ },
+ {
+ "width": 188,
+ "height": 17.2
+ },
+ {
+ "width": 188,
+ "height": 17.6
+ },
+ {
+ "width": 188,
+ "height": 18.4
+ },
+ {
+ "width": 188,
+ "height": 19.2
+ },
+ {
+ "width": 188,
+ "height": 19.6
+ },
+ {
+ "width": 188,
+ "height": 20
+ },
+ {
+ "width": 188,
+ "height": 20.4
+ },
+ {
+ "width": 188,
+ "height": 20.8
+ },
+ {
+ "width": 188,
+ "height": 21.2
+ },
+ {
+ "width": 188,
+ "height": 21.6
+ },
+ {
+ "width": 188,
+ "height": 21.6
+ },
+ {
+ "width": 188,
+ "height": 21.6
+ },
+ {
+ "width": 188,
+ "height": 21.6
+ },
+ {
+ "width": 188,
+ "height": 21.6
+ },
+ {
+ "width": 188,
+ "height": 21.6
+ },
+ {
+ "width": 188,
+ "height": 21.6
+ },
+ {
+ "width": 188,
+ "height": 21.6
+ },
+ {
+ "width": 188,
+ "height": 21.2
+ },
+ {
+ "width": 188,
+ "height": 20.8
+ },
+ {
+ "width": 188,
+ "height": 20.4
+ },
+ {
+ "width": 188,
+ "height": 20
+ },
+ {
+ "width": 188,
+ "height": 19.6
+ },
+ {
+ "width": 188,
+ "height": 19.2
+ },
+ {
+ "width": 188,
+ "height": 18.4
+ },
+ {
+ "width": 188,
+ "height": 17.6
+ },
+ {
+ "width": 188,
+ "height": 17.2
+ },
+ {
+ "width": 188,
+ "height": 16.4
+ },
+ {
+ "width": 188,
+ "height": 15.6
+ },
+ {
+ "width": 188,
+ "height": 14.8
+ },
+ {
+ "width": 188,
+ "height": 13.6
+ },
+ {
+ "width": 188,
+ "height": 12.8
+ },
+ {
+ "width": 188,
+ "height": 12
+ },
+ {
+ "width": 188,
+ "height": 10.8
+ },
+ {
+ "width": 188,
+ "height": 10
+ },
+ {
+ "width": 188,
+ "height": 8.8
+ },
+ {
+ "width": 188,
+ "height": 7.6
+ },
+ {
+ "width": 188,
+ "height": 6.4
+ },
+ {
+ "width": 188,
+ "height": 5.2
+ },
+ {
+ "width": 188,
+ "height": 4
+ },
+ {
+ "width": 188,
+ "height": 2.8
+ },
+ {
+ "width": 188,
+ "height": 1.6
+ },
+ {
+ "width": 188,
+ "height": 0.4
+ },
+ {
+ "width": 188,
+ "height": 0
+ },
+ {
+ "width": 188,
+ "height": 0
+ },
+ {
+ "width": 188,
+ "height": 0
+ },
+ {
+ "width": 188,
+ "height": 0
+ },
+ {
+ "width": 188,
+ "height": 0
+ },
+ {
+ "width": 188,
+ "height": 0
+ },
+ {
+ "width": 188,
+ "height": 0
+ },
+ {
+ "width": 188,
+ "height": 0
+ }
+ ]
+ },
+ {
+ "name": "RevealElement_alpha",
+ "type": "float",
+ "data_points": [
+ {
+ "type": "not_found"
+ },
+ {
+ "type": "not_found"
+ },
+ {
+ "type": "not_found"
+ },
+ {
+ "type": "not_found"
+ },
+ 0,
+ 0,
+ 0,
+ 0,
+ 0.012518823,
+ 0.0741024,
+ 0.2254293,
+ 0.42628878,
+ 0.5976641,
+ 0.7280312,
+ 0.82100236,
+ 0.8845844,
+ 0.9267946,
+ 0.95419544,
+ 0.9716705,
+ 0.98265487,
+ 0.98947525,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 0.9944124,
+ 0.9417388,
+ 0.8184184,
+ 0.6157812,
+ 0.4361611,
+ 0.2968906,
+ 0.19641554,
+ 0.12716137,
+ 0.080921985,
+ 0.050773025,
+ 0.03147719,
+ 0.019312752,
+ 0.011740655
+ ]
+ }
+ ]
+} \ No newline at end of file
diff --git a/packages/SystemUI/compose/scene/tests/goldens/floating_verticalReveal_triggeredRevealCloseTransition.json b/packages/SystemUI/compose/scene/tests/goldens/floating_verticalReveal_triggeredRevealCloseTransition.json
new file mode 100644
index 000000000000..86fac739372e
--- /dev/null
+++ b/packages/SystemUI/compose/scene/tests/goldens/floating_verticalReveal_triggeredRevealCloseTransition.json
@@ -0,0 +1,314 @@
+{
+ "frame_ids": [
+ 0,
+ 16,
+ 32,
+ 48,
+ 64,
+ 80,
+ 96,
+ 112,
+ 128,
+ 144,
+ 160,
+ 176,
+ 192,
+ 208,
+ 224,
+ 240,
+ 256,
+ 272,
+ 288,
+ 304,
+ 320,
+ 336,
+ 352,
+ 368,
+ 384,
+ 400,
+ 416,
+ 432,
+ 448
+ ],
+ "features": [
+ {
+ "name": "RevealElement_position",
+ "type": "dpOffset",
+ "data_points": [
+ {
+ "x": 50,
+ "y": 50
+ },
+ {
+ "x": 50,
+ "y": 50
+ },
+ {
+ "x": 50,
+ "y": 50
+ },
+ {
+ "x": 50,
+ "y": 50
+ },
+ {
+ "x": 50,
+ "y": 50
+ },
+ {
+ "x": 50,
+ "y": 50
+ },
+ {
+ "x": 50,
+ "y": 50
+ },
+ {
+ "x": 50,
+ "y": 50
+ },
+ {
+ "x": 50,
+ "y": 50
+ },
+ {
+ "x": 50,
+ "y": 50
+ },
+ {
+ "x": 50,
+ "y": 50
+ },
+ {
+ "x": 50,
+ "y": 50
+ },
+ {
+ "x": 50,
+ "y": 50
+ },
+ {
+ "x": 50,
+ "y": 50
+ },
+ {
+ "x": 50,
+ "y": 50
+ },
+ {
+ "x": 50,
+ "y": 50
+ },
+ {
+ "x": 50,
+ "y": 50
+ },
+ {
+ "x": 50,
+ "y": 50
+ },
+ {
+ "x": 50,
+ "y": 50
+ },
+ {
+ "x": 50,
+ "y": 50
+ },
+ {
+ "x": 50,
+ "y": 50
+ },
+ {
+ "x": 50,
+ "y": 50
+ },
+ {
+ "x": 50,
+ "y": 50
+ },
+ {
+ "x": 50,
+ "y": 50
+ },
+ {
+ "x": 50,
+ "y": 50
+ },
+ {
+ "x": 50,
+ "y": 50
+ },
+ {
+ "x": 50,
+ "y": 50
+ },
+ {
+ "x": 50,
+ "y": 50
+ },
+ {
+ "x": 50,
+ "y": 50
+ }
+ ]
+ },
+ {
+ "name": "RevealElement_size",
+ "type": "dpSize",
+ "data_points": [
+ {
+ "width": 188,
+ "height": 400
+ },
+ {
+ "width": 188,
+ "height": 400
+ },
+ {
+ "width": 188,
+ "height": 372
+ },
+ {
+ "width": 188,
+ "height": 312.8
+ },
+ {
+ "width": 188,
+ "height": 246.8
+ },
+ {
+ "width": 188,
+ "height": 185.2
+ },
+ {
+ "width": 188,
+ "height": 133.6
+ },
+ {
+ "width": 188,
+ "height": 93.2
+ },
+ {
+ "width": 188,
+ "height": 58.8
+ },
+ {
+ "width": 188,
+ "height": 34.4
+ },
+ {
+ "width": 188,
+ "height": 18
+ },
+ {
+ "width": 188,
+ "height": 7.6
+ },
+ {
+ "width": 188,
+ "height": 0.8
+ },
+ {
+ "width": 188,
+ "height": 0
+ },
+ {
+ "width": 188,
+ "height": 0
+ },
+ {
+ "width": 188,
+ "height": 0
+ },
+ {
+ "width": 188,
+ "height": 0
+ },
+ {
+ "width": 188,
+ "height": 0
+ },
+ {
+ "width": 188,
+ "height": 0
+ },
+ {
+ "width": 188,
+ "height": 0
+ },
+ {
+ "width": 188,
+ "height": 0
+ },
+ {
+ "width": 188,
+ "height": 0
+ },
+ {
+ "width": 188,
+ "height": 0
+ },
+ {
+ "width": 188,
+ "height": 0
+ },
+ {
+ "width": 188,
+ "height": 0
+ },
+ {
+ "width": 188,
+ "height": 0
+ },
+ {
+ "width": 188,
+ "height": 0
+ },
+ {
+ "width": 188,
+ "height": 0
+ },
+ {
+ "width": 188,
+ "height": 0
+ }
+ ]
+ },
+ {
+ "name": "RevealElement_alpha",
+ "type": "float",
+ "data_points": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 0.91758585,
+ 0.72435355,
+ 0.52812576,
+ 0.3665868,
+ 0.24600428,
+ 0.16102076,
+ 0.103373945,
+ 0.06533456,
+ 0.04075712,
+ 0.025142312,
+ 0.015358448,
+ 0.0092999935,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0
+ ]
+ }
+ ]
+} \ No newline at end of file
diff --git a/packages/SystemUI/compose/scene/tests/goldens/floating_verticalReveal_triggeredRevealOpenTransition.json b/packages/SystemUI/compose/scene/tests/goldens/floating_verticalReveal_triggeredRevealOpenTransition.json
new file mode 100644
index 000000000000..ad282f216f8f
--- /dev/null
+++ b/packages/SystemUI/compose/scene/tests/goldens/floating_verticalReveal_triggeredRevealOpenTransition.json
@@ -0,0 +1,244 @@
+{
+ "frame_ids": [
+ 0,
+ 16,
+ 32,
+ 48,
+ 64,
+ 80,
+ 96,
+ 112,
+ 128,
+ 144,
+ 160,
+ 176,
+ 192,
+ 208,
+ 224,
+ 240,
+ 256,
+ 272,
+ 288,
+ 304,
+ 320,
+ 336
+ ],
+ "features": [
+ {
+ "name": "RevealElement_position",
+ "type": "dpOffset",
+ "data_points": [
+ {
+ "type": "not_found"
+ },
+ {
+ "x": 50,
+ "y": 50
+ },
+ {
+ "x": 50,
+ "y": 50
+ },
+ {
+ "x": 50,
+ "y": 50
+ },
+ {
+ "x": 50,
+ "y": 50
+ },
+ {
+ "x": 50,
+ "y": 50
+ },
+ {
+ "x": 50,
+ "y": 50
+ },
+ {
+ "x": 50,
+ "y": 50
+ },
+ {
+ "x": 50,
+ "y": 50
+ },
+ {
+ "x": 50,
+ "y": 50
+ },
+ {
+ "x": 50,
+ "y": 50
+ },
+ {
+ "x": 50,
+ "y": 50
+ },
+ {
+ "x": 50,
+ "y": 50
+ },
+ {
+ "x": 50,
+ "y": 50
+ },
+ {
+ "x": 50,
+ "y": 50
+ },
+ {
+ "x": 50,
+ "y": 50
+ },
+ {
+ "x": 50,
+ "y": 50
+ },
+ {
+ "x": 50,
+ "y": 50
+ },
+ {
+ "x": 50,
+ "y": 50
+ },
+ {
+ "x": 50,
+ "y": 50
+ },
+ {
+ "x": 50,
+ "y": 50
+ },
+ {
+ "x": 50,
+ "y": 50
+ }
+ ]
+ },
+ {
+ "name": "RevealElement_size",
+ "type": "dpSize",
+ "data_points": [
+ {
+ "type": "not_found"
+ },
+ {
+ "width": 188,
+ "height": 0
+ },
+ {
+ "width": 188,
+ "height": 6.8
+ },
+ {
+ "width": 188,
+ "height": 21.6
+ },
+ {
+ "width": 188,
+ "height": 52.8
+ },
+ {
+ "width": 188,
+ "height": 129.6
+ },
+ {
+ "width": 188,
+ "height": 196.4
+ },
+ {
+ "width": 188,
+ "height": 250.4
+ },
+ {
+ "width": 188,
+ "height": 293.2
+ },
+ {
+ "width": 188,
+ "height": 325.2
+ },
+ {
+ "width": 188,
+ "height": 348.8
+ },
+ {
+ "width": 188,
+ "height": 365.6
+ },
+ {
+ "width": 188,
+ "height": 377.2
+ },
+ {
+ "width": 188,
+ "height": 385.6
+ },
+ {
+ "width": 188,
+ "height": 391.2
+ },
+ {
+ "width": 188,
+ "height": 394.8
+ },
+ {
+ "width": 188,
+ "height": 396.8
+ },
+ {
+ "width": 188,
+ "height": 398
+ },
+ {
+ "width": 188,
+ "height": 398.8
+ },
+ {
+ "width": 188,
+ "height": 399.2
+ },
+ {
+ "width": 188,
+ "height": 399.6
+ },
+ {
+ "width": 188,
+ "height": 399.6
+ }
+ ]
+ },
+ {
+ "name": "RevealElement_alpha",
+ "type": "float",
+ "data_points": [
+ {
+ "type": "not_found"
+ },
+ 0,
+ 0.05698657,
+ 0.24197984,
+ 0.44158113,
+ 0.6097554,
+ 0.73685503,
+ 0.8271309,
+ 0.8886989,
+ 0.9294886,
+ 0.9559254,
+ 0.97276413,
+ 0.98333716,
+ 0.98989624,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ }
+ ]
+} \ No newline at end of file
diff --git a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/reveal/ContentRevealTest.kt b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/reveal/ContentRevealTest.kt
index f4e2328f16ab..ed73d100dc6b 100644
--- a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/reveal/ContentRevealTest.kt
+++ b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/reveal/ContentRevealTest.kt
@@ -37,7 +37,6 @@ import androidx.compose.ui.test.swipeUp
import androidx.compose.ui.test.swipeWithVelocity
import androidx.compose.ui.unit.DpSize
import androidx.compose.ui.unit.dp
-import androidx.test.ext.junit.runners.AndroidJUnit4
import com.android.compose.animation.scene.ContentScope
import com.android.compose.animation.scene.ElementKey
import com.android.compose.animation.scene.FeatureCaptures.elementAlpha
@@ -47,8 +46,8 @@ import com.android.compose.animation.scene.SceneTransitionLayoutForTesting
import com.android.compose.animation.scene.Swipe
import com.android.compose.animation.scene.featureOfElement
import com.android.compose.animation.scene.transitions
-import com.android.mechanics.behavior.EdgeContainerExpansionSpec
-import com.android.mechanics.behavior.edgeContainerExpansionBackground
+import com.android.mechanics.behavior.VerticalExpandContainerSpec
+import com.android.mechanics.behavior.verticalExpandContainerBackground
import kotlin.math.sin
import kotlinx.coroutines.CoroutineScope
import org.junit.Rule
@@ -63,26 +62,48 @@ import platform.test.motion.compose.createFixedConfigurationComposeMotionTestRul
import platform.test.motion.compose.recordMotion
import platform.test.motion.compose.runTest
import platform.test.motion.testing.createGoldenPathManager
+import platform.test.runner.parameterized.ParameterizedAndroidJunit4
+import platform.test.runner.parameterized.Parameters
+import platform.test.screenshot.PathConfig
+import platform.test.screenshot.PathElementNoContext
-@RunWith(AndroidJUnit4::class)
@MotionTest
-class ContentRevealTest {
+@RunWith(ParameterizedAndroidJunit4::class)
+class ContentRevealTest(private val isFloating: Boolean) {
+
+ private val pathConfig =
+ PathConfig(
+ PathElementNoContext("floating", isDir = false) {
+ if (isFloating) "floating" else "edge"
+ }
+ )
private val goldenPaths =
- createGoldenPathManager("frameworks/base/packages/SystemUI/compose/scene/tests/goldens")
+ createGoldenPathManager(
+ "frameworks/base/packages/SystemUI/compose/scene/tests/goldens",
+ pathConfig,
+ )
@get:Rule val motionRule = createFixedConfigurationComposeMotionTestRule(goldenPaths)
private val fakeHaptics = FakeHaptics()
+ private val motionSpec = VerticalExpandContainerSpec(isFloating)
+
@Test
fun verticalReveal_triggeredRevealOpenTransition() {
- assertVerticalContainerRevealMotion(TriggeredRevealMotion(SceneClosed, SceneOpen))
+ assertVerticalContainerRevealMotion(
+ TriggeredRevealMotion(SceneClosed, SceneOpen),
+ "verticalReveal_triggeredRevealOpenTransition",
+ )
}
@Test
fun verticalReveal_triggeredRevealCloseTransition() {
- assertVerticalContainerRevealMotion(TriggeredRevealMotion(SceneOpen, SceneClosed))
+ assertVerticalContainerRevealMotion(
+ TriggeredRevealMotion(SceneOpen, SceneClosed),
+ "verticalReveal_triggeredRevealCloseTransition",
+ )
}
@Test
@@ -98,7 +119,8 @@ class ContentRevealTest {
},
gestureDurationMillis,
)
- }
+ },
+ "verticalReveal_gesture_magneticDetachAndReattach",
)
}
@@ -107,7 +129,8 @@ class ContentRevealTest {
assertVerticalContainerRevealMotion(
GestureRevealMotion(SceneClosed) {
swipeDown(endY = 200.dp.toPx(), durationMillis = 500)
- }
+ },
+ "verticalReveal_gesture_dragOpen",
)
}
@@ -117,7 +140,8 @@ class ContentRevealTest {
GestureRevealMotion(SceneClosed) {
val end = Offset(centerX, 80.dp.toPx())
swipeWithVelocity(start = topCenter, end = end, endVelocity = FlingVelocity.toPx())
- }
+ },
+ "verticalReveal_gesture_flingOpen",
)
}
@@ -126,7 +150,8 @@ class ContentRevealTest {
assertVerticalContainerRevealMotion(
GestureRevealMotion(SceneOpen) {
swipeUp(200.dp.toPx(), 0.dp.toPx(), durationMillis = 500)
- }
+ },
+ "verticalReveal_gesture_dragFullyClose",
)
}
@@ -135,7 +160,8 @@ class ContentRevealTest {
assertVerticalContainerRevealMotion(
GestureRevealMotion(SceneOpen) {
swipeUp(350.dp.toPx(), 100.dp.toPx(), durationMillis = 500)
- }
+ },
+ "verticalReveal_gesture_dragHalfClose",
)
}
@@ -146,7 +172,8 @@ class ContentRevealTest {
val start = Offset(centerX, 260.dp.toPx())
val end = Offset(centerX, 200.dp.toPx())
swipeWithVelocity(start, end, FlingVelocity.toPx())
- }
+ },
+ "verticalReveal_gesture_flingClose",
)
}
@@ -164,11 +191,14 @@ class ContentRevealTest {
val gestureControl: TouchInjectionScope.() -> Unit,
) : RevealMotion
- private fun assertVerticalContainerRevealMotion(testInstructions: RevealMotion) =
+ private fun assertVerticalContainerRevealMotion(
+ testInstructions: RevealMotion,
+ goldenName: String,
+ ) =
motionRule.runTest {
val transitions = transitions {
from(SceneClosed, to = SceneOpen) {
- verticalContainerReveal(RevealElement, MotionSpec, fakeHaptics)
+ verticalContainerReveal(RevealElement, motionSpec, fakeHaptics)
}
}
@@ -241,7 +271,7 @@ class ContentRevealTest {
recordingSpec,
)
- assertThat(motion).timeSeriesMatchesGolden()
+ assertThat(motion).timeSeriesMatchesGolden(goldenName)
}
@Composable
@@ -256,7 +286,7 @@ class ContentRevealTest {
modifier =
Modifier.element(RevealElement)
.size(ContainerSize)
- .edgeContainerExpansionBackground(Color.DarkGray, MotionSpec)
+ .verticalExpandContainerBackground(Color.DarkGray, motionSpec)
)
}
}
@@ -266,6 +296,8 @@ class ContentRevealTest {
}
companion object {
+ @get:Parameters @JvmStatic val parameterValues = listOf(true, false)
+
val ContainerSize = DpSize(200.dp, 400.dp)
val FlingVelocity = 1000.dp // dp/sec
@@ -274,6 +306,5 @@ class ContentRevealTest {
val SceneOpen = SceneKey("SceneB")
val RevealElement = ElementKey("RevealElement")
- val MotionSpec = EdgeContainerExpansionSpec()
}
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/clipboardoverlay/ActionIntentCreatorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/clipboardoverlay/ActionIntentCreatorTest.kt
index 239e02640908..652a2ff21e9b 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/clipboardoverlay/ActionIntentCreatorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/clipboardoverlay/ActionIntentCreatorTest.kt
@@ -25,6 +25,10 @@ import androidx.test.filters.SmallTest
import androidx.test.runner.AndroidJUnit4
import com.android.systemui.SysuiTestCase
import com.android.systemui.res.R
+import kotlinx.coroutines.test.TestCoroutineScheduler
+import kotlinx.coroutines.test.TestScope
+import kotlinx.coroutines.test.UnconfinedTestDispatcher
+import kotlinx.coroutines.test.runTest
import org.junit.Assert.assertEquals
import org.junit.Assert.assertTrue
import org.junit.Test
@@ -33,7 +37,11 @@ import org.junit.runner.RunWith
@SmallTest
@RunWith(AndroidJUnit4::class)
class ActionIntentCreatorTest : SysuiTestCase() {
- val creator = ActionIntentCreator()
+ private val scheduler = TestCoroutineScheduler()
+ private val mainDispatcher = UnconfinedTestDispatcher(scheduler)
+ private val testScope = TestScope(mainDispatcher)
+
+ val creator = ActionIntentCreator(testScope.backgroundScope)
@Test
fun test_getTextEditorIntent() {
@@ -65,7 +73,7 @@ class ActionIntentCreatorTest : SysuiTestCase() {
}
@Test
- fun test_getImageEditIntent() {
+ fun test_getImageEditIntent() = runTest {
context.getOrCreateTestableResources().addOverride(R.string.config_screenshotEditor, "")
val fakeUri = Uri.parse("content://foo")
var intent = creator.getImageEditIntent(fakeUri, context)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/clipboardoverlay/DefaultIntentCreatorTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/clipboardoverlay/DefaultIntentCreatorTest.java
index 126b3fa9e7ca..10c5c52d21a7 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/clipboardoverlay/DefaultIntentCreatorTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/clipboardoverlay/DefaultIntentCreatorTest.java
@@ -34,6 +34,8 @@ import com.android.systemui.res.R;
import org.junit.Test;
import org.junit.runner.RunWith;
+import java.util.concurrent.atomic.AtomicReference;
+
@SmallTest
@RunWith(AndroidJUnit4.class)
public class DefaultIntentCreatorTest extends SysuiTestCase {
@@ -73,12 +75,16 @@ public class DefaultIntentCreatorTest extends SysuiTestCase {
}
@Test
- public void test_getImageEditIntent() {
+ public void test_getImageEditIntentAsync() {
getContext().getOrCreateTestableResources().addOverride(R.string.config_screenshotEditor,
"");
Uri fakeUri = Uri.parse("content://foo");
- Intent intent = mIntentCreator.getImageEditIntent(fakeUri, getContext());
+ final AtomicReference<Intent> intentHolder = new AtomicReference<>(null);
+ mIntentCreator.getImageEditIntentAsync(fakeUri, getContext(), output -> {
+ intentHolder.set(output);
+ });
+ Intent intent = intentHolder.get();
assertEquals(Intent.ACTION_EDIT, intent.getAction());
assertEquals("image/*", intent.getType());
assertEquals(null, intent.getComponent());
@@ -90,8 +96,10 @@ public class DefaultIntentCreatorTest extends SysuiTestCase {
"com.android.remotecopy.RemoteCopyActivity");
getContext().getOrCreateTestableResources().addOverride(R.string.config_screenshotEditor,
fakeComponent.flattenToString());
- intent = mIntentCreator.getImageEditIntent(fakeUri, getContext());
- assertEquals(fakeComponent, intent.getComponent());
+ mIntentCreator.getImageEditIntentAsync(fakeUri, getContext(), output -> {
+ intentHolder.set(output);
+ });
+ assertEquals(fakeComponent, intentHolder.get().getComponent());
}
@Test
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/display/data/repository/DisplayRepositoryTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/display/data/repository/DisplayRepositoryTest.kt
index 197b0eea05cb..859137507bbf 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/display/data/repository/DisplayRepositoryTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/display/data/repository/DisplayRepositoryTest.kt
@@ -26,6 +26,7 @@ import android.view.Display.TYPE_INTERNAL
import android.view.IWindowManager
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
+import com.android.app.displaylib.DisplayRepository.PendingDisplay
import com.android.systemui.SysuiTestCase
import com.android.systemui.coroutines.FlowValue
import com.android.systemui.coroutines.collectLastValue
@@ -71,14 +72,20 @@ class DisplayRepositoryTest : SysuiTestCase() {
// that the initial state (soon after construction) contains the expected ones set in every
// test.
private val displayRepository: DisplayRepositoryImpl by lazy {
- DisplayRepositoryImpl(
+ // TODO b/401305290 - move this to kosmos
+ val displayRepositoryFromLib =
+ com.android.app.displaylib.DisplayRepositoryImpl(
displayManager,
- commandQueue,
- windowManager,
testHandler,
- TestScope(UnconfinedTestDispatcher()),
+ testScope.backgroundScope,
UnconfinedTestDispatcher(),
)
+ DisplayRepositoryImpl(
+ commandQueue,
+ windowManager,
+ testScope.backgroundScope,
+ displayRepositoryFromLib,
+ )
.also {
verify(displayManager, never()).registerDisplayListener(any(), any())
// It needs to be called, just once, for the initial value.
@@ -403,7 +410,7 @@ class DisplayRepositoryTest : SysuiTestCase() {
val pendingDisplay by lastPendingDisplay()
sendOnDisplayConnected(1, TYPE_EXTERNAL)
- val initialPendingDisplay: DisplayRepository.PendingDisplay? = pendingDisplay
+ val initialPendingDisplay: PendingDisplay? = pendingDisplay
assertThat(pendingDisplay).isNotNull()
sendOnDisplayChanged(1)
@@ -416,7 +423,7 @@ class DisplayRepositoryTest : SysuiTestCase() {
val pendingDisplay by lastPendingDisplay()
sendOnDisplayConnected(1, TYPE_EXTERNAL)
- val initialPendingDisplay: DisplayRepository.PendingDisplay? = pendingDisplay
+ val initialPendingDisplay: PendingDisplay? = pendingDisplay
assertThat(pendingDisplay).isNotNull()
sendOnDisplayConnected(2, TYPE_EXTERNAL)
@@ -648,7 +655,7 @@ class DisplayRepositoryTest : SysuiTestCase() {
return flowValue
}
- private fun TestScope.lastPendingDisplay(): FlowValue<DisplayRepository.PendingDisplay?> {
+ private fun TestScope.lastPendingDisplay(): FlowValue<PendingDisplay?> {
val flowValue = collectLastValue(displayRepository.pendingDisplay)
captureAddedRemovedListener()
verify(displayManager)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/display/data/repository/PerDisplayInstanceRepositoryImplTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/display/data/repository/PerDisplayInstanceRepositoryImplTest.kt
index e41d46ce90af..6c7783ae44e1 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/display/data/repository/PerDisplayInstanceRepositoryImplTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/display/data/repository/PerDisplayInstanceRepositoryImplTest.kt
@@ -31,7 +31,7 @@ import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.ArgumentMatchers.anyString
-import org.mockito.kotlin.eq
+import org.mockito.kotlin.any
import org.mockito.kotlin.verify
@RunWith(AndroidJUnit4::class)
@@ -105,7 +105,7 @@ class PerDisplayInstanceRepositoryImplTest : SysuiTestCase() {
@Test
fun start_registersDumpable() {
- verify(kosmos.dumpManager).registerNormalDumpable(anyString(), eq(underTest))
+ verify(kosmos.dumpManager).registerNormalDumpable(anyString(), any())
}
private fun createDisplay(displayId: Int): Display =
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/binder/WindowManagerLockscreenVisibilityManagerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/binder/WindowManagerLockscreenVisibilityManagerTest.kt
index 97c746c49cba..d0762a3797c0 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/binder/WindowManagerLockscreenVisibilityManagerTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/binder/WindowManagerLockscreenVisibilityManagerTest.kt
@@ -25,10 +25,13 @@ import android.view.IRemoteAnimationFinishedCallback
import android.view.RemoteAnimationTarget
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
+import com.android.internal.widget.LockPatternUtils
import com.android.systemui.SysuiTestCase
import com.android.systemui.keyguard.WindowManagerLockscreenVisibilityManager
import com.android.systemui.keyguard.domain.interactor.KeyguardDismissTransitionInteractor
+import com.android.systemui.keyguard.domain.interactor.KeyguardShowWhileAwakeInteractor
import com.android.systemui.statusbar.policy.KeyguardStateController
+import com.android.systemui.user.domain.interactor.SelectedUserInteractor
import com.android.systemui.util.concurrency.FakeExecutor
import com.android.systemui.util.time.FakeSystemClock
import com.android.window.flags.Flags
@@ -63,6 +66,9 @@ class WindowManagerLockscreenVisibilityManagerTest : SysuiTestCase() {
@Mock
private lateinit var keyguardDismissTransitionInteractor: KeyguardDismissTransitionInteractor
@Mock private lateinit var keyguardTransitions: KeyguardTransitions
+ @Mock private lateinit var lockPatternUtils: LockPatternUtils
+ @Mock private lateinit var keyguardShowWhileAwakeInteractor: KeyguardShowWhileAwakeInteractor
+ @Mock private lateinit var selectedUserInteractor: SelectedUserInteractor
@Before
fun setUp() {
@@ -77,6 +83,9 @@ class WindowManagerLockscreenVisibilityManagerTest : SysuiTestCase() {
keyguardSurfaceBehindAnimator = keyguardSurfaceBehindAnimator,
keyguardDismissTransitionInteractor = keyguardDismissTransitionInteractor,
keyguardTransitions = keyguardTransitions,
+ selectedUserInteractor = selectedUserInteractor,
+ lockPatternUtils = lockPatternUtils,
+ keyguardShowWhileAwakeInteractor = keyguardShowWhileAwakeInteractor,
)
}
@@ -236,6 +245,8 @@ class WindowManagerLockscreenVisibilityManagerTest : SysuiTestCase() {
.whenever(keyguardDismissTransitionInteractor)
.startDismissKeyguardTransition(any(), any())
+ whenever(selectedUserInteractor.getSelectedUserId()).thenReturn(-1)
+
underTest.onKeyguardGoingAwayRemoteAnimationStart(
transit = 0,
apps = arrayOf(mock<RemoteAnimationTarget>()),
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/NotificationShadeDepthControllerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/NotificationShadeDepthControllerTest.kt
index 326d8ffd3c7c..3ecf302204bc 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/NotificationShadeDepthControllerTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/NotificationShadeDepthControllerTest.kt
@@ -32,14 +32,12 @@ import com.android.systemui.dump.DumpManager
import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
import com.android.systemui.kosmos.testScope
import com.android.systemui.plugins.statusbar.StatusBarStateController
-import com.android.systemui.res.R
import com.android.systemui.shade.ShadeExpansionChangeEvent
+import com.android.systemui.shade.domain.interactor.ShadeModeInteractor
import com.android.systemui.statusbar.phone.BiometricUnlockController
import com.android.systemui.statusbar.phone.DozeParameters
import com.android.systemui.statusbar.phone.ScrimController
-import com.android.systemui.statusbar.policy.FakeConfigurationController
import com.android.systemui.statusbar.policy.KeyguardStateController
-import com.android.systemui.statusbar.policy.ResourcesSplitShadeStateController
import com.android.systemui.testKosmos
import com.android.systemui.util.WallpaperController
import com.android.systemui.util.mockito.eq
@@ -77,7 +75,6 @@ class NotificationShadeDepthControllerTest : SysuiTestCase() {
private val kosmos = testKosmos()
private val applicationScope = kosmos.testScope.backgroundScope
- @Mock private lateinit var windowRootViewBlurInteractor: WindowRootViewBlurInteractor
@Mock private lateinit var statusBarStateController: StatusBarStateController
@Mock private lateinit var blurUtils: BlurUtils
@Mock private lateinit var biometricUnlockController: BiometricUnlockController
@@ -87,6 +84,8 @@ class NotificationShadeDepthControllerTest : SysuiTestCase() {
@Mock private lateinit var wallpaperController: WallpaperController
@Mock private lateinit var wallpaperInteractor: WallpaperInteractor
@Mock private lateinit var notificationShadeWindowController: NotificationShadeWindowController
+ @Mock private lateinit var windowRootViewBlurInteractor: WindowRootViewBlurInteractor
+ @Mock private lateinit var shadeModeInteractor: ShadeModeInteractor
@Mock private lateinit var dumpManager: DumpManager
@Mock private lateinit var appZoomOutOptional: Optional<AppZoomOut>
@Mock private lateinit var root: View
@@ -103,7 +102,6 @@ class NotificationShadeDepthControllerTest : SysuiTestCase() {
private var statusBarState = StatusBarState.SHADE
private val maxBlur = 150
private lateinit var notificationShadeDepthController: NotificationShadeDepthController
- private val configurationController = FakeConfigurationController()
@Before
fun setup() {
@@ -133,13 +131,11 @@ class NotificationShadeDepthControllerTest : SysuiTestCase() {
wallpaperInteractor,
notificationShadeWindowController,
dozeParameters,
- context,
- ResourcesSplitShadeStateController(),
+ shadeModeInteractor,
windowRootViewBlurInteractor,
appZoomOutOptional,
applicationScope,
- dumpManager,
- configurationController,
+ dumpManager
)
notificationShadeDepthController.shadeAnimation = shadeAnimation
notificationShadeDepthController.brightnessMirrorSpring = brightnessSpring
@@ -492,15 +488,10 @@ class NotificationShadeDepthControllerTest : SysuiTestCase() {
}
private fun enableSplitShade() {
- setSplitShadeEnabled(true)
+ `when` (shadeModeInteractor.isSplitShade).thenReturn(true)
}
private fun disableSplitShade() {
- setSplitShadeEnabled(false)
- }
-
- private fun setSplitShadeEnabled(enabled: Boolean) {
- overrideResource(R.bool.config_use_split_notification_shade, enabled)
- configurationController.notifyConfigurationChanged()
+ `when` (shadeModeInteractor.isSplitShade).thenReturn(false)
}
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerTest.kt
index 1b8d64d5483c..387c62d76083 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerTest.kt
@@ -71,6 +71,7 @@ import com.android.systemui.statusbar.notification.collection.provider.HighPrior
import com.android.systemui.statusbar.notification.domain.interactor.activeNotificationsInteractor
import com.android.systemui.statusbar.notification.headsup.HeadsUpManager
import com.android.systemui.statusbar.notification.people.PeopleNotificationIdentifier
+import com.android.systemui.statusbar.notification.promoted.domain.interactor.PackageDemotionInteractor
import com.android.systemui.statusbar.notification.row.icon.AppIconProvider
import com.android.systemui.statusbar.notification.row.icon.NotificationIconStyleProvider
import com.android.systemui.statusbar.notification.row.icon.appIconProvider
@@ -80,6 +81,9 @@ import com.android.systemui.statusbar.policy.DeviceProvisionedController
import com.android.systemui.testKosmos
import com.android.systemui.util.kotlin.JavaAdapter
import com.android.systemui.wmshell.BubblesManager
+import java.util.Optional
+import kotlin.test.assertNotNull
+import kotlin.test.fail
import kotlinx.coroutines.test.runCurrent
import kotlinx.coroutines.test.runTest
import org.junit.Assert.assertEquals
@@ -107,9 +111,6 @@ import org.mockito.kotlin.verify
import org.mockito.kotlin.whenever
import platform.test.runner.parameterized.ParameterizedAndroidJunit4
import platform.test.runner.parameterized.Parameters
-import java.util.Optional
-import kotlin.test.assertNotNull
-import kotlin.test.fail
/** Tests for [NotificationGutsManager]. */
@SmallTest
@@ -149,6 +150,7 @@ class NotificationGutsManagerTest(flags: FlagsParameterization) : SysuiTestCase(
@Mock private lateinit var launcherApps: LauncherApps
@Mock private lateinit var shortcutManager: ShortcutManager
@Mock private lateinit var channelEditorDialogController: ChannelEditorDialogController
+ @Mock private lateinit var packageDemotionInteractor: PackageDemotionInteractor
@Mock private lateinit var peopleNotificationIdentifier: PeopleNotificationIdentifier
@Mock private lateinit var contextTracker: UserContextProvider
@Mock private lateinit var bubblesManager: BubblesManager
@@ -214,6 +216,7 @@ class NotificationGutsManagerTest(flags: FlagsParameterization) : SysuiTestCase(
launcherApps,
shortcutManager,
channelEditorDialogController,
+ packageDemotionInteractor,
contextTracker,
assistantFeedbackController,
Optional.of(bubblesManager),
@@ -509,6 +512,7 @@ class NotificationGutsManagerTest(flags: FlagsParameterization) : SysuiTestCase(
.setImportance(NotificationManager.IMPORTANCE_HIGH)
.build()
+ whenever(row.canViewBeDismissed()).thenReturn(true)
whenever(highPriorityProvider.isHighPriority(entry)).thenReturn(true)
val statusBarNotification = entry.sbn
gutsManager.initializeNotificationInfo(row, notificationInfoView)
@@ -521,6 +525,7 @@ class NotificationGutsManagerTest(flags: FlagsParameterization) : SysuiTestCase(
any<NotificationIconStyleProvider>(),
eq(onUserInteractionCallback),
eq(channelEditorDialogController),
+ any<PackageDemotionInteractor>(),
eq(statusBarNotification.packageName),
any<NotificationChannel>(),
eq(entry),
@@ -530,6 +535,7 @@ class NotificationGutsManagerTest(flags: FlagsParameterization) : SysuiTestCase(
any<UiEventLogger>(),
/* isDeviceProvisioned = */ eq(false),
/* isNonblockable = */ eq(false),
+ /* isDismissable = */ eq(true),
/* wasShownHighPriority = */ eq(true),
eq(assistantFeedbackController),
eq(metricsLogger),
@@ -545,6 +551,7 @@ class NotificationGutsManagerTest(flags: FlagsParameterization) : SysuiTestCase(
NotificationEntryHelper.modifyRanking(row.entry)
.setUserSentiment(NotificationListenerService.Ranking.USER_SENTIMENT_NEGATIVE)
.build()
+ whenever(row.canViewBeDismissed()).thenReturn(true)
val statusBarNotification = row.entry.sbn
val entry = row.entry
@@ -560,6 +567,7 @@ class NotificationGutsManagerTest(flags: FlagsParameterization) : SysuiTestCase(
any<NotificationIconStyleProvider>(),
eq(onUserInteractionCallback),
eq(channelEditorDialogController),
+ any<PackageDemotionInteractor>(),
eq(statusBarNotification.packageName),
any<NotificationChannel>(),
eq(entry),
@@ -569,6 +577,7 @@ class NotificationGutsManagerTest(flags: FlagsParameterization) : SysuiTestCase(
any<UiEventLogger>(),
/* isDeviceProvisioned = */ eq(true),
/* isNonblockable = */ eq(false),
+ /* isDismissable = */ eq(true),
/* wasShownHighPriority = */ eq(false),
eq(assistantFeedbackController),
eq(metricsLogger),
@@ -584,6 +593,7 @@ class NotificationGutsManagerTest(flags: FlagsParameterization) : SysuiTestCase(
NotificationEntryHelper.modifyRanking(row.entry)
.setUserSentiment(NotificationListenerService.Ranking.USER_SENTIMENT_NEGATIVE)
.build()
+ whenever(row.canViewBeDismissed()).thenReturn(true)
val statusBarNotification = row.entry.sbn
val entry = row.entry
@@ -597,6 +607,7 @@ class NotificationGutsManagerTest(flags: FlagsParameterization) : SysuiTestCase(
any<NotificationIconStyleProvider>(),
eq(onUserInteractionCallback),
eq(channelEditorDialogController),
+ any<PackageDemotionInteractor>(),
eq(statusBarNotification.packageName),
any<NotificationChannel>(),
eq(entry),
@@ -606,6 +617,7 @@ class NotificationGutsManagerTest(flags: FlagsParameterization) : SysuiTestCase(
any<UiEventLogger>(),
/* isDeviceProvisioned = */ eq(false),
/* isNonblockable = */ eq(false),
+ /* isDismissable = */ eq(true),
/* wasShownHighPriority = */ eq(false),
eq(assistantFeedbackController),
eq(metricsLogger),
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/NotificationInfoTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/NotificationInfoTest.kt
index 96ae07035ed2..b5f3269903b8 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/NotificationInfoTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/NotificationInfoTest.kt
@@ -49,6 +49,7 @@ import android.view.View.GONE
import android.view.View.VISIBLE
import android.widget.ImageView
import android.widget.TextView
+import androidx.core.view.isVisible
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.internal.logging.MetricsLogger
@@ -64,6 +65,7 @@ import com.android.systemui.statusbar.RankingBuilder
import com.android.systemui.statusbar.notification.AssistantFeedbackController
import com.android.systemui.statusbar.notification.collection.NotificationEntry
import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder
+import com.android.systemui.statusbar.notification.promoted.domain.interactor.PackageDemotionInteractor
import com.android.systemui.statusbar.notification.row.icon.AppIconProvider
import com.android.systemui.statusbar.notification.row.icon.NotificationIconStyleProvider
import com.android.systemui.statusbar.notification.row.icon.appIconProvider
@@ -105,6 +107,7 @@ class NotificationInfoTest : SysuiTestCase() {
private val onUserInteractionCallback = mock<OnUserInteractionCallback>()
private val mockINotificationManager = mock<INotificationManager>()
private val channelEditorDialogController = mock<ChannelEditorDialogController>()
+ private val packageDemotionInteractor = mock<PackageDemotionInteractor>()
private val assistantFeedbackController = mock<AssistantFeedbackController>()
@Before
@@ -863,6 +866,31 @@ class NotificationInfoTest : SysuiTestCase() {
assertThat(underTest.findViewById<View>(R.id.feedback).visibility).isEqualTo(GONE)
}
+ @Test
+ @Throws(RemoteException::class)
+ fun testDismissListenerBound() {
+ val latch = CountDownLatch(1)
+ bindNotification(onCloseClick = { _: View? -> latch.countDown() })
+
+ val dismissView = underTest.findViewById<View>(R.id.inline_dismiss)
+ assertThat(dismissView.isVisible).isTrue()
+ dismissView.performClick()
+
+ // Verify that listener was triggered.
+ assertThat(latch.count).isEqualTo(0)
+ }
+
+ @Test
+ @Throws(RemoteException::class)
+ fun testDismissHiddenWhenUndismissable() {
+
+ entry.sbn.notification.flags =
+ entry.sbn.notification.flags or android.app.Notification.FLAG_NO_DISMISS
+ bindNotification(isDismissable = false)
+ val dismissView = underTest.findViewById<View>(R.id.inline_dismiss)
+ assertThat(dismissView.isVisible).isFalse()
+ }
+
private fun bindNotification(
pm: PackageManager = this.mockPackageManager,
iNotificationManager: INotificationManager = this.mockINotificationManager,
@@ -871,6 +899,7 @@ class NotificationInfoTest : SysuiTestCase() {
onUserInteractionCallback: OnUserInteractionCallback = this.onUserInteractionCallback,
channelEditorDialogController: ChannelEditorDialogController =
this.channelEditorDialogController,
+ packageDemotionInteractor: PackageDemotionInteractor = this.packageDemotionInteractor,
pkg: String = TEST_PACKAGE_NAME,
notificationChannel: NotificationChannel = this.notificationChannel,
entry: NotificationEntry = this.entry,
@@ -880,6 +909,7 @@ class NotificationInfoTest : SysuiTestCase() {
uiEventLogger: UiEventLogger = this.uiEventLogger,
isDeviceProvisioned: Boolean = true,
isNonblockable: Boolean = false,
+ isDismissable: Boolean = true,
wasShownHighPriority: Boolean = true,
assistantFeedbackController: AssistantFeedbackController = this.assistantFeedbackController,
metricsLogger: MetricsLogger = kosmos.metricsLogger,
@@ -892,6 +922,7 @@ class NotificationInfoTest : SysuiTestCase() {
iconStyleProvider,
onUserInteractionCallback,
channelEditorDialogController,
+ packageDemotionInteractor,
pkg,
notificationChannel,
entry,
@@ -901,6 +932,7 @@ class NotificationInfoTest : SysuiTestCase() {
uiEventLogger,
isDeviceProvisioned,
isNonblockable,
+ isDismissable,
wasShownHighPriority,
assistantFeedbackController,
metricsLogger,
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/NotificationSnoozeTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/NotificationSnoozeTest.java
index af67a04d2f2a..2d4063b2f667 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/NotificationSnoozeTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/NotificationSnoozeTest.java
@@ -27,7 +27,6 @@ import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
-import android.platform.test.annotations.EnableFlags;
import android.provider.Settings;
import android.testing.TestableResources;
import android.view.View;
@@ -39,7 +38,6 @@ import androidx.test.annotation.UiThreadTest;
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.animation.AnimatorTestRule;
import com.android.systemui.plugins.statusbar.NotificationSwipeActionHelper;
@@ -93,7 +91,6 @@ public class NotificationSnoozeTest extends SysuiTestCase {
}
@Test
- @EnableFlags(Flags.FLAG_NOTIFICATION_UNDO_GUTS_ON_CONFIG_CHANGED)
public void closeControls_withoutSave_performsUndo() {
ArrayList<SnoozeOption> options = mUnderTest.getDefaultSnoozeOptions();
mUnderTest.mSelectedOption = options.getFirst();
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/PromotedNotificationInfoTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/PromotedNotificationInfoTest.java
index 5638e0b434aa..209dfb2d2ed6 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/PromotedNotificationInfoTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/PromotedNotificationInfoTest.java
@@ -48,6 +48,7 @@ import com.android.systemui.res.R;
import com.android.systemui.statusbar.notification.AssistantFeedbackController;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder;
+import com.android.systemui.statusbar.notification.promoted.domain.interactor.PackageDemotionInteractor;
import com.android.systemui.statusbar.notification.row.icon.AppIconProvider;
import com.android.systemui.statusbar.notification.row.icon.NotificationIconStyleProvider;
@@ -92,6 +93,8 @@ public class PromotedNotificationInfoTest extends SysuiTestCase {
@Mock
private ChannelEditorDialogController mChannelEditorDialogController;
@Mock
+ private PackageDemotionInteractor mPackageDemotionInteractor;
+ @Mock
private AssistantFeedbackController mAssistantFeedbackController;
@Mock
private TelecomManager mTelecomManager;
@@ -138,6 +141,7 @@ public class PromotedNotificationInfoTest extends SysuiTestCase {
mMockIconStyleProvider,
mOnUserInteractionCallback,
mChannelEditorDialogController,
+ mPackageDemotionInteractor,
TEST_PACKAGE_NAME,
mNotificationChannel,
mEntry,
@@ -148,6 +152,7 @@ public class PromotedNotificationInfoTest extends SysuiTestCase {
true,
false,
true,
+ true,
mAssistantFeedbackController,
mMetricsLogger,
null);
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 91b3896332f5..39cf02dbc772 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
@@ -54,9 +54,7 @@ class FakeHomeStatusBarViewModel(
MutableStateFlow(OngoingActivityChipModel.Inactive())
override val ongoingActivityChips =
- MutableStateFlow(
- ChipsVisibilityModel(MultipleOngoingActivityChipsModel(), areChipsAllowed = false)
- )
+ ChipsVisibilityModel(MultipleOngoingActivityChipsModel(), areChipsAllowed = false)
override val ongoingActivityChipsLegacy =
MutableStateFlow(MultipleOngoingActivityChipsModelLegacy())
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 2da692b4cb45..20cf3ae6b8dd 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
@@ -49,6 +49,7 @@ import com.android.systemui.kosmos.runCurrent
import com.android.systemui.kosmos.runTest
import com.android.systemui.kosmos.testScope
import com.android.systemui.kosmos.useUnconfinedTestDispatcher
+import com.android.systemui.lifecycle.activateIn
import com.android.systemui.log.assertLogsWtf
import com.android.systemui.mediaprojection.data.model.MediaProjectionState
import com.android.systemui.mediaprojection.data.repository.fakeMediaProjectionRepository
@@ -107,7 +108,8 @@ import org.junit.runner.RunWith
@RunWith(AndroidJUnit4::class)
class HomeStatusBarViewModelImplTest : SysuiTestCase() {
private val kosmos = testKosmos().useUnconfinedTestDispatcher()
- private val Kosmos.underTest by Kosmos.Fixture { kosmos.homeStatusBarViewModel }
+ private val Kosmos.underTest by
+ Kosmos.Fixture { kosmos.homeStatusBarViewModel.also { it.activateIn(kosmos.testScope) } }
@Before
fun setUp() {
@@ -891,32 +893,26 @@ class HomeStatusBarViewModelImplTest : SysuiTestCase() {
@EnableChipsModernization
fun ongoingActivityChips_statusBarHidden_noSecureCamera_noHun_notAllowed() =
kosmos.runTest {
- val latest by collectLastValue(underTest.ongoingActivityChips)
-
// home status bar not allowed
kosmos.sceneContainerRepository.snapToScene(Scenes.Lockscreen)
kosmos.keyguardOcclusionRepository.setShowWhenLockedActivityInfo(false, taskInfo = null)
- assertThat(latest!!.areChipsAllowed).isFalse()
+ assertThat(underTest.ongoingActivityChips.areChipsAllowed).isFalse()
}
@Test
@EnableChipsModernization
fun ongoingActivityChips_statusBarNotHidden_noSecureCamera_noHun_isAllowed() =
kosmos.runTest {
- val latest by collectLastValue(underTest.ongoingActivityChips)
-
transitionKeyguardToGone()
- assertThat(latest!!.areChipsAllowed).isTrue()
+ assertThat(underTest.ongoingActivityChips.areChipsAllowed).isTrue()
}
@Test
@EnableChipsModernization
fun ongoingActivityChips_statusBarNotHidden_secureCamera_noHun_notAllowed() =
kosmos.runTest {
- val latest by collectLastValue(underTest.ongoingActivityChips)
-
fakeKeyguardTransitionRepository.sendTransitionSteps(
from = KeyguardState.LOCKSCREEN,
to = KeyguardState.OCCLUDED,
@@ -924,7 +920,7 @@ class HomeStatusBarViewModelImplTest : SysuiTestCase() {
)
kosmos.keyguardInteractor.onCameraLaunchDetected(CAMERA_LAUNCH_SOURCE_POWER_DOUBLE_TAP)
- assertThat(latest!!.areChipsAllowed).isFalse()
+ assertThat(underTest.ongoingActivityChips.areChipsAllowed).isFalse()
}
@Test
@@ -932,8 +928,6 @@ class HomeStatusBarViewModelImplTest : SysuiTestCase() {
@EnableChipsModernization
fun ongoingActivityChips_statusBarNotHidden_noSecureCamera_hunBySystem_noHunFlagOff_notAllowed() =
kosmos.runTest {
- val latest by collectLastValue(underTest.ongoingActivityChips)
-
transitionKeyguardToGone()
headsUpNotificationRepository.setNotifications(
@@ -943,7 +937,7 @@ class HomeStatusBarViewModelImplTest : SysuiTestCase() {
)
)
- assertThat(latest!!.areChipsAllowed).isFalse()
+ assertThat(underTest.ongoingActivityChips.areChipsAllowed).isFalse()
}
@Test
@@ -951,8 +945,6 @@ class HomeStatusBarViewModelImplTest : SysuiTestCase() {
@EnableChipsModernization
fun ongoingActivityChips_statusBarNotHidden_noSecureCamera_hunByUser_noHunFlagOff_isAllowed() =
kosmos.runTest {
- val latest by collectLastValue(underTest.ongoingActivityChips)
-
transitionKeyguardToGone()
headsUpNotificationRepository.setNotifications(
@@ -962,16 +954,14 @@ class HomeStatusBarViewModelImplTest : SysuiTestCase() {
)
)
- assertThat(latest!!.areChipsAllowed).isTrue()
+ assertThat(underTest.ongoingActivityChips.areChipsAllowed).isTrue()
}
@Test
@EnableFlags(StatusBarNoHunBehavior.FLAG_NAME)
@EnableChipsModernization
- fun ongoingActivityChips_tatusBarNotHidden_noSecureCamera_hunBySystem_noHunFlagOn_isAllowed() =
+ fun ongoingActivityChips_statusBarNotHidden_noSecureCamera_hunBySystem_noHunFlagOn_isAllowed() =
kosmos.runTest {
- val latest by collectLastValue(underTest.ongoingActivityChips)
-
transitionKeyguardToGone()
headsUpNotificationRepository.setNotifications(
@@ -981,7 +971,7 @@ class HomeStatusBarViewModelImplTest : SysuiTestCase() {
)
)
- assertThat(latest!!.areChipsAllowed).isTrue()
+ assertThat(underTest.ongoingActivityChips.areChipsAllowed).isTrue()
}
@Test
@@ -989,8 +979,6 @@ class HomeStatusBarViewModelImplTest : SysuiTestCase() {
@EnableChipsModernization
fun ongoingActivityChips_statusBarNotHidden_noSecureCamera_hunByUser_noHunFlagOn_isAllowed() =
kosmos.runTest {
- val latest by collectLastValue(underTest.ongoingActivityChips)
-
transitionKeyguardToGone()
headsUpNotificationRepository.setNotifications(
@@ -1000,7 +988,7 @@ class HomeStatusBarViewModelImplTest : SysuiTestCase() {
)
)
- assertThat(latest!!.areChipsAllowed).isTrue()
+ assertThat(underTest.ongoingActivityChips.areChipsAllowed).isTrue()
}
@Test
@@ -1008,17 +996,16 @@ class HomeStatusBarViewModelImplTest : SysuiTestCase() {
@EnableChipsModernization
fun ongoingActivityChips_followsChipsViewModel() =
kosmos.runTest {
- val latest by collectLastValue(underTest.ongoingActivityChips)
transitionKeyguardToGone()
screenRecordRepository.screenRecordState.value = ScreenRecordModel.Recording
- assertIsScreenRecordChip(latest!!.chips.active[0])
+ assertIsScreenRecordChip(underTest.ongoingActivityChips.chips.active[0])
addOngoingCallState(key = "call")
- assertIsScreenRecordChip(latest!!.chips.active[0])
- assertIsCallChip(latest!!.chips.active[1], "call", context)
+ assertIsScreenRecordChip(underTest.ongoingActivityChips.chips.active[0])
+ assertIsCallChip(underTest.ongoingActivityChips.chips.active[1], "call", context)
}
@Test
diff --git a/packages/SystemUI/res/layout/notification_2025_info.xml b/packages/SystemUI/res/layout/notification_2025_info.xml
index 7b6916652924..fa852a2b8e85 100644
--- a/packages/SystemUI/res/layout/notification_2025_info.xml
+++ b/packages/SystemUI/res/layout/notification_2025_info.xml
@@ -18,6 +18,7 @@
<!-- extends LinearLayout -->
<com.android.systemui.statusbar.notification.row.NotificationInfo
xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
android:id="@+id/notification_guts"
android:layout_width="match_parent"
@@ -324,18 +325,34 @@
</com.android.systemui.statusbar.notification.row.ButtonLinearLayout>
</LinearLayout>
-
- <LinearLayout
+ <androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/bottom_buttons"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="@*android:dimen/notification_2025_margin"
android:minHeight="@dimen/notification_2025_guts_button_size"
- android:gravity="center_vertical"
- >
+ android:gravity="center_vertical">
+
+ <TextView
+ android:id="@+id/inline_dismiss"
+ android:text="@string/notification_inline_dismiss"
+ android:paddingEnd="@dimen/notification_importance_button_padding"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:paddingTop="8dp"
+ android:paddingBottom="@*android:dimen/notification_2025_margin"
+ app:layout_constraintStart_toStartOf="parent"
+ android:gravity="center"
+ android:minWidth="@dimen/notification_2025_min_tap_target_size"
+ android:minHeight="@dimen/notification_2025_min_tap_target_size"
+ android:maxWidth="200dp"
+ style="@style/TextAppearance.NotificationInfo.Button"
+ android:textSize="@*android:dimen/notification_2025_action_text_size"
+ />
<TextView
android:id="@+id/turn_off_notifications"
android:text="@string/inline_turn_off_notifications"
+ android:paddingStart="@dimen/notification_importance_button_padding"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="32dp"
@@ -345,6 +362,8 @@
android:minWidth="@dimen/notification_2025_min_tap_target_size"
android:minHeight="@dimen/notification_2025_min_tap_target_size"
android:maxWidth="200dp"
+ app:layout_constraintStart_toEndOf="@id/inline_dismiss"
+ app:layout_constraintBaseline_toBaselineOf="@id/inline_dismiss"
style="@style/TextAppearance.NotificationInfo.Button"
android:textSize="@*android:dimen/notification_2025_action_text_size"/>
<TextView
@@ -354,12 +373,18 @@
android:layout_height="wrap_content"
android:paddingTop="8dp"
android:paddingBottom="@*android:dimen/notification_2025_margin"
- android:gravity="center"
+ android:gravity="end"
+ app:layout_constraintEnd_toEndOf="parent"
android:minWidth="@dimen/notification_2025_min_tap_target_size"
android:minHeight="@dimen/notification_2025_min_tap_target_size"
android:maxWidth="125dp"
style="@style/TextAppearance.NotificationInfo.Button"
android:textSize="@*android:dimen/notification_2025_action_text_size"/>
- </LinearLayout>
+ <androidx.constraintlayout.helper.widget.Flow
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ app:constraint_referenced_ids="inline_dismiss,turn_off_notifications,done"
+ app:flow_wrapMode="chain"/>
+ </androidx.constraintlayout.widget.ConstraintLayout>
</LinearLayout>
</com.android.systemui.statusbar.notification.row.NotificationInfo>
diff --git a/packages/SystemUI/res/layout/notification_info.xml b/packages/SystemUI/res/layout/notification_info.xml
index 089ceaee6ce3..d4bd142d9089 100644
--- a/packages/SystemUI/res/layout/notification_info.xml
+++ b/packages/SystemUI/res/layout/notification_info.xml
@@ -341,15 +341,28 @@ asked for it -->
android:paddingEnd="4dp"
>
<TextView
+ android:id="@+id/inline_dismiss"
+ android:text="@string/notification_inline_dismiss"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_alignParentStart="true"
+ android:gravity="start|center_vertical"
+ android:minWidth="@dimen/notification_importance_toggle_size"
+ android:minHeight="@dimen/notification_importance_toggle_size"
+ android:maxWidth="200dp"
+ android:paddingEnd="@dimen/notification_importance_button_padding"
+ style="@style/TextAppearance.NotificationInfo.Button"/>
+ <TextView
android:id="@+id/turn_off_notifications"
android:text="@string/inline_turn_off_notifications"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:layout_alignParentStart="true"
+ android:layout_toEndOf="@id/inline_dismiss"
android:gravity="start|center_vertical"
android:minWidth="@dimen/notification_importance_toggle_size"
android:minHeight="@dimen/notification_importance_toggle_size"
android:maxWidth="200dp"
+ android:paddingStart="@dimen/notification_importance_button_padding"
style="@style/TextAppearance.NotificationInfo.Button"/>
<TextView
android:id="@+id/done"
diff --git a/packages/SystemUI/res/layout/partial_conversation_info.xml b/packages/SystemUI/res/layout/partial_conversation_info.xml
index 4850b35833e5..d1755eff6dab 100644
--- a/packages/SystemUI/res/layout/partial_conversation_info.xml
+++ b/packages/SystemUI/res/layout/partial_conversation_info.xml
@@ -143,15 +143,28 @@
android:paddingEnd="4dp"
>
<TextView
+ android:id="@+id/inline_dismiss"
+ android:text="@string/notification_inline_dismiss"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_alignParentStart="true"
+ android:gravity="start|center_vertical"
+ android:minWidth="@dimen/notification_importance_toggle_size"
+ android:minHeight="@dimen/notification_importance_toggle_size"
+ android:maxWidth="200dp"
+ android:paddingEnd="@dimen/notification_importance_button_padding"
+ style="@style/TextAppearance.NotificationInfo.Button"/>
+ <TextView
android:id="@+id/turn_off_notifications"
android:text="@string/inline_turn_off_notifications"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:layout_alignParentStart="true"
+ android:layout_toEndOf="@id/inline_dismiss"
android:gravity="start|center_vertical"
android:minWidth="@dimen/notification_importance_toggle_size"
android:minHeight="@dimen/notification_importance_toggle_size"
android:maxWidth="200dp"
+ android:paddingStart="@dimen/notification_importance_button_padding"
style="@style/TextAppearance.NotificationInfo.Button"/>
<TextView
android:id="@+id/done"
diff --git a/packages/SystemUI/res/layout/promoted_notification_info.xml b/packages/SystemUI/res/layout/promoted_notification_info.xml
index 2e0a0ca1185c..3982a6638666 100644
--- a/packages/SystemUI/res/layout/promoted_notification_info.xml
+++ b/packages/SystemUI/res/layout/promoted_notification_info.xml
@@ -373,15 +373,28 @@ asked for it -->
android:paddingEnd="4dp"
>
<TextView
+ android:id="@+id/inline_dismiss"
+ android:text="@string/notification_inline_dismiss"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_alignParentStart="true"
+ android:gravity="start|center_vertical"
+ android:minWidth="@dimen/notification_importance_toggle_size"
+ android:minHeight="@dimen/notification_importance_toggle_size"
+ android:maxWidth="200dp"
+ android:paddingEnd="@dimen/notification_importance_button_padding"
+ style="@style/TextAppearance.NotificationInfo.Button"/>
+ <TextView
android:id="@+id/turn_off_notifications"
android:text="@string/inline_turn_off_notifications"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:layout_alignParentStart="true"
+ android:layout_toEndOf="@id/inline_dismiss"
android:gravity="start|center_vertical"
android:minWidth="@dimen/notification_importance_toggle_size"
android:minHeight="@dimen/notification_importance_toggle_size"
android:maxWidth="200dp"
+ android:paddingStart="@dimen/notification_importance_button_padding"
style="@style/TextAppearance.NotificationInfo.Button"/>
<TextView
android:id="@+id/done"
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index cd94a265aa80..fefc222f283f 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -2056,7 +2056,7 @@
<string name="inline_ok_button">Apply</string>
<!-- Notification inline controls: button to show block screen [CHAR_LIMIT=35] -->
- <string name="inline_turn_off_notifications">Turn off notifications</string>
+ <string name="inline_turn_off_notifications">Turn off</string>
<!-- [CHAR LIMIT=100] Notification Importance title -->
<string name="notification_silence_title">Silent</string>
diff --git a/packages/SystemUI/src/com/android/keyguard/ClockEventController.kt b/packages/SystemUI/src/com/android/keyguard/ClockEventController.kt
index 763b1072f968..f2f177356fab 100644
--- a/packages/SystemUI/src/com/android/keyguard/ClockEventController.kt
+++ b/packages/SystemUI/src/com/android/keyguard/ClockEventController.kt
@@ -572,10 +572,6 @@ constructor(
}
fun handleFidgetTap(x: Float, y: Float) {
- if (!com.android.systemui.Flags.clockFidgetAnimation()) {
- return
- }
-
clock?.run {
smallClock.animations.onFidgetTap(x, y)
largeClock.animations.onFidgetTap(x, y)
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
index d84b034eade8..60ec051315d0 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
@@ -124,7 +124,6 @@ import com.android.settingslib.Utils;
import com.android.settingslib.WirelessUtils;
import com.android.settingslib.fuelgauge.BatteryStatus;
import com.android.systemui.CoreStartable;
-import com.android.systemui.Dumpable;
import com.android.systemui.Flags;
import com.android.systemui.biometrics.AuthController;
import com.android.systemui.biometrics.FingerprintInteractiveToAuthProvider;
@@ -301,7 +300,8 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, CoreSt
private final Provider<SceneInteractor> mSceneInteractor;
private final Provider<AlternateBouncerInteractor> mAlternateBouncerInteractor;
private final Provider<CommunalSceneInteractor> mCommunalSceneInteractor;
- private final KeyguardServiceShowLockscreenInteractor mKeyguardServiceShowLockscreenInteractor;
+ private final Provider<KeyguardServiceShowLockscreenInteractor>
+ mKeyguardServiceShowLockscreenInteractor;
private final AuthController mAuthController;
private final UiEventLogger mUiEventLogger;
private final Set<String> mAllowFingerprintOnOccludingActivitiesFromPackage;
@@ -2219,7 +2219,8 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, CoreSt
Provider<JavaAdapter> javaAdapter,
Provider<SceneInteractor> sceneInteractor,
Provider<CommunalSceneInteractor> communalSceneInteractor,
- KeyguardServiceShowLockscreenInteractor keyguardServiceShowLockscreenInteractor) {
+ Provider<KeyguardServiceShowLockscreenInteractor>
+ keyguardServiceShowLockscreenInteractor) {
mContext = context;
mSubscriptionManager = subscriptionManager;
mUserTracker = userTracker;
@@ -2553,7 +2554,7 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, CoreSt
if (KeyguardWmStateRefactor.isEnabled()) {
mJavaAdapter.get().alwaysCollectFlow(
- mKeyguardServiceShowLockscreenInteractor.getShowNowEvents(),
+ mKeyguardServiceShowLockscreenInteractor.get().getShowNowEvents(),
this::onKeyguardServiceShowLockscreenNowEvents
);
}
diff --git a/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ActionIntentCreator.kt b/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ActionIntentCreator.kt
index df6c1b18e3e9..8cebe04d4e01 100644
--- a/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ActionIntentCreator.kt
+++ b/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ActionIntentCreator.kt
@@ -24,11 +24,17 @@ import android.content.Intent
import android.net.Uri
import android.text.TextUtils
import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.res.R
+import java.util.function.Consumer
import javax.inject.Inject
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.launch
@SysUISingleton
-class ActionIntentCreator @Inject constructor() : IntentCreator {
+class ActionIntentCreator
+@Inject
+constructor(@Application private val applicationScope: CoroutineScope) : IntentCreator {
override fun getTextEditorIntent(context: Context?) =
Intent(context, EditTextActivity::class.java).apply {
addFlags(Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK)
@@ -65,7 +71,7 @@ class ActionIntentCreator @Inject constructor() : IntentCreator {
.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
}
- override fun getImageEditIntent(uri: Uri?, context: Context): Intent {
+ suspend fun getImageEditIntent(uri: Uri?, context: Context): Intent {
val editorPackage = context.getString(R.string.config_screenshotEditor)
return Intent(Intent.ACTION_EDIT).apply {
if (!TextUtils.isEmpty(editorPackage)) {
@@ -78,6 +84,14 @@ class ActionIntentCreator @Inject constructor() : IntentCreator {
}
}
+ override fun getImageEditIntentAsync(
+ uri: Uri?,
+ context: Context,
+ outputConsumer: Consumer<Intent>,
+ ) {
+ applicationScope.launch { outputConsumer.accept(getImageEditIntent(uri, context)) }
+ }
+
override fun getRemoteCopyIntent(clipData: ClipData?, context: Context): Intent {
val remoteCopyPackage = context.getString(R.string.config_remoteCopyPackage)
return Intent(REMOTE_COPY_ACTION).apply {
diff --git a/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardOverlayController.java b/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardOverlayController.java
index 314b6e7f5a28..984d2478eb72 100644
--- a/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardOverlayController.java
+++ b/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardOverlayController.java
@@ -558,8 +558,10 @@ public class ClipboardOverlayController implements ClipboardListener.ClipboardOv
private void editImage(Uri uri) {
mClipboardLogger.logSessionComplete(CLIPBOARD_OVERLAY_EDIT_TAPPED);
- mContext.startActivity(mIntentCreator.getImageEditIntent(uri, mContext));
- animateOut();
+ mIntentCreator.getImageEditIntentAsync(uri, mContext, intent -> {
+ mContext.startActivity(intent);
+ animateOut();
+ });
}
private void editText() {
@@ -747,8 +749,10 @@ public class ClipboardOverlayController implements ClipboardListener.ClipboardOv
mIntentCreator.getTextEditorIntent(mContext));
break;
case IMAGE:
- finishWithSharedTransition(CLIPBOARD_OVERLAY_EDIT_TAPPED,
- mIntentCreator.getImageEditIntent(mClipboardModel.getUri(), mContext));
+ mIntentCreator.getImageEditIntentAsync(mClipboardModel.getUri(), mContext,
+ intent -> {
+ finishWithSharedTransition(CLIPBOARD_OVERLAY_EDIT_TAPPED, intent);
+ });
break;
default:
Log.w(TAG, "Got preview tapped callback for non-editable type "
diff --git a/packages/SystemUI/src/com/android/systemui/clipboardoverlay/DefaultIntentCreator.java b/packages/SystemUI/src/com/android/systemui/clipboardoverlay/DefaultIntentCreator.java
index 4b24536ad28f..e9a9cbf106fc 100644
--- a/packages/SystemUI/src/com/android/systemui/clipboardoverlay/DefaultIntentCreator.java
+++ b/packages/SystemUI/src/com/android/systemui/clipboardoverlay/DefaultIntentCreator.java
@@ -27,6 +27,8 @@ import android.text.TextUtils;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.res.R;
+import java.util.function.Consumer;
+
import javax.inject.Inject;
@SysUISingleton
@@ -73,7 +75,7 @@ public class DefaultIntentCreator implements IntentCreator {
return chooserIntent;
}
- public Intent getImageEditIntent(Uri uri, Context context) {
+ public void getImageEditIntentAsync(Uri uri, Context context, Consumer<Intent> outputConsumer) {
String editorPackage = context.getString(R.string.config_screenshotEditor);
Intent editIntent = new Intent(Intent.ACTION_EDIT);
if (!TextUtils.isEmpty(editorPackage)) {
@@ -83,7 +85,7 @@ public class DefaultIntentCreator implements IntentCreator {
editIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
editIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
editIntent.putExtra(EXTRA_EDIT_SOURCE, EDIT_SOURCE_CLIPBOARD);
- return editIntent;
+ outputConsumer.accept(editIntent);
}
public Intent getRemoteCopyIntent(ClipData clipData, Context context) {
diff --git a/packages/SystemUI/src/com/android/systemui/clipboardoverlay/IntentCreator.java b/packages/SystemUI/src/com/android/systemui/clipboardoverlay/IntentCreator.java
index c8a6b05f090b..283596f9f309 100644
--- a/packages/SystemUI/src/com/android/systemui/clipboardoverlay/IntentCreator.java
+++ b/packages/SystemUI/src/com/android/systemui/clipboardoverlay/IntentCreator.java
@@ -21,9 +21,11 @@ import android.content.Context;
import android.content.Intent;
import android.net.Uri;
+import java.util.function.Consumer;
+
public interface IntentCreator {
Intent getTextEditorIntent(Context context);
Intent getShareIntent(ClipData clipData, Context context);
- Intent getImageEditIntent(Uri uri, Context context);
+ void getImageEditIntentAsync(Uri uri, Context context, Consumer<Intent> outputConsumer);
Intent getRemoteCopyIntent(ClipData clipData, Context context);
}
diff --git a/packages/SystemUI/src/com/android/systemui/display/DisplayModule.kt b/packages/SystemUI/src/com/android/systemui/display/DisplayModule.kt
index 9b181be93b61..f3316958f01d 100644
--- a/packages/SystemUI/src/com/android/systemui/display/DisplayModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/display/DisplayModule.kt
@@ -16,8 +16,13 @@
package com.android.systemui.display
+import android.hardware.display.DisplayManager
+import android.os.Handler
+import com.android.app.displaylib.DisplayLibComponent
+import com.android.app.displaylib.createDisplayLibComponent
import com.android.systemui.CoreStartable
import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.display.data.repository.DeviceStateRepository
import com.android.systemui.display.data.repository.DeviceStateRepositoryImpl
import com.android.systemui.display.data.repository.DisplayRepository
@@ -28,6 +33,8 @@ import com.android.systemui.display.data.repository.DisplayWindowPropertiesRepos
import com.android.systemui.display.data.repository.DisplayWindowPropertiesRepositoryImpl
import com.android.systemui.display.data.repository.FocusedDisplayRepository
import com.android.systemui.display.data.repository.FocusedDisplayRepositoryImpl
+import com.android.systemui.display.data.repository.PerDisplayRepoDumpHelper
+import com.android.systemui.display.data.repository.PerDisplayRepository
import com.android.systemui.display.domain.interactor.ConnectedDisplayInteractor
import com.android.systemui.display.domain.interactor.ConnectedDisplayInteractorImpl
import com.android.systemui.display.domain.interactor.DisplayWindowPropertiesInteractorModule
@@ -40,9 +47,11 @@ import dagger.Module
import dagger.Provides
import dagger.multibindings.ClassKey
import dagger.multibindings.IntoMap
+import kotlinx.coroutines.CoroutineDispatcher
+import kotlinx.coroutines.CoroutineScope
/** Module binding display related classes. */
-@Module(includes = [DisplayWindowPropertiesInteractorModule::class])
+@Module(includes = [DisplayWindowPropertiesInteractorModule::class, DisplayLibModule::class])
interface DisplayModule {
@Binds
fun bindConnectedDisplayInteractor(
@@ -73,6 +82,9 @@ interface DisplayModule {
impl: DisplayWindowPropertiesRepositoryImpl
): DisplayWindowPropertiesRepository
+ @Binds
+ fun dumpRegistrationLambda(helper: PerDisplayRepoDumpHelper): PerDisplayRepository.InitCallback
+
companion object {
@Provides
@SysUISingleton
@@ -103,3 +115,31 @@ interface DisplayModule {
}
}
}
+
+/** Module to bind the DisplayRepository from displaylib to the systemui dagger graph. */
+@Module
+object DisplayLibModule {
+ @Provides
+ @SysUISingleton
+ fun displayLibComponent(
+ displayManager: DisplayManager,
+ @Background backgroundHandler: Handler,
+ @Background bgApplicationScope: CoroutineScope,
+ @Background backgroundCoroutineDispatcher: CoroutineDispatcher,
+ ): DisplayLibComponent {
+ return createDisplayLibComponent(
+ displayManager,
+ backgroundHandler,
+ bgApplicationScope,
+ backgroundCoroutineDispatcher,
+ )
+ }
+
+ @Provides
+ @SysUISingleton
+ fun providesDisplayRepositoryFromLib(
+ displayLibComponent: DisplayLibComponent
+ ): com.android.app.displaylib.DisplayRepository {
+ return displayLibComponent.displayRepository
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/display/data/repository/DisplayRepository.kt b/packages/SystemUI/src/com/android/systemui/display/data/repository/DisplayRepository.kt
index 101e8cc23f17..051fe7e5517c 100644
--- a/packages/SystemUI/src/com/android/systemui/display/data/repository/DisplayRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/display/data/repository/DisplayRepository.kt
@@ -17,106 +17,30 @@
package com.android.systemui.display.data.repository
import android.annotation.SuppressLint
-import android.hardware.display.DisplayManager
-import android.hardware.display.DisplayManager.DISPLAY_CATEGORY_ALL_INCLUDING_DISABLED
-import android.hardware.display.DisplayManager.DisplayListener
-import android.hardware.display.DisplayManager.EVENT_TYPE_DISPLAY_ADDED
-import android.hardware.display.DisplayManager.EVENT_TYPE_DISPLAY_CHANGED
-import android.hardware.display.DisplayManager.EVENT_TYPE_DISPLAY_REMOVED
-import android.os.Handler
-import android.util.Log
-import android.view.Display
import android.view.IWindowManager
import com.android.app.displaylib.DisplayRepository as DisplayRepositoryFromLib
-import com.android.app.tracing.FlowTracing.traceEach
-import com.android.app.tracing.traceSection
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Background
-import com.android.systemui.display.data.DisplayEvent
import com.android.systemui.statusbar.CommandQueue
-import com.android.systemui.util.Compile
-import com.android.systemui.util.kotlin.pairwiseBy
-import com.android.systemui.utils.coroutines.flow.conflatedCallbackFlow
import javax.inject.Inject
-import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.channels.awaitClose
import kotlinx.coroutines.flow.Flow
-import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.StateFlow
-import kotlinx.coroutines.flow.asFlow
import kotlinx.coroutines.flow.callbackFlow
-import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.filter
-import kotlinx.coroutines.flow.filterIsInstance
-import kotlinx.coroutines.flow.flatMapLatest
-import kotlinx.coroutines.flow.flowOn
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.merge
-import kotlinx.coroutines.flow.onEach
-import kotlinx.coroutines.flow.onStart
import kotlinx.coroutines.flow.scan
import kotlinx.coroutines.flow.stateIn
/** Repository for providing access to display related information and events. */
interface DisplayRepository : DisplayRepositoryFromLib {
- /** Display change event indicating a change to the given displayId has occurred. */
- val displayChangeEvent: Flow<Int>
-
- /** Display addition event indicating a new display has been added. */
- val displayAdditionEvent: Flow<Display?>
-
- /** Display removal event indicating a display has been removed. */
- val displayRemovalEvent: Flow<Int>
/** A [StateFlow] that maintains a set of display IDs that should have system decorations. */
val displayIdsWithSystemDecorations: StateFlow<Set<Int>>
-
- /**
- * Provides the current set of display ids.
- *
- * Note that it is preferred to use this instead of [displays] if only the
- * [Display.getDisplayId] is needed.
- */
- val displayIds: StateFlow<Set<Int>>
-
- /**
- * Pending display id that can be enabled/disabled.
- *
- * When `null`, it means there is no pending display waiting to be enabled.
- */
- val pendingDisplay: Flow<PendingDisplay?>
-
- /** Whether the default display is currently off. */
- val defaultDisplayOff: Flow<Boolean>
-
- /**
- * Given a display ID int, return the corresponding Display object, or null if none exist.
- *
- * This method is guaranteed to not result in any binder call.
- */
- fun getDisplay(displayId: Int): Display? =
- displays.value.firstOrNull { it.displayId == displayId }
-
- /** Represents a connected display that has not been enabled yet. */
- interface PendingDisplay {
- /** Id of the pending display. */
- val id: Int
-
- /** Enables the display, making it available to the system. */
- suspend fun enable()
-
- /**
- * Ignores the pending display. When called, this specific display id doesn't appear as
- * pending anymore until the display is disconnected and reconnected again.
- */
- suspend fun ignore()
-
- /** Disables the display, making it unavailable to the system. */
- suspend fun disable()
- }
}
@SysUISingleton
@@ -124,310 +48,11 @@ interface DisplayRepository : DisplayRepositoryFromLib {
class DisplayRepositoryImpl
@Inject
constructor(
- private val displayManager: DisplayManager,
private val commandQueue: CommandQueue,
private val windowManager: IWindowManager,
- @Background backgroundHandler: Handler,
@Background bgApplicationScope: CoroutineScope,
- @Background backgroundCoroutineDispatcher: CoroutineDispatcher,
-) : DisplayRepository {
- private val allDisplayEvents: Flow<DisplayEvent> =
- conflatedCallbackFlow {
- val callback =
- object : DisplayListener {
- override fun onDisplayAdded(displayId: Int) {
- trySend(DisplayEvent.Added(displayId))
- }
-
- override fun onDisplayRemoved(displayId: Int) {
- trySend(DisplayEvent.Removed(displayId))
- }
-
- override fun onDisplayChanged(displayId: Int) {
- trySend(DisplayEvent.Changed(displayId))
- }
- }
- displayManager.registerDisplayListener(
- callback,
- backgroundHandler,
- EVENT_TYPE_DISPLAY_ADDED or
- EVENT_TYPE_DISPLAY_CHANGED or
- EVENT_TYPE_DISPLAY_REMOVED,
- )
- awaitClose { displayManager.unregisterDisplayListener(callback) }
- }
- .onStart { emit(DisplayEvent.Changed(Display.DEFAULT_DISPLAY)) }
- .debugLog("allDisplayEvents")
- .flowOn(backgroundCoroutineDispatcher)
-
- override val displayChangeEvent: Flow<Int> =
- allDisplayEvents.filterIsInstance<DisplayEvent.Changed>().map { event -> event.displayId }
-
- override val displayRemovalEvent: Flow<Int> =
- allDisplayEvents.filterIsInstance<DisplayEvent.Removed>().map { it.displayId }
-
- // This is necessary because there might be multiple displays, and we could
- // have missed events for those added before this process or flow started.
- // Note it causes a binder call from the main thread (it's traced).
- private val initialDisplays: Set<Display> =
- traceSection("$TAG#initialDisplays") { displayManager.displays?.toSet() ?: emptySet() }
- private val initialDisplayIds = initialDisplays.map { display -> display.displayId }.toSet()
-
- /** Propagate to the listeners only enabled displays */
- private val enabledDisplayIds: StateFlow<Set<Int>> =
- allDisplayEvents
- .scan(initial = initialDisplayIds) { previousIds: Set<Int>, event: DisplayEvent ->
- val id = event.displayId
- when (event) {
- is DisplayEvent.Removed -> previousIds - id
- is DisplayEvent.Added,
- is DisplayEvent.Changed -> previousIds + id
- }
- }
- .distinctUntilChanged()
- .debugLog("enabledDisplayIds")
- .stateIn(bgApplicationScope, SharingStarted.WhileSubscribed(), initialDisplayIds)
-
- private val defaultDisplay by lazy {
- getDisplayFromDisplayManager(Display.DEFAULT_DISPLAY)
- ?: error("Unable to get default display.")
- }
-
- /**
- * Represents displays that went though the [DisplayListener.onDisplayAdded] callback.
- *
- * Those are commonly the ones provided by [DisplayManager.getDisplays] by default.
- */
- private val enabledDisplays: StateFlow<Set<Display>> =
- enabledDisplayIds
- .mapElementsLazily { displayId -> getDisplayFromDisplayManager(displayId) }
- .onEach {
- if (it.isEmpty()) Log.wtf(TAG, "No enabled displays. This should never happen.")
- }
- .flowOn(backgroundCoroutineDispatcher)
- .debugLog("enabledDisplays")
- .stateIn(
- bgApplicationScope,
- started = SharingStarted.WhileSubscribed(),
- // This triggers a single binder call on the UI thread per process. The
- // alternative would be to use sharedFlows, but they are prohibited due to
- // performance concerns.
- // Ultimately, this is a trade-off between a one-time UI thread binder call and
- // the constant overhead of sharedFlows.
- initialValue = initialDisplays,
- )
-
- /**
- * Represents displays that went though the [DisplayListener.onDisplayAdded] callback.
- *
- * Those are commonly the ones provided by [DisplayManager.getDisplays] by default.
- */
- override val displays: StateFlow<Set<Display>> = enabledDisplays
-
- override val displayIds: StateFlow<Set<Int>> = enabledDisplayIds
-
- /**
- * Implementation that maps from [displays], instead of [allDisplayEvents] for 2 reasons:
- * 1. Guarantee that it emits __after__ [displays] emitted. This way it is guaranteed that
- * calling [getDisplay] for the newly added display will be non-null.
- * 2. Reuse the existing instance of [Display] without a new call to [DisplayManager].
- */
- override val displayAdditionEvent: Flow<Display?> =
- displays
- .pairwiseBy { previousDisplays, currentDisplays -> currentDisplays - previousDisplays }
- .flatMapLatest { it.asFlow() }
-
- val _ignoredDisplayIds = MutableStateFlow<Set<Int>>(emptySet())
- private val ignoredDisplayIds: Flow<Set<Int>> = _ignoredDisplayIds.debugLog("ignoredDisplayIds")
-
- private fun getInitialConnectedDisplays(): Set<Int> =
- traceSection("$TAG#getInitialConnectedDisplays") {
- displayManager
- .getDisplays(DISPLAY_CATEGORY_ALL_INCLUDING_DISABLED)
- .map { it.displayId }
- .toSet()
- .also {
- if (DEBUG) {
- Log.d(TAG, "getInitialConnectedDisplays: $it")
- }
- }
- }
-
- /* keeps connected displays until they are disconnected. */
- private val connectedDisplayIds: StateFlow<Set<Int>> =
- conflatedCallbackFlow {
- val connectedIds = getInitialConnectedDisplays().toMutableSet()
- val callback =
- object : DisplayConnectionListener {
- override fun onDisplayConnected(id: Int) {
- if (DEBUG) {
- Log.d(TAG, "display with id=$id connected.")
- }
- connectedIds += id
- _ignoredDisplayIds.value -= id
- trySend(connectedIds.toSet())
- }
-
- override fun onDisplayDisconnected(id: Int) {
- connectedIds -= id
- if (DEBUG) {
- Log.d(TAG, "display with id=$id disconnected.")
- }
- _ignoredDisplayIds.value -= id
- trySend(connectedIds.toSet())
- }
- }
- trySend(connectedIds.toSet())
- displayManager.registerDisplayListener(
- callback,
- backgroundHandler,
- /* eventFlags */ 0,
- DisplayManager.PRIVATE_EVENT_TYPE_DISPLAY_CONNECTION_CHANGED,
- )
- awaitClose { displayManager.unregisterDisplayListener(callback) }
- }
- .distinctUntilChanged()
- .debugLog("connectedDisplayIds")
- .stateIn(
- bgApplicationScope,
- started = SharingStarted.WhileSubscribed(),
- // The initial value is set to empty, but connected displays are gathered as soon as
- // the flow starts being collected. This is to ensure the call to get displays (an
- // IPC) happens in the background instead of when this object
- // is instantiated.
- initialValue = emptySet(),
- )
-
- private val connectedExternalDisplayIds: Flow<Set<Int>> =
- connectedDisplayIds
- .map { connectedDisplayIds ->
- traceSection("$TAG#filteringExternalDisplays") {
- connectedDisplayIds
- .filter { id -> getDisplayType(id) == Display.TYPE_EXTERNAL }
- .toSet()
- }
- }
- .flowOn(backgroundCoroutineDispatcher)
- .debugLog("connectedExternalDisplayIds")
-
- private fun getDisplayType(displayId: Int): Int? =
- traceSection("$TAG#getDisplayType") { displayManager.getDisplay(displayId)?.type }
-
- private fun getDisplayFromDisplayManager(displayId: Int): Display? =
- traceSection("$TAG#getDisplay") { displayManager.getDisplay(displayId) }
-
- /**
- * Pending displays are the ones connected, but not enabled and not ignored.
- *
- * A connected display is ignored after the user makes the decision to use it or not. For now,
- * the initial decision from the user is final and not reversible.
- */
- private val pendingDisplayIds: Flow<Set<Int>> =
- combine(enabledDisplayIds, connectedExternalDisplayIds, ignoredDisplayIds) {
- enabledDisplaysIds,
- connectedExternalDisplayIds,
- ignoredDisplayIds ->
- if (DEBUG) {
- Log.d(
- TAG,
- "combining enabled=$enabledDisplaysIds, " +
- "connectedExternalDisplayIds=$connectedExternalDisplayIds, " +
- "ignored=$ignoredDisplayIds",
- )
- }
- connectedExternalDisplayIds - enabledDisplaysIds - ignoredDisplayIds
- }
- .debugLog("allPendingDisplayIds")
-
- /** Which display id should be enabled among the pending ones. */
- private val pendingDisplayId: Flow<Int?> =
- pendingDisplayIds.map { it.maxOrNull() }.distinctUntilChanged().debugLog("pendingDisplayId")
-
- override val pendingDisplay: Flow<DisplayRepository.PendingDisplay?> =
- pendingDisplayId
- .map { displayId ->
- val id = displayId ?: return@map null
- object : DisplayRepository.PendingDisplay {
- override val id = id
-
- override suspend fun enable() {
- traceSection("DisplayRepository#enable($id)") {
- if (DEBUG) {
- Log.d(TAG, "Enabling display with id=$id")
- }
- displayManager.enableConnectedDisplay(id)
- }
- // After the display has been enabled, it is automatically ignored.
- ignore()
- }
-
- override suspend fun ignore() {
- traceSection("DisplayRepository#ignore($id)") {
- _ignoredDisplayIds.value += id
- }
- }
-
- override suspend fun disable() {
- ignore()
- traceSection("DisplayRepository#disable($id)") {
- if (DEBUG) {
- Log.d(TAG, "Disabling display with id=$id")
- }
- displayManager.disableConnectedDisplay(id)
- }
- }
- }
- }
- .debugLog("pendingDisplay")
-
- override val defaultDisplayOff: Flow<Boolean> =
- displayChangeEvent
- .filter { it == Display.DEFAULT_DISPLAY }
- .map { defaultDisplay.state == Display.STATE_OFF }
- .distinctUntilChanged()
-
- private fun <T> Flow<T>.debugLog(flowName: String): Flow<T> {
- return if (DEBUG) {
- traceEach(flowName, logcat = true, traceEmissionCount = true)
- } else {
- this
- }
- }
-
- /**
- * Maps a set of T to a set of V, minimizing the number of `createValue` calls taking into
- * account the diff between each root flow emission.
- *
- * This is needed to minimize the number of [getDisplayFromDisplayManager] in this class. Note
- * that if the [createValue] returns a null element, it will not be added in the output set.
- */
- private fun <T, V> Flow<Set<T>>.mapElementsLazily(createValue: (T) -> V?): Flow<Set<V>> {
- data class State<T, V>(
- val previousSet: Set<T>,
- // Caches T values from the previousSet that were already converted to V
- val valueMap: Map<T, V>,
- val resultSet: Set<V>,
- )
-
- val emptyInitialState = State(emptySet<T>(), emptyMap(), emptySet<V>())
- return this.scan(emptyInitialState) { state, currentSet ->
- if (currentSet == state.previousSet) {
- state
- } else {
- val removed = state.previousSet - currentSet
- val added = currentSet - state.previousSet
- val newMap = state.valueMap.toMutableMap()
-
- added.forEach { key -> createValue(key)?.let { newMap[key] = it } }
- removed.forEach { key -> newMap.remove(key) }
-
- val resultSet = newMap.values.toSet()
- State(currentSet, newMap, resultSet)
- }
- }
- .filter { it != emptyInitialState }
- .map { it.resultSet }
- }
+ private val displayRepositoryFromLib: com.android.app.displaylib.DisplayRepository,
+) : DisplayRepositoryFromLib by displayRepositoryFromLib, DisplayRepository {
private val decorationEvents: Flow<Event> = callbackFlow {
val callback =
@@ -481,20 +106,5 @@ constructor(
private companion object {
const val TAG = "DisplayRepository"
- val DEBUG = Log.isLoggable(TAG, Log.DEBUG) || Compile.IS_DEBUG
}
}
-
-/** Used to provide default implementations for all methods. */
-private interface DisplayConnectionListener : DisplayListener {
-
- override fun onDisplayConnected(id: Int) {}
-
- override fun onDisplayDisconnected(id: Int) {}
-
- override fun onDisplayAdded(id: Int) {}
-
- override fun onDisplayRemoved(id: Int) {}
-
- override fun onDisplayChanged(id: Int) {}
-}
diff --git a/packages/SystemUI/src/com/android/systemui/display/data/repository/PerDisplayRepoDumpHelper.kt b/packages/SystemUI/src/com/android/systemui/display/data/repository/PerDisplayRepoDumpHelper.kt
new file mode 100644
index 000000000000..212d55612935
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/display/data/repository/PerDisplayRepoDumpHelper.kt
@@ -0,0 +1,39 @@
+/*
+ * 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.display.data.repository
+
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dump.DumpManager
+import com.android.systemui.dump.DumpableFromToString
+import javax.inject.Inject
+
+/** Helper class to register PerDisplayRepository in the dump manager in SystemUI. */
+@SysUISingleton
+class PerDisplayRepoDumpHelper @Inject constructor(private val dumpManager: DumpManager) :
+ PerDisplayRepository.InitCallback {
+ /**
+ * Registers PerDisplayRepository in the dump manager.
+ *
+ * The repository will be identified by the given debug name.
+ */
+ override fun onInit(debugName: String, instance: Any) {
+ dumpManager.registerNormalDumpable(
+ "PerDisplayRepository-$debugName",
+ DumpableFromToString(instance),
+ )
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/display/data/repository/PerDisplayRepository.kt b/packages/SystemUI/src/com/android/systemui/display/data/repository/PerDisplayRepository.kt
index d1d013542fbf..7e00c60dc43a 100644
--- a/packages/SystemUI/src/com/android/systemui/display/data/repository/PerDisplayRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/display/data/repository/PerDisplayRepository.kt
@@ -20,13 +20,10 @@ import android.util.Log
import android.view.Display
import com.android.app.tracing.coroutines.launchTraced as launch
import com.android.app.tracing.traceSection
-import com.android.systemui.Dumpable
import com.android.systemui.dagger.qualifiers.Background
-import com.android.systemui.dump.DumpManager
import dagger.assisted.Assisted
import dagger.assisted.AssistedFactory
import dagger.assisted.AssistedInject
-import java.io.PrintWriter
import java.util.concurrent.ConcurrentHashMap
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.collectLatest
@@ -88,6 +85,20 @@ interface PerDisplayRepository<T> {
/** Debug name for this repository, mainly for tracing and logging. */
val debugName: String
+
+ /**
+ * Callback to run when a given repository is initialized.
+ *
+ * This allows the caller to perform custom logic when the repository is ready to be used, e.g.
+ * register to dumpManager.
+ *
+ * Note that the instance is *leaked* outside of this class, so it should only be done when
+ * repository is meant to live as long as the caller. In systemUI this is ok because the
+ * repository lives as long as the process itself.
+ */
+ interface InitCallback {
+ fun onInit(debugName: String, instance: Any)
+ }
}
/**
@@ -110,8 +121,8 @@ constructor(
@Assisted private val instanceProvider: PerDisplayInstanceProvider<T>,
@Background private val backgroundApplicationScope: CoroutineScope,
private val displayRepository: DisplayRepository,
- private val dumpManager: DumpManager,
-) : PerDisplayRepository<T>, Dumpable {
+ private val initCallback: PerDisplayRepository.InitCallback,
+) : PerDisplayRepository<T> {
private val perDisplayInstances = ConcurrentHashMap<Int, T?>()
@@ -120,7 +131,7 @@ constructor(
}
private suspend fun start() {
- dumpManager.registerNormalDumpable("PerDisplayRepository-${debugName}", this)
+ initCallback.onInit(debugName, this)
displayRepository.displayIds.collectLatest { displayIds ->
val toRemove = perDisplayInstances.keys - displayIds
toRemove.forEach { displayId ->
@@ -169,8 +180,9 @@ constructor(
private const val TAG = "PerDisplayInstanceRepo"
}
- override fun dump(pw: PrintWriter, args: Array<out String>) {
- pw.println(perDisplayInstances)
+ override fun toString(): String {
+ return "PerDisplayInstanceRepositoryImpl(" +
+ "debugName='$debugName', instances=$perDisplayInstances)"
}
}
@@ -193,6 +205,7 @@ class DefaultDisplayOnlyInstanceRepositoryImpl<T>(
private val lazyDefaultDisplayInstance by lazy {
instanceProvider.createInstance(Display.DEFAULT_DISPLAY)
}
+
override fun get(displayId: Int): T? = lazyDefaultDisplayInstance
}
diff --git a/packages/SystemUI/src/com/android/systemui/display/domain/interactor/ConnectedDisplayInteractor.kt b/packages/SystemUI/src/com/android/systemui/display/domain/interactor/ConnectedDisplayInteractor.kt
index 15a3cbdb8072..84f103e8f730 100644
--- a/packages/SystemUI/src/com/android/systemui/display/domain/interactor/ConnectedDisplayInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/display/domain/interactor/ConnectedDisplayInteractor.kt
@@ -18,6 +18,7 @@ package com.android.systemui.display.domain.interactor
import android.companion.virtual.VirtualDeviceManager
import android.view.Display
+import com.android.app.displaylib.DisplayRepository as DisplayRepositoryFromLib
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.display.data.repository.DeviceStateRepository
@@ -138,7 +139,8 @@ constructor(
.distinctUntilChanged()
.flowOn(backgroundCoroutineDispatcher)
- private fun DisplayRepository.PendingDisplay.toInteractorPendingDisplay(): PendingDisplay =
+ private fun DisplayRepositoryFromLib.PendingDisplay.toInteractorPendingDisplay():
+ PendingDisplay =
object : PendingDisplay {
override suspend fun enable() = this@toInteractorPendingDisplay.enable()
diff --git a/packages/SystemUI/src/com/android/systemui/dump/DumpableFromToString.kt b/packages/SystemUI/src/com/android/systemui/dump/DumpableFromToString.kt
new file mode 100644
index 000000000000..438931a1962d
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/dump/DumpableFromToString.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.systemui.dump
+
+import com.android.systemui.Dumpable
+import java.io.PrintWriter
+
+/** Dumpable implementation that just calls toString() on the instance. */
+class DumpableFromToString<T>(private val instance: T) : Dumpable {
+ override fun dump(pw: PrintWriter, args: Array<out String>) {
+ pw.println("$instance")
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialogLite.java b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialogLite.java
index cf5c3402792e..f2a10cc43fd9 100644
--- a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialogLite.java
+++ b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialogLite.java
@@ -147,14 +147,14 @@ import com.android.systemui.util.RingerModeTracker;
import com.android.systemui.util.settings.GlobalSettings;
import com.android.systemui.util.settings.SecureSettings;
+import dagger.Lazy;
+
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.Executor;
import javax.inject.Inject;
-import dagger.Lazy;
-
/**
* Helper to show the global actions dialog. Each item is an {@link Action} that may show depending
* on whether the keyguard is showing, and whether the device is provisioned.
@@ -270,6 +270,16 @@ public class GlobalActionsDialogLite implements DialogInterface.OnDismissListene
private final UserLogoutInteractor mLogoutInteractor;
private final GlobalActionsInteractor mInteractor;
private final Lazy<DisplayWindowPropertiesRepository> mDisplayWindowPropertiesRepositoryLazy;
+ private final Handler mHandler;
+
+ private final UserTracker.Callback mOnUserSwitched = new UserTracker.Callback() {
+ @Override
+ public void onBeforeUserSwitching(int newUser) {
+ // Dismiss the dialog as soon as we start switching. This will schedule a message
+ // in a handler so it will be pretty quick.
+ dismissDialog();
+ }
+ };
@VisibleForTesting
public enum GlobalActionsEvent implements UiEventLogger.UiEventEnum {
@@ -425,6 +435,29 @@ public class GlobalActionsDialogLite implements DialogInterface.OnDismissListene
mInteractor = interactor;
mDisplayWindowPropertiesRepositoryLazy = displayWindowPropertiesRepository;
+ mHandler = new Handler(mMainHandler.getLooper()) {
+ public void handleMessage(Message msg) {
+ switch (msg.what) {
+ case MESSAGE_DISMISS:
+ if (mDialog != null) {
+ if (SYSTEM_DIALOG_REASON_DREAM.equals(msg.obj)) {
+ // Hide instantly.
+ mDialog.hide();
+ mDialog.dismiss();
+ } else {
+ mDialog.dismiss();
+ }
+ mDialog = null;
+ }
+ break;
+ case MESSAGE_REFRESH:
+ refreshSilentMode();
+ mAdapter.notifyDataSetChanged();
+ break;
+ }
+ }
+ };
+
// receive broadcasts
IntentFilter filter = new IntentFilter();
filter.addAction(Intent.ACTION_CLOSE_SYSTEM_DIALOGS);
@@ -537,6 +570,7 @@ public class GlobalActionsDialogLite implements DialogInterface.OnDismissListene
expandable != null ? expandable.dialogTransitionController(
new DialogCuj(InteractionJankMonitor.CUJ_SHADE_DIALOG_OPEN,
INTERACTION_JANK_TAG)) : null;
+ mUserTracker.addCallback(mOnUserSwitched, mBackgroundExecutor);
if (controller != null) {
mDialogTransitionAnimator.show(mDialog, controller);
} else {
@@ -1404,6 +1438,7 @@ public class GlobalActionsDialogLite implements DialogInterface.OnDismissListene
mWindowManagerFuncs.onGlobalActionsHidden();
mLifecycle.setCurrentState(Lifecycle.State.CREATED);
mInteractor.onDismissed();
+ mUserTracker.removeCallback(mOnUserSwitched);
}
/**
@@ -2228,29 +2263,6 @@ public class GlobalActionsDialogLite implements DialogInterface.OnDismissListene
mDialogPressDelay = 0; // ms
}
- private Handler mHandler = new Handler() {
- public void handleMessage(Message msg) {
- switch (msg.what) {
- case MESSAGE_DISMISS:
- if (mDialog != null) {
- if (SYSTEM_DIALOG_REASON_DREAM.equals(msg.obj)) {
- // Hide instantly.
- mDialog.hide();
- mDialog.dismiss();
- } else {
- mDialog.dismiss();
- }
- mDialog = null;
- }
- break;
- case MESSAGE_REFRESH:
- refreshSilentMode();
- mAdapter.notifyDataSetChanged();
- break;
- }
- }
- };
-
private void onAirplaneModeChanged() {
// Let the service state callbacks handle the state.
if (mHasTelephony || mAirplaneModeOn == null) return;
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java
index 757464976261..79685088fed7 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java
@@ -673,7 +673,8 @@ public class KeyguardService extends Service {
if (SceneContainerFlag.isEnabled()) {
mDeviceEntryInteractorLazy.get().lockNow("doKeyguardTimeout");
} else if (KeyguardWmStateRefactor.isEnabled()) {
- mKeyguardServiceShowLockscreenInteractor.onKeyguardServiceDoKeyguardTimeout();
+ mKeyguardServiceShowLockscreenInteractor
+ .onKeyguardServiceDoKeyguardTimeout(options);
}
mKeyguardViewMediator.doKeyguardTimeout(options);
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/WindowManagerLockscreenVisibilityManager.kt b/packages/SystemUI/src/com/android/systemui/keyguard/WindowManagerLockscreenVisibilityManager.kt
index 58692746d1e0..51b953ef290c 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/WindowManagerLockscreenVisibilityManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/WindowManagerLockscreenVisibilityManager.kt
@@ -22,11 +22,14 @@ import android.util.Log
import android.view.IRemoteAnimationFinishedCallback
import android.view.RemoteAnimationTarget
import android.view.WindowManager
+import com.android.internal.widget.LockPatternUtils
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.keyguard.domain.interactor.KeyguardDismissTransitionInteractor
+import com.android.systemui.keyguard.domain.interactor.KeyguardShowWhileAwakeInteractor
import com.android.systemui.keyguard.ui.binder.KeyguardSurfaceBehindParamsApplier
import com.android.systemui.statusbar.policy.KeyguardStateController
+import com.android.systemui.user.domain.interactor.SelectedUserInteractor
import com.android.window.flags.Flags
import com.android.wm.shell.keyguard.KeyguardTransitions
import java.util.concurrent.Executor
@@ -46,6 +49,9 @@ constructor(
private val keyguardSurfaceBehindAnimator: KeyguardSurfaceBehindParamsApplier,
private val keyguardDismissTransitionInteractor: KeyguardDismissTransitionInteractor,
private val keyguardTransitions: KeyguardTransitions,
+ private val selectedUserInteractor: SelectedUserInteractor,
+ private val lockPatternUtils: LockPatternUtils,
+ private val keyguardShowWhileAwakeInteractor: KeyguardShowWhileAwakeInteractor,
) {
/**
@@ -92,12 +98,23 @@ constructor(
* second timeout).
*/
private var isKeyguardGoingAway = false
- private set(value) {
+ private set(goingAway) {
// TODO(b/278086361): Extricate the keyguard state controller.
- keyguardStateController.notifyKeyguardGoingAway(value)
- field = value
+ keyguardStateController.notifyKeyguardGoingAway(goingAway)
+
+ if (goingAway) {
+ keyguardGoingAwayRequestedForUserId = selectedUserInteractor.getSelectedUserId()
+ }
+
+ field = goingAway
}
+ /**
+ * The current user ID when we asked WM to start the keyguard going away animation. This is used
+ * for validation when user switching occurs during unlock.
+ */
+ private var keyguardGoingAwayRequestedForUserId: Int = -1
+
/** Callback provided by WM to call once we're done with the going away animation. */
private var goingAwayRemoteAnimationFinishedCallback: IRemoteAnimationFinishedCallback? = null
@@ -171,6 +188,14 @@ constructor(
nonApps: Array<RemoteAnimationTarget>,
finishedCallback: IRemoteAnimationFinishedCallback,
) {
+ goingAwayRemoteAnimationFinishedCallback = finishedCallback
+
+ if (maybeStartTransitionIfUserSwitchedDuringGoingAway()) {
+ Log.d(TAG, "User switched during keyguard going away - ending remote animation.")
+ endKeyguardGoingAwayAnimation()
+ return
+ }
+
// If we weren't expecting the keyguard to be going away, WM triggered this transition.
if (!isKeyguardGoingAway) {
// Since WM triggered this, we're likely not transitioning to GONE yet. See if we can
@@ -198,7 +223,6 @@ constructor(
}
if (apps.isNotEmpty()) {
- goingAwayRemoteAnimationFinishedCallback = finishedCallback
keyguardSurfaceBehindAnimator.applyParamsToSurface(apps[0])
} else {
// Nothing to do here if we have no apps, end the animation, which will cancel it and WM
@@ -211,6 +235,7 @@ constructor(
// If WM cancelled the animation, we need to end immediately even if we're still using the
// animation.
endKeyguardGoingAwayAnimation()
+ maybeStartTransitionIfUserSwitchedDuringGoingAway()
}
/**
@@ -301,6 +326,29 @@ constructor(
}
}
+ /**
+ * If necessary, start a transition to show/hide keyguard in response to a user switch during
+ * keyguard going away.
+ *
+ * Returns [true] if a transition was started, or false if a transition was not necessary.
+ */
+ private fun maybeStartTransitionIfUserSwitchedDuringGoingAway(): Boolean {
+ val currentUser = selectedUserInteractor.getSelectedUserId()
+ if (currentUser != keyguardGoingAwayRequestedForUserId) {
+ if (lockPatternUtils.isSecure(currentUser)) {
+ keyguardShowWhileAwakeInteractor.onSwitchedToSecureUserWhileKeyguardGoingAway()
+ } else {
+ keyguardDismissTransitionInteractor.startDismissKeyguardTransition(
+ reason = "User switch during keyguard going away, and new user is insecure"
+ )
+ }
+
+ return true
+ } else {
+ return false
+ }
+ }
+
companion object {
private val TAG = "WindowManagerLsVis"
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardServiceShowLockscreenRepository.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardServiceShowLockscreenRepository.kt
new file mode 100644
index 000000000000..16c2d14b78ae
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardServiceShowLockscreenRepository.kt
@@ -0,0 +1,46 @@
+/*
+ * 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.keyguard.data.repository
+
+import android.os.IRemoteCallback
+import com.android.systemui.dagger.SysUISingleton
+import javax.inject.Inject
+
+/**
+ * Holds an IRemoteCallback along with the current user ID at the time the callback was provided.
+ */
+data class ShowLockscreenCallback(val userId: Int, val remoteCallback: IRemoteCallback)
+
+/** Maintains state related to KeyguardService requests to show the lockscreen. */
+@SysUISingleton
+class KeyguardServiceShowLockscreenRepository @Inject constructor() {
+ val showLockscreenCallbacks = ArrayList<ShowLockscreenCallback>()
+
+ /**
+ * Adds a callback that we'll notify when we show the lockscreen (or affirmatively decide not to
+ * show it).
+ */
+ fun addShowLockscreenCallback(forUser: Int, callback: IRemoteCallback) {
+ synchronized(showLockscreenCallbacks) {
+ showLockscreenCallbacks.add(ShowLockscreenCallback(forUser, callback))
+ }
+ }
+
+ companion object {
+ private const val TAG = "ShowLockscreenRepository"
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardClockInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardClockInteractor.kt
index ab0efed2cb76..02e04aa279d8 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardClockInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardClockInteractor.kt
@@ -226,6 +226,10 @@ constructor(
}
fun handleFidgetTap(x: Float, y: Float) {
+ if (!com.android.systemui.Flags.clockFidgetAnimation()) {
+ return
+ }
+
if (selectedClockSize.value == ClockSizeSetting.DYNAMIC) {
clockEventController.handleFidgetTap(x, y)
} else {
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardServiceShowLockscreenInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardServiceShowLockscreenInteractor.kt
index b55bb383c308..07a31e16384c 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardServiceShowLockscreenInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardServiceShowLockscreenInteractor.kt
@@ -17,16 +17,27 @@
package com.android.systemui.keyguard.domain.interactor
import android.annotation.SuppressLint
+import android.app.KeyguardManager.LOCK_ON_USER_SWITCH_CALLBACK
+import android.os.Bundle
+import android.os.IRemoteCallback
+import android.os.RemoteException
+import android.util.Log
+import com.android.systemui.CoreStartable
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Background
+import com.android.systemui.keyguard.data.repository.KeyguardServiceShowLockscreenRepository
+import com.android.systemui.keyguard.data.repository.ShowLockscreenCallback
+import com.android.systemui.settings.UserTracker
+import com.android.systemui.user.domain.interactor.SelectedUserInteractor
+import dagger.Lazy
import javax.inject.Inject
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.launch
/**
- * Logic around requests by [KeyguardService] to show keyguard right now, even though the device is
- * awake and not going to sleep.
+ * Logic around requests by [KeyguardService] and friends to show keyguard right now, even though
+ * the device is awake and not going to sleep.
*
* This can happen if WM#lockNow() is called, if KeyguardService#showDismissibleKeyguard is called
* because we're folding with "continue using apps on fold" set to "swipe up to continue", or if the
@@ -38,7 +49,28 @@ import kotlinx.coroutines.launch
@SysUISingleton
class KeyguardServiceShowLockscreenInteractor
@Inject
-constructor(@Background val backgroundScope: CoroutineScope) {
+constructor(
+ @Background val backgroundScope: CoroutineScope,
+ private val selectedUserInteractor: SelectedUserInteractor,
+ private val repository: KeyguardServiceShowLockscreenRepository,
+ private val userTracker: UserTracker,
+ private val wmLockscreenVisibilityInteractor: Lazy<WindowManagerLockscreenVisibilityInteractor>,
+ private val keyguardEnabledInteractor: KeyguardEnabledInteractor,
+) : CoreStartable {
+
+ override fun start() {
+ backgroundScope.launch {
+ // Whenever we tell ATMS that lockscreen is visible, notify any showLockscreenCallbacks.
+ // This is not the only place we notify the lockNowCallbacks - there are cases where we
+ // decide not to show the lockscreen despite being asked to, and we need to notify the
+ // callback in those cases as well.
+ wmLockscreenVisibilityInteractor.get().lockscreenVisibility.collect { visible ->
+ if (visible) {
+ notifyShowLockscreenCallbacks()
+ }
+ }
+ }
+ }
/**
* Emits whenever [KeyguardService] receives a call that indicates we should show the lockscreen
@@ -57,9 +89,38 @@ constructor(@Background val backgroundScope: CoroutineScope) {
/**
* Called by [KeyguardService] when it receives a doKeyguardTimeout() call. This indicates that
* the device locked while the screen was on.
+ *
+ * We'll show keyguard, and if provided, save the lock on user switch callback, to notify it
+ * later when we successfully show.
*/
- fun onKeyguardServiceDoKeyguardTimeout() {
+ fun onKeyguardServiceDoKeyguardTimeout(options: Bundle? = null) {
backgroundScope.launch {
+ if (options?.getBinder(LOCK_ON_USER_SWITCH_CALLBACK) != null) {
+ val userId = userTracker.userId
+
+ // This callback needs to be invoked after we show the lockscreen (or decide not to
+ // show it) otherwise System UI will crash in 20 seconds, as a security measure.
+ repository.addShowLockscreenCallback(
+ userId,
+ IRemoteCallback.Stub.asInterface(
+ options.getBinder(LOCK_ON_USER_SWITCH_CALLBACK)
+ ),
+ )
+
+ Log.d(
+ TAG,
+ "Showing lockscreen now - setting required callback for user $userId. " +
+ "SysUI will crash if this callback is not invoked.",
+ )
+
+ // If the keyguard is disabled or suppressed, we'll never actually show the
+ // lockscreen. Notify the callback so we don't crash.
+ if (!keyguardEnabledInteractor.isKeyguardEnabledAndNotSuppressed()) {
+ Log.d(TAG, "Keyguard is disabled or suppressed, notifying callbacks now.")
+ notifyShowLockscreenCallbacks()
+ }
+ }
+
showNowEvents.emit(ShowWhileAwakeReason.KEYGUARD_TIMEOUT_WHILE_SCREEN_ON)
}
}
@@ -74,4 +135,33 @@ constructor(@Background val backgroundScope: CoroutineScope) {
showNowEvents.emit(ShowWhileAwakeReason.FOLDED_WITH_SWIPE_UP_TO_CONTINUE)
}
}
+
+ /** Notifies the callbacks that we've either locked, or decided not to lock. */
+ private fun notifyShowLockscreenCallbacks() {
+ var callbacks: MutableList<ShowLockscreenCallback>
+ synchronized(repository.showLockscreenCallbacks) {
+ callbacks = ArrayList(repository.showLockscreenCallbacks)
+ repository.showLockscreenCallbacks.clear()
+ }
+
+ val iter: MutableIterator<ShowLockscreenCallback> = callbacks.listIterator()
+ while (iter.hasNext()) {
+ val callback = iter.next()
+ iter.remove()
+ if (callback.userId != selectedUserInteractor.getSelectedUserId()) {
+ Log.i(TAG, "Not notifying lockNowCallback due to user mismatch")
+ continue
+ }
+ Log.i(TAG, "Notifying lockNowCallback")
+ try {
+ callback.remoteCallback.sendResult(null)
+ } catch (e: RemoteException) {
+ Log.e(TAG, "Could not issue LockNowCallback sendResult", e)
+ }
+ }
+ }
+
+ companion object {
+ private const val TAG = "ShowLockscreenInteractor"
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardShowWhileAwakeInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardShowWhileAwakeInteractor.kt
index a8000a568a6a..c67939a0738a 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardShowWhileAwakeInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardShowWhileAwakeInteractor.kt
@@ -16,15 +16,20 @@
package com.android.systemui.keyguard.domain.interactor
+import android.annotation.SuppressLint
import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.keyguard.data.repository.BiometricSettingsRepository
import com.android.systemui.util.kotlin.sample
import javax.inject.Inject
+import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.filter
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.merge
+import kotlinx.coroutines.launch
/** The reason we're showing lockscreen while awake, used for logging. */
enum class ShowWhileAwakeReason(private val logReason: String) {
@@ -38,6 +43,9 @@ enum class ShowWhileAwakeReason(private val logReason: String) {
),
KEYGUARD_TIMEOUT_WHILE_SCREEN_ON(
"Timed out while the screen was kept on, or WM#lockNow() was called."
+ ),
+ SWITCHED_TO_SECURE_USER_WHILE_GOING_AWAY(
+ "User switch to secure user occurred during keyguardGoingAway sequence, so we're locking."
);
override fun toString(): String {
@@ -68,6 +76,7 @@ enum class ShowWhileAwakeReason(private val logReason: String) {
class KeyguardShowWhileAwakeInteractor
@Inject
constructor(
+ @Background val backgroundScope: CoroutineScope,
biometricSettingsRepository: BiometricSettingsRepository,
keyguardEnabledInteractor: KeyguardEnabledInteractor,
keyguardServiceShowLockscreenInteractor: KeyguardServiceShowLockscreenInteractor,
@@ -91,6 +100,15 @@ constructor(
.filter { reshow -> reshow }
.map { ShowWhileAwakeReason.KEYGUARD_REENABLED }
+ /**
+ * Emits whenever a user switch to a secure user occurs during keyguard going away.
+ *
+ * This is an event flow, hence the SharedFlow.
+ */
+ @SuppressLint("SharedFlowCreation")
+ val switchedToSecureUserDuringGoingAway: MutableSharedFlow<ShowWhileAwakeReason> =
+ MutableSharedFlow()
+
/** Emits whenever we should show lockscreen while the screen is on, for any reason. */
val showWhileAwakeEvents: Flow<ShowWhileAwakeReason> =
merge(
@@ -108,5 +126,15 @@ constructor(
keyguardServiceShowLockscreenInteractor.showNowEvents.filter {
keyguardEnabledInteractor.isKeyguardEnabledAndNotSuppressed()
},
+ switchedToSecureUserDuringGoingAway,
)
+
+ /** A user switch to a secure user occurred while we were going away. We need to re-lock. */
+ fun onSwitchedToSecureUserWhileKeyguardGoingAway() {
+ backgroundScope.launch {
+ switchedToSecureUserDuringGoingAway.emit(
+ ShowWhileAwakeReason.SWITCHED_TO_SECURE_USER_WHILE_GOING_AWAY
+ )
+ }
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionCoreStartable.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionCoreStartable.kt
index 2b4582a2a096..780b7fb06b5a 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionCoreStartable.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionCoreStartable.kt
@@ -29,6 +29,7 @@ constructor(
private val auditLogger: KeyguardTransitionAuditLogger,
private val statusBarDisableFlagsInteractor: StatusBarDisableFlagsInteractor,
private val keyguardStateCallbackInteractor: KeyguardStateCallbackInteractor,
+ private val keyguardServiceShowLockscreenInteractor: KeyguardServiceShowLockscreenInteractor,
) : CoreStartable {
override fun start() {
@@ -54,6 +55,7 @@ constructor(
auditLogger.start()
statusBarDisableFlagsInteractor.start()
keyguardStateCallbackInteractor.start()
+ keyguardServiceShowLockscreenInteractor.start()
}
companion object {
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/binder/SeekBarObserver.kt b/packages/SystemUI/src/com/android/systemui/media/controls/ui/binder/SeekBarObserver.kt
index cedf661d8ee3..5b65531cdd55 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/binder/SeekBarObserver.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/binder/SeekBarObserver.kt
@@ -127,7 +127,6 @@ open class SeekBarObserver(private val holder: MediaViewHolder) :
}
holder.seekBar.setMax(data.duration)
- val totalTimeDescription = data.durationDescription
if (data.scrubbing) {
holder.scrubbingTotalTimeView.text = formatTimeLabel(data.duration)
}
@@ -147,17 +146,9 @@ open class SeekBarObserver(private val holder: MediaViewHolder) :
}
}
- val elapsedTimeDescription = data.elapsedTimeDescription
if (data.scrubbing) {
holder.scrubbingElapsedTimeView.text = formatTimeLabel(it)
}
-
- holder.seekBar.contentDescription =
- holder.seekBar.context.getString(
- R.string.controls_media_seekbar_description,
- elapsedTimeDescription,
- totalTimeDescription,
- )
}
}
@@ -166,6 +157,18 @@ open class SeekBarObserver(private val holder: MediaViewHolder) :
return DateUtils.formatElapsedTime(milliseconds / DateUtils.SECOND_IN_MILLIS)
}
+ fun updateContentDescription(
+ elapsedTimeDescription: CharSequence,
+ durationDescription: CharSequence,
+ ) {
+ holder.seekBar.contentDescription =
+ holder.seekBar.context.getString(
+ R.string.controls_media_seekbar_description,
+ elapsedTimeDescription,
+ durationDescription,
+ )
+ }
+
@VisibleForTesting
open fun buildResetAnimator(targetTime: Int): Animator {
val animator =
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaControlPanel.java b/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaControlPanel.java
index 006eb203a669..f69985ee5364 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaControlPanel.java
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaControlPanel.java
@@ -224,6 +224,8 @@ public class MediaControlPanel {
this::setIsScrubbing;
private final SeekBarViewModel.EnabledChangeListener mEnabledChangeListener =
this::setIsSeekBarEnabled;
+ private final SeekBarViewModel.ContentDescriptionListener mContentDescriptionListener =
+ this::setSeekbarContentDescription;
private final BroadcastDialogController mBroadcastDialogController;
private boolean mIsCurrentBroadcastedApp = false;
@@ -327,6 +329,7 @@ public class MediaControlPanel {
}
mSeekBarViewModel.removeScrubbingChangeListener(mScrubbingChangeListener);
mSeekBarViewModel.removeEnabledChangeListener(mEnabledChangeListener);
+ mSeekBarViewModel.removeContentDescriptionListener(mContentDescriptionListener);
mSeekBarViewModel.onDestroy();
mMediaViewController.onDestroy();
}
@@ -395,6 +398,10 @@ public class MediaControlPanel {
});
}
+ private void setSeekbarContentDescription(CharSequence elapsedTime, CharSequence duration) {
+ mSeekBarObserver.updateContentDescription(elapsedTime, duration);
+ }
+
/**
* Reloads animator duration scale.
*/
@@ -424,6 +431,7 @@ public class MediaControlPanel {
mSeekBarViewModel.attachTouchHandlers(vh.getSeekBar());
mSeekBarViewModel.setScrubbingChangeListener(mScrubbingChangeListener);
mSeekBarViewModel.setEnabledChangeListener(mEnabledChangeListener);
+ mSeekBarViewModel.setContentDescriptionListener(mContentDescriptionListener);
mMediaViewController.attach(player);
vh.getPlayer().setOnLongClickListener(v -> {
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaViewController.kt b/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaViewController.kt
index dba190022c8b..e87d5de56177 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaViewController.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaViewController.kt
@@ -229,6 +229,20 @@ constructor(
}
}
+ private val seekbarDescriptionListener =
+ object : SeekBarViewModel.ContentDescriptionListener {
+ override fun onContentDescriptionChanged(
+ elapsedTimeDescription: CharSequence,
+ durationDescription: CharSequence,
+ ) {
+ if (!SceneContainerFlag.isEnabled) return
+ seekBarObserver.updateContentDescription(
+ elapsedTimeDescription,
+ durationDescription,
+ )
+ }
+ }
+
/**
* Sets the listening state of the player.
*
@@ -350,6 +364,7 @@ constructor(
}
seekBarViewModel.removeScrubbingChangeListener(scrubbingChangeListener)
seekBarViewModel.removeEnabledChangeListener(enabledChangeListener)
+ seekBarViewModel.removeContentDescriptionListener(seekbarDescriptionListener)
seekBarViewModel.onDestroy()
}
mediaHostStatesManager.removeController(this)
@@ -653,6 +668,7 @@ constructor(
seekBarViewModel.attachTouchHandlers(mediaViewHolder.seekBar)
seekBarViewModel.setScrubbingChangeListener(scrubbingChangeListener)
seekBarViewModel.setEnabledChangeListener(enabledChangeListener)
+ seekBarViewModel.setContentDescriptionListener(seekbarDescriptionListener)
val mediaCard = mediaViewHolder.player
attach(mediaViewHolder.player)
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/viewmodel/SeekBarViewModel.kt b/packages/SystemUI/src/com/android/systemui/media/controls/ui/viewmodel/SeekBarViewModel.kt
index 8744c5c9a838..78a8cf8e9432 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/viewmodel/SeekBarViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/viewmodel/SeekBarViewModel.kt
@@ -104,19 +104,20 @@ constructor(
)
set(value) {
val enabledChanged = value.enabled != field.enabled
+ field = value
if (enabledChanged) {
enabledChangeListener?.onEnabledChanged(value.enabled)
}
+ _progress.postValue(value)
+
bgExecutor.execute {
val durationDescription = formatTimeContentDescription(value.duration)
val elapsedDescription =
value.elapsedTime?.let { formatTimeContentDescription(it) } ?: ""
- field =
- value.copy(
- durationDescription = durationDescription,
- elapsedTimeDescription = elapsedDescription,
- )
- _progress.postValue(field)
+ contentDescriptionListener?.onContentDescriptionChanged(
+ elapsedDescription,
+ durationDescription,
+ )
}
}
@@ -175,6 +176,7 @@ constructor(
private var scrubbingChangeListener: ScrubbingChangeListener? = null
private var enabledChangeListener: EnabledChangeListener? = null
+ private var contentDescriptionListener: ContentDescriptionListener? = null
/** Set to true when the user is touching the seek bar to change the position. */
private var scrubbing = false
@@ -394,6 +396,16 @@ constructor(
}
}
+ fun setContentDescriptionListener(listener: ContentDescriptionListener) {
+ contentDescriptionListener = listener
+ }
+
+ fun removeContentDescriptionListener(listener: ContentDescriptionListener) {
+ if (listener == contentDescriptionListener) {
+ contentDescriptionListener = null
+ }
+ }
+
/** returns a pair of whether seekbar is enabled and the duration of media. */
private fun getEnabledStateAndDuration(metadata: MediaMetadata?): Pair<Boolean, Int> {
val duration = metadata?.getLong(MediaMetadata.METADATA_KEY_DURATION)?.toInt() ?: 0
@@ -468,6 +480,13 @@ constructor(
fun onEnabledChanged(enabled: Boolean)
}
+ interface ContentDescriptionListener {
+ fun onContentDescriptionChanged(
+ elapsedTimeDescription: CharSequence,
+ durationDescription: CharSequence,
+ )
+ }
+
private class SeekBarChangeListener(
val viewModel: SeekBarViewModel,
val falsingManager: FalsingManager,
@@ -639,7 +658,5 @@ constructor(
val duration: Int,
/** whether seekBar is listening to progress updates */
val listening: Boolean,
- val elapsedTimeDescription: CharSequence = "",
- val durationDescription: CharSequence = "",
)
}
diff --git a/packages/SystemUI/src/com/android/systemui/media/remedia/ui/compose/Media.kt b/packages/SystemUI/src/com/android/systemui/media/remedia/ui/compose/Media.kt
index 7543e0f2af2e..f07238895aa5 100644
--- a/packages/SystemUI/src/com/android/systemui/media/remedia/ui/compose/Media.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/remedia/ui/compose/Media.kt
@@ -46,7 +46,6 @@ import androidx.compose.foundation.interaction.MutableInteractionSource
import androidx.compose.foundation.interaction.PressInteraction
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
-import androidx.compose.foundation.layout.BoxWithConstraints
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
@@ -55,7 +54,6 @@ import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
-import androidx.compose.foundation.layout.widthIn
import androidx.compose.foundation.shape.CircleShape
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material3.ButtonDefaults
@@ -75,6 +73,7 @@ import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.Stable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.key
+import androidx.compose.runtime.mutableIntStateOf
import androidx.compose.runtime.mutableStateListOf
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
@@ -101,6 +100,7 @@ import androidx.compose.ui.input.pointer.PointerInputChange
import androidx.compose.ui.input.pointer.pointerInput
import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.layout.Layout
+import androidx.compose.ui.layout.layout
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.semantics.clearAndSetSemantics
import androidx.compose.ui.semantics.contentDescription
@@ -110,6 +110,7 @@ import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp
import androidx.compose.ui.util.fastForEach
import androidx.compose.ui.util.fastForEachIndexed
+import androidx.compose.ui.util.fastRoundToInt
import com.android.compose.PlatformButton
import com.android.compose.PlatformIconButton
import com.android.compose.PlatformOutlinedButton
@@ -138,6 +139,7 @@ import com.android.systemui.media.remedia.ui.viewmodel.MediaPlayPauseActionViewM
import com.android.systemui.media.remedia.ui.viewmodel.MediaSecondaryActionViewModel
import com.android.systemui.media.remedia.ui.viewmodel.MediaViewModel
import kotlin.math.max
+import kotlin.math.min
/**
* Renders a media controls UI element.
@@ -406,7 +408,7 @@ private fun ContentScope.CardForegroundContent(
)
) {
// Always add the first/top row, regardless of presentation style.
- BoxWithConstraints(modifier = Modifier.fillMaxWidth()) {
+ Box(modifier = Modifier.fillMaxWidth()) {
// Icon.
Icon(
icon = viewModel.icon,
@@ -418,9 +420,26 @@ private fun ContentScope.CardForegroundContent(
.clip(CircleShape),
)
+ var cardMaxWidth: Int by remember { mutableIntStateOf(0) }
Row(
horizontalArrangement = Arrangement.spacedBy(8.dp),
- modifier = Modifier.align(Alignment.TopEnd),
+ modifier =
+ Modifier.align(Alignment.TopEnd)
+ // Output switcher chips must each be limited to at most 40% of the maximum
+ // width of the card.
+ //
+ // This saves the maximum possible width of the card so it can be referred
+ // to by child custom layout code below.
+ //
+ // The assumption is that the row can be as wide as the entire card.
+ .layout { measurable, constraints ->
+ cardMaxWidth = constraints.maxWidth
+ val placeable = measurable.measure(constraints)
+
+ layout(placeable.measuredWidth, placeable.measuredHeight) {
+ placeable.place(0, 0)
+ }
+ },
) {
viewModel.outputSwitcherChips.fastForEach { chip ->
OutputSwitcherChip(
@@ -433,9 +452,23 @@ private fun ContentScope.CardForegroundContent(
//
// The underlying assumption is that there'll never be more than one
// chip with text and one more icon-only chip. Only the one with
- // text
- // can ever end up being too wide.
- .widthIn(max = this@BoxWithConstraints.maxWidth * 0.4f),
+ // text can ever end up being too wide.
+ .layout { measurable, constraints ->
+ val placeable =
+ measurable.measure(
+ constraints.copy(
+ maxWidth =
+ min(
+ (cardMaxWidth * 0.4f).fastRoundToInt(),
+ constraints.maxWidth,
+ )
+ )
+ )
+
+ layout(placeable.measuredWidth, placeable.measuredHeight) {
+ placeable.place(0, 0)
+ }
+ },
)
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/composefragment/QSFragmentCompose.kt b/packages/SystemUI/src/com/android/systemui/qs/composefragment/QSFragmentCompose.kt
index a4386dee7264..05a60a6db31e 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/composefragment/QSFragmentCompose.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/composefragment/QSFragmentCompose.kt
@@ -18,7 +18,6 @@ package com.android.systemui.qs.composefragment
import android.annotation.SuppressLint
import android.content.Context
-import android.content.res.Configuration
import android.graphics.PointF
import android.graphics.Rect
import android.os.Bundle
@@ -49,7 +48,6 @@ import androidx.compose.foundation.layout.requiredHeightIn
import androidx.compose.foundation.verticalScroll
import androidx.compose.material3.MaterialTheme
import androidx.compose.runtime.Composable
-import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.runtime.DisposableEffect
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
@@ -72,8 +70,6 @@ import androidx.compose.ui.layout.onSizeChanged
import androidx.compose.ui.layout.positionInRoot
import androidx.compose.ui.layout.positionOnScreen
import androidx.compose.ui.platform.ComposeView
-import androidx.compose.ui.platform.LocalConfiguration
-import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.dimensionResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.semantics.CustomAccessibilityAction
@@ -255,7 +251,7 @@ constructor(
@Composable
private fun Content() {
- PlatformTheme(isDarkTheme = true /* Delete AlwaysDarkMode when removing this */) {
+ PlatformTheme {
ProvideShortcutHelperIndication(interactionsConfig = interactionsConfig()) {
// TODO(b/389985793): Make sure that there is no coroutine work or recompositions
// happening when alwaysCompose is true but isQsVisibleAndAnyShadeExpanded is false.
@@ -747,25 +743,22 @@ constructor(
)
val BrightnessSlider =
@Composable {
- AlwaysDarkMode {
- Box(
- Modifier.systemGestureExclusionInShade(
- enabled = {
- layoutState.transitionState is TransitionState.Idle
- }
- )
- ) {
- BrightnessSliderContainer(
- viewModel =
- containerViewModel.brightnessSliderViewModel,
- containerColors =
- ContainerColors(
- Color.Transparent,
- ContainerColors.defaultContainerColor,
- ),
- modifier = Modifier.fillMaxWidth(),
- )
- }
+ Box(
+ Modifier.systemGestureExclusionInShade(
+ enabled = {
+ layoutState.transitionState is TransitionState.Idle
+ }
+ )
+ ) {
+ BrightnessSliderContainer(
+ viewModel = containerViewModel.brightnessSliderViewModel,
+ containerColors =
+ ContainerColors(
+ Color.Transparent,
+ ContainerColors.defaultContainerColor,
+ ),
+ modifier = Modifier.fillMaxWidth(),
+ )
}
}
val TileGrid =
@@ -1243,28 +1236,3 @@ private fun interactionsConfig() =
private inline val alwaysCompose
get() = Flags.alwaysComposeQsUiFragment()
-
-/**
- * Forces the configuration and themes to be dark theme. This is needed in order to have
- * [colorResource] retrieve the dark mode colors.
- *
- * This should be removed when we remove the force dark mode in [PlatformTheme] at the root of the
- * compose hierarchy.
- */
-@Composable
-private fun AlwaysDarkMode(content: @Composable () -> Unit) {
- val currentConfig = LocalConfiguration.current
- val darkConfig =
- Configuration(currentConfig).apply {
- uiMode =
- (uiMode and (Configuration.UI_MODE_NIGHT_MASK.inv())) or
- Configuration.UI_MODE_NIGHT_YES
- }
- val newContext = LocalContext.current.createConfigurationContext(darkConfig)
- CompositionLocalProvider(
- LocalConfiguration provides darkConfig,
- LocalContext provides newContext,
- ) {
- content()
- }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/PaginatedGridLayout.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/PaginatedGridLayout.kt
index 22971a9eb703..a7ebb2289814 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/PaginatedGridLayout.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/PaginatedGridLayout.kt
@@ -88,7 +88,11 @@ constructor(
LaunchedEffect(listening, pagerState) {
snapshotFlow { listening() }
.collect {
- if (!listening()) {
+ // Whenever we go from not listening to listening, we should be in the first
+ // page. If we did this when going from listening to not listening, opening
+ // edit mode in second page will cause it to go to first page during the
+ // transition.
+ if (listening()) {
pagerState.scrollToPage(0)
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/infinitegrid/EditTile.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/infinitegrid/EditTile.kt
index 69b967a68c3c..ccbd8fdbe00c 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/infinitegrid/EditTile.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/infinitegrid/EditTile.kt
@@ -63,6 +63,7 @@ import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.automirrored.filled.ArrowBack
import androidx.compose.material.icons.filled.Add
import androidx.compose.material.icons.filled.Clear
+import androidx.compose.material3.ButtonDefaults
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
@@ -89,6 +90,7 @@ import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.draw.drawBehind
+import androidx.compose.ui.geometry.CornerRadius
import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.geometry.isSpecified
import androidx.compose.ui.graphics.Color
@@ -113,7 +115,9 @@ import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import androidx.compose.ui.util.fastMap
+import com.android.compose.gesture.effect.rememberOffsetOverscrollEffectFactory
import com.android.compose.modifiers.height
+import com.android.compose.theme.LocalAndroidColorScheme
import com.android.systemui.common.ui.compose.load
import com.android.systemui.qs.panels.shared.model.SizedTile
import com.android.systemui.qs.panels.shared.model.SizedTileImpl
@@ -131,6 +135,7 @@ import com.android.systemui.qs.panels.ui.compose.infinitegrid.EditModeTileDefaul
import com.android.systemui.qs.panels.ui.compose.infinitegrid.EditModeTileDefaults.AUTO_SCROLL_SPEED
import com.android.systemui.qs.panels.ui.compose.infinitegrid.EditModeTileDefaults.AvailableTilesGridMinHeight
import com.android.systemui.qs.panels.ui.compose.infinitegrid.EditModeTileDefaults.CurrentTilesGridPadding
+import com.android.systemui.qs.panels.ui.compose.infinitegrid.EditModeTileDefaults.GridBackgroundCornerRadius
import com.android.systemui.qs.panels.ui.compose.selection.InteractiveTileContainer
import com.android.systemui.qs.panels.ui.compose.selection.MutableSelectionState
import com.android.systemui.qs.panels.ui.compose.selection.ResizingState
@@ -163,14 +168,27 @@ object TileType
@OptIn(ExperimentalMaterial3Api::class)
@Composable
private fun EditModeTopBar(onStopEditing: () -> Unit, onReset: (() -> Unit)?) {
-
+ val primaryContainerColor = MaterialTheme.colorScheme.primaryContainer
TopAppBar(
- colors = TopAppBarDefaults.topAppBarColors(containerColor = Color.Transparent),
- title = { Text(text = stringResource(id = R.string.qs_edit)) },
+ colors =
+ TopAppBarDefaults.topAppBarColors(
+ containerColor = Color.Transparent,
+ titleContentColor = MaterialTheme.colorScheme.onSurface,
+ ),
+ title = {
+ Text(
+ text = stringResource(id = R.string.qs_edit),
+ modifier = Modifier.padding(start = 24.dp),
+ )
+ },
navigationIcon = {
- IconButton(onClick = onStopEditing) {
+ IconButton(
+ onClick = onStopEditing,
+ modifier = Modifier.drawBehind { drawCircle(primaryContainerColor) },
+ ) {
Icon(
Icons.AutoMirrored.Filled.ArrowBack,
+ tint = MaterialTheme.colorScheme.onSurface,
contentDescription =
stringResource(id = com.android.internal.R.string.action_bar_up_description),
)
@@ -178,11 +196,19 @@ private fun EditModeTopBar(onStopEditing: () -> Unit, onReset: (() -> Unit)?) {
},
actions = {
if (onReset != null) {
- TextButton(onClick = onReset) {
+ TextButton(
+ onClick = onReset,
+ colors =
+ ButtonDefaults.textButtonColors(
+ containerColor = MaterialTheme.colorScheme.primary,
+ contentColor = MaterialTheme.colorScheme.onPrimary,
+ ),
+ ) {
Text(stringResource(id = com.android.internal.R.string.reset))
}
}
},
+ modifier = Modifier.padding(vertical = 8.dp),
)
}
@@ -215,7 +241,9 @@ fun DefaultEditTileGrid(
containerColor = Color.Transparent,
topBar = { EditModeTopBar(onStopEditing = onStopEditing, onReset = reset) },
) { innerPadding ->
- CompositionLocalProvider(LocalOverscrollFactory provides null) {
+ CompositionLocalProvider(
+ LocalOverscrollFactory provides rememberOffsetOverscrollEffectFactory()
+ ) {
val scrollState = rememberScrollState()
AutoScrollGrid(listState, scrollState, innerPadding)
@@ -244,7 +272,7 @@ fun DefaultEditTileGrid(
targetState = listState.dragInProgress || selectionState.selected,
label = "QSEditHeader",
contentAlignment = Alignment.Center,
- modifier = Modifier.fillMaxWidth().heightIn(min = 80.dp),
+ modifier = Modifier.fillMaxWidth().heightIn(min = 48.dp),
) { showRemoveTarget ->
EditGridHeader {
if (showRemoveTarget) {
@@ -289,10 +317,6 @@ fun DefaultEditTileGrid(
spacedBy(dimensionResource(id = R.dimen.qs_label_container_margin)),
modifier = modifier.fillMaxSize(),
) {
- EditGridHeader {
- Text(text = stringResource(id = R.string.drag_to_add_tiles))
- }
-
val availableTiles = remember {
mutableStateListOf<AvailableTileGridCell>().apply {
addAll(toAvailableTiles(listState.tiles, otherTiles))
@@ -371,9 +395,7 @@ private fun EditGridHeader(
modifier: Modifier = Modifier,
content: @Composable BoxScope.() -> Unit,
) {
- CompositionLocalProvider(
- LocalContentColor provides MaterialTheme.colorScheme.onBackground.copy(alpha = .5f)
- ) {
+ CompositionLocalProvider(LocalContentColor provides MaterialTheme.colorScheme.onSurface) {
Box(contentAlignment = Alignment.Center, modifier = modifier.fillMaxWidth()) { content() }
}
}
@@ -420,6 +442,7 @@ private fun CurrentTilesGrid(
listState.tiles.fastMap { Pair(it, BounceableTileViewModel()) }
}
+ val primaryColor = MaterialTheme.colorScheme.primary
TileLazyGrid(
state = gridState,
columns = GridCells.Fixed(columns),
@@ -428,9 +451,9 @@ private fun CurrentTilesGrid(
Modifier.fillMaxWidth()
.height { totalHeight.roundToPx() }
.border(
- width = 1.dp,
- color = MaterialTheme.colorScheme.onBackground.copy(alpha = .5f),
- shape = RoundedCornerShape((TileHeight / 2) + CurrentTilesGridPadding),
+ width = 2.dp,
+ color = primaryColor,
+ shape = RoundedCornerShape(GridBackgroundCornerRadius),
)
.dragAndDropTileList(gridState, { gridContentOffset }, listState) { spec ->
onSetTiles(currentListState.tileSpecs())
@@ -439,6 +462,13 @@ private fun CurrentTilesGrid(
.onGloballyPositioned { coordinates ->
gridContentOffset = coordinates.positionInRoot()
}
+ .drawBehind {
+ drawRoundRect(
+ primaryColor,
+ cornerRadius = CornerRadius(GridBackgroundCornerRadius.toPx()),
+ alpha = .15f,
+ )
+ }
.testTag(CURRENT_TILES_GRID_TEST_TAG),
) {
EditTiles(cells, listState, selectionState, coroutineScope, largeTilesSpan, onRemoveTile) {
@@ -469,7 +499,6 @@ private fun AvailableTileGrid(
remember(tiles.fastMap { it.tile.category }, tiles.fastMap { it.tile.label }) {
groupAndSort(tiles)
}
- val labelColors = EditModeTileDefaults.editTileColors()
// Available tiles
Column(
@@ -480,32 +509,45 @@ private fun AvailableTileGrid(
) {
groupedTiles.forEach { (category, tiles) ->
key(category) {
- Text(
- text = category.label.load() ?: "",
- fontSize = 20.sp,
- color = labelColors.label,
+ val surfaceColor = MaterialTheme.colorScheme.surface
+ Column(
+ verticalArrangement = spacedBy(16.dp),
modifier =
- Modifier.fillMaxWidth().padding(start = 16.dp, bottom = 8.dp, top = 8.dp),
- )
- tiles.chunked(columns).forEach { row ->
- Row(
- horizontalArrangement = spacedBy(TileArrangementPadding),
- modifier = Modifier.fillMaxWidth().height(IntrinsicSize.Max),
- ) {
- row.forEach { tileGridCell ->
- key(tileGridCell.key) {
- AvailableTileGridCell(
- cell = tileGridCell,
- dragAndDropState = dragAndDropState,
- selectionState = selectionState,
- onAddTile = onAddTile,
- modifier = Modifier.weight(1f).fillMaxHeight(),
+ Modifier.drawBehind {
+ drawRoundRect(
+ surfaceColor,
+ cornerRadius = CornerRadius(GridBackgroundCornerRadius.toPx()),
+ alpha = .32f,
)
}
- }
+ .padding(16.dp),
+ ) {
+ Text(
+ text = category.label.load() ?: "",
+ fontSize = 20.sp,
+ color = MaterialTheme.colorScheme.onSurface,
+ modifier = Modifier.fillMaxWidth().padding(start = 8.dp, bottom = 16.dp),
+ )
+ tiles.chunked(columns).forEach { row ->
+ Row(
+ horizontalArrangement = spacedBy(TileArrangementPadding),
+ modifier = Modifier.fillMaxWidth().height(IntrinsicSize.Max),
+ ) {
+ row.forEach { tileGridCell ->
+ key(tileGridCell.key) {
+ AvailableTileGridCell(
+ cell = tileGridCell,
+ dragAndDropState = dragAndDropState,
+ selectionState = selectionState,
+ onAddTile = onAddTile,
+ modifier = Modifier.weight(1f).fillMaxHeight(),
+ )
+ }
+ }
- // Spacers for incomplete rows
- repeat(columns - row.size) { Spacer(modifier = Modifier.weight(1f)) }
+ // Spacers for incomplete rows
+ repeat(columns - row.size) { Spacer(modifier = Modifier.weight(1f)) }
+ }
}
}
}
@@ -761,7 +803,7 @@ private fun AvailableTileGridCell(
color = colors.label,
overflow = TextOverflow.Ellipsis,
textAlign = TextAlign.Center,
- modifier = Modifier.align(Alignment.Center),
+ modifier = Modifier.align(Alignment.TopCenter),
)
}
}
@@ -861,15 +903,16 @@ private object EditModeTileDefaults {
const val AUTO_SCROLL_SPEED = 2 // 2ms per pixel
val CurrentTilesGridPadding = 10.dp
val AvailableTilesGridMinHeight = 200.dp
+ val GridBackgroundCornerRadius = 42.dp
@Composable
fun editTileColors(): TileColors =
TileColors(
- background = MaterialTheme.colorScheme.surfaceVariant,
- iconBackground = MaterialTheme.colorScheme.surfaceVariant,
- label = MaterialTheme.colorScheme.onSurfaceVariant,
- secondaryLabel = MaterialTheme.colorScheme.onSurfaceVariant,
- icon = MaterialTheme.colorScheme.onSurfaceVariant,
+ background = LocalAndroidColorScheme.current.surfaceEffect2,
+ iconBackground = Color.Transparent,
+ label = MaterialTheme.colorScheme.onSurface,
+ secondaryLabel = MaterialTheme.colorScheme.onSurface,
+ icon = MaterialTheme.colorScheme.onSurface,
)
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/infinitegrid/Tile.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/infinitegrid/Tile.kt
index bf63c3858542..6bafd432669a 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/infinitegrid/Tile.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/infinitegrid/Tile.kt
@@ -25,6 +25,7 @@ import android.service.quicksettings.Tile.STATE_ACTIVE
import android.service.quicksettings.Tile.STATE_INACTIVE
import androidx.compose.animation.animateColorAsState
import androidx.compose.animation.core.animateDpAsState
+import androidx.compose.animation.core.animateFloatAsState
import androidx.compose.foundation.ExperimentalFoundationApi
import androidx.compose.foundation.background
import androidx.compose.foundation.combinedClickable
@@ -59,6 +60,7 @@ import androidx.compose.ui.draw.clip
import androidx.compose.ui.geometry.Size
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.Shape
+import androidx.compose.ui.graphics.graphicsLayer
import androidx.compose.ui.platform.LocalConfiguration
import androidx.compose.ui.platform.LocalResources
import androidx.compose.ui.semantics.Role
@@ -74,7 +76,9 @@ import androidx.compose.ui.util.trace
import com.android.app.tracing.coroutines.launchTraced as launch
import com.android.compose.animation.Expandable
import com.android.compose.animation.bounceable
+import com.android.compose.animation.rememberExpandableController
import com.android.compose.modifiers.thenIf
+import com.android.compose.theme.LocalAndroidColorScheme
import com.android.systemui.Flags
import com.android.systemui.animation.Expandable
import com.android.systemui.common.shared.model.Icon
@@ -165,6 +169,7 @@ fun Tile(
// TODO(b/361789146): Draw the shapes instead of clipping
val tileShape by TileDefaults.animateTileShapeAsState(uiState.state)
val animatedColor by animateColorAsState(colors.background, label = "QSTileBackgroundColor")
+ val animatedAlpha by animateFloatAsState(colors.alpha, label = "QSTileAlpha")
TileExpandable(
color = { animatedColor },
@@ -181,7 +186,8 @@ fun Tile(
nextBounceable = currentBounceableInfo.nextTile,
orientation = Orientation.Horizontal,
bounceEnd = currentBounceableInfo.bounceEnd,
- ),
+ )
+ .graphicsLayer { alpha = animatedAlpha },
) { expandable ->
val longClick: (() -> Unit)? =
{
@@ -260,8 +266,7 @@ private fun TileExpandable(
content: @Composable (Expandable) -> Unit,
) {
Expandable(
- color = color(),
- shape = shape,
+ controller = rememberExpandableController(color = color, shape = shape),
modifier = modifier.clip(shape).verticalSquish(squishiness),
useModifierBasedImplementation = true,
) {
@@ -370,6 +375,7 @@ data class TileColors(
val label: Color,
val secondaryLabel: Color,
val icon: Color,
+ val alpha: Float = 1f,
)
private object TileDefaults {
@@ -393,10 +399,10 @@ private object TileDefaults {
@ReadOnlyComposable
fun activeDualTargetTileColors(): TileColors =
TileColors(
- background = MaterialTheme.colorScheme.surfaceVariant,
+ background = LocalAndroidColorScheme.current.surfaceEffect2,
iconBackground = MaterialTheme.colorScheme.primary,
- label = MaterialTheme.colorScheme.onSurfaceVariant,
- secondaryLabel = MaterialTheme.colorScheme.onSurfaceVariant,
+ label = MaterialTheme.colorScheme.onSurface,
+ secondaryLabel = MaterialTheme.colorScheme.onSurface,
icon = MaterialTheme.colorScheme.onPrimary,
)
@@ -404,34 +410,36 @@ private object TileDefaults {
@ReadOnlyComposable
fun inactiveDualTargetTileColors(): TileColors =
TileColors(
- background = MaterialTheme.colorScheme.surfaceVariant,
- iconBackground = MaterialTheme.colorScheme.surfaceContainerHighest,
- label = MaterialTheme.colorScheme.onSurfaceVariant,
- secondaryLabel = MaterialTheme.colorScheme.onSurfaceVariant,
- icon = MaterialTheme.colorScheme.onSurfaceVariant,
+ background = LocalAndroidColorScheme.current.surfaceEffect2,
+ iconBackground = LocalAndroidColorScheme.current.surfaceEffect3,
+ label = MaterialTheme.colorScheme.onSurface,
+ secondaryLabel = MaterialTheme.colorScheme.onSurface,
+ icon = MaterialTheme.colorScheme.onSurface,
)
@Composable
@ReadOnlyComposable
fun inactiveTileColors(): TileColors =
TileColors(
- background = MaterialTheme.colorScheme.surfaceVariant,
- iconBackground = MaterialTheme.colorScheme.surfaceVariant,
- label = MaterialTheme.colorScheme.onSurfaceVariant,
- secondaryLabel = MaterialTheme.colorScheme.onSurfaceVariant,
- icon = MaterialTheme.colorScheme.onSurfaceVariant,
+ background = LocalAndroidColorScheme.current.surfaceEffect2,
+ iconBackground = Color.Transparent,
+ label = MaterialTheme.colorScheme.onSurface,
+ secondaryLabel = MaterialTheme.colorScheme.onSurface,
+ icon = MaterialTheme.colorScheme.onSurface,
)
@Composable
@ReadOnlyComposable
- fun unavailableTileColors(): TileColors =
- TileColors(
- background = MaterialTheme.colorScheme.surface,
- iconBackground = MaterialTheme.colorScheme.surface,
+ fun unavailableTileColors(): TileColors {
+ return TileColors(
+ background = LocalAndroidColorScheme.current.surfaceEffect2,
+ iconBackground = LocalAndroidColorScheme.current.surfaceEffect2,
label = MaterialTheme.colorScheme.onSurface,
secondaryLabel = MaterialTheme.colorScheme.onSurface,
icon = MaterialTheme.colorScheme.onSurface,
+ alpha = .38f,
)
+ }
@Composable
@ReadOnlyComposable
diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/selection/Selection.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/selection/Selection.kt
index a66b51f6fe50..57f63c755b43 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/selection/Selection.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/selection/Selection.kt
@@ -62,7 +62,6 @@ import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import androidx.compose.ui.unit.toSize
import androidx.compose.ui.zIndex
-import com.android.compose.modifiers.size
import com.android.compose.modifiers.thenIf
import com.android.systemui.qs.panels.ui.compose.infinitegrid.CommonTileDefaults.InactiveCornerRadius
import com.android.systemui.qs.panels.ui.compose.selection.SelectionDefaults.BADGE_ANGLE_RAD
@@ -155,6 +154,7 @@ fun InteractiveTileContainer(
Icon(
Icons.Default.Remove,
contentDescription = null,
+ tint = MaterialTheme.colorScheme.onPrimaryContainer,
modifier =
Modifier.size(size).align(Alignment.Center).graphicsLayer {
this.alpha = badgeIconAlpha
@@ -218,14 +218,15 @@ fun StaticTileBadge(
)
}
) {
- val secondaryColor = MaterialTheme.colorScheme.secondary
val size = with(LocalDensity.current) { BadgeIconSize.toDp() }
+ val primaryColor = MaterialTheme.colorScheme.primary
Icon(
icon,
contentDescription = contentDescription,
+ tint = MaterialTheme.colorScheme.onPrimary,
modifier =
Modifier.size(size).align(Alignment.Center).drawBehind {
- drawCircle(secondaryColor, radius = BadgeSize.toPx() / 2)
+ drawCircle(primaryColor, radius = BadgeSize.toPx() / 2)
},
)
}
@@ -291,7 +292,7 @@ private fun Transition<TileState>.animateColor(): State<Color> {
return animateColor { state ->
when (state) {
None -> Color.Transparent
- Removable -> MaterialTheme.colorScheme.secondary
+ Removable -> MaterialTheme.colorScheme.primaryContainer
Selected -> MaterialTheme.colorScheme.primary
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/reardisplay/RearDisplayCoreStartable.kt b/packages/SystemUI/src/com/android/systemui/reardisplay/RearDisplayCoreStartable.kt
index bc15bbb5e57d..263ef09ea767 100644
--- a/packages/SystemUI/src/com/android/systemui/reardisplay/RearDisplayCoreStartable.kt
+++ b/packages/SystemUI/src/com/android/systemui/reardisplay/RearDisplayCoreStartable.kt
@@ -20,6 +20,8 @@ import android.content.Context
import android.hardware.devicestate.DeviceStateManager
import android.hardware.devicestate.feature.flags.Flags
import androidx.annotation.VisibleForTesting
+import com.android.keyguard.KeyguardUpdateMonitor
+import com.android.keyguard.KeyguardUpdateMonitorCallback
import com.android.systemui.CoreStartable
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
@@ -28,8 +30,11 @@ import com.android.systemui.statusbar.phone.SystemUIDialog
import javax.inject.Inject
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Job
-import kotlinx.coroutines.flow.launchIn
-import kotlinx.coroutines.flow.map
+import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.asStateFlow
+import kotlinx.coroutines.flow.collectLatest
+import kotlinx.coroutines.flow.combine
+import kotlinx.coroutines.launch
/**
* Provides a {@link com.android.systemui.statusbar.phone.SystemUIDialog} to be shown on the inner
@@ -46,6 +51,7 @@ internal constructor(
private val rearDisplayStateInteractor: RearDisplayStateInteractor,
private val rearDisplayInnerDialogDelegateFactory: RearDisplayInnerDialogDelegate.Factory,
@Application private val scope: CoroutineScope,
+ private val keyguardUpdateMonitor: KeyguardUpdateMonitor,
) : CoreStartable, AutoCloseable {
companion object {
@@ -53,6 +59,16 @@ internal constructor(
}
@VisibleForTesting var stateChangeListener: Job? = null
+ private val keyguardVisible = MutableStateFlow(false)
+ private val keyguardVisibleFlow = keyguardVisible.asStateFlow()
+
+ @VisibleForTesting
+ val keyguardCallback =
+ object : KeyguardUpdateMonitorCallback() {
+ override fun onKeyguardVisibilityChanged(visible: Boolean) {
+ keyguardVisible.value = visible
+ }
+ }
override fun close() {
stateChangeListener?.cancel()
@@ -62,28 +78,39 @@ internal constructor(
if (Flags.deviceStateRdmV2()) {
var dialog: SystemUIDialog? = null
+ keyguardUpdateMonitor.registerCallback(keyguardCallback)
+
stateChangeListener =
- rearDisplayStateInteractor.state
- .map {
- when (it) {
- is RearDisplayStateInteractor.State.Enabled -> {
- val rearDisplayContext =
- context.createDisplayContext(it.innerDisplay)
- val delegate =
- rearDisplayInnerDialogDelegateFactory.create(
- rearDisplayContext,
- deviceStateManager::cancelStateRequest,
- )
- dialog = delegate.createDialog().apply { show() }
- }
+ scope.launch {
+ combine(rearDisplayStateInteractor.state, keyguardVisibleFlow) {
+ rearDisplayState,
+ keyguardVisible ->
+ Pair(rearDisplayState, keyguardVisible)
+ }
+ .collectLatest { (rearDisplayState, keyguardVisible) ->
+ when (rearDisplayState) {
+ is RearDisplayStateInteractor.State.Enabled -> {
+ if (!keyguardVisible) {
+ val rearDisplayContext =
+ context.createDisplayContext(
+ rearDisplayState.innerDisplay
+ )
+ val delegate =
+ rearDisplayInnerDialogDelegateFactory.create(
+ rearDisplayContext,
+ deviceStateManager::cancelStateRequest,
+ )
+ dialog = delegate.createDialog().apply { show() }
+ }
+ }
- is RearDisplayStateInteractor.State.Disabled -> {
- dialog?.dismiss()
- dialog = null
+ is RearDisplayStateInteractor.State.Disabled -> {
+ dialog?.dismiss()
+ dialog = null
+ }
}
}
- }
- .launchIn(scope)
+ }
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/BlurUtils.kt b/packages/SystemUI/src/com/android/systemui/statusbar/BlurUtils.kt
index f45971b57b65..2bacee12db8a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/BlurUtils.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/BlurUtils.kt
@@ -86,7 +86,7 @@ open class BlurUtils @Inject constructor(
*/
fun prepareBlur(viewRootImpl: ViewRootImpl?, radius: Int) {
if (viewRootImpl == null || !viewRootImpl.surfaceControl.isValid ||
- !supportsBlursOnWindows() || earlyWakeupEnabled
+ !shouldBlur(radius) || earlyWakeupEnabled
) {
return
}
@@ -113,7 +113,7 @@ open class BlurUtils @Inject constructor(
return
}
createTransaction().use {
- if (supportsBlursOnWindows()) {
+ if (shouldBlur(radius)) {
it.setBackgroundBlurRadius(viewRootImpl.surfaceControl, radius)
if (!earlyWakeupEnabled && lastAppliedBlur == 0 && radius != 0) {
Trace.asyncTraceForTrackBegin(
@@ -142,6 +142,14 @@ open class BlurUtils @Inject constructor(
return SurfaceControl.Transaction()
}
+ private fun shouldBlur(radius: Int): Boolean {
+ return supportsBlursOnWindows() ||
+ ((Flags.notificationShadeBlur() || Flags.bouncerUiRevamp()) &&
+ supportsBlursOnWindowsBase() &&
+ lastAppliedBlur > 0 &&
+ radius == 0)
+ }
+
/**
* If this device can render blurs.
*
@@ -149,8 +157,11 @@ open class BlurUtils @Inject constructor(
* @return {@code true} when supported.
*/
open fun supportsBlursOnWindows(): Boolean {
+ return supportsBlursOnWindowsBase() && crossWindowBlurListeners.isCrossWindowBlurEnabled
+ }
+
+ private fun supportsBlursOnWindowsBase(): Boolean {
return CROSS_WINDOW_BLUR_SUPPORTED && ActivityManager.isHighEndGfx() &&
- crossWindowBlurListeners.isCrossWindowBlurEnabled() &&
!SystemProperties.getBoolean("persist.sysui.disableBlur", false)
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShadeDepthController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShadeDepthController.kt
index 2e83910a2a93..472dc823423e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShadeDepthController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShadeDepthController.kt
@@ -19,8 +19,6 @@ package com.android.systemui.statusbar
import android.animation.Animator
import android.animation.AnimatorListenerAdapter
import android.animation.ValueAnimator
-import android.content.Context
-import android.content.res.Configuration
import android.os.SystemClock
import android.util.IndentingPrintWriter
import android.util.Log
@@ -42,16 +40,14 @@ import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.dump.DumpManager
import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
import com.android.systemui.plugins.statusbar.StatusBarStateController
-import com.android.systemui.shade.ShadeDisplayAware
import com.android.systemui.shade.ShadeExpansionChangeEvent
import com.android.systemui.shade.ShadeExpansionListener
+import com.android.systemui.shade.domain.interactor.ShadeModeInteractor
import com.android.systemui.statusbar.phone.BiometricUnlockController
import com.android.systemui.statusbar.phone.BiometricUnlockController.MODE_WAKE_AND_UNLOCK
import com.android.systemui.statusbar.phone.DozeParameters
import com.android.systemui.statusbar.phone.ScrimController
-import com.android.systemui.statusbar.policy.ConfigurationController
import com.android.systemui.statusbar.policy.KeyguardStateController
-import com.android.systemui.statusbar.policy.SplitShadeStateController
import com.android.systemui.util.WallpaperController
import com.android.systemui.wallpapers.domain.interactor.WallpaperInteractor
import com.android.systemui.window.domain.interactor.WindowRootViewBlurInteractor
@@ -82,13 +78,11 @@ constructor(
private val wallpaperInteractor: WallpaperInteractor,
private val notificationShadeWindowController: NotificationShadeWindowController,
private val dozeParameters: DozeParameters,
- @ShadeDisplayAware private val context: Context,
- private val splitShadeStateController: SplitShadeStateController,
+ private val shadeModeInteractor: ShadeModeInteractor,
private val windowRootViewBlurInteractor: WindowRootViewBlurInteractor,
private val appZoomOutOptional: Optional<AppZoomOut>,
@Application private val applicationScope: CoroutineScope,
dumpManager: DumpManager,
- configurationController: ConfigurationController,
) : ShadeExpansionListener, Dumpable {
companion object {
private const val WAKE_UP_ANIMATION_ENABLED = true
@@ -110,7 +104,6 @@ constructor(
private var isOpen: Boolean = false
private var isBlurred: Boolean = false
private var listeners = mutableListOf<DepthListener>()
- private var inSplitShade: Boolean = false
private var prevTracking: Boolean = false
private var prevTimestamp: Long = -1
@@ -294,7 +287,7 @@ constructor(
private fun blurRadiusToZoomOut(blurRadius: Float): Float {
var zoomOut = MathUtils.saturate(blurUtils.ratioOfBlurRadius(blurRadius))
- if (inSplitShade) {
+ if (shadeModeInteractor.isSplitShade) {
zoomOut = 0f
}
@@ -432,14 +425,6 @@ constructor(
}
shadeAnimation.setStiffness(SpringForce.STIFFNESS_LOW)
shadeAnimation.setDampingRatio(SpringForce.DAMPING_RATIO_NO_BOUNCY)
- updateResources()
- configurationController.addCallback(
- object : ConfigurationController.ConfigurationListener {
- override fun onConfigChanged(newConfig: Configuration?) {
- updateResources()
- }
- }
- )
applicationScope.launch {
wallpaperInteractor.wallpaperSupportsAmbientMode.collect { supported ->
wallpaperSupportsAmbientMode = supported
@@ -469,10 +454,6 @@ constructor(
}
}
- private fun updateResources() {
- inSplitShade = splitShadeStateController.shouldUseSplitNotificationShade(context.resources)
- }
-
fun addListener(listener: DepthListener) {
listeners.add(listener)
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/OWNERS b/packages/SystemUI/src/com/android/systemui/statusbar/OWNERS
index 6cebcd98a0ba..6d3c12d139db 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/OWNERS
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/OWNERS
@@ -18,10 +18,10 @@ per-file *Keyguard* = file:../keyguard/OWNERS
per-file *Notification* = file:notification/OWNERS
# Files that control blur effects on shade
per-file *NotificationShadeDepth* = set noparent
-per-file *NotificationShadeDepth* = shanh@google.com, rahulbanerjee@google.com
+per-file *NotificationShadeDepth* = shanh@google.com, rahulbanerjee@google.com, tracyzhou@google.com
per-file *NotificationShadeDepth* = file:../keyguard/OWNERS
per-file *Blur* = set noparent
-per-file *Blur* = shanh@google.com, rahulbanerjee@google.com
+per-file *Blur* = shanh@google.com, rahulbanerjee@google.com, tracyzhou@google.com
# Not setting noparent here, since *Mode* matches many other classes (e.g., *ViewModel*)
per-file *Mode* = file:notification/OWNERS
per-file *SmartReply* = set noparent
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/BundleEntryAdapter.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/BundleEntryAdapter.kt
index 26c302bf6409..b1a26af336d8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/BundleEntryAdapter.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/BundleEntryAdapter.kt
@@ -113,6 +113,10 @@ class BundleEntryAdapter(val entry: BundleEntry) : EntryAdapter {
return false
}
+ override fun isPromotedOngoing(): Boolean {
+ return false
+ }
+
override fun isFullScreenCapable(): Boolean {
return false
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/EntryAdapter.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/EntryAdapter.java
index 3118ce56ac69..4299825bd5e3 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/EntryAdapter.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/EntryAdapter.java
@@ -132,6 +132,11 @@ public interface EntryAdapter {
boolean isAmbient();
+ /**
+ * Returns whether this row represents promoted ongoing notification.
+ */
+ boolean isPromotedOngoing();
+
default boolean isFullScreenCapable() {
return false;
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntryAdapter.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntryAdapter.kt
index 1168c647c26a..345b6aae9673 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntryAdapter.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntryAdapter.kt
@@ -137,6 +137,10 @@ class NotificationEntryAdapter(
return entry.ranking.isAmbient
}
+ override fun isPromotedOngoing(): Boolean {
+ return entry.isPromotedOngoing
+ }
+
override fun isFullScreenCapable(): Boolean {
return entry.sbn.notification.fullScreenIntent != null
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ShadeListBuilder.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ShadeListBuilder.java
index 238ba8d9f490..780e8f47a7fe 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ShadeListBuilder.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ShadeListBuilder.java
@@ -289,6 +289,9 @@ public class ShadeListBuilder implements Dumpable, PipelineDumpable {
mIdToBundleEntry.clear();
for (String id: mNotifBundler.getBundleIds()) {
+ if (BundleCoordinator.debugBundleUi) {
+ Log.i(TAG, "create BundleEntry with id: " + id);
+ }
mIdToBundleEntry.put(id, new BundleEntry(id));
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/BundleCoordinator.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/BundleCoordinator.kt
index 8833ff1ce20c..4478d0e97920 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/BundleCoordinator.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/BundleCoordinator.kt
@@ -98,9 +98,14 @@ class BundleCoordinator @Inject constructor(
object : NotifBundler("NotifBundler") {
// Use list instead of set to keep fixed order
- override val bundleIds: List<String> = SYSTEM_RESERVED_IDS
+ override val bundleIds: List<String> =
+ if (debugBundleUi) SYSTEM_RESERVED_IDS + "notify"
+ else SYSTEM_RESERVED_IDS
override fun getBundleIdOrNull(entry: NotificationEntry?): String? {
+ if (debugBundleUi && entry?.key?.contains("notify") == true) {
+ return "notify"
+ }
return entry?.representativeEntry?.channel?.id?.takeIf { it in this.bundleIds }
}
}
@@ -110,4 +115,9 @@ class BundleCoordinator @Inject constructor(
pipeline.setNotifBundler(bundler)
}
}
+
+ companion object {
+ @kotlin.jvm.JvmField
+ var debugBundleUi: Boolean = true
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/ViewConfigCoordinator.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/ViewConfigCoordinator.kt
index a0a86710b4ba..f43767d3effb 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/ViewConfigCoordinator.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/ViewConfigCoordinator.kt
@@ -22,7 +22,6 @@ import com.android.internal.widget.MessagingGroup
import com.android.internal.widget.MessagingMessage
import com.android.keyguard.KeyguardUpdateMonitor
import com.android.keyguard.KeyguardUpdateMonitorCallback
-import com.android.systemui.Flags
import com.android.systemui.shade.ShadeDisplayAware
import com.android.systemui.statusbar.NotificationLockscreenUserManager
import com.android.systemui.statusbar.NotificationLockscreenUserManager.UserChangedListener
@@ -147,9 +146,7 @@ internal constructor(
traceSection("updateNotifOnUiModeChanged") {
mPipeline?.allNotifs?.forEach { entry ->
entry.row?.onUiModeChanged()
- if (Flags.notificationUndoGutsOnConfigChanged()) {
- mGutsManager.closeAndUndoGuts()
- }
+ mGutsManager.closeAndUndoGuts()
}
}
}
@@ -158,16 +155,7 @@ internal constructor(
colorUpdateLogger.logEvent("VCC.updateNotificationsOnDensityOrFontScaleChanged()")
mPipeline?.allNotifs?.forEach { entry ->
entry.onDensityOrFontScaleChanged()
- if (Flags.notificationUndoGutsOnConfigChanged()) {
- mGutsManager.closeAndUndoGuts()
- } else {
- // This property actually gets reset when the guts are re-inflated, so we're never
- // actually calling onDensityOrFontScaleChanged below.
- val exposedGuts = entry.areGutsExposed()
- if (exposedGuts) {
- mGutsManager.onDensityOrFontScaleChanged(entry)
- }
- }
+ mGutsManager.closeAndUndoGuts()
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/VisualStabilityCoordinator.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/VisualStabilityCoordinator.java
index bdbdc53c4b1c..27765635edcb 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/VisualStabilityCoordinator.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/VisualStabilityCoordinator.java
@@ -39,9 +39,9 @@ import com.android.systemui.shade.domain.interactor.ShadeAnimationInteractor;
import com.android.systemui.shade.domain.interactor.ShadeInteractor;
import com.android.systemui.statusbar.notification.VisibilityLocationProvider;
import com.android.systemui.statusbar.notification.collection.GroupEntry;
-import com.android.systemui.statusbar.notification.collection.PipelineEntry;
import com.android.systemui.statusbar.notification.collection.NotifPipeline;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
+import com.android.systemui.statusbar.notification.collection.PipelineEntry;
import com.android.systemui.statusbar.notification.collection.listbuilder.OnBeforeRenderListListener;
import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifStabilityManager;
import com.android.systemui.statusbar.notification.collection.provider.VisualStabilityProvider;
@@ -113,6 +113,8 @@ public class VisualStabilityCoordinator implements Coordinator, Dumpable {
@VisibleForTesting
protected static final long ALLOW_SECTION_CHANGE_TIMEOUT = 500;
+ private final boolean mCheckLockScreenTransitionEnabled = Flags.checkLockscreenGoneTransition();
+
@Inject
public VisualStabilityCoordinator(
@Background DelayableExecutor delayableExecutor,
@@ -182,7 +184,7 @@ public class VisualStabilityCoordinator implements Coordinator, Dumpable {
this::onTrackingHeadsUpModeChanged);
}
- if (Flags.checkLockscreenGoneTransition()) {
+ if (mCheckLockScreenTransitionEnabled) {
if (SceneContainerFlag.isEnabled()) {
mJavaAdapter.alwaysCollectFlow(mKeyguardTransitionInteractor.isInTransition(
Edge.create(KeyguardState.LOCKSCREEN, Scenes.Gone), null),
@@ -437,7 +439,7 @@ public class VisualStabilityCoordinator implements Coordinator, Dumpable {
boolean wasReorderingAllowed = mReorderingAllowed;
// No need to run notification pipeline when the lockscreen is in fading animation.
mPipelineRunAllowed = !(isPanelCollapsingOrLaunchingActivity()
- || (Flags.checkLockscreenGoneTransition() && mLockscreenInGoneTransition));
+ || (mCheckLockScreenTransitionEnabled && mLockscreenInGoneTransition));
mReorderingAllowed = isReorderingAllowed();
if (wasPipelineRunAllowed != mPipelineRunAllowed
|| wasReorderingAllowed != mReorderingAllowed) {
@@ -566,7 +568,7 @@ public class VisualStabilityCoordinator implements Coordinator, Dumpable {
pw.println("pipelineRunAllowed: " + mPipelineRunAllowed);
pw.println(" notifPanelCollapsing: " + mNotifPanelCollapsing);
pw.println(" launchingNotifActivity: " + mNotifPanelLaunchingActivity);
- if (Flags.checkLockscreenGoneTransition()) {
+ if (mCheckLockScreenTransitionEnabled) {
pw.println(" lockscreenInGoneTransition: " + mLockscreenInGoneTransition);
}
pw.println("reorderingAllowed: " + mReorderingAllowed);
@@ -627,7 +629,7 @@ public class VisualStabilityCoordinator implements Coordinator, Dumpable {
}
private void onLockscreenInGoneTransitionChanged(boolean inGoneTransition) {
- if (!Flags.checkLockscreenGoneTransition()) {
+ if (!mCheckLockScreenTransitionEnabled) {
return;
}
if (inGoneTransition == mLockscreenInGoneTransition) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/ShadeViewDiffer.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/ShadeViewDiffer.kt
index 8021d8f58ccc..a552ca554fd5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/ShadeViewDiffer.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/ShadeViewDiffer.kt
@@ -234,21 +234,21 @@ private class ShadeNode(val controller: NodeController) {
fun getChildCount(): Int = controller.getChildCount()
fun addChildAt(child: ShadeNode, index: Int) {
- traceSection("ShadeNode#addChildAt") {
+ traceSection({ "ShadeNode#${controller::class.simpleName}#addChildAt" }) {
controller.addChildAt(child.controller, index)
child.controller.onViewAdded()
}
}
fun moveChildTo(child: ShadeNode, index: Int) {
- traceSection("ShadeNode#moveChildTo") {
+ traceSection({ "ShadeNode#${controller::class.simpleName}#moveChildTo" }) {
controller.moveChildTo(child.controller, index)
child.controller.onViewMoved()
}
}
fun removeChild(child: ShadeNode, isTransfer: Boolean) {
- traceSection("ShadeNode#removeChild") {
+ traceSection({ "ShadeNode#${controller::class.simpleName}#removeChild" }) {
controller.removeChild(child.controller, isTransfer)
child.controller.onViewRemoved()
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/promoted/AODPromotedNotification.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/promoted/AODPromotedNotification.kt
index 2a01a14f56aa..777392df67cc 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/promoted/AODPromotedNotification.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/promoted/AODPromotedNotification.kt
@@ -19,19 +19,26 @@ package com.android.systemui.statusbar.notification.promoted
import android.app.Flags
import android.app.Flags.notificationsRedesignTemplates
import android.app.Notification
+import android.content.Context
import android.graphics.PorterDuff
import android.util.Log
import android.view.LayoutInflater
import android.view.View
import android.view.View.GONE
+import android.view.View.MeasureSpec.AT_MOST
+import android.view.View.MeasureSpec.EXACTLY
+import android.view.View.MeasureSpec.UNSPECIFIED
+import android.view.View.MeasureSpec.makeMeasureSpec
import android.view.View.VISIBLE
import android.view.ViewGroup.MarginLayoutParams
import android.view.ViewStub
import android.widget.Chronometer
import android.widget.DateTimeView
+import android.widget.FrameLayout
import android.widget.ImageView
import android.widget.ProgressBar
import android.widget.TextView
+import androidx.annotation.DimenRes
import androidx.compose.foundation.BorderStroke
import androidx.compose.foundation.border
import androidx.compose.foundation.layout.Box
@@ -42,7 +49,9 @@ import androidx.compose.runtime.Composable
import androidx.compose.runtime.key
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.SolidColor
+import androidx.compose.ui.platform.LocalDensity
import androidx.compose.ui.res.dimensionResource
+import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp
import androidx.compose.ui.viewinterop.AndroidView
import androidx.core.view.isVisible
@@ -88,22 +97,12 @@ fun AODPromotedNotification(
}
key(content.identity) {
- val sidePaddings = dimensionResource(systemuiR.dimen.notification_side_paddings)
- val sidePaddingValues = PaddingValues(horizontal = sidePaddings, vertical = 0.dp)
-
- val borderStroke = BorderStroke(1.dp, SecondaryText.brush)
-
- val borderRadius = dimensionResource(systemuiR.dimen.notification_corner_radius)
- val borderShape = RoundedCornerShape(borderRadius)
-
- Box(modifier = modifier.padding(sidePaddingValues)) {
- AODPromotedNotificationView(
- layoutResource = layoutResource,
- content = content,
- audiblyAlertedIconVisible = audiblyAlertedIconVisible,
- modifier = Modifier.border(borderStroke, borderShape),
- )
- }
+ AODPromotedNotificationView(
+ layoutResource = layoutResource,
+ content = content,
+ audiblyAlertedIconVisible = audiblyAlertedIconVisible,
+ modifier = modifier,
+ )
}
}
@@ -114,27 +113,91 @@ fun AODPromotedNotificationView(
audiblyAlertedIconVisible: Boolean,
modifier: Modifier = Modifier,
) {
- AndroidView(
- factory = { context ->
- val view =
- traceSection("$TAG.inflate") {
- LayoutInflater.from(context).inflate(layoutResource, /* root= */ null)
- }
-
- val updater =
- traceSection("$TAG.findViews") { AODPromotedNotificationViewUpdater(view) }
-
- view.setTag(viewUpdaterTagId, updater)
-
- view
- },
- update = { view ->
- val updater = view.getTag(viewUpdaterTagId) as AODPromotedNotificationViewUpdater
-
- traceSection("$TAG.update") { updater.update(content, audiblyAlertedIconVisible) }
- },
- modifier = modifier,
- )
+ val sidePaddings = dimensionResource(systemuiR.dimen.notification_side_paddings)
+ val sidePaddingValues = PaddingValues(horizontal = sidePaddings, vertical = 0.dp)
+
+ val boxModifier = modifier.padding(sidePaddingValues)
+
+ val borderStroke = BorderStroke(1.dp, SecondaryText.brush)
+
+ val borderRadius = dimensionResource(systemuiR.dimen.notification_corner_radius)
+ val borderShape = RoundedCornerShape(borderRadius)
+
+ val maxHeight =
+ with(LocalDensity.current) {
+ scaledFontHeight(systemuiR.dimen.notification_max_height_for_promoted_ongoing)
+ .toPx()
+ }
+ .toInt()
+
+ val viewModifier = Modifier.border(borderStroke, borderShape)
+
+ Box(modifier = boxModifier) {
+ AndroidView(
+ factory = { context ->
+ val notif =
+ traceSection("$TAG.inflate") {
+ LayoutInflater.from(context).inflate(layoutResource, /* root= */ null)
+ }
+ val updater =
+ traceSection("$TAG.findViews") { AODPromotedNotificationViewUpdater(notif) }
+
+ val frame = FrameLayoutWithMaxHeight(maxHeight, context)
+ frame.addView(notif)
+ frame.setTag(viewUpdaterTagId, updater)
+
+ frame
+ },
+ update = { frame ->
+ val updater = frame.getTag(viewUpdaterTagId) as AODPromotedNotificationViewUpdater
+
+ traceSection("$TAG.update") { updater.update(content, audiblyAlertedIconVisible) }
+ frame.maxHeight = maxHeight
+ },
+ modifier = viewModifier,
+ )
+ }
+}
+
+private class FrameLayoutWithMaxHeight(maxHeight: Int, context: Context) : FrameLayout(context) {
+ var maxHeight = maxHeight
+ set(value) {
+ if (field != value) {
+ field = value
+ requestLayout()
+ }
+ }
+
+ // This mirrors the logic in NotificationContentView.onMeasure.
+ override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
+ if (childCount < 1) {
+ return
+ }
+
+ val child = getChildAt(0)
+ val childLayoutHeight = child.layoutParams.height
+ val childHeightSpec =
+ if (childLayoutHeight >= 0) {
+ makeMeasureSpec(maxHeight.coerceAtMost(childLayoutHeight), EXACTLY)
+ } else {
+ makeMeasureSpec(maxHeight, AT_MOST)
+ }
+ measureChildWithMargins(child, widthMeasureSpec, 0, childHeightSpec, 0)
+ val childMeasuredHeight = child.measuredHeight
+
+ val ownHeightMode = MeasureSpec.getMode(heightMeasureSpec)
+ val ownHeightSize = MeasureSpec.getSize(heightMeasureSpec)
+
+ val ownMeasuredWidth = MeasureSpec.getSize(widthMeasureSpec)
+ val ownMeasuredHeight =
+ if (ownHeightMode != UNSPECIFIED) {
+ childMeasuredHeight.coerceAtMost(ownHeightSize)
+ } else {
+ childMeasuredHeight
+ }
+
+ setMeasuredDimension(ownMeasuredWidth, ownMeasuredHeight)
+ }
}
private val PromotedNotificationContentModel.layoutResource: Int?
@@ -521,6 +584,11 @@ private enum class AodPromotedNotificationColor(val colorInt: Int) {
val brush = SolidColor(androidx.compose.ui.graphics.Color(colorInt))
}
+@Composable
+private fun scaledFontHeight(@DimenRes dimenId: Int): Dp {
+ return dimensionResource(dimenId) * LocalDensity.current.fontScale.coerceAtLeast(1f)
+}
+
private val viewUpdaterTagId = systemuiR.id.aod_promoted_notification_view_updater_tag
private const val TAG = "AODPromotedNotification"
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/promoted/domain/interactor/PackageDemotionInteractor.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/promoted/domain/interactor/PackageDemotionInteractor.kt
new file mode 100644
index 000000000000..14295357c54f
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/promoted/domain/interactor/PackageDemotionInteractor.kt
@@ -0,0 +1,39 @@
+/*
+ * 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.statusbar.notification.promoted.domain.interactor
+
+import com.android.systemui.dagger.SysUISingleton
+import javax.inject.Inject
+
+/** A class which can receive both a demotion signal and a single handler of that signal */
+@SysUISingleton
+class PackageDemotionInteractor @Inject constructor() {
+ private var demotionSignalHandler: ((packageName: String, uid: Int) -> Unit)? = null
+
+ /**
+ * called after sending a the demotion signal to
+ * [android.app.INotificationManager.setCanBePromoted]
+ */
+ fun onPackageDemoted(packageName: String, uid: Int) {
+ demotionSignalHandler?.invoke(packageName, uid)
+ }
+
+ /** sets the [handler] that will be called when [onPackageDemoted] is called. */
+ fun setOnPackageDemotionHandler(handler: (packageName: String, uid: Int) -> Unit) {
+ demotionSignalHandler = handler
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationView.java
index 689222608abe..e76867373139 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationView.java
@@ -40,6 +40,7 @@ import android.view.animation.Interpolator;
import com.android.app.animation.Interpolators;
import com.android.internal.jank.InteractionJankMonitor;
import com.android.internal.jank.InteractionJankMonitor.Configuration;
+import com.android.systemui.Flags;
import com.android.systemui.Gefingerpoken;
import com.android.systemui.common.shared.colors.SurfaceEffectColors;
import com.android.systemui.res.R;
@@ -101,7 +102,8 @@ public abstract class ActivatableNotificationView extends ExpandableOutlineView
private ValueAnimator mBackgroundColorAnimator;
private float mAppearAnimationFraction = -1.0f;
private float mAppearAnimationTranslation;
- private int mNormalColor;
+ protected int mNormalColor;
+ protected int mOpaqueColor;
private boolean mIsBelowSpeedBump;
private long mLastActionUpTime;
@@ -130,17 +132,13 @@ public abstract class ActivatableNotificationView extends ExpandableOutlineView
protected void updateColors() {
if (notificationRowTransparency()) {
- if (mIsBlurSupported) {
- mNormalColor = SurfaceEffectColors.surfaceEffect1(getContext());
- } else {
- mNormalColor = mContext.getColor(
- com.android.internal.R.color.materialColorSurfaceContainer);
- }
+ mNormalColor = SurfaceEffectColors.surfaceEffect1(getContext());
+ mOpaqueColor = mContext.getColor(
+ com.android.internal.R.color.materialColorSurfaceContainer);
} else {
mNormalColor = mContext.getColor(
com.android.internal.R.color.materialColorSurfaceContainerHigh);
}
- setBackgroundToNormalColor();
mTintedRippleColor = mContext.getColor(
R.color.notification_ripple_tinted_color);
mNormalRippleColor = mContext.getColor(
@@ -151,12 +149,6 @@ public abstract class ActivatableNotificationView extends ExpandableOutlineView
mOverrideAmount = 0.0f;
}
- private void setBackgroundToNormalColor() {
- if (mBackgroundNormal != null) {
- mBackgroundNormal.setNormalColor(mNormalColor);
- }
- }
-
/**
* Reload background colors from resources and invalidate views.
*/
@@ -186,7 +178,6 @@ public abstract class ActivatableNotificationView extends ExpandableOutlineView
mBackgroundNormal = findViewById(R.id.backgroundNormal);
mFakeShadow = findViewById(R.id.fake_shadow);
mShadowHidden = mFakeShadow.getVisibility() != VISIBLE;
- setBackgroundToNormalColor();
initBackground();
updateBackgroundTint();
updateOutlineAlpha();
@@ -352,7 +343,7 @@ public abstract class ActivatableNotificationView extends ExpandableOutlineView
}
protected boolean usesTransparentBackground() {
- return mIsBlurSupported && notificationRowTransparency();
+ return false;
}
@Override
@@ -709,7 +700,11 @@ public abstract class ActivatableNotificationView extends ExpandableOutlineView
if (withTint && mBgTint != NO_COLOR) {
return mBgTint;
} else {
- return mNormalColor;
+ if (Flags.notificationRowTransparency()) {
+ return usesTransparentBackground() ? mNormalColor : mOpaqueColor;
+ } else {
+ return mNormalColor;
+ }
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
index 8da2f768bf71..76830646587d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
@@ -82,6 +82,7 @@ import androidx.dynamicanimation.animation.SpringAnimation;
import com.android.app.animation.Interpolators;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.graphics.ColorUtils;
import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.UiEventLogger;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
@@ -979,7 +980,7 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
} else if (isAboveShelf() != wasAboveShelf) {
mAboveShelfChangedListener.onAboveShelfStateChanged(!wasAboveShelf);
}
- updateColors();
+ updateBackgroundTint();
}
/**
@@ -1678,20 +1679,34 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
@Override
protected void setBackgroundTintColor(int color) {
- super.setBackgroundTintColor(color);
- NotificationContentView view = getShowingLayout();
- if (view != null) {
- view.setBackgroundTintColor(color);
- }
- if (notificationRowTransparency() && mBackgroundNormal != null) {
+ if (notificationRowTransparency()) {
+ boolean isColorized = false;
if (NotificationBundleUi.isEnabled() && mEntryAdapter != null) {
- mBackgroundNormal.setBgIsColorized(mEntryAdapter.isColorized());
+ isColorized = mEntryAdapter.isColorized();
} else {
if (mEntry != null) {
- mBackgroundNormal.setBgIsColorized(
- mEntry.getSbn().getNotification().isColorized());
+ isColorized = mEntry.getSbn().getNotification().isColorized();
}
}
+ boolean isTransparent = usesTransparentBackground();
+ if (isColorized) {
+ // For colorized notifications, use a color that matches the tint color at 90% alpha
+ // when the row is transparent.
+ color = ColorUtils.setAlphaComponent(
+ color, (int) (0xFF * (isTransparent ? 0.9f : 1)));
+ } else {
+ // For non-colorized notifications, use the semi-transparent normal color token
+ // when the row is transparent, and the opaque color token otherwise.
+ if (!isTransparent && mBgTint == NO_COLOR) {
+ color = mOpaqueColor;
+ }
+ }
+ }
+
+ super.setBackgroundTintColor(color);
+ NotificationContentView view = getShowingLayout();
+ if (view != null) {
+ view.setBackgroundTintColor(color);
}
}
@@ -3113,7 +3128,7 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
mChildrenContainer.setOnKeyguard(onKeyguard);
}
}
- updateColors();
+ updateBackgroundTint();
}
}
@@ -3243,12 +3258,19 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
return false;
}
- final NotificationEntry entry = mEntry;
- if (entry == null) {
- return false;
+ if (NotificationBundleUi.isEnabled()) {
+ final EntryAdapter entryAdapter = mEntryAdapter;
+ if (entryAdapter == null) {
+ return false;
+ }
+ return entryAdapter.isPromotedOngoing();
+ } else {
+ final NotificationEntry entry = mEntry;
+ if (entry == null) {
+ return false;
+ }
+ return entry.isPromotedOngoing();
}
-
- return entry.isPromotedOngoing();
}
private boolean isPromotedNotificationExpanded(boolean allowOnKeyguard) {
@@ -4633,6 +4655,10 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
@Override
protected boolean usesTransparentBackground() {
- return super.usesTransparentBackground() && !mIsHeadsUp && !mOnKeyguard;
+ // Row background should be opaque when it's displayed as a heads-up notification or
+ // displayed on keyguard.
+ // TODO(b/388891313): Account for isBlurSupported when it is initialized and updated
+ // correctly.
+ return notificationRowTransparency() && !mIsHeadsUp && !mOnKeyguard;
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationBackgroundView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationBackgroundView.java
index 4914e1073059..e4997e4f53ad 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationBackgroundView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationBackgroundView.java
@@ -36,9 +36,9 @@ import android.view.View;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
-import com.android.internal.graphics.ColorUtils;
import com.android.internal.util.ContrastColorUtil;
import com.android.systemui.Dumpable;
+import com.android.systemui.common.shared.colors.SurfaceEffectColors;
import com.android.systemui.res.R;
import com.android.systemui.statusbar.notification.shared.NotificationAddXOnHoverToDismiss;
import com.android.systemui.util.DrawableDumpKt;
@@ -52,7 +52,6 @@ import java.util.Arrays;
public class NotificationBackgroundView extends View implements Dumpable,
ExpandableNotificationRow.DismissButtonTargetVisibilityListener {
- private static final int MAX_ALPHA = 0xFF;
private final boolean mDontModifyCorners;
private Drawable mBackground;
private int mClipTopAmount;
@@ -73,8 +72,6 @@ public class NotificationBackgroundView extends View implements Dumpable,
private final ColorStateList mLightColoredStatefulColors;
private final ColorStateList mDarkColoredStatefulColors;
private int mNormalColor;
- private boolean mBgIsColorized = false;
- private boolean mForceOpaque = false;
private final int convexR = 9;
private final int concaveR = 22;
@@ -88,13 +85,15 @@ public class NotificationBackgroundView extends View implements Dumpable,
R.color.notification_state_color_light);
mDarkColoredStatefulColors = getResources().getColorStateList(
R.color.notification_state_color_dark);
+ if (notificationRowTransparency()) {
+ mNormalColor = SurfaceEffectColors.surfaceEffect1(getContext());
+ } else {
+ mNormalColor = mContext.getColor(
+ com.android.internal.R.color.materialColorSurfaceContainerHigh);
+ }
mFocusOverlayStroke = getResources().getDimension(R.dimen.notification_focus_stroke_width);
}
- public void setNormalColor(int color) {
- mNormalColor = color;
- }
-
@Override
public void onTargetVisibilityChanged(boolean targetVisible) {
if (NotificationAddXOnHoverToDismiss.isUnexpectedlyInLegacyMode()) {
@@ -140,21 +139,6 @@ public class NotificationBackgroundView extends View implements Dumpable,
}
}
- /**
- * A way to tell whether the background has been colorized.
- */
- public boolean isColorized() {
- return mBgIsColorized;
- }
-
- /**
- * A way to inform this class whether the background has been colorized.
- * We need to know this, in order to *not* override that color.
- */
- public void setBgIsColorized(boolean b) {
- mBgIsColorized = b;
- }
-
private Path calculateDismissButtonCutoutPath(Rect backgroundBounds) {
// TODO(b/365585705): Adapt to RTL after the UX design is finalized.
@@ -311,28 +295,21 @@ public class NotificationBackgroundView extends View implements Dumpable,
return ((LayerDrawable) mBackground).getDrawable(1);
}
- private void updateBaseLayerColor() {
- // BG base layer being a drawable, there isn't a method like setColor() to color it.
- // Instead, we set a color filter that essentially replaces every pixel of the drawable.
- // For non-colorized notifications, this function specifies a new color token.
- // For colorized notifications, this uses a color that matches the tint color at 90% alpha.
- int color = isColorized()
- ? ColorUtils.setAlphaComponent(mTintColor, (int) (MAX_ALPHA * 0.9f))
- : mNormalColor;
- getBaseBackgroundLayer().setColorFilter(
- new PorterDuffColorFilter(
- color,
- PorterDuff.Mode.SRC)); // SRC operator discards the drawable's color+alpha
- }
-
public void setTint(int tintColor) {
Drawable baseLayer = getBaseBackgroundLayer();
- baseLayer.mutate().setTintMode(PorterDuff.Mode.SRC_ATOP);
- baseLayer.setTint(tintColor);
- mTintColor = tintColor;
if (notificationRowTransparency()) {
- updateBaseLayerColor();
+ // BG base layer being a drawable, there isn't a method like setColor() to color it.
+ // Instead, we set a color filter that essentially replaces every pixel of the drawable.
+ baseLayer.setColorFilter(
+ new PorterDuffColorFilter(
+ tintColor,
+ // SRC operator discards the drawable's color+alpha
+ PorterDuff.Mode.SRC));
+ } else {
+ baseLayer.mutate().setTintMode(PorterDuff.Mode.SRC_ATOP);
+ baseLayer.setTint(tintColor);
}
+ mTintColor = tintColor;
setStatefulColors();
invalidate();
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java
index db2ee2e3aad3..e9993ae31514 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java
@@ -269,6 +269,7 @@ public class NotificationContentView extends FrameLayout implements Notification
mNotificationMaxHeight = maxHeight;
}
+ // This logic is mirrored in FrameLayoutWithMaxHeight.onMeasure in AODPromotedNotification.kt.
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int heightMode = MeasureSpec.getMode(heightMeasureSpec);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGutsManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGutsManager.java
index 9a75253295d5..cdb78d99538b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGutsManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGutsManager.java
@@ -47,7 +47,6 @@ import com.android.internal.logging.nano.MetricsProto;
import com.android.internal.statusbar.IStatusBarService;
import com.android.settingslib.notification.ConversationIconFactory;
import com.android.systemui.CoreStartable;
-import com.android.systemui.Flags;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.dagger.qualifiers.Background;
import com.android.systemui.dagger.qualifiers.Main;
@@ -71,6 +70,7 @@ import com.android.systemui.statusbar.notification.collection.provider.HighPrior
import com.android.systemui.statusbar.notification.collection.render.NotifGutsViewListener;
import com.android.systemui.statusbar.notification.collection.render.NotifGutsViewManager;
import com.android.systemui.statusbar.notification.headsup.HeadsUpManager;
+import com.android.systemui.statusbar.notification.promoted.domain.interactor.PackageDemotionInteractor;
import com.android.systemui.statusbar.notification.row.icon.AppIconProvider;
import com.android.systemui.statusbar.notification.row.icon.NotificationIconStyleProvider;
import com.android.systemui.statusbar.notification.shared.NotificationBundleUi;
@@ -100,6 +100,7 @@ public class NotificationGutsManager implements NotifGutsViewManager, CoreStarta
private final AccessibilityManager mAccessibilityManager;
private final HighPriorityProvider mHighPriorityProvider;
private final ChannelEditorDialogController mChannelEditorDialogController;
+ private final PackageDemotionInteractor mPackageDemotionInteractor;
private final OnUserInteractionCallback mOnUserInteractionCallback;
// Dependencies:
@@ -155,6 +156,7 @@ public class NotificationGutsManager implements NotifGutsViewManager, CoreStarta
LauncherApps launcherApps,
ShortcutManager shortcutManager,
ChannelEditorDialogController channelEditorDialogController,
+ PackageDemotionInteractor packageDemotionInteractor,
UserContextProvider contextTracker,
AssistantFeedbackController assistantFeedbackController,
Optional<BubblesManager> bubblesManagerOptional,
@@ -184,6 +186,7 @@ public class NotificationGutsManager implements NotifGutsViewManager, CoreStarta
mShortcutManager = shortcutManager;
mContextTracker = contextTracker;
mChannelEditorDialogController = channelEditorDialogController;
+ mPackageDemotionInteractor = packageDemotionInteractor;
mAssistantFeedbackController = assistantFeedbackController;
mBubblesManagerOptional = bubblesManagerOptional;
mUiEventLogger = uiEventLogger;
@@ -231,15 +234,6 @@ public class NotificationGutsManager implements NotifGutsViewManager, CoreStarta
}
}
- public void onDensityOrFontScaleChanged(NotificationEntry entry) {
- if (!Flags.notificationUndoGutsOnConfigChanged()) {
- Log.wtf(TAG, "onDensityOrFontScaleChanged should not be called if"
- + " notificationUndoGutsOnConfigChanged is off");
- }
- setExposedGuts(entry.getGuts());
- bindGuts(entry.getRow());
- }
-
/**
* Sends an intent to open the notification settings for a particular package and optional
* channel.
@@ -291,11 +285,6 @@ public class NotificationGutsManager implements NotifGutsViewManager, CoreStarta
mNotificationActivityStarter.startNotificationGutsIntent(intent, uid, row);
}
- private boolean bindGuts(final ExpandableNotificationRow row) {
- row.ensureGutsInflated();
- return bindGuts(row, mGutsMenuItem);
- }
-
@VisibleForTesting
protected boolean bindGuts(final ExpandableNotificationRow row,
NotificationMenuRowPlugin.MenuItem item) {
@@ -429,6 +418,7 @@ public class NotificationGutsManager implements NotifGutsViewManager, CoreStarta
mIconStyleProvider,
mOnUserInteractionCallback,
mChannelEditorDialogController,
+ mPackageDemotionInteractor,
packageName,
row.getEntry().getChannel(),
row.getEntry(),
@@ -440,6 +430,7 @@ public class NotificationGutsManager implements NotifGutsViewManager, CoreStarta
NotificationBundleUi.isEnabled()
? !row.getEntry().isBlockable()
: row.getIsNonblockable(),
+ row.canViewBeDismissed(),
mHighPriorityProvider.isHighPriority(row.getEntry()),
mAssistantFeedbackController,
mMetricsLogger,
@@ -605,6 +596,7 @@ public class NotificationGutsManager implements NotifGutsViewManager, CoreStarta
return mNotificationGutsExposed;
}
+ @VisibleForTesting
public void setExposedGuts(NotificationGuts guts) {
mNotificationGutsExposed = guts;
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInfo.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInfo.java
index 661122510c6c..b6f4ffce8e00 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInfo.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInfo.java
@@ -74,6 +74,7 @@ import com.android.systemui.Dependency;
import com.android.systemui.res.R;
import com.android.systemui.statusbar.notification.AssistantFeedbackController;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
+import com.android.systemui.statusbar.notification.promoted.domain.interactor.PackageDemotionInteractor;
import com.android.systemui.statusbar.notification.row.icon.AppIconProvider;
import com.android.systemui.statusbar.notification.row.icon.NotificationIconStyleProvider;
@@ -120,6 +121,7 @@ public class NotificationInfo extends LinearLayout implements NotificationGuts.G
private boolean mIsAutomaticChosen;
private boolean mIsSingleDefaultChannel;
private boolean mIsNonblockable;
+ private boolean mIsDismissable;
private NotificationEntry mEntry;
private StatusBarNotification mSbn;
private boolean mIsDeviceProvisioned;
@@ -160,6 +162,7 @@ public class NotificationInfo extends LinearLayout implements NotificationGuts.G
mPressedApply = true;
mGutsContainer.closeControls(v, /* save= */ true);
};
+ private OnClickListener mOnCloseClickListener;
public NotificationInfo(Context context, AttributeSet attrs) {
super(context, attrs);
@@ -193,6 +196,7 @@ public class NotificationInfo extends LinearLayout implements NotificationGuts.G
NotificationIconStyleProvider iconStyleProvider,
OnUserInteractionCallback onUserInteractionCallback,
ChannelEditorDialogController channelEditorDialogController,
+ PackageDemotionInteractor packageDemotionInteractor,
String pkg,
NotificationChannel notificationChannel,
NotificationEntry entry,
@@ -202,6 +206,7 @@ public class NotificationInfo extends LinearLayout implements NotificationGuts.G
UiEventLogger uiEventLogger,
boolean isDeviceProvisioned,
boolean isNonblockable,
+ boolean isDismissable,
boolean wasShownHighPriority,
AssistantFeedbackController assistantFeedbackController,
MetricsLogger metricsLogger,
@@ -226,11 +231,13 @@ public class NotificationInfo extends LinearLayout implements NotificationGuts.G
mStartingChannelImportance = mSingleNotificationChannel.getImportance();
mWasShownHighPriority = wasShownHighPriority;
mIsNonblockable = isNonblockable;
+ mIsDismissable = isDismissable;
mAppUid = mSbn.getUid();
mDelegatePkg = mSbn.getOpPkg();
mIsDeviceProvisioned = isDeviceProvisioned;
mShowAutomaticSetting = mAssistantFeedbackController.isFeedbackEnabled();
mUiEventLogger = uiEventLogger;
+ mOnCloseClickListener = onCloseClick;
mIsSystemRegisteredCall = mSbn.getNotification().isStyle(Notification.CallStyle.class)
&& mINotificationManager.isInCall(mSbn.getPackageName(), mSbn.getUid());
@@ -277,6 +284,11 @@ public class NotificationInfo extends LinearLayout implements NotificationGuts.G
turnOffButton.setVisibility(turnOffButton.hasOnClickListeners() && !mIsNonblockable
? VISIBLE : GONE);
+ View dismissButton = findViewById(R.id.inline_dismiss);
+ dismissButton.setOnClickListener(mOnCloseClickListener);
+ dismissButton.setVisibility(dismissButton.hasOnClickListeners() && mIsDismissable
+ ? VISIBLE : GONE);
+
View done = findViewById(R.id.done);
done.setOnClickListener(mOnDismissSettings);
done.setAccessibilityDelegate(mGutsContainer.getAccessibilityDelegate());
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationSnooze.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationSnooze.java
index 83897f5bc3a7..cec0ae696b26 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationSnooze.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationSnooze.java
@@ -51,7 +51,6 @@ import com.android.app.animation.Interpolators;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
-import com.android.systemui.Flags;
import com.android.systemui.plugins.statusbar.NotificationSwipeActionHelper;
import com.android.systemui.plugins.statusbar.NotificationSwipeActionHelper.SnoozeOption;
import com.android.systemui.res.R;
@@ -477,7 +476,7 @@ public class NotificationSnooze extends LinearLayout
@Override
public boolean handleCloseControls(boolean save, boolean force) {
- if (Flags.notificationUndoGutsOnConfigChanged() && !save) {
+ if (!save) {
// Undo changes and let the guts handle closing the view
mSelectedOption = null;
showSnoozeOptions(false);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/PromotedNotificationInfo.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/PromotedNotificationInfo.java
index 6ff711deeb01..01ee788f7fd7 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/PromotedNotificationInfo.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/PromotedNotificationInfo.java
@@ -31,6 +31,7 @@ import com.android.internal.logging.UiEventLogger;
import com.android.systemui.res.R;
import com.android.systemui.statusbar.notification.AssistantFeedbackController;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
+import com.android.systemui.statusbar.notification.promoted.domain.interactor.PackageDemotionInteractor;
import com.android.systemui.statusbar.notification.row.icon.AppIconProvider;
import com.android.systemui.statusbar.notification.row.icon.NotificationIconStyleProvider;
@@ -42,6 +43,7 @@ import com.android.systemui.statusbar.notification.row.icon.NotificationIconStyl
public class PromotedNotificationInfo extends NotificationInfo {
private static final String TAG = "PromotedNotifInfoGuts";
private INotificationManager mNotificationManager;
+ private PackageDemotionInteractor mPackageDemotionInteractor;
private NotificationGuts mGutsContainer;
public PromotedNotificationInfo(Context context, AttributeSet attrs) {
@@ -56,6 +58,7 @@ public class PromotedNotificationInfo extends NotificationInfo {
NotificationIconStyleProvider iconStyleProvider,
OnUserInteractionCallback onUserInteractionCallback,
ChannelEditorDialogController channelEditorDialogController,
+ PackageDemotionInteractor packageDemotionInteractor,
String pkg,
NotificationChannel notificationChannel,
NotificationEntry entry,
@@ -65,16 +68,19 @@ public class PromotedNotificationInfo extends NotificationInfo {
UiEventLogger uiEventLogger,
boolean isDeviceProvisioned,
boolean isNonblockable,
+ boolean isDismissable,
boolean wasShownHighPriority,
AssistantFeedbackController assistantFeedbackController,
MetricsLogger metricsLogger, OnClickListener onCloseClick) throws RemoteException {
super.bindNotification(pm, iNotificationManager, appIconProvider, iconStyleProvider,
- onUserInteractionCallback, channelEditorDialogController, pkg, notificationChannel,
+ onUserInteractionCallback, channelEditorDialogController, packageDemotionInteractor,
+ pkg, notificationChannel,
entry, onSettingsClick, onAppSettingsClick, feedbackClickListener, uiEventLogger,
- isDeviceProvisioned, isNonblockable, wasShownHighPriority,
+ isDeviceProvisioned, isNonblockable, isDismissable, wasShownHighPriority,
assistantFeedbackController, metricsLogger, onCloseClick);
mNotificationManager = iNotificationManager;
+ mPackageDemotionInteractor = packageDemotionInteractor;
bindDemote(entry.getSbn(), pkg);
}
@@ -94,8 +100,8 @@ public class PromotedNotificationInfo extends NotificationInfo {
private OnClickListener getDemoteClickListener(StatusBarNotification sbn, String packageName) {
return ((View v) -> {
try {
- // TODO(b/391661009): Signal AutomaticPromotionCoordinator here
mNotificationManager.setCanBePromoted(packageName, sbn.getUid(), false, true);
+ mPackageDemotionInteractor.onPackageDemoted(packageName, sbn.getUid());
mGutsContainer.closeControls(v, true);
} catch (RemoteException e) {
Log.e(TAG, "Couldn't revoke live update permission", e);
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 e6bb1b9f0273..a5f711050c46 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
@@ -64,6 +64,7 @@ import android.util.AttributeSet;
import android.util.IndentingPrintWriter;
import android.util.Log;
import android.util.MathUtils;
+import android.util.Pair;
import android.view.DisplayCutout;
import android.view.InputDevice;
import android.view.LayoutInflater;
@@ -140,6 +141,7 @@ import com.android.systemui.statusbar.notification.stack.ui.view.NotificationScr
import com.android.systemui.statusbar.phone.HeadsUpAppearanceController;
import com.android.systemui.statusbar.policy.ScrollAdapter;
import com.android.systemui.statusbar.policy.SplitShadeStateController;
+import com.android.systemui.statusbar.ui.SystemBarUtilsProxy;
import com.android.systemui.util.Assert;
import com.android.systemui.util.ColorUtilKt;
import com.android.systemui.util.DumpUtilsKt;
@@ -3852,8 +3854,6 @@ public class NotificationStackScrollLayout
// existing overScroll, we have to scroll the view
customOverScrollBy((int) scrollAmount, getOwnScrollY(),
range, getHeight() / 2);
- // If we're scrolling, leavebehinds should be dismissed
- mController.checkSnoozeLeavebehind();
}
}
break;
@@ -6002,7 +6002,6 @@ public class NotificationStackScrollLayout
* LockscreenShadeTransitionController resets fraction to 0
* where it remains until the next lockscreen-to-shade transition.
*/
- @Override
public void setFractionToShade(float fraction) {
mAmbientState.setFractionToShade(fraction);
updateContentHeight(); // Recompute stack height with different section gap.
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java
index f7f8acf5fda9..bb3abc1fba38 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java
@@ -1735,7 +1735,6 @@ public class NotificationStackScrollLayoutController implements Dumpable {
* they remain until the next lockscreen-to-shade transition.
*/
public void setTransitionToFullShadeAmount(float fraction) {
- SceneContainerFlag.assertInLegacyMode();
mView.setFractionToShade(fraction);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/view/NotificationScrollView.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/view/NotificationScrollView.kt
index ac89f3a63fcd..9c855e9cd9b7 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/view/NotificationScrollView.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/view/NotificationScrollView.kt
@@ -107,9 +107,6 @@ interface NotificationScrollView {
/** sets the current expand fraction */
fun setExpandFraction(expandFraction: Float)
- /** Sets the fraction of the LockScreen -> Shade transition. */
- fun setFractionToShade(fraction: Float)
-
/** sets the current QS expand fraction */
fun setQsExpandFraction(expandFraction: Float)
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/NotificationScrollViewBinder.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/NotificationScrollViewBinder.kt
index 40739b386d20..653344ae9203 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/NotificationScrollViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/NotificationScrollViewBinder.kt
@@ -95,11 +95,6 @@ constructor(
view.setExpandFraction(it.coerceIn(0f, 1f))
}
}
- launch {
- viewModel.lockScreenToShadeTransitionProgress.collectTraced {
- view.setFractionToShade(it.coerceIn(0f, 1f))
- }
- }
launch { viewModel.qsExpandFraction.collectTraced { view.setQsExpandFraction(it) } }
launch { viewModel.blurRadius(maxBlurRadius).collect(view::setBlurRadius) }
launch {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationScrollViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationScrollViewModel.kt
index 940b2e541758..c1aa5f12aa99 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationScrollViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationScrollViewModel.kt
@@ -207,44 +207,6 @@ constructor(
val qsExpandFraction: Flow<Float> =
shadeInteractor.qsExpansion.dumpWhileCollecting("qsExpandFraction")
- /**
- * Fraction of the LockScreen -> Shade transition. 0..1 while the transition in progress, and
- * snaps back to 0 when it is Idle.
- */
- val lockScreenToShadeTransitionProgress: Flow<Float> =
- combine(
- shadeInteractor.shadeExpansion,
- shadeModeInteractor.shadeMode,
- sceneInteractor.transitionState,
- ) { shadeExpansion, _, transitionState ->
- when (transitionState) {
- is Idle -> 0f
- is ChangeScene ->
- if (
- transitionState.isTransitioning(
- from = Scenes.Lockscreen,
- to = Scenes.Shade,
- )
- ) {
- shadeExpansion
- } else {
- 0f
- }
-
- is Transition.OverlayTransition ->
- if (
- transitionState.currentScene == Scenes.Lockscreen &&
- transitionState.isTransitioning(to = Overlays.NotificationsShade)
- ) {
- shadeExpansion
- } else {
- 0f
- }
- }
- }
- .distinctUntilChanged()
- .dumpWhileCollecting("lockScreenToShadeTransitionProgress")
-
val isOccluded: Flow<Boolean> =
bouncerInteractor.bouncerExpansion
.map { it == 1f }
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 ba666512af5a..2f9cff46d687 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
@@ -196,9 +196,8 @@ fun StatusBarRoot(
setContent {
PlatformTheme {
- val chipsVisibilityModel by
+ val chipsVisibilityModel =
statusBarViewModel.ongoingActivityChips
- .collectAsStateWithLifecycle()
if (chipsVisibilityModel.areChipsAllowed) {
OngoingActivityChips(
chips = chipsVisibilityModel.chips,
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 c717b180575c..540babad5dd1 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
@@ -21,6 +21,8 @@ import android.graphics.Rect
import android.view.Display
import android.view.View
import androidx.compose.runtime.getValue
+import com.android.app.tracing.FlowTracing.traceEach
+import com.android.app.tracing.TrackGroupUtils.trackGroup
import com.android.app.tracing.coroutines.launchTraced as launch
import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
@@ -130,7 +132,7 @@ interface HomeStatusBarViewModel : Activatable {
val primaryOngoingActivityChip: StateFlow<OngoingActivityChipModel>
/** All supported activity chips, whether they are currently active or not. */
- val ongoingActivityChips: StateFlow<ChipsVisibilityModel>
+ val ongoingActivityChips: ChipsVisibilityModel
/**
* The multiple ongoing activity chips that should be shown on the left-hand side of the status
@@ -386,11 +388,9 @@ constructor(
}
override val isHomeStatusBarAllowed =
- isHomeStatusBarAllowedCompat.stateIn(
- bgScope,
- SharingStarted.WhileSubscribed(),
- initialValue = false,
- )
+ isHomeStatusBarAllowedCompat
+ .traceEach(trackGroup(TRACK_GROUP, "isHomeStatusBarAllowed"), logcat = true)
+ .stateIn(bgScope, SharingStarted.WhileSubscribed(), initialValue = false)
private val shouldHomeStatusBarBeVisible =
combine(
@@ -461,24 +461,29 @@ constructor(
isHomeStatusBarAllowed && !isSecureCameraActive && !hideStartSideContentForHeadsUp
}
- override val ongoingActivityChips =
+ private val chipsVisibilityModel: Flow<ChipsVisibilityModel> =
combine(ongoingActivityChipsViewModel.chips, canShowOngoingActivityChips) { chips, canShow
->
ChipsVisibilityModel(chips, areChipsAllowed = canShow)
}
- .stateIn(
- bgScope,
- SharingStarted.WhileSubscribed(),
- initialValue =
- ChipsVisibilityModel(
- chips = MultipleOngoingActivityChipsModel(),
- areChipsAllowed = false,
- ),
- )
+ .traceEach(trackGroup(TRACK_GROUP, "chips"), logcat = true) {
+ "Chips[allowed=${it.areChipsAllowed} numChips=${it.chips.active.size}]"
+ }
+
+ override val ongoingActivityChips: ChipsVisibilityModel by
+ hydrator.hydratedStateOf(
+ traceName = "ongoingActivityChips",
+ initialValue =
+ ChipsVisibilityModel(
+ chips = MultipleOngoingActivityChipsModel(),
+ areChipsAllowed = false,
+ ),
+ source = chipsVisibilityModel,
+ )
private val hasOngoingActivityChips =
if (StatusBarChipsModernization.isEnabled) {
- ongoingActivityChips.map { it.chips.active.any { chip -> !chip.isHidden } }
+ chipsVisibilityModel.map { it.chips.active.any { chip -> !chip.isHidden } }
} else if (StatusBarNotifChips.isEnabled) {
ongoingActivityChipsLegacy.map { it.primary is OngoingActivityChipModel.Active }
} else {
@@ -607,6 +612,8 @@ constructor(
private const val COL_PREFIX_NOTIF_CONTAINER = "notifContainer"
private const val COL_PREFIX_SYSTEM_INFO = "systemInfo"
+ private const val TRACK_GROUP = "StatusBar"
+
fun tableLogBufferName(displayId: Int) = "HomeStatusBarViewModel[$displayId]"
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/DeviceStateRotationLockSettingController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/DeviceStateRotationLockSettingController.java
index b13e01be40f7..fa022b4768fc 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/DeviceStateRotationLockSettingController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/DeviceStateRotationLockSettingController.java
@@ -27,6 +27,7 @@ import android.util.IndentingPrintWriter;
import androidx.annotation.NonNull;
+import com.android.settingslib.devicestate.DeviceStateAutoRotateSettingManager;
import com.android.settingslib.devicestate.DeviceStateRotationLockSettingsManager;
import com.android.systemui.Dumpable;
import com.android.systemui.dagger.qualifiers.Main;
@@ -56,8 +57,8 @@ public final class DeviceStateRotationLockSettingController
private int mDeviceState = -1;
@Nullable
private DeviceStateManager.DeviceStateCallback mDeviceStateCallback;
- private DeviceStateRotationLockSettingsManager.DeviceStateRotationLockSettingsListener
- mDeviceStateRotationLockSettingsListener;
+ private DeviceStateAutoRotateSettingManager.DeviceStateAutoRotateSettingListener
+ mDeviceStateAutoRotateSettingListener;
@Inject
public DeviceStateRotationLockSettingController(
@@ -83,17 +84,17 @@ public final class DeviceStateRotationLockSettingController
// is no user action.
mDeviceStateCallback = this::updateDeviceState;
mDeviceStateManager.registerCallback(mMainExecutor, mDeviceStateCallback);
- mDeviceStateRotationLockSettingsListener = () ->
+ mDeviceStateAutoRotateSettingListener = () ->
readPersistedSetting("deviceStateRotationLockChange", mDeviceState);
mDeviceStateRotationLockSettingsManager.registerListener(
- mDeviceStateRotationLockSettingsListener);
+ mDeviceStateAutoRotateSettingListener);
} else {
if (mDeviceStateCallback != null) {
mDeviceStateManager.unregisterCallback(mDeviceStateCallback);
}
- if (mDeviceStateRotationLockSettingsListener != null) {
+ if (mDeviceStateAutoRotateSettingListener != null) {
mDeviceStateRotationLockSettingsManager.unregisterListener(
- mDeviceStateRotationLockSettingsListener);
+ mDeviceStateAutoRotateSettingListener);
}
}
}
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
index 1bc3096d0ac1..9475bdbd043b 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
@@ -2746,7 +2746,7 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase {
() -> mJavaAdapter,
() -> mSceneInteractor,
() -> mCommunalSceneInteractor,
- mKeyguardServiceShowLockscreenInteractor);
+ () -> mKeyguardServiceShowLockscreenInteractor);
setAlternateBouncerVisibility(false);
setPrimaryBouncerVisibility(false);
setStrongAuthTracker(KeyguardUpdateMonitorTest.this.mStrongAuthTracker);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/clipboardoverlay/ClipboardOverlayControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/clipboardoverlay/ClipboardOverlayControllerTest.java
index 5c893da45b8d..6a4f3da054e6 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/clipboardoverlay/ClipboardOverlayControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/clipboardoverlay/ClipboardOverlayControllerTest.java
@@ -44,6 +44,7 @@ import android.app.RemoteAction;
import android.content.ClipData;
import android.content.ClipDescription;
import android.content.Context;
+import android.content.Intent;
import android.graphics.Insets;
import android.graphics.Rect;
import android.net.Uri;
@@ -77,6 +78,7 @@ import org.mockito.invocation.InvocationOnMock;
import org.mockito.stubbing.Answer;
import java.util.Optional;
+import java.util.function.Consumer;
@SmallTest
@RunWith(AndroidJUnit4.class)
@@ -158,6 +160,31 @@ public class ClipboardOverlayControllerTest extends SysuiTestCase {
* is false are removed.[
*/
private void initController() {
+ IntentCreator fakeIntentCreator = new IntentCreator() {
+ @Override
+ public Intent getTextEditorIntent(Context context) {
+ return new Intent();
+ }
+
+ @Override
+ public Intent getShareIntent(ClipData clipData, Context context) {
+ Intent intent = Intent.createChooser(new Intent(Intent.ACTION_SEND), null);
+ intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ return intent;
+ }
+
+ @Override
+ public void getImageEditIntentAsync(Uri uri, Context context,
+ Consumer<Intent> outputConsumer) {
+ outputConsumer.accept(new Intent(Intent.ACTION_EDIT));
+ }
+
+ @Override
+ public Intent getRemoteCopyIntent(ClipData clipData, Context context) {
+ return new Intent();
+ }
+ };
+
mOverlayController = new ClipboardOverlayController(
mContext,
mClipboardOverlayView,
@@ -171,7 +198,7 @@ public class ClipboardOverlayControllerTest extends SysuiTestCase {
mClipboardTransitionExecutor,
mClipboardIndicationProvider,
mUiEventLogger,
- new ActionIntentCreator());
+ fakeIntentCreator);
verify(mClipboardOverlayView).setCallbacks(mOverlayCallbacksCaptor.capture());
mCallbacks = mOverlayCallbacksCaptor.getValue();
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsDialogLiteTest.java b/packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsDialogLiteTest.java
index fb70846049da..061f7984d44b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsDialogLiteTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsDialogLiteTest.java
@@ -134,7 +134,6 @@ public class GlobalActionsDialogLiteTest extends SysuiTestCase {
@Mock private RingerModeTracker mRingerModeTracker;
@Mock private RingerModeLiveData mRingerModeLiveData;
@Mock private PackageManager mPackageManager;
- @Mock private Handler mHandler;
@Mock private UserContextProvider mUserContextProvider;
@Mock private VibratorHelper mVibratorHelper;
@Mock private ShadeController mShadeController;
@@ -148,6 +147,7 @@ public class GlobalActionsDialogLiteTest extends SysuiTestCase {
private TestableLooper mTestableLooper;
private KosmosJavaAdapter mKosmos = new KosmosJavaAdapter(this);
private GlobalActionsInteractor mInteractor;
+ private Handler mHandler;
@Before
public void setUp() throws Exception {
@@ -166,6 +166,7 @@ public class GlobalActionsDialogLiteTest extends SysuiTestCase {
mGlobalSettings = new FakeGlobalSettings();
mSecureSettings = new FakeSettings();
mInteractor = mKosmos.getGlobalActionsInteractor();
+ mHandler = new Handler(mTestableLooper.getLooper());
mGlobalActionsDialogLite = new GlobalActionsDialogLite(mContext,
mWindowManagerFuncs,
@@ -771,6 +772,31 @@ public class GlobalActionsDialogLiteTest extends SysuiTestCase {
mGlobalActionsDialogLite.showOrHideDialog(false, false, null, Display.DEFAULT_DISPLAY);
}
+ @Test
+ public void userSwitching_dismissDialog() {
+ String[] actions = {
+ GlobalActionsDialogLite.GLOBAL_ACTION_KEY_POWER,
+ GlobalActionsDialogLite.GLOBAL_ACTION_KEY_RESTART,
+ };
+ doReturn(actions).when(mResources)
+ .getStringArray(com.android.internal.R.array.config_globalActionsList);
+
+ mGlobalActionsDialogLite.showOrHideDialog(false, true, null, Display.DEFAULT_DISPLAY);
+ mTestableLooper.processAllMessages();
+
+ assertThat(mGlobalActionsDialogLite.mDialog.isShowing()).isTrue();
+
+ ArgumentCaptor<UserTracker.Callback> captor =
+ ArgumentCaptor.forClass(UserTracker.Callback.class);
+
+ verify(mUserTracker).addCallback(captor.capture(), any());
+
+ captor.getValue().onBeforeUserSwitching(100);
+ mTestableLooper.processAllMessages();
+
+ assertThat(mGlobalActionsDialogLite.mDialog).isNull();
+ }
+
private UserInfo mockCurrentUser(int flags) {
return new UserInfo(10, "A User", flags);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/viewmodel/SeekBarViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/viewmodel/SeekBarViewModelTest.kt
index f394c805f5b7..8f1d07bac4df 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/viewmodel/SeekBarViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/viewmodel/SeekBarViewModelTest.kt
@@ -855,6 +855,20 @@ public class SeekBarViewModelTest : SysuiTestCase() {
@Test
fun contentDescriptionUpdated() {
+ var elapsedTimeDesc: CharSequence? = null
+ var durationDesc: CharSequence? = null
+ val listener =
+ object : SeekBarViewModel.ContentDescriptionListener {
+ override fun onContentDescriptionChanged(
+ elapsedTimeDescription: CharSequence,
+ durationDescription: CharSequence,
+ ) {
+ elapsedTimeDesc = elapsedTimeDescription
+ durationDesc = durationDescription
+ }
+ }
+ viewModel.setContentDescriptionListener(listener)
+
// When there is a duration and position
val duration = (1.5 * 60 * 60 * 1000).toLong()
val metadata =
@@ -875,9 +889,7 @@ public class SeekBarViewModelTest : SysuiTestCase() {
viewModel.updateController(mockController)
fakeExecutor.runNextReady()
- // Then the content description is set
- val result = viewModel.progress.value!!
-
+ // Then the content description listener gets an update
val expectedProgress =
MeasureFormat.getInstance(Locale.getDefault(), MeasureFormat.FormatWidth.WIDE)
.formatMeasures(Measure(3, MeasureUnit.SECOND))
@@ -888,7 +900,7 @@ public class SeekBarViewModelTest : SysuiTestCase() {
Measure(30, MeasureUnit.MINUTE),
Measure(0, MeasureUnit.SECOND),
)
- assertThat(result.durationDescription).isEqualTo(expectedDuration)
- assertThat(result.elapsedTimeDescription).isEqualTo(expectedProgress)
+ assertThat(elapsedTimeDesc).isEqualTo(expectedProgress)
+ assertThat(durationDesc).isEqualTo(expectedDuration)
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/panels/ui/compose/DragAndDropTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/panels/ui/compose/DragAndDropTest.kt
index 92b26ea3a8ef..7e42ec7e83b1 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/panels/ui/compose/DragAndDropTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/panels/ui/compose/DragAndDropTest.kt
@@ -30,6 +30,7 @@ import androidx.compose.ui.text.AnnotatedString
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.FlakyTest
import androidx.test.filters.SmallTest
+import com.android.compose.theme.PlatformTheme
import com.android.systemui.SysuiTestCase
import com.android.systemui.common.shared.model.ContentDescription
import com.android.systemui.common.shared.model.Icon
@@ -55,19 +56,21 @@ class DragAndDropTest : SysuiTestCase() {
listState: EditTileListState,
onSetTiles: (List<TileSpec>) -> Unit,
) {
- DefaultEditTileGrid(
- listState = listState,
- otherTiles = listOf(),
- columns = 4,
- largeTilesSpan = 4,
- modifier = Modifier.fillMaxSize(),
- onAddTile = {},
- onRemoveTile = {},
- onSetTiles = onSetTiles,
- onResize = { _, _ -> },
- onStopEditing = {},
- onReset = null,
- )
+ PlatformTheme {
+ DefaultEditTileGrid(
+ listState = listState,
+ otherTiles = listOf(),
+ columns = 4,
+ largeTilesSpan = 4,
+ modifier = Modifier.fillMaxSize(),
+ onAddTile = {},
+ onRemoveTile = {},
+ onSetTiles = onSetTiles,
+ onResize = { _, _ -> },
+ onStopEditing = {},
+ onReset = null,
+ )
+ }
}
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/panels/ui/compose/EditModeTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/panels/ui/compose/EditModeTest.kt
index e76be82c9340..9d4a425c678b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/panels/ui/compose/EditModeTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/panels/ui/compose/EditModeTest.kt
@@ -33,6 +33,7 @@ import androidx.compose.ui.test.performClick
import androidx.compose.ui.text.AnnotatedString
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
+import com.android.compose.theme.PlatformTheme
import com.android.systemui.SysuiTestCase
import com.android.systemui.common.shared.model.ContentDescription
import com.android.systemui.common.shared.model.Icon
@@ -56,19 +57,22 @@ class EditModeTest : SysuiTestCase() {
var tiles by remember { mutableStateOf(TestEditTiles) }
val (currentTiles, otherTiles) = tiles.partition { it.tile.isCurrent }
val listState = EditTileListState(currentTiles, columns = 4, largeTilesSpan = 2)
- DefaultEditTileGrid(
- listState = listState,
- otherTiles = otherTiles,
- columns = 4,
- largeTilesSpan = 4,
- modifier = Modifier.fillMaxSize(),
- onAddTile = { tiles = tiles.add(it) },
- onRemoveTile = { tiles = tiles.remove(it) },
- onSetTiles = {},
- onResize = { _, _ -> },
- onStopEditing = {},
- onReset = null,
- )
+
+ PlatformTheme {
+ DefaultEditTileGrid(
+ listState = listState,
+ otherTiles = otherTiles,
+ columns = 4,
+ largeTilesSpan = 4,
+ modifier = Modifier.fillMaxSize(),
+ onAddTile = { tiles = tiles.add(it) },
+ onRemoveTile = { tiles = tiles.remove(it) },
+ onSetTiles = {},
+ onResize = { _, _ -> },
+ onStopEditing = {},
+ onReset = null,
+ )
+ }
}
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/panels/ui/compose/ResizingTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/panels/ui/compose/ResizingTest.kt
index 021be3235ee1..5e76000cc7f0 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/panels/ui/compose/ResizingTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/panels/ui/compose/ResizingTest.kt
@@ -35,6 +35,7 @@ import androidx.compose.ui.test.swipeRight
import androidx.compose.ui.text.AnnotatedString
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
+import com.android.compose.theme.PlatformTheme
import com.android.systemui.SysuiTestCase
import com.android.systemui.common.shared.model.ContentDescription
import com.android.systemui.common.shared.model.Icon
@@ -61,19 +62,21 @@ class ResizingTest : SysuiTestCase() {
listState: EditTileListState,
onResize: (TileSpec, Boolean) -> Unit,
) {
- DefaultEditTileGrid(
- listState = listState,
- otherTiles = listOf(),
- columns = 4,
- largeTilesSpan = 4,
- modifier = Modifier.fillMaxSize(),
- onAddTile = {},
- onRemoveTile = {},
- onSetTiles = {},
- onResize = onResize,
- onStopEditing = {},
- onReset = null,
- )
+ PlatformTheme {
+ DefaultEditTileGrid(
+ listState = listState,
+ otherTiles = listOf(),
+ columns = 4,
+ largeTilesSpan = 4,
+ modifier = Modifier.fillMaxSize(),
+ onAddTile = {},
+ onRemoveTile = {},
+ onSetTiles = {},
+ onResize = onResize,
+ onStopEditing = {},
+ onReset = null,
+ )
+ }
}
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/reardisplay/RearDisplayCoreStartableTest.kt b/packages/SystemUI/tests/src/com/android/systemui/reardisplay/RearDisplayCoreStartableTest.kt
index cf54df8565d3..997cf417fe10 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/reardisplay/RearDisplayCoreStartableTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/reardisplay/RearDisplayCoreStartableTest.kt
@@ -22,6 +22,7 @@ import android.platform.test.annotations.DisableFlags
import android.platform.test.annotations.EnableFlags
import android.view.Display
import androidx.test.filters.SmallTest
+import com.android.keyguard.keyguardUpdateMonitor
import com.android.systemui.SysuiTestCase
import com.android.systemui.deviceStateManager
import com.android.systemui.display.domain.interactor.RearDisplayStateInteractor
@@ -37,6 +38,7 @@ import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableSharedFlow
import org.junit.Before
import org.junit.Test
+import org.mockito.Mockito.times
import org.mockito.kotlin.any
import org.mockito.kotlin.mock
import org.mockito.kotlin.never
@@ -59,6 +61,7 @@ class RearDisplayCoreStartableTest : SysuiTestCase() {
fakeRearDisplayStateInteractor,
kosmos.rearDisplayInnerDialogDelegateFactory,
kosmos.testScope,
+ kosmos.keyguardUpdateMonitor,
)
@Before
@@ -96,6 +99,26 @@ class RearDisplayCoreStartableTest : SysuiTestCase() {
}
}
+ @Test
+ @EnableFlags(FLAG_DEVICE_STATE_RDM_V2)
+ fun testDialogResumesAfterKeyguardGone() =
+ kosmos.runTest {
+ impl.use {
+ it.start()
+ fakeRearDisplayStateInteractor.emitRearDisplay()
+
+ verify(mockDialog).show()
+
+ it.keyguardCallback.onKeyguardVisibilityChanged(true)
+ // Do not need to check that the dialog is dismissed, since SystemUIDialog
+ // implementation handles that. We just toggle keyguard here so that the flow
+ // emits.
+
+ it.keyguardCallback.onKeyguardVisibilityChanged(false)
+ verify(mockDialog, times(2)).show()
+ }
+ }
+
private class FakeRearDisplayStateInteractor(private val kosmos: Kosmos) :
RearDisplayStateInteractor {
private val stateFlow = MutableSharedFlow<RearDisplayStateInteractor.State>()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/ViewConfigCoordinatorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/ViewConfigCoordinatorTest.kt
index 3937d3d46d68..ff17a362eb32 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/ViewConfigCoordinatorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/ViewConfigCoordinatorTest.kt
@@ -20,7 +20,6 @@ import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.keyguard.KeyguardUpdateMonitor
import com.android.keyguard.KeyguardUpdateMonitorCallback
-import com.android.systemui.Flags
import com.android.systemui.SysuiTestCase
import com.android.systemui.statusbar.NotificationLockscreenUserManager
import com.android.systemui.statusbar.NotificationLockscreenUserManager.UserChangedListener
@@ -97,7 +96,6 @@ class ViewConfigCoordinatorTest : SysuiTestCase() {
fun themeChangePropagatesToEntry() {
configurationListener.onThemeChanged()
verify(entry).onDensityOrFontScaleChanged()
- checkGutsExposedCalled()
verifyNoMoreInteractions(entry, row)
}
@@ -105,7 +103,6 @@ class ViewConfigCoordinatorTest : SysuiTestCase() {
fun densityChangePropagatesToEntry() {
configurationListener.onDensityOrFontScaleChanged()
verify(entry).onDensityOrFontScaleChanged()
- checkGutsExposedCalled()
verifyNoMoreInteractions(entry, row)
}
@@ -129,7 +126,6 @@ class ViewConfigCoordinatorTest : SysuiTestCase() {
verify(entry).row
verify(row).onUiModeChanged()
verify(entry).onDensityOrFontScaleChanged()
- checkGutsExposedCalled()
verifyNoMoreInteractions(entry, row)
clearInvocations(entry, row)
@@ -160,7 +156,6 @@ class ViewConfigCoordinatorTest : SysuiTestCase() {
verify(entry).row
verify(row).onUiModeChanged()
verify(entry).onDensityOrFontScaleChanged()
- checkGutsExposedCalled()
verifyNoMoreInteractions(entry, row)
clearInvocations(entry, row)
@@ -196,14 +191,7 @@ class ViewConfigCoordinatorTest : SysuiTestCase() {
verify(entry).row
verify(row).onUiModeChanged()
verify(entry).onDensityOrFontScaleChanged()
- checkGutsExposedCalled()
verifyNoMoreInteractions(entry, row)
clearInvocations(entry, row)
}
-
- private fun checkGutsExposedCalled() {
- if (!Flags.notificationUndoGutsOnConfigChanged()) {
- verify(entry).areGutsExposed()
- }
- }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowTest.java
index 84f39be2eeed..00ee893e0e4d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowTest.java
@@ -61,6 +61,7 @@ import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
import com.android.internal.R;
+import com.android.internal.logging.MetricsLogger;
import com.android.internal.widget.CachingIconView;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.SysuiTestableContext;
@@ -71,12 +72,18 @@ import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.statusbar.chips.notification.shared.StatusBarNotifChips;
import com.android.systemui.statusbar.notification.AboveShelfChangedListener;
import com.android.systemui.statusbar.notification.FeedbackIcon;
+import com.android.systemui.statusbar.notification.NotificationActivityStarter;
import com.android.systemui.statusbar.notification.SourceType;
+import com.android.systemui.statusbar.notification.collection.EntryAdapter;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
+import com.android.systemui.statusbar.notification.collection.NotificationEntryAdapter;
+import com.android.systemui.statusbar.notification.collection.coordinator.VisualStabilityCoordinator;
import com.android.systemui.statusbar.notification.headsup.PinnedStatus;
+import com.android.systemui.statusbar.notification.people.PeopleNotificationIdentifier;
import com.android.systemui.statusbar.notification.promoted.PromotedNotificationUi;
import com.android.systemui.statusbar.notification.promoted.PromotedNotificationUiForceExpanded;
import com.android.systemui.statusbar.notification.row.ExpandableView.OnHeightChangedListener;
+import com.android.systemui.statusbar.notification.row.icon.NotificationIconStyleProvider;
import com.android.systemui.statusbar.notification.row.wrapper.NotificationViewWrapper;
import com.android.systemui.statusbar.notification.shared.NotificationBundleUi;
import com.android.systemui.statusbar.notification.shared.NotificationContentAlphaOptimization;
@@ -942,9 +949,7 @@ public class ExpandableNotificationRowTest extends SysuiTestCase {
public void isExpanded_sensitivePromotedNotification_notExpanded() throws Exception {
// GIVEN
final ExpandableNotificationRow row = mNotificationTestHelper.createRow();
- NotificationEntry entry = mock(NotificationEntry.class);
- when(entry.isPromotedOngoing()).thenReturn(true);
- row.setEntry(entry);
+ setRowPromotedOngoing(row);
row.setSensitive(/* sensitive= */true, /* hideSensitive= */false);
row.setHideSensitiveForIntrinsicHeight(/* hideSensitive= */true);
@@ -957,9 +962,7 @@ public class ExpandableNotificationRowTest extends SysuiTestCase {
public void isExpanded_promotedNotificationNotOnKeyguard_expanded() throws Exception {
// GIVEN
final ExpandableNotificationRow row = mNotificationTestHelper.createRow();
- NotificationEntry entry = mock(NotificationEntry.class);
- when(entry.isPromotedOngoing()).thenReturn(true);
- row.setEntry(entry);
+ setRowPromotedOngoing(row);
row.setOnKeyguard(false);
// THEN
@@ -971,9 +974,7 @@ public class ExpandableNotificationRowTest extends SysuiTestCase {
public void isExpanded_promotedNotificationAllowOnKeyguard_expanded() throws Exception {
// GIVEN
final ExpandableNotificationRow row = mNotificationTestHelper.createRow();
- NotificationEntry entry = mock(NotificationEntry.class);
- when(entry.isPromotedOngoing()).thenReturn(true);
- row.setEntry(entry);
+ setRowPromotedOngoing(row);
row.setOnKeyguard(true);
// THEN
@@ -986,9 +987,7 @@ public class ExpandableNotificationRowTest extends SysuiTestCase {
throws Exception {
// GIVEN
final ExpandableNotificationRow row = mNotificationTestHelper.createRow();
- NotificationEntry entry = mock(NotificationEntry.class);
- when(entry.isPromotedOngoing()).thenReturn(true);
- row.setEntry(entry);
+ setRowPromotedOngoing(row);
row.setOnKeyguard(true);
row.setIgnoreLockscreenConstraints(true);
@@ -996,15 +995,31 @@ public class ExpandableNotificationRowTest extends SysuiTestCase {
assertThat(row.isExpanded()).isTrue();
}
+ private static void setRowPromotedOngoing(ExpandableNotificationRow row) {
+ final NotificationEntry entry = mock(NotificationEntry.class);
+ when(entry.isPromotedOngoing()).thenReturn(true);
+ if (NotificationBundleUi.isEnabled()) {
+ final EntryAdapter entryAdapter = new NotificationEntryAdapter(
+ mock(NotificationActivityStarter.class),
+ mock(MetricsLogger.class),
+ mock(PeopleNotificationIdentifier.class),
+ mock(NotificationIconStyleProvider.class),
+ mock(VisualStabilityCoordinator.class),
+ mock(NotificationActionClickManager.class),
+ entry);
+ row.setEntryAdapter(entryAdapter);
+ } else {
+ row.setEntry(entry);
+ }
+ }
+
@Test
@EnableFlags({PromotedNotificationUi.FLAG_NAME, PromotedNotificationUiForceExpanded.FLAG_NAME})
public void isExpanded_promotedNotificationSaveSpaceOnLockScreen_notExpanded()
throws Exception {
// GIVEN
final ExpandableNotificationRow row = mNotificationTestHelper.createRow();
- NotificationEntry entry = mock(NotificationEntry.class);
- when(entry.isPromotedOngoing()).thenReturn(true);
- row.setEntry(entry);
+ setRowPromotedOngoing(row);
row.setOnKeyguard(true);
row.setSaveSpaceOnLockscreen(true);
@@ -1018,9 +1033,7 @@ public class ExpandableNotificationRowTest extends SysuiTestCase {
throws Exception {
// GIVEN
final ExpandableNotificationRow row = mNotificationTestHelper.createRow();
- NotificationEntry entry = mock(NotificationEntry.class);
- when(entry.isPromotedOngoing()).thenReturn(true);
- row.setEntry(entry);
+ setRowPromotedOngoing(row);
row.setOnKeyguard(true);
row.setSaveSpaceOnLockscreen(false);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerWithScenesTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerWithScenesTest.kt
index 0c0ef9d5edfe..10de86644015 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerWithScenesTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerWithScenesTest.kt
@@ -67,6 +67,7 @@ import com.android.systemui.statusbar.notification.collection.provider.HighPrior
import com.android.systemui.statusbar.notification.domain.interactor.activeNotificationsInteractor
import com.android.systemui.statusbar.notification.headsup.mockHeadsUpManager
import com.android.systemui.statusbar.notification.people.PeopleNotificationIdentifier
+import com.android.systemui.statusbar.notification.promoted.domain.interactor.PackageDemotionInteractor
import com.android.systemui.statusbar.notification.row.icon.appIconProvider
import com.android.systemui.statusbar.notification.row.icon.notificationIconStyleProvider
import com.android.systemui.statusbar.notification.stack.NotificationListContainer
@@ -146,6 +147,7 @@ class NotificationGutsManagerWithScenesTest : SysuiTestCase() {
@Mock private lateinit var notificationManager: INotificationManager
@Mock private lateinit var shortcutManager: ShortcutManager
@Mock private lateinit var channelEditorDialogController: ChannelEditorDialogController
+ @Mock private lateinit var packageDemotionInteractor: PackageDemotionInteractor
@Mock private lateinit var peopleNotificationIdentifier: PeopleNotificationIdentifier
@Mock private lateinit var contextTracker: UserContextProvider
@Mock private lateinit var bubblesManager: BubblesManager
@@ -185,6 +187,7 @@ class NotificationGutsManagerWithScenesTest : SysuiTestCase() {
launcherApps,
shortcutManager,
channelEditorDialogController,
+ packageDemotionInteractor,
contextTracker,
assistantFeedbackController,
Optional.of(bubblesManager),
@@ -297,45 +300,6 @@ class NotificationGutsManagerWithScenesTest : SysuiTestCase() {
}
@Test
- fun testChangeDensityOrFontScale() {
- val guts = spy(NotificationGuts(mContext))
- whenever(guts.post(any())).thenAnswer { invocation: InvocationOnMock ->
- handler.post((invocation.arguments[0] as Runnable))
- null
- }
-
- // Test doesn't support animation since the guts view is not attached.
- doNothing()
- .whenever(guts)
- .openControls(any<Int>(), any<Int>(), any<Boolean>(), any<Runnable>())
- val realRow = createTestNotificationRow()
- val menuItem = createTestMenuItem(realRow)
- val row = spy(realRow)
- whenever(row!!.windowToken).thenReturn(Binder())
- whenever(row.guts).thenReturn(guts)
- doNothing().whenever(row).ensureGutsInflated()
- val realEntry = realRow!!.entry
- val entry = spy(realEntry)
- whenever(entry.row).thenReturn(row)
- whenever(entry.getGuts()).thenReturn(guts)
- Assert.assertTrue(gutsManager.openGutsInternal(row, 0, 0, menuItem))
- executor.runAllReady()
- verify(guts).openControls(any<Int>(), any<Int>(), any<Boolean>(), any<Runnable>())
-
- // called once by mGutsManager.bindGuts() in mGutsManager.openGuts()
- verify(row).setGutsView(any())
- row.onDensityOrFontScaleChanged()
- gutsManager.onDensityOrFontScaleChanged(entry)
- executor.runAllReady()
- gutsManager.closeAndSaveGuts(false, false, false, 0, 0, false)
- verify(guts)
- .closeControls(any<Boolean>(), any<Boolean>(), any<Int>(), any<Int>(), any<Boolean>())
-
- // called again by mGutsManager.bindGuts(), in mGutsManager.onDensityOrFontScaleChanged()
- verify(row, times(2)).setGutsView(any())
- }
-
- @Test
fun testAppOpsSettingsIntent_camera() {
val ops = ArraySet<Int>()
ops.add(AppOpsManager.OP_CAMERA)
@@ -427,6 +391,7 @@ class NotificationGutsManagerWithScenesTest : SysuiTestCase() {
.setUserSentiment(Ranking.USER_SENTIMENT_NEGATIVE)
.setImportance(NotificationManager.IMPORTANCE_HIGH)
.build()
+ whenever(row.canViewBeDismissed()).thenReturn(true)
whenever(highPriorityProvider.isHighPriority(entry)).thenReturn(true)
val statusBarNotification = entry.sbn
gutsManager.initializeNotificationInfo(row, notificationInfoView)
@@ -438,6 +403,7 @@ class NotificationGutsManagerWithScenesTest : SysuiTestCase() {
eq(iconStyleProvider),
eq(onUserInteractionCallback),
eq(channelEditorDialogController),
+ eq(packageDemotionInteractor),
eq(statusBarNotification.packageName),
any<NotificationChannel>(),
eq(entry),
@@ -447,7 +413,8 @@ class NotificationGutsManagerWithScenesTest : SysuiTestCase() {
any<UiEventLogger>(),
eq(true),
eq(false),
- eq(true), /* wasShownHighPriority */
+ eq(true),
+ eq(true),
eq(assistantFeedbackController),
any<MetricsLogger>(),
any<View.OnClickListener>(),
@@ -462,6 +429,7 @@ class NotificationGutsManagerWithScenesTest : SysuiTestCase() {
NotificationEntryHelper.modifyRanking(row.entry)
.setUserSentiment(Ranking.USER_SENTIMENT_NEGATIVE)
.build()
+ whenever(row.canViewBeDismissed()).thenReturn(true)
val statusBarNotification = row.entry.sbn
val entry = row.entry
gutsManager.initializeNotificationInfo(row, notificationInfoView)
@@ -473,6 +441,7 @@ class NotificationGutsManagerWithScenesTest : SysuiTestCase() {
eq(iconStyleProvider),
eq(onUserInteractionCallback),
eq(channelEditorDialogController),
+ eq(packageDemotionInteractor),
eq(statusBarNotification.packageName),
any<NotificationChannel>(),
eq(entry),
@@ -482,7 +451,8 @@ class NotificationGutsManagerWithScenesTest : SysuiTestCase() {
any<UiEventLogger>(),
eq(true),
eq(false),
- eq(false), /* wasShownHighPriority */
+ eq(true), /* wasShownHighPriority */
+ eq(false),
eq(assistantFeedbackController),
any<MetricsLogger>(),
any<View.OnClickListener>(),
@@ -497,6 +467,7 @@ class NotificationGutsManagerWithScenesTest : SysuiTestCase() {
NotificationEntryHelper.modifyRanking(row.entry)
.setUserSentiment(Ranking.USER_SENTIMENT_NEGATIVE)
.build()
+ whenever(row.canViewBeDismissed()).thenReturn(true)
val statusBarNotification = row.entry.sbn
val entry = row.entry
gutsManager.initializeNotificationInfo(row, notificationInfoView)
@@ -508,6 +479,7 @@ class NotificationGutsManagerWithScenesTest : SysuiTestCase() {
eq(iconStyleProvider),
eq(onUserInteractionCallback),
eq(channelEditorDialogController),
+ eq(packageDemotionInteractor),
eq(statusBarNotification.packageName),
any<NotificationChannel>(),
eq(entry),
@@ -517,7 +489,8 @@ class NotificationGutsManagerWithScenesTest : SysuiTestCase() {
any<UiEventLogger>(),
eq(true),
eq(false),
- eq(false), /* wasShownHighPriority */
+ eq(true), /* wasShownHighPriority */
+ eq(false),
eq(assistantFeedbackController),
any<MetricsLogger>(),
any<View.OnClickListener>(),
diff --git a/packages/SystemUI/tests/src/com/android/systemui/wallpapers/GradientColorWallpaperTest.kt b/packages/SystemUI/tests/src/com/android/systemui/wallpapers/GradientColorWallpaperTest.kt
index 5f3442048fcd..422b20e8b951 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/wallpapers/GradientColorWallpaperTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/wallpapers/GradientColorWallpaperTest.kt
@@ -45,7 +45,7 @@ import org.mockito.kotlin.any
import org.mockito.kotlin.doReturn
import org.mockito.kotlin.times
import org.mockito.kotlin.verify
-import org.mockito.kotlin.verifyZeroInteractions
+import org.mockito.kotlin.verifyNoMoreInteractions
import org.mockito.kotlin.whenever
@SmallTest
@@ -92,7 +92,7 @@ class GradientColorWallpaperTest : SysuiTestCase() {
engine.onSurfaceRedrawNeeded(surfaceHolder)
- verifyZeroInteractions(canvas)
+ verifyNoMoreInteractions(canvas)
}
@Test
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/display/data/repository/FakeDisplayRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/display/data/repository/FakeDisplayRepository.kt
index 663a85330f70..338e4bec7aa2 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/display/data/repository/FakeDisplayRepository.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/display/data/repository/FakeDisplayRepository.kt
@@ -26,6 +26,7 @@ import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asStateFlow
+import com.android.app.displaylib.DisplayRepository.PendingDisplay
import org.mockito.Mockito.`when` as whenever
/** Creates a mock display. */
@@ -41,8 +42,8 @@ fun display(type: Int, flags: Int = 0, id: Int = 0, state: Int? = null): Display
}
/** Creates a mock [DisplayRepository.PendingDisplay]. */
-fun createPendingDisplay(id: Int = 0): DisplayRepository.PendingDisplay =
- mock<DisplayRepository.PendingDisplay> { whenever(this.id).thenReturn(id) }
+fun createPendingDisplay(id: Int = 0): PendingDisplay =
+ mock<PendingDisplay> { whenever(this.id).thenReturn(id) }
@SysUISingleton
/** Fake [DisplayRepository] implementation for testing. */
@@ -50,7 +51,7 @@ class FakeDisplayRepository @Inject constructor() : DisplayRepository {
private val flow = MutableStateFlow<Set<Display>>(emptySet())
private val displayIdFlow = MutableStateFlow<Set<Int>>(emptySet())
private val pendingDisplayFlow =
- MutableSharedFlow<DisplayRepository.PendingDisplay?>(replay = 1)
+ MutableSharedFlow<PendingDisplay?>(replay = 1)
private val displayAdditionEventFlow = MutableSharedFlow<Display?>(replay = 0)
private val displayRemovalEventFlow = MutableSharedFlow<Int>(replay = 0)
private val displayIdsWithSystemDecorationsFlow = MutableStateFlow<Set<Int>>(emptySet())
@@ -101,7 +102,7 @@ class FakeDisplayRepository @Inject constructor() : DisplayRepository {
suspend fun emit(value: Set<Display>) = flow.emit(value)
/** Emits [value] as [pendingDisplay] flow value. */
- suspend fun emit(value: DisplayRepository.PendingDisplay?) = pendingDisplayFlow.emit(value)
+ suspend fun emit(value: PendingDisplay?) = pendingDisplayFlow.emit(value)
override val displays: StateFlow<Set<Display>>
get() = flow
@@ -109,7 +110,7 @@ class FakeDisplayRepository @Inject constructor() : DisplayRepository {
override val displayIds: StateFlow<Set<Int>>
get() = displayIdFlow
- override val pendingDisplay: Flow<DisplayRepository.PendingDisplay?>
+ override val pendingDisplay: Flow<PendingDisplay?>
get() = pendingDisplayFlow
private val _defaultDisplayOff: MutableStateFlow<Boolean> = MutableStateFlow(false)
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/display/data/repository/PerDisplayStoreKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/display/data/repository/PerDisplayStoreKosmos.kt
index aa23aa30b7bc..5ab3b3de49f4 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/display/data/repository/PerDisplayStoreKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/display/data/repository/PerDisplayStoreKosmos.kt
@@ -66,6 +66,7 @@ class FakePerDisplayInstanceProviderWithTeardown :
val Kosmos.fakePerDisplayInstanceProviderWithTeardown by
Kosmos.Fixture { FakePerDisplayInstanceProviderWithTeardown() }
+val Kosmos.perDisplayDumpHelper by Kosmos.Fixture { PerDisplayRepoDumpHelper(dumpManager) }
val Kosmos.fakePerDisplayInstanceRepository by
Kosmos.Fixture {
PerDisplayInstanceRepositoryImpl(
@@ -73,6 +74,6 @@ val Kosmos.fakePerDisplayInstanceRepository by
instanceProvider = fakePerDisplayInstanceProviderWithTeardown,
testScope.backgroundScope,
displayRepository,
- dumpManager,
+ perDisplayDumpHelper,
)
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardServiceShowLockscreenInteractor.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/KeyguardServiceShowLockscreenRepositoryKosmos.kt
index 3cec5a9cd822..361d21dc134d 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardServiceShowLockscreenInteractor.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/KeyguardServiceShowLockscreenRepositoryKosmos.kt
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2024 The Android Open Source Project
+ * 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.
@@ -14,10 +14,9 @@
* limitations under the License.
*/
-package com.android.systemui.keyguard.domain.interactor
+package com.android.systemui.keyguard.data.repository
import com.android.systemui.kosmos.Kosmos
-import com.android.systemui.kosmos.testScope
-val Kosmos.keyguardServiceShowLockscreenInteractor by
- Kosmos.Fixture { KeyguardServiceShowLockscreenInteractor(backgroundScope = testScope) }
+val Kosmos.keyguardServiceShowLockscreenRepository by
+ Kosmos.Fixture { KeyguardServiceShowLockscreenRepository() }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardServiceShowLockscreenInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardServiceShowLockscreenInteractorKosmos.kt
new file mode 100644
index 000000000000..447aa1255463
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardServiceShowLockscreenInteractorKosmos.kt
@@ -0,0 +1,35 @@
+/*
+ * 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.keyguard.domain.interactor
+
+import com.android.systemui.keyguard.data.repository.keyguardServiceShowLockscreenRepository
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.testScope
+import com.android.systemui.settings.userTracker
+import com.android.systemui.user.domain.interactor.selectedUserInteractor
+
+val Kosmos.keyguardServiceShowLockscreenInteractor by
+ Kosmos.Fixture {
+ KeyguardServiceShowLockscreenInteractor(
+ backgroundScope = testScope,
+ selectedUserInteractor = selectedUserInteractor,
+ repository = keyguardServiceShowLockscreenRepository,
+ userTracker = userTracker,
+ wmLockscreenVisibilityInteractor = { windowManagerLockscreenVisibilityInteractor },
+ keyguardEnabledInteractor = keyguardEnabledInteractor,
+ )
+ }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardShowWhileAwakeInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardShowWhileAwakeInteractorKosmos.kt
index f7caeb69f17e..dd868e767d9b 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardShowWhileAwakeInteractorKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardShowWhileAwakeInteractorKosmos.kt
@@ -18,10 +18,12 @@ package com.android.systemui.keyguard.domain.interactor
import com.android.systemui.keyguard.data.repository.biometricSettingsRepository
import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.testScope
val Kosmos.keyguardShowWhileAwakeInteractor by
Kosmos.Fixture {
KeyguardShowWhileAwakeInteractor(
+ backgroundScope = testScope,
biometricSettingsRepository = biometricSettingsRepository,
keyguardEnabledInteractor = keyguardEnabledInteractor,
keyguardServiceShowLockscreenInteractor = keyguardServiceShowLockscreenInteractor,
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardWakeDirectlyToGoneInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardWakeDirectlyToGoneInteractorKosmos.kt
index 43fa718cff9a..c89fb705f417 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardWakeDirectlyToGoneInteractorKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardWakeDirectlyToGoneInteractorKosmos.kt
@@ -27,7 +27,7 @@ import com.android.systemui.user.domain.interactor.selectedUserInteractor
import com.android.systemui.util.settings.fakeSettings
import com.android.systemui.util.time.systemClock
-val Kosmos.keyguardWakeDirectlyToGoneInteractor by
+val Kosmos.keyguardWakeDirectlyToGoneInteractor: KeyguardWakeDirectlyToGoneInteractor by
Kosmos.Fixture {
KeyguardWakeDirectlyToGoneInteractor(
applicationCoroutineScope,
diff --git a/packages/SystemUI/src/com/android/systemui/display/data/DisplayEvent.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/promoted/domain/interactor/PackageDemotionInteractorKosmos.kt
index 626a68f6a59d..38b59945ab7d 100644
--- a/packages/SystemUI/src/com/android/systemui/display/data/DisplayEvent.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/promoted/domain/interactor/PackageDemotionInteractorKosmos.kt
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2023 The Android Open Source Project
+ * 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.
@@ -14,11 +14,8 @@
* limitations under the License.
*/
-package com.android.systemui.display.data
+package com.android.systemui.statusbar.notification.promoted.domain.interactor
-sealed interface DisplayEvent {
- val displayId: Int
- data class Added(override val displayId: Int) : DisplayEvent
- data class Removed(override val displayId: Int) : DisplayEvent
- data class Changed(override val displayId: Int) : DisplayEvent
-}
+import com.android.systemui.kosmos.Kosmos
+
+val Kosmos.packageDemotionInteractor by Kosmos.Fixture { PackageDemotionInteractor() }
diff --git a/ravenwood/TEST_MAPPING b/ravenwood/TEST_MAPPING
index 1148539187ac..083d2aa15316 100644
--- a/ravenwood/TEST_MAPPING
+++ b/ravenwood/TEST_MAPPING
@@ -180,9 +180,11 @@
// AUTO-GENERATED-END
],
"ravenwood-postsubmit": [
- {
- "name": "SystemUiRavenTests",
- "host": true
- }
+ // We haven't maintained SystemUiRavenTests, and as a result, it's been demoted already.
+ // Disable it until we fix the issues: b/319647875
+ // {
+ // "name": "SystemUiRavenTests",
+ // "host": true
+ // }
]
}
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
index 6c26c1f74002..703e37fad5ad 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
@@ -531,7 +531,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
AccessibilitySecurityPolicy securityPolicy,
SystemActionPerformer systemActionPerformer,
AccessibilityWindowManager a11yWindowManager,
- AccessibilityDisplayListener a11yDisplayListener,
+ AccessibilityDisplayListener.DisplayManagerWrapper displayManagerWrapper,
MagnificationController magnificationController,
@Nullable AccessibilityInputFilter inputFilter,
ProxyManager proxyManager,
@@ -550,7 +550,8 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
mSecurityPolicy = securityPolicy;
mSystemActionPerformer = systemActionPerformer;
mA11yWindowManager = a11yWindowManager;
- mA11yDisplayListener = a11yDisplayListener;
+ mA11yDisplayListener = new AccessibilityDisplayListener(displayManagerWrapper,
+ new MainHandler(Looper.getMainLooper()));
mMagnificationController = magnificationController;
mMagnificationProcessor = new MagnificationProcessor(mMagnificationController);
mCaptioningManagerImpl = new CaptioningManagerImpl(mContext);
@@ -596,7 +597,8 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
this, LocalServices.getService(PackageManagerInternal.class));
mA11yWindowManager = new AccessibilityWindowManager(mLock, mMainHandler,
mWindowManagerService, this, mSecurityPolicy, this, mTraceManager);
- mA11yDisplayListener = new AccessibilityDisplayListener(mContext, mMainHandler);
+ mA11yDisplayListener = new AccessibilityDisplayListener(
+ new AccessibilityDisplayListener.DisplayManagerWrapper(mContext), mMainHandler);
mMagnificationController = new MagnificationController(
this,
mLock,
@@ -5457,11 +5459,11 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
* A Utility class to handle display state.
*/
public class AccessibilityDisplayListener implements DisplayManager.DisplayListener {
- private final DisplayManager mDisplayManager;
+ private final DisplayManagerWrapper mDisplayManager;
private final ArrayList<Display> mDisplaysList = new ArrayList<>();
private int mSystemUiUid = 0;
- AccessibilityDisplayListener(Context context, Handler handler) {
+ AccessibilityDisplayListener(DisplayManagerWrapper displayManager, Handler handler) {
// Avoid concerns about one thread adding displays while another thread removes
// them by ensuring the looper is the main looper and the DisplayListener
// callbacks are always executed on the one main thread.
@@ -5474,7 +5476,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
Slog.e(LOG_TAG, errorMessage);
}
- mDisplayManager = (DisplayManager) context.getSystemService(Context.DISPLAY_SERVICE);
+ mDisplayManager = displayManager;
mDisplayManager.registerDisplayListener(this, handler);
initializeDisplayList();
@@ -5626,6 +5628,34 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
}
return true;
}
+
+ /** Wrapper of DisplayManager for testing. */
+ @VisibleForTesting
+ static class DisplayManagerWrapper {
+ private final DisplayManager mDm;
+
+ DisplayManagerWrapper(Context context) {
+ mDm = context.getSystemService(DisplayManager.class);
+ }
+
+ /**
+ * @see DisplayManager#registerDisplayListener(DisplayManager.DisplayListener, Handler)
+ */
+ public void registerDisplayListener(@NonNull DisplayManager.DisplayListener listener,
+ @Nullable Handler handler) {
+ mDm.registerDisplayListener(listener, handler);
+ }
+
+ /** @see DisplayManager#getDisplays() */
+ public Display[] getDisplays() {
+ return mDm.getDisplays();
+ }
+
+ /** @see DisplayManager#getDisplay(int) */
+ public Display getDisplay(int displayId) {
+ return mDm.getDisplay(displayId);
+ }
+ }
}
/** Represents an {@link AccessibilityManager} */
diff --git a/services/accessibility/java/com/android/server/accessibility/HearingDevicePhoneCallNotificationController.java b/services/accessibility/java/com/android/server/accessibility/HearingDevicePhoneCallNotificationController.java
index 805d7f820c8d..94cef418b6c8 100644
--- a/services/accessibility/java/com/android/server/accessibility/HearingDevicePhoneCallNotificationController.java
+++ b/services/accessibility/java/com/android/server/accessibility/HearingDevicePhoneCallNotificationController.java
@@ -165,6 +165,9 @@ public class HearingDevicePhoneCallNotificationController {
if (state == TelephonyManager.CALL_STATE_OFFHOOK) {
if (com.android.server.accessibility.Flags.hearingInputChangeWhenCommDevice()) {
AudioDeviceInfo commDevice = mAudioManager.getCommunicationDevice();
+ if (commDevice == null) {
+ return;
+ }
mHearingDevice = getSupportedInputHearingDeviceInfo(List.of(commDevice));
if (mHearingDevice != null) {
showNotificationIfNeeded();
diff --git a/services/core/java/com/android/server/TelephonyRegistry.java b/services/core/java/com/android/server/TelephonyRegistry.java
index b75b7ddf8181..9ae8ff869800 100644
--- a/services/core/java/com/android/server/TelephonyRegistry.java
+++ b/services/core/java/com/android/server/TelephonyRegistry.java
@@ -3871,6 +3871,10 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub {
*/
public void notifyCarrierRoamingNtnSignalStrengthChanged(int subId,
@NonNull NtnSignalStrength ntnSignalStrength) {
+ if (!SubscriptionManager.isValidSubscriptionId(subId)) {
+ log("notifyCarrierRoamingNtnSignalStrengthChanged: invalid subscription id");
+ return;
+ }
if (!checkNotifyPermission("notifyCarrierRoamingNtnSignalStrengthChanged")) {
log("notifyCarrierRoamingNtnSignalStrengthChanged: caller does not have required "
+ "permissions.");
diff --git a/services/core/java/com/android/server/am/OWNERS b/services/core/java/com/android/server/am/OWNERS
index 4b6d6bc955cc..fd2d8352eb4f 100644
--- a/services/core/java/com/android/server/am/OWNERS
+++ b/services/core/java/com/android/server/am/OWNERS
@@ -61,7 +61,7 @@ per-file *Oom* = file:/OOM_ADJUSTER_OWNERS
per-file ProcessStateController.java = file:/OOM_ADJUSTER_OWNERS
# Miscellaneous
-per-file SettingsToPropertiesMapper.java = omakoto@google.com, yamasani@google.com, dzshen@google.com, zhidou@google.com, tedbauer@google.com
+per-file SettingsToPropertiesMapper.java = omakoto@google.com, yamasani@google.com, dzshen@google.com, zhidou@google.com
per-file CarUserSwitchingDialog.java = file:platform/packages/services/Car:/OWNERS
# Activity Security
diff --git a/services/core/java/com/android/server/appop/DiscreteOpsDbHelper.java b/services/core/java/com/android/server/appop/DiscreteOpsDbHelper.java
index c53e4bdc2205..3a8d5835ccb1 100644
--- a/services/core/java/com/android/server/appop/DiscreteOpsDbHelper.java
+++ b/services/core/java/com/android/server/appop/DiscreteOpsDbHelper.java
@@ -24,6 +24,7 @@ import android.database.DatabaseErrorHandler;
import android.database.DefaultDatabaseErrorHandler;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteException;
+import android.database.sqlite.SQLiteFullException;
import android.database.sqlite.SQLiteOpenHelper;
import android.database.sqlite.SQLiteRawStatement;
import android.os.Environment;
@@ -174,11 +175,16 @@ class DiscreteOpsDbHelper extends SQLiteOpenHelper {
if (DEBUG) {
Slog.i(LOG_TAG, "DB execSQL, sql: " + sql);
}
- SQLiteDatabase db = getWritableDatabase();
- if (bindArgs == null) {
- db.execSQL(sql);
- } else {
- db.execSQL(sql, bindArgs);
+ try {
+ SQLiteDatabase db = getWritableDatabase();
+ if (bindArgs == null) {
+ db.execSQL(sql);
+ } else {
+ db.execSQL(sql, bindArgs);
+ }
+ } catch (SQLiteFullException exception) {
+ Slog.e(LOG_TAG, "Couldn't exec sql command, disk is full. Discrete ops "
+ + "db file size (bytes) : " + getDatabaseFile().length(), exception);
}
}
diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java
index 01e3d670d12a..d917bffa06e9 100644
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -15109,6 +15109,61 @@ public class AudioService extends IAudioService.Stub
/**
* @hide
+ * Returns the current audio output device delay value of key in milliseconds.
+ *
+ * In aidl implementation, the param of getParameters is "key" and reply delay of all supported
+ * devices which be composited as "key=device,address,delay|device,address,delay|...".
+ * e.g.
+ * param: additional_output_device_delay=
+ * reply: additional_output_device_delay=2,,0|4,,0|8,,0|128,,0|256,,0|512,,0|1024,,0|8192,,0|
+ * 16384,,0|262144,,0|262145,,0|524288,,0|67108864,,0|134217728,,0|536870912,,0|536870913,,0|
+ * 536870914,,0
+ *
+ * In hidl implementation, the param of getParameters is "key=device,address" and reply a
+ * specific device delay which be composited as "key=device,address,delay".
+ * e.g.
+ * param: additional_output_device_delay=2,
+ * reply: additional_output_device_delay=2,,0
+ *
+ * @param key
+ * @param deviceType
+ * @param address
+ * @return the delay value of key. This is a non-negative number.
+ * {@code 0} is returned if unsupported.
+ */
+ private long getDelayByKeyDevice(@NonNull String key, @NonNull AudioDeviceAttributes device) {
+ long delayMillis = 0;
+
+ try {
+ if (AudioHalVersionInfo.AUDIO_HAL_TYPE_AIDL == getHalVersion().getHalType()) {
+ final String reply = AudioSystem.getParameters(key);
+ final String keyDeviceAddressPrefix =
+ Integer.toUnsignedString(device.getInternalType()) + "," + device.getAddress() +
+ ",";
+ int start = reply.indexOf(keyDeviceAddressPrefix);
+ int end = -1;
+ if (start != -1) {
+ start += keyDeviceAddressPrefix.length();
+ end = reply.indexOf("|", start);
+ delayMillis = Long.parseLong(
+ end == -1 ? reply.substring(start) : reply.substring(start, end));
+ }
+ } else {
+ final String reply = AudioSystem.getParameters(
+ key + "=" + device.getInternalType() + "," + device.getAddress());
+ if (reply.contains(key)) {
+ delayMillis = Long.parseLong(reply.substring(key.length() + 1));
+ }
+ }
+ } catch (NullPointerException e) {
+ Log.w(TAG, "NullPointerException when getting delay for device " + device, e);
+ }
+
+ return delayMillis;
+ }
+
+ /**
+ * @hide
* Returns the current additional audio output device delay in milliseconds.
*
* @param deviceType
@@ -15124,17 +15179,8 @@ public class AudioService extends IAudioService.Stub
device = retrieveBluetoothAddress(device);
final String key = "additional_output_device_delay";
- final String reply = AudioSystem.getParameters(
- key + "=" + device.getInternalType() + "," + device.getAddress());
- long delayMillis = 0;
- if (reply.contains(key)) {
- try {
- delayMillis = Long.parseLong(reply.substring(key.length() + 1));
- } catch (NullPointerException e) {
- delayMillis = 0;
- }
- }
- return delayMillis;
+
+ return getDelayByKeyDevice(key, device);
}
/**
@@ -15156,17 +15202,8 @@ public class AudioService extends IAudioService.Stub
device = retrieveBluetoothAddress(device);
final String key = "max_additional_output_device_delay";
- final String reply = AudioSystem.getParameters(
- key + "=" + device.getInternalType() + "," + device.getAddress());
- long delayMillis = 0;
- if (reply.contains(key)) {
- try {
- delayMillis = Long.parseLong(reply.substring(key.length() + 1));
- } catch (NullPointerException e) {
- delayMillis = 0;
- }
- }
- return delayMillis;
+
+ return getDelayByKeyDevice(key, device);
}
@android.annotation.EnforcePermission(MODIFY_AUDIO_ROUTING)
diff --git a/services/core/java/com/android/server/display/DisplayDeviceInfo.java b/services/core/java/com/android/server/display/DisplayDeviceInfo.java
index 3aaf4f6fe85a..7450dffc05ab 100644
--- a/services/core/java/com/android/server/display/DisplayDeviceInfo.java
+++ b/services/core/java/com/android/server/display/DisplayDeviceInfo.java
@@ -122,8 +122,8 @@ final class DisplayDeviceInfo {
public static final int FLAG_MASK_DISPLAY_CUTOUT = 1 << 11;
/**
- * Flag: This flag identifies secondary displays that should show system decorations, such as
- * navigation bar, home activity or wallpaper.
+ * Flag: This flag identifies secondary displays that should always show system decorations,
+ * such as navigation bar, home activity or wallpaper.
* <p>Note that this flag doesn't work without {@link #FLAG_TRUSTED}</p>
* @hide
*/
@@ -191,6 +191,19 @@ final class DisplayDeviceInfo {
public static final int FLAG_STEAL_TOP_FOCUS_DISABLED = 1 << 19;
/**
+ * Flag: Indicates that the display is allowed to switch the content mode between
+ * projected/extended and mirroring. This allows the display to dynamically add or remove the
+ * home and system decorations.
+ *
+ * Note that this flag should not be enabled with any of {@link #FLAG_PRIVATE},
+ * {@link #FLAG_SHOULD_SHOW_SYSTEM_DECORATIONS}, or {@link #FLAG_OWN_CONTENT_ONLY} at the
+ * same time; otherwise it will be ignored.
+ *
+ * @hide
+ */
+ public static final int FLAG_ALLOWS_CONTENT_MODE_SWITCH = 1 << 20;
+
+ /**
* Touch attachment: Display does not receive touch.
*/
public static final int TOUCH_NONE = 0;
diff --git a/services/core/java/com/android/server/display/DisplayGroup.java b/services/core/java/com/android/server/display/DisplayGroup.java
index f73b66c78fce..ce8d8a6db9d3 100644
--- a/services/core/java/com/android/server/display/DisplayGroup.java
+++ b/services/core/java/com/android/server/display/DisplayGroup.java
@@ -16,6 +16,8 @@
package com.android.server.display;
+import android.util.IndentingPrintWriter;
+
import java.util.ArrayList;
import java.util.List;
@@ -97,4 +99,14 @@ public class DisplayGroup {
}
return displayIds;
}
+
+ /** Dumps information about the DisplayGroup. */
+ void dumpLocked(IndentingPrintWriter ipw) {
+ final int numDisplays = mDisplays.size();
+ for (int i = 0; i < numDisplays; i++) {
+ LogicalDisplay logicalDisplay = mDisplays.get(i);
+ ipw.println("Display " + logicalDisplay.getDisplayIdLocked() + " "
+ + logicalDisplay.getPrimaryDisplayDeviceLocked());
+ }
+ }
}
diff --git a/services/core/java/com/android/server/display/LocalDisplayAdapter.java b/services/core/java/com/android/server/display/LocalDisplayAdapter.java
index 7b714ad2bd9e..2cad7ed2e9e9 100644
--- a/services/core/java/com/android/server/display/LocalDisplayAdapter.java
+++ b/services/core/java/com/android/server/display/LocalDisplayAdapter.java
@@ -766,7 +766,7 @@ final class LocalDisplayAdapter extends DisplayAdapter {
mInfo.flags |= DisplayDeviceInfo.FLAG_ROUND;
}
} else {
- if (!res.getBoolean(R.bool.config_localDisplaysMirrorContent)) {
+ if (shouldOwnContentOnly()) {
mInfo.flags |= DisplayDeviceInfo.FLAG_OWN_CONTENT_ONLY;
}
@@ -780,6 +780,15 @@ final class LocalDisplayAdapter extends DisplayAdapter {
}
}
+ if (getFeatureFlags().isDisplayContentModeManagementEnabled()) {
+ // Public display with FLAG_OWN_CONTENT_ONLY disabled is allowed to switch the
+ // content mode.
+ if (mIsFirstDisplay
+ || (!isDisplayPrivate(physicalAddress) && !shouldOwnContentOnly())) {
+ mInfo.flags |= DisplayDeviceInfo.FLAG_ALLOWS_CONTENT_MODE_SWITCH;
+ }
+ }
+
if (DisplayCutout.getMaskBuiltInDisplayCutout(res, mInfo.uniqueId)) {
mInfo.flags |= DisplayDeviceInfo.FLAG_MASK_DISPLAY_CUTOUT;
}
@@ -822,6 +831,7 @@ final class LocalDisplayAdapter extends DisplayAdapter {
R.string.display_manager_hdmi_display_name);
}
}
+
mInfo.frameRateOverrides = mFrameRateOverrides;
// The display is trusted since it is created by system.
@@ -1467,6 +1477,11 @@ final class LocalDisplayAdapter extends DisplayAdapter {
return false;
}
+ private boolean shouldOwnContentOnly() {
+ final Resources res = getOverlayContext().getResources();
+ return !res.getBoolean(R.bool.config_localDisplaysMirrorContent);
+ }
+
private boolean isDisplayStealTopFocusDisabled(DisplayAddress.Physical physicalAddress) {
if (physicalAddress == null) {
return false;
diff --git a/services/core/java/com/android/server/display/LogicalDisplay.java b/services/core/java/com/android/server/display/LogicalDisplay.java
index b2b9ef17ec8d..0e6870f7ed7d 100644
--- a/services/core/java/com/android/server/display/LogicalDisplay.java
+++ b/services/core/java/com/android/server/display/LogicalDisplay.java
@@ -489,6 +489,11 @@ final class LogicalDisplay {
if ((deviceInfo.flags & DisplayDeviceInfo.FLAG_STEAL_TOP_FOCUS_DISABLED) != 0) {
mBaseDisplayInfo.flags |= Display.FLAG_STEAL_TOP_FOCUS_DISABLED;
}
+ // Rear display should not be allowed to use the content mode switch.
+ if ((deviceInfo.flags & DisplayDeviceInfo.FLAG_ALLOWS_CONTENT_MODE_SWITCH) != 0
+ && mDevicePosition != Layout.Display.POSITION_REAR) {
+ mBaseDisplayInfo.flags |= Display.FLAG_ALLOWS_CONTENT_MODE_SWITCH;
+ }
Rect maskingInsets = getMaskingInsets(deviceInfo);
int maskedWidth = deviceInfo.width - maskingInsets.left - maskingInsets.right;
int maskedHeight = deviceInfo.height - maskingInsets.top - maskingInsets.bottom;
@@ -1155,6 +1160,7 @@ final class LogicalDisplay {
pw.println("mRequestedMinimalPostProcessing=" + mRequestedMinimalPostProcessing);
pw.println("mFrameRateOverrides=" + Arrays.toString(mFrameRateOverrides));
pw.println("mPendingFrameRateOverrideUids=" + mPendingFrameRateOverrideUids);
+ pw.println("mDisplayGroupId=" + mDisplayGroupId);
pw.println("mDisplayGroupName=" + mDisplayGroupName);
pw.println("mThermalBrightnessThrottlingDataId=" + mThermalBrightnessThrottlingDataId);
pw.println("mLeadDisplayId=" + mLeadDisplayId);
diff --git a/services/core/java/com/android/server/display/LogicalDisplayMapper.java b/services/core/java/com/android/server/display/LogicalDisplayMapper.java
index f4daf8761e9b..4a4c616b34e3 100644
--- a/services/core/java/com/android/server/display/LogicalDisplayMapper.java
+++ b/services/core/java/com/android/server/display/LogicalDisplayMapper.java
@@ -478,6 +478,21 @@ class LogicalDisplayMapper implements DisplayDeviceRepository.Listener {
ipw.decreaseIndent();
ipw.println();
}
+
+ final int displayGroupCount = mDisplayGroups.size();
+ ipw.println();
+ ipw.println("Display Groups: size=" + displayGroupCount);
+ for (int i = 0; i < displayGroupCount; i++) {
+ int groupId = mDisplayGroups.keyAt(i);
+ DisplayGroup displayGroup = mDisplayGroups.valueAt(i);
+ ipw.println("Group " + groupId + ":");
+ ipw.increaseIndent();
+ displayGroup.dumpLocked(ipw);
+ ipw.decreaseIndent();
+ ipw.println();
+ }
+
+
mDeviceStateToLayoutMap.dumpLocked(ipw);
}
diff --git a/services/core/java/com/android/server/display/OverlayDisplayAdapter.java b/services/core/java/com/android/server/display/OverlayDisplayAdapter.java
index b5a9b19bc5c5..60b7fca99e7b 100644
--- a/services/core/java/com/android/server/display/OverlayDisplayAdapter.java
+++ b/services/core/java/com/android/server/display/OverlayDisplayAdapter.java
@@ -76,6 +76,7 @@ import java.util.regex.Pattern;
* <li><code>secure</code>: creates a secure display</li>
* <li><code>own_content_only</code>: only shows this display's own content</li>
* <li><code>should_show_system_decorations</code>: supports system decorations</li>
+ * <li><code>fixed_content_mode</code>: not allowed to switch content mode</li>
* <li><code>gravity_top_left</code>: display the overlay at the top left of the screen</li>
* <li><code>gravity_top_right</code>: display the overlay at the top right of the screen</li>
* <li><code>gravity_bottom_right</code>: display the overlay at the bottom right of the screen</li>
@@ -117,6 +118,18 @@ final class OverlayDisplayAdapter extends DisplayAdapter {
private static final String OVERLAY_DISPLAY_FLAG_SHOULD_SHOW_SYSTEM_DECORATIONS =
"should_show_system_decorations";
+ /**
+ * When this flag is set, the overlay display is not allowed to switch content mode.
+ * Note that it is the opposite of {@link DisplayDeviceInfo#FLAG_ALLOWS_CONTENT_MODE_SWITCH},
+ * because we want overlay displays (such as those used for connected display simulation in
+ * development) to have {@link DisplayDeviceInfo#FLAG_ALLOWS_CONTENT_MODE_SWITCH} enabled by
+ * default without explicitly specifying it.
+ *
+ * @see DisplayDeviceInfo#FLAG_ALLOWS_CONTENT_MODE_SWITCH
+ */
+ private static final String OVERLAY_DISPLAY_FLAG_FIXED_CONTENT_MODE =
+ "fixed_content_mode";
+
// Gravity flags to decide where the overlay should be shown.
private static final String GRAVITY_TOP_LEFT = "gravity_top_left";
private static final String GRAVITY_BOTTOM_RIGHT = "gravity_bottom_right";
@@ -384,6 +397,17 @@ final class OverlayDisplayAdapter extends DisplayAdapter {
if (mFlags.mShouldShowSystemDecorations) {
mInfo.flags |= DisplayDeviceInfo.FLAG_SHOULD_SHOW_SYSTEM_DECORATIONS;
}
+ if (getFeatureFlags().isDisplayContentModeManagementEnabled()) {
+ if (!mFlags.mFixedContentMode
+ && !mFlags.mOwnContentOnly
+ && !mFlags.mShouldShowSystemDecorations) {
+ // For overlay displays, if FLAG_SHOULD_SHOW_SYSTEM_DECORATIONS and
+ // FLAG_OWN_CONTENT_ONLY are both disabled,
+ // then FLAG_ALLOWS_CONTENT_MODE_SWITCH should be enabled by default,
+ // unless OVERLAY_DISPLAY_FLAG_FIXED_CONTENT_MODE is set.
+ mInfo.flags |= DisplayDeviceInfo.FLAG_ALLOWS_CONTENT_MODE_SWITCH;
+ }
+ }
mInfo.type = Display.TYPE_OVERLAY;
mInfo.touch = DisplayDeviceInfo.TOUCH_VIRTUAL;
mInfo.state = mState;
@@ -628,16 +652,21 @@ final class OverlayDisplayAdapter extends DisplayAdapter {
/** See {@link #OVERLAY_DISPLAY_FLAG_SHOULD_SHOW_SYSTEM_DECORATIONS}. */
final boolean mShouldShowSystemDecorations;
+ /** See {@link #OVERLAY_DISPLAY_FLAG_FIXED_CONTENT_MODE}. */
+ final boolean mFixedContentMode;
+
final int mGravity;
OverlayFlags(
boolean secure,
boolean ownContentOnly,
boolean shouldShowSystemDecorations,
+ boolean fixedContentMode,
int gravity) {
mSecure = secure;
mOwnContentOnly = ownContentOnly;
mShouldShowSystemDecorations = shouldShowSystemDecorations;
+ mFixedContentMode = fixedContentMode;
mGravity = gravity;
}
@@ -647,12 +676,14 @@ final class OverlayDisplayAdapter extends DisplayAdapter {
false /* secure */,
false /* ownContentOnly */,
false /* shouldShowSystemDecorations */,
+ false /* fixedContentMode */,
Gravity.NO_GRAVITY);
}
boolean secure = false;
boolean ownContentOnly = false;
boolean shouldShowSystemDecorations = false;
+ boolean fixedContentMode = false;
int gravity = Gravity.NO_GRAVITY;
for (String flag: flagString.split(FLAG_SPLITTER)) {
if (OVERLAY_DISPLAY_FLAG_SECURE.equals(flag)) {
@@ -661,11 +692,14 @@ final class OverlayDisplayAdapter extends DisplayAdapter {
ownContentOnly = true;
} else if (OVERLAY_DISPLAY_FLAG_SHOULD_SHOW_SYSTEM_DECORATIONS.equals(flag)) {
shouldShowSystemDecorations = true;
+ } else if (OVERLAY_DISPLAY_FLAG_FIXED_CONTENT_MODE.equals(flag)) {
+ fixedContentMode = true;
} else {
gravity = parseOverlayGravity(flag);
}
}
- return new OverlayFlags(secure, ownContentOnly, shouldShowSystemDecorations, gravity);
+ return new OverlayFlags(secure, ownContentOnly, shouldShowSystemDecorations,
+ fixedContentMode, gravity);
}
@Override
@@ -674,6 +708,7 @@ final class OverlayDisplayAdapter extends DisplayAdapter {
.append("secure=").append(mSecure)
.append(", ownContentOnly=").append(mOwnContentOnly)
.append(", shouldShowSystemDecorations=").append(mShouldShowSystemDecorations)
+ .append(", fixedContentMode=").append(mFixedContentMode)
.append(", gravity").append(Gravity.toString(mGravity))
.append("}")
.toString();
diff --git a/services/core/java/com/android/server/display/WifiDisplayAdapter.java b/services/core/java/com/android/server/display/WifiDisplayAdapter.java
index 902eefa824b5..89679c728127 100644
--- a/services/core/java/com/android/server/display/WifiDisplayAdapter.java
+++ b/services/core/java/com/android/server/display/WifiDisplayAdapter.java
@@ -666,6 +666,12 @@ final class WifiDisplayAdapter extends DisplayAdapter {
mInfo.setAssumedDensityForExternalDisplay(mWidth, mHeight);
// The display is trusted since it is created by system.
mInfo.flags |= DisplayDeviceInfo.FLAG_TRUSTED;
+ if (getFeatureFlags().isDisplayContentModeManagementEnabled()) {
+ // The wifi display is allowed to switch content mode since FLAG_PRIVATE,
+ // FLAG_OWN_CONTENT_ONLY, and FLAG_SHOULD_SHOW_SYSTEM_DECORATIONS are not
+ // enabled in WifiDisplayDevice#getDisplayDeviceInfoLocked().
+ mInfo.flags |= DisplayDeviceInfo.FLAG_ALLOWS_CONTENT_MODE_SWITCH;
+ }
mInfo.displayShape =
DisplayShape.createDefaultDisplayShape(mInfo.width, mInfo.height, false);
}
diff --git a/services/core/java/com/android/server/notification/ConditionProviders.java b/services/core/java/com/android/server/notification/ConditionProviders.java
index dd52cce9e927..3f2c2228e453 100644
--- a/services/core/java/com/android/server/notification/ConditionProviders.java
+++ b/services/core/java/com/android/server/notification/ConditionProviders.java
@@ -46,7 +46,6 @@ import java.io.IOException;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Arrays;
-import java.util.List;
public class ConditionProviders extends ManagedServices {
@@ -203,14 +202,7 @@ public class ConditionProviders extends ManagedServices {
@Override
protected void loadDefaultsFromConfig() {
- for (String dndPackage : getDefaultDndAccessPackages(mContext)) {
- addDefaultComponentOrPackage(dndPackage);
- }
- }
-
- static List<String> getDefaultDndAccessPackages(Context context) {
- ArrayList<String> packages = new ArrayList<>();
- String defaultDndAccess = context.getResources().getString(
+ String defaultDndAccess = mContext.getResources().getString(
R.string.config_defaultDndAccessPackages);
if (defaultDndAccess != null) {
String[] dnds = defaultDndAccess.split(ManagedServices.ENABLED_SERVICES_SEPARATOR);
@@ -218,10 +210,9 @@ public class ConditionProviders extends ManagedServices {
if (TextUtils.isEmpty(dnds[i])) {
continue;
}
- packages.add(dnds[i]);
+ addDefaultComponentOrPackage(dnds[i]);
}
}
- return packages;
}
@Override
diff --git a/services/core/java/com/android/server/notification/TEST_MAPPING b/services/core/java/com/android/server/notification/TEST_MAPPING
index dc7129cde5e5..ea7ee4addbe6 100644
--- a/services/core/java/com/android/server/notification/TEST_MAPPING
+++ b/services/core/java/com/android/server/notification/TEST_MAPPING
@@ -4,7 +4,10 @@
"name": "CtsNotificationTestCases_notification"
},
{
- "name": "FrameworksUiServicesTests_notification"
+ "name": "FrameworksUiServicesNotificationTests"
+ },
+ {
+ "name": "FrameworksUiServicesZenTests"
}
],
"postsubmit": [
diff --git a/services/core/java/com/android/server/notification/ZenConfigTrimmer.java b/services/core/java/com/android/server/notification/ZenConfigTrimmer.java
deleted file mode 100644
index d65954d11646..000000000000
--- a/services/core/java/com/android/server/notification/ZenConfigTrimmer.java
+++ /dev/null
@@ -1,109 +0,0 @@
-/*
- * 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.notification;
-
-import android.content.Context;
-import android.os.Parcel;
-import android.service.notification.SystemZenRules;
-import android.service.notification.ZenModeConfig;
-import android.util.Slog;
-
-import java.util.ArrayList;
-import java.util.Comparator;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Map;
-
-class ZenConfigTrimmer {
-
- private static final String TAG = "ZenConfigTrimmer";
- private static final int MAXIMUM_PARCELED_SIZE = 150_000; // bytes
-
- private final HashSet<String> mTrustedPackages;
-
- ZenConfigTrimmer(Context context) {
- mTrustedPackages = new HashSet<>();
- mTrustedPackages.add(SystemZenRules.PACKAGE_ANDROID);
- mTrustedPackages.addAll(ConditionProviders.getDefaultDndAccessPackages(context));
- }
-
- void trimToMaximumSize(ZenModeConfig config) {
- Map<String, PackageRules> rulesPerPackage = new HashMap<>();
- for (ZenModeConfig.ZenRule rule : config.automaticRules.values()) {
- PackageRules pkgRules = rulesPerPackage.computeIfAbsent(rule.pkg, PackageRules::new);
- pkgRules.mRules.add(rule);
- }
-
- int totalSize = 0;
- for (PackageRules pkgRules : rulesPerPackage.values()) {
- totalSize += pkgRules.dataSize();
- }
-
- if (totalSize > MAXIMUM_PARCELED_SIZE) {
- List<PackageRules> deletionCandidates = new ArrayList<>();
- for (PackageRules pkgRules : rulesPerPackage.values()) {
- if (!mTrustedPackages.contains(pkgRules.mPkg)) {
- deletionCandidates.add(pkgRules);
- }
- }
- deletionCandidates.sort(Comparator.comparingInt(PackageRules::dataSize).reversed());
-
- evictPackagesFromConfig(config, deletionCandidates, totalSize);
- }
- }
-
- private static void evictPackagesFromConfig(ZenModeConfig config,
- List<PackageRules> deletionCandidates, int currentSize) {
- while (currentSize > MAXIMUM_PARCELED_SIZE && !deletionCandidates.isEmpty()) {
- PackageRules rulesToDelete = deletionCandidates.removeFirst();
- Slog.w(TAG, String.format("Evicting %s zen rules from package '%s' (%s bytes)",
- rulesToDelete.mRules.size(), rulesToDelete.mPkg, rulesToDelete.dataSize()));
-
- for (ZenModeConfig.ZenRule rule : rulesToDelete.mRules) {
- config.automaticRules.remove(rule.id);
- }
-
- currentSize -= rulesToDelete.dataSize();
- }
- }
-
- private static class PackageRules {
- private final String mPkg;
- private final List<ZenModeConfig.ZenRule> mRules;
- private int mParceledSize = -1;
-
- PackageRules(String pkg) {
- mPkg = pkg;
- mRules = new ArrayList<>();
- }
-
- private int dataSize() {
- if (mParceledSize >= 0) {
- return mParceledSize;
- }
- Parcel parcel = Parcel.obtain();
- try {
- parcel.writeParcelableList(mRules, 0);
- mParceledSize = parcel.dataSize();
- return mParceledSize;
- } finally {
- parcel.recycle();
- }
- }
- }
-}
diff --git a/services/core/java/com/android/server/notification/ZenModeHelper.java b/services/core/java/com/android/server/notification/ZenModeHelper.java
index 8b09c2acb96a..889df512dd60 100644
--- a/services/core/java/com/android/server/notification/ZenModeHelper.java
+++ b/services/core/java/com/android/server/notification/ZenModeHelper.java
@@ -48,7 +48,6 @@ import static android.service.notification.ZenModeConfig.isImplicitRuleId;
import static com.android.internal.util.FrameworkStatsLog.DND_MODE_RULE;
import static com.android.internal.util.Preconditions.checkArgument;
import static com.android.server.notification.Flags.preventZenDeviceEffectsWhileDriving;
-import static com.android.server.notification.Flags.limitZenConfigSize;
import static java.util.Objects.requireNonNull;
@@ -193,7 +192,6 @@ public class ZenModeHelper {
private final ConditionProviders.Config mServiceConfig;
private final SystemUiSystemPropertiesFlags.FlagResolver mFlagResolver;
private final ZenModeEventLogger mZenModeEventLogger;
- private final ZenConfigTrimmer mConfigTrimmer;
@VisibleForTesting protected int mZenMode;
@VisibleForTesting protected NotificationManager.Policy mConsolidatedPolicy;
@@ -228,7 +226,6 @@ public class ZenModeHelper {
mClock = clock;
addCallback(mMetrics);
mAppOps = context.getSystemService(AppOpsManager.class);
- mConfigTrimmer = new ZenConfigTrimmer(mContext);
mDefaultConfig = Flags.modesUi()
? ZenModeConfig.getDefaultConfig()
@@ -2064,20 +2061,20 @@ public class ZenModeHelper {
Log.w(TAG, "Invalid config in setConfigLocked; " + config);
return false;
}
- if (limitZenConfigSize() && (origin == ORIGIN_APP || origin == ORIGIN_USER_IN_APP)) {
- mConfigTrimmer.trimToMaximumSize(config);
- }
-
if (config.user != mUser) {
// simply store away for background users
- mConfigs.put(config.user, config);
+ synchronized (mConfigLock) {
+ mConfigs.put(config.user, config);
+ }
if (DEBUG) Log.d(TAG, "setConfigLocked: store config for user " + config.user);
return true;
}
// handle CPS backed conditions - danger! may modify config
mConditions.evaluateConfig(config, null, false /*processSubscriptions*/);
- mConfigs.put(config.user, config);
+ synchronized (mConfigLock) {
+ mConfigs.put(config.user, config);
+ }
if (DEBUG) Log.d(TAG, "setConfigLocked reason=" + reason, new Throwable());
ZenLog.traceConfig(origin, reason, triggeringComponent, mConfig, config, callingUid);
diff --git a/services/core/java/com/android/server/notification/flags.aconfig b/services/core/java/com/android/server/notification/flags.aconfig
index 346d65a06cc9..76cd5c88b388 100644
--- a/services/core/java/com/android/server/notification/flags.aconfig
+++ b/services/core/java/com/android/server/notification/flags.aconfig
@@ -212,16 +212,6 @@ flag {
}
flag {
- name: "limit_zen_config_size"
- namespace: "systemui"
- description: "Enforce a maximum (serialized) size for the Zen configuration"
- bug: "387498139"
- metadata {
- purpose: PURPOSE_BUGFIX
- }
-}
-
-flag {
name: "managed_services_concurrent_multiuser"
namespace: "systemui"
description: "Enables ManagedServices to support Concurrent multi user environment"
diff --git a/services/core/java/com/android/server/power/Notifier.java b/services/core/java/com/android/server/power/Notifier.java
index 102dc071c7b6..417b5c7ae62b 100644
--- a/services/core/java/com/android/server/power/Notifier.java
+++ b/services/core/java/com/android/server/power/Notifier.java
@@ -16,6 +16,7 @@
package com.android.server.power;
+import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SuppressLint;
import android.annotation.UserIdInt;
@@ -36,8 +37,8 @@ import android.os.BatteryStats;
import android.os.BatteryStatsInternal;
import android.os.Bundle;
import android.os.Handler;
-import android.os.IWakeLockCallback;
import android.os.IScreenTimeoutPolicyListener;
+import android.os.IWakeLockCallback;
import android.os.Looper;
import android.os.Message;
import android.os.PowerManager;
@@ -67,6 +68,7 @@ import com.android.internal.app.IBatteryStats;
import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.internal.util.FrameworkStatsLog;
+import com.android.internal.util.IndentingPrintWriter;
import com.android.server.EventLogTags;
import com.android.server.LocalServices;
import com.android.server.input.InputManagerInternal;
@@ -147,7 +149,8 @@ public class Notifier {
@Nullable private final StatusBarManagerInternal mStatusBarManagerInternal;
private final TrustManager mTrustManager;
private final Vibrator mVibrator;
- private final WakeLockLog mWakeLockLog;
+ @NonNull private final WakeLockLog mPartialWakeLockLog;
+ @NonNull private final WakeLockLog mFullWakeLockLog;
private final DisplayManagerInternal mDisplayManagerInternal;
private final NotifierHandler mHandler;
@@ -250,7 +253,9 @@ public class Notifier {
mShowWirelessChargingAnimationConfig = context.getResources().getBoolean(
com.android.internal.R.bool.config_showBuiltinWirelessChargingAnim);
- mWakeLockLog = mInjector.getWakeLockLog(context);
+ mFullWakeLockLog = mInjector.getWakeLockLog(context);
+ mPartialWakeLockLog = mInjector.getWakeLockLog(context);
+
// Initialize interactive state for battery stats.
try {
mBatteryStats.noteInteractive(true);
@@ -324,7 +329,8 @@ public class Notifier {
// Ignore
}
}
- mWakeLockLog.onWakeLockAcquired(tag, ownerUid, flags, /*eventTime=*/ -1);
+ getWakeLockLog(flags).onWakeLockAcquired(tag,
+ getUidForWakeLockLog(ownerUid, workSource), flags, /*eventTime=*/ -1);
}
mWakefulnessSessionObserver.onWakeLockAcquired(flags);
}
@@ -473,7 +479,8 @@ public class Notifier {
// Ignore
}
}
- mWakeLockLog.onWakeLockReleased(tag, ownerUid, /*eventTime=*/ -1);
+ getWakeLockLog(flags).onWakeLockReleased(tag,
+ getUidForWakeLockLog(ownerUid, workSource), /*eventTime=*/ -1);
}
mWakefulnessSessionObserver.onWakeLockReleased(flags, releaseReason);
}
@@ -960,11 +967,18 @@ public class Notifier {
* @param pw The stream to print to.
*/
public void dump(PrintWriter pw) {
- if (mWakeLockLog != null) {
- mWakeLockLog.dump(pw);
- }
+ pw.println("Notifier:");
+
+ IndentingPrintWriter ipw = new IndentingPrintWriter(pw, " ");
+ ipw.println("Partial Wakelock Log:");
+ mPartialWakeLockLog.dump(ipw);
+
+ ipw.println("");
+ ipw.println("Full Wakelock Log:");
+ mFullWakeLockLog.dump(ipw);
- mWakefulnessSessionObserver.dump(pw);
+ ipw.println("");
+ mWakefulnessSessionObserver.dump(ipw);
}
private void updatePendingBroadcastLocked() {
@@ -1232,7 +1246,9 @@ public class Notifier {
// Do Nothing
}
}
- mWakeLockLog.onWakeLockAcquired(tag, ownerUid, flags, currentTime);
+
+ getWakeLockLog(flags).onWakeLockAcquired(tag, getUidForWakeLockLog(ownerUid, workSource),
+ flags, currentTime);
}
@SuppressLint("AndroidFrameworkRequiresPermission")
@@ -1253,7 +1269,8 @@ public class Notifier {
// Ignore
}
}
- mWakeLockLog.onWakeLockReleased(tag, ownerUid, currentTime);
+ getWakeLockLog(flags).onWakeLockReleased(tag, getUidForWakeLockLog(ownerUid, workSource),
+ currentTime);
}
@SuppressLint("AndroidFrameworkRequiresPermission")
@@ -1419,6 +1436,15 @@ public class Notifier {
}
}
+ private @NonNull WakeLockLog getWakeLockLog(int flags) {
+ return PowerManagerService.isScreenLock(flags) ? mFullWakeLockLog : mPartialWakeLockLog;
+ }
+
+ private int getUidForWakeLockLog(int ownerUid, WorkSource workSource) {
+ int attributionUid = workSource != null ? workSource.getAttributionUid() : -1;
+ return attributionUid != -1 ? attributionUid : ownerUid;
+ }
+
private final class NotifierHandler extends Handler {
public NotifierHandler(Looper looper) {
@@ -1501,7 +1527,7 @@ public class Notifier {
/**
* Gets the WakeLockLog object
*/
- WakeLockLog getWakeLockLog(Context context);
+ @NonNull WakeLockLog getWakeLockLog(Context context);
/**
* Gets the AppOpsManager system service
@@ -1522,7 +1548,7 @@ public class Notifier {
}
@Override
- public WakeLockLog getWakeLockLog(Context context) {
+ public @NonNull WakeLockLog getWakeLockLog(Context context) {
return new WakeLockLog(context);
}
diff --git a/services/core/java/com/android/server/power/PowerManagerService.java b/services/core/java/com/android/server/power/PowerManagerService.java
index dd454cd61b2c..3eac4b54cd2b 100644
--- a/services/core/java/com/android/server/power/PowerManagerService.java
+++ b/services/core/java/com/android/server/power/PowerManagerService.java
@@ -1723,9 +1723,16 @@ public final class PowerManagerService extends SystemService
}
}
- @SuppressWarnings("deprecation")
private static boolean isScreenLock(final WakeLock wakeLock) {
- switch (wakeLock.mFlags & PowerManager.WAKE_LOCK_LEVEL_MASK) {
+ return isScreenLock(wakeLock.mFlags);
+ }
+
+ /**
+ * Returns if a wakelock flag corresponds to a screen wake lock.
+ */
+ @SuppressWarnings("deprecation")
+ public static boolean isScreenLock(int flags) {
+ switch (flags & PowerManager.WAKE_LOCK_LEVEL_MASK) {
case PowerManager.FULL_WAKE_LOCK:
case PowerManager.SCREEN_BRIGHT_WAKE_LOCK:
case PowerManager.SCREEN_DIM_WAKE_LOCK:
diff --git a/services/core/java/com/android/server/power/WakeLockLog.java b/services/core/java/com/android/server/power/WakeLockLog.java
index eda222e71c9e..7f152d6fc9fa 100644
--- a/services/core/java/com/android/server/power/WakeLockLog.java
+++ b/services/core/java/com/android/server/power/WakeLockLog.java
@@ -81,11 +81,12 @@ final class WakeLockLog {
private static final int TYPE_ACQUIRE = 0x1;
private static final int TYPE_RELEASE = 0x2;
private static final int MAX_LOG_ENTRY_BYTE_SIZE = 9;
- private static final int LOG_SIZE = 1024 * 10;
+ private static final int LOG_SIZE = 1024 * 3;
private static final int LOG_SIZE_MIN = MAX_LOG_ENTRY_BYTE_SIZE + 1;
- private static final int TAG_DATABASE_SIZE = 128;
+ private static final int TAG_DATABASE_SIZE = 64;
private static final int TAG_DATABASE_SIZE_MAX = 128;
+ private static final int TAG_DATABASE_STARTING_SIZE = 16;
private static final int LEVEL_SCREEN_TIMEOUT_OVERRIDE_WAKE_LOCK = 0;
private static final int LEVEL_PARTIAL_WAKE_LOCK = 1;
@@ -182,7 +183,7 @@ final class WakeLockLog {
* @param pw The {@code PrintWriter} to write to.
*/
public void dump(PrintWriter pw) {
- dump(pw, false);
+ dump(pw, /* includeTagDb= */ true);
}
@VisibleForTesting
@@ -1161,15 +1162,16 @@ final class WakeLockLog {
*/
static class TagDatabase {
private final int mInvalidIndex;
- private final TagData[] mArray;
+ private final int mMaxArraySize;
+ private TagData[] mArray;
private Callback mCallback;
TagDatabase(Injector injector) {
- int size = Math.min(injector.getTagDatabaseSize(), TAG_DATABASE_SIZE_MAX);
-
- // Largest possible index used as "INVALID", hence the (size - 1) sizing.
- mArray = new TagData[size - 1];
- mInvalidIndex = size - 1;
+ // Largest possible index used as "INVALID", hence the (size - 1) sizing
+ mMaxArraySize = Math.min(injector.getTagDatabaseSize(), TAG_DATABASE_SIZE_MAX - 1);
+ int startingSize = Math.min(mMaxArraySize, injector.getTagDatabaseStartingSize());
+ mArray = new TagData[startingSize];
+ mInvalidIndex = mMaxArraySize;
}
@Override
@@ -1195,8 +1197,10 @@ final class WakeLockLog {
sb.append(", entries: ").append(entries);
sb.append(", Bytes used: ").append(byteEstimate);
if (DEBUG) {
- sb.append(", Avg tag size: ").append(tagSize / tags);
- sb.append("\n ").append(Arrays.toString(mArray));
+ sb.append(", Avg tag size: ").append(tags == 0 ? 0 : (tagSize / tags));
+ for (int i = 0; i < mArray.length; i++) {
+ sb.append("\n [").append(i).append("] ").append(mArray[i]);
+ }
}
return sb.toString();
}
@@ -1284,6 +1288,18 @@ final class WakeLockLog {
return null;
}
+ // We don't have a spot available, see if we can still increase the array size
+ if (firstAvailable == -1) {
+ if (mArray.length < mMaxArraySize) {
+ int oldSize = mArray.length;
+ int newSize = Math.min(oldSize * 2, mMaxArraySize);
+ TagData[] newArray = new TagData[newSize];
+ System.arraycopy(mArray, 0, newArray, 0, oldSize);
+ mArray = newArray;
+ firstAvailable = oldSize;
+ }
+ }
+
// If we need to remove an index, report to listeners that we are removing an index.
boolean useOldest = firstAvailable == -1;
if (useOldest && mCallback != null) {
@@ -1402,6 +1418,10 @@ final class WakeLockLog {
return TAG_DATABASE_SIZE;
}
+ public int getTagDatabaseStartingSize() {
+ return TAG_DATABASE_STARTING_SIZE;
+ }
+
public int getLogSize() {
return LOG_SIZE;
}
diff --git a/services/core/java/com/android/server/wallpaper/WallpaperCropper.java b/services/core/java/com/android/server/wallpaper/WallpaperCropper.java
index 6e640d890fb8..424439df3c4b 100644
--- a/services/core/java/com/android/server/wallpaper/WallpaperCropper.java
+++ b/services/core/java/com/android/server/wallpaper/WallpaperCropper.java
@@ -856,10 +856,14 @@ public class WallpaperCropper {
BitmapFactory.decodeFile(wallpaperFile.getAbsolutePath(), options);
wallpaperImageSize.set(options.outWidth, options.outHeight);
}
+ boolean isRtl = TextUtils.getLayoutDirectionFromLocale(Locale.getDefault())
+ == View.LAYOUT_DIRECTION_RTL;
+ Rect croppedImageBound = getCrop(displaySize, mDefaultDisplayInfo, wallpaperImageSize,
+ getRelativeCropHints(wallpaperData), isRtl);
- double maxDisplayToImageRatio = Math.max((double) displaySize.x / wallpaperImageSize.x,
- (double) displaySize.y / wallpaperImageSize.y);
- if (maxDisplayToImageRatio > 1.5) {
+ double maxDisplayToImageRatio = Math.max((double) displaySize.x / croppedImageBound.width(),
+ (double) displaySize.y / croppedImageBound.height());
+ if (maxDisplayToImageRatio > 1.3) {
return false;
}
diff --git a/services/core/java/com/android/server/wm/DesktopModeBoundsCalculator.java b/services/core/java/com/android/server/wm/DesktopModeBoundsCalculator.java
index ce3ad889a308..d9354323ae7c 100644
--- a/services/core/java/com/android/server/wm/DesktopModeBoundsCalculator.java
+++ b/services/core/java/com/android/server/wm/DesktopModeBoundsCalculator.java
@@ -73,9 +73,12 @@ public final class DesktopModeBoundsCalculator {
final Rect stableBounds = new Rect();
task.getDisplayArea().getStableRect(stableBounds);
- // If the options bounds size is flexible, update size with calculated desired size.
+ final boolean hasFullscreenOverride = activity != null
+ && activity.mAppCompatController.getAspectRatioOverrides().hasFullscreenOverride();
+ // If the options bounds size is flexible and no fullscreen override has been applied,
+ // update size with calculated desired size.
final boolean updateOptionBoundsSize = options != null
- && options.getFlexibleLaunchSize();
+ && options.getFlexibleLaunchSize() && !hasFullscreenOverride;
// If cascading is also enabled, the position of the options bounds must be respected
// during the size update.
final boolean shouldRespectOptionPosition =
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index 76a39d9c884a..59a042981375 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -41,6 +41,7 @@ import static android.util.TypedValue.COMPLEX_UNIT_DIP;
import static android.util.TypedValue.COMPLEX_UNIT_MASK;
import static android.util.TypedValue.COMPLEX_UNIT_SHIFT;
import static android.view.Display.DEFAULT_DISPLAY;
+import static android.view.Display.FLAG_ALLOWS_CONTENT_MODE_SWITCH;
import static android.view.Display.FLAG_CAN_SHOW_WITH_INSECURE_KEYGUARD;
import static android.view.Display.FLAG_PRIVATE;
import static android.view.Display.FLAG_SHOULD_SHOW_SYSTEM_DECORATIONS;
@@ -2161,7 +2162,7 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
/** Re-show the previously hidden windows if all seamless rotated windows are done. */
void finishAsyncRotationIfPossible() {
final AsyncRotationController controller = mAsyncRotationController;
- if (controller != null && !mDisplayRotation.hasSeamlessRotatingWindow()) {
+ if (controller != null) {
controller.completeAll();
mAsyncRotationController = null;
}
@@ -2238,11 +2239,7 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
*/
private void applyRotation(final int oldRotation, final int rotation) {
mDisplayRotation.applyCurrentRotation(rotation);
- final boolean shellTransitions = mTransitionController.getTransitionPlayer() != null;
- final boolean rotateSeamlessly =
- mDisplayRotation.isRotatingSeamlessly() && !shellTransitions;
- final Transaction transaction =
- shellTransitions ? getSyncTransaction() : getPendingTransaction();
+
// We need to update our screen size information to match the new rotation. If the rotation
// has actually changed then this method will return true and, according to the comment at
// the top of the method, the caller is obligated to call computeNewConfigurationLocked().
@@ -2250,25 +2247,13 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
// #computeScreenConfiguration() later.
updateDisplayAndOrientation(null /* outConfig */);
- if (!shellTransitions) {
- forAllWindows(w -> {
- w.seamlesslyRotateIfAllowed(transaction, oldRotation, rotation, rotateSeamlessly);
- }, true /* traverseTopToBottom */);
- mPinnedTaskController.startSeamlessRotationIfNeeded(transaction, oldRotation, rotation);
- if (!mDisplayRotation.hasSeamlessRotatingWindow()) {
- // Make sure DisplayRotation#isRotatingSeamlessly() will return false.
- mDisplayRotation.cancelSeamlessRotation();
- }
- }
+ // Before setDisplayProjection is applied by the start transaction of transition,
+ // set the transform hint to avoid using surface in old rotation.
+ setFixedTransformHint(getPendingTransaction(), mSurfaceControl, rotation);
+ // The sync transaction should already contains setDisplayProjection, so unset the
+ // hint to restore the natural state when the transaction is applied.
+ getSyncTransaction().unsetFixedTransformHint(mSurfaceControl);
- if (shellTransitions) {
- // Before setDisplayProjection is applied by the start transaction of transition,
- // set the transform hint to avoid using surface in old rotation.
- setFixedTransformHint(getPendingTransaction(), mSurfaceControl, rotation);
- // The sync transaction should already contains setDisplayProjection, so unset the
- // hint to restore the natural state when the transaction is applied.
- transaction.unsetFixedTransformHint(mSurfaceControl);
- }
scheduleAnimation();
mWmService.mRotationWatcherController.dispatchDisplayRotationChange(mDisplayId, rotation);
@@ -2870,8 +2855,7 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
// If the transition finished callback cannot match the token for some reason, make sure the
// rotated state is cleared if it is already invisible.
if (mFixedRotationLaunchingApp != null && !mFixedRotationLaunchingApp.isVisibleRequested()
- && !mFixedRotationLaunchingApp.isVisible()
- && !mDisplayRotation.isRotatingSeamlessly()) {
+ && !mFixedRotationLaunchingApp.isVisible()) {
clearFixedRotationLaunchingApp();
}
// If there won't be a transition to notify the launch is done, then it should be ready to
@@ -3283,41 +3267,40 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
/* inTopology= */ shouldShowContent);
}
- /**
- * Whether the display is allowed to switch the content mode between extended and mirroring.
- * If the content mode is extended, the display will start home activity and show system
- * decorations, such as wallpapaer, status bar and navigation bar.
- * If the content mode is mirroring, the display will not show home activity or system
- * decorations.
- * The content mode is switched when {@link Display#canHostTasks()} changes.
- *
- * Note that we only allow displays that are able to show system decorations to use the content
- * mode switch; however, not all displays that are able to show system decorations are allowed
- * to use the content mode switch.
- */
- boolean allowContentModeSwitch() {
- // The default display should always show system decorations.
- if (isDefaultDisplay) {
+ /**
+ * Whether the display is allowed to switch the content mode between extended and mirroring.
+ * If the content mode is extended, the display will start home activity and show system
+ * decorations, such as wallpapaer, status bar and navigation bar.
+ * If the content mode is mirroring, the display will not show home activity or system
+ * decorations.
+ * The content mode is switched when {@link Display#canHostTasks()} changes.
+ *
+ * Note that we only allow displays that are able to show system decorations to use the content
+ * mode switch; however, not all displays that are able to show system decorations are allowed
+ * to use the content mode switch.
+ */
+ boolean allowContentModeSwitch() {
+ if ((mDisplay.getFlags() & FLAG_ALLOWS_CONTENT_MODE_SWITCH) == 0) {
return false;
}
- // Private display should never show system decorations.
- if (isPrivate()) {
+ // The default display should always show system decorations.
+ if (isDefaultDisplay) {
return false;
}
- if (shouldNeverShowSystemDecorations()) {
+ // Private or untrusted display should never show system decorations.
+ if (isPrivate() || !isTrusted()) {
return false;
}
- // TODO(b/391965805): Remove this after introducing FLAG_ALLOW_CONTENT_MODE_SWITCH.
- if ((mDisplay.getFlags() & Display.FLAG_REAR) != 0) {
+ if (shouldNeverShowSystemDecorations()) {
return false;
}
- // TODO(b/391965805): Remove this after introducing FLAG_ALLOW_CONTENT_MODE_SWITCH.
- // Virtual displays cannot add or remove system decorations during their lifecycle.
- if (mDisplay.getType() == Display.TYPE_VIRTUAL) {
+ // Display with FLAG_SHOULD_SHOW_SYSTEM_DECORATIONS enabled should always show system
+ // decorations, and should not switch the content mode.
+ if ((mDisplay.getFlags() & FLAG_SHOULD_SHOW_SYSTEM_DECORATIONS) != 0) {
return false;
}
@@ -5072,7 +5055,7 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
}
mLastHasContent = mTmpApplySurfaceChangesTransactionState.displayHasContent;
- if (!inTransition() && !mDisplayRotation.isRotatingSeamlessly()) {
+ if (!inTransition()) {
mWmService.mDisplayManagerInternal.setDisplayProperties(mDisplayId,
mLastHasContent,
mTmpApplySurfaceChangesTransactionState.preferredRefreshRate,
diff --git a/services/core/java/com/android/server/wm/DisplayRotation.java b/services/core/java/com/android/server/wm/DisplayRotation.java
index 9cf792d82f56..9edbb70c3b74 100644
--- a/services/core/java/com/android/server/wm/DisplayRotation.java
+++ b/services/core/java/com/android/server/wm/DisplayRotation.java
@@ -168,19 +168,6 @@ public class DisplayRotation {
private int mDeferredRotationPauseCount;
/**
- * A count of the windows which are 'seamlessly rotated', e.g. a surface at an old orientation
- * is being transformed. We freeze orientation updates while any windows are seamlessly rotated,
- * so we need to track when this hits zero so we can apply deferred orientation updates.
- */
- private int mSeamlessRotationCount;
-
- /**
- * True in the interval from starting seamless rotation until the last rotated window draws in
- * the new orientation.
- */
- private boolean mRotatingSeamlessly;
-
- /**
* Behavior of rotation suggestions.
*
* @see Settings.Secure#SHOW_ROTATION_SUGGESTIONS
@@ -630,15 +617,6 @@ public class DisplayRotation {
return true;
}
- if (shouldRotateSeamlessly(oldRotation, rotation, forceUpdate)) {
- // The screen rotation animation uses a screenshot to freeze the screen while windows
- // resize underneath. When we are rotating seamlessly, we allow the elements to
- // transition to their rotated state independently and without a freeze required.
- prepareSeamlessRotation();
- } else {
- cancelSeamlessRotation();
- }
-
// Give a remote handler (system ui) some time to reposition things.
startRemoteRotation(oldRotation, mRotation);
@@ -677,42 +655,6 @@ public class DisplayRotation {
}
}
- /**
- * This ensures that normal rotation animation is used. E.g. {@link #mRotatingSeamlessly} was
- * set by previous {@link #updateRotationUnchecked}, but another orientation change happens
- * before calling {@link DisplayContent#sendNewConfiguration} (remote rotation hasn't finished)
- * and it doesn't choose seamless rotation.
- */
- void cancelSeamlessRotation() {
- if (!mRotatingSeamlessly) {
- return;
- }
- mDisplayContent.forAllWindows(w -> {
- if (w.mSeamlesslyRotated) {
- w.cancelSeamlessRotation();
- w.mSeamlesslyRotated = false;
- }
- }, true /* traverseTopToBottom */);
- mSeamlessRotationCount = 0;
- mRotatingSeamlessly = false;
- mDisplayContent.finishAsyncRotationIfPossible();
- }
-
- private void prepareSeamlessRotation() {
- // We are careful to reset this in case a window was removed before it finished
- // seamless rotation.
- mSeamlessRotationCount = 0;
- mRotatingSeamlessly = true;
- }
-
- boolean isRotatingSeamlessly() {
- return mRotatingSeamlessly;
- }
-
- boolean hasSeamlessRotatingWindow() {
- return mSeamlessRotationCount > 0;
- }
-
@VisibleForTesting
boolean shouldRotateSeamlessly(int oldRotation, int newRotation, boolean forceUpdate) {
// Display doesn't need to be frozen because application has been started in correct
@@ -750,13 +692,6 @@ public class DisplayRotation {
return false;
}
- // We can't rotate (seamlessly or not) while waiting for the last seamless rotation to
- // complete (that is, waiting for windows to redraw). It's tempting to check
- // mSeamlessRotationCount but that could be incorrect in the case of window-removal.
- if (!forceUpdate && mDisplayContent.getWindow(win -> win.mSeamlesslyRotated) != null) {
- return false;
- }
-
return true;
}
@@ -774,28 +709,6 @@ public class DisplayRotation {
return oldRotation != Surface.ROTATION_180 && newRotation != Surface.ROTATION_180;
}
- void markForSeamlessRotation(WindowState w, boolean seamlesslyRotated) {
- if (seamlesslyRotated == w.mSeamlesslyRotated || w.mForceSeamlesslyRotate) {
- return;
- }
-
- w.mSeamlesslyRotated = seamlesslyRotated;
- if (seamlesslyRotated) {
- mSeamlessRotationCount++;
- } else {
- mSeamlessRotationCount--;
- }
- if (mSeamlessRotationCount == 0) {
- ProtoLog.i(WM_DEBUG_ORIENTATION,
- "Performing post-rotate rotation after seamless rotation");
- // Finish seamless rotation.
- mRotatingSeamlessly = false;
- mDisplayContent.finishAsyncRotationIfPossible();
-
- updateRotationAndSendNewConfigIfChanged();
- }
- }
-
void restoreSettings(int userRotationMode, int userRotation, int fixedToUserRotation) {
mFixedToUserRotation = fixedToUserRotation;
diff --git a/services/core/java/com/android/server/wm/PinnedTaskController.java b/services/core/java/com/android/server/wm/PinnedTaskController.java
index 6dd7d35856df..6e59828c8ff2 100644
--- a/services/core/java/com/android/server/wm/PinnedTaskController.java
+++ b/services/core/java/com/android/server/wm/PinnedTaskController.java
@@ -21,20 +21,13 @@ import static android.app.WindowConfiguration.ROTATION_UNDEFINED;
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
-import android.app.PictureInPictureParams;
import android.content.res.Resources;
-import android.graphics.Insets;
-import android.graphics.Matrix;
import android.graphics.Rect;
import android.os.IBinder;
import android.os.RemoteException;
import android.util.Log;
-import android.util.RotationUtils;
import android.util.Slog;
import android.view.IPinnedTaskListener;
-import android.view.Surface;
-import android.view.SurfaceControl;
-import android.window.PictureInPictureSurfaceTransaction;
import java.io.PrintWriter;
@@ -71,11 +64,7 @@ class PinnedTaskController {
* based on the new rotation.
*/
private Rect mDestRotatedBounds;
- /**
- * Non-null if the entering PiP task from recents animation will cause display rotation to
- * change. The transaction is based on the old rotation.
- */
- private PictureInPictureSurfaceTransaction mPipTransaction;
+
/** Whether to skip task configuration change once. */
private boolean mFreezingTaskConfig;
/** Defer display orientation change if the PiP task is animating across orientations. */
@@ -212,14 +201,12 @@ class PinnedTaskController {
}
/**
- * Sets the transaction for {@link #startSeamlessRotationIfNeeded} if the orientation of display
- * will be changed. This is only called when finishing recents animation with pending
- * orientation change that will be handled by
- * {@link DisplayContent.FixedRotationTransitionListener#onFinishRecentsAnimation}.
+ * Sets a hint if the orientation of display will be changed. This is only called when
+ * finishing recents animation with pending orientation change that will be handled by
+ * {@link DisplayContent.FixedRotationTransitionListener}.
*/
- void setEnterPipTransaction(PictureInPictureSurfaceTransaction tx) {
+ void setEnterPipWithRotatedTransientLaunch() {
mFreezingTaskConfig = true;
- mPipTransaction = tx;
}
/** Called when the activity in PiP task has PiP windowing mode (at the end of animation). */
@@ -233,81 +220,6 @@ class PinnedTaskController {
}
/**
- * Resets rotation and applies scale and position to PiP task surface to match the current
- * rotation of display. The final surface matrix will be replaced by PiPTaskOrganizer after it
- * receives the callback of fixed rotation completion.
- */
- void startSeamlessRotationIfNeeded(SurfaceControl.Transaction t,
- int oldRotation, int newRotation) {
- final Rect bounds = mDestRotatedBounds;
- final PictureInPictureSurfaceTransaction pipTx = mPipTransaction;
- final boolean emptyPipPositionTx = pipTx == null || pipTx.mPosition == null;
- if (bounds == null && emptyPipPositionTx) {
- return;
- }
- final TaskDisplayArea taskArea = mDisplayContent.getDefaultTaskDisplayArea();
- final Task pinnedTask = taskArea.getRootPinnedTask();
- if (pinnedTask == null) {
- return;
- }
-
- mDestRotatedBounds = null;
- mPipTransaction = null;
- final Rect areaBounds = taskArea.getBounds();
- if (!emptyPipPositionTx) {
- // The transaction from recents animation is in old rotation. So the position needs to
- // be rotated.
- float dx = pipTx.mPosition.x;
- float dy = pipTx.mPosition.y;
- final Matrix matrix = pipTx.getMatrix();
- if (pipTx.mRotation == 90) {
- dx = pipTx.mPosition.y;
- dy = areaBounds.right - pipTx.mPosition.x;
- matrix.postRotate(-90);
- } else if (pipTx.mRotation == -90) {
- dx = areaBounds.bottom - pipTx.mPosition.y;
- dy = pipTx.mPosition.x;
- matrix.postRotate(90);
- }
- matrix.postTranslate(dx, dy);
- final SurfaceControl leash = pinnedTask.getSurfaceControl();
- t.setMatrix(leash, matrix, new float[9]);
- if (pipTx.hasCornerRadiusSet()) {
- t.setCornerRadius(leash, pipTx.mCornerRadius);
- }
- Slog.i(TAG, "Seamless rotation PiP tx=" + pipTx + " pos=" + dx + "," + dy);
- return;
- }
-
- final PictureInPictureParams params = pinnedTask.getPictureInPictureParams();
- final Rect sourceHintRect = params != null && params.hasSourceBoundsHint()
- ? params.getSourceRectHint()
- : null;
- Slog.i(TAG, "Seamless rotation PiP bounds=" + bounds + " hintRect=" + sourceHintRect);
- final int rotationDelta = RotationUtils.deltaRotation(oldRotation, newRotation);
- // Adjust for display cutout if applicable.
- if (sourceHintRect != null && rotationDelta == Surface.ROTATION_270) {
- if (pinnedTask.getDisplayCutoutInsets() != null) {
- final int rotationBackDelta = RotationUtils.deltaRotation(newRotation, oldRotation);
- final Rect displayCutoutInsets = RotationUtils.rotateInsets(
- Insets.of(pinnedTask.getDisplayCutoutInsets()), rotationBackDelta).toRect();
- sourceHintRect.offset(displayCutoutInsets.left, displayCutoutInsets.top);
- }
- }
- final Rect contentBounds = sourceHintRect != null && areaBounds.contains(sourceHintRect)
- ? sourceHintRect : areaBounds;
- final int w = contentBounds.width();
- final int h = contentBounds.height();
- final float scale = w <= h ? (float) bounds.width() / w : (float) bounds.height() / h;
- final int insetLeft = (int) ((contentBounds.left - areaBounds.left) * scale + .5f);
- final int insetTop = (int) ((contentBounds.top - areaBounds.top) * scale + .5f);
- final Matrix matrix = new Matrix();
- matrix.setScale(scale, scale);
- matrix.postTranslate(bounds.left - insetLeft, bounds.top - insetTop);
- t.setMatrix(pinnedTask.getSurfaceControl(), matrix, new float[9]);
- }
-
- /**
* Returns {@code true} to skip {@link Task#onConfigurationChanged} because it is expected that
* there will be a orientation change and a PiP configuration change.
*/
@@ -321,7 +233,6 @@ class PinnedTaskController {
mFreezingTaskConfig = false;
mDeferOrientationChanging = false;
mDestRotatedBounds = null;
- mPipTransaction = null;
}
/**
@@ -381,9 +292,6 @@ class PinnedTaskController {
if (mDestRotatedBounds != null) {
pw.println(prefix + " mPendingBounds=" + mDestRotatedBounds);
}
- if (mPipTransaction != null) {
- pw.println(prefix + " mPipTransaction=" + mPipTransaction);
- }
pw.println(prefix + " mIsImeShowing=" + mIsImeShowing);
pw.println(prefix + " mImeHeight=" + mImeHeight);
pw.println(prefix + " mMinAspectRatio=" + mMinAspectRatio);
diff --git a/services/core/java/com/android/server/wm/RootWindowContainer.java b/services/core/java/com/android/server/wm/RootWindowContainer.java
index 609302ce3f56..242aea941429 100644
--- a/services/core/java/com/android/server/wm/RootWindowContainer.java
+++ b/services/core/java/com/android/server/wm/RootWindowContainer.java
@@ -79,8 +79,6 @@ import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
import static com.android.server.wm.WindowManagerService.UPDATE_FOCUS_NORMAL;
import static com.android.server.wm.WindowManagerService.UPDATE_FOCUS_PLACING_SURFACES;
import static com.android.server.wm.WindowManagerService.UPDATE_FOCUS_WILL_PLACE_SURFACES;
-import static com.android.server.wm.WindowSurfacePlacer.SET_UPDATE_ROTATION;
-import static com.android.server.wm.WindowSurfacePlacer.SET_WALLPAPER_ACTION_PENDING;
import static java.lang.Integer.MAX_VALUE;
@@ -655,9 +653,6 @@ class RootWindowContainer extends WindowContainer<DisplayContent>
final int count = mChildren.size();
for (int i = 0; i < count; ++i) {
final int pendingChanges = mChildren.get(i).pendingLayoutChanges;
- if ((pendingChanges & FINISH_LAYOUT_REDO_WALLPAPER) != 0) {
- animator.mBulkUpdateParams |= SET_WALLPAPER_ACTION_PENDING;
- }
if (pendingChanges != 0) {
hasChanges = true;
}
@@ -1024,18 +1019,6 @@ class RootWindowContainer extends WindowContainer<DisplayContent>
return changed;
}
- boolean copyAnimToLayoutParams() {
- boolean doRequest = false;
-
- final int bulkUpdateParams = mWmService.mAnimator.mBulkUpdateParams;
- if ((bulkUpdateParams & SET_UPDATE_ROTATION) != 0) {
- mUpdateRotation = true;
- doRequest = true;
- }
-
- return doRequest;
- }
-
private final class MyHandler extends Handler {
public MyHandler(Looper looper) {
diff --git a/services/core/java/com/android/server/wm/Transition.java b/services/core/java/com/android/server/wm/Transition.java
index 803c21ccab6e..30313fc63857 100644
--- a/services/core/java/com/android/server/wm/Transition.java
+++ b/services/core/java/com/android/server/wm/Transition.java
@@ -1249,7 +1249,7 @@ class Transition implements BLASTSyncEngine.TransactionReadyListener {
// Skip dispatching the change for PiP task to avoid its activity drawing for the
// intermediate state which will cause flickering. The final PiP bounds in new
// rotation will be applied by PipTransition.
- ar.mDisplayContent.mPinnedTaskController.setEnterPipTransaction(null);
+ ar.mDisplayContent.mPinnedTaskController.setEnterPipWithRotatedTransientLaunch();
}
return inPip;
}
diff --git a/services/core/java/com/android/server/wm/WindowAnimator.java b/services/core/java/com/android/server/wm/WindowAnimator.java
index 80137a298ac2..3f2b40c1d7c9 100644
--- a/services/core/java/com/android/server/wm/WindowAnimator.java
+++ b/services/core/java/com/android/server/wm/WindowAnimator.java
@@ -65,9 +65,6 @@ public class WindowAnimator {
/** Time of current animation step. Reset on each iteration */
long mCurrentTime;
- int mBulkUpdateParams = 0;
- Object mLastWindowFreezeSource;
-
private boolean mInitialized = false;
private Choreographer mChoreographer;
@@ -145,7 +142,6 @@ public class WindowAnimator {
final int animationFlags = useShellTransition ? CHILDREN : (TRANSITION | CHILDREN);
boolean rootAnimating = false;
mCurrentTime = frameTimeNs / TimeUtils.NANOS_PER_MS;
- mBulkUpdateParams = 0;
if (DEBUG_WINDOW_TRACE) {
Slog.i(TAG, "!!! animate: entry time=" + mCurrentTime);
}
@@ -202,8 +198,7 @@ public class WindowAnimator {
}
final boolean hasPendingLayoutChanges = root.hasPendingLayoutChanges(this);
- final boolean doRequest = mBulkUpdateParams != 0 && root.copyAnimToLayoutParams();
- if (hasPendingLayoutChanges || doRequest) {
+ if (hasPendingLayoutChanges) {
mService.mWindowPlacerLocked.requestTraversal();
}
@@ -245,7 +240,6 @@ public class WindowAnimator {
if (DEBUG_WINDOW_TRACE) {
Slog.i(TAG, "!!! animate: exit"
- + " mBulkUpdateParams=" + Integer.toHexString(mBulkUpdateParams)
+ " hasPendingLayoutChanges=" + hasPendingLayoutChanges);
}
}
@@ -265,17 +259,6 @@ public class WindowAnimator {
mRunningExpensiveAnimations = runningExpensiveAnimations;
}
- private static String bulkUpdateParamsToString(int bulkUpdateParams) {
- StringBuilder builder = new StringBuilder(128);
- if ((bulkUpdateParams & WindowSurfacePlacer.SET_UPDATE_ROTATION) != 0) {
- builder.append(" UPDATE_ROTATION");
- }
- if ((bulkUpdateParams & WindowSurfacePlacer.SET_WALLPAPER_ACTION_PENDING) != 0) {
- builder.append(" SET_WALLPAPER_ACTION_PENDING");
- }
- return builder.toString();
- }
-
public void dumpLocked(PrintWriter pw, String prefix, boolean dumpAll) {
final String subPrefix = " " + prefix;
@@ -292,11 +275,6 @@ public class WindowAnimator {
pw.print(prefix); pw.print("mCurrentTime=");
pw.println(TimeUtils.formatUptime(mCurrentTime));
}
- if (mBulkUpdateParams != 0) {
- pw.print(prefix); pw.print("mBulkUpdateParams=0x");
- pw.print(Integer.toHexString(mBulkUpdateParams));
- pw.println(bulkUpdateParamsToString(mBulkUpdateParams));
- }
}
void scheduleAnimation() {
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index c078d67b6cc6..ff43d72c5a07 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -198,6 +198,8 @@ import android.graphics.Rect;
import android.graphics.Region;
import android.hardware.configstore.V1_0.OptionalBool;
import android.hardware.configstore.V1_1.ISurfaceFlingerConfigs;
+import android.hardware.devicestate.DeviceState;
+import android.hardware.devicestate.DeviceStateManager;
import android.hardware.display.DisplayManager;
import android.hardware.display.DisplayManagerInternal;
import android.hardware.input.InputSettings;
@@ -1032,6 +1034,21 @@ public class WindowManagerService extends IWindowManager.Stub
PowerManager mPowerManager;
PowerManagerInternal mPowerManagerInternal;
+ private DeviceStateManager mDeviceStateManager;
+ private DeviceStateCallback mDeviceStateCallback;
+ private class DeviceStateCallback implements DeviceStateManager.DeviceStateCallback {
+ private DeviceState mCurrentDeviceState;
+ @Override
+ public void onDeviceStateChanged(@NonNull DeviceState state) {
+ mCurrentDeviceState = state;
+ }
+
+ boolean isInRearDisplayOuterDefaultState() {
+ return mCurrentDeviceState != null && mCurrentDeviceState
+ .hasProperties(DeviceState.PROPERTY_FEATURE_REAR_DISPLAY_OUTER_DEFAULT);
+ }
+ }
+
private float mWindowAnimationScaleSetting = 1.0f;
private float mTransitionAnimationScaleSetting = 1.0f;
private float mAnimatorDurationScaleSetting = 1.0f;
@@ -1317,6 +1334,10 @@ public class WindowManagerService extends IWindowManager.Stub
mPowerManager = (PowerManager)context.getSystemService(Context.POWER_SERVICE);
mPowerManagerInternal = LocalServices.getService(PowerManagerInternal.class);
+ mDeviceStateManager = context.getSystemService(DeviceStateManager.class);
+ mDeviceStateCallback = new DeviceStateCallback();
+ mDeviceStateManager.registerCallback(new HandlerExecutor(mH), mDeviceStateCallback);
+
if (mPowerManagerInternal != null) {
mPowerManagerInternal.registerLowPowerModeObserver(
new PowerManagerInternal.LowPowerModeListener() {
@@ -2132,7 +2153,6 @@ public class WindowManagerService extends IWindowManager.Stub
}
final DisplayContent dc = win.getDisplayContent();
- dc.getDisplayRotation().markForSeamlessRotation(win, false /* seamlesslyRotated */);
win.resetAppOpsState();
@@ -8950,6 +8970,17 @@ public class WindowManagerService extends IWindowManager.Stub
}
}
+ if (mDeviceStateCallback.isInRearDisplayOuterDefaultState()) {
+ final Display[] rearDisplays = mDisplayManager
+ .getDisplays(DisplayManager.DISPLAY_CATEGORY_REAR);
+ if (rearDisplays.length > 0 && rearDisplays[0].getDisplayId() == t.getDisplayId()) {
+ // Do not change display focus to the inner display if we're in this mode. Note that
+ // in this mode, the inner display is configured as a rear display.
+ Slog.w(TAG, "Ignoring focus change because device is in RDM.");
+ return;
+ }
+ }
+
clearPointerDownOutsideFocusRunnable();
final InputTarget focusedInputTarget = mFocusedInputTarget;
diff --git a/services/core/java/com/android/server/wm/WindowOrganizerController.java b/services/core/java/com/android/server/wm/WindowOrganizerController.java
index 6c3d51622ff5..c19fa8c03e0a 100644
--- a/services/core/java/com/android/server/wm/WindowOrganizerController.java
+++ b/services/core/java/com/android/server/wm/WindowOrganizerController.java
@@ -473,35 +473,6 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub
transition.setAllReady();
}
- // TODO(b/365884835): remove this method and callers.
- @Override
- public int startLegacyTransition(int type, @NonNull RemoteAnimationAdapter adapter,
- @NonNull IWindowContainerTransactionCallback callback,
- @NonNull WindowContainerTransaction t) {
- enforceTaskPermission("startLegacyTransition()");
- final CallerInfo caller = new CallerInfo();
- final long ident = Binder.clearCallingIdentity();
- int syncId;
- try {
- synchronized (mGlobalLock) {
- if (type < 0) {
- throw new IllegalArgumentException("Can't create transition with no type");
- }
- if (mTransitionController.getTransitionPlayer() != null) {
- throw new IllegalArgumentException("Can't use legacy transitions in"
- + " when shell transitions are enabled.");
- }
- syncId = startSyncWithOrganizer(callback);
- applyTransaction(t, syncId, mService.mChainTracker.startLegacy("legacyTransit"),
- caller);
- setSyncReady(syncId);
- }
- } finally {
- Binder.restoreCallingIdentity(ident);
- }
- return syncId;
- }
-
@Override
public void finishTransition(@NonNull IBinder transitionToken,
@Nullable WindowContainerTransaction t) {
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index 4a93904e466c..a270af56cbcd 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -169,7 +169,6 @@ import static com.android.server.wm.WindowStateProto.IS_READY_FOR_DISPLAY;
import static com.android.server.wm.WindowStateProto.IS_VISIBLE;
import static com.android.server.wm.WindowStateProto.KEEP_CLEAR_AREAS;
import static com.android.server.wm.WindowStateProto.MERGED_LOCAL_INSETS_SOURCES;
-import static com.android.server.wm.WindowStateProto.PENDING_SEAMLESS_ROTATION;
import static com.android.server.wm.WindowStateProto.REMOVED;
import static com.android.server.wm.WindowStateProto.REMOVE_ON_EXIT;
import static com.android.server.wm.WindowStateProto.REQUESTED_HEIGHT;
@@ -231,7 +230,6 @@ import android.view.InsetsSource;
import android.view.InsetsSourceControl;
import android.view.InsetsState;
import android.view.Surface;
-import android.view.Surface.Rotation;
import android.view.SurfaceControl;
import android.view.View;
import android.view.ViewDebug;
@@ -399,7 +397,6 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
* rotation.
*/
final boolean mForceSeamlesslyRotate;
- SeamlessRotator mPendingSeamlessRotate;
private RemoteCallbackList<IWindowFocusObserver> mFocusCallbacks;
@@ -593,13 +590,6 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
/** The time when the window was last requested to redraw for orientation change. */
private long mOrientationChangeRedrawRequestTime;
- /**
- * The orientation during the last visible call to relayout. If our
- * current orientation is different, the window can't be ready
- * to be shown.
- */
- int mLastVisibleLayoutRotation = -1;
-
/** Is this window now (or just being) removed? */
boolean mRemoved;
@@ -655,15 +645,6 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
boolean mIsSurfacePositionPaused;
/**
- * During seamless rotation we have two phases, first the old window contents
- * are rotated to look as if they didn't move in the new coordinate system. Then we
- * have to freeze updates to this layer (to preserve the transformation) until
- * the resize actually occurs. This is true from when the transformation is set
- * and false until the transaction to resize is sent.
- */
- boolean mSeamlesslyRotated = false;
-
- /**
* Whether the IME insets have been consumed. If {@code true}, this window won't be able to
* receive visible IME insets; {@code false}, otherwise.
*/
@@ -778,11 +759,6 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
*/
private boolean mInsetsAnimationRunning;
- private final Consumer<SurfaceControl.Transaction> mSeamlessRotationFinishedConsumer = t -> {
- finishSeamlessRotation(t);
- updateSurfacePosition(t);
- };
-
private final Consumer<SurfaceControl.Transaction> mSetSurfacePositionConsumer = t -> {
// Only apply the position to the surface when there's no leash created.
if (mSurfaceControl != null && mSurfaceControl.isValid() && !mSurfaceAnimator.hasLeash()) {
@@ -899,69 +875,6 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
return visible && mFrozenInsetsState == null;
}
- void seamlesslyRotateIfAllowed(Transaction transaction, @Rotation int oldRotation,
- @Rotation int rotation, boolean requested) {
- // Invisible windows and the wallpaper do not participate in the seamless rotation animation
- if (!isVisibleNow() || mIsWallpaper) {
- return;
- }
-
- if (mToken.hasFixedRotationTransform()) {
- // The transform of its surface is handled by fixed rotation.
- return;
- }
- final Task task = getTask();
- if (task != null && task.inPinnedWindowingMode()) {
- // It is handled by PinnedTaskController. Note that the windowing mode of activity
- // and windows may still be fullscreen.
- return;
- }
-
- if (mPendingSeamlessRotate != null) {
- oldRotation = mPendingSeamlessRotate.getOldRotation();
- }
-
- // Skip performing seamless rotation when the controlled insets is IME with visible state.
- if (mControllableInsetProvider != null
- && mControllableInsetProvider.getSource().getType() == WindowInsets.Type.ime()) {
- return;
- }
-
- if (mForceSeamlesslyRotate || requested) {
- if (mControllableInsetProvider != null) {
- mControllableInsetProvider.startSeamlessRotation();
- }
- mPendingSeamlessRotate = new SeamlessRotator(oldRotation, rotation, getDisplayInfo(),
- false /* applyFixedTransformationHint */);
- // The surface position is going to be unrotated according to the last position.
- // Make sure the source position is up-to-date.
- mLastSurfacePosition.set(mSurfacePosition.x, mSurfacePosition.y);
- mPendingSeamlessRotate.unrotate(transaction, this);
- getDisplayContent().getDisplayRotation().markForSeamlessRotation(this,
- true /* seamlesslyRotated */);
- applyWithNextDraw(mSeamlessRotationFinishedConsumer);
- }
- }
-
- void cancelSeamlessRotation() {
- finishSeamlessRotation(getPendingTransaction());
- }
-
- void finishSeamlessRotation(SurfaceControl.Transaction t) {
- if (mPendingSeamlessRotate == null) {
- return;
- }
-
- mPendingSeamlessRotate.finish(t, this);
- mPendingSeamlessRotate = null;
-
- getDisplayContent().getDisplayRotation().markForSeamlessRotation(this,
- false /* seamlesslyRotated */);
- if (mControllableInsetProvider != null) {
- mControllableInsetProvider.finishSeamlessRotation();
- }
- }
-
List<Rect> getSystemGestureExclusion() {
return mExclusionRects;
}
@@ -2176,8 +2089,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
&& (mAttrs.privateFlags & PRIVATE_FLAG_NO_MOVE_ANIMATION) == 0
&& !isDragResizing()
&& hasMovementAnimation
- && !mWinAnimator.mLastHidden
- && !mSeamlesslyRotated;
+ && !mWinAnimator.mLastHidden;
}
/**
@@ -3998,7 +3910,6 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
proto.write(REMOVED, mRemoved);
proto.write(IS_ON_SCREEN, isOnScreen());
proto.write(IS_VISIBLE, isVisible);
- proto.write(PENDING_SEAMLESS_ROTATION, mPendingSeamlessRotate != null);
proto.write(FORCE_SEAMLESS_ROTATION, mForceSeamlesslyRotate);
proto.write(HAS_COMPAT_SCALE, hasCompatScale());
proto.write(GLOBAL_SCALE, mGlobalScale);
@@ -4144,14 +4055,6 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
+ " mDestroying=" + mDestroying
+ " mRemoved=" + mRemoved);
}
- pw.print(prefix + "mForceSeamlesslyRotate=" + mForceSeamlesslyRotate
- + " seamlesslyRotate: pending=");
- if (mPendingSeamlessRotate != null) {
- mPendingSeamlessRotate.dump(pw);
- } else {
- pw.print("null");
- }
- pw.println();
if (mXOffset != 0 || mYOffset != 0) {
pw.println(prefix + "mXOffset=" + mXOffset + " mYOffset=" + mYOffset);
@@ -4883,8 +4786,6 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
mWinAnimator.mEnterAnimationPending = true;
}
- mLastVisibleLayoutRotation = getDisplayContent().getRotation();
-
mWinAnimator.mEnteringAnimation = true;
Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "prepareToDisplay");
@@ -5282,9 +5183,8 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
final AsyncRotationController asyncRotationController =
mDisplayContent.getAsyncRotationController();
- if ((asyncRotationController != null
- && asyncRotationController.hasSeamlessOperation(mToken))
- || mPendingSeamlessRotate != null) {
+ if (asyncRotationController != null
+ && asyncRotationController.hasSeamlessOperation(mToken)) {
// Freeze position while un-rotating the window, so its surface remains at the position
// corresponding to the original rotation.
return;
diff --git a/services/core/java/com/android/server/wm/WindowSurfacePlacer.java b/services/core/java/com/android/server/wm/WindowSurfacePlacer.java
index a34b5115faf9..4fb74ef00914 100644
--- a/services/core/java/com/android/server/wm/WindowSurfacePlacer.java
+++ b/services/core/java/com/android/server/wm/WindowSurfacePlacer.java
@@ -42,9 +42,6 @@ class WindowSurfacePlacer {
/** Only do a maximum of 6 repeated layouts. After that quit */
private int mLayoutRepeatCount;
- static final int SET_UPDATE_ROTATION = 1 << 0;
- static final int SET_WALLPAPER_ACTION_PENDING = 1 << 1;
-
private boolean mTraversalScheduled;
private int mDeferDepth = 0;
/** The number of layout requests when deferring. */
diff --git a/services/core/jni/com_android_server_input_InputManagerService.cpp b/services/core/jni/com_android_server_input_InputManagerService.cpp
index e32ce525cb40..ec8794f8073f 100644
--- a/services/core/jni/com_android_server_input_InputManagerService.cpp
+++ b/services/core/jni/com_android_server_input_InputManagerService.cpp
@@ -671,9 +671,13 @@ void NativeInputManager::setDisplayTopology(JNIEnv* env, jobject topologyGraph)
return;
}
- // TODO(b/383092013): Add topology validation
const DisplayTopologyGraph displayTopology =
android_hardware_display_DisplayTopologyGraph_toNative(env, topologyGraph);
+ if (input_flags::enable_display_topology_validation() && !displayTopology.isValid()) {
+ LOG(ERROR) << "Ignoring Invalid DisplayTopology";
+ return;
+ }
+
mInputManager->getDispatcher().setDisplayTopology(displayTopology);
mInputManager->getChoreographer().setDisplayTopology(displayTopology);
}
diff --git a/services/foldables/devicestateprovider/src/com/android/server/policy/FoldableDeviceStateProvider.java b/services/foldables/devicestateprovider/src/com/android/server/policy/FoldableDeviceStateProvider.java
index de78271acddc..69e856de401a 100644
--- a/services/foldables/devicestateprovider/src/com/android/server/policy/FoldableDeviceStateProvider.java
+++ b/services/foldables/devicestateprovider/src/com/android/server/policy/FoldableDeviceStateProvider.java
@@ -325,7 +325,8 @@ public final class FoldableDeviceStateProvider implements DeviceStateProvider,
}
if (newState != INVALID_DEVICE_STATE_IDENTIFIER && newState != mLastReportedState) {
- if (Trace.isTagEnabled(TRACE_TAG_SYSTEM_SERVER)) {
+ if (mLastHingeAngleSensorEvent != null
+ && Trace.isTagEnabled(TRACE_TAG_SYSTEM_SERVER)) {
Trace.instant(TRACE_TAG_SYSTEM_SERVER,
"[Device state changed] Last hinge sensor event timestamp: "
+ mLastHingeAngleSensorEvent.timestamp);
diff --git a/services/tests/displayservicetests/src/com/android/server/display/DisplayPowerProximityStateControllerTest.java b/services/tests/displayservicetests/src/com/android/server/display/DisplayPowerProximityStateControllerTest.java
index 29f07227a12d..fecbc7c81347 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/DisplayPowerProximityStateControllerTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/DisplayPowerProximityStateControllerTest.java
@@ -25,7 +25,7 @@ import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.times;
-import static org.mockito.Mockito.verifyZeroInteractions;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
import static org.mockito.Mockito.when;
import android.hardware.Sensor;
@@ -396,7 +396,7 @@ public final class DisplayPowerProximityStateControllerTest {
assertTrue(mDisplayPowerProximityStateController.isProximitySensorEnabled());
assertFalse(mDisplayPowerProximityStateController.shouldIgnoreProximityUntilChanged());
assertFalse(mDisplayPowerProximityStateController.isScreenOffBecauseOfProximity());
- verifyZeroInteractions(mWakelockController);
+ verifyNoMoreInteractions(mWakelockController);
}
private void setScreenOffBecauseOfPositiveProximityState() {
diff --git a/services/tests/displayservicetests/src/com/android/server/display/LocalDisplayAdapterTest.java b/services/tests/displayservicetests/src/com/android/server/display/LocalDisplayAdapterTest.java
index f8b4113a3c04..b8a5f341eb8a 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/LocalDisplayAdapterTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/LocalDisplayAdapterTest.java
@@ -234,6 +234,7 @@ public class LocalDisplayAdapterTest {
doReturn(true).when(mFlags).isDisplayOffloadEnabled();
doReturn(true).when(mFlags).isEvenDimmerEnabled();
+ doReturn(true).when(mFlags).isDisplayContentModeManagementEnabled();
initDisplayOffloadSession();
}
@@ -1468,6 +1469,103 @@ public class LocalDisplayAdapterTest {
assertFalse(mDisplayOffloadSession.isActive());
}
+ @Test
+ public void testAllowsContentSwitch_firstDisplay() throws Exception {
+ // Set up a first display
+ setUpDisplay(new FakeDisplay(PORT_A));
+ updateAvailableDisplays();
+ mAdapter.registerLocked();
+ waitForHandlerToComplete(mHandler, HANDLER_WAIT_MS);
+
+ // The first display should be allowed to use the content mode switch
+ DisplayDevice firstDisplayDevice = mListener.addedDisplays.get(0);
+ assertTrue((firstDisplayDevice.getDisplayDeviceInfoLocked().flags
+ & DisplayDeviceInfo.FLAG_ALLOWS_CONTENT_MODE_SWITCH) != 0);
+ }
+
+ @Test
+ public void testAllowsContentSwitch_secondaryDisplayPublicAndNotShouldShowOwnContent()
+ throws Exception {
+ // Set up a first display and a secondary display
+ setUpDisplay(new FakeDisplay(PORT_A));
+ setUpDisplay(new FakeDisplay(PORT_B));
+ updateAvailableDisplays();
+
+ // Set the secondary display to be a public display
+ doReturn(new int[0]).when(mMockedResources)
+ .getIntArray(com.android.internal.R.array.config_localPrivateDisplayPorts);
+ // Disable FLAG_OWN_CONTENT_ONLY for the secondary display
+ doReturn(true).when(mMockedResources)
+ .getBoolean(com.android.internal.R.bool.config_localDisplaysMirrorContent);
+ mAdapter.registerLocked();
+ waitForHandlerToComplete(mHandler, HANDLER_WAIT_MS);
+
+ // This secondary display should be allowed to use the content mode switch
+ DisplayDevice secondaryDisplayDevice = mListener.addedDisplays.get(1);
+ assertTrue((secondaryDisplayDevice.getDisplayDeviceInfoLocked().flags
+ & DisplayDeviceInfo.FLAG_ALLOWS_CONTENT_MODE_SWITCH) != 0);
+ }
+
+ @Test
+ public void testAllowsContentSwitch_privateDisplay() throws Exception {
+ // Set up a first display and a secondary display
+ setUpDisplay(new FakeDisplay(PORT_A));
+ setUpDisplay(new FakeDisplay(PORT_B));
+ updateAvailableDisplays();
+
+ // Set the secondary display to be a private display
+ doReturn(new int[]{ PORT_B }).when(mMockedResources)
+ .getIntArray(com.android.internal.R.array.config_localPrivateDisplayPorts);
+ mAdapter.registerLocked();
+ waitForHandlerToComplete(mHandler, HANDLER_WAIT_MS);
+
+ // The private display should not be allowed to use the content mode switch
+ DisplayDevice secondaryDisplayDevice = mListener.addedDisplays.get(1);
+ assertTrue((secondaryDisplayDevice.getDisplayDeviceInfoLocked().flags
+ & DisplayDeviceInfo.FLAG_ALLOWS_CONTENT_MODE_SWITCH) == 0);
+ }
+
+ @Test
+ public void testAllowsContentSwitch_ownContentOnlyDisplay() throws Exception {
+ // Set up a first display and a secondary display
+ setUpDisplay(new FakeDisplay(PORT_A));
+ setUpDisplay(new FakeDisplay(PORT_B));
+ updateAvailableDisplays();
+
+ // Enable FLAG_OWN_CONTENT_ONLY for the secondary display
+ doReturn(false).when(mMockedResources)
+ .getBoolean(com.android.internal.R.bool.config_localDisplaysMirrorContent);
+ mAdapter.registerLocked();
+ waitForHandlerToComplete(mHandler, HANDLER_WAIT_MS);
+
+ // The secondary display with FLAG_OWN_CONTENT_ONLY enabled should not be allowed to use the
+ // content mode switch
+ DisplayDevice secondaryDisplayDevice = mListener.addedDisplays.get(1);
+ assertTrue((secondaryDisplayDevice.getDisplayDeviceInfoLocked().flags
+ & DisplayDeviceInfo.FLAG_ALLOWS_CONTENT_MODE_SWITCH) == 0);
+ }
+
+ @Test
+ public void testAllowsContentSwitch_flagShouldShowSystemDecorations() throws Exception {
+ // Set up a display
+ FakeDisplay display = new FakeDisplay(PORT_A);
+ setUpDisplay(display);
+ updateAvailableDisplays();
+
+ mAdapter.registerLocked();
+ waitForHandlerToComplete(mHandler, HANDLER_WAIT_MS);
+
+ // Display with FLAG_SHOULD_SHOW_SYSTEM_DECORATIONS enabled should not be allowed to use the
+ // content mode switch
+ DisplayDevice displayDevice = mListener.addedDisplays.get(0);
+ int flags = displayDevice.getDisplayDeviceInfoLocked().flags;
+ boolean allowsContentModeSwitch =
+ ((flags & DisplayDeviceInfo.FLAG_ALLOWS_CONTENT_MODE_SWITCH) != 0);
+ boolean shouldShowSystemDecorations =
+ ((flags & DisplayDeviceInfo.FLAG_SHOULD_SHOW_SYSTEM_DECORATIONS) != 0);
+ assertFalse(allowsContentModeSwitch && shouldShowSystemDecorations);
+ }
+
private void initDisplayOffloadSession() {
when(mDisplayOffloader.startOffload()).thenReturn(true);
when(mDisplayOffloader.allowAutoBrightnessInDoze()).thenReturn(true);
diff --git a/services/tests/displayservicetests/src/com/android/server/display/WakelockControllerTest.java b/services/tests/displayservicetests/src/com/android/server/display/WakelockControllerTest.java
index 019b70ef1424..f067fa10f611 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/WakelockControllerTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/WakelockControllerTest.java
@@ -21,7 +21,7 @@ import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.verifyZeroInteractions;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
import android.hardware.display.DisplayManagerInternal;
@@ -210,7 +210,7 @@ public final class WakelockControllerTest {
// Validate one suspend blocker was released
assertFalse(mWakelockController.isProximityPositiveAcquired());
- verifyZeroInteractions(mDisplayPowerCallbacks);
+ verifyNoMoreInteractions(mDisplayPowerCallbacks);
}
@Test
@@ -238,7 +238,7 @@ public final class WakelockControllerTest {
// Validate one suspend blocker was released
assertFalse(mWakelockController.isProximityNegativeAcquired());
- verifyZeroInteractions(mDisplayPowerCallbacks);
+ verifyNoMoreInteractions(mDisplayPowerCallbacks);
}
@Test
@@ -265,7 +265,7 @@ public final class WakelockControllerTest {
// Validate one suspend blocker was released
assertFalse(mWakelockController.isOnStateChangedPending());
- verifyZeroInteractions(mDisplayPowerCallbacks);
+ verifyNoMoreInteractions(mDisplayPowerCallbacks);
}
@Test
diff --git a/services/tests/displayservicetests/src/com/android/server/display/brightness/DisplayBrightnessControllerTest.java b/services/tests/displayservicetests/src/com/android/server/display/brightness/DisplayBrightnessControllerTest.java
index 49de80179683..bf0543939d85 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/brightness/DisplayBrightnessControllerTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/brightness/DisplayBrightnessControllerTest.java
@@ -28,7 +28,6 @@ import static org.mockito.Mockito.never;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoMoreInteractions;
-import static org.mockito.Mockito.verifyZeroInteractions;
import static org.mockito.Mockito.when;
import android.content.Context;
@@ -422,7 +421,7 @@ public final class DisplayBrightnessControllerTest {
mDisplayBrightnessController.setAutomaticBrightnessController(
automaticBrightnessController);
assertEquals(brightness, mDisplayBrightnessController.getCurrentBrightness(), 0.01f);
- verifyZeroInteractions(automaticBrightnessController);
+ verifyNoMoreInteractions(automaticBrightnessController);
verify(mBrightnessSetting, never()).getBrightnessNitsForDefaultDisplay();
verify(mBrightnessSetting, never()).setBrightnessNoNotify(brightness);
}
diff --git a/services/tests/mockingservicestests/src/com/android/server/alarm/AlarmManagerServiceTest.java b/services/tests/mockingservicestests/src/com/android/server/alarm/AlarmManagerServiceTest.java
index d79d88400cf9..f0e61ec9c692 100644
--- a/services/tests/mockingservicestests/src/com/android/server/alarm/AlarmManagerServiceTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/alarm/AlarmManagerServiceTest.java
@@ -109,7 +109,7 @@ import static org.mockito.ArgumentMatchers.isNull;
import static org.mockito.Mockito.atLeastOnce;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.times;
-import static org.mockito.Mockito.verifyZeroInteractions;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
import android.Manifest;
import android.app.ActivityManager;
@@ -3724,8 +3724,8 @@ public final class AlarmManagerServiceTest {
setDeviceConfigInt(KEY_TEMPORARY_QUOTA_BUMP, 0);
mAppStandbyListener.triggerTemporaryQuotaBump(TEST_CALLING_PACKAGE, TEST_CALLING_USER);
- verifyZeroInteractions(mPackageManagerInternal);
- verifyZeroInteractions(mService.mHandler);
+ verifyNoMoreInteractions(mPackageManagerInternal);
+ verifyNoMoreInteractions(mService.mHandler);
}
private void testTemporaryQuota_bumpedAfterDeferral(int standbyBucket) throws Exception {
diff --git a/services/tests/mockingservicestests/src/com/android/server/alarm/AlarmStoreTest.java b/services/tests/mockingservicestests/src/com/android/server/alarm/AlarmStoreTest.java
index 7dab1c854625..859d2d2f2e38 100644
--- a/services/tests/mockingservicestests/src/com/android/server/alarm/AlarmStoreTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/alarm/AlarmStoreTest.java
@@ -30,7 +30,7 @@ import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.verifyZeroInteractions;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
import android.app.AlarmManager;
import android.app.PendingIntent;
@@ -244,7 +244,7 @@ public class AlarmStoreTest {
addAlarmsToStore(simpleAlarm, alarmClock);
mAlarmStore.remove(simpleAlarm::equals);
- verifyZeroInteractions(onRemoved);
+ verifyNoMoreInteractions(onRemoved);
mAlarmStore.remove(alarmClock::equals);
verify(onRemoved).run();
diff --git a/services/tests/mockingservicestests/src/com/android/server/am/ActivityManagerServiceTest.java b/services/tests/mockingservicestests/src/com/android/server/am/ActivityManagerServiceTest.java
index 35ab2d233563..acc06d0c7cba 100644
--- a/services/tests/mockingservicestests/src/com/android/server/am/ActivityManagerServiceTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/am/ActivityManagerServiceTest.java
@@ -74,7 +74,6 @@ import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.timeout;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoMoreInteractions;
-import static org.mockito.Mockito.verifyZeroInteractions;
import android.Manifest;
import android.app.ActivityManager;
@@ -584,7 +583,7 @@ public class ActivityManagerServiceTest {
if (app.uid == uidRec.getUid() && expectedBlockState == NETWORK_STATE_BLOCK) {
verify(app.getThread()).setNetworkBlockSeq(uidRec.curProcStateSeq);
} else {
- verifyZeroInteractions(app.getThread());
+ verifyNoMoreInteractions(app.getThread());
}
Mockito.reset(app.getThread());
}
diff --git a/services/tests/mockingservicestests/src/com/android/server/backup/utils/FullBackupUtilsTest.java b/services/tests/mockingservicestests/src/com/android/server/backup/utils/FullBackupUtilsTest.java
index ae0452a60161..b7087c74bf8d 100644
--- a/services/tests/mockingservicestests/src/com/android/server/backup/utils/FullBackupUtilsTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/backup/utils/FullBackupUtilsTest.java
@@ -21,7 +21,7 @@ import static com.google.common.truth.Truth.assertThat;
import static org.junit.Assert.fail;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.verifyZeroInteractions;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
import android.os.ParcelFileDescriptor;
import android.platform.test.annotations.Presubmit;
@@ -105,7 +105,7 @@ public class FullBackupUtilsTest {
} catch (EOFException expected) {
}
- verifyZeroInteractions(mOutputStreamMock);
+ verifyNoMoreInteractions(mOutputStreamMock);
assertThat(mTemporaryFileDescriptor.getFileDescriptor().valid()).isTrue();
}
@@ -126,7 +126,7 @@ public class FullBackupUtilsTest {
} catch (EOFException expected) {
}
- verifyZeroInteractions(mOutputStreamMock);
+ verifyNoMoreInteractions(mOutputStreamMock);
assertThat(mTemporaryFileDescriptor.getFileDescriptor().valid()).isTrue();
}
@@ -141,7 +141,7 @@ public class FullBackupUtilsTest {
FullBackupUtils.routeSocketDataToOutput(mTemporaryFileDescriptor, mOutputStreamMock);
- verifyZeroInteractions(mOutputStreamMock);
+ verifyNoMoreInteractions(mOutputStreamMock);
assertThat(mTemporaryFileDescriptor.getFileDescriptor().valid()).isTrue();
}
diff --git a/services/tests/mockingservicestests/src/com/android/server/backup/utils/TarBackupReaderTest.java b/services/tests/mockingservicestests/src/com/android/server/backup/utils/TarBackupReaderTest.java
index bf7e3a0bd0a6..346d5f787621 100644
--- a/services/tests/mockingservicestests/src/com/android/server/backup/utils/TarBackupReaderTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/backup/utils/TarBackupReaderTest.java
@@ -32,7 +32,6 @@ import static com.google.common.truth.Truth.assertThat;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoMoreInteractions;
-import static org.mockito.Mockito.verifyZeroInteractions;
import static org.mockito.internal.verification.VerificationModeFactory.times;
import android.app.backup.IBackupManagerMonitor;
@@ -239,7 +238,7 @@ public class TarBackupReaderTest {
mMockPackageManagerInternal, mUserId, mContext);
assertThat(policy).isEqualTo(RestorePolicy.IGNORE);
- verifyZeroInteractions(mBackupManagerMonitorMock);
+ verifyNoMoreInteractions(mBackupManagerMonitorMock);
}
@Test
diff --git a/services/tests/mockingservicestests/src/com/android/server/job/controllers/QuotaControllerTest.java b/services/tests/mockingservicestests/src/com/android/server/job/controllers/QuotaControllerTest.java
index 3e8794377d37..2d84887afb41 100644
--- a/services/tests/mockingservicestests/src/com/android/server/job/controllers/QuotaControllerTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/job/controllers/QuotaControllerTest.java
@@ -1029,6 +1029,7 @@ public class QuotaControllerTest {
@Test
@EnableFlags(Flags.FLAG_ADJUST_QUOTA_DEFAULT_CONSTANTS)
+ @DisableFlags(Flags.FLAG_TUNE_QUOTA_WINDOW_DEFAULT_PARAMETERS)
public void testGetExecutionStatsLocked_Values_NewDefaultBucketWindowSizes() {
final long now = JobSchedulerService.sElapsedRealtimeClock.millis();
mQuotaController.saveTimingSession(0, "com.android.test",
@@ -1127,6 +1128,54 @@ public class QuotaControllerTest {
}
}
+ @Test
+ @EnableFlags({Flags.FLAG_ADJUST_QUOTA_DEFAULT_CONSTANTS,
+ Flags.FLAG_TUNE_QUOTA_WINDOW_DEFAULT_PARAMETERS})
+ public void testGetExecutionStatsLocked_Values_NewDefaultBucketWindowSizes_Tuning() {
+ final long now = JobSchedulerService.sElapsedRealtimeClock.millis();
+ mQuotaController.saveTimingSession(0, "com.android.test",
+ createTimingSession(now - (23 * HOUR_IN_MILLIS), 10 * MINUTE_IN_MILLIS, 5), false);
+ mQuotaController.saveTimingSession(0, "com.android.test",
+ createTimingSession(now - (7 * HOUR_IN_MILLIS), 10 * MINUTE_IN_MILLIS, 5), false);
+ mQuotaController.saveTimingSession(0, "com.android.test",
+ createTimingSession(now - (2 * HOUR_IN_MILLIS), 10 * MINUTE_IN_MILLIS, 5), false);
+ mQuotaController.saveTimingSession(0, "com.android.test",
+ createTimingSession(now - (6 * MINUTE_IN_MILLIS), 3 * MINUTE_IN_MILLIS, 5), false);
+
+ ExecutionStats expectedStats = new ExecutionStats();
+
+ // Exempted
+ expectedStats.allowedTimePerPeriodMs = mQcConstants.ALLOWED_TIME_PER_PERIOD_EXEMPTED_MS;
+ expectedStats.windowSizeMs = mQcConstants.WINDOW_SIZE_EXEMPTED_MS;
+ expectedStats.jobCountLimit = mQcConstants.MAX_JOB_COUNT_EXEMPTED;
+ expectedStats.sessionCountLimit = mQcConstants.MAX_SESSION_COUNT_EXEMPTED;
+ expectedStats.expirationTimeElapsed = now + 34 * MINUTE_IN_MILLIS;
+ expectedStats.executionTimeInWindowMs = 3 * MINUTE_IN_MILLIS;
+ expectedStats.bgJobCountInWindow = 5;
+ expectedStats.executionTimeInMaxPeriodMs = 33 * MINUTE_IN_MILLIS;
+ expectedStats.bgJobCountInMaxPeriod = 20;
+ expectedStats.sessionCountInWindow = 1;
+ synchronized (mQuotaController.mLock) {
+ assertEquals(expectedStats,
+ mQuotaController.getExecutionStatsLocked(0, "com.android.test",
+ EXEMPTED_INDEX));
+ }
+
+ // Active
+ expectedStats.allowedTimePerPeriodMs = mQcConstants.ALLOWED_TIME_PER_PERIOD_ACTIVE_MS;
+ expectedStats.windowSizeMs = mQcConstants.WINDOW_SIZE_ACTIVE_MS;
+ expectedStats.jobCountLimit = mQcConstants.MAX_JOB_COUNT_ACTIVE;
+ expectedStats.sessionCountLimit = mQcConstants.MAX_SESSION_COUNT_ACTIVE;
+ // There is only one session in the past active bucket window, the empty time for this
+ // window is the bucket window size - duration of the session.
+ expectedStats.expirationTimeElapsed = now + 54 * MINUTE_IN_MILLIS;
+ synchronized (mQuotaController.mLock) {
+ assertEquals(expectedStats,
+ mQuotaController.getExecutionStatsLocked(0, "com.android.test",
+ ACTIVE_INDEX));
+ }
+ }
+
/**
* Tests that getExecutionStatsLocked returns the correct stats soon after device startup.
*/
@@ -1195,6 +1244,7 @@ public class QuotaControllerTest {
@Test
@EnableFlags(Flags.FLAG_ADJUST_QUOTA_DEFAULT_CONSTANTS)
+ @DisableFlags(Flags.FLAG_TUNE_QUOTA_WINDOW_DEFAULT_PARAMETERS)
public void testGetExecutionStatsLocked_Values_BeginningOfTime_NewDefaultBucketWindowSizes() {
// Set time to 3 minutes after boot.
advanceElapsedClock(-JobSchedulerService.sElapsedRealtimeClock.millis());
@@ -1206,10 +1256,10 @@ public class QuotaControllerTest {
ExecutionStats expectedStats = new ExecutionStats();
// Exempted
- expectedStats.allowedTimePerPeriodMs = mQcConstants.ALLOWED_TIME_PER_PERIOD_ACTIVE_MS;
+ expectedStats.allowedTimePerPeriodMs = mQcConstants.ALLOWED_TIME_PER_PERIOD_EXEMPTED_MS;
expectedStats.windowSizeMs = mQcConstants.WINDOW_SIZE_EXEMPTED_MS;
- expectedStats.jobCountLimit = mQcConstants.MAX_JOB_COUNT_ACTIVE;
- expectedStats.sessionCountLimit = mQcConstants.MAX_SESSION_COUNT_ACTIVE;
+ expectedStats.jobCountLimit = mQcConstants.MAX_JOB_COUNT_EXEMPTED;
+ expectedStats.sessionCountLimit = mQcConstants.MAX_SESSION_COUNT_EXEMPTED;
expectedStats.expirationTimeElapsed = 10 * MINUTE_IN_MILLIS + 11 * MINUTE_IN_MILLIS;
expectedStats.executionTimeInWindowMs = MINUTE_IN_MILLIS;
expectedStats.bgJobCountInWindow = 2;
@@ -1268,6 +1318,49 @@ public class QuotaControllerTest {
}
}
+ @Test
+ @EnableFlags({Flags.FLAG_ADJUST_QUOTA_DEFAULT_CONSTANTS,
+ Flags.FLAG_TUNE_QUOTA_WINDOW_DEFAULT_PARAMETERS})
+ public void testGetExecutionStatsLocked_Values_BeginningOfTime_NewDefaultBucketWindowSizes_Tunning() {
+ // Set time to 3 minutes after boot.
+ advanceElapsedClock(-JobSchedulerService.sElapsedRealtimeClock.millis());
+ advanceElapsedClock(3 * MINUTE_IN_MILLIS);
+
+ mQuotaController.saveTimingSession(0, "com.android.test",
+ createTimingSession(MINUTE_IN_MILLIS, MINUTE_IN_MILLIS, 2), false);
+
+ ExecutionStats expectedStats = new ExecutionStats();
+
+ // Exempted
+ expectedStats.allowedTimePerPeriodMs = mQcConstants.ALLOWED_TIME_PER_PERIOD_EXEMPTED_MS;
+ expectedStats.windowSizeMs = mQcConstants.WINDOW_SIZE_EXEMPTED_MS;
+ expectedStats.jobCountLimit = mQcConstants.MAX_JOB_COUNT_EXEMPTED;
+ expectedStats.sessionCountLimit = mQcConstants.MAX_SESSION_COUNT_EXEMPTED;
+ expectedStats.expirationTimeElapsed = 30 * MINUTE_IN_MILLIS + 11 * MINUTE_IN_MILLIS;
+ expectedStats.executionTimeInWindowMs = MINUTE_IN_MILLIS;
+ expectedStats.bgJobCountInWindow = 2;
+ expectedStats.executionTimeInMaxPeriodMs = MINUTE_IN_MILLIS;
+ expectedStats.bgJobCountInMaxPeriod = 2;
+ expectedStats.sessionCountInWindow = 1;
+ synchronized (mQuotaController.mLock) {
+ assertEquals(expectedStats,
+ mQuotaController.getExecutionStatsLocked(0, "com.android.test",
+ EXEMPTED_INDEX));
+ }
+
+ // Active
+ expectedStats.allowedTimePerPeriodMs = mQcConstants.ALLOWED_TIME_PER_PERIOD_ACTIVE_MS;
+ expectedStats.windowSizeMs = mQcConstants.WINDOW_SIZE_ACTIVE_MS;
+ expectedStats.jobCountLimit = mQcConstants.MAX_JOB_COUNT_ACTIVE;
+ expectedStats.sessionCountLimit = mQcConstants.MAX_SESSION_COUNT_ACTIVE;
+ expectedStats.expirationTimeElapsed = 50 * MINUTE_IN_MILLIS + 11 * MINUTE_IN_MILLIS;
+ synchronized (mQuotaController.mLock) {
+ assertEquals(expectedStats,
+ mQuotaController.getExecutionStatsLocked(0, "com.android.test",
+ ACTIVE_INDEX));
+ }
+ }
+
/**
* Tests that getExecutionStatsLocked returns the correct timing session stats when coalescing.
*/
@@ -1425,6 +1518,7 @@ public class QuotaControllerTest {
@Test
@EnableFlags(Flags.FLAG_ADJUST_QUOTA_DEFAULT_CONSTANTS)
+ @DisableFlags(Flags.FLAG_TUNE_QUOTA_WINDOW_DEFAULT_PARAMETERS)
public void testGetExecutionStatsLocked_CoalescingSessions_NewDefaultBucketWindowSizes() {
for (int i = 0; i < 20; ++i) {
mQuotaController.saveTimingSession(0, "com.android.test",
@@ -1581,6 +1675,165 @@ public class QuotaControllerTest {
}
}
+ @Test
+ @EnableFlags({Flags.FLAG_ADJUST_QUOTA_DEFAULT_CONSTANTS,
+ Flags.FLAG_TUNE_QUOTA_WINDOW_DEFAULT_PARAMETERS})
+ public void testGetExecutionStatsLocked_CoalescingSessions_NewDefaultBucketWindowSizes_Tuning() {
+ for (int i = 0; i < 20; ++i) {
+ mQuotaController.saveTimingSession(0, "com.android.test",
+ createTimingSession(
+ JobSchedulerService.sElapsedRealtimeClock.millis(),
+ 5 * MINUTE_IN_MILLIS, 5), false);
+ advanceElapsedClock(5 * MINUTE_IN_MILLIS);
+ advanceElapsedClock(5 * MINUTE_IN_MILLIS);
+ for (int j = 0; j < 5; ++j) {
+ mQuotaController.saveTimingSession(0, "com.android.test",
+ createTimingSession(
+ JobSchedulerService.sElapsedRealtimeClock.millis(),
+ MINUTE_IN_MILLIS, 2), false);
+ advanceElapsedClock(MINUTE_IN_MILLIS);
+ advanceElapsedClock(54 * SECOND_IN_MILLIS);
+ mQuotaController.saveTimingSession(0, "com.android.test",
+ createTimingSession(
+ JobSchedulerService.sElapsedRealtimeClock.millis(), 500, 1), false);
+ advanceElapsedClock(500);
+ advanceElapsedClock(400);
+ mQuotaController.saveTimingSession(0, "com.android.test",
+ createTimingSession(
+ JobSchedulerService.sElapsedRealtimeClock.millis(), 100, 1), false);
+ advanceElapsedClock(100);
+ advanceElapsedClock(5 * SECOND_IN_MILLIS);
+ }
+ advanceElapsedClock(40 * MINUTE_IN_MILLIS);
+ }
+
+ setDeviceConfigLong(QcConstants.KEY_TIMING_SESSION_COALESCING_DURATION_MS, 0);
+
+ synchronized (mQuotaController.mLock) {
+ mQuotaController.invalidateAllExecutionStatsLocked();
+ assertEquals(16, mQuotaController.getExecutionStatsLocked(
+ 0, "com.android.test", ACTIVE_INDEX).sessionCountInWindow);
+ assertEquals(64, mQuotaController.getExecutionStatsLocked(
+ 0, "com.android.test", WORKING_INDEX).sessionCountInWindow);
+ assertEquals(192, mQuotaController.getExecutionStatsLocked(
+ 0, "com.android.test", FREQUENT_INDEX).sessionCountInWindow);
+ assertEquals(320, mQuotaController.getExecutionStatsLocked(
+ 0, "com.android.test", RARE_INDEX).sessionCountInWindow);
+ }
+
+ setDeviceConfigLong(QcConstants.KEY_TIMING_SESSION_COALESCING_DURATION_MS, 500);
+
+ synchronized (mQuotaController.mLock) {
+ mQuotaController.invalidateAllExecutionStatsLocked();
+ assertEquals(11, mQuotaController.getExecutionStatsLocked(
+ 0, "com.android.test", ACTIVE_INDEX).sessionCountInWindow);
+ // WINDOW_SIZE_WORKING_MS * 5 TimingSessions are coalesced
+ assertEquals(44, mQuotaController.getExecutionStatsLocked(
+ 0, "com.android.test", WORKING_INDEX).sessionCountInWindow);
+ // WINDOW_SIZE_FREQUENT_MS * 5 TimingSessions are coalesced
+ assertEquals(132, mQuotaController.getExecutionStatsLocked(
+ 0, "com.android.test", FREQUENT_INDEX).sessionCountInWindow);
+ // WINDOW_SIZE_RARE_MS * 5 TimingSessions are coalesced
+ assertEquals(220, mQuotaController.getExecutionStatsLocked(
+ 0, "com.android.test", RARE_INDEX).sessionCountInWindow);
+ }
+
+ setDeviceConfigLong(QcConstants.KEY_TIMING_SESSION_COALESCING_DURATION_MS, 1000);
+
+ synchronized (mQuotaController.mLock) {
+ mQuotaController.invalidateAllExecutionStatsLocked();
+ assertEquals(11, mQuotaController.getExecutionStatsLocked(
+ 0, "com.android.test", ACTIVE_INDEX).sessionCountInWindow);
+ assertEquals(44, mQuotaController.getExecutionStatsLocked(
+ 0, "com.android.test", WORKING_INDEX).sessionCountInWindow);
+ assertEquals(132, mQuotaController.getExecutionStatsLocked(
+ 0, "com.android.test", FREQUENT_INDEX).sessionCountInWindow);
+ assertEquals(220, mQuotaController.getExecutionStatsLocked(
+ 0, "com.android.test", RARE_INDEX).sessionCountInWindow);
+ }
+
+ setDeviceConfigLong(QcConstants.KEY_TIMING_SESSION_COALESCING_DURATION_MS,
+ 5 * SECOND_IN_MILLIS);
+
+ synchronized (mQuotaController.mLock) {
+ mQuotaController.invalidateAllExecutionStatsLocked();
+ assertEquals(7, mQuotaController.getExecutionStatsLocked(
+ 0, "com.android.test", ACTIVE_INDEX).sessionCountInWindow);
+ // WINDOW_SIZE_WORKING_MS * 9 TimingSessions are coalesced
+ assertEquals(28, mQuotaController.getExecutionStatsLocked(
+ 0, "com.android.test", WORKING_INDEX).sessionCountInWindow);
+ // WINDOW_SIZE_FREQUENT_MS * 9 TimingSessions are coalesced
+ assertEquals(84, mQuotaController.getExecutionStatsLocked(
+ 0, "com.android.test", FREQUENT_INDEX).sessionCountInWindow);
+ // WINDOW_SIZE_RARE_MS * 9 TimingSessions are coalesced
+ assertEquals(140, mQuotaController.getExecutionStatsLocked(
+ 0, "com.android.test", RARE_INDEX).sessionCountInWindow);
+ }
+
+ setDeviceConfigLong(QcConstants.KEY_TIMING_SESSION_COALESCING_DURATION_MS,
+ MINUTE_IN_MILLIS);
+
+ // Only two TimingSessions there for every hour.
+ synchronized (mQuotaController.mLock) {
+ mQuotaController.invalidateAllExecutionStatsLocked();
+ assertEquals(2, mQuotaController.getExecutionStatsLocked(
+ 0, "com.android.test", ACTIVE_INDEX).sessionCountInWindow);
+ assertEquals(8, mQuotaController.getExecutionStatsLocked(
+ 0, "com.android.test", WORKING_INDEX).sessionCountInWindow);
+ assertEquals(24, mQuotaController.getExecutionStatsLocked(
+ 0, "com.android.test", FREQUENT_INDEX).sessionCountInWindow);
+ assertEquals(40, mQuotaController.getExecutionStatsLocked(
+ 0, "com.android.test", RARE_INDEX).sessionCountInWindow);
+ }
+
+ setDeviceConfigLong(QcConstants.KEY_TIMING_SESSION_COALESCING_DURATION_MS,
+ 5 * MINUTE_IN_MILLIS);
+
+ // Only one TimingSessions there for every hour
+ synchronized (mQuotaController.mLock) {
+ mQuotaController.invalidateAllExecutionStatsLocked();
+ assertEquals(1, mQuotaController.getExecutionStatsLocked(
+ 0, "com.android.test", ACTIVE_INDEX).sessionCountInWindow);
+ assertEquals(4, mQuotaController.getExecutionStatsLocked(
+ 0, "com.android.test", WORKING_INDEX).sessionCountInWindow);
+ assertEquals(12, mQuotaController.getExecutionStatsLocked(
+ 0, "com.android.test", FREQUENT_INDEX).sessionCountInWindow);
+ assertEquals(20, mQuotaController.getExecutionStatsLocked(
+ 0, "com.android.test", RARE_INDEX).sessionCountInWindow);
+ }
+
+ setDeviceConfigLong(QcConstants.KEY_TIMING_SESSION_COALESCING_DURATION_MS,
+ 15 * MINUTE_IN_MILLIS);
+
+ synchronized (mQuotaController.mLock) {
+ mQuotaController.invalidateAllExecutionStatsLocked();
+ assertEquals(1, mQuotaController.getExecutionStatsLocked(
+ 0, "com.android.test", ACTIVE_INDEX).sessionCountInWindow);
+ assertEquals(4, mQuotaController.getExecutionStatsLocked(
+ 0, "com.android.test", WORKING_INDEX).sessionCountInWindow);
+ assertEquals(12, mQuotaController.getExecutionStatsLocked(
+ 0, "com.android.test", FREQUENT_INDEX).sessionCountInWindow);
+ assertEquals(20, mQuotaController.getExecutionStatsLocked(
+ 0, "com.android.test", RARE_INDEX).sessionCountInWindow);
+ }
+
+ // QuotaController caps the duration at 15 minutes, so there shouldn't be any difference
+ // between an hour and 15 minutes.
+ setDeviceConfigLong(QcConstants.KEY_TIMING_SESSION_COALESCING_DURATION_MS, HOUR_IN_MILLIS);
+
+ synchronized (mQuotaController.mLock) {
+ mQuotaController.invalidateAllExecutionStatsLocked();
+ assertEquals(1, mQuotaController.getExecutionStatsLocked(
+ 0, "com.android.test", ACTIVE_INDEX).sessionCountInWindow);
+ assertEquals(4, mQuotaController.getExecutionStatsLocked(
+ 0, "com.android.test", WORKING_INDEX).sessionCountInWindow);
+ assertEquals(12, mQuotaController.getExecutionStatsLocked(
+ 0, "com.android.test", FREQUENT_INDEX).sessionCountInWindow);
+ assertEquals(20, mQuotaController.getExecutionStatsLocked(
+ 0, "com.android.test", RARE_INDEX).sessionCountInWindow);
+ }
+ }
+
/**
* Tests that getExecutionStatsLocked properly caches the stats and returns the cached object.
*/
@@ -2231,32 +2484,6 @@ public class QuotaControllerTest {
}
}
- @Test
- @EnableFlags(Flags.FLAG_ADJUST_QUOTA_DEFAULT_CONSTANTS)
- public void testGetTimeUntilQuotaConsumedLocked_AllowedEqualsWindow_NewDefaultBucketWindowSizes() {
- final long now = JobSchedulerService.sElapsedRealtimeClock.millis();
- mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE,
- createTimingSession(now - (8 * HOUR_IN_MILLIS), 20 * MINUTE_IN_MILLIS, 5), false);
- mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE,
- createTimingSession(now - (10 * MINUTE_IN_MILLIS), 10 * MINUTE_IN_MILLIS, 5),
- false);
-
- setDeviceConfigLong(QcConstants.KEY_ALLOWED_TIME_PER_PERIOD_EXEMPTED_MS,
- 20 * MINUTE_IN_MILLIS);
- setDeviceConfigLong(QcConstants.KEY_WINDOW_SIZE_EXEMPTED_MS, 20 * MINUTE_IN_MILLIS);
- // window size = allowed time, so jobs can essentially run non-stop until they reach the
- // max execution time.
- setStandbyBucket(EXEMPTED_INDEX);
- synchronized (mQuotaController.mLock) {
- assertEquals(10 * MINUTE_IN_MILLIS,
- mQuotaController.getRemainingExecutionTimeLocked(
- SOURCE_USER_ID, SOURCE_PACKAGE));
- assertEquals(mQcConstants.MAX_EXECUTION_TIME_MS - 30 * MINUTE_IN_MILLIS,
- mQuotaController.getTimeUntilQuotaConsumedLocked(
- SOURCE_USER_ID, SOURCE_PACKAGE));
- }
- }
-
/**
* Test getTimeUntilQuotaConsumedLocked when the determination is based within the bucket
* window.
@@ -2327,6 +2554,7 @@ public class QuotaControllerTest {
@Test
@EnableFlags(Flags.FLAG_ADJUST_QUOTA_DEFAULT_CONSTANTS)
+ @DisableFlags(Flags.FLAG_TUNE_QUOTA_WINDOW_DEFAULT_PARAMETERS)
public void testGetTimeUntilQuotaConsumedLocked_BucketWindow_NewDefaultBucketWindowSizes() {
final long now = JobSchedulerService.sElapsedRealtimeClock.millis();
// Close to RARE boundary.
@@ -2390,7 +2618,30 @@ public class QuotaControllerTest {
@Test
@EnableFlags({Flags.FLAG_ADJUST_QUOTA_DEFAULT_CONSTANTS,
+ Flags.FLAG_TUNE_QUOTA_WINDOW_DEFAULT_PARAMETERS})
+ public void testGetTimeUntilQuotaConsumedLocked_BucketWindow_NewDefaultBucketWindowSizes_Tuning() {
+ final long now = JobSchedulerService.sElapsedRealtimeClock.millis();
+ // Close to ACTIVE boundary.
+ mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE,
+ createTimingSession(now - (mQcConstants.WINDOW_SIZE_ACTIVE_MS - MINUTE_IN_MILLIS),
+ 3 * MINUTE_IN_MILLIS, 5), false);
+
+ // ACTIVE window != allowed time.
+ setStandbyBucket(ACTIVE_INDEX);
+ synchronized (mQuotaController.mLock) {
+ assertEquals(17 * MINUTE_IN_MILLIS,
+ mQuotaController.getRemainingExecutionTimeLocked(
+ SOURCE_USER_ID, SOURCE_PACKAGE));
+ assertEquals(20 * MINUTE_IN_MILLIS,
+ mQuotaController.getTimeUntilQuotaConsumedLocked(
+ SOURCE_USER_ID, SOURCE_PACKAGE));
+ }
+ }
+
+ @Test
+ @EnableFlags({Flags.FLAG_ADJUST_QUOTA_DEFAULT_CONSTANTS,
Flags.FLAG_ADDITIONAL_QUOTA_FOR_SYSTEM_INSTALLER})
+ @DisableFlags(Flags.FLAG_TUNE_QUOTA_WINDOW_DEFAULT_PARAMETERS)
public void testGetTimeUntilQuotaConsumedLocked_Installer() {
PackageInfo pi = new PackageInfo();
pi.packageName = SOURCE_PACKAGE;
@@ -2412,7 +2663,7 @@ public class QuotaControllerTest {
// Far away from FREQUENT boundary.
mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE,
createTimingSession(
- now - (mQcConstants.WINDOW_SIZE_FREQUENT_MS - HOUR_IN_MILLIS),
+ now - (mQcConstants.WINDOW_SIZE_FREQUENT_MS - HOUR_IN_MILLIS),
2 * MINUTE_IN_MILLIS, 5), false);
// Overlap WORKING_SET boundary.
mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE,
@@ -2422,12 +2673,12 @@ public class QuotaControllerTest {
// Close to ACTIVE boundary.
mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE,
createTimingSession(
- now - (mQcConstants.WINDOW_SIZE_ACTIVE_MS - MINUTE_IN_MILLIS),
+ now - (mQcConstants.WINDOW_SIZE_ACTIVE_MS - MINUTE_IN_MILLIS),
2 * MINUTE_IN_MILLIS, 5), false);
// Close to EXEMPTED boundary.
mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE,
createTimingSession(
- now - (mQcConstants.WINDOW_SIZE_EXEMPTED_MS - MINUTE_IN_MILLIS),
+ now - (mQcConstants.WINDOW_SIZE_EXEMPTED_MS - MINUTE_IN_MILLIS),
2 * MINUTE_IN_MILLIS, 5), false);
// No additional quota for the system installer when the app is in RARE, FREQUENT,
@@ -2486,6 +2737,42 @@ public class QuotaControllerTest {
}
}
+ @Test
+ @EnableFlags({Flags.FLAG_ADJUST_QUOTA_DEFAULT_CONSTANTS,
+ Flags.FLAG_ADDITIONAL_QUOTA_FOR_SYSTEM_INSTALLER,
+ Flags.FLAG_TUNE_QUOTA_WINDOW_DEFAULT_PARAMETERS})
+ public void testGetTimeUntilQuotaConsumedLocked_Installer_Tuning() {
+ PackageInfo pi = new PackageInfo();
+ pi.packageName = SOURCE_PACKAGE;
+ pi.requestedPermissions = new String[]{Manifest.permission.INSTALL_PACKAGES};
+ pi.requestedPermissionsFlags = new int[]{PackageInfo.REQUESTED_PERMISSION_GRANTED};
+ pi.applicationInfo = new ApplicationInfo();
+ pi.applicationInfo.uid = mSourceUid;
+ doReturn(List.of(pi)).when(mPackageManager).getInstalledPackagesAsUser(anyInt(), anyInt());
+ doReturn(PackageManager.PERMISSION_GRANTED).when(mContext).checkPermission(
+ eq(Manifest.permission.INSTALL_PACKAGES), anyInt(), eq(mSourceUid));
+ mQuotaController.onSystemServicesReady();
+
+ final long now = JobSchedulerService.sElapsedRealtimeClock.millis();
+ // Close to EXEMPTED boundary.
+ mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE,
+ createTimingSession(
+ now - (mQcConstants.WINDOW_SIZE_EXEMPTED_MS - MINUTE_IN_MILLIS),
+ 2 * MINUTE_IN_MILLIS, 5), false);
+
+ // Additional quota for the system installer when the app is in EXEMPTED bucket.
+ // EXEMPTED window == allowed time.
+ setStandbyBucket(EXEMPTED_INDEX);
+ synchronized (mQuotaController.mLock) {
+ assertEquals(38 * MINUTE_IN_MILLIS,
+ mQuotaController.getRemainingExecutionTimeLocked(
+ SOURCE_USER_ID, SOURCE_PACKAGE));
+ assertEquals(mQcConstants.MAX_EXECUTION_TIME_MS - 2 * MINUTE_IN_MILLIS,
+ mQuotaController.getTimeUntilQuotaConsumedLocked(
+ SOURCE_USER_ID, SOURCE_PACKAGE));
+ }
+ }
+
/**
* Test getTimeUntilQuotaConsumedLocked when the app is close to the max execution limit.
*/
@@ -2637,33 +2924,6 @@ public class QuotaControllerTest {
}
}
- @Test
- @EnableFlags(Flags.FLAG_ADJUST_QUOTA_DEFAULT_CONSTANTS)
- public void testGetTimeUntilQuotaConsumedLocked_EdgeOfWindow_AllowedEqualsWindow_NewDefaultBucketWindowSizes() {
- final long now = JobSchedulerService.sElapsedRealtimeClock.millis();
- mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE,
- createTimingSession(now - (24 * HOUR_IN_MILLIS),
- mQcConstants.MAX_EXECUTION_TIME_MS - 20 * MINUTE_IN_MILLIS, 5),
- false);
- mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE,
- createTimingSession(now - (20 * MINUTE_IN_MILLIS), 20 * MINUTE_IN_MILLIS, 5),
- false);
-
- setDeviceConfigLong(QcConstants.KEY_ALLOWED_TIME_PER_PERIOD_EXEMPTED_MS,
- 20 * MINUTE_IN_MILLIS);
- setDeviceConfigLong(QcConstants.KEY_WINDOW_SIZE_EXEMPTED_MS, 20 * MINUTE_IN_MILLIS);
- // window size != allowed time.
- setStandbyBucket(EXEMPTED_INDEX);
- synchronized (mQuotaController.mLock) {
- assertEquals(0,
- mQuotaController.getRemainingExecutionTimeLocked(
- SOURCE_USER_ID, SOURCE_PACKAGE));
- assertEquals(mQcConstants.MAX_EXECUTION_TIME_MS - 20 * MINUTE_IN_MILLIS,
- mQuotaController.getTimeUntilQuotaConsumedLocked(
- SOURCE_USER_ID, SOURCE_PACKAGE));
- }
- }
-
/**
* Test getTimeUntilQuotaConsumedLocked when the determination is based within the bucket
* window and the session is rolling out of the window.
diff --git a/services/tests/mockingservicestests/src/com/android/server/pm/BackgroundUserSoundNotifierTest.java b/services/tests/mockingservicestests/src/com/android/server/pm/BackgroundUserSoundNotifierTest.java
index 3d0c63780ef3..29af7d28339d 100644
--- a/services/tests/mockingservicestests/src/com/android/server/pm/BackgroundUserSoundNotifierTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/pm/BackgroundUserSoundNotifierTest.java
@@ -27,7 +27,7 @@ import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.verifyZeroInteractions;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
import static org.mockito.Mockito.when;
import static org.testng.AssertJUnit.assertEquals;
@@ -128,7 +128,7 @@ public class BackgroundUserSoundNotifierTest {
AudioManager.AUDIOFOCUS_NONE, /* flags= */ 0, Build.VERSION.SDK_INT);
clearInvocations(mNotificationManager);
mBackgroundUserSoundNotifier.notifyForegroundUserAboutSoundIfNecessary(afi);
- verifyZeroInteractions(mNotificationManager);
+ verifyNoMoreInteractions(mNotificationManager);
}
@Test
@@ -143,7 +143,7 @@ public class BackgroundUserSoundNotifierTest {
Build.VERSION.SDK_INT);
clearInvocations(mNotificationManager);
mBackgroundUserSoundNotifier.notifyForegroundUserAboutSoundIfNecessary(afi);
- verifyZeroInteractions(mNotificationManager);
+ verifyNoMoreInteractions(mNotificationManager);
}
@Test
diff --git a/services/tests/mockingservicestests/src/com/android/server/sensorprivacy/CameraPrivacyLightControllerTest.java b/services/tests/mockingservicestests/src/com/android/server/sensorprivacy/CameraPrivacyLightControllerTest.java
index dc04b6aea318..bf3fe8c70bf1 100644
--- a/services/tests/mockingservicestests/src/com/android/server/sensorprivacy/CameraPrivacyLightControllerTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/sensorprivacy/CameraPrivacyLightControllerTest.java
@@ -29,7 +29,7 @@ import static org.mockito.Mockito.atLeastOnce;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.times;
-import static org.mockito.Mockito.verifyZeroInteractions;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
import android.app.AppOpsManager;
import android.content.Context;
@@ -176,9 +176,9 @@ public class CameraPrivacyLightControllerTest {
prepareCameraPrivacyLightController(List.of(getNextLight(true)), Collections.EMPTY_SET,
true, new int[0], mDefaultAlsThresholdsLux, mDefaultAlsAveragingIntervalMillis);
- verifyZeroInteractions(mLightsManager);
- verifyZeroInteractions(mAppOpsManager);
- verifyZeroInteractions(mSensorManager);
+ verifyNoMoreInteractions(mLightsManager);
+ verifyNoMoreInteractions(mAppOpsManager);
+ verifyNoMoreInteractions(mSensorManager);
}
@Test
diff --git a/services/tests/mockingservicestests/src/com/android/server/wallpaper/WallpaperCropperTest.java b/services/tests/mockingservicestests/src/com/android/server/wallpaper/WallpaperCropperTest.java
index 241ffdc19ce4..4f74667f3ff2 100644
--- a/services/tests/mockingservicestests/src/com/android/server/wallpaper/WallpaperCropperTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/wallpaper/WallpaperCropperTest.java
@@ -33,10 +33,8 @@ import static com.android.window.flags.Flags.FLAG_MULTI_CROP;
import static com.google.common.truth.Truth.assertThat;
-import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.eq;
-import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
import static org.mockito.MockitoAnnotations.initMocks;
@@ -48,7 +46,6 @@ import android.platform.test.annotations.RequiresFlagsEnabled;
import android.util.ArraySet;
import android.util.Log;
import android.util.SparseArray;
-import android.view.Display;
import android.view.DisplayInfo;
import android.view.WindowInsets;
import android.view.WindowManager;
@@ -95,7 +92,6 @@ public class WallpaperCropperTest {
@Mock
private Resources mResources;
- private WallpaperCropper mWallpaperCropper;
private static final Point PORTRAIT_ONE = new Point(500, 800);
private static final Point PORTRAIT_TWO = new Point(400, 1000);
@@ -171,7 +167,6 @@ public class WallpaperCropperTest {
return getWallpaperTestDir(userId);
}).when(() -> WallpaperUtils.getWallpaperDir(anyInt()));
- mWallpaperCropper = new WallpaperCropper(mWallpaperDisplayHelper);
}
private File getWallpaperTestDir(int userId) {
@@ -738,13 +733,13 @@ public class WallpaperCropperTest {
DisplayInfo displayInfo = new DisplayInfo();
displayInfo.logicalWidth = 2560;
displayInfo.logicalHeight = 1044;
+ setUpWithDisplays(List.of(new Point(displayInfo.logicalWidth, displayInfo.logicalHeight)));
doReturn(displayInfo).when(mWallpaperDisplayHelper).getDisplayInfo(eq(DEFAULT_DISPLAY));
WallpaperData wallpaperData = createWallpaperData(/* isStockWallpaper = */ false,
new Point(100, 100));
- assertThat(
- mWallpaperCropper.isWallpaperCompatibleForDisplay(DEFAULT_DISPLAY,
- wallpaperData)).isTrue();
+ assertThat(new WallpaperCropper(mWallpaperDisplayHelper).isWallpaperCompatibleForDisplay(
+ DEFAULT_DISPLAY, wallpaperData)).isTrue();
}
// Test isWallpaperCompatibleForDisplay always return true for the stock wallpaper.
@@ -755,49 +750,67 @@ public class WallpaperCropperTest {
DisplayInfo displayInfo = new DisplayInfo();
displayInfo.logicalWidth = 2560;
displayInfo.logicalHeight = 1044;
+ setUpWithDisplays(List.of(new Point(displayInfo.logicalWidth, displayInfo.logicalHeight)));
doReturn(displayInfo).when(mWallpaperDisplayHelper).getDisplayInfo(eq(displayId));
WallpaperData wallpaperData = createWallpaperData(/* isStockWallpaper = */ true,
new Point(100, 100));
- assertThat(
- mWallpaperCropper.isWallpaperCompatibleForDisplay(displayId,
- wallpaperData)).isTrue();
+ assertThat(new WallpaperCropper(mWallpaperDisplayHelper).isWallpaperCompatibleForDisplay(
+ displayId, wallpaperData)).isTrue();
}
// Test isWallpaperCompatibleForDisplay wallpaper is suitable for the display and wallpaper
// aspect ratio meets the hard-coded aspect ratio.
@Test
- public void isWallpaperCompatibleForDisplay_wallpaperSizeSuitableForDisplayAndMeetAspectRatio_returnTrue()
+ public void isWallpaperCompatibleForDisplay_wallpaperSizeLargerThanDisplayAndMeetAspectRatio_returnTrue()
throws Exception {
final int displayId = 2;
DisplayInfo displayInfo = new DisplayInfo();
displayInfo.logicalWidth = 2560;
displayInfo.logicalHeight = 1044;
+ setUpWithDisplays(List.of(new Point(displayInfo.logicalWidth, displayInfo.logicalHeight)));
doReturn(displayInfo).when(mWallpaperDisplayHelper).getDisplayInfo(eq(displayId));
WallpaperData wallpaperData = createWallpaperData(/* isStockWallpaper = */ false,
new Point(4000, 3000));
- assertThat(
- mWallpaperCropper.isWallpaperCompatibleForDisplay(displayId,
- wallpaperData)).isTrue();
+ assertThat(new WallpaperCropper(mWallpaperDisplayHelper).isWallpaperCompatibleForDisplay(
+ displayId, wallpaperData)).isTrue();
}
- // Test isWallpaperCompatibleForDisplay wallpaper is not suitable for the display and wallpaper
- // aspect ratio meets the hard-coded aspect ratio.
+ // Test isWallpaperCompatibleForDisplay wallpaper is smaller than the display but larger than
+ // the threshold and wallpaper aspect ratio meets the hard-coded aspect ratio.
@Test
- public void isWallpaperCompatibleForDisplay_wallpaperSizeNotSuitableForDisplayAndMeetAspectRatio_returnFalse()
+ public void isWallpaperCompatibleForDisplay_wallpaperSizeSmallerThanDisplayButBeyondThresholdAndMeetAspectRatio_returnTrue()
throws Exception {
final int displayId = 2;
DisplayInfo displayInfo = new DisplayInfo();
displayInfo.logicalWidth = 2560;
displayInfo.logicalHeight = 1044;
+ setUpWithDisplays(List.of(new Point(displayInfo.logicalWidth, displayInfo.logicalHeight)));
doReturn(displayInfo).when(mWallpaperDisplayHelper).getDisplayInfo(eq(displayId));
WallpaperData wallpaperData = createWallpaperData(/* isStockWallpaper = */ false,
- new Point(1000, 500));
+ new Point(2000, 900));
+
+ assertThat(new WallpaperCropper(mWallpaperDisplayHelper).isWallpaperCompatibleForDisplay(
+ displayId, wallpaperData)).isTrue();
+ }
+
+ // Test isWallpaperCompatibleForDisplay wallpaper is smaller than the display but larger than
+ // the threshold and wallpaper aspect ratio meets the hard-coded aspect ratio.
+ @Test
+ public void isWallpaperCompatibleForDisplay_wallpaperSizeSmallerThanDisplayButAboveThresholdAndMeetAspectRatio_returnFalse()
+ throws Exception {
+ final int displayId = 2;
+ DisplayInfo displayInfo = new DisplayInfo();
+ displayInfo.logicalWidth = 2560;
+ displayInfo.logicalHeight = 1044;
+ setUpWithDisplays(List.of(new Point(displayInfo.logicalWidth, displayInfo.logicalHeight)));
+ doReturn(displayInfo).when(mWallpaperDisplayHelper).getDisplayInfo(eq(displayId));
+ WallpaperData wallpaperData = createWallpaperData(/* isStockWallpaper = */ false,
+ new Point(2000, 800));
- assertThat(
- mWallpaperCropper.isWallpaperCompatibleForDisplay(displayId,
- wallpaperData)).isFalse();
+ assertThat(new WallpaperCropper(mWallpaperDisplayHelper).isWallpaperCompatibleForDisplay(
+ displayId, wallpaperData)).isFalse();
}
// Test isWallpaperCompatibleForDisplay wallpaper is suitable for the display and wallpaper
@@ -809,13 +822,13 @@ public class WallpaperCropperTest {
DisplayInfo displayInfo = new DisplayInfo();
displayInfo.logicalWidth = 2560;
displayInfo.logicalHeight = 1044;
+ setUpWithDisplays(List.of(new Point(displayInfo.logicalWidth, displayInfo.logicalHeight)));
doReturn(displayInfo).when(mWallpaperDisplayHelper).getDisplayInfo(eq(displayId));
WallpaperData wallpaperData = createWallpaperData(/* isStockWallpaper = */ false,
new Point(2000, 4000));
- assertThat(
- mWallpaperCropper.isWallpaperCompatibleForDisplay(displayId,
- wallpaperData)).isFalse();
+ assertThat(new WallpaperCropper(mWallpaperDisplayHelper).isWallpaperCompatibleForDisplay(
+ displayId, wallpaperData)).isFalse();
}
// Test isWallpaperCompatibleForDisplay, portrait display, wallpaper is suitable for the display
@@ -827,24 +840,13 @@ public class WallpaperCropperTest {
DisplayInfo displayInfo = new DisplayInfo();
displayInfo.logicalWidth = 1044;
displayInfo.logicalHeight = 2560;
+ setUpWithDisplays(List.of(new Point(displayInfo.logicalWidth, displayInfo.logicalHeight)));
doReturn(displayInfo).when(mWallpaperDisplayHelper).getDisplayInfo(eq(displayId));
WallpaperData wallpaperData = createWallpaperData(/* isStockWallpaper = */ false,
new Point(2000, 4000));
- assertThat(
- mWallpaperCropper.isWallpaperCompatibleForDisplay(displayId,
- wallpaperData)).isTrue();
- }
-
- private void mockDisplay(int displayId, Point displayResolution) {
- final Display mockDisplay = mock(Display.class);
- when(mockDisplay.getDisplayInfo(any(DisplayInfo.class))).thenAnswer(invocation -> {
- DisplayInfo displayInfo = invocation.getArgument(0);
- displayInfo.displayId = displayId;
- displayInfo.logicalWidth = displayResolution.x;
- displayInfo.logicalHeight = displayResolution.y;
- return true;
- });
+ assertThat(new WallpaperCropper(mWallpaperDisplayHelper).isWallpaperCompatibleForDisplay(
+ displayId, wallpaperData)).isTrue();
}
private WallpaperData createWallpaperData(boolean isStockWallpaper, Point wallpaperSize)
diff --git a/services/tests/powerservicetests/src/com/android/server/power/NotifierTest.java b/services/tests/powerservicetests/src/com/android/server/power/NotifierTest.java
index 4e56422ec391..94ce72368a92 100644
--- a/services/tests/powerservicetests/src/com/android/server/power/NotifierTest.java
+++ b/services/tests/powerservicetests/src/com/android/server/power/NotifierTest.java
@@ -39,9 +39,9 @@ import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoMoreInteractions;
-import static org.mockito.Mockito.verifyZeroInteractions;
import static org.mockito.Mockito.when;
+import android.annotation.NonNull;
import android.app.ActivityManagerInternal;
import android.app.AppOpsManager;
import android.content.Context;
@@ -53,8 +53,8 @@ import android.os.BatteryStats;
import android.os.BatteryStatsInternal;
import android.os.Handler;
import android.os.IBinder;
-import android.os.IWakeLockCallback;
import android.os.IScreenTimeoutPolicyListener;
+import android.os.IWakeLockCallback;
import android.os.Looper;
import android.os.PowerManager;
import android.os.RemoteException;
@@ -205,7 +205,7 @@ public class NotifierTest {
mTestExecutor.simulateAsyncExecutionOfLastCommand();
// THEN the device doesn't vibrate
- verifyZeroInteractions(mVibrator);
+ verifyNoMoreInteractions(mVibrator);
}
@Test
@@ -238,7 +238,7 @@ public class NotifierTest {
mTestExecutor.simulateAsyncExecutionOfLastCommand();
// THEN the device doesn't vibrate
- verifyZeroInteractions(mVibrator);
+ verifyNoMoreInteractions(mVibrator);
}
@Test
@@ -725,10 +725,11 @@ public class NotifierTest {
final int uid = 1234;
final int pid = 5678;
+
mNotifier.onWakeLockReleased(PowerManager.PARTIAL_WAKE_LOCK, "wakelockTag",
"my.package.name", uid, pid, /* workSource= */ null, /* historyTag= */ null,
exceptingCallback);
- verifyZeroInteractions(mWakeLockLog);
+ verifyNoMoreInteractions(mWakeLockLog);
mTestLooper.dispatchAll();
verify(mWakeLockLog).onWakeLockReleased("wakelockTag", uid, 1);
clearInvocations(mBatteryStats);
@@ -790,6 +791,55 @@ public class NotifierTest {
}
@Test
+ public void test_wakeLockLogUsesWorkSource() {
+ createNotifier();
+ clearInvocations(mWakeLockLog);
+ IWakeLockCallback exceptingCallback = new IWakeLockCallback.Stub() {
+ @Override public void onStateChanged(boolean enabled) throws RemoteException {
+ throw new RemoteException("Just testing");
+ }
+ };
+
+ final int uid = 1234;
+ final int pid = 5678;
+ WorkSource worksource = new WorkSource(1212);
+ WorkSource worksource2 = new WorkSource(3131);
+
+ mNotifier.onWakeLockAcquired(PowerManager.PARTIAL_WAKE_LOCK, "wakelockTag",
+ "my.package.name", uid, pid, worksource, /* historyTag= */ null,
+ exceptingCallback);
+ verify(mWakeLockLog).onWakeLockAcquired("wakelockTag", 1212,
+ PowerManager.PARTIAL_WAKE_LOCK, -1);
+
+ // Release the wakelock
+ mNotifier.onWakeLockReleased(PowerManager.FULL_WAKE_LOCK, "wakelockTag2",
+ "my.package.name", uid, pid, worksource2, /* historyTag= */ null,
+ exceptingCallback);
+ verify(mWakeLockLog).onWakeLockReleased("wakelockTag2", 3131, -1);
+
+ // clear the handler
+ mTestLooper.dispatchAll();
+
+ // Now test with improveWakelockLatency flag true
+ clearInvocations(mWakeLockLog);
+ when(mPowerManagerFlags.improveWakelockLatency()).thenReturn(true);
+
+ mNotifier.onWakeLockAcquired(PowerManager.PARTIAL_WAKE_LOCK, "wakelockTag",
+ "my.package.name", uid, pid, worksource, /* historyTag= */ null,
+ exceptingCallback);
+ mTestLooper.dispatchAll();
+ verify(mWakeLockLog).onWakeLockAcquired("wakelockTag", 1212,
+ PowerManager.PARTIAL_WAKE_LOCK, 1);
+
+ // Release the wakelock
+ mNotifier.onWakeLockReleased(PowerManager.FULL_WAKE_LOCK, "wakelockTag2",
+ "my.package.name", uid, pid, worksource2, /* historyTag= */ null,
+ exceptingCallback);
+ mTestLooper.dispatchAll();
+ verify(mWakeLockLog).onWakeLockReleased("wakelockTag2", 3131, 1);
+ }
+
+ @Test
public void
test_notifierProcessesWorkSourceDeepCopy_OnWakelockChanging() throws RemoteException {
when(mPowerManagerFlags.improveWakelockLatency()).thenReturn(true);
@@ -845,7 +895,7 @@ public class NotifierTest {
exceptingCallback);
// No interaction because we expect that to happen in async
- verifyZeroInteractions(mWakeLockLog, mBatteryStats, mAppOpsManager);
+ verifyNoMoreInteractions(mWakeLockLog, mBatteryStats, mAppOpsManager);
// Progressing the looper, and validating all the interactions
mTestLooper.dispatchAll();
@@ -944,15 +994,23 @@ public class NotifierTest {
assertEquals(mNotifier.getWakelockMonitorTypeForLogging(PowerManager.PARTIAL_WAKE_LOCK),
PowerManager.PARTIAL_WAKE_LOCK);
assertEquals(mNotifier.getWakelockMonitorTypeForLogging(
+ PowerManager.DOZE_WAKE_LOCK), -1);
+ }
+
+ @Test
+ public void getWakelockMonitorTypeForLogging_evaluateProximityLevel() {
+ // How proximity wakelock is evaluated depends on boolean configuration. Test both.
+ when(mResourcesSpy.getBoolean(
+ com.android.internal.R.bool.config_suspendWhenScreenOffDueToProximity))
+ .thenReturn(false);
+ createNotifier();
+ assertEquals(mNotifier.getWakelockMonitorTypeForLogging(
PowerManager.PROXIMITY_SCREEN_OFF_WAKE_LOCK),
PowerManager.PARTIAL_WAKE_LOCK);
- assertEquals(mNotifier.getWakelockMonitorTypeForLogging(
- PowerManager.DOZE_WAKE_LOCK), -1);
when(mResourcesSpy.getBoolean(
com.android.internal.R.bool.config_suspendWhenScreenOffDueToProximity))
.thenReturn(true);
-
createNotifier();
assertEquals(mNotifier.getWakelockMonitorTypeForLogging(
PowerManager.PROXIMITY_SCREEN_OFF_WAKE_LOCK), -1);
@@ -1239,7 +1297,7 @@ public class NotifierTest {
}
@Override
- public WakeLockLog getWakeLockLog(Context context) {
+ public @NonNull WakeLockLog getWakeLockLog(Context context) {
return mWakeLockLog;
}
diff --git a/services/tests/powerservicetests/src/com/android/server/power/WakeLockLogTest.java b/services/tests/powerservicetests/src/com/android/server/power/WakeLockLogTest.java
index c1d7c7b4a4c2..534337ee9dbf 100644
--- a/services/tests/powerservicetests/src/com/android/server/power/WakeLockLogTest.java
+++ b/services/tests/powerservicetests/src/com/android/server/power/WakeLockLogTest.java
@@ -27,6 +27,8 @@ import android.content.pm.PackageManager;
import android.os.PowerManager;
import android.os.Process;
+import com.android.server.power.WakeLockLog.TagData;
+
import org.junit.Before;
import org.junit.Test;
import org.mockito.Mock;
@@ -62,8 +64,9 @@ public class WakeLockLogTest {
@Test
public void testAddTwoItems_withNoEventTimeSupplied() {
final int tagDatabaseSize = 128;
+ final int tagStartingSize = 16;
final int logSize = 20;
- TestInjector injectorSpy = spy(new TestInjector(tagDatabaseSize, logSize));
+ TestInjector injectorSpy = spy(new TestInjector(tagDatabaseSize, tagStartingSize, logSize));
WakeLockLog log = new WakeLockLog(injectorSpy, mContext);
when(injectorSpy.currentTimeMillis()).thenReturn(1000L);
log.onWakeLockAcquired("TagPartial", 101,
@@ -93,8 +96,9 @@ public class WakeLockLogTest {
@Test
public void testAddTwoItems() {
final int tagDatabaseSize = 128;
+ final int tagStartingSize = 16;
final int logSize = 20;
- TestInjector injectorSpy = spy(new TestInjector(tagDatabaseSize, logSize));
+ TestInjector injectorSpy = spy(new TestInjector(tagDatabaseSize, tagStartingSize, logSize));
WakeLockLog log = new WakeLockLog(injectorSpy, mContext);
log.onWakeLockAcquired("TagPartial", 101,
@@ -117,8 +121,9 @@ public class WakeLockLogTest {
@Test
public void testAddTwoItemsWithTimeReset() {
final int tagDatabaseSize = 128;
+ final int tagStartingSize = 16;
final int logSize = 20;
- TestInjector injectorSpy = spy(new TestInjector(tagDatabaseSize, logSize));
+ TestInjector injectorSpy = spy(new TestInjector(tagDatabaseSize, tagStartingSize, logSize));
WakeLockLog log = new WakeLockLog(injectorSpy, mContext);
log.onWakeLockAcquired("TagPartial", 101, PowerManager.PARTIAL_WAKE_LOCK, 1000L);
@@ -136,9 +141,10 @@ public class WakeLockLogTest {
@Test
public void testAddTwoItemsWithTagOverwrite() {
- final int tagDatabaseSize = 2;
+ final int tagDatabaseSize = 1;
+ final int tagStartingSize = 1;
final int logSize = 20;
- TestInjector injectorSpy = spy(new TestInjector(tagDatabaseSize, logSize));
+ TestInjector injectorSpy = spy(new TestInjector(tagDatabaseSize, tagStartingSize, logSize));
WakeLockLog log = new WakeLockLog(injectorSpy, mContext);
log.onWakeLockAcquired("TagPartial", 101, PowerManager.PARTIAL_WAKE_LOCK, 1000L);
@@ -157,8 +163,9 @@ public class WakeLockLogTest {
@Test
public void testAddFourItemsWithRingBufferOverflow() {
final int tagDatabaseSize = 6;
+ final int tagStartingSize = 2;
final int logSize = 10;
- TestInjector injectorSpy = spy(new TestInjector(tagDatabaseSize, logSize));
+ TestInjector injectorSpy = spy(new TestInjector(tagDatabaseSize, tagStartingSize, logSize));
WakeLockLog log = new WakeLockLog(injectorSpy, mContext);
// Wake lock 1 acquired - log size = 3
@@ -206,8 +213,9 @@ public class WakeLockLogTest {
@Test
public void testAddItemWithBadTag() {
final int tagDatabaseSize = 6;
+ final int tagStartingSize = 2;
final int logSize = 10;
- TestInjector injectorSpy = spy(new TestInjector(tagDatabaseSize, logSize));
+ TestInjector injectorSpy = spy(new TestInjector(tagDatabaseSize, tagStartingSize, logSize));
WakeLockLog log = new WakeLockLog(injectorSpy, mContext);
// Bad tag means it wont get written
@@ -224,8 +232,9 @@ public class WakeLockLogTest {
@Test
public void testAddItemWithReducedTagName() {
final int tagDatabaseSize = 6;
+ final int tagStartingSize = 2;
final int logSize = 10;
- TestInjector injectorSpy = spy(new TestInjector(tagDatabaseSize, logSize));
+ TestInjector injectorSpy = spy(new TestInjector(tagDatabaseSize, tagStartingSize, logSize));
WakeLockLog log = new WakeLockLog(injectorSpy, mContext);
log.onWakeLockAcquired("*job*/com.one.two.3hree/.one..Last", 101,
@@ -242,9 +251,10 @@ public class WakeLockLogTest {
@Test
public void testAddAcquireAndReleaseWithRepeatTagName() {
- final int tagDatabaseSize = 6;
+ final int tagDatabaseSize = 5;
+ final int tagStartingSize = 5;
final int logSize = 10;
- TestInjector injectorSpy = spy(new TestInjector(tagDatabaseSize, logSize));
+ TestInjector injectorSpy = spy(new TestInjector(tagDatabaseSize, tagStartingSize, logSize));
WakeLockLog log = new WakeLockLog(injectorSpy, mContext);
log.onWakeLockAcquired("HowdyTag", 101, PowerManager.PARTIAL_WAKE_LOCK, 1000L);
@@ -263,8 +273,9 @@ public class WakeLockLogTest {
@Test
public void testAddAcquireAndReleaseWithTimeTravel() {
final int tagDatabaseSize = 6;
+ final int tagStartingSize = 2;
final int logSize = 10;
- TestInjector injectorSpy = spy(new TestInjector(tagDatabaseSize, logSize));
+ TestInjector injectorSpy = spy(new TestInjector(tagDatabaseSize, tagStartingSize, logSize));
WakeLockLog log = new WakeLockLog(injectorSpy, mContext);
log.onWakeLockAcquired("HowdyTag", 101, PowerManager.PARTIAL_WAKE_LOCK, 1100L);
@@ -283,8 +294,9 @@ public class WakeLockLogTest {
@Test
public void testAddSystemWakelock() {
final int tagDatabaseSize = 6;
+ final int tagStartingSize = 2;
final int logSize = 10;
- TestInjector injectorSpy = spy(new TestInjector(tagDatabaseSize, logSize));
+ TestInjector injectorSpy = spy(new TestInjector(tagDatabaseSize, tagStartingSize, logSize));
WakeLockLog log = new WakeLockLog(injectorSpy, mContext);
log.onWakeLockAcquired("TagPartial", 101,
@@ -302,8 +314,9 @@ public class WakeLockLogTest {
@Test
public void testAddItemWithNoPackageName() {
final int tagDatabaseSize = 128;
+ final int tagStartingSize = 16;
final int logSize = 20;
- TestInjector injectorSpy = spy(new TestInjector(tagDatabaseSize, logSize));
+ TestInjector injectorSpy = spy(new TestInjector(tagDatabaseSize, tagStartingSize, logSize));
WakeLockLog log = new WakeLockLog(injectorSpy, mContext);
when(mPackageManager.getPackagesForUid(101)).thenReturn(null);
@@ -322,8 +335,9 @@ public class WakeLockLogTest {
@Test
public void testAddItemWithMultiplePackageNames() {
final int tagDatabaseSize = 128;
+ final int tagStartingSize = 16;
final int logSize = 20;
- TestInjector injectorSpy = spy(new TestInjector(tagDatabaseSize, logSize));
+ TestInjector injectorSpy = spy(new TestInjector(tagDatabaseSize, tagStartingSize, logSize));
WakeLockLog log = new WakeLockLog(injectorSpy, mContext);
when(mPackageManager.getPackagesForUid(101)).thenReturn(
@@ -344,8 +358,9 @@ public class WakeLockLogTest {
@Test
public void testAddItemsWithRepeatOwnerUid_UsesCache() {
final int tagDatabaseSize = 128;
+ final int tagStartingSize = 16;
final int logSize = 20;
- TestInjector injectorSpy = spy(new TestInjector(tagDatabaseSize, logSize));
+ TestInjector injectorSpy = spy(new TestInjector(tagDatabaseSize, tagStartingSize, logSize));
WakeLockLog log = new WakeLockLog(injectorSpy, mContext);
log.onWakeLockAcquired("TagPartial", 101,
@@ -375,8 +390,9 @@ public class WakeLockLogTest {
@Test
public void testAddItemsWithRepeatOwnerUid_SavedAcquisitions_UsesCache() {
final int tagDatabaseSize = 128;
+ final int tagStartingSize = 16;
final int logSize = 10;
- TestInjector injectorSpy = spy(new TestInjector(tagDatabaseSize, logSize));
+ TestInjector injectorSpy = spy(new TestInjector(tagDatabaseSize, tagStartingSize, logSize));
WakeLockLog log = new WakeLockLog(injectorSpy, mContext);
log.onWakeLockAcquired("TagPartial", 101,
@@ -420,6 +436,34 @@ public class WakeLockLogTest {
verify(mPackageManager, times(1)).getPackagesForUid(101);
}
+ @Test
+ public void testTagDatabaseGrowsBeyondStartingSize() {
+ final int tagDatabaseSize = 3;
+ final int tagStartingSize = 1;
+ final int logSize = 10;
+ // start with size = 1 and max size
+ TestInjector injector = new TestInjector(tagDatabaseSize, tagStartingSize, logSize);
+ WakeLockLog.TagDatabase td = new WakeLockLog.TagDatabase(injector);
+
+ // Add one
+ TagData data1 = td.findOrCreateTag("Tagname1", 1001, /* shouldCreate= */ true);
+ assertEquals(0, td.getTagIndex(data1));
+
+ // Check that it grows by adding 1 more
+ TagData data2 = td.findOrCreateTag("Tagname2", 1001, /* shouldCreate= */ true);
+ assertEquals(1, td.getTagIndex(data2));
+
+ // Lets add the last one to fill up the DB to maxSize
+ TagData data3 = td.findOrCreateTag("Tagname3", 1001, /* shouldCreate= */ true);
+ assertEquals(2, td.getTagIndex(data3));
+
+ // Adding a fourth one should replace the oldest one (Tagname1)
+ TagData data4 = td.findOrCreateTag("Tagname4", 1001, /* shouldCreate= */ true);
+ assertEquals(0, td.getTagIndex(data4));
+ assertEquals(tagDatabaseSize, td.getTagIndex(data1));
+
+ }
+
private String dumpLog(WakeLockLog log, boolean includeTagDb) {
StringWriter sw = new StringWriter();
PrintWriter pw = new PrintWriter(sw);
@@ -429,10 +473,12 @@ public class WakeLockLogTest {
public static class TestInjector extends WakeLockLog.Injector {
private final int mTagDatabaseSize;
+ private final int mTagStartingSize;
private final int mLogSize;
- public TestInjector(int tagDatabaseSize, int logSize) {
+ public TestInjector(int tagDatabaseSize, int tagStartingSize, int logSize) {
mTagDatabaseSize = tagDatabaseSize;
+ mTagStartingSize = tagStartingSize;
mLogSize = logSize;
}
@@ -442,6 +488,11 @@ public class WakeLockLogTest {
}
@Override
+ public int getTagDatabaseStartingSize() {
+ return mTagStartingSize;
+ }
+
+ @Override
public int getLogSize() {
return mLogSize;
}
diff --git a/services/tests/servicestests/src/com/android/server/NetworkScoreServiceTest.java b/services/tests/servicestests/src/com/android/server/NetworkScoreServiceTest.java
index 75df9a8707c0..d254e9689048 100644
--- a/services/tests/servicestests/src/com/android/server/NetworkScoreServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/NetworkScoreServiceTest.java
@@ -35,7 +35,6 @@ import static org.mockito.Mockito.timeout;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoMoreInteractions;
-import static org.mockito.Mockito.verifyZeroInteractions;
import static org.mockito.Mockito.when;
import android.Manifest.permission;
@@ -243,7 +242,7 @@ public class NetworkScoreServiceTest {
@Test
public void testRequestScores_providerNotConnected() throws Exception {
assertFalse(mNetworkScoreService.requestScores(new NetworkKey[0]));
- verifyZeroInteractions(mRecommendationProvider);
+ verifyNoMoreInteractions(mRecommendationProvider);
}
@Test
@@ -604,7 +603,7 @@ public class NetworkScoreServiceTest {
consumer.accept(mNetworkScoreCache, null /*cookie*/);
verify(mNetworkScoreCache).updateScores(scoredNetworkList);
- verifyZeroInteractions(mCurrentNetworkFilter, mScanResultsFilter);
+ verifyNoMoreInteractions(mCurrentNetworkFilter, mScanResultsFilter);
}
@Test
@@ -618,7 +617,7 @@ public class NetworkScoreServiceTest {
consumer.accept(mNetworkScoreCache, NetworkScoreManager.SCORE_FILTER_NONE);
verify(mNetworkScoreCache).updateScores(scoredNetworkList);
- verifyZeroInteractions(mCurrentNetworkFilter, mScanResultsFilter);
+ verifyNoMoreInteractions(mCurrentNetworkFilter, mScanResultsFilter);
}
@Test
@@ -632,7 +631,7 @@ public class NetworkScoreServiceTest {
consumer.accept(mNetworkScoreCache, -1 /*cookie*/);
verify(mNetworkScoreCache).updateScores(scoredNetworkList);
- verifyZeroInteractions(mCurrentNetworkFilter, mScanResultsFilter);
+ verifyNoMoreInteractions(mCurrentNetworkFilter, mScanResultsFilter);
}
@Test
@@ -646,7 +645,7 @@ public class NetworkScoreServiceTest {
consumer.accept(mNetworkScoreCache, "not an int" /*cookie*/);
verify(mNetworkScoreCache).updateScores(scoredNetworkList);
- verifyZeroInteractions(mCurrentNetworkFilter, mScanResultsFilter);
+ verifyNoMoreInteractions(mCurrentNetworkFilter, mScanResultsFilter);
}
@Test
@@ -658,7 +657,7 @@ public class NetworkScoreServiceTest {
consumer.accept(mNetworkScoreCache, NetworkScoreManager.SCORE_FILTER_NONE);
- verifyZeroInteractions(mNetworkScoreCache, mCurrentNetworkFilter, mScanResultsFilter);
+ verifyNoMoreInteractions(mNetworkScoreCache, mCurrentNetworkFilter, mScanResultsFilter);
}
@Test
@@ -676,7 +675,7 @@ public class NetworkScoreServiceTest {
consumer.accept(mNetworkScoreCache, NetworkScoreManager.SCORE_FILTER_CURRENT_NETWORK);
verify(mNetworkScoreCache).updateScores(filteredList);
- verifyZeroInteractions(mScanResultsFilter);
+ verifyNoMoreInteractions(mScanResultsFilter);
}
@Test
@@ -694,7 +693,7 @@ public class NetworkScoreServiceTest {
consumer.accept(mNetworkScoreCache, NetworkScoreManager.SCORE_FILTER_SCAN_RESULTS);
verify(mNetworkScoreCache).updateScores(filteredList);
- verifyZeroInteractions(mCurrentNetworkFilter);
+ verifyNoMoreInteractions(mCurrentNetworkFilter);
}
@Test
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/AbstractAccessibilityServiceConnectionTest.java b/services/tests/servicestests/src/com/android/server/accessibility/AbstractAccessibilityServiceConnectionTest.java
index 42b84bdc51e6..c7c8c5846bb1 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/AbstractAccessibilityServiceConnectionTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/AbstractAccessibilityServiceConnectionTest.java
@@ -64,7 +64,6 @@ import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoMoreInteractions;
-import static org.mockito.Mockito.verifyZeroInteractions;
import static org.mockito.Mockito.when;
import android.accessibilityservice.AccessibilityService;
@@ -838,7 +837,7 @@ public class AbstractAccessibilityServiceConnectionTest {
// ...without secure layers included
assertThat(layerArgsCaptor.getValue().mCaptureSecureLayers).isFalse();
// No error sent to callback
- verifyZeroInteractions(mMockCallback);
+ verifyNoMoreInteractions(mMockCallback);
}
@Test
@@ -856,7 +855,7 @@ public class AbstractAccessibilityServiceConnectionTest {
// ...with secure layers included
assertThat(layerArgsCaptor.getValue().mCaptureSecureLayers).isTrue();
// No error sent to callback
- verifyZeroInteractions(mMockCallback);
+ verifyNoMoreInteractions(mMockCallback);
}
@Test
@@ -889,7 +888,7 @@ public class AbstractAccessibilityServiceConnectionTest {
// ...with secure layers included
assertThat(layerArgsCaptor.getValue().mCaptureSecureLayers).isTrue();
// No error sent to callback
- verifyZeroInteractions(mMockCallback);
+ verifyNoMoreInteractions(mMockCallback);
}
private void takeScreenshotOfWindow(int windowFlags) throws Exception {
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 8253595a50d1..2ccd33648c3e 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityManagerServiceTest.java
@@ -50,6 +50,7 @@ import static org.junit.Assume.assumeTrue;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyLong;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.atLeastOnce;
import static org.mockito.Mockito.doAnswer;
@@ -66,6 +67,7 @@ import android.Manifest;
import android.accessibilityservice.AccessibilityServiceInfo;
import android.accessibilityservice.IAccessibilityServiceClient;
import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.annotation.UserIdInt;
import android.app.PendingIntent;
import android.app.RemoteAction;
@@ -77,10 +79,12 @@ import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
+import android.content.pm.PackageManagerInternal;
import android.content.pm.ResolveInfo;
import android.content.pm.ServiceInfo;
import android.content.res.XmlResourceParser;
import android.graphics.drawable.Icon;
+import android.hardware.display.DisplayManager;
import android.hardware.display.DisplayManagerGlobal;
import android.hardware.input.KeyGestureEvent;
import android.net.Uri;
@@ -114,6 +118,7 @@ import android.view.accessibility.IUserInitializationCompleteCallback;
import androidx.test.core.app.ApplicationProvider;
import androidx.test.filters.SmallTest;
+import androidx.test.platform.app.InstrumentationRegistry;
import com.android.compatibility.common.util.TestUtils;
import com.android.internal.R;
@@ -136,6 +141,8 @@ import com.android.server.statusbar.StatusBarManagerInternal;
import com.android.server.wm.ActivityTaskManagerInternal;
import com.android.server.wm.WindowManagerInternal;
+import com.google.common.truth.Correspondence;
+
import org.junit.After;
import org.junit.Before;
import org.junit.Rule;
@@ -188,6 +195,8 @@ public class AccessibilityManagerServiceTest {
DESCRIPTION,
TEST_PENDING_INTENT);
+ private static final int FAKE_SYSTEMUI_UID = 1000;
+
private static final int TEST_DISPLAY = Display.DEFAULT_DISPLAY + 1;
private static final String TARGET_MAGNIFICATION = MAGNIFICATION_CONTROLLER_NAME;
private static final ComponentName TARGET_ALWAYS_ON_A11Y_SERVICE =
@@ -207,11 +216,12 @@ public class AccessibilityManagerServiceTest {
@Mock private AbstractAccessibilityServiceConnection.SystemSupport mMockSystemSupport;
@Mock private WindowManagerInternal.AccessibilityControllerInternal mMockA11yController;
@Mock private PackageManager mMockPackageManager;
+ @Mock
+ private PackageManagerInternal mMockPackageManagerInternal;
@Mock private WindowManagerInternal mMockWindowManagerService;
@Mock private AccessibilitySecurityPolicy mMockSecurityPolicy;
@Mock private SystemActionPerformer mMockSystemActionPerformer;
@Mock private AccessibilityWindowManager mMockA11yWindowManager;
- @Mock private AccessibilityDisplayListener mMockA11yDisplayListener;
@Mock private ActivityTaskManagerInternal mMockActivityTaskManagerInternal;
@Mock private UserManagerInternal mMockUserManagerInternal;
@Mock private IBinder mMockBinder;
@@ -234,6 +244,7 @@ public class AccessibilityManagerServiceTest {
private TestableLooper mTestableLooper;
private Handler mHandler;
private FakePermissionEnforcer mFakePermissionEnforcer;
+ private TestDisplayManagerWrapper mTestDisplayManagerWrapper;
@Before
public void setUp() throws Exception {
@@ -246,6 +257,7 @@ public class AccessibilityManagerServiceTest {
LocalServices.removeServiceForTest(UserManagerInternal.class);
LocalServices.removeServiceForTest(StatusBarManagerInternal.class);
LocalServices.removeServiceForTest(PermissionEnforcer.class);
+ LocalServices.removeServiceForTest(PackageManagerInternal.class);
LocalServices.addService(
WindowManagerInternal.class, mMockWindowManagerService);
LocalServices.addService(
@@ -256,6 +268,12 @@ public class AccessibilityManagerServiceTest {
mInputFilter = mock(FakeInputFilter.class);
mTestableContext.addMockSystemService(DevicePolicyManager.class, mDevicePolicyManager);
+ when(mMockPackageManagerInternal.getSystemUiServiceComponent()).thenReturn(
+ new ComponentName("com.android.systemui", "com.android.systemui.SystemUIService"));
+ when(mMockPackageManagerInternal.getPackageUid(eq("com.android.systemui"), anyLong(),
+ anyInt())).thenReturn(FAKE_SYSTEMUI_UID);
+ LocalServices.addService(PackageManagerInternal.class, mMockPackageManagerInternal);
+
when(mMockMagnificationController.getMagnificationConnectionManager()).thenReturn(
mMockMagnificationConnectionManager);
when(mMockMagnificationController.getFullScreenMagnificationController()).thenReturn(
@@ -273,15 +291,9 @@ public class AccessibilityManagerServiceTest {
eq(UserHandle.USER_CURRENT)))
.thenReturn(mTestableContext.getUserId());
- final ArrayList<Display> displays = new ArrayList<>();
- final Display defaultDisplay = new Display(DisplayManagerGlobal.getInstance(),
- Display.DEFAULT_DISPLAY, new DisplayInfo(),
- DisplayAdjustments.DEFAULT_DISPLAY_ADJUSTMENTS);
- final Display testDisplay = new Display(DisplayManagerGlobal.getInstance(), TEST_DISPLAY,
- new DisplayInfo(), DisplayAdjustments.DEFAULT_DISPLAY_ADJUSTMENTS);
- displays.add(defaultDisplay);
- displays.add(testDisplay);
- when(mMockA11yDisplayListener.getValidDisplayList()).thenReturn(displays);
+ mTestDisplayManagerWrapper = new TestDisplayManagerWrapper(mTestableContext);
+ mTestDisplayManagerWrapper.mDisplays = createFakeDisplayList(Display.TYPE_INTERNAL,
+ Display.TYPE_EXTERNAL);
mA11yms = new AccessibilityManagerService(
mTestableContext,
@@ -290,7 +302,7 @@ public class AccessibilityManagerServiceTest {
mMockSecurityPolicy,
mMockSystemActionPerformer,
mMockA11yWindowManager,
- mMockA11yDisplayListener,
+ mTestDisplayManagerWrapper,
mMockMagnificationController,
mInputFilter,
mProxyManager,
@@ -2309,6 +2321,73 @@ public class AccessibilityManagerServiceTest {
mA11yms.getCurrentUserIdLocked())).isEmpty();
}
+ @Test
+ public void displayListReturnsDisplays() {
+ mTestDisplayManagerWrapper.mDisplays = createFakeDisplayList(
+ Display.TYPE_INTERNAL,
+ Display.TYPE_EXTERNAL,
+ Display.TYPE_WIFI,
+ Display.TYPE_OVERLAY,
+ Display.TYPE_VIRTUAL
+ );
+ InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> {
+ // In #setUp() we already have TYPE_INTERNAL and TYPE_EXTERNAL. Call the rest.
+ for (int i = 2; i < mTestDisplayManagerWrapper.mDisplays.size(); i++) {
+ mTestDisplayManagerWrapper.mRegisteredListener.onDisplayAdded(
+ mTestDisplayManagerWrapper.mDisplays.get(i).getDisplayId());
+ }
+ });
+
+ List<Display> displays = mA11yms.getValidDisplayList();
+ assertThat(displays).hasSize(5);
+ assertThat(displays)
+ .comparingElementsUsing(
+ Correspondence.transforming(Display::getType, "has a type of"))
+ .containsExactly(Display.TYPE_INTERNAL,
+ Display.TYPE_EXTERNAL,
+ Display.TYPE_WIFI,
+ Display.TYPE_OVERLAY,
+ Display.TYPE_VIRTUAL);
+ }
+
+ @Test
+ public void displayListReturnsDisplays_excludesVirtualPrivate() {
+ // Add a private virtual display whose uid is different from systemui.
+ final List<Display> displays = createFakeDisplayList(Display.TYPE_INTERNAL,
+ Display.TYPE_EXTERNAL);
+ displays.add(createFakeVirtualPrivateDisplay(/* displayId= */ 2, FAKE_SYSTEMUI_UID + 100));
+ mTestDisplayManagerWrapper.mDisplays = displays;
+ InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> {
+ mTestDisplayManagerWrapper.mRegisteredListener.onDisplayAdded(2);
+ });
+
+ List<Display> validDisplays = mA11yms.getValidDisplayList();
+ assertThat(validDisplays).hasSize(2);
+ assertThat(validDisplays)
+ .comparingElementsUsing(
+ Correspondence.transforming(Display::getType, "has a type of"))
+ .doesNotContain(Display.TYPE_VIRTUAL);
+ }
+
+ @Test
+ public void displayListReturnsDisplays_includesVirtualSystemUIPrivate() {
+ // Add a private virtual display whose uid is systemui.
+ final List<Display> displays = createFakeDisplayList(Display.TYPE_INTERNAL,
+ Display.TYPE_EXTERNAL);
+ displays.add(createFakeVirtualPrivateDisplay(/* displayId= */ 2, FAKE_SYSTEMUI_UID));
+ mTestDisplayManagerWrapper.mDisplays = displays;
+ InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> {
+ mTestDisplayManagerWrapper.mRegisteredListener.onDisplayAdded(2);
+ });
+
+ List<Display> validDisplays = mA11yms.getValidDisplayList();
+ assertThat(validDisplays).hasSize(3);
+ assertThat(validDisplays)
+ .comparingElementsUsing(
+ Correspondence.transforming(Display::getType, "has a type of"))
+ .contains(Display.TYPE_VIRTUAL);
+ }
+
private Set<String> readStringsFromSetting(String setting) {
final Set<String> result = new ArraySet<>();
mA11yms.readColonDelimitedSettingToSet(
@@ -2422,6 +2501,27 @@ public class AccessibilityManagerServiceTest {
});
}
+ private static List<Display> createFakeDisplayList(int... types) {
+ final ArrayList<Display> displays = new ArrayList<>();
+ for (int i = 0; i < types.length; i++) {
+ final DisplayInfo info = new DisplayInfo();
+ info.type = types[i];
+ final Display display = new Display(DisplayManagerGlobal.getInstance(),
+ i, info, DisplayAdjustments.DEFAULT_DISPLAY_ADJUSTMENTS);
+ displays.add(display);
+ }
+ return displays;
+ }
+
+ private static Display createFakeVirtualPrivateDisplay(int displayId, int uid) {
+ final DisplayInfo info = new DisplayInfo();
+ info.type = Display.TYPE_VIRTUAL;
+ info.flags |= Display.FLAG_PRIVATE;
+ info.ownerUid = uid;
+ return new Display(DisplayManagerGlobal.getInstance(),
+ displayId, info, DisplayAdjustments.DEFAULT_DISPLAY_ADJUSTMENTS);
+ }
+
public static class FakeInputFilter extends AccessibilityInputFilter {
FakeInputFilter(Context context,
AccessibilityManagerService service) {
@@ -2506,4 +2606,35 @@ public class AccessibilityManagerServiceTest {
Set<String> setting = readStringsFromSetting(ShortcutUtils.convertToKey(shortcutType));
assertThat(setting).containsExactlyElementsIn(value);
}
+
+ private static class TestDisplayManagerWrapper extends
+ AccessibilityDisplayListener.DisplayManagerWrapper {
+ List<Display> mDisplays;
+ DisplayManager.DisplayListener mRegisteredListener;
+
+ TestDisplayManagerWrapper(Context context) {
+ super(context);
+ }
+
+ @Override
+ public Display[] getDisplays() {
+ return mDisplays.toArray(new Display[0]);
+ }
+
+ @Override
+ public Display getDisplay(int displayId) {
+ for (final Display display : mDisplays) {
+ if (display.getDisplayId() == displayId) {
+ return display;
+ }
+ }
+ return null;
+ }
+
+ @Override
+ public void registerDisplayListener(@NonNull DisplayManager.DisplayListener listener,
+ @Nullable Handler handler) {
+ mRegisteredListener = listener;
+ }
+ }
}
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/FingerprintGestureControllerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/FingerprintGestureControllerTest.java
index 96ae102e53f3..d0dc2cbb0f86 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/FingerprintGestureControllerTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/FingerprintGestureControllerTest.java
@@ -24,7 +24,7 @@ import static org.junit.Assert.assertTrue;
import static org.mockito.Mockito.reset;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.verifyZeroInteractions;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
import static org.mockito.Mockito.when;
import android.accessibilityservice.FingerprintGestureController;
@@ -86,7 +86,7 @@ public class FingerprintGestureControllerTest {
mMockFingerprintGestureCallback);
mFingerprintGestureController.onGestureDetectionActiveChanged(true);
mFingerprintGestureController.onGestureDetectionActiveChanged(false);
- verifyZeroInteractions(mMockFingerprintGestureCallback);
+ verifyNoMoreInteractions(mMockFingerprintGestureCallback);
}
@Test
@@ -118,7 +118,7 @@ public class FingerprintGestureControllerTest {
mFingerprintGestureController.onGestureDetectionActiveChanged(true);
mFingerprintGestureController.onGestureDetectionActiveChanged(false);
assertFalse(messageCapturingHandler.hasMessages());
- verifyZeroInteractions(mMockFingerprintGestureCallback);
+ verifyNoMoreInteractions(mMockFingerprintGestureCallback);
messageCapturingHandler.removeAllMessages();
}
@@ -135,7 +135,7 @@ public class FingerprintGestureControllerTest {
mFingerprintGestureController.unregisterFingerprintGestureCallback(
mMockFingerprintGestureCallback);
mFingerprintGestureController.onGesture(FINGERPRINT_GESTURE_SWIPE_DOWN);
- verifyZeroInteractions(mMockFingerprintGestureCallback);
+ verifyNoMoreInteractions(mMockFingerprintGestureCallback);
}
@Test
@@ -159,7 +159,7 @@ public class FingerprintGestureControllerTest {
mMockFingerprintGestureCallback);
mFingerprintGestureController.onGesture(FINGERPRINT_GESTURE_SWIPE_DOWN);
assertFalse(messageCapturingHandler.hasMessages());
- verifyZeroInteractions(mMockFingerprintGestureCallback);
+ verifyNoMoreInteractions(mMockFingerprintGestureCallback);
messageCapturingHandler.removeAllMessages();
}
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/HearingDevicePhoneCallNotificationControllerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/HearingDevicePhoneCallNotificationControllerTest.java
index 63c572af37b2..3565244d90b3 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/HearingDevicePhoneCallNotificationControllerTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/HearingDevicePhoneCallNotificationControllerTest.java
@@ -36,6 +36,8 @@ import android.content.Context;
import android.media.AudioDeviceInfo;
import android.media.AudioDevicePort;
import android.media.AudioManager;
+import android.platform.test.annotations.EnableFlags;
+import android.platform.test.flag.junit.SetFlagsRule;
import android.telephony.TelephonyCallback;
import android.telephony.TelephonyManager;
@@ -67,6 +69,8 @@ import java.util.concurrent.Executor;
public class HearingDevicePhoneCallNotificationControllerTest {
@Rule
public MockitoRule mockito = MockitoJUnit.rule();
+ @Rule
+ public SetFlagsRule mSetFlagsRule = new SetFlagsRule();
private static final String TEST_ADDRESS = "55:66:77:88:99:AA";
@@ -118,6 +122,7 @@ public class HearingDevicePhoneCallNotificationControllerTest {
AudioManager.DEVICE_OUT_BLE_HEADSET);
when(mAudioManager.getDevices(AudioManager.GET_DEVICES_INPUTS)).thenReturn(
new AudioDeviceInfo[]{hapDeviceInfo});
+ when(mAudioManager.getCommunicationDevice()).thenReturn(hapDeviceInfo);
when(mAudioManager.getAvailableCommunicationDevices()).thenReturn(List.of(hapDeviceInfo));
mTestCallStateListener.onCallStateChanged(TelephonyManager.CALL_STATE_OFFHOOK);
@@ -132,6 +137,7 @@ public class HearingDevicePhoneCallNotificationControllerTest {
AudioManager.DEVICE_OUT_BLUETOOTH_A2DP);
when(mAudioManager.getDevices(AudioManager.GET_DEVICES_INPUTS)).thenReturn(
new AudioDeviceInfo[]{a2dpDeviceInfo});
+ when(mAudioManager.getCommunicationDevice()).thenReturn(a2dpDeviceInfo);
when(mAudioManager.getAvailableCommunicationDevices()).thenReturn(List.of(a2dpDeviceInfo));
mTestCallStateListener.onCallStateChanged(TelephonyManager.CALL_STATE_OFFHOOK);
@@ -146,6 +152,7 @@ public class HearingDevicePhoneCallNotificationControllerTest {
AudioManager.DEVICE_OUT_BLE_HEADSET);
when(mAudioManager.getDevices(AudioManager.GET_DEVICES_INPUTS)).thenReturn(
new AudioDeviceInfo[]{hapDeviceInfo});
+ when(mAudioManager.getCommunicationDevice()).thenReturn(hapDeviceInfo);
when(mAudioManager.getAvailableCommunicationDevices()).thenReturn(List.of(hapDeviceInfo));
mTestCallStateListener.onCallStateChanged(TelephonyManager.CALL_STATE_OFFHOOK);
@@ -155,6 +162,51 @@ public class HearingDevicePhoneCallNotificationControllerTest {
eq(SystemMessageProto.SystemMessage.NOTE_HEARING_DEVICE_INPUT_SWITCH));
}
+ @Test
+ @EnableFlags(Flags.FLAG_HEARING_INPUT_CHANGE_WHEN_COMM_DEVICE)
+ public void onCallStateChanged_nonHearingDevice_offHookThenIdle_callAddAndRemoveListener() {
+ final ArgumentCaptor<AudioManager.OnCommunicationDeviceChangedListener> listenerCaptor =
+ ArgumentCaptor.forClass(AudioManager.OnCommunicationDeviceChangedListener.class);
+ AudioDeviceInfo a2dpDeviceInfo = createAudioDeviceInfo(TEST_ADDRESS,
+ AudioManager.DEVICE_OUT_BLUETOOTH_A2DP);
+ when(mAudioManager.getDevices(AudioManager.GET_DEVICES_INPUTS)).thenReturn(
+ new AudioDeviceInfo[]{a2dpDeviceInfo});
+ when(mAudioManager.getCommunicationDevice()).thenReturn(a2dpDeviceInfo);
+
+ mTestCallStateListener.onCallStateChanged(TelephonyManager.CALL_STATE_OFFHOOK);
+ mTestCallStateListener.onCallStateChanged(TelephonyManager.CALL_STATE_IDLE);
+
+ verify(mAudioManager).addOnCommunicationDeviceChangedListener(any(Executor.class),
+ listenerCaptor.capture());
+ verify(mAudioManager).removeOnCommunicationDeviceChangedListener(
+ eq(listenerCaptor.getValue()));
+ }
+
+
+ @Test
+ @EnableFlags(Flags.FLAG_HEARING_INPUT_CHANGE_WHEN_COMM_DEVICE)
+ public void onCallStateChanged_hearingDeviceFromCommunicationDeviceChanged_showNotification() {
+ final ArgumentCaptor<AudioManager.OnCommunicationDeviceChangedListener> listenerCaptor =
+ ArgumentCaptor.forClass(AudioManager.OnCommunicationDeviceChangedListener.class);
+ AudioDeviceInfo hapDeviceInfo = createAudioDeviceInfo(TEST_ADDRESS,
+ AudioManager.DEVICE_OUT_BLE_HEADSET);
+ AudioDeviceInfo a2dpDeviceInfo = createAudioDeviceInfo(TEST_ADDRESS,
+ AudioManager.DEVICE_OUT_BLUETOOTH_A2DP);
+ when(mAudioManager.getDevices(AudioManager.GET_DEVICES_INPUTS)).thenReturn(
+ new AudioDeviceInfo[]{a2dpDeviceInfo});
+ when(mAudioManager.getCommunicationDevice()).thenReturn(a2dpDeviceInfo);
+
+ mTestCallStateListener.onCallStateChanged(TelephonyManager.CALL_STATE_OFFHOOK);
+ verify(mAudioManager).addOnCommunicationDeviceChangedListener(any(Executor.class),
+ listenerCaptor.capture());
+ when(mAudioManager.getDevices(AudioManager.GET_DEVICES_INPUTS)).thenReturn(
+ new AudioDeviceInfo[]{hapDeviceInfo});
+ listenerCaptor.getValue().onCommunicationDeviceChanged(hapDeviceInfo);
+
+ verify(mNotificationManager).notify(
+ eq(SystemMessageProto.SystemMessage.NOTE_HEARING_DEVICE_INPUT_SWITCH), any());
+ }
+
private AudioDeviceInfo createAudioDeviceInfo(String address, int type) {
AudioDevicePort audioDevicePort = mock(AudioDevicePort.class);
doReturn(type).when(audioDevicePort).type();
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/KeyEventDispatcherTest.java b/services/tests/servicestests/src/com/android/server/accessibility/KeyEventDispatcherTest.java
index c3256cad0fd8..186f7425b189 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/KeyEventDispatcherTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/KeyEventDispatcherTest.java
@@ -27,7 +27,7 @@ import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.verifyZeroInteractions;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
import static org.mockito.Mockito.when;
import static org.mockito.hamcrest.MockitoHamcrest.argThat;
@@ -159,7 +159,7 @@ public class KeyEventDispatcherTest {
mFilter1SequenceCaptor.getValue());
assertTrue(mInputEventsHandler.hasMessages(SEND_FRAMEWORK_KEY_EVENT));
- verifyZeroInteractions(mMockPowerManagerService);
+ verifyNoMoreInteractions(mMockPowerManagerService);
assertFalse(isTimeoutPending(mMessageCapturingHandler));
}
@@ -189,7 +189,7 @@ public class KeyEventDispatcherTest {
mFilter2SequenceCaptor.getValue());
assertTrue(mInputEventsHandler.hasMessages(SEND_FRAMEWORK_KEY_EVENT));
- verifyZeroInteractions(mMockPowerManagerService);
+ verifyNoMoreInteractions(mMockPowerManagerService);
assertFalse(isTimeoutPending(mMessageCapturingHandler));
}
@@ -261,7 +261,7 @@ public class KeyEventDispatcherTest {
mKeyEventDispatcher.setOnKeyEventResult(mKeyEventFilter2, false,
mFilter2SequenceCaptor.getValue());
assertTrue(mInputEventsHandler.hasMessages(SEND_FRAMEWORK_KEY_EVENT));
- verifyZeroInteractions(mMockPowerManagerService);
+ verifyNoMoreInteractions(mMockPowerManagerService);
assertFalse(isTimeoutPending(mMessageCapturingHandler));
}
@@ -278,7 +278,7 @@ public class KeyEventDispatcherTest {
mKeyEventDispatcher.handleMessage(getTimedMessage(mMessageCapturingHandler, 0));
assertTrue(mInputEventsHandler.hasMessages(SEND_FRAMEWORK_KEY_EVENT));
- verifyZeroInteractions(mMockPowerManagerService);
+ verifyNoMoreInteractions(mMockPowerManagerService);
}
@Test
@@ -293,7 +293,7 @@ public class KeyEventDispatcherTest {
mKeyEventDispatcher.handleMessage(getTimedMessage(mMessageCapturingHandler, 0));
assertTrue(mInputEventsHandler.hasMessages(SEND_FRAMEWORK_KEY_EVENT));
- verifyZeroInteractions(mMockPowerManagerService);
+ verifyNoMoreInteractions(mMockPowerManagerService);
}
@Test
@@ -327,7 +327,7 @@ public class KeyEventDispatcherTest {
mFilter1SequenceCaptor.getValue());
assertFalse(mInputEventsHandler.hasMessages(SEND_FRAMEWORK_KEY_EVENT));
- verifyZeroInteractions(mMockPowerManagerService);
+ verifyNoMoreInteractions(mMockPowerManagerService);
}
@Test
@@ -344,7 +344,7 @@ public class KeyEventDispatcherTest {
mKeyEventDispatcher.handleMessage(getTimedMessage(mMessageCapturingHandler, 0));
assertFalse(mInputEventsHandler.hasMessages(SEND_FRAMEWORK_KEY_EVENT));
- verifyZeroInteractions(mMockPowerManagerService);
+ verifyNoMoreInteractions(mMockPowerManagerService);
}
@Test
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/MotionEventInjectorTest.java b/services/tests/servicestests/src/com/android/server/accessibility/MotionEventInjectorTest.java
index 9b8e619c964d..367f2d143acc 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/MotionEventInjectorTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/MotionEventInjectorTest.java
@@ -37,7 +37,6 @@ import static org.mockito.Mockito.reset;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoMoreInteractions;
-import static org.mockito.Mockito.verifyZeroInteractions;
import static org.mockito.hamcrest.MockitoHamcrest.argThat;
import android.accessibilityservice.GestureDescription.GestureStep;
@@ -223,7 +222,7 @@ public class MotionEventInjectorTest {
verifyNoMoreInteractions(next);
reset(next);
- verifyZeroInteractions(mServiceInterface);
+ verifyNoMoreInteractions(mServiceInterface);
mMessageCapturingHandler.sendOneMessage(); // Send a motion event
verify(next).onMotionEvent(argThat(allOf(mIsLineEnd, hasRightDownTime)),
diff --git a/services/tests/servicestests/src/com/android/server/attention/AttentionManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/attention/AttentionManagerServiceTest.java
index 3475c8f5444d..20a95e90b668 100644
--- a/services/tests/servicestests/src/com/android/server/attention/AttentionManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/attention/AttentionManagerServiceTest.java
@@ -34,7 +34,6 @@ import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoMoreInteractions;
-import static org.mockito.Mockito.verifyZeroInteractions;
import android.attention.AttentionManagerInternal.AttentionCallbackInternal;
import android.attention.AttentionManagerInternal.ProximityUpdateCallbackInternal;
@@ -196,7 +195,7 @@ public class AttentionManagerServiceTest {
@Test
public void testUnregisterProximityUpdates_noCrashWhenNoCallbackIsRegistered() {
mSpyAttentionManager.onStopProximityUpdates(mMockProximityUpdateCallbackInternal);
- verifyZeroInteractions(mMockProximityUpdateCallbackInternal);
+ verifyNoMoreInteractions(mMockProximityUpdateCallbackInternal);
}
@Test
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/FaceDetectClientTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/FaceDetectClientTest.java
index 7a770338a34b..f4e87177e072 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/FaceDetectClientTest.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/FaceDetectClientTest.java
@@ -24,7 +24,7 @@ import static org.mockito.Mockito.inOrder;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.same;
import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.verifyZeroInteractions;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
import static org.mockito.Mockito.when;
import android.hardware.biometrics.BiometricFaceConstants;
@@ -172,7 +172,7 @@ public class FaceDetectClientTest {
client.onInteractionDetected();
client.stopHalOperation();
- verifyZeroInteractions(mVibrator);
+ verifyNoMoreInteractions(mVibrator);
}
private FaceDetectClient createClient() throws RemoteException {
diff --git a/services/tests/servicestests/src/com/android/server/contentcapture/ContentCaptureManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/contentcapture/ContentCaptureManagerServiceTest.java
index b445226be60f..4fa75b9823e0 100644
--- a/services/tests/servicestests/src/com/android/server/contentcapture/ContentCaptureManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/contentcapture/ContentCaptureManagerServiceTest.java
@@ -23,7 +23,7 @@ import static org.mockito.ArgumentMatchers.anyLong;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.verifyZeroInteractions;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
import static org.mockito.Mockito.when;
import android.annotation.NonNull;
@@ -132,8 +132,8 @@ public class ContentCaptureManagerServiceTest {
assertThat(mContentProtectionAllowlistManagersCreated).isEqualTo(0);
assertThat(mContentProtectionConsentManagersCreated).isEqualTo(0);
assertThat(mContentProtectionServiceInfosCreated).isEqualTo(0);
- verifyZeroInteractions(mMockContentProtectionAllowlistManager);
- verifyZeroInteractions(mMockContentProtectionConsentManager);
+ verifyNoMoreInteractions(mMockContentProtectionAllowlistManager);
+ verifyNoMoreInteractions(mMockContentProtectionConsentManager);
}
@Test
@@ -147,7 +147,7 @@ public class ContentCaptureManagerServiceTest {
assertThat(mContentProtectionServiceInfosCreated).isEqualTo(0);
verify(mMockContentProtectionAllowlistManager).start(anyLong());
verify(mMockContentProtectionAllowlistManager, never()).stop();
- verifyZeroInteractions(mMockContentProtectionConsentManager);
+ verifyNoMoreInteractions(mMockContentProtectionConsentManager);
}
@Test
@@ -157,8 +157,8 @@ public class ContentCaptureManagerServiceTest {
assertThat(mContentProtectionAllowlistManagersCreated).isEqualTo(0);
assertThat(mContentProtectionConsentManagersCreated).isEqualTo(0);
assertThat(mContentProtectionServiceInfosCreated).isEqualTo(0);
- verifyZeroInteractions(mMockContentProtectionAllowlistManager);
- verifyZeroInteractions(mMockContentProtectionConsentManager);
+ verifyNoMoreInteractions(mMockContentProtectionAllowlistManager);
+ verifyNoMoreInteractions(mMockContentProtectionConsentManager);
}
@Test
@@ -172,7 +172,7 @@ public class ContentCaptureManagerServiceTest {
assertThat(mContentProtectionServiceInfosCreated).isEqualTo(0);
verify(mMockContentProtectionAllowlistManager).start(anyLong());
verify(mMockContentProtectionAllowlistManager, never()).stop();
- verifyZeroInteractions(mMockContentProtectionConsentManager);
+ verifyNoMoreInteractions(mMockContentProtectionConsentManager);
}
@Test
@@ -187,7 +187,7 @@ public class ContentCaptureManagerServiceTest {
assertThat(mContentProtectionServiceInfosCreated).isEqualTo(0);
verify(mMockContentProtectionAllowlistManager).start(anyLong());
verify(mMockContentProtectionAllowlistManager, never()).stop();
- verifyZeroInteractions(mMockContentProtectionConsentManager);
+ verifyNoMoreInteractions(mMockContentProtectionConsentManager);
}
@Test
@@ -203,7 +203,7 @@ public class ContentCaptureManagerServiceTest {
assertThat(mContentProtectionServiceInfosCreated).isEqualTo(0);
verify(mMockContentProtectionAllowlistManager).start(anyLong());
verify(mMockContentProtectionAllowlistManager).stop();
- verifyZeroInteractions(mMockContentProtectionConsentManager);
+ verifyNoMoreInteractions(mMockContentProtectionConsentManager);
}
@Test
@@ -216,8 +216,8 @@ public class ContentCaptureManagerServiceTest {
assertThat(mContentProtectionAllowlistManagersCreated).isEqualTo(0);
assertThat(mContentProtectionConsentManagersCreated).isEqualTo(0);
assertThat(mContentProtectionServiceInfosCreated).isEqualTo(0);
- verifyZeroInteractions(mMockContentProtectionAllowlistManager);
- verifyZeroInteractions(mMockContentProtectionConsentManager);
+ verifyNoMoreInteractions(mMockContentProtectionAllowlistManager);
+ verifyNoMoreInteractions(mMockContentProtectionConsentManager);
}
@Test
@@ -230,8 +230,8 @@ public class ContentCaptureManagerServiceTest {
assertThat(mContentProtectionAllowlistManagersCreated).isEqualTo(0);
assertThat(mContentProtectionConsentManagersCreated).isEqualTo(0);
assertThat(mContentProtectionServiceInfosCreated).isEqualTo(0);
- verifyZeroInteractions(mMockContentProtectionAllowlistManager);
- verifyZeroInteractions(mMockContentProtectionConsentManager);
+ verifyNoMoreInteractions(mMockContentProtectionAllowlistManager);
+ verifyNoMoreInteractions(mMockContentProtectionConsentManager);
}
@Test
@@ -248,7 +248,7 @@ public class ContentCaptureManagerServiceTest {
assertThat(mContentProtectionServiceInfosCreated).isEqualTo(0);
verify(mMockContentProtectionAllowlistManager).start(anyLong());
verify(mMockContentProtectionAllowlistManager).stop();
- verifyZeroInteractions(mMockContentProtectionConsentManager);
+ verifyNoMoreInteractions(mMockContentProtectionConsentManager);
}
@Test
@@ -265,7 +265,7 @@ public class ContentCaptureManagerServiceTest {
assertThat(mContentProtectionServiceInfosCreated).isEqualTo(0);
verify(mMockContentProtectionAllowlistManager).start(anyLong());
verify(mMockContentProtectionAllowlistManager).stop();
- verifyZeroInteractions(mMockContentProtectionConsentManager);
+ verifyNoMoreInteractions(mMockContentProtectionConsentManager);
}
@Test
@@ -513,7 +513,7 @@ public class ContentCaptureManagerServiceTest {
assertThat(mContentProtectionServiceInfosCreated).isEqualTo(0);
assertThat(mRemoteContentProtectionServicesCreated).isEqualTo(0);
- verifyZeroInteractions(mMockRemoteContentProtectionService);
+ verifyNoMoreInteractions(mMockRemoteContentProtectionService);
}
@Test
@@ -528,7 +528,7 @@ public class ContentCaptureManagerServiceTest {
assertThat(mContentProtectionServiceInfosCreated).isEqualTo(1);
assertThat(mRemoteContentProtectionServicesCreated).isEqualTo(0);
- verifyZeroInteractions(mMockRemoteContentProtectionService);
+ verifyNoMoreInteractions(mMockRemoteContentProtectionService);
}
@Test
diff --git a/services/tests/servicestests/src/com/android/server/contentprotection/ContentProtectionAllowlistManagerTest.java b/services/tests/servicestests/src/com/android/server/contentprotection/ContentProtectionAllowlistManagerTest.java
index 195ab68427b9..9d37b99c5bf4 100644
--- a/services/tests/servicestests/src/com/android/server/contentprotection/ContentProtectionAllowlistManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/contentprotection/ContentProtectionAllowlistManagerTest.java
@@ -24,7 +24,7 @@ import static org.mockito.Mockito.doThrow;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.verifyZeroInteractions;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
import static org.mockito.Mockito.when;
import android.os.Handler;
@@ -98,10 +98,10 @@ public class ContentProtectionAllowlistManagerTest {
@Test
public void constructor() {
assertThat(mHandler.hasMessagesOrCallbacks()).isFalse();
- verifyZeroInteractions(mMockContentCaptureManagerService);
- verifyZeroInteractions(mMockPackageMonitor);
- verifyZeroInteractions(mMockRemoteContentProtectionService);
- verifyZeroInteractions(mMockAllowlistCallback);
+ verifyNoMoreInteractions(mMockContentCaptureManagerService);
+ verifyNoMoreInteractions(mMockPackageMonitor);
+ verifyNoMoreInteractions(mMockRemoteContentProtectionService);
+ verifyNoMoreInteractions(mMockAllowlistCallback);
}
@Test
@@ -110,10 +110,10 @@ public class ContentProtectionAllowlistManagerTest {
mTestLooper.dispatchAll();
assertThat(mHandler.hasMessagesOrCallbacks()).isTrue();
- verifyZeroInteractions(mMockContentCaptureManagerService);
- verifyZeroInteractions(mMockPackageMonitor);
- verifyZeroInteractions(mMockRemoteContentProtectionService);
- verifyZeroInteractions(mMockAllowlistCallback);
+ verifyNoMoreInteractions(mMockContentCaptureManagerService);
+ verifyNoMoreInteractions(mMockPackageMonitor);
+ verifyNoMoreInteractions(mMockRemoteContentProtectionService);
+ verifyNoMoreInteractions(mMockAllowlistCallback);
}
@Test
@@ -126,8 +126,8 @@ public class ContentProtectionAllowlistManagerTest {
verify(mMockContentCaptureManagerService).createRemoteContentProtectionService();
verify(mMockPackageMonitor).register(any(), eq(UserHandle.ALL), eq(mHandler));
verify(mMockPackageMonitor, never()).unregister();
- verifyZeroInteractions(mMockRemoteContentProtectionService);
- verifyZeroInteractions(mMockAllowlistCallback);
+ verifyNoMoreInteractions(mMockRemoteContentProtectionService);
+ verifyNoMoreInteractions(mMockAllowlistCallback);
}
@Test
@@ -142,8 +142,8 @@ public class ContentProtectionAllowlistManagerTest {
verify(mMockContentCaptureManagerService).createRemoteContentProtectionService();
verify(mMockPackageMonitor).register(any(), eq(UserHandle.ALL), eq(mHandler));
verify(mMockPackageMonitor, never()).unregister();
- verifyZeroInteractions(mMockRemoteContentProtectionService);
- verifyZeroInteractions(mMockAllowlistCallback);
+ verifyNoMoreInteractions(mMockRemoteContentProtectionService);
+ verifyNoMoreInteractions(mMockAllowlistCallback);
}
@Test
@@ -153,11 +153,11 @@ public class ContentProtectionAllowlistManagerTest {
mContentProtectionAllowlistManager.stop();
assertThat(mHandler.hasMessagesOrCallbacks()).isFalse();
- verifyZeroInteractions(mMockContentCaptureManagerService);
+ verifyNoMoreInteractions(mMockContentCaptureManagerService);
verify(mMockPackageMonitor, never()).register(any(), any(), any());
verify(mMockPackageMonitor).unregister();
- verifyZeroInteractions(mMockRemoteContentProtectionService);
- verifyZeroInteractions(mMockAllowlistCallback);
+ verifyNoMoreInteractions(mMockRemoteContentProtectionService);
+ verifyNoMoreInteractions(mMockAllowlistCallback);
}
@Test
@@ -169,11 +169,11 @@ public class ContentProtectionAllowlistManagerTest {
mContentProtectionAllowlistManager.stop();
assertThat(mHandler.hasMessagesOrCallbacks()).isFalse();
- verifyZeroInteractions(mMockContentCaptureManagerService);
+ verifyNoMoreInteractions(mMockContentCaptureManagerService);
verify(mMockPackageMonitor, never()).register(any(), any(), any());
verify(mMockPackageMonitor).unregister();
- verifyZeroInteractions(mMockRemoteContentProtectionService);
- verifyZeroInteractions(mMockAllowlistCallback);
+ verifyNoMoreInteractions(mMockRemoteContentProtectionService);
+ verifyNoMoreInteractions(mMockAllowlistCallback);
}
@Test
@@ -188,8 +188,8 @@ public class ContentProtectionAllowlistManagerTest {
verify(mMockContentCaptureManagerService).createRemoteContentProtectionService();
verify(mMockPackageMonitor).register(any(), eq(UserHandle.ALL), eq(mHandler));
verify(mMockPackageMonitor).unregister();
- verifyZeroInteractions(mMockRemoteContentProtectionService);
- verifyZeroInteractions(mMockAllowlistCallback);
+ verifyNoMoreInteractions(mMockRemoteContentProtectionService);
+ verifyNoMoreInteractions(mMockAllowlistCallback);
}
@Test
@@ -205,8 +205,8 @@ public class ContentProtectionAllowlistManagerTest {
assertThat(mHandler.hasMessagesOrCallbacks()).isFalse();
verify(mMockPackageMonitor).register(any(), eq(UserHandle.ALL), eq(mHandler));
verify(mMockPackageMonitor).unregister();
- verifyZeroInteractions(mMockRemoteContentProtectionService);
- verifyZeroInteractions(mMockAllowlistCallback);
+ verifyNoMoreInteractions(mMockRemoteContentProtectionService);
+ verifyNoMoreInteractions(mMockAllowlistCallback);
}
@Test
@@ -223,8 +223,8 @@ public class ContentProtectionAllowlistManagerTest {
assertThat(mHandler.hasMessagesOrCallbacks()).isFalse();
verify(mMockPackageMonitor, times(2)).register(any(), eq(UserHandle.ALL), eq(mHandler));
verify(mMockPackageMonitor).unregister();
- verifyZeroInteractions(mMockRemoteContentProtectionService);
- verifyZeroInteractions(mMockAllowlistCallback);
+ verifyNoMoreInteractions(mMockRemoteContentProtectionService);
+ verifyNoMoreInteractions(mMockAllowlistCallback);
}
@Test
@@ -232,10 +232,10 @@ public class ContentProtectionAllowlistManagerTest {
boolean actual = mContentProtectionAllowlistManager.isAllowed(FIRST_PACKAGE_NAME);
assertThat(actual).isFalse();
- verifyZeroInteractions(mMockContentCaptureManagerService);
- verifyZeroInteractions(mMockPackageMonitor);
- verifyZeroInteractions(mMockRemoteContentProtectionService);
- verifyZeroInteractions(mMockAllowlistCallback);
+ verifyNoMoreInteractions(mMockContentCaptureManagerService);
+ verifyNoMoreInteractions(mMockPackageMonitor);
+ verifyNoMoreInteractions(mMockRemoteContentProtectionService);
+ verifyNoMoreInteractions(mMockAllowlistCallback);
}
@Test
@@ -248,9 +248,9 @@ public class ContentProtectionAllowlistManagerTest {
boolean actual = manager.isAllowed(SECOND_PACKAGE_NAME);
assertThat(actual).isFalse();
- verifyZeroInteractions(mMockContentCaptureManagerService);
- verifyZeroInteractions(mMockPackageMonitor);
- verifyZeroInteractions(mMockRemoteContentProtectionService);
+ verifyNoMoreInteractions(mMockContentCaptureManagerService);
+ verifyNoMoreInteractions(mMockPackageMonitor);
+ verifyNoMoreInteractions(mMockRemoteContentProtectionService);
}
@Test
@@ -263,9 +263,9 @@ public class ContentProtectionAllowlistManagerTest {
boolean actual = manager.isAllowed(FIRST_PACKAGE_NAME);
assertThat(actual).isTrue();
- verifyZeroInteractions(mMockContentCaptureManagerService);
- verifyZeroInteractions(mMockPackageMonitor);
- verifyZeroInteractions(mMockRemoteContentProtectionService);
+ verifyNoMoreInteractions(mMockContentCaptureManagerService);
+ verifyNoMoreInteractions(mMockPackageMonitor);
+ verifyNoMoreInteractions(mMockRemoteContentProtectionService);
}
@Test
@@ -276,8 +276,8 @@ public class ContentProtectionAllowlistManagerTest {
manager.mPackageMonitor.onSomePackagesChanged();
verify(mMockContentCaptureManagerService).createRemoteContentProtectionService();
- verifyZeroInteractions(mMockRemoteContentProtectionService);
- verifyZeroInteractions(mMockAllowlistCallback);
+ verifyNoMoreInteractions(mMockRemoteContentProtectionService);
+ verifyNoMoreInteractions(mMockAllowlistCallback);
}
@Test
@@ -291,7 +291,7 @@ public class ContentProtectionAllowlistManagerTest {
verify(mMockRemoteContentProtectionService)
.onUpdateAllowlistRequest(mMockAllowlistCallback);
- verifyZeroInteractions(mMockAllowlistCallback);
+ verifyNoMoreInteractions(mMockAllowlistCallback);
}
@Test
@@ -309,7 +309,7 @@ public class ContentProtectionAllowlistManagerTest {
// Does not rethrow
verify(mMockRemoteContentProtectionService)
.onUpdateAllowlistRequest(mMockAllowlistCallback);
- verifyZeroInteractions(mMockAllowlistCallback);
+ verifyNoMoreInteractions(mMockAllowlistCallback);
}
@Test
@@ -321,8 +321,8 @@ public class ContentProtectionAllowlistManagerTest {
manager.mPackageMonitor.onSomePackagesChanged();
verify(mMockContentCaptureManagerService, times(2)).createRemoteContentProtectionService();
- verifyZeroInteractions(mMockRemoteContentProtectionService);
- verifyZeroInteractions(mMockAllowlistCallback);
+ verifyNoMoreInteractions(mMockRemoteContentProtectionService);
+ verifyNoMoreInteractions(mMockAllowlistCallback);
}
@Test
@@ -338,7 +338,7 @@ public class ContentProtectionAllowlistManagerTest {
verify(mMockContentCaptureManagerService).createRemoteContentProtectionService();
verify(mMockRemoteContentProtectionService)
.onUpdateAllowlistRequest(mMockAllowlistCallback);
- verifyZeroInteractions(mMockAllowlistCallback);
+ verifyNoMoreInteractions(mMockAllowlistCallback);
}
@Test
@@ -355,7 +355,7 @@ public class ContentProtectionAllowlistManagerTest {
verify(mMockContentCaptureManagerService, times(2)).createRemoteContentProtectionService();
verify(mMockRemoteContentProtectionService, times(2))
.onUpdateAllowlistRequest(mMockAllowlistCallback);
- verifyZeroInteractions(mMockAllowlistCallback);
+ verifyNoMoreInteractions(mMockAllowlistCallback);
}
@Test
diff --git a/services/tests/servicestests/src/com/android/server/contentprotection/ContentProtectionConsentManagerTest.java b/services/tests/servicestests/src/com/android/server/contentprotection/ContentProtectionConsentManagerTest.java
index b012aaaed3bf..cd36a1889d2f 100644
--- a/services/tests/servicestests/src/com/android/server/contentprotection/ContentProtectionConsentManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/contentprotection/ContentProtectionConsentManagerTest.java
@@ -27,7 +27,7 @@ import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.verifyZeroInteractions;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
import static org.mockito.Mockito.when;
import android.app.admin.DevicePolicyCache;
@@ -112,8 +112,8 @@ public class ContentProtectionConsentManagerTest {
boolean actual = manager.isConsentGranted(TEST_USER_ID);
assertThat(actual).isFalse();
- verifyZeroInteractions(mMockDevicePolicyManagerInternal);
- verifyZeroInteractions(mMockDevicePolicyCache);
+ verifyNoMoreInteractions(mMockDevicePolicyManagerInternal);
+ verifyNoMoreInteractions(mMockDevicePolicyCache);
}
@Test
@@ -125,8 +125,8 @@ public class ContentProtectionConsentManagerTest {
boolean actual = manager.isConsentGranted(TEST_USER_ID);
assertThat(actual).isFalse();
- verifyZeroInteractions(mMockDevicePolicyManagerInternal);
- verifyZeroInteractions(mMockDevicePolicyCache);
+ verifyNoMoreInteractions(mMockDevicePolicyManagerInternal);
+ verifyNoMoreInteractions(mMockDevicePolicyCache);
}
@Test
@@ -138,8 +138,8 @@ public class ContentProtectionConsentManagerTest {
boolean actual = manager.isConsentGranted(TEST_USER_ID);
assertThat(actual).isFalse();
- verifyZeroInteractions(mMockDevicePolicyManagerInternal);
- verifyZeroInteractions(mMockDevicePolicyCache);
+ verifyNoMoreInteractions(mMockDevicePolicyManagerInternal);
+ verifyNoMoreInteractions(mMockDevicePolicyCache);
}
@Test
@@ -152,7 +152,7 @@ public class ContentProtectionConsentManagerTest {
assertThat(actual).isTrue();
verify(mMockDevicePolicyManagerInternal).isUserOrganizationManaged(TEST_USER_ID);
- verifyZeroInteractions(mMockDevicePolicyCache);
+ verifyNoMoreInteractions(mMockDevicePolicyCache);
}
@Test
@@ -166,7 +166,7 @@ public class ContentProtectionConsentManagerTest {
boolean actual = manager.isConsentGranted(TEST_USER_ID);
assertThat(actual).isFalse();
- verifyZeroInteractions(mMockDevicePolicyCache);
+ verifyNoMoreInteractions(mMockDevicePolicyCache);
}
@Test
@@ -179,7 +179,7 @@ public class ContentProtectionConsentManagerTest {
assertThat(actual).isFalse();
verify(mMockDevicePolicyManagerInternal).isUserOrganizationManaged(TEST_USER_ID);
- verifyZeroInteractions(mMockDevicePolicyCache);
+ verifyNoMoreInteractions(mMockDevicePolicyCache);
}
@Test
@@ -192,7 +192,7 @@ public class ContentProtectionConsentManagerTest {
assertThat(actual).isTrue();
verify(mMockDevicePolicyManagerInternal).isUserOrganizationManaged(TEST_USER_ID);
- verifyZeroInteractions(mMockDevicePolicyCache);
+ verifyNoMoreInteractions(mMockDevicePolicyCache);
}
@Test
@@ -289,8 +289,8 @@ public class ContentProtectionConsentManagerTest {
boolean actual = manager.isConsentGranted(TEST_USER_ID);
assertThat(actual).isFalse();
- verifyZeroInteractions(mMockDevicePolicyManagerInternal);
- verifyZeroInteractions(mMockDevicePolicyCache);
+ verifyNoMoreInteractions(mMockDevicePolicyManagerInternal);
+ verifyNoMoreInteractions(mMockDevicePolicyCache);
}
@Test
@@ -302,8 +302,8 @@ public class ContentProtectionConsentManagerTest {
boolean actual = manager.isConsentGranted(TEST_USER_ID);
assertThat(actual).isFalse();
- verifyZeroInteractions(mMockDevicePolicyManagerInternal);
- verifyZeroInteractions(mMockDevicePolicyCache);
+ verifyNoMoreInteractions(mMockDevicePolicyManagerInternal);
+ verifyNoMoreInteractions(mMockDevicePolicyCache);
}
@Test
@@ -316,7 +316,7 @@ public class ContentProtectionConsentManagerTest {
assertThat(actual).isTrue();
verify(mMockDevicePolicyManagerInternal).isUserOrganizationManaged(TEST_USER_ID);
- verifyZeroInteractions(mMockDevicePolicyCache);
+ verifyNoMoreInteractions(mMockDevicePolicyCache);
}
@Test
@@ -339,7 +339,7 @@ public class ContentProtectionConsentManagerTest {
assertThat(thirdActual).isTrue();
verify(mMockDevicePolicyManagerInternal).isUserOrganizationManaged(TEST_USER_ID);
- verifyZeroInteractions(mMockDevicePolicyCache);
+ verifyNoMoreInteractions(mMockDevicePolicyCache);
}
@Test
@@ -362,7 +362,7 @@ public class ContentProtectionConsentManagerTest {
assertThat(thirdActual).isTrue();
verify(mMockDevicePolicyManagerInternal).isUserOrganizationManaged(TEST_USER_ID);
- verifyZeroInteractions(mMockDevicePolicyCache);
+ verifyNoMoreInteractions(mMockDevicePolicyCache);
}
@Test
@@ -385,7 +385,7 @@ public class ContentProtectionConsentManagerTest {
assertThat(thirdActual).isTrue();
verify(mMockDevicePolicyManagerInternal).isUserOrganizationManaged(TEST_USER_ID);
- verifyZeroInteractions(mMockDevicePolicyCache);
+ verifyNoMoreInteractions(mMockDevicePolicyCache);
}
@Test
@@ -408,7 +408,7 @@ public class ContentProtectionConsentManagerTest {
assertThat(thirdActual).isTrue();
verify(mMockDevicePolicyManagerInternal, times(3)).isUserOrganizationManaged(TEST_USER_ID);
- verifyZeroInteractions(mMockDevicePolicyCache);
+ verifyNoMoreInteractions(mMockDevicePolicyCache);
}
private void putGlobalSettings(String key, int value) {
diff --git a/services/tests/servicestests/src/com/android/server/contentprotection/RemoteContentProtectionServiceTest.java b/services/tests/servicestests/src/com/android/server/contentprotection/RemoteContentProtectionServiceTest.java
index 6a7e2865fb32..563a6799e9e5 100644
--- a/services/tests/servicestests/src/com/android/server/contentprotection/RemoteContentProtectionServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/contentprotection/RemoteContentProtectionServiceTest.java
@@ -20,7 +20,7 @@ import static com.google.common.truth.Truth.assertThat;
import static org.junit.Assert.fail;
import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.verifyZeroInteractions;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
import static org.mockito.Mockito.when;
import android.annotation.NonNull;
@@ -87,7 +87,7 @@ public class RemoteContentProtectionServiceTest {
@Test
public void doesNotAutoConnect() {
assertThat(mConnectCallCount).isEqualTo(0);
- verifyZeroInteractions(mMockContentProtectionService);
+ verifyNoMoreInteractions(mMockContentProtectionService);
}
@Test
@@ -124,7 +124,7 @@ public class RemoteContentProtectionServiceTest {
mRemoteContentProtectionService.onServiceConnectionStatusChanged(
mMockContentProtectionService, /* isConnected= */ true);
- verifyZeroInteractions(mMockContentProtectionService);
+ verifyNoMoreInteractions(mMockContentProtectionService);
assertThat(mConnectCallCount).isEqualTo(0);
}
@@ -133,7 +133,7 @@ public class RemoteContentProtectionServiceTest {
mRemoteContentProtectionService.onServiceConnectionStatusChanged(
mMockContentProtectionService, /* isConnected= */ false);
- verifyZeroInteractions(mMockContentProtectionService);
+ verifyNoMoreInteractions(mMockContentProtectionService);
assertThat(mConnectCallCount).isEqualTo(0);
}
diff --git a/services/tests/servicestests/src/com/android/server/credentials/ProviderRegistryGetSessionTest.java b/services/tests/servicestests/src/com/android/server/credentials/ProviderRegistryGetSessionTest.java
index 0f3f27aa2896..9e98af32d6e5 100644
--- a/services/tests/servicestests/src/com/android/server/credentials/ProviderRegistryGetSessionTest.java
+++ b/services/tests/servicestests/src/com/android/server/credentials/ProviderRegistryGetSessionTest.java
@@ -21,7 +21,7 @@ import static com.google.common.truth.Truth.assertThat;
import static org.mockito.ArgumentMatchers.anySet;
import static org.mockito.Mockito.anyString;
import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.verifyZeroInteractions;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
import static org.mockito.Mockito.when;
import static org.testng.Assert.assertThrows;
@@ -207,7 +207,7 @@ public class ProviderRegistryGetSessionTest {
ProviderRegistryGetSession.CREDENTIAL_ENTRY_KEY,
"unsupportedKey", providerPendingIntentResponse);
- verifyZeroInteractions(mGetRequestSession);
+ verifyNoMoreInteractions(mGetRequestSession);
}
@Test
@@ -216,7 +216,7 @@ public class ProviderRegistryGetSessionTest {
ProviderRegistryGetSession.CREDENTIAL_ENTRY_KEY,
ProviderRegistryGetSession.CREDENTIAL_ENTRY_KEY, null);
- verifyZeroInteractions(mGetRequestSession);
+ verifyNoMoreInteractions(mGetRequestSession);
}
@Test
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
index 01bcc2584fe1..c50c62323212 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
@@ -88,7 +88,6 @@ import static org.mockito.Mockito.timeout;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoMoreInteractions;
-import static org.mockito.Mockito.verifyZeroInteractions;
import static org.mockito.Mockito.when;
import static org.mockito.hamcrest.MockitoHamcrest.argThat;
import static org.testng.Assert.assertThrows;
@@ -5305,7 +5304,7 @@ public class DevicePolicyManagerTest extends DpmTestBase {
// both the user restriction and the policy were set by the PO.
verify(getServices().userManagerInternal).removeUserEvenWhenDisallowed(
MANAGED_PROFILE_USER_ID);
- verifyZeroInteractions(getServices().recoverySystem);
+ verifyNoMoreInteractions(getServices().recoverySystem);
}
@Test
@@ -5339,7 +5338,7 @@ public class DevicePolicyManagerTest extends DpmTestBase {
// not wiped.
verify(getServices().userManagerInternal, never())
.removeUserEvenWhenDisallowed(anyInt());
- verifyZeroInteractions(getServices().recoverySystem);
+ verifyNoMoreInteractions(getServices().recoverySystem);
}
@Test
@@ -5380,7 +5379,7 @@ public class DevicePolicyManagerTest extends DpmTestBase {
dpm.reportFailedPasswordAttempt(UserHandle.USER_SYSTEM);
// DISALLOW_FACTORY_RESET was set by the system, not the DO, so the device is not wiped.
- verifyZeroInteractions(getServices().recoverySystem);
+ verifyNoMoreInteractions(getServices().recoverySystem);
verify(getServices().userManagerInternal, never())
.removeUserEvenWhenDisallowed(anyInt());
}
@@ -7535,7 +7534,7 @@ public class DevicePolicyManagerTest extends DpmTestBase {
verify(getServices().notificationManager, never())
.notify(anyInt(), any(Notification.class));
// Apps shouldn't be suspended.
- verifyZeroInteractions(getServices().ipackageManager);
+ verifyNoMoreInteractions(getServices().ipackageManager);
clearInvocations(getServices().alarmManager);
setUserUnlocked(CALLER_USER_HANDLE, false);
@@ -7548,7 +7547,7 @@ public class DevicePolicyManagerTest extends DpmTestBase {
verify(getServices().notificationManager, never())
.notify(anyInt(), any(Notification.class));
// Apps shouldn't be suspended.
- verifyZeroInteractions(getServices().ipackageManager);
+ verifyNoMoreInteractions(getServices().ipackageManager);
clearInvocations(getServices().alarmManager);
// Pretend the alarm went off.
@@ -7561,7 +7560,7 @@ public class DevicePolicyManagerTest extends DpmTestBase {
verify(getServices().notificationManager, times(1))
.notifyAsUser(any(), anyInt(), any(), any());
// Apps shouldn't be suspended yet.
- verifyZeroInteractions(getServices().ipackageManager);
+ verifyNoMoreInteractions(getServices().ipackageManager);
clearInvocations(getServices().alarmManager);
clearInvocations(getServices().notificationManager);
@@ -7570,7 +7569,7 @@ public class DevicePolicyManagerTest extends DpmTestBase {
sendBroadcastWithUser(dpms, ACTION_PROFILE_OFF_DEADLINE, CALLER_USER_HANDLE);
// Verify the alarm was not set.
- verifyZeroInteractions(getServices().alarmManager);
+ verifyNoMoreInteractions(getServices().alarmManager);
// Now the user should see a notification about suspended apps.
verify(getServices().notificationManager, times(1))
.notifyAsUser(any(), anyInt(), any(), any());
@@ -8754,7 +8753,7 @@ public class DevicePolicyManagerTest extends DpmTestBase {
sendBroadcastWithUser(dpms, Intent.ACTION_MANAGED_PROFILE_REMOVED, CALLER_USER_HANDLE);
// Verify that EuiccManager was not called to delete the subscription.
- verifyZeroInteractions(getServices().euiccManager);
+ verifyNoMoreInteractions(getServices().euiccManager);
}
private void setupVpnAuthorization(String userVpnPackage, int userVpnUid) {
diff --git a/services/tests/servicestests/src/com/android/server/locales/SystemAppUpdateTrackerTest.java b/services/tests/servicestests/src/com/android/server/locales/SystemAppUpdateTrackerTest.java
index e20f1e7065d4..a39f07105eab 100644
--- a/services/tests/servicestests/src/com/android/server/locales/SystemAppUpdateTrackerTest.java
+++ b/services/tests/servicestests/src/com/android/server/locales/SystemAppUpdateTrackerTest.java
@@ -31,7 +31,7 @@ import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.verifyZeroInteractions;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
import android.app.ActivityManagerInternal;
import android.content.Context;
@@ -226,7 +226,7 @@ public class SystemAppUpdateTrackerTest {
assertTrue(!mSystemAppUpdateTracker.getUpdatedApps().contains(DEFAULT_PACKAGE_NAME_2));
// getApplicationLocales should be never be invoked if not a system app.
- verifyZeroInteractions(mMockActivityTaskManager);
+ verifyNoMoreInteractions(mMockActivityTaskManager);
// Broadcast should be never sent if not a system app.
verify(mMockContext, never()).sendBroadcastAsUser(any(), any());
// It shouldn't write to the file if not a system app.
@@ -244,7 +244,7 @@ public class SystemAppUpdateTrackerTest {
Binder.getCallingUid());
// getApplicationLocales should be never be invoked if not installer is not present.
- verifyZeroInteractions(mMockActivityTaskManager);
+ verifyNoMoreInteractions(mMockActivityTaskManager);
// Broadcast should be never sent if installer is not present.
verify(mMockContext, never()).sendBroadcastAsUser(any(), any());
// It shouldn't write to file if no installer present.
diff --git a/services/tests/servicestests/src/com/android/server/media/projection/MediaProjectionManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/media/projection/MediaProjectionManagerServiceTest.java
index a58a9cd2a28f..4a05ea68daec 100644
--- a/services/tests/servicestests/src/com/android/server/media/projection/MediaProjectionManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/media/projection/MediaProjectionManagerServiceTest.java
@@ -48,7 +48,7 @@ import static org.mockito.Mockito.never;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.verifyZeroInteractions;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
import static org.mockito.Mockito.when;
import static org.testng.Assert.assertThrows;
@@ -488,7 +488,7 @@ public class MediaProjectionManagerServiceTest {
projection.stop(StopReason.STOP_UNKNOWN);
- verifyZeroInteractions(mMediaProjectionMetricsLogger);
+ verifyNoMoreInteractions(mMediaProjectionMetricsLogger);
}
@Test
diff --git a/services/tests/servicestests/src/com/android/server/pm/dex/DynamicCodeLoggerTests.java b/services/tests/servicestests/src/com/android/server/pm/dex/DynamicCodeLoggerTests.java
index d55f96782084..2ed27048c288 100644
--- a/services/tests/servicestests/src/com/android/server/pm/dex/DynamicCodeLoggerTests.java
+++ b/services/tests/servicestests/src/com/android/server/pm/dex/DynamicCodeLoggerTests.java
@@ -23,7 +23,7 @@ import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.doThrow;
import static org.mockito.Mockito.reset;
import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.verifyZeroInteractions;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
import static org.mockito.Mockito.when;
import android.content.pm.ApplicationInfo;
@@ -273,7 +273,7 @@ public class DynamicCodeLoggerTests {
assertThat(mMessagesForUid).isEmpty();
assertThat(mWriteTriggered).isFalse();
- verifyZeroInteractions(mPM);
+ verifyNoMoreInteractions(mPM);
}
@Test
diff --git a/services/tests/timetests/src/com/android/server/timedetector/GnssTimeUpdateServiceTest.java b/services/tests/timetests/src/com/android/server/timedetector/GnssTimeUpdateServiceTest.java
index c8afb78bc12f..630a7e47fa48 100644
--- a/services/tests/timetests/src/com/android/server/timedetector/GnssTimeUpdateServiceTest.java
+++ b/services/tests/timetests/src/com/android/server/timedetector/GnssTimeUpdateServiceTest.java
@@ -23,7 +23,7 @@ import static org.mockito.Mockito.anyLong;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.reset;
import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.verifyZeroInteractions;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
import static org.mockito.Mockito.when;
import android.app.AlarmManager;
@@ -126,7 +126,7 @@ public final class GnssTimeUpdateServiceTest {
locationListener.onLocationChanged(location);
verify(mMockLocationManager).removeUpdates(locationListener);
- verifyZeroInteractions(mMockTimeDetectorInternal);
+ verifyNoMoreInteractions(mMockTimeDetectorInternal);
verify(mMockAlarmManager).set(
eq(AlarmManager.ELAPSED_REALTIME_WAKEUP),
anyLong(),
@@ -150,7 +150,7 @@ public final class GnssTimeUpdateServiceTest {
// Verify the service returned to location listening.
verify(mMockLocationManager).requestLocationUpdates(any(), any(), any(), any());
- verifyZeroInteractions(mMockAlarmManager, mMockTimeDetectorInternal);
+ verifyNoMoreInteractions(mMockAlarmManager, mMockTimeDetectorInternal);
}
// Tests what happens when a call is made to startGnssListeningInternal() when service is
@@ -172,7 +172,7 @@ public final class GnssTimeUpdateServiceTest {
// listening again.
verify(mMockAlarmManager).cancel(alarmListenerCaptor.getValue());
verify(mMockLocationManager).requestLocationUpdates(any(), any(), any(), any());
- verifyZeroInteractions(mMockTimeDetectorInternal);
+ verifyNoMoreInteractions(mMockTimeDetectorInternal);
}
private void advanceServiceToSleepingState(
@@ -190,7 +190,7 @@ public final class GnssTimeUpdateServiceTest {
any(), any(), any(), locationListenerCaptor.capture());
LocationListener locationListener = locationListenerCaptor.getValue();
Location location = new Location(LocationManager.GPS_PROVIDER);
- verifyZeroInteractions(mMockAlarmManager, mMockTimeDetectorInternal);
+ verifyNoMoreInteractions(mMockAlarmManager, mMockTimeDetectorInternal);
locationListener.onLocationChanged(location);
diff --git a/services/tests/uiservicestests/Android.bp b/services/tests/uiservicestests/Android.bp
index 66d7611a29c6..d34d74d80882 100644
--- a/services/tests/uiservicestests/Android.bp
+++ b/services/tests/uiservicestests/Android.bp
@@ -11,13 +11,8 @@ package {
default_applicable_licenses: ["frameworks_base_license"],
}
-android_test {
- name: "FrameworksUiServicesTests",
-
- // Include test java files
- srcs: [
- "src/**/*.java",
- ],
+java_defaults {
+ name: "FrameworksUiServicesTests-defaults",
static_libs: [
"compatibility-device-util-axt-minus-dexmaker",
@@ -95,12 +90,72 @@ android_test {
javacflags: ["-parameters"],
}
-test_module_config {
- name: "FrameworksUiServicesTests_notification",
- base: "FrameworksUiServicesTests",
- test_suites: [
- "automotive-tests",
- "device-tests",
+// Utility files used by multiple tests
+filegroup {
+ name: "shared-srcs",
+ srcs: [
+ "src/android/app/ExampleActivity.java",
+ "src/android/app/NotificationSystemUtil.java",
+ "src/com/android/frameworks/tests/uiservices/DummyProvider.java",
+ "src/com/android/internal/logging/InstanceIdSequenceFake.java",
+ "src/com/android/server/UiServiceTestCase.java",
+ "src/com/android/server/notification/ZenChangeOrigin.java",
+ "src/com/android/server/notification/ZenModeEventLoggerFake.java",
+ ],
+ visibility: ["//visibility:private"],
+}
+
+filegroup {
+ name: "notification-srcs",
+ srcs: [
+ "src/**/Notification*.java",
+ "src/com/android/server/notification/*.java",
+ ],
+ visibility: ["//visibility:private"],
+}
+
+filegroup {
+ name: "notification-zen-srcs",
+ srcs: [
+ "src/android/app/NotificationManagerZenTest.java",
+ "src/com/android/server/notification/Zen*Test.java",
+ ],
+ visibility: ["//visibility:private"],
+}
+
+android_test {
+ name: "FrameworksUiServicesTests",
+
+ // Include test java files but not the notification & zen ones which are separated
+ srcs: [
+ "src/**/*.java",
+ ],
+
+ exclude_srcs: [
+ ":notification-srcs",
+ ":notification-zen-srcs",
+ ],
+
+ defaults: ["FrameworksUiServicesTests-defaults"],
+}
+
+android_test {
+ name: "FrameworksUiServicesNotificationTests",
+ srcs: [
+ ":notification-srcs",
+ ":shared-srcs",
+ ],
+ exclude_srcs: [":notification-zen-srcs"],
+ defaults: ["FrameworksUiServicesTests-defaults"],
+ test_config: "notification-tests.xml",
+}
+
+android_test {
+ name: "FrameworksUiServicesZenTests",
+ srcs: [
+ ":notification-zen-srcs",
+ ":shared-srcs",
],
- exclude_annotations: ["androidx.test.filters.LargeTest"],
+ defaults: ["FrameworksUiServicesTests-defaults"],
+ test_config: "notification-zen-tests.xml",
}
diff --git a/services/tests/uiservicestests/AndroidTest.xml b/services/tests/uiservicestests/AndroidTest.xml
index 11e8f090a8fa..93c8c72630f1 100644
--- a/services/tests/uiservicestests/AndroidTest.xml
+++ b/services/tests/uiservicestests/AndroidTest.xml
@@ -15,6 +15,7 @@
-->
<configuration description="Runs Frameworks UI Services Tests.">
<target_preparer class="com.android.tradefed.targetprep.TestAppInstallSetup">
+ <option name="cleanup-apks" value="true" />
<option name="test-file-name" value="FrameworksUiServicesTests.apk" />
</target_preparer>
diff --git a/services/tests/uiservicestests/notification-tests.xml b/services/tests/uiservicestests/notification-tests.xml
new file mode 100644
index 000000000000..acfd844efe26
--- /dev/null
+++ b/services/tests/uiservicestests/notification-tests.xml
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+<configuration description="Runs Frameworks UI Services Tests (notifications subset).">
+ <target_preparer class="com.android.tradefed.targetprep.TestAppInstallSetup">
+ <option name="cleanup-apks" value="true" />
+ <option name="test-file-name" value="FrameworksUiServicesNotificationTests.apk" />
+ </target_preparer>
+
+ <option name="test-suite-tag" value="apct" />
+ <option name="test-suite-tag" value="framework-base-presubmit" />
+ <option name="test-tag" value="FrameworksUiServicesNotificationTests" />
+ <test class="com.android.tradefed.testtype.AndroidJUnitTest" >
+ <option name="package" value="com.android.frameworks.tests.uiservices" />
+ <option name="runner" value="android.testing.TestableInstrumentation" />
+ <option name="hidden-api-checks" value="false"/>
+ </test>
+</configuration>
diff --git a/services/tests/uiservicestests/notification-zen-tests.xml b/services/tests/uiservicestests/notification-zen-tests.xml
new file mode 100644
index 000000000000..01d8aab83d59
--- /dev/null
+++ b/services/tests/uiservicestests/notification-zen-tests.xml
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+<configuration description="Runs Frameworks UI Services Tests (zen mode subset).">
+ <target_preparer class="com.android.tradefed.targetprep.TestAppInstallSetup">
+ <option name="cleanup-apks" value="true" />
+ <option name="test-file-name" value="FrameworksUiServicesZenTests.apk" />
+ </target_preparer>
+
+ <option name="test-suite-tag" value="apct" />
+ <option name="test-suite-tag" value="framework-base-presubmit" />
+ <option name="test-tag" value="FrameworksUiServicesZenTests" />
+ <test class="com.android.tradefed.testtype.AndroidJUnitTest" >
+ <option name="package" value="com.android.frameworks.tests.uiservices" />
+ <option name="runner" value="android.testing.TestableInstrumentation" />
+ <option name="hidden-api-checks" value="false"/>
+ </test>
+</configuration>
diff --git a/services/tests/uiservicestests/src/com/android/server/UiModeManagerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/UiModeManagerServiceTest.java
index 9930c9f07ed8..7b1ce446da7c 100644
--- a/services/tests/uiservicestests/src/com/android/server/UiModeManagerServiceTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/UiModeManagerServiceTest.java
@@ -62,7 +62,6 @@ import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoMoreInteractions;
-import static org.mockito.Mockito.verifyZeroInteractions;
import static org.mockito.Mockito.when;
import static org.testng.Assert.assertThrows;
@@ -992,7 +991,7 @@ public class UiModeManagerServiceTest extends UiServiceTestCase {
() -> mService.requestProjection(mBinder, PROJECTION_TYPE_NONE, PACKAGE_NAME));
verify(mContext, never()).enforceCallingPermission(
eq(Manifest.permission.TOGGLE_AUTOMOTIVE_PROJECTION), any());
- verifyZeroInteractions(mBinder);
+ verifyNoMoreInteractions(mBinder);
assertEquals(PROJECTION_TYPE_NONE, mService.getActiveProjectionTypes());
}
@@ -1008,7 +1007,7 @@ public class UiModeManagerServiceTest extends UiServiceTestCase {
() -> mService.requestProjection(mBinder, multipleProjectionTypes, PACKAGE_NAME));
verify(mContext, never()).enforceCallingPermission(
eq(Manifest.permission.TOGGLE_AUTOMOTIVE_PROJECTION), any());
- verifyZeroInteractions(mBinder);
+ verifyNoMoreInteractions(mBinder);
assertEquals(PROJECTION_TYPE_NONE, mService.getActiveProjectionTypes());
}
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/ConditionProvidersTest.java b/services/tests/uiservicestests/src/com/android/server/notification/ConditionProvidersTest.java
index 6b989cb0aaee..b33233107766 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/ConditionProvidersTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/ConditionProvidersTest.java
@@ -38,7 +38,6 @@ import android.os.IInterface;
import android.platform.test.flag.junit.SetFlagsRule;
import android.service.notification.Condition;
-import com.android.internal.R;
import com.android.server.UiServiceTestCase;
import org.junit.Before;
@@ -47,8 +46,6 @@ import org.junit.Test;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
-import java.util.List;
-
public class ConditionProvidersTest extends UiServiceTestCase {
private ConditionProviders mProviders;
@@ -172,15 +169,4 @@ public class ConditionProvidersTest extends UiServiceTestCase {
assertTrue(mProviders.getApproved(userId, true).isEmpty());
}
-
- @Test
- public void getDefaultDndAccessPackages_returnsPackages() {
- mContext.getOrCreateTestableResources().addOverride(
- R.string.config_defaultDndAccessPackages,
- "com.example.a:com.example.b::::com.example.c");
-
- List<String> packages = ConditionProviders.getDefaultDndAccessPackages(mContext);
-
- assertThat(packages).containsExactly("com.example.a", "com.example.b", "com.example.c");
- }
}
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/DefaultDeviceEffectsApplierTest.java b/services/tests/uiservicestests/src/com/android/server/notification/DefaultDeviceEffectsApplierTest.java
index 5ce9a3e8d4d4..8023bdd08927 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/DefaultDeviceEffectsApplierTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/DefaultDeviceEffectsApplierTest.java
@@ -36,7 +36,6 @@ import static org.mockito.Mockito.never;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoMoreInteractions;
-import static org.mockito.Mockito.verifyZeroInteractions;
import static org.mockito.Mockito.when;
import android.app.KeyguardManager;
@@ -184,7 +183,7 @@ public class DefaultDeviceEffectsApplierTest {
mApplier.apply(noEffects, ORIGIN_USER_IN_SYSTEMUI);
verify(mPowerManager).suppressAmbientDisplay(anyString(), eq(false));
- verifyZeroInteractions(mColorDisplayManager, mWallpaperManager, mUiModeManager);
+ verifyNoMoreInteractions(mColorDisplayManager, mWallpaperManager, mUiModeManager);
}
@Test
@@ -252,8 +251,8 @@ public class DefaultDeviceEffectsApplierTest {
// Wallpaper dimming was undone, Grayscale was applied, nothing else was touched.
verify(mWallpaperManager).setWallpaperDimAmount(eq(0.0f));
verify(mColorDisplayManager).setSaturationLevel(eq(0));
- verifyZeroInteractions(mPowerManager);
- verifyZeroInteractions(mUiModeManager);
+ verifyNoMoreInteractions(mPowerManager);
+ verifyNoMoreInteractions(mUiModeManager);
}
@Test
@@ -269,7 +268,7 @@ public class DefaultDeviceEffectsApplierTest {
ORIGIN_APP);
// Effect was not yet applied, but a broadcast receiver was registered.
- verifyZeroInteractions(mUiModeManager);
+ verifyNoMoreInteractions(mUiModeManager);
verify(mContext).registerReceiver(broadcastReceiverCaptor.capture(),
intentFilterCaptor.capture(), anyInt());
assertThat(intentFilterCaptor.getValue().getAction(0)).isEqualTo(Intent.ACTION_SCREEN_OFF);
@@ -337,7 +336,7 @@ public class DefaultDeviceEffectsApplierTest {
origin.value());
// Effect was not applied, will be on next screen-off.
- verifyZeroInteractions(mUiModeManager);
+ verifyNoMoreInteractions(mUiModeManager);
verify(mContext).registerReceiver(any(),
argThat(filter -> Intent.ACTION_SCREEN_OFF.equals(filter.getAction(0))),
anyInt());
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/GroupHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/GroupHelperTest.java
index 46be9a57ce57..11143653a75d 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/GroupHelperTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/GroupHelperTest.java
@@ -60,7 +60,7 @@ import static org.mockito.Mockito.reset;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.verifyZeroInteractions;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
import static org.mockito.Mockito.when;
import android.annotation.SuppressLint;
@@ -313,7 +313,7 @@ public class GroupHelperTest extends UiServiceTestCase {
getNotificationRecord(pkg, i, String.valueOf(i), UserHandle.SYSTEM),
false);
}
- verifyZeroInteractions(mCallback);
+ verifyNoMoreInteractions(mCallback);
}
@Test
@@ -327,7 +327,7 @@ public class GroupHelperTest extends UiServiceTestCase {
}
mGroupHelper.onNotificationPosted(
getNotificationRecord(pkg2, AUTOGROUP_AT_COUNT, "four", UserHandle.SYSTEM), false);
- verifyZeroInteractions(mCallback);
+ verifyNoMoreInteractions(mCallback);
}
@Test
@@ -340,7 +340,7 @@ public class GroupHelperTest extends UiServiceTestCase {
}
mGroupHelper.onNotificationPosted(
getNotificationRecord(pkg, AUTOGROUP_AT_COUNT, "four", UserHandle.of(7)), false);
- verifyZeroInteractions(mCallback);
+ verifyNoMoreInteractions(mCallback);
}
@Test
@@ -353,7 +353,7 @@ public class GroupHelperTest extends UiServiceTestCase {
mGroupHelper.onNotificationPosted(
getNotificationRecord(pkg, AUTOGROUP_AT_COUNT, "four", UserHandle.SYSTEM, "a", false),
false);
- verifyZeroInteractions(mCallback);
+ verifyNoMoreInteractions(mCallback);
}
@Test
@@ -1744,7 +1744,7 @@ public class GroupHelperTest extends UiServiceTestCase {
notificationList.add(r);
mGroupHelper.onNotificationPostedWithDelay(r, notificationList, summaryByGroup);
}
- verifyZeroInteractions(mCallback);
+ verifyNoMoreInteractions(mCallback);
}
@Test
@@ -1759,7 +1759,7 @@ public class GroupHelperTest extends UiServiceTestCase {
notificationList.add(r);
mGroupHelper.onNotificationPostedWithDelay(r, notificationList, summaryByGroup);
}
- verifyZeroInteractions(mCallback);
+ verifyNoMoreInteractions(mCallback);
}
@Test
@@ -1775,7 +1775,7 @@ public class GroupHelperTest extends UiServiceTestCase {
notificationList.add(r);
mGroupHelper.onNotificationPostedWithDelay(r, notificationList, summaryByGroup);
}
- verifyZeroInteractions(mCallback);
+ verifyNoMoreInteractions(mCallback);
}
@Test
@@ -1791,7 +1791,7 @@ public class GroupHelperTest extends UiServiceTestCase {
notificationList.add(r);
mGroupHelper.onNotificationPostedWithDelay(r, notificationList, summaryByGroup);
}
- verifyZeroInteractions(mCallback);
+ verifyNoMoreInteractions(mCallback);
}
@Test
@@ -1811,7 +1811,7 @@ public class GroupHelperTest extends UiServiceTestCase {
String.valueOf(AUTOGROUP_AT_COUNT), UserHandle.SYSTEM, "testGrp", true);
notificationList.add(r);
mGroupHelper.onNotificationPostedWithDelay(r, notificationList, summaryByGroup);
- verifyZeroInteractions(mCallback);
+ verifyNoMoreInteractions(mCallback);
}
@Test
@@ -1830,7 +1830,7 @@ public class GroupHelperTest extends UiServiceTestCase {
String.valueOf(AUTOGROUP_AT_COUNT), UserHandle.of(7), "testGrp", true);
notificationList.add(r);
mGroupHelper.onNotificationPostedWithDelay(r, notificationList, summaryByGroup);
- verifyZeroInteractions(mCallback);
+ verifyNoMoreInteractions(mCallback);
}
@Test
@@ -1853,7 +1853,7 @@ public class GroupHelperTest extends UiServiceTestCase {
String.valueOf(AUTOGROUP_AT_COUNT + 1), UserHandle.SYSTEM, "testGrp", false);
notificationList.add(child);
mGroupHelper.onNotificationPostedWithDelay(summary, notificationList, summaryByGroup);
- verifyZeroInteractions(mCallback);
+ verifyNoMoreInteractions(mCallback);
}
@Test
@@ -1877,7 +1877,7 @@ public class GroupHelperTest extends UiServiceTestCase {
notificationList.add(child);
summaryByGroup.put(summary.getGroupKey(), summary);
mGroupHelper.onNotificationPostedWithDelay(child, notificationList, summaryByGroup);
- verifyZeroInteractions(mCallback);
+ verifyNoMoreInteractions(mCallback);
}
@Test
@@ -2209,7 +2209,7 @@ public class GroupHelperTest extends UiServiceTestCase {
childrenToRemove.add(child);
}
mGroupHelper.onNotificationPostedWithDelay(summary, notificationList, summaryByGroup);
- verifyZeroInteractions(mCallback);
+ verifyNoMoreInteractions(mCallback);
// Remove all child notifications from the valid group => summary without children
Mockito.reset(mCallback);
@@ -2273,7 +2273,7 @@ public class GroupHelperTest extends UiServiceTestCase {
}
}
mGroupHelper.onNotificationPostedWithDelay(summary, notificationList, summaryByGroup);
- verifyZeroInteractions(mCallback);
+ verifyNoMoreInteractions(mCallback);
// Remove some child notifications from the valid group, transform into a singleton group
Mockito.reset(mCallback);
@@ -2329,7 +2329,7 @@ public class GroupHelperTest extends UiServiceTestCase {
notificationList.add(child);
}
mGroupHelper.onNotificationPostedWithDelay(summary, notificationList, summaryByGroup);
- verifyZeroInteractions(mCallback);
+ verifyNoMoreInteractions(mCallback);
// Remove all child notifications from the valid group => summary without children
Mockito.reset(mCallback);
@@ -2343,7 +2343,7 @@ public class GroupHelperTest extends UiServiceTestCase {
mGroupHelper.onGroupedNotificationRemovedWithDelay(summary, notificationList,
summaryByGroup);
// Check that nothing was force grouped
- verifyZeroInteractions(mCallback);
+ verifyNoMoreInteractions(mCallback);
}
@Test
@@ -3837,7 +3837,7 @@ public class GroupHelperTest extends UiServiceTestCase {
mGroupHelper.onNotificationPostedWithDelay(child, notificationList, summaryByGroup);
mGroupHelper.onNotificationPostedWithDelay(summary, notificationList, summaryByGroup);
}
- verifyZeroInteractions(mCallback);
+ verifyNoMoreInteractions(mCallback);
}
@@ -3861,7 +3861,7 @@ public class GroupHelperTest extends UiServiceTestCase {
mGroupHelper.onNotificationPostedWithDelay(summary, notificationList, summaryByGroup);
}
// FLAG_NOTIFICATION_FORCE_GROUP_SINGLETONS is disabled => don't force group
- verifyZeroInteractions(mCallback);
+ verifyNoMoreInteractions(mCallback);
}
@Test
@@ -4498,7 +4498,7 @@ public class GroupHelperTest extends UiServiceTestCase {
mGroupHelper.onNotificationPostedWithDelay(extra, notifList, summaryByGroupKey);
// no autogrouping should have occurred
- verifyZeroInteractions(mCallback);
+ verifyNoMoreInteractions(mCallback);
}
@Test
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 a02f628ce9b7..43228f434997 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java
@@ -96,7 +96,7 @@ import static org.mockito.Mockito.never;
import static org.mockito.Mockito.reset;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.verifyZeroInteractions;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
import static org.mockito.Mockito.when;
import android.app.AppOpsManager;
@@ -6372,7 +6372,7 @@ public class PreferencesHelperTest extends UiServiceTestCase {
NotificationChannel same = new NotificationChannel("id", "Bah", IMPORTANCE_DEFAULT);
mHelper.createNotificationChannel(PKG_P, 0, same, true, false, 0, false);
- verifyZeroInteractions(mHandler);
+ verifyNoMoreInteractions(mHandler);
}
@Test
@@ -6398,7 +6398,7 @@ public class PreferencesHelperTest extends UiServiceTestCase {
mHelper.updateNotificationChannel(PKG_P, 0, same, false, 0, false);
- verifyZeroInteractions(mHandler);
+ verifyNoMoreInteractions(mHandler);
}
@Test
@@ -6412,7 +6412,7 @@ public class PreferencesHelperTest extends UiServiceTestCase {
public void setShowBadge_same_doesNotRequestSort() {
mHelper.setShowBadge(PKG_P, 0, true); // true == DEFAULT_SHOW_BADGE
- verifyZeroInteractions(mHandler);
+ verifyNoMoreInteractions(mHandler);
}
@Test
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/ZenConfigTrimmerTest.java b/services/tests/uiservicestests/src/com/android/server/notification/ZenConfigTrimmerTest.java
deleted file mode 100644
index 154a905c776b..000000000000
--- a/services/tests/uiservicestests/src/com/android/server/notification/ZenConfigTrimmerTest.java
+++ /dev/null
@@ -1,124 +0,0 @@
-/*
- * 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.notification;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import android.os.Parcel;
-import android.service.notification.ZenModeConfig;
-import android.service.notification.ZenModeConfig.ZenRule;
-
-import androidx.test.ext.junit.runners.AndroidJUnit4;
-
-import com.android.internal.R;
-import com.android.server.UiServiceTestCase;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-@RunWith(AndroidJUnit4.class)
-public class ZenConfigTrimmerTest extends UiServiceTestCase {
-
- private static final String TRUSTED_PACKAGE = "com.trust.me";
- private static final int ONE_PERCENT = 1_500;
-
- private ZenConfigTrimmer mTrimmer;
-
- @Before
- public void setUp() {
- mContext.getOrCreateTestableResources().addOverride(
- R.string.config_defaultDndAccessPackages, TRUSTED_PACKAGE);
-
- mTrimmer = new ZenConfigTrimmer(mContext);
- }
-
- @Test
- public void trimToMaximumSize_belowMax_untouched() {
- ZenModeConfig config = new ZenModeConfig();
- addZenRule(config, "1", "pkg1", 10 * ONE_PERCENT);
- addZenRule(config, "2", "pkg1", 10 * ONE_PERCENT);
- addZenRule(config, "3", "pkg1", 10 * ONE_PERCENT);
- addZenRule(config, "4", "pkg2", 20 * ONE_PERCENT);
- addZenRule(config, "5", "pkg2", 20 * ONE_PERCENT);
-
- mTrimmer.trimToMaximumSize(config);
-
- assertThat(config.automaticRules.keySet()).containsExactly("1", "2", "3", "4", "5");
- }
-
- @Test
- public void trimToMaximumSize_exceedsMax_removesAllRulesOfLargestPackages() {
- ZenModeConfig config = new ZenModeConfig();
- addZenRule(config, "1", "pkg1", 10 * ONE_PERCENT);
- addZenRule(config, "2", "pkg1", 10 * ONE_PERCENT);
- addZenRule(config, "3", "pkg1", 10 * ONE_PERCENT);
- addZenRule(config, "4", "pkg2", 20 * ONE_PERCENT);
- addZenRule(config, "5", "pkg2", 20 * ONE_PERCENT);
- addZenRule(config, "6", "pkg3", 35 * ONE_PERCENT);
- addZenRule(config, "7", "pkg4", 38 * ONE_PERCENT);
-
- mTrimmer.trimToMaximumSize(config);
-
- assertThat(config.automaticRules.keySet()).containsExactly("1", "2", "3", "6");
- assertThat(config.automaticRules.values().stream().map(r -> r.pkg).distinct())
- .containsExactly("pkg1", "pkg3");
- }
-
- @Test
- public void trimToMaximumSize_keepsRulesFromTrustedPackages() {
- ZenModeConfig config = new ZenModeConfig();
- addZenRule(config, "1", "pkg1", 10 * ONE_PERCENT);
- addZenRule(config, "2", "pkg1", 10 * ONE_PERCENT);
- addZenRule(config, "3", "pkg1", 10 * ONE_PERCENT);
- addZenRule(config, "4", TRUSTED_PACKAGE, 60 * ONE_PERCENT);
- addZenRule(config, "5", "pkg2", 20 * ONE_PERCENT);
- addZenRule(config, "6", "pkg3", 35 * ONE_PERCENT);
-
- mTrimmer.trimToMaximumSize(config);
-
- assertThat(config.automaticRules.keySet()).containsExactly("4", "5");
- assertThat(config.automaticRules.values().stream().map(r -> r.pkg).distinct())
- .containsExactly(TRUSTED_PACKAGE, "pkg2");
- }
-
- /**
- * Create a ZenRule that, when serialized to a Parcel, will take <em>approximately</em>
- * {@code desiredSize} bytes (within 100 bytes). Try to make the tests not rely on a very tight
- * fit.
- */
- private static void addZenRule(ZenModeConfig config, String id, String pkg, int desiredSize) {
- ZenRule rule = new ZenRule();
- rule.id = id;
- rule.pkg = pkg;
- config.automaticRules.put(id, rule);
-
- // Make the ZenRule as large as desired. Not to the exact byte, because otherwise this
- // test would have to be adjusted whenever we change the parceling of ZenRule in any way.
- // (Still might need adjustment if we change the serialization _significantly_).
- int nameLength = desiredSize - id.length() - pkg.length() - 232;
- rule.name = "A".repeat(nameLength);
-
- Parcel verification = Parcel.obtain();
- try {
- verification.writeParcelable(rule, 0);
- assertThat(verification.dataSize()).isWithin(100).of(desiredSize);
- } finally {
- verification.recycle();
- }
- }
-}
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java
index 5377102e5a13..bfce647356ec 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java
@@ -90,7 +90,6 @@ import static com.android.os.dnd.DNDProtoEnums.PEOPLE_STARRED;
import static com.android.os.dnd.DNDProtoEnums.ROOT_CONFIG;
import static com.android.os.dnd.DNDProtoEnums.STATE_ALLOW;
import static com.android.os.dnd.DNDProtoEnums.STATE_DISALLOW;
-import static com.android.server.notification.Flags.FLAG_LIMIT_ZEN_CONFIG_SIZE;
import static com.android.server.notification.Flags.FLAG_PREVENT_ZEN_DEVICE_EFFECTS_WHILE_DRIVING;
import static com.android.server.notification.ZenModeEventLogger.ACTIVE_RULE_TYPE_MANUAL;
import static com.android.server.notification.ZenModeHelper.RULE_LIMIT_PER_PACKAGE;
@@ -238,7 +237,6 @@ import java.util.stream.Collectors;
@SmallTest
@SuppressLint("GuardedBy") // It's ok for this test to access guarded methods from the service.
@RunWith(ParameterizedAndroidJunit4.class)
-@EnableFlags(FLAG_LIMIT_ZEN_CONFIG_SIZE) // Should be parameterization, but off path does nothing.
@TestableLooper.RunWithLooper
public class ZenModeHelperTest extends UiServiceTestCase {
@@ -7485,45 +7483,6 @@ public class ZenModeHelperTest extends UiServiceTestCase {
assertThat(getZenRule(ruleId).lastActivation).isNull();
}
- @Test
- @EnableFlags(FLAG_LIMIT_ZEN_CONFIG_SIZE)
- public void addAutomaticZenRule_trimsConfiguration() {
- mZenModeHelper.mConfig.automaticRules.clear();
- AutomaticZenRule smallRule = new AutomaticZenRule.Builder("Reasonable", CONDITION_ID)
- .setConfigurationActivity(new ComponentName(mPkg, "cls"))
- .build();
- AutomaticZenRule systemRule = new AutomaticZenRule.Builder("System", CONDITION_ID)
- .setOwner(new ComponentName("android", "ScheduleConditionProvider"))
- .build();
-
- AutomaticZenRule bigRule = new AutomaticZenRule.Builder("Yuge", CONDITION_ID)
- .setConfigurationActivity(new ComponentName("evil.package", "cls"))
- .setTriggerDescription("0123456789".repeat(6000)) // ~60k bytes utf16.
- .build();
-
- String systemRuleId = mZenModeHelper.addAutomaticZenRule(UserHandle.CURRENT, "android",
- systemRule, ORIGIN_SYSTEM, "add", SYSTEM_UID);
- String smallRuleId = mZenModeHelper.addAutomaticZenRule(UserHandle.CURRENT, mPkg, smallRule,
- ORIGIN_APP, "add", CUSTOM_PKG_UID);
- String bigRuleId1 = mZenModeHelper.addAutomaticZenRule(UserHandle.CURRENT, "evil.package",
- bigRule, ORIGIN_APP, "add", CUSTOM_PKG_UID);
- assertThat(mZenModeHelper.mConfig.automaticRules.keySet()).containsExactly(
- systemRuleId, smallRuleId, bigRuleId1);
-
- String bigRuleId2 = mZenModeHelper.addAutomaticZenRule(UserHandle.CURRENT, "evil.package",
- bigRule, ORIGIN_APP, "add", CUSTOM_PKG_UID);
- assertThat(mZenModeHelper.mConfig.automaticRules.keySet()).containsExactly(
- systemRuleId, smallRuleId, bigRuleId1, bigRuleId2);
-
- // This should go over the threshold
- String bigRuleId3 = mZenModeHelper.addAutomaticZenRule(UserHandle.CURRENT, "evil.package",
- bigRule, ORIGIN_APP, "add", CUSTOM_PKG_UID);
-
- // Rules from evil.package are gone.
- assertThat(mZenModeHelper.mConfig.automaticRules.keySet()).containsExactly(
- systemRuleId, smallRuleId);
- }
-
private static void addZenRule(ZenModeConfig config, String id, String ownerPkg, int zenMode,
@Nullable ZenPolicy zenPolicy) {
ZenRule rule = new ZenRule();
diff --git a/services/tests/vibrator/src/com/android/server/vibrator/VibratorControlServiceTest.java b/services/tests/vibrator/src/com/android/server/vibrator/VibratorControlServiceTest.java
index 79e272b7ec01..01698b5bdd6b 100644
--- a/services/tests/vibrator/src/com/android/server/vibrator/VibratorControlServiceTest.java
+++ b/services/tests/vibrator/src/com/android/server/vibrator/VibratorControlServiceTest.java
@@ -33,7 +33,6 @@ import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoMoreInteractions;
-import static org.mockito.Mockito.verifyZeroInteractions;
import static org.mockito.Mockito.when;
import android.content.ComponentName;
@@ -133,7 +132,7 @@ public class VibratorControlServiceTest {
mVibratorControlService.registerVibratorController(controller1);
mVibratorControlService.unregisterVibratorController(controller2);
- verifyZeroInteractions(mMockVibrationScaler);
+ verifyNoMoreInteractions(mMockVibrationScaler);
assertThat(controller1.isLinkedToDeath).isTrue();
}
@@ -187,7 +186,7 @@ public class VibratorControlServiceTest {
verify(mStatsLoggerMock).logVibrationParamResponseIgnored();
verifyNoMoreInteractions(mStatsLoggerMock);
- verifyZeroInteractions(mMockVibrationScaler);
+ verifyNoMoreInteractions(mMockVibrationScaler);
}
@Test(expected = IllegalArgumentException.class)
@@ -242,7 +241,7 @@ public class VibratorControlServiceTest {
mFakeVibratorController);
verify(mStatsLoggerMock, never()).logVibrationParamScale(anyFloat());
- verifyZeroInteractions(mMockVibrationScaler);
+ verifyNoMoreInteractions(mMockVibrationScaler);
}
@Test(expected = IllegalArgumentException.class)
@@ -280,7 +279,7 @@ public class VibratorControlServiceTest {
mFakeVibratorController);
verify(mStatsLoggerMock, never()).logVibrationParamScale(anyFloat());
- verifyZeroInteractions(mMockVibrationScaler);
+ verifyNoMoreInteractions(mMockVibrationScaler);
}
@Test
diff --git a/services/tests/voiceinteractiontests/src/com/android/server/soundtrigger_middleware/SoundTriggerHalConcurrentCaptureHandlerTest.java b/services/tests/voiceinteractiontests/src/com/android/server/soundtrigger_middleware/SoundTriggerHalConcurrentCaptureHandlerTest.java
index 9a59ede20e23..011971d85f42 100644
--- a/services/tests/voiceinteractiontests/src/com/android/server/soundtrigger_middleware/SoundTriggerHalConcurrentCaptureHandlerTest.java
+++ b/services/tests/voiceinteractiontests/src/com/android/server/soundtrigger_middleware/SoundTriggerHalConcurrentCaptureHandlerTest.java
@@ -28,7 +28,6 @@ import static org.mockito.Mockito.never;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoMoreInteractions;
-import static org.mockito.Mockito.verifyZeroInteractions;
import android.media.soundtrigger.RecognitionStatus;
import android.media.soundtrigger_middleware.RecognitionEventSys;
@@ -76,7 +75,7 @@ public class SoundTriggerHalConcurrentCaptureHandlerTest {
assertEquals(event.halEventReceivedMillis, -1);
assertEquals(event.recognitionEvent.status, RecognitionStatus.ABORTED);
assertFalse(event.recognitionEvent.recognitionStillActive);
- verifyZeroInteractions(mGlobalCallback);
+ verifyNoMoreInteractions(mGlobalCallback);
clearInvocations(callback, mUnderlying);
mNotifier.setActive(false);
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
index 8992c2e632b2..7f242dea9f45 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
@@ -2021,8 +2021,6 @@ public class ActivityRecordTests extends WindowTestsBase {
display.setFixedRotationLaunchingAppUnchecked(activity);
displayRotation.updateRotationUnchecked(true /* forceUpdate */);
- assertTrue(displayRotation.isRotatingSeamlessly());
-
// The launching rotated app should not be cleared when waiting for remote rotation.
display.continueUpdateOrientationForDiffOrienLaunchingApp();
assertTrue(display.isFixedRotationLaunchingApp(activity));
diff --git a/services/tests/wmtests/src/com/android/server/wm/DesktopModeLaunchParamsModifierTests.java b/services/tests/wmtests/src/com/android/server/wm/DesktopModeLaunchParamsModifierTests.java
index 48731cb460d4..00b617e91bfd 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DesktopModeLaunchParamsModifierTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DesktopModeLaunchParamsModifierTests.java
@@ -383,7 +383,7 @@ public class DesktopModeLaunchParamsModifierTests extends
spyOn(mActivity.mAppCompatController.getAspectRatioOverrides());
doReturn(true).when(
mActivity.mAppCompatController.getAspectRatioOverrides())
- .isUserFullscreenOverrideEnabled();
+ .hasFullscreenOverride();
final int desiredWidth =
(int) (LANDSCAPE_DISPLAY_BOUNDS.width() * DESKTOP_MODE_INITIAL_BOUNDS_SCALE);
@@ -411,7 +411,7 @@ public class DesktopModeLaunchParamsModifierTests extends
spyOn(mActivity.mAppCompatController.getAspectRatioOverrides());
doReturn(true).when(
mActivity.mAppCompatController.getAspectRatioOverrides())
- .isSystemOverrideToFullscreenEnabled();
+ .hasFullscreenOverride();
final int desiredWidth =
(int) (LANDSCAPE_DISPLAY_BOUNDS.width() * DESKTOP_MODE_INITIAL_BOUNDS_SCALE);
@@ -1170,6 +1170,32 @@ public class DesktopModeLaunchParamsModifierTests extends
@Test
@EnableFlags({Flags.FLAG_ENABLE_DESKTOP_WINDOWING_MODE,
Flags.FLAG_ENABLE_SHELL_INITIAL_BOUNDS_REGRESSION_BUG_FIX})
+ public void testOptionsBoundsSet_flexibleLaunchSizeWithFullscreenOverride_noModifications() {
+ setupDesktopModeLaunchParamsModifier();
+
+ final TestDisplayContent display = createNewDisplayContent(WINDOWING_MODE_FULLSCREEN);
+ final Task task = new TaskBuilder(mSupervisor).setActivityType(
+ ACTIVITY_TYPE_STANDARD).setDisplay(display).build();
+ final ActivityOptions options = ActivityOptions.makeBasic()
+ .setLaunchBounds(new Rect(
+ DISPLAY_STABLE_BOUNDS.left,
+ DISPLAY_STABLE_BOUNDS.top,
+ /* right = */ 500,
+ /* bottom = */ 500))
+ .setFlexibleLaunchSize(true);
+ spyOn(mActivity.mAppCompatController.getAspectRatioOverrides());
+ doReturn(true).when(
+ mActivity.mAppCompatController.getAspectRatioOverrides())
+ .hasFullscreenOverride();
+
+ assertEquals(RESULT_DONE,
+ new CalculateRequestBuilder().setTask(task).setOptions(options).calculate());
+ assertEquals(options.getLaunchBounds(), mResult.mBounds);
+ }
+
+ @Test
+ @EnableFlags({Flags.FLAG_ENABLE_DESKTOP_WINDOWING_MODE,
+ Flags.FLAG_ENABLE_SHELL_INITIAL_BOUNDS_REGRESSION_BUG_FIX})
public void testOptionsBoundsSet_flexibleLaunchSize_boundsSizeModified() {
setupDesktopModeLaunchParamsModifier();
diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayAreaTest.java b/services/tests/wmtests/src/com/android/server/wm/DisplayAreaTest.java
index 0af41ea1f634..89aa3b9a2443 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayAreaTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayAreaTest.java
@@ -56,7 +56,7 @@ import static org.mockito.Mockito.clearInvocations;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.verifyZeroInteractions;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
import static org.mockito.Mockito.when;
import android.content.pm.ActivityInfo;
@@ -174,7 +174,7 @@ public class DisplayAreaTest extends WindowTestsBase {
da1.reduceOnAllTaskDisplayAreas(callback2, 0);
da1.getItemFromTaskDisplayAreas(callback3);
- verifyZeroInteractions(da2);
+ verifyNoMoreInteractions(da2);
// Traverse the child if the current DA has type ANY
final DisplayArea<WindowContainer> da3 = new DisplayArea<>(mWm, ANY, "DA3");
@@ -207,7 +207,7 @@ public class DisplayAreaTest extends WindowTestsBase {
da5.reduceOnAllTaskDisplayAreas(callback2, 0);
da5.getItemFromTaskDisplayAreas(callback3);
- verifyZeroInteractions(da6);
+ verifyNoMoreInteractions(da6);
}
@Test
diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
index 4c81f738138a..ed00a9e8e74b 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
@@ -30,7 +30,10 @@ import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
import static android.os.Build.VERSION_CODES.P;
import static android.os.Build.VERSION_CODES.Q;
import static android.view.Display.DEFAULT_DISPLAY;
+import static android.view.Display.FLAG_ALLOWS_CONTENT_MODE_SWITCH;
import static android.view.Display.FLAG_PRIVATE;
+import static android.view.Display.FLAG_SHOULD_SHOW_SYSTEM_DECORATIONS;
+import static android.view.Display.FLAG_TRUSTED;
import static android.view.DisplayCutout.BOUNDS_POSITION_TOP;
import static android.view.DisplayCutout.fromBoundingRect;
import static android.view.Surface.ROTATION_0;
@@ -1706,8 +1709,6 @@ public class DisplayContentTests extends WindowTestsBase {
app.setVisible(true);
doReturn(false).when(app).inTransition();
mDisplayContent.mFixedRotationTransitionListener.onAppTransitionFinishedLocked(app.token);
- mStatusBarWindow.finishSeamlessRotation(t);
- mNavBarWindow.finishSeamlessRotation(t);
// The fixed rotation should be cleared and the new rotation is applied to display.
assertFalse(app.hasFixedRotationTransform());
@@ -2925,6 +2926,63 @@ public class DisplayContentTests extends WindowTestsBase {
assertFalse(dc.mWmService.mDisplayWindowSettings.shouldShowSystemDecorsLocked(dc));
}
+ @EnableFlags(FLAG_ENABLE_DISPLAY_CONTENT_MODE_MANAGEMENT)
+ @Test
+ public void testSetShouldShowSystemDecorations_shouldShowSystemDecorationsDisplay() {
+ // Set up a non-default display with FLAG_SHOULD_SHOW_SYSTEM_DECORATIONS enabled
+ final DisplayInfo displayInfo = new DisplayInfo(mDisplayInfo);
+ displayInfo.displayId = DEFAULT_DISPLAY + 1;
+ displayInfo.flags = FLAG_SHOULD_SHOW_SYSTEM_DECORATIONS;
+ final DisplayContent dc = createNewDisplay(displayInfo);
+
+ dc.onDisplayInfoChangeApplied();
+ assertFalse(dc.mWmService.mDisplayWindowSettings.shouldShowSystemDecorsLocked(dc));
+ }
+
+ @EnableFlags(FLAG_ENABLE_DISPLAY_CONTENT_MODE_MANAGEMENT)
+ @Test
+ public void testSetShouldShowSystemDecorations_notAllowContentModeSwitchDisplay() {
+ // Set up a non-default display without FLAG_ALLOWS_CONTENT_MODE_SWITCH enabled
+ final DisplayInfo displayInfo = new DisplayInfo(mDisplayInfo);
+ displayInfo.displayId = DEFAULT_DISPLAY + 1;
+ displayInfo.flags = FLAG_TRUSTED;
+ final DisplayContent dc = createNewDisplay(displayInfo);
+
+ dc.onDisplayInfoChangeApplied();
+ assertFalse(dc.mWmService.mDisplayWindowSettings.shouldShowSystemDecorsLocked(dc));
+ }
+
+ @EnableFlags(FLAG_ENABLE_DISPLAY_CONTENT_MODE_MANAGEMENT)
+ @Test
+ public void testSetShouldShowSystemDecorations_untrustedDisplay() {
+ // Set up a non-default display without FLAG_TRUSTED enabled
+ final DisplayInfo displayInfo = new DisplayInfo(mDisplayInfo);
+ displayInfo.displayId = DEFAULT_DISPLAY + 1;
+ displayInfo.flags = FLAG_ALLOWS_CONTENT_MODE_SWITCH;
+ final DisplayContent dc = createNewDisplay(displayInfo);
+
+ dc.onDisplayInfoChangeApplied();
+ assertFalse(dc.mWmService.mDisplayWindowSettings.shouldShowSystemDecorsLocked(dc));
+ }
+
+ @EnableFlags(FLAG_ENABLE_DISPLAY_CONTENT_MODE_MANAGEMENT)
+ @Test
+ public void testSetShouldShowSystemDecorations_nonDefaultNonPrivateDisplay() {
+ final DisplayInfo displayInfo = new DisplayInfo(mDisplayInfo);
+ displayInfo.displayId = DEFAULT_DISPLAY + 1;
+ displayInfo.flags = (FLAG_ALLOWS_CONTENT_MODE_SWITCH | FLAG_TRUSTED);
+ final DisplayContent dc = createNewDisplay(displayInfo);
+
+ spyOn(dc.mDisplay);
+ doReturn(false).when(dc.mDisplay).canHostTasks();
+ dc.onDisplayInfoChangeApplied();
+ assertFalse(dc.mWmService.mDisplayWindowSettings.shouldShowSystemDecorsLocked(dc));
+
+ doReturn(true).when(dc.mDisplay).canHostTasks();
+ dc.onDisplayInfoChangeApplied();
+ assertTrue(dc.mWmService.mDisplayWindowSettings.shouldShowSystemDecorsLocked(dc));
+ }
+
@EnableFlags(FLAG_ENABLE_PERSISTING_DISPLAY_SIZE_FOR_CONNECTED_DISPLAYS)
@Test
public void testForcedDensityRatioSetForExternalDisplays_persistDensityScaleFlagEnabled() {
@@ -2993,23 +3051,6 @@ public class DisplayContentTests extends WindowTestsBase {
assertEquals(320, displayContent.mBaseDisplayDensity);
}
- @EnableFlags(FLAG_ENABLE_DISPLAY_CONTENT_MODE_MANAGEMENT)
- @Test
- public void testSetShouldShowSystemDecorations_nonDefaultNonPrivateDisplay() {
- final DisplayInfo displayInfo = new DisplayInfo(mDisplayInfo);
- displayInfo.displayId = DEFAULT_DISPLAY + 1;
- final DisplayContent dc = createNewDisplay(displayInfo);
-
- spyOn(dc.mDisplay);
- doReturn(false).when(dc.mDisplay).canHostTasks();
- dc.onDisplayInfoChangeApplied();
- assertFalse(dc.mWmService.mDisplayWindowSettings.shouldShowSystemDecorsLocked(dc));
-
- doReturn(true).when(dc.mDisplay).canHostTasks();
- dc.onDisplayInfoChangeApplied();
- assertTrue(dc.mWmService.mDisplayWindowSettings.shouldShowSystemDecorsLocked(dc));
- }
-
private void removeRootTaskTests(Runnable runnable) {
final TaskDisplayArea taskDisplayArea = mRootWindowContainer.getDefaultTaskDisplayArea();
final Task rootTask1 = taskDisplayArea.createRootTask(WINDOWING_MODE_FULLSCREEN,
diff --git a/services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java b/services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java
index 8a7e7434e604..2c6884e7a35a 100644
--- a/services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java
@@ -52,7 +52,7 @@ import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.reset;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.verifyZeroInteractions;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
import static java.lang.Integer.MAX_VALUE;
@@ -1293,7 +1293,7 @@ public class RecentTasksTest extends WindowTestsBase {
// Add secondTask to top again
mRecentTasks.add(secondTask);
- verifyZeroInteractions(controller);
+ verifyNoMoreInteractions(controller);
}
@Test
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java
index 59ee2f5c8e9f..5624677779a2 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java
@@ -23,9 +23,6 @@ import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
import static android.permission.flags.Flags.FLAG_SENSITIVE_NOTIFICATION_APP_PROTECTION;
import static android.view.Display.DEFAULT_DISPLAY;
import static android.view.InsetsSource.ID_IME;
-import static android.view.Surface.ROTATION_0;
-import static android.view.Surface.ROTATION_270;
-import static android.view.Surface.ROTATION_90;
import static android.view.WindowInsets.Type.ime;
import static android.view.WindowInsets.Type.navigationBars;
import static android.view.WindowInsets.Type.statusBars;
@@ -88,7 +85,6 @@ import static org.mockito.Mockito.when;
import android.content.ContentResolver;
import android.content.res.CompatibilityInfo;
import android.content.res.Configuration;
-import android.graphics.Matrix;
import android.graphics.Point;
import android.graphics.Rect;
import android.graphics.Region;
@@ -673,56 +669,6 @@ public class WindowStateTests extends WindowTestsBase {
}
@Test
- public void testSeamlesslyRotateWindow() {
- final WindowState app = newWindowBuilder("app", TYPE_APPLICATION).build();
- final SurfaceControl.Transaction t = spy(StubTransaction.class);
-
- makeWindowVisible(app);
- app.mSurfaceControl = mock(SurfaceControl.class);
- final Rect frame = app.getFrame();
- frame.set(10, 20, 60, 80);
- app.updateSurfacePosition(t);
- assertTrue(app.mLastSurfacePosition.equals(frame.left, frame.top));
- app.seamlesslyRotateIfAllowed(t, ROTATION_0, ROTATION_90, true /* requested */);
- assertTrue(app.mSeamlesslyRotated);
-
- // Verify we un-rotate the window state surface.
- final Matrix matrix = new Matrix();
- // Un-rotate 90 deg.
- matrix.setRotate(270);
- // Translate it back to origin.
- matrix.postTranslate(0, mDisplayInfo.logicalWidth);
- verify(t).setMatrix(eq(app.mSurfaceControl), eq(matrix), any(float[].class));
-
- // Verify we update the position as well.
- final float[] curSurfacePos = {app.mLastSurfacePosition.x, app.mLastSurfacePosition.y};
- matrix.mapPoints(curSurfacePos);
- verify(t).setPosition(eq(app.mSurfaceControl), eq(curSurfacePos[0]), eq(curSurfacePos[1]));
-
- app.finishSeamlessRotation(t);
- assertFalse(app.mSeamlesslyRotated);
- assertNull(app.mPendingSeamlessRotate);
-
- // Simulate the case with deferred layout and animation.
- app.resetSurfacePositionForAnimationLeash(t);
- clearInvocations(t);
- mWm.mWindowPlacerLocked.deferLayout();
- app.updateSurfacePosition(t);
- // Because layout is deferred, the position should keep the reset value.
- assertTrue(app.mLastSurfacePosition.equals(0, 0));
-
- app.seamlesslyRotateIfAllowed(t, ROTATION_0, ROTATION_270, true /* requested */);
- // The last position must be updated so the surface can be unrotated properly.
- assertTrue(app.mLastSurfacePosition.equals(frame.left, frame.top));
- matrix.setRotate(90);
- matrix.postTranslate(mDisplayInfo.logicalHeight, 0);
- curSurfacePos[0] = frame.left;
- curSurfacePos[1] = frame.top;
- matrix.mapPoints(curSurfacePos);
- verify(t).setPosition(eq(app.mSurfaceControl), eq(curSurfacePos[0]), eq(curSurfacePos[1]));
- }
-
- @Test
public void testVisibilityChangeSwitchUser() {
final WindowState window = newWindowBuilder("app", TYPE_APPLICATION).build();
window.mHasSurface = true;
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowTokenTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowTokenTests.java
index a02c3db1e636..8907a72e0b34 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowTokenTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowTokenTests.java
@@ -17,6 +17,8 @@
package com.android.server.wm;
import static android.view.InsetsSource.ID_IME;
+import static android.view.Surface.ROTATION_0;
+import static android.view.Surface.ROTATION_90;
import static android.view.WindowManager.LayoutParams.FIRST_SUB_WINDOW;
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION;
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
@@ -35,16 +37,19 @@ import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.clearInvocations;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.content.res.Configuration;
+import android.graphics.Matrix;
import android.os.Bundle;
import android.os.IBinder;
import android.platform.test.annotations.EnableFlags;
import android.platform.test.annotations.Presubmit;
+import android.view.SurfaceControl;
import android.view.WindowInsets;
import android.window.WindowContext;
@@ -335,6 +340,31 @@ public class WindowTokenTests extends WindowTestsBase {
}
@Test
+ public void testSeamlesslyRotate() {
+ final SurfaceControl.Transaction t = mTransaction;
+ final TestWindowToken token = createTestWindowToken(0, mDisplayContent);
+ token.mLastSurfacePosition.x = 10;
+ token.mLastSurfacePosition.y = 20;
+ final SeamlessRotator rotator = new SeamlessRotator(ROTATION_0, ROTATION_90,
+ mDisplayContent.getDisplayInfo(), false /* applyFixedTransformationHint */);
+ clearInvocations(t);
+ rotator.unrotate(t, token);
+
+ // Verify surface is un-rotated.
+ final Matrix matrix = new Matrix();
+ // Un-rotate 90 deg.
+ matrix.setRotate(270);
+ // Translate it back to origin.
+ matrix.postTranslate(0, mDisplayInfo.logicalWidth);
+ verify(t).setMatrix(eq(token.mSurfaceControl), eq(matrix), any(float[].class));
+
+ final float[] curSurfacePos = {token.mLastSurfacePosition.x, token.mLastSurfacePosition.y};
+ matrix.mapPoints(curSurfacePos);
+ verify(t).setPosition(eq(token.mSurfaceControl),
+ eq(curSurfacePos[0]), eq(curSurfacePos[1]));
+ }
+
+ @Test
@EnableFlags(Flags.FLAG_REPARENT_WINDOW_TOKEN_API)
public void onDisplayChanged_differentDisplay_reparented() {
final TestWindowToken token = createTestWindowToken(0, mDisplayContent);
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowTracingLegacyTest.java b/services/tests/wmtests/src/com/android/server/wm/WindowTracingLegacyTest.java
index a1d35a7d447c..0749c0b94537 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowTracingLegacyTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowTracingLegacyTest.java
@@ -22,7 +22,7 @@ import static com.android.dx.mockito.inline.extended.ExtendedMockito.doAnswer;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.times;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
-import static com.android.dx.mockito.inline.extended.ExtendedMockito.verifyZeroInteractions;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.verifyNoMoreInteractions;
import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;
@@ -118,7 +118,7 @@ public class WindowTracingLegacyTest {
@Test
public void trace_discared_whenNotTracing() {
mWindowTracing.logState("where");
- verifyZeroInteractions(mWmMock);
+ verifyNoMoreInteractions(mWmMock);
}
@Test
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowTracingPerfettoTest.java b/services/tests/wmtests/src/com/android/server/wm/WindowTracingPerfettoTest.java
index 9367941e32a3..3da279bb9663 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowTracingPerfettoTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowTracingPerfettoTest.java
@@ -22,7 +22,7 @@ import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentat
import static com.android.dx.mockito.inline.extended.ExtendedMockito.times;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
-import static com.android.dx.mockito.inline.extended.ExtendedMockito.verifyZeroInteractions;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.verifyNoMoreInteractions;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
@@ -128,7 +128,7 @@ public class WindowTracingPerfettoTest {
@Test
public void trace_ignoresLogStateCalls_ifTracingIsDisabled() {
sWindowTracing.logState("where");
- verifyZeroInteractions(sWmMock);
+ verifyNoMoreInteractions(sWmMock);
}
@Test
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index 14d567d141cb..2983e4442a78 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -691,7 +691,7 @@ public class TelephonyManager {
case UNKNOWN:
modemCount = 1;
// check for voice and data support, 0 if not supported
- if (!isVoiceCapable() && !isSmsCapable() && !isDataCapable()) {
+ if (!isDeviceVoiceCapable() && !isSmsCapable() && !isDataCapable()) {
modemCount = 0;
}
break;
@@ -2814,7 +2814,7 @@ public class TelephonyManager {
*/
@RequiresFeature(PackageManager.FEATURE_TELEPHONY)
public int getPhoneType() {
- if (!isVoiceCapable() && !isDataCapable()) {
+ if (!isDeviceVoiceCapable() && !isDataCapable()) {
return PHONE_TYPE_NONE;
}
return getCurrentPhoneType();
diff --git a/tests/TrustTests/src/android/trust/test/GrantAndRevokeTrustTest.kt b/tests/TrustTests/src/android/trust/test/GrantAndRevokeTrustTest.kt
index 0c3c7e2af6f2..e868a6cc5a80 100644
--- a/tests/TrustTests/src/android/trust/test/GrantAndRevokeTrustTest.kt
+++ b/tests/TrustTests/src/android/trust/test/GrantAndRevokeTrustTest.kt
@@ -34,7 +34,7 @@ import org.junit.Rule
import org.junit.Test
import org.junit.rules.RuleChain
import org.junit.runner.RunWith
-import org.mockito.Mockito.verifyZeroInteractions
+import org.mockito.Mockito.verifyNoMoreInteractions
/**
* Test for testing revokeTrust & grantTrust for non-renewable trust.
@@ -120,7 +120,7 @@ class GrantAndRevokeTrustTest {
trustAgentRule.agent.grantTrust(GRANT_MESSAGE, 0, 0, callback)
await()
- verifyZeroInteractions(callback)
+ verifyNoMoreInteractions(callback)
}
companion object {
diff --git a/tests/UsbTests/src/com/android/server/usb/UsbServiceTest.java b/tests/UsbTests/src/com/android/server/usb/UsbServiceTest.java
index f5d4b0c5e345..cc7eebca1d00 100644
--- a/tests/UsbTests/src/com/android/server/usb/UsbServiceTest.java
+++ b/tests/UsbTests/src/com/android/server/usb/UsbServiceTest.java
@@ -33,7 +33,7 @@ import static org.mockito.ArgumentMatchers.isNull;
import static org.mockito.Mockito.clearInvocations;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.verifyZeroInteractions;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
import static org.mockito.Mockito.when;
import android.content.Context;
@@ -120,7 +120,7 @@ public class UsbServiceTest {
verify(mUsbPortManager).enableUsbData(TEST_PORT_ID,
enable, TEST_TRANSACTION_ID, mCallback, null);
- verifyZeroInteractions(mCallback);
+ verifyNoMoreInteractions(mCallback);
clearInvocations(mUsbPortManager);
clearInvocations(mCallback);
@@ -131,7 +131,7 @@ public class UsbServiceTest {
assertFalse(mUsbService.enableUsbDataInternal(TEST_PORT_ID, enable,
TEST_TRANSACTION_ID, mCallback, requester, isInternalRequest));
- verifyZeroInteractions(mUsbPortManager);
+ verifyNoMoreInteractions(mUsbPortManager);
verify(mCallback).onOperationComplete(USB_OPERATION_ERROR_INTERNAL);
clearInvocations(mUsbPortManager);
@@ -188,7 +188,7 @@ public class UsbServiceTest {
mUsbService.enableUsbDataWhileDockedInternal(TEST_PORT_ID, TEST_TRANSACTION_ID,
mCallback, TEST_SECOND_CALLER_ID, false);
- verifyZeroInteractions(mUsbPortManager);
+ verifyNoMoreInteractions(mUsbPortManager);
verify(mCallback).onOperationComplete(USB_OPERATION_ERROR_INTERNAL);
}
@@ -203,7 +203,7 @@ public class UsbServiceTest {
verify(mUsbPortManager).enableUsbDataWhileDocked(TEST_PORT_ID, TEST_TRANSACTION_ID,
mCallback, null);
- verifyZeroInteractions(mCallback);
+ verifyNoMoreInteractions(mCallback);
}
/**