summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--AconfigFlags.bp2
-rw-r--r--api/api.go5
-rw-r--r--core/api/current.txt1
-rw-r--r--core/api/system-current.txt40
-rw-r--r--core/api/test-current.txt43
-rw-r--r--core/java/android/app/ActivityOptions.java3
-rw-r--r--core/java/android/app/INotificationManager.aidl1
-rw-r--r--core/java/android/app/NotificationManager.java20
-rw-r--r--core/java/android/app/PropertyInvalidatedCache.java38
-rw-r--r--core/java/android/companion/virtual/VirtualDeviceInternal.java15
-rw-r--r--core/java/android/companion/virtual/VirtualDeviceManager.java10
-rw-r--r--core/java/android/companion/virtual/VirtualDeviceParams.java60
-rw-r--r--core/java/android/companion/virtual/flags/flags.aconfig8
-rw-r--r--core/java/android/content/Intent.java13
-rw-r--r--core/java/android/content/pm/LauncherApps.java6
-rw-r--r--core/java/android/database/sqlite/SQLiteOpenHelper.java140
-rw-r--r--core/java/android/database/sqlite/flags.aconfig9
-rw-r--r--core/java/android/hardware/contexthub/HubEndpoint.java291
-rw-r--r--core/java/android/hardware/devicestate/feature/flags.aconfig14
-rw-r--r--core/java/android/hardware/display/VirtualDisplayConfig.java4
-rw-r--r--core/java/android/hardware/input/VirtualStylus.java3
-rw-r--r--core/java/android/hardware/input/VirtualStylusButtonEvent.java4
-rw-r--r--core/java/android/hardware/input/VirtualStylusConfig.java4
-rw-r--r--core/java/android/hardware/input/VirtualStylusMotionEvent.java4
-rw-r--r--core/java/android/hardware/input/input_framework.aconfig7
-rw-r--r--core/java/android/os/IpcDataCache.java70
-rw-r--r--core/java/android/os/Looper.java42
-rw-r--r--core/java/android/os/health/SystemHealthManager.java12
-rw-r--r--core/java/android/provider/Settings.java28
-rw-r--r--core/java/android/security/flags.aconfig9
-rw-r--r--core/java/android/service/notification/StatusBarNotification.java63
-rw-r--r--core/java/android/service/settings/preferences/SettingsPreferenceValue.java103
-rw-r--r--core/java/android/text/Layout.java6
-rw-r--r--core/java/android/view/InsetsAnimationControlImpl.java19
-rw-r--r--core/java/android/view/SurfaceControl.java4
-rw-r--r--core/java/android/view/SurfaceView.java2
-rw-r--r--core/java/android/window/flags/lse_desktop_experience.aconfig17
-rw-r--r--core/java/com/android/internal/accessibility/dialog/AccessibilityTargetHelper.java3
-rw-r--r--core/java/com/android/internal/accessibility/util/ShortcutUtils.java45
-rw-r--r--core/java/com/android/internal/widget/NotificationProgressBar.java19
-rw-r--r--core/res/res/values/attrs.xml2
-rw-r--r--core/tests/coretests/src/android/app/NotificationManagerTest.java44
-rw-r--r--core/tests/coretests/src/android/os/SystemHealthManagerUnitTest.java155
-rw-r--r--core/tests/coretests/src/android/service/notification/StatusBarNotificationTest.java33
-rw-r--r--core/tests/coretests/src/android/text/LayoutTest.java49
-rw-r--r--core/tests/coretests/src/com/android/internal/accessibility/util/ShortcutUtilsTest.java112
-rw-r--r--core/tests/coretests/src/com/android/internal/widget/NotificationProgressBarTest.java179
-rw-r--r--data/etc/preinstalled-packages-platform.xml7
-rw-r--r--libs/WindowManager/Shell/Android.bp2
-rw-r--r--libs/WindowManager/Shell/AndroidManifest.xml2
-rw-r--r--libs/WindowManager/Shell/aconfig/Android.bp1
-rw-r--r--libs/WindowManager/Shell/aconfig/automotive.aconfig11
-rw-r--r--libs/WindowManager/Shell/aconfig/multitasking.aconfig7
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/appzoomout/AppZoomOutController.java11
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/automotive/AutoTaskStackControllerImpl.kt10
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/common/ComponentUtils.kt40
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayImeController.java15
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitScreenUtils.java29
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java5
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt49
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksTransitionObserver.kt44
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java4
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransitionController.java14
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipMotionHelper.java14
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipScheduler.java33
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTransition.java59
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTransitionState.java20
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/recents/IRecentsAnimationRunner.aidl6
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentTasksController.java8
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentsShellCommandHandler.kt51
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentsTransitionHandler.java5
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java15
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java8
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/taskview/TaskViewTaskController.java5
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultMixedHandler.java6
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java63
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecoration.java31
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/MultiDisplayVeiledResizeTaskPositioner.kt293
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/VeiledResizeTaskPositioner.java5
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/WindowDecoration.java2
-rw-r--r--libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/functional/EnterDesktopWithDragExistingWindowsTest.kt27
-rw-r--r--libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/EnterDesktopWithAppHandleMenuExistingWindows.kt70
-rw-r--r--libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/EnterDesktopWithDragExistingWindows.kt74
-rw-r--r--libs/WindowManager/Shell/tests/e2e/splitscreen/scenarios/src/com/android/wm/shell/scenarios/CopyContentInSplit.kt4
-rw-r--r--libs/WindowManager/Shell/tests/e2e/splitscreen/scenarios/src/com/android/wm/shell/scenarios/DismissSplitScreenByDivider.kt4
-rw-r--r--libs/WindowManager/Shell/tests/e2e/splitscreen/scenarios/src/com/android/wm/shell/scenarios/DismissSplitScreenByGoHome.kt4
-rw-r--r--libs/WindowManager/Shell/tests/e2e/splitscreen/scenarios/src/com/android/wm/shell/scenarios/DragDividerToResize.kt4
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/utils/RecentTasksUtils.kt27
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksControllerTest.kt25
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip2/phone/PipSchedulerTest.java21
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip2/phone/PipTransitionStateTest.java18
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/recents/RecentsTransitionHandlerTest.java4
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModelTests.kt54
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModelTestsBase.kt15
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorationTests.java56
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/MultiDisplayVeiledResizeTaskPositionerTest.kt611
-rw-r--r--location/java/android/location/Geocoder.java7
-rw-r--r--media/java/android/media/quality/MediaQualityManager.java53
-rw-r--r--media/tests/MediaRouter/Android.bp2
-rw-r--r--native/android/TEST_MAPPING4
-rw-r--r--native/android/libandroid.map.txt1
-rw-r--r--native/android/system_health.cpp60
-rw-r--r--native/android/tests/system_health/Android.bp66
-rw-r--r--native/android/tests/system_health/NativeSystemHealthUnitTest.cpp231
-rw-r--r--packages/CrashRecovery/framework/Android.bp9
-rw-r--r--packages/CrashRecovery/services/module/java/com/android/server/RescueParty.java132
-rw-r--r--packages/CrashRecovery/services/module/java/com/android/util/ArrayUtils.java73
-rw-r--r--packages/CrashRecovery/services/module/java/com/android/util/FileUtils.java11
-rw-r--r--packages/CrashRecovery/services/module/java/com/android/util/XmlUtils.java53
-rw-r--r--packages/SettingsLib/CardPreference/res/drawable/settingslib_card_preference_background.xml28
-rw-r--r--packages/SettingsLib/CardPreference/res/layout/settingslib_expressive_preference_card.xml35
-rw-r--r--packages/SettingsLib/CardPreference/res/values/styles_expressive.xml4
-rw-r--r--packages/SettingsLib/Metadata/processor/src/com/android/settingslib/metadata/PreferenceScreenAnnotationProcessor.kt10
-rw-r--r--packages/SettingsLib/SettingsTheme/res/values/strings.xml2
-rw-r--r--packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/framework/common/BytesFormatter.kt17
-rw-r--r--packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/model/app/AppStorageRepository.kt92
-rw-r--r--packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/AppStorageSize.kt40
-rw-r--r--packages/SettingsLib/SpaPrivileged/tests/src/com/android/settingslib/spaprivileged/model/app/AppStorageRepositoryTest.kt94
-rw-r--r--packages/SettingsLib/SpaPrivileged/tests/src/com/android/settingslib/spaprivileged/template/app/AppStorageSizeTest.kt85
-rw-r--r--packages/SettingsLib/aconfig/settingslib.aconfig10
-rw-r--r--packages/SettingsLib/search/processor-src/com/android/settingslib/search/IndexableProcessor.java7
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/bluetooth/CsipDeviceManager.java2
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothProfileManager.java16
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/notification/data/repository/FakeZenModeRepository.kt3
-rw-r--r--packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java1
-rw-r--r--packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java2
-rw-r--r--packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java1
-rw-r--r--packages/SystemUI/Android.bp3
-rw-r--r--packages/SystemUI/AndroidManifest.xml6
-rw-r--r--packages/SystemUI/accessibility/accessibilitymenu/AndroidManifest.xml5
-rw-r--r--packages/SystemUI/aconfig/Android.bp1
-rw-r--r--packages/SystemUI/aconfig/accessibility.aconfig20
-rw-r--r--packages/SystemUI/aconfig/systemui.aconfig17
-rw-r--r--packages/SystemUI/animation/lib/src/com/android/systemui/animation/ViewUIComponent.java12
-rw-r--r--packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/BouncerContent.kt2
-rw-r--r--packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/PatternBouncer.kt228
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/floatingmenu/MenuListViewTouchHandlerTest.java3
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/floatingmenu/MenuViewTest.java134
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/hearingaid/HearingDevicesDialogDelegateTest.java4
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/clipboardoverlay/ClipboardImageLoaderTest.kt65
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/domain/autoaddable/A11yShortcutAutoAddableListTest.kt26
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/domain/autoaddable/ReduceBrightColorsAutoAddableTest.kt121
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/domain/interactor/AccessibilityTilesInteractorTest.kt39
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/HearingDevicesTileTest.java15
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/hearingdevices/domain/interactor/HearingDevicesTileDataInteractorTest.kt15
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/shade/display/StatusBarTouchShadeDisplayPolicyTest.kt70
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/shade/domain/interactor/ShadeExpandedStateInteractorTest.kt6
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/FakeHomeStatusBarViewModel.kt2
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/HomeStatusBarViewModelImplTest.kt92
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/StatusBarOperatorNameViewModelTest.kt17
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/wallpapers/GradientColorWallpaperTest.kt85
-rw-r--r--packages/SystemUI/res/layout/qs_footer_impl.xml5
-rw-r--r--packages/SystemUI/res/layout/volume_dialog_slider.xml4
-rw-r--r--packages/SystemUI/res/values/strings.xml4
-rw-r--r--packages/SystemUI/shared/src/com/android/systemui/shared/system/RecentsAnimationListener.java3
-rw-r--r--packages/SystemUI/src/com/android/systemui/KairosActivatable.kt212
-rw-r--r--packages/SystemUI/src/com/android/systemui/accessibility/MagnificationImpl.java7
-rw-r--r--packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationController.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuAnimationController.java33
-rw-r--r--packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuInfoRepository.java6
-rw-r--r--packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuListViewTouchHandler.java3
-rw-r--r--packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuView.java25
-rw-r--r--packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuViewAppearance.java35
-rw-r--r--packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuViewLayer.java55
-rw-r--r--packages/SystemUI/src/com/android/systemui/accessibility/hearingaid/HearingDevicesDialogDelegate.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/accessibility/hearingaid/HearingDevicesDialogReceiver.java6
-rw-r--r--packages/SystemUI/src/com/android/systemui/accessibility/qs/QSAccessibilityModule.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardImageLoader.kt19
-rw-r--r--packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/inputdevice/tutorial/ui/composable/ActionTutorialContent.kt60
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/composable/ShortcutHelper.kt3
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/controls/ui/animation/ColorSchemeTransition.kt92
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/BuildTextView.kt45
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/pipeline/dagger/BaseAutoAddableModule.kt9
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/pipeline/domain/autoaddable/A11yShortcutAutoAddableList.kt49
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/pipeline/domain/autoaddable/ReduceBrightColorsAutoAddable.kt72
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/pipeline/domain/interactor/AccessibilityTilesInteractor.kt15
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/HearingDevicesTile.java6
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/impl/hearingdevices/domain/interactor/HearingDevicesTileDataInteractor.kt4
-rw-r--r--packages/SystemUI/src/com/android/systemui/shade/display/ShadeDisplayPolicy.kt23
-rw-r--r--packages/SystemUI/src/com/android/systemui/shade/display/StatusBarTouchShadeDisplayPolicy.kt56
-rw-r--r--packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeDisplaysInteractor.kt20
-rw-r--r--packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeExpandedStateInteractor.kt68
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/core/StatusBarConnectedDisplays.kt (renamed from packages/SystemUI/src/com/android/systemui/statusbar/core/StatusBarConntectedDisplays.kt)2
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/core/StatusBarRootModernization.kt3
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java12
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewController.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/CarrierConfigRepository.kt7
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/CarrierConfigRepositoryImpl.kt8
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/MobileConnectionsRepository.kt4
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/MobileRepositorySwitcher.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/demo/DemoMobileConnectionsRepository.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionsRepositoryImpl.kt21
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconsInteractor.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/composable/StatusBarRoot.kt15
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/HomeStatusBarViewModel.kt13
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/StatusBarOperatorNameViewModel.kt7
-rw-r--r--packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/composable/TutorialSelectionScreen.kt4
-rw-r--r--packages/SystemUI/src/com/android/systemui/util/DelayableMarqueeTextView.kt24
-rw-r--r--packages/SystemUI/src/com/android/systemui/volume/dialog/ringer/ui/binder/VolumeDialogRingerViewBinder.kt31
-rw-r--r--packages/SystemUI/src/com/android/systemui/volume/dialog/ringer/ui/viewmodel/RingerViewModel.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/volume/dialog/ringer/ui/viewmodel/VolumeDialogRingerDrawerViewModel.kt27
-rw-r--r--packages/SystemUI/src/com/android/systemui/volume/dialog/shared/VolumeDialogLogger.kt46
-rw-r--r--packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/VolumeDialogSliderViewBinder.kt10
-rw-r--r--packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/viewmodel/VolumeDialogSliderIconProvider.kt40
-rw-r--r--packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/viewmodel/VolumeDialogSliderViewModel.kt8
-rw-r--r--packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/viewmodel/VolumeDialogSlidersViewModel.kt8
-rw-r--r--packages/SystemUI/src/com/android/systemui/volume/panel/component/volume/slider/ui/viewmodel/AudioStreamSliderViewModel.kt10
-rw-r--r--packages/SystemUI/src/com/android/systemui/wallpapers/GradientColorWallpaper.kt62
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuAnimationControllerTest.java4
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuViewAppearanceTest.java72
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuViewLayerTest.java40
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewControllerTest.kt50
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionsRepositoryTest.kt27
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/shade/data/repository/ShadeDisplaysRepositoryKosmos.kt14
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/shade/domain/interactor/ShadeDisplaysInteractorKosmos.kt2
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/shade/domain/interactor/ShadeInteractorKosmos.kt17
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/FakeCarrierConfigRepository.kt9
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/FakeMobileIconsInteractor.kt2
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/volume/dialog/sliders/dagger/VolumeDialogSliderComponentKosmos.kt2
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/volume/dialog/sliders/ui/viewmodel/VolumeDialogSliderViewModelKosmos.kt4
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/volume/dialog/sliders/ui/viewmodel/VolumeDialogSlidersViewModelKosmos.kt2
-rw-r--r--packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/BuildScope.kt28
-rw-r--r--packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/Events.kt10
-rw-r--r--packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/Filter.kt8
-rw-r--r--packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/GroupBy.kt6
-rw-r--r--packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/Incremental.kt4
-rw-r--r--packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/Merge.kt18
-rw-r--r--packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/State.kt12
-rw-r--r--packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/StateScope.kt60
-rw-r--r--packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/Transactional.kt4
-rw-r--r--packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/internal/store/MapK.kt8
-rw-r--r--packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/util/MapPatch.kt2
-rw-r--r--packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/util/Maybe.kt2
-rw-r--r--packages/Vcn/service-b/src/com/android/server/vcn/Vcn.java2
-rw-r--r--packages/Vcn/service-b/src/com/android/server/vcn/VcnNetworkProvider.java8
-rw-r--r--ravenwood/texts/ravenwood-framework-policies.txt2
-rwxr-xr-xravenwood/tools/hoststubgen/scripts/dump-jar29
-rw-r--r--ravenwood/tools/hoststubgen/src/com/android/hoststubgen/filters/TextFileFilterPolicyParser.kt28
-rwxr-xr-xravenwood/tools/hoststubgen/test-tiny-framework/diff-and-update-golden.sh9
-rwxr-xr-xravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework-dump-test.py20
-rw-r--r--services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java21
-rw-r--r--services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java20
-rw-r--r--services/companion/java/com/android/server/companion/virtual/VirtualDeviceImpl.java59
-rw-r--r--services/companion/java/com/android/server/companion/virtual/VirtualDeviceManagerService.java55
-rw-r--r--services/core/java/com/android/server/am/ActivityManagerConstants.java4
-rw-r--r--services/core/java/com/android/server/am/flags.aconfig10
-rw-r--r--services/core/java/com/android/server/appop/DiscreteOpsDbHelper.java7
-rw-r--r--services/core/java/com/android/server/display/feature/DisplayManagerFlags.java14
-rw-r--r--services/core/java/com/android/server/display/feature/display_flags.aconfig8
-rw-r--r--services/core/java/com/android/server/hdmi/PowerStatusMonitorActionFromPlayback.java43
-rw-r--r--services/core/java/com/android/server/inputmethod/ImeVisibilityStateComputer.java24
-rw-r--r--services/core/java/com/android/server/inputmethod/InputMethodManagerService.java31
-rw-r--r--services/core/java/com/android/server/location/contexthub/ContextHubServiceUtil.java4
-rw-r--r--services/core/java/com/android/server/location/fudger/LocationFudger.java28
-rw-r--r--services/core/java/com/android/server/locksettings/LockSettingsService.java5
-rw-r--r--services/core/java/com/android/server/media/quality/MediaQualityService.java208
-rw-r--r--services/core/java/com/android/server/notification/NotificationManagerService.java95
-rw-r--r--services/core/java/com/android/server/notification/NotificationRecordLogger.java15
-rw-r--r--services/core/java/com/android/server/notification/PreferencesHelper.java75
-rw-r--r--services/core/java/com/android/server/pm/DeletePackageHelper.java26
-rw-r--r--services/core/java/com/android/server/pm/InstallPackageHelper.java5
-rw-r--r--services/core/java/com/android/server/pm/PackageSetting.java6
-rw-r--r--services/core/java/com/android/server/pm/UserManagerService.java9
-rw-r--r--services/core/java/com/android/server/policy/DeviceStateProviderImpl.java60
-rw-r--r--services/core/java/com/android/server/power/TEST_MAPPING12
-rw-r--r--services/core/java/com/android/server/security/authenticationpolicy/AuthenticationPolicyService.java13
-rw-r--r--services/core/java/com/android/server/selinux/QuotaExceededException.java23
-rw-r--r--services/core/java/com/android/server/selinux/SelinuxAuditLogsCollector.java147
-rw-r--r--services/core/java/com/android/server/selinux/SelinuxAuditLogsJob.java8
-rw-r--r--services/core/java/com/android/server/statusbar/StatusBarManagerService.java23
-rw-r--r--services/core/java/com/android/server/timezonedetector/Environment.java80
-rw-r--r--services/core/java/com/android/server/timezonedetector/EnvironmentImpl.java10
-rw-r--r--services/core/java/com/android/server/timezonedetector/NotifyingTimeZoneChangeListener.java20
-rw-r--r--services/core/java/com/android/server/timezonedetector/TimeZoneDetectorStrategyImpl.java55
-rw-r--r--services/core/java/com/android/server/wm/ActivityRecord.java189
-rw-r--r--services/core/java/com/android/server/wm/ActivityStarter.java1
-rw-r--r--services/core/java/com/android/server/wm/AppCompatAspectRatioPolicy.java11
-rw-r--r--services/core/java/com/android/server/wm/AppCompatController.java30
-rw-r--r--services/core/java/com/android/server/wm/AppCompatOrientationOverrides.java2
-rw-r--r--services/core/java/com/android/server/wm/AppCompatOrientationPolicy.java2
-rw-r--r--services/core/java/com/android/server/wm/AppCompatOverrides.java8
-rw-r--r--services/core/java/com/android/server/wm/AppCompatSizeCompatModePolicy.java120
-rw-r--r--services/core/java/com/android/server/wm/AppCompatUtils.java6
-rw-r--r--services/core/java/com/android/server/wm/BackNavigationController.java24
-rw-r--r--services/core/java/com/android/server/wm/DesktopAppCompatAspectRatioPolicy.java6
-rw-r--r--services/core/java/com/android/server/wm/DesktopModeBoundsCalculator.java6
-rw-r--r--services/core/java/com/android/server/wm/DisplayContent.java20
-rw-r--r--services/core/java/com/android/server/wm/DragDropController.java4
-rw-r--r--services/core/java/com/android/server/wm/DragState.java39
-rw-r--r--services/core/java/com/android/server/wm/LaunchParamsController.java3
-rw-r--r--services/core/java/com/android/server/wm/RootWindowContainer.java32
-rw-r--r--services/core/java/com/android/server/wm/SnapshotPersistQueue.java7
-rw-r--r--services/core/java/com/android/server/wm/TaskLaunchParamsModifier.java6
-rw-r--r--services/core/java/com/android/server/wm/TransparentPolicy.java2
-rw-r--r--services/core/java/com/android/server/wm/WindowManagerService.java53
-rw-r--r--services/core/java/com/android/server/wm/WindowState.java83
-rw-r--r--services/core/java/com/android/server/wm/WindowStateAnimator.java66
-rw-r--r--services/core/java/com/android/server/wm/WindowSurfacePlacer.java1
-rw-r--r--services/core/xsd/device-state-config/device-state-config.xsd9
-rw-r--r--services/core/xsd/device-state-config/schema/current.txt7
-rw-r--r--services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java2
-rw-r--r--services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/ImeVisibilityStateComputerTest.java6
-rw-r--r--services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/UserDataRepositoryTest.java10
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/location/fudger/LocationFudgerTest.java27
-rw-r--r--services/tests/selinux/src/com/android/server/selinux/SelinuxAuditLogsCollectorTest.java38
-rw-r--r--services/tests/selinux/src/com/android/server/selinux/SelinuxAuditLogsJobTest.java8
-rw-r--r--services/tests/servicestests/src/com/android/server/accessibility/AccessibilityManagerServiceTest.java66
-rw-r--r--services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDevicePlaybackTest.java3
-rw-r--r--services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest1.java30
-rw-r--r--services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest2.java24
-rw-r--r--services/tests/servicestests/src/com/android/server/policy/DeviceStateProviderImplTest.java40
-rw-r--r--services/tests/servicestests/src/com/android/server/security/authenticationpolicy/AuthenticationPolicyServiceTest.java57
-rw-r--r--services/tests/timetests/src/com/android/server/timezonedetector/FakeEnvironment.java141
-rw-r--r--services/tests/timetests/src/com/android/server/timezonedetector/NotifyingTimeZoneChangeListenerTest.java114
-rw-r--r--services/tests/timetests/src/com/android/server/timezonedetector/TimeZoneDetectorStrategyImplTest.java110
-rw-r--r--services/tests/uiservicestests/src/com/android/server/notification/NotificationAssistantsTest.java120
-rw-r--r--services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java118
-rw-r--r--services/tests/wmtests/Android.bp66
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java16
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/AppCompatActivityRobot.java12
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/AppCompatAspectRatioOverridesTest.java4
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/AppCompatLetterboxPolicyTest.java2
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/AppCompatOrientationOverridesTest.java2
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/AppCompatOrientationPolicyTest.java4
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/AppCompatUtilsTest.java6
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/CameraCompatFreeformPolicyTests.java2
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/DesktopAppCompatAspectRatioPolicyTests.java10
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/DesktopModeLaunchParamsModifierTests.java50
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java36
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/DualDisplayAreaGroupPolicyTest.java24
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/RootWindowContainerTests.java2
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java162
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/SystemServicesTestRule.java40
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/TaskFragmentTest.java8
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/TransparentPolicyTest.java2
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceTestSupport.kt112
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java3
338 files changed, 7458 insertions, 3600 deletions
diff --git a/AconfigFlags.bp b/AconfigFlags.bp
index a60ced5835ea..1a0afadd5fe2 100644
--- a/AconfigFlags.bp
+++ b/AconfigFlags.bp
@@ -467,7 +467,7 @@ java_aconfig_library {
"//apex_available:platform",
"com.android.art",
"com.android.art.debug",
- "com.android.btservices",
+ "com.android.bt",
"com.android.mediaprovider",
"com.android.permission",
],
diff --git a/api/api.go b/api/api.go
index cbdb7e81ab86..640773be0f9b 100644
--- a/api/api.go
+++ b/api/api.go
@@ -104,8 +104,9 @@ func (a *CombinedApis) DepsMutator(ctx android.BottomUpMutatorContext) {
}
func (a *CombinedApis) GenerateAndroidBuildActions(ctx android.ModuleContext) {
- ctx.WalkDeps(func(child, parent android.Module) bool {
- if _, ok := android.OtherModuleProvider(ctx, child, java.AndroidLibraryInfoProvider); ok && child.Name() != "framework-res" {
+ ctx.WalkDepsProxy(func(child, parent android.ModuleProxy) bool {
+ javaInfo, ok := android.OtherModuleProvider(ctx, child, java.JavaInfoProvider)
+ if ok && javaInfo.AndroidLibraryDependencyInfo != nil && child.Name() != "framework-res" {
// Stubs of BCP and SSCP libraries should not have any dependencies on apps
// This check ensures that we do not run into circular dependencies when UNBUNDLED_BUILD_TARGET_SDK_WITH_API_FINGERPRINT=true
ctx.ModuleErrorf(
diff --git a/core/api/current.txt b/core/api/current.txt
index b7f7a7f9e779..19f68eb0c787 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -38278,7 +38278,6 @@ package android.provider {
field public static final String ACTION_NOTIFICATION_LISTENER_DETAIL_SETTINGS = "android.settings.NOTIFICATION_LISTENER_DETAIL_SETTINGS";
field public static final String ACTION_NOTIFICATION_LISTENER_SETTINGS = "android.settings.ACTION_NOTIFICATION_LISTENER_SETTINGS";
field public static final String ACTION_NOTIFICATION_POLICY_ACCESS_SETTINGS = "android.settings.NOTIFICATION_POLICY_ACCESS_SETTINGS";
- field @FlaggedApi("android.provider.system_regional_preferences_api_enabled") public static final String ACTION_NUMBERING_SYSTEM_SETTINGS = "android.settings.NUMBERING_SYSTEM_SETTINGS";
field public static final String ACTION_PRINT_SETTINGS = "android.settings.ACTION_PRINT_SETTINGS";
field public static final String ACTION_PRIVACY_SETTINGS = "android.settings.PRIVACY_SETTINGS";
field public static final String ACTION_PROCESS_WIFI_EASY_CONNECT_URI = "android.settings.PROCESS_WIFI_EASY_CONNECT_URI";
diff --git a/core/api/system-current.txt b/core/api/system-current.txt
index 16c70174d5c2..ae5542be7548 100644
--- a/core/api/system-current.txt
+++ b/core/api/system-current.txt
@@ -3405,12 +3405,12 @@ package android.companion.virtual {
public final class VirtualDeviceManager {
method @NonNull @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE) public android.companion.virtual.VirtualDeviceManager.VirtualDevice createVirtualDevice(int, @NonNull android.companion.virtual.VirtualDeviceParams);
- method @FlaggedApi("android.companion.virtual.flags.persistent_device_id_api") @NonNull public java.util.Set<java.lang.String> getAllPersistentDeviceIds();
- method @FlaggedApi("android.companion.virtual.flags.persistent_device_id_api") @Nullable public CharSequence getDisplayNameForPersistentDeviceId(@NonNull String);
+ method @NonNull public java.util.Set<java.lang.String> getAllPersistentDeviceIds();
+ method @Nullable public CharSequence getDisplayNameForPersistentDeviceId(@NonNull String);
field public static final int LAUNCH_FAILURE_NO_ACTIVITY = 2; // 0x2
field public static final int LAUNCH_FAILURE_PENDING_INTENT_CANCELED = 1; // 0x1
field public static final int LAUNCH_SUCCESS = 0; // 0x0
- field @FlaggedApi("android.companion.virtual.flags.persistent_device_id_api") public static final String PERSISTENT_DEVICE_ID_DEFAULT = "default:0";
+ field public static final String PERSISTENT_DEVICE_ID_DEFAULT = "default:0";
}
public static interface VirtualDeviceManager.ActivityListener {
@@ -3432,7 +3432,7 @@ package android.companion.virtual {
public static class VirtualDeviceManager.VirtualDevice implements java.lang.AutoCloseable {
method public void addActivityListener(@NonNull java.util.concurrent.Executor, @NonNull android.companion.virtual.VirtualDeviceManager.ActivityListener);
- method @FlaggedApi("android.companion.virtual.flags.dynamic_policy") public void addActivityPolicyExemption(@NonNull android.content.ComponentName);
+ method public void addActivityPolicyExemption(@NonNull android.content.ComponentName);
method @FlaggedApi("android.companion.virtualdevice.flags.activity_control_api") public void addActivityPolicyExemption(@NonNull android.companion.virtual.ActivityPolicyExemption);
method public void addSoundEffectListener(@NonNull java.util.concurrent.Executor, @NonNull android.companion.virtual.VirtualDeviceManager.SoundEffectListener);
method public void close();
@@ -3448,7 +3448,7 @@ package android.companion.virtual {
method @Deprecated @NonNull public android.hardware.input.VirtualMouse createVirtualMouse(@NonNull android.hardware.display.VirtualDisplay, @NonNull String, int, int);
method @NonNull public android.hardware.input.VirtualNavigationTouchpad createVirtualNavigationTouchpad(@NonNull android.hardware.input.VirtualNavigationTouchpadConfig);
method @FlaggedApi("android.companion.virtualdevice.flags.virtual_rotary") @NonNull public android.hardware.input.VirtualRotaryEncoder createVirtualRotaryEncoder(@NonNull android.hardware.input.VirtualRotaryEncoderConfig);
- method @FlaggedApi("android.companion.virtual.flags.virtual_stylus") @NonNull public android.hardware.input.VirtualStylus createVirtualStylus(@NonNull android.hardware.input.VirtualStylusConfig);
+ method @NonNull public android.hardware.input.VirtualStylus createVirtualStylus(@NonNull android.hardware.input.VirtualStylusConfig);
method @NonNull public android.hardware.input.VirtualTouchscreen createVirtualTouchscreen(@NonNull android.hardware.input.VirtualTouchscreenConfig);
method @Deprecated @NonNull public android.hardware.input.VirtualTouchscreen createVirtualTouchscreen(@NonNull android.hardware.display.VirtualDisplay, @NonNull String, int, int);
method public int getDeviceId();
@@ -3458,10 +3458,10 @@ package android.companion.virtual {
method public void launchPendingIntent(int, @NonNull android.app.PendingIntent, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.IntConsumer);
method public void registerIntentInterceptor(@NonNull android.content.IntentFilter, @NonNull java.util.concurrent.Executor, @NonNull android.companion.virtual.VirtualDeviceManager.IntentInterceptorCallback);
method public void removeActivityListener(@NonNull android.companion.virtual.VirtualDeviceManager.ActivityListener);
- method @FlaggedApi("android.companion.virtual.flags.dynamic_policy") public void removeActivityPolicyExemption(@NonNull android.content.ComponentName);
+ method public void removeActivityPolicyExemption(@NonNull android.content.ComponentName);
method @FlaggedApi("android.companion.virtualdevice.flags.activity_control_api") public void removeActivityPolicyExemption(@NonNull android.companion.virtual.ActivityPolicyExemption);
method public void removeSoundEffectListener(@NonNull android.companion.virtual.VirtualDeviceManager.SoundEffectListener);
- method @FlaggedApi("android.companion.virtual.flags.dynamic_policy") public void setDevicePolicy(int, int);
+ method public void setDevicePolicy(int, int);
method @FlaggedApi("android.companion.virtualdevice.flags.activity_control_api") public void setDevicePolicy(int, int, int);
method @FlaggedApi("android.companion.virtual.flags.vdm_custom_ime") public void setDisplayImePolicy(int, int);
method public void setShowPointerIcon(boolean);
@@ -3481,7 +3481,7 @@ package android.companion.virtual {
method @Deprecated public int getDefaultNavigationPolicy();
method public int getDevicePolicy(int);
method @FlaggedApi("android.companion.virtualdevice.flags.device_aware_display_power") @NonNull public java.time.Duration getDimDuration();
- method @FlaggedApi("android.companion.virtual.flags.vdm_custom_home") @Nullable public android.content.ComponentName getHomeComponent();
+ method @Nullable public android.content.ComponentName getHomeComponent();
method @FlaggedApi("android.companion.virtual.flags.vdm_custom_ime") @Nullable public android.content.ComponentName getInputMethodComponent();
method public int getLockState();
method @Nullable public String getName();
@@ -3498,11 +3498,11 @@ package android.companion.virtual {
field public static final int LOCK_STATE_DEFAULT = 0; // 0x0
field @Deprecated public static final int NAVIGATION_POLICY_DEFAULT_ALLOWED = 0; // 0x0
field @Deprecated public static final int NAVIGATION_POLICY_DEFAULT_BLOCKED = 1; // 0x1
- field @FlaggedApi("android.companion.virtual.flags.dynamic_policy") public static final int POLICY_TYPE_ACTIVITY = 3; // 0x3
+ field public static final int POLICY_TYPE_ACTIVITY = 3; // 0x3
field public static final int POLICY_TYPE_AUDIO = 1; // 0x1
field @FlaggedApi("android.companion.virtualdevice.flags.activity_control_api") public static final int POLICY_TYPE_BLOCKED_ACTIVITY = 6; // 0x6
field @FlaggedApi("android.companion.virtual.flags.virtual_camera") public static final int POLICY_TYPE_CAMERA = 5; // 0x5
- field @FlaggedApi("android.companion.virtual.flags.cross_device_clipboard") public static final int POLICY_TYPE_CLIPBOARD = 4; // 0x4
+ field public static final int POLICY_TYPE_CLIPBOARD = 4; // 0x4
field @FlaggedApi("android.companion.virtualdevice.flags.default_device_camera_access_policy") public static final int POLICY_TYPE_DEFAULT_DEVICE_CAMERA_ACCESS = 7; // 0x7
field public static final int POLICY_TYPE_RECENTS = 2; // 0x2
field public static final int POLICY_TYPE_SENSORS = 0; // 0x0
@@ -3520,7 +3520,7 @@ package android.companion.virtual {
method @Deprecated @NonNull public android.companion.virtual.VirtualDeviceParams.Builder setBlockedCrossTaskNavigations(@NonNull java.util.Set<android.content.ComponentName>);
method @NonNull public android.companion.virtual.VirtualDeviceParams.Builder setDevicePolicy(int, int);
method @FlaggedApi("android.companion.virtualdevice.flags.device_aware_display_power") @NonNull public android.companion.virtual.VirtualDeviceParams.Builder setDimDuration(@NonNull java.time.Duration);
- method @FlaggedApi("android.companion.virtual.flags.vdm_custom_home") @NonNull public android.companion.virtual.VirtualDeviceParams.Builder setHomeComponent(@Nullable android.content.ComponentName);
+ method @NonNull public android.companion.virtual.VirtualDeviceParams.Builder setHomeComponent(@Nullable android.content.ComponentName);
method @FlaggedApi("android.companion.virtual.flags.vdm_custom_ime") @NonNull public android.companion.virtual.VirtualDeviceParams.Builder setInputMethodComponent(@Nullable android.content.ComponentName);
method @NonNull @RequiresPermission(value=android.Manifest.permission.ADD_ALWAYS_UNLOCKED_DISPLAY, conditional=true) public android.companion.virtual.VirtualDeviceParams.Builder setLockState(int);
method @NonNull public android.companion.virtual.VirtualDeviceParams.Builder setName(@NonNull String);
@@ -5332,13 +5332,13 @@ package android.hardware.display {
public final class VirtualDisplayConfig implements android.os.Parcelable {
method @FlaggedApi("android.companion.virtualdevice.flags.virtual_display_insets") @Nullable public android.view.DisplayCutout getDisplayCutout();
- method @FlaggedApi("android.companion.virtual.flags.vdm_custom_home") public boolean isHomeSupported();
+ method public boolean isHomeSupported();
method @FlaggedApi("com.android.window.flags.vdm_force_app_universal_resizable_api") public boolean isIgnoreActivitySizeRestrictions();
}
public static final class VirtualDisplayConfig.Builder {
method @FlaggedApi("android.companion.virtualdevice.flags.virtual_display_insets") @NonNull public android.hardware.display.VirtualDisplayConfig.Builder setDisplayCutout(@Nullable android.view.DisplayCutout);
- method @FlaggedApi("android.companion.virtual.flags.vdm_custom_home") @NonNull public android.hardware.display.VirtualDisplayConfig.Builder setHomeSupported(boolean);
+ method @NonNull public android.hardware.display.VirtualDisplayConfig.Builder setHomeSupported(boolean);
method @FlaggedApi("com.android.window.flags.vdm_force_app_universal_resizable_api") @NonNull public android.hardware.display.VirtualDisplayConfig.Builder setIgnoreActivitySizeRestrictions(boolean);
}
@@ -5970,13 +5970,13 @@ package android.hardware.input {
method @NonNull public android.hardware.input.VirtualRotaryEncoderScrollEvent.Builder setScrollAmount(@FloatRange(from=-1.0F, to=1.0f) float);
}
- @FlaggedApi("android.companion.virtual.flags.virtual_stylus") public class VirtualStylus implements java.io.Closeable {
+ public class VirtualStylus implements java.io.Closeable {
method public void close();
method public void sendButtonEvent(@NonNull android.hardware.input.VirtualStylusButtonEvent);
method public void sendMotionEvent(@NonNull android.hardware.input.VirtualStylusMotionEvent);
}
- @FlaggedApi("android.companion.virtual.flags.virtual_stylus") public final class VirtualStylusButtonEvent implements android.os.Parcelable {
+ public final class VirtualStylusButtonEvent implements android.os.Parcelable {
method public int describeContents();
method public int getAction();
method public int getButtonCode();
@@ -5989,7 +5989,7 @@ package android.hardware.input {
field @NonNull public static final android.os.Parcelable.Creator<android.hardware.input.VirtualStylusButtonEvent> CREATOR;
}
- @FlaggedApi("android.companion.virtual.flags.virtual_stylus") public static final class VirtualStylusButtonEvent.Builder {
+ public static final class VirtualStylusButtonEvent.Builder {
ctor public VirtualStylusButtonEvent.Builder();
method @NonNull public android.hardware.input.VirtualStylusButtonEvent build();
method @NonNull public android.hardware.input.VirtualStylusButtonEvent.Builder setAction(int);
@@ -5997,7 +5997,7 @@ package android.hardware.input {
method @NonNull public android.hardware.input.VirtualStylusButtonEvent.Builder setEventTimeNanos(long);
}
- @FlaggedApi("android.companion.virtual.flags.virtual_stylus") public final class VirtualStylusConfig extends android.hardware.input.VirtualInputDeviceConfig implements android.os.Parcelable {
+ public final class VirtualStylusConfig extends android.hardware.input.VirtualInputDeviceConfig implements android.os.Parcelable {
method public int describeContents();
method public int getHeight();
method public int getWidth();
@@ -6005,12 +6005,12 @@ package android.hardware.input {
field @NonNull public static final android.os.Parcelable.Creator<android.hardware.input.VirtualStylusConfig> CREATOR;
}
- @FlaggedApi("android.companion.virtual.flags.virtual_stylus") public static final class VirtualStylusConfig.Builder extends android.hardware.input.VirtualInputDeviceConfig.Builder<android.hardware.input.VirtualStylusConfig.Builder> {
+ public static final class VirtualStylusConfig.Builder extends android.hardware.input.VirtualInputDeviceConfig.Builder<android.hardware.input.VirtualStylusConfig.Builder> {
ctor public VirtualStylusConfig.Builder(@IntRange(from=1) int, @IntRange(from=1) int);
method @NonNull public android.hardware.input.VirtualStylusConfig build();
}
- @FlaggedApi("android.companion.virtual.flags.virtual_stylus") public final class VirtualStylusMotionEvent implements android.os.Parcelable {
+ public final class VirtualStylusMotionEvent implements android.os.Parcelable {
method public int describeContents();
method public int getAction();
method public long getEventTimeNanos();
@@ -6029,7 +6029,7 @@ package android.hardware.input {
field public static final int TOOL_TYPE_STYLUS = 2; // 0x2
}
- @FlaggedApi("android.companion.virtual.flags.virtual_stylus") public static final class VirtualStylusMotionEvent.Builder {
+ public static final class VirtualStylusMotionEvent.Builder {
ctor public VirtualStylusMotionEvent.Builder();
method @NonNull public android.hardware.input.VirtualStylusMotionEvent build();
method @NonNull public android.hardware.input.VirtualStylusMotionEvent.Builder setAction(int);
diff --git a/core/api/test-current.txt b/core/api/test-current.txt
index a352d9d2ea06..b118c7b6fed8 100644
--- a/core/api/test-current.txt
+++ b/core/api/test-current.txt
@@ -436,34 +436,6 @@ package android.app {
ctor public PictureInPictureUiState(boolean);
}
- public class PropertyInvalidatedCache<Query, Result> {
- ctor public PropertyInvalidatedCache(int, @NonNull String, @NonNull String, @NonNull String, @NonNull android.app.PropertyInvalidatedCache.QueryHandler<Query,Result>);
- method @NonNull public static String createPropertyName(@NonNull String, @NonNull String);
- method public void disableForCurrentProcess();
- method public static void disableForCurrentProcess(@NonNull String);
- method public static void disableForTestMode();
- method public final void disableInstance();
- method public final void disableSystemWide();
- method public final void forgetDisableLocal();
- method public boolean getDisabledState();
- method public void invalidateCache();
- method public static void invalidateCache(@NonNull String, @NonNull String);
- method public final boolean isDisabled();
- method @Nullable public Result query(@NonNull Query);
- method public static void setTestMode(boolean);
- method public void testPropertyName();
- field public static final String MODULE_BLUETOOTH = "bluetooth";
- field public static final String MODULE_SYSTEM = "system_server";
- field public static final String MODULE_TELEPHONY = "telephony";
- field public static final String MODULE_TEST = "test";
- }
-
- public abstract static class PropertyInvalidatedCache.QueryHandler<Q, R> {
- ctor public PropertyInvalidatedCache.QueryHandler();
- method @Nullable public abstract R apply(@NonNull Q);
- method public boolean shouldBypassCache(@NonNull Q);
- }
-
public class StatusBarManager {
method public void cancelRequestAddTile(@NonNull String);
method public void clickNotification(@Nullable String, int, int, boolean);
@@ -2444,17 +2416,28 @@ package android.os {
method @FlaggedApi("android.os.mainline_vcn_platform_api") public final void removeEqualMessages(int, @Nullable Object);
}
- public class IpcDataCache<Query, Result> extends android.app.PropertyInvalidatedCache<Query,Result> {
+ public class IpcDataCache<Query, Result> {
ctor public IpcDataCache(int, @NonNull String, @NonNull String, @NonNull String, @NonNull android.os.IpcDataCache.QueryHandler<Query,Result>);
+ method public void disableForCurrentProcess();
method public static void disableForCurrentProcess(@NonNull String);
+ method public final void disableInstance();
+ method public final void disableSystemWide();
+ method public final void forgetDisableLocal();
+ method public boolean getDisabledState();
+ method public void invalidateCache();
method public static void invalidateCache(@NonNull String, @NonNull String);
+ method public final boolean isDisabled();
+ method @Nullable public Result query(@NonNull Query);
+ 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";
}
- public abstract static class IpcDataCache.QueryHandler<Q, R> extends android.app.PropertyInvalidatedCache.QueryHandler<Q,R> {
+ public abstract static class IpcDataCache.QueryHandler<Q, R> {
ctor public IpcDataCache.QueryHandler();
+ method @Nullable public abstract R apply(@NonNull Q);
+ method public boolean shouldBypassCache(@NonNull Q);
}
public final class MessageQueue {
diff --git a/core/java/android/app/ActivityOptions.java b/core/java/android/app/ActivityOptions.java
index af6978a6b70c..82c746a8ad4c 100644
--- a/core/java/android/app/ActivityOptions.java
+++ b/core/java/android/app/ActivityOptions.java
@@ -1846,6 +1846,7 @@ public class ActivityOptions extends ComponentOptions {
}
/** @hide */
+ @WindowConfiguration.WindowingMode
public int getLaunchWindowingMode() {
return mLaunchWindowingMode;
}
@@ -1855,7 +1856,7 @@ public class ActivityOptions extends ComponentOptions {
* @hide
*/
@TestApi
- public void setLaunchWindowingMode(int windowingMode) {
+ public void setLaunchWindowingMode(@WindowConfiguration.WindowingMode int windowingMode) {
mLaunchWindowingMode = windowingMode;
}
diff --git a/core/java/android/app/INotificationManager.aidl b/core/java/android/app/INotificationManager.aidl
index 0451ac0d6897..1738a92b7672 100644
--- a/core/java/android/app/INotificationManager.aidl
+++ b/core/java/android/app/INotificationManager.aidl
@@ -114,6 +114,7 @@ interface INotificationManager
NotificationChannel getNotificationChannelForPackage(String pkg, int uid, String channelId, String conversationId, boolean includeDeleted);
void deleteNotificationChannel(String pkg, String channelId);
ParceledListSlice getNotificationChannels(String callingPkg, String targetPkg, int userId);
+ ParceledListSlice getOrCreateNotificationChannels(String callingPkg, String targetPkg, int userId, boolean createPrefsIfNeeded);
ParceledListSlice getNotificationChannelsForPackage(String pkg, int uid, boolean includeDeleted);
int getNumNotificationChannelsForPackage(String pkg, int uid, boolean includeDeleted);
int getDeletedChannelCount(String pkg, int uid);
diff --git a/core/java/android/app/NotificationManager.java b/core/java/android/app/NotificationManager.java
index aede8aa70ede..d22926791cc3 100644
--- a/core/java/android/app/NotificationManager.java
+++ b/core/java/android/app/NotificationManager.java
@@ -1211,7 +1211,8 @@ public class NotificationManager {
mNotificationChannelListCache.query(new NotificationChannelQuery(
mContext.getOpPackageName(),
mContext.getPackageName(),
- mContext.getUserId())));
+ mContext.getUserId(),
+ true))); // create (default channel) if needed
} else {
INotificationManager service = service();
try {
@@ -1239,7 +1240,8 @@ public class NotificationManager {
mNotificationChannelListCache.query(new NotificationChannelQuery(
mContext.getOpPackageName(),
mContext.getPackageName(),
- mContext.getUserId())));
+ mContext.getUserId(),
+ true))); // create (default channel) if needed
} else {
INotificationManager service = service();
try {
@@ -1263,9 +1265,10 @@ public class NotificationManager {
public List<NotificationChannel> getNotificationChannels() {
if (Flags.nmBinderPerfCacheChannels()) {
return mNotificationChannelListCache.query(new NotificationChannelQuery(
- mContext.getOpPackageName(),
- mContext.getPackageName(),
- mContext.getUserId()));
+ mContext.getOpPackageName(),
+ mContext.getPackageName(),
+ mContext.getUserId(),
+ false));
} else {
INotificationManager service = service();
try {
@@ -1405,8 +1408,8 @@ public class NotificationManager {
public List<NotificationChannel> apply(NotificationChannelQuery query) {
INotificationManager service = service();
try {
- return service.getNotificationChannels(query.callingPkg,
- query.targetPkg, query.userId).getList();
+ return service.getOrCreateNotificationChannels(query.callingPkg,
+ query.targetPkg, query.userId, query.createIfNeeded).getList();
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -1434,7 +1437,8 @@ public class NotificationManager {
private record NotificationChannelQuery(
String callingPkg,
String targetPkg,
- int userId) {}
+ int userId,
+ boolean createIfNeeded) {}
/**
* @hide
diff --git a/core/java/android/app/PropertyInvalidatedCache.java b/core/java/android/app/PropertyInvalidatedCache.java
index e7e9b0027812..660d88007c9a 100644
--- a/core/java/android/app/PropertyInvalidatedCache.java
+++ b/core/java/android/app/PropertyInvalidatedCache.java
@@ -22,7 +22,6 @@ import static com.android.internal.util.Preconditions.checkArgumentPositive;
import android.annotation.NonNull;
import android.annotation.Nullable;
-import android.annotation.TestApi;
import android.os.Binder;
import android.os.Handler;
import android.os.Looper;
@@ -77,7 +76,6 @@ import java.util.concurrent.atomic.AtomicLong;
* @param <Result> The class holding cache entries; use a boxed primitive if possible
* @hide
*/
-@TestApi
@android.ravenwood.annotation.RavenwoodKeepWholeClass
public class PropertyInvalidatedCache<Query, Result> {
/**
@@ -95,7 +93,6 @@ public class PropertyInvalidatedCache<Query, Result> {
* This is a configuration class that customizes a cache instance.
* @hide
*/
- @TestApi
public static abstract class QueryHandler<Q,R> {
/**
* Compute a result given a query. The semantics are those of Functor.
@@ -134,7 +131,6 @@ public class PropertyInvalidatedCache<Query, Result> {
* the system has permissions to write properties with this module.
* @hide
*/
- @TestApi
public static final String MODULE_TEST = "test";
/**
@@ -142,18 +138,17 @@ public class PropertyInvalidatedCache<Query, Result> {
* the system processes.
* @hide
*/
- @TestApi
public static final String MODULE_SYSTEM = "system_server";
/**
* The module used for bluetooth caches.
* @hide
*/
- @TestApi
public static final String MODULE_BLUETOOTH = "bluetooth";
/**
* The module used for telephony caches.
+ * @hide
*/
public static final String MODULE_TELEPHONY = "telephony";
@@ -171,7 +166,6 @@ public class PropertyInvalidatedCache<Query, Result> {
* error message.
* @hide
*/
- @TestApi
public static @NonNull String createPropertyName(@NonNull String module,
@NonNull String apiName) {
char[] api = apiName.toCharArray();
@@ -1382,7 +1376,6 @@ public class PropertyInvalidatedCache<Query, Result> {
* @param computer The code to compute values that are not in the cache.
* @hide
*/
- @TestApi
public PropertyInvalidatedCache(int maxEntries, @NonNull String module, @NonNull String api,
@NonNull String cacheName, @NonNull QueryHandler<Query, Result> computer) {
this(new Args(module).maxEntries(maxEntries).api(api), cacheName, computer);
@@ -1409,7 +1402,7 @@ public class PropertyInvalidatedCache<Query, Result> {
* current logic does not care.
* @hide
*/
- @TestApi
+ @VisibleForTesting
public static void setTestMode(boolean mode) {
synchronized (sGlobalLock) {
if (sTestMode == mode) {
@@ -1450,7 +1443,6 @@ public class PropertyInvalidatedCache<Query, Result> {
* must be true when this method is called.
* @hide
*/
- @TestApi
public void testPropertyName() {
synchronized (sGlobalLock) {
if (sTestMode == false) {
@@ -1542,8 +1534,8 @@ public class PropertyInvalidatedCache<Query, Result> {
* be re-enabled.
* @hide
*/
- @TestApi
- public final void disableInstance() {
+ @VisibleForTesting
+ public void disableInstance() {
synchronized (mLock) {
mDisabled = true;
clear();
@@ -1579,8 +1571,8 @@ public class PropertyInvalidatedCache<Query, Result> {
* found in the list of disabled caches.
* @hide
*/
- @TestApi
- public final void forgetDisableLocal() {
+ @VisibleForTesting
+ public void forgetDisableLocal() {
synchronized (sGlobalLock) {
sDisabledKeys.remove(mCacheName);
}
@@ -1603,13 +1595,11 @@ public class PropertyInvalidatedCache<Query, Result> {
* property. Once disabled, a cache cannot be reenabled.
* @hide
*/
- @TestApi
public void disableForCurrentProcess() {
disableLocal(mCacheName);
}
/** @hide */
- @TestApi
public static void disableForCurrentProcess(@NonNull String cacheName) {
disableLocal(cacheName);
}
@@ -1618,8 +1608,8 @@ public class PropertyInvalidatedCache<Query, Result> {
* Return whether a cache instance is disabled.
* @hide
*/
- @TestApi
- public final boolean isDisabled() {
+ @VisibleForTesting
+ public boolean isDisabled() {
return mDisabled || !sEnabled;
}
@@ -1627,7 +1617,6 @@ public class PropertyInvalidatedCache<Query, Result> {
* Get a value from the cache or recompute it.
* @hide
*/
- @TestApi
public @Nullable Result query(@NonNull Query query) {
// Let access to mDisabled race: it's atomic anyway.
long currentNonce = (!isDisabled()) ? getCurrentNonce() : NONCE_DISABLED;
@@ -1767,8 +1756,8 @@ public class PropertyInvalidatedCache<Query, Result> {
* just use the static version of this function.
* @hide
*/
- @TestApi
- public final void disableSystemWide() {
+ @VisibleForTesting
+ public void disableSystemWide() {
disableSystemWide(mPropertyName);
}
@@ -1788,7 +1777,6 @@ public class PropertyInvalidatedCache<Query, Result> {
* to look up the NonceHandler for a given property name.
* @hide
*/
- @TestApi
public void invalidateCache() {
mNonce.invalidate();
}
@@ -1817,7 +1805,6 @@ public class PropertyInvalidatedCache<Query, Result> {
* Invalidate caches in all processes that are keyed for the module and api.
* @hide
*/
- @TestApi
public static void invalidateCache(@NonNull String module, @NonNull String api) {
invalidateCache(createPropertyName(module, api));
}
@@ -2059,7 +2046,6 @@ public class PropertyInvalidatedCache<Query, Result> {
* temporarily disable caching, use the corking mechanism.
* @hide
*/
- @TestApi
public static void disableForTestMode() {
Log.d(TAG, "disabling all caches in the process");
sEnabled = false;
@@ -2068,10 +2054,8 @@ public class PropertyInvalidatedCache<Query, Result> {
/**
* Report the disabled status of this cache instance. The return value does not
* reflect status of the property key.
- * @hide
*/
- @TestApi
- public boolean getDisabledState() {
+ private boolean getDisabledState() {
return isDisabled();
}
diff --git a/core/java/android/companion/virtual/VirtualDeviceInternal.java b/core/java/android/companion/virtual/VirtualDeviceInternal.java
index 311e24ba6254..3ef78affb7a5 100644
--- a/core/java/android/companion/virtual/VirtualDeviceInternal.java
+++ b/core/java/android/companion/virtual/VirtualDeviceInternal.java
@@ -32,7 +32,6 @@ import android.companion.virtual.audio.VirtualAudioDevice;
import android.companion.virtual.camera.VirtualCamera;
import android.companion.virtual.camera.VirtualCameraConfig;
import android.companion.virtual.sensor.VirtualSensor;
-import android.companion.virtualdevice.flags.Flags;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
@@ -473,14 +472,12 @@ public class VirtualDeviceInternal {
@Nullable VirtualAudioDevice.AudioConfigurationChangeCallback callback) {
if (mVirtualAudioDevice == null) {
try {
- Context context = mContext;
- if (Flags.deviceAwareRecordAudioPermission()) {
- // When using a default policy for audio device-aware RECORD_AUDIO permission
- // should not take effect, thus register policies with the default context.
- if (mVirtualDevice.getDevicePolicy(POLICY_TYPE_AUDIO) == DEVICE_POLICY_CUSTOM) {
- context = mContext.createDeviceContext(getDeviceId());
- }
- }
+ // When using a default policy for audio, the device-aware RECORD_AUDIO permission
+ // should not take effect, thus register policies with the default context.
+ final Context context =
+ mVirtualDevice.getDevicePolicy(POLICY_TYPE_AUDIO) == DEVICE_POLICY_CUSTOM
+ ? mContext.createDeviceContext(getDeviceId())
+ : mContext;
mVirtualAudioDevice = new VirtualAudioDevice(context, mVirtualDevice, display,
executor, callback, () -> mVirtualAudioDevice = null);
} catch (RemoteException e) {
diff --git a/core/java/android/companion/virtual/VirtualDeviceManager.java b/core/java/android/companion/virtual/VirtualDeviceManager.java
index 73ea9f0462d5..91ea673ab6f9 100644
--- a/core/java/android/companion/virtual/VirtualDeviceManager.java
+++ b/core/java/android/companion/virtual/VirtualDeviceManager.java
@@ -170,7 +170,6 @@ public final class VirtualDeviceManager {
* @hide
*/
@SystemApi
- @FlaggedApi(Flags.FLAG_PERSISTENT_DEVICE_ID_API)
public static final String PERSISTENT_DEVICE_ID_DEFAULT =
"default:" + Context.DEVICE_ID_DEFAULT;
@@ -393,7 +392,6 @@ public final class VirtualDeviceManager {
* @hide
*/
// TODO(b/315481938): Link @see VirtualDevice#getPersistentDeviceId()
- @FlaggedApi(Flags.FLAG_PERSISTENT_DEVICE_ID_API)
@SystemApi
@Nullable
public CharSequence getDisplayNameForPersistentDeviceId(@NonNull String persistentDeviceId) {
@@ -416,7 +414,6 @@ public final class VirtualDeviceManager {
* @hide
*/
// TODO(b/315481938): Link @see VirtualDevice#getPersistentDeviceId()
- @FlaggedApi(Flags.FLAG_PERSISTENT_DEVICE_ID_API)
@SystemApi
@NonNull
public Set<String> getAllPersistentDeviceIds() {
@@ -780,7 +777,6 @@ public final class VirtualDeviceManager {
* @see VirtualDeviceParams#POLICY_TYPE_RECENTS
* @see VirtualDeviceParams#POLICY_TYPE_ACTIVITY
*/
- @FlaggedApi(Flags.FLAG_DYNAMIC_POLICY)
public void setDevicePolicy(@VirtualDeviceParams.DynamicPolicyType int policyType,
@VirtualDeviceParams.DevicePolicy int devicePolicy) {
mVirtualDeviceInternal.setDevicePolicy(policyType, devicePolicy);
@@ -802,7 +798,6 @@ public final class VirtualDeviceManager {
* @see #removeActivityPolicyExemption(ComponentName)
* @see #setDevicePolicy
*/
- @FlaggedApi(Flags.FLAG_DYNAMIC_POLICY)
public void addActivityPolicyExemption(@NonNull ComponentName componentName) {
addActivityPolicyExemption(new ActivityPolicyExemption.Builder()
.setComponentName(componentName)
@@ -825,7 +820,6 @@ public final class VirtualDeviceManager {
* @see #addActivityPolicyExemption(ComponentName)
* @see #setDevicePolicy
*/
- @FlaggedApi(Flags.FLAG_DYNAMIC_POLICY)
public void removeActivityPolicyExemption(@NonNull ComponentName componentName) {
removeActivityPolicyExemption(new ActivityPolicyExemption.Builder()
.setComponentName(componentName)
@@ -1037,9 +1031,7 @@ public final class VirtualDeviceManager {
* @param config the touchscreen configurations for the virtual stylus.
*/
@NonNull
- @FlaggedApi(Flags.FLAG_VIRTUAL_STYLUS)
- public VirtualStylus createVirtualStylus(
- @NonNull VirtualStylusConfig config) {
+ public VirtualStylus createVirtualStylus(@NonNull VirtualStylusConfig config) {
return mVirtualDeviceInternal.createVirtualStylus(config);
}
diff --git a/core/java/android/companion/virtual/VirtualDeviceParams.java b/core/java/android/companion/virtual/VirtualDeviceParams.java
index 2be27dabcf90..761e75bd9076 100644
--- a/core/java/android/companion/virtual/VirtualDeviceParams.java
+++ b/core/java/android/companion/virtual/VirtualDeviceParams.java
@@ -248,7 +248,6 @@ public final class VirtualDeviceParams implements Parcelable {
*/
// TODO(b/333443509): Update the documentation of custom policy and link to the new policy
// POLICY_TYPE_BLOCKED_ACTIVITY
- @FlaggedApi(Flags.FLAG_DYNAMIC_POLICY)
public static final int POLICY_TYPE_ACTIVITY = 3;
/**
@@ -264,7 +263,6 @@ public final class VirtualDeviceParams implements Parcelable {
*
* @see android.hardware.display.DisplayManager#VIRTUAL_DISPLAY_FLAG_TRUSTED
*/
- @FlaggedApi(Flags.FLAG_CROSS_DEVICE_CLIPBOARD)
public static final int POLICY_TYPE_CLIPBOARD = 4;
/**
@@ -431,7 +429,6 @@ public final class VirtualDeviceParams implements Parcelable {
* @see Builder#setHomeComponent
* @see VirtualDisplayConfig#isHomeSupported()
*/
- @FlaggedApi(Flags.FLAG_VDM_CUSTOM_HOME)
@Nullable
public ComponentName getHomeComponent() {
return mHomeComponent;
@@ -926,7 +923,6 @@ public final class VirtualDeviceParams implements Parcelable {
*
* @see VirtualDisplayConfig#isHomeSupported()
*/
- @FlaggedApi(Flags.FLAG_VDM_CUSTOM_HOME)
@NonNull
public Builder setHomeComponent(@Nullable ComponentName homeComponent) {
mHomeComponent = homeComponent;
@@ -1282,33 +1278,31 @@ public final class VirtualDeviceParams implements Parcelable {
mVirtualSensorDirectChannelCallback);
}
- if (Flags.dynamicPolicy()) {
- switch (mDevicePolicies.get(POLICY_TYPE_ACTIVITY, -1)) {
- case DEVICE_POLICY_DEFAULT:
- if (mDefaultActivityPolicyConfigured
- && mDefaultActivityPolicy == ACTIVITY_POLICY_DEFAULT_BLOCKED) {
- throw new IllegalArgumentException(
- "DEVICE_POLICY_DEFAULT is explicitly configured for "
- + "POLICY_TYPE_ACTIVITY, which is exclusive with "
- + "setAllowedActivities.");
- }
- break;
- case DEVICE_POLICY_CUSTOM:
- if (mDefaultActivityPolicyConfigured
- && mDefaultActivityPolicy == ACTIVITY_POLICY_DEFAULT_ALLOWED) {
- throw new IllegalArgumentException(
- "DEVICE_POLICY_CUSTOM is explicitly configured for "
- + "POLICY_TYPE_ACTIVITY, which is exclusive with "
- + "setBlockedActivities.");
- }
- break;
- default:
- if (mDefaultActivityPolicyConfigured
- && mDefaultActivityPolicy == ACTIVITY_POLICY_DEFAULT_BLOCKED) {
- mDevicePolicies.put(POLICY_TYPE_ACTIVITY, DEVICE_POLICY_CUSTOM);
- }
- break;
- }
+ switch (mDevicePolicies.get(POLICY_TYPE_ACTIVITY, -1)) {
+ case DEVICE_POLICY_DEFAULT:
+ if (mDefaultActivityPolicyConfigured
+ && mDefaultActivityPolicy == ACTIVITY_POLICY_DEFAULT_BLOCKED) {
+ throw new IllegalArgumentException(
+ "DEVICE_POLICY_DEFAULT is explicitly configured for "
+ + "POLICY_TYPE_ACTIVITY, which is exclusive with "
+ + "setAllowedActivities.");
+ }
+ break;
+ case DEVICE_POLICY_CUSTOM:
+ if (mDefaultActivityPolicyConfigured
+ && mDefaultActivityPolicy == ACTIVITY_POLICY_DEFAULT_ALLOWED) {
+ throw new IllegalArgumentException(
+ "DEVICE_POLICY_CUSTOM is explicitly configured for "
+ + "POLICY_TYPE_ACTIVITY, which is exclusive with "
+ + "setBlockedActivities.");
+ }
+ break;
+ default:
+ if (mDefaultActivityPolicyConfigured
+ && mDefaultActivityPolicy == ACTIVITY_POLICY_DEFAULT_BLOCKED) {
+ mDevicePolicies.put(POLICY_TYPE_ACTIVITY, DEVICE_POLICY_CUSTOM);
+ }
+ break;
}
if (mDimDuration.compareTo(mScreenOffTimeout) > 0) {
@@ -1319,10 +1313,6 @@ public final class VirtualDeviceParams implements Parcelable {
mScreenOffTimeout = INFINITE_TIMEOUT;
}
- if (!Flags.crossDeviceClipboard()) {
- mDevicePolicies.delete(POLICY_TYPE_CLIPBOARD);
- }
-
if (!Flags.virtualCamera()) {
mDevicePolicies.delete(POLICY_TYPE_CAMERA);
}
diff --git a/core/java/android/companion/virtual/flags/flags.aconfig b/core/java/android/companion/virtual/flags/flags.aconfig
index 84af84072f1b..6da2a073ec19 100644
--- a/core/java/android/companion/virtual/flags/flags.aconfig
+++ b/core/java/android/companion/virtual/flags/flags.aconfig
@@ -19,14 +19,6 @@ flag {
flag {
namespace: "virtual_devices"
- name: "device_aware_record_audio_permission"
- description: "Enable device-aware RECORD_AUDIO permission"
- bug: "291737188"
- is_fixed_read_only: true
-}
-
-flag {
- namespace: "virtual_devices"
name: "media_projection_keyguard_restrictions"
description: "Auto-stop MP when the device locks"
bug: "348335290"
diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java
index 01e24d81a7cd..885a2dbc471e 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -4215,6 +4215,17 @@ public class Intent implements Parcelable, Cloneable {
public static final String ACTION_USER_INFO_CHANGED =
"android.intent.action.USER_INFO_CHANGED";
+
+ /**
+ * Broadcast sent to the system when a user's information changes. Carries an extra
+ * {@link #EXTRA_USER_HANDLE} to indicate which user's information changed.
+ * This is only sent to permission protected manifest receivers. It is sent to all users.
+ * @hide
+ */
+ @BroadcastBehavior(includeBackground = true)
+ public static final String ACTION_USER_INFO_CHANGED_BACKGROUND =
+ "android.intent.action.USER_INFO_CHANGED_BACKGROUND";
+
/**
* Broadcast sent to the primary user when an associated managed profile is added (the profile
* was created and is ready to be used). Carries an extra {@link #EXTRA_USER} that specifies
@@ -5460,7 +5471,7 @@ public class Intent implements Parcelable, Cloneable {
/**
* Activities that can be safely invoked from a browser must support this
* category. For example, if the user is viewing a web page or an e-mail
- * and clicks on a link in the text, the Intent generated execute that
+ * and clicks on a link in the text, the Intent generated to execute that
* link will require the BROWSABLE category, so that only activities
* supporting this category will be considered as possible actions. By
* supporting this category, you are promising that there is nothing
diff --git a/core/java/android/content/pm/LauncherApps.java b/core/java/android/content/pm/LauncherApps.java
index a0c0f122497f..1724d9ff0deb 100644
--- a/core/java/android/content/pm/LauncherApps.java
+++ b/core/java/android/content/pm/LauncherApps.java
@@ -1932,6 +1932,9 @@ public class LauncherApps {
* caller should have normal {@link android.Manifest.permission#ACCESS_HIDDEN_PROFILES}
* permission and the {@link android.app.role.RoleManager#ROLE_HOME} role.
*
+ * <p>This callback will also receive changes to the {@link LauncherUserInfo#getUserConfig()},
+ * allowing clients to monitor updates to the user-specific configuration.
+ *
* @param callback The callback to register.
*/
// Alternatively, a system app can access this api for private profile if they've been granted
@@ -1950,6 +1953,9 @@ public class LauncherApps {
* caller should have normal {@link android.Manifest.permission#ACCESS_HIDDEN_PROFILES}
* permission and the {@link android.app.role.RoleManager#ROLE_HOME} role.
*
+ * <p>This callback will also receive changes to the {@link LauncherUserInfo#getUserConfig()},
+ * allowing clients to monitor updates to the user-specific configuration.
+ *
* @param callback The callback to register.
* @param handler that should be used to post callbacks on, may be null.
*/
diff --git a/core/java/android/database/sqlite/SQLiteOpenHelper.java b/core/java/android/database/sqlite/SQLiteOpenHelper.java
index 78c8954cfe5f..88d69b665c87 100644
--- a/core/java/android/database/sqlite/SQLiteOpenHelper.java
+++ b/core/java/android/database/sqlite/SQLiteOpenHelper.java
@@ -25,10 +25,15 @@ import android.database.DatabaseErrorHandler;
import android.database.SQLException;
import android.database.sqlite.SQLiteDatabase.CursorFactory;
import android.os.FileUtils;
+import android.util.ArrayMap;
import android.util.Log;
+import com.android.internal.annotations.GuardedBy;
+
import java.io.File;
+import java.io.IOException;
import java.util.Objects;
+import java.util.concurrent.ConcurrentHashMap;
/**
* A helper class to manage database creation and version management.
@@ -54,6 +59,13 @@ import java.util.Objects;
public abstract class SQLiteOpenHelper implements AutoCloseable {
private static final String TAG = SQLiteOpenHelper.class.getSimpleName();
+ // Every database file has a lock, saved in this map. The lock is held while the database is
+ // opened.
+ private static final ConcurrentHashMap<String, Object> sDbLock = new ConcurrentHashMap<>();
+
+ // The lock that this open helper instance must hold when the database is opened.
+ private final Object mLock;
+
private final Context mContext;
@UnsupportedAppUsage
private final String mName;
@@ -168,6 +180,21 @@ public abstract class SQLiteOpenHelper implements AutoCloseable {
mNewVersion = version;
mMinimumSupportedVersion = Math.max(0, minimumSupportedVersion);
setOpenParamsBuilder(openParamsBuilder);
+
+ Object lock = null;
+ if (mName == null || !Flags.concurrentOpenHelper()) {
+ lock = new Object();
+ } else {
+ try {
+ final String path = mContext.getDatabasePath(mName).getCanonicalPath();
+ lock = sDbLock.computeIfAbsent(path, (String k) -> new Object());
+ } catch (IOException e) {
+ Log.d(TAG, "failed to construct db path for " + mName);
+ // Ensure the lock is not null.
+ lock = new Object();
+ }
+ }
+ mLock = lock;
}
/**
@@ -358,74 +385,77 @@ public abstract class SQLiteOpenHelper implements AutoCloseable {
SQLiteDatabase db = mDatabase;
try {
- mIsInitializing = true;
+ synchronized (mLock) {
+ mIsInitializing = true;
- if (db != null) {
- if (writable && db.isReadOnly()) {
- db.reopenReadWrite();
- }
- } else if (mName == null) {
- db = SQLiteDatabase.createInMemory(mOpenParamsBuilder.build());
- } else {
- final File filePath = mContext.getDatabasePath(mName);
- SQLiteDatabase.OpenParams params = mOpenParamsBuilder.build();
- try {
- db = SQLiteDatabase.openDatabase(filePath, params);
- // Keep pre-O-MR1 behavior by resetting file permissions to 660
- setFilePermissionsForDb(filePath.getPath());
- } catch (SQLException ex) {
- if (writable) {
- throw ex;
+ if (db != null) {
+ if (writable && db.isReadOnly()) {
+ db.reopenReadWrite();
+ }
+ } else if (mName == null) {
+ db = SQLiteDatabase.createInMemory(mOpenParamsBuilder.build());
+ } else {
+ final File filePath = mContext.getDatabasePath(mName);
+ SQLiteDatabase.OpenParams params = mOpenParamsBuilder.build();
+ try {
+ db = SQLiteDatabase.openDatabase(filePath, params);
+ // Keep pre-O-MR1 behavior by resetting file permissions to 660
+ setFilePermissionsForDb(filePath.getPath());
+ } catch (SQLException ex) {
+ if (writable) {
+ throw ex;
+ }
+ Log.e(TAG, "Couldn't open database for writing (will try read-only):", ex);
+ params = params.toBuilder()
+ .addOpenFlags(SQLiteDatabase.OPEN_READONLY).build();
+ db = SQLiteDatabase.openDatabase(filePath, params);
}
- Log.e(TAG, "Couldn't open database for writing (will try read-only):", ex);
- params = params.toBuilder().addOpenFlags(SQLiteDatabase.OPEN_READONLY).build();
- db = SQLiteDatabase.openDatabase(filePath, params);
}
- }
-
- onConfigure(db);
- final int version = db.getVersion();
- if (version != mNewVersion) {
- if (db.isReadOnly()) {
- throw new SQLiteException("Can't upgrade read-only database from version " +
- db.getVersion() + " to " + mNewVersion + ": " + mName);
- }
+ onConfigure(db);
- if (version > 0 && version < mMinimumSupportedVersion) {
- File databaseFile = new File(db.getPath());
- onBeforeDelete(db);
- db.close();
- if (SQLiteDatabase.deleteDatabase(databaseFile)) {
- mIsInitializing = false;
- return getDatabaseLocked(writable);
- } else {
- throw new IllegalStateException("Unable to delete obsolete database "
- + mName + " with version " + version);
+ final int version = db.getVersion();
+ if (version != mNewVersion) {
+ if (db.isReadOnly()) {
+ throw new SQLiteException("Can't upgrade read-only database from version "
+ + db.getVersion() + " to " + mNewVersion + ": " + mName);
}
- } else {
- db.beginTransaction();
- try {
- if (version == 0) {
- onCreate(db);
+
+ if (version > 0 && version < mMinimumSupportedVersion) {
+ File databaseFile = new File(db.getPath());
+ onBeforeDelete(db);
+ db.close();
+ if (SQLiteDatabase.deleteDatabase(databaseFile)) {
+ mIsInitializing = false;
+ return getDatabaseLocked(writable);
} else {
- if (version > mNewVersion) {
- onDowngrade(db, version, mNewVersion);
+ throw new IllegalStateException("Unable to delete obsolete database "
+ + mName + " with version " + version);
+ }
+ } else {
+ db.beginTransaction();
+ try {
+ if (version == 0) {
+ onCreate(db);
} else {
- onUpgrade(db, version, mNewVersion);
+ if (version > mNewVersion) {
+ onDowngrade(db, version, mNewVersion);
+ } else {
+ onUpgrade(db, version, mNewVersion);
+ }
}
+ db.setVersion(mNewVersion);
+ db.setTransactionSuccessful();
+ } finally {
+ db.endTransaction();
}
- db.setVersion(mNewVersion);
- db.setTransactionSuccessful();
- } finally {
- db.endTransaction();
}
}
- }
- onOpen(db);
- mDatabase = db;
- return db;
+ onOpen(db);
+ mDatabase = db;
+ return db;
+ }
} finally {
mIsInitializing = false;
if (db != null && db != mDatabase) {
diff --git a/core/java/android/database/sqlite/flags.aconfig b/core/java/android/database/sqlite/flags.aconfig
index d43a66904af8..1d17a51f3653 100644
--- a/core/java/android/database/sqlite/flags.aconfig
+++ b/core/java/android/database/sqlite/flags.aconfig
@@ -17,3 +17,12 @@ flag {
description: "SQLite APIs held back for Android 15"
bug: "279043253"
}
+
+flag {
+ name: "concurrent_open_helper"
+ is_exported: true
+ namespace: "system_performance"
+ is_fixed_read_only: false
+ description: "Make SQLiteOpenHelper thread-safe"
+ bug: "335904370"
+}
diff --git a/core/java/android/hardware/contexthub/HubEndpoint.java b/core/java/android/hardware/contexthub/HubEndpoint.java
index 25cdc508fdce..14911de6d032 100644
--- a/core/java/android/hardware/contexthub/HubEndpoint.java
+++ b/core/java/android/hardware/contexthub/HubEndpoint.java
@@ -107,6 +107,13 @@ public class HubEndpoint {
@GuardedBy("mLock")
private final SparseArray<HubEndpointSession> mActiveSessions = new SparseArray<>();
+ /*
+ * Internal interface used to invoke IContextHubEndpoint calls.
+ */
+ interface EndpointConsumer {
+ void accept(IContextHubEndpoint endpoint) throws RemoteException;
+ }
+
private final IContextHubEndpointCallback mServiceCallback =
new IContextHubEndpointCallback.Stub() {
@Override
@@ -115,20 +122,19 @@ public class HubEndpoint {
HubEndpointInfo initiator,
@Nullable String serviceDescriptor)
throws RemoteException {
- HubEndpointSession activeSession;
+ boolean sessionExists;
synchronized (mLock) {
- activeSession = mActiveSessions.get(sessionId);
+ sessionExists = mActiveSessions.contains(sessionId);
// TODO(b/378974199): Consider refactor these assertions
- if (activeSession != null) {
- Log.i(
+ if (sessionExists) {
+ Log.w(
TAG,
"onSessionOpenComplete: session already exists, id="
+ sessionId);
- return;
}
}
- if (mLifecycleCallback != null) {
+ if (!sessionExists && mLifecycleCallback != null) {
mLifecycleCallbackExecutor.execute(
() ->
processSessionOpenRequestResult(
@@ -142,96 +148,6 @@ public class HubEndpoint {
}
}
- private void processSessionOpenRequestResult(
- int sessionId,
- HubEndpointInfo initiator,
- @Nullable String serviceDescriptor,
- HubEndpointSessionResult result) {
- if (result == null) {
- throw new IllegalArgumentException(
- "HubEndpointSessionResult shouldn't be null.");
- }
-
- if (result.isAccepted()) {
- acceptSession(sessionId, initiator, serviceDescriptor);
- } else {
- Log.i(
- TAG,
- "Session "
- + sessionId
- + " from "
- + initiator
- + " was rejected, reason="
- + result.getReason());
- rejectSession(sessionId);
- }
-
- invokeCallbackFinished();
- }
-
- private void acceptSession(
- int sessionId,
- HubEndpointInfo initiator,
- @Nullable String serviceDescriptor) {
- if (mServiceToken == null || mAssignedHubEndpointInfo == null) {
- // No longer registered?
- return;
- }
-
- // Retrieve the active session
- HubEndpointSession activeSession;
- synchronized (mLock) {
- activeSession = mActiveSessions.get(sessionId);
- // TODO(b/378974199): Consider refactor these assertions
- if (activeSession != null) {
- Log.e(
- TAG,
- "onSessionOpenRequestResult: session already exists, id="
- + sessionId);
- return;
- }
-
- activeSession =
- new HubEndpointSession(
- sessionId,
- HubEndpoint.this,
- mAssignedHubEndpointInfo,
- initiator,
- serviceDescriptor);
- try {
- // oneway call to notify system service that the request is completed
- mServiceToken.openSessionRequestComplete(sessionId);
- } catch (RemoteException e) {
- Log.e(TAG, "onSessionOpenRequestResult: ", e);
- return;
- }
-
- mActiveSessions.put(sessionId, activeSession);
- }
-
- // Execute the callback
- activeSession.setOpened();
- if (mLifecycleCallback != null) {
- final HubEndpointSession finalActiveSession = activeSession;
- mLifecycleCallbackExecutor.execute(
- () -> mLifecycleCallback.onSessionOpened(finalActiveSession));
- }
- }
-
- private void rejectSession(int sessionId) {
- if (mServiceToken == null || mAssignedHubEndpointInfo == null) {
- // No longer registered?
- return;
- }
-
- try {
- mServiceToken.closeSession(
- sessionId, REASON_OPEN_ENDPOINT_SESSION_REQUEST_REJECTED);
- } catch (RemoteException e) {
- e.rethrowFromSystemServer();
- }
- }
-
@Override
public void onSessionOpenComplete(int sessionId) throws RemoteException {
final HubEndpointSession activeSession;
@@ -242,16 +158,15 @@ public class HubEndpoint {
}
// TODO(b/378974199): Consider refactor these assertions
if (activeSession == null) {
- Log.i(
+ Log.w(
TAG,
"onSessionOpenComplete: no pending session open request? id="
+ sessionId);
- return;
+ } else {
+ activeSession.setOpened();
}
- // Execute the callback
- activeSession.setOpened();
- if (mLifecycleCallback != null) {
+ if (activeSession != null && mLifecycleCallback != null) {
mLifecycleCallbackExecutor.execute(
() -> {
mLifecycleCallback.onSessionOpened(activeSession);
@@ -272,12 +187,11 @@ public class HubEndpoint {
}
// TODO(b/378974199): Consider refactor these assertions
if (activeSession == null) {
- Log.i(TAG, "onSessionClosed: session not active, id=" + sessionId);
- return;
+ Log.w(TAG, "onSessionClosed: session not active, id=" + sessionId);
}
// Execute the callback
- if (mLifecycleCallback != null) {
+ if (activeSession != null && mLifecycleCallback != null) {
mLifecycleCallbackExecutor.execute(
() -> {
mLifecycleCallback.onSessionClosed(activeSession, reason);
@@ -304,44 +218,120 @@ public class HubEndpoint {
activeSession = mActiveSessions.get(sessionId);
}
if (activeSession == null) {
- Log.i(TAG, "onMessageReceived: session not active, id=" + sessionId);
+ Log.w(TAG, "onMessageReceived: session not active, id=" + sessionId);
}
if (activeSession == null || mMessageCallback == null) {
- if (message.isResponseRequired()) {
- try {
- mServiceToken.sendMessageDeliveryStatus(
- sessionId,
- message.getMessageSequenceNumber(),
- ErrorCode.DESTINATION_NOT_FOUND);
- } catch (RemoteException e) {
- e.rethrowFromSystemServer();
- }
- }
- return;
+ sendMessageDeliveryStatus(
+ sessionId, message, ErrorCode.DESTINATION_NOT_FOUND);
+ } else {
+ mMessageCallbackExecutor.execute(
+ () -> {
+ mMessageCallback.onMessageReceived(activeSession, message);
+ sendMessageDeliveryStatus(sessionId, message, ErrorCode.OK);
+ });
}
+ }
- // Execute the callback
- mMessageCallbackExecutor.execute(
- () -> {
- mMessageCallback.onMessageReceived(activeSession, message);
- if (message.isResponseRequired()) {
- try {
- mServiceToken.sendMessageDeliveryStatus(
+ private void sendMessageDeliveryStatus(
+ int sessionId, HubMessage message, byte errorCode) {
+ if (message.isResponseRequired()) {
+ invokeCallback(
+ (callback) ->
+ callback.sendMessageDeliveryStatus(
sessionId,
message.getMessageSequenceNumber(),
- ErrorCode.OK);
- } catch (RemoteException e) {
- e.rethrowFromSystemServer();
- }
- }
- invokeCallbackFinished();
- });
+ errorCode));
+ }
+ invokeCallbackFinished();
+ }
+
+ private void processSessionOpenRequestResult(
+ int sessionId,
+ HubEndpointInfo initiator,
+ @Nullable String serviceDescriptor,
+ HubEndpointSessionResult result) {
+ if (result == null) {
+ throw new IllegalArgumentException(
+ "HubEndpointSessionResult shouldn't be null.");
+ }
+
+ if (result.isAccepted()) {
+ acceptSession(sessionId, initiator, serviceDescriptor);
+ } else {
+ Log.e(
+ TAG,
+ "Session "
+ + sessionId
+ + " from "
+ + initiator
+ + " was rejected, reason="
+ + result.getReason());
+ rejectSession(sessionId);
+ }
+
+ invokeCallbackFinished();
+ }
+
+ private void acceptSession(
+ int sessionId,
+ HubEndpointInfo initiator,
+ @Nullable String serviceDescriptor) {
+ // Retrieve the active session
+ HubEndpointSession activeSession;
+ synchronized (mLock) {
+ activeSession = mActiveSessions.get(sessionId);
+ // TODO(b/378974199): Consider refactor these assertions
+ if (activeSession != null) {
+ Log.e(
+ TAG,
+ "onSessionOpenRequestResult: session already exists, id="
+ + sessionId);
+ return;
+ }
+
+ activeSession =
+ new HubEndpointSession(
+ sessionId,
+ HubEndpoint.this,
+ mAssignedHubEndpointInfo,
+ initiator,
+ serviceDescriptor);
+
+ invokeCallback(
+ (callback) -> callback.openSessionRequestComplete(sessionId));
+ mActiveSessions.put(sessionId, activeSession);
+ }
+
+ // Execute the callback
+ activeSession.setOpened();
+ if (mLifecycleCallback != null) {
+ final HubEndpointSession finalActiveSession = activeSession;
+ mLifecycleCallbackExecutor.execute(
+ () -> mLifecycleCallback.onSessionOpened(finalActiveSession));
+ }
+ }
+
+ private void rejectSession(int sessionId) {
+ invokeCallback(
+ (callback) ->
+ callback.closeSession(
+ sessionId,
+ REASON_OPEN_ENDPOINT_SESSION_REQUEST_REJECTED));
}
private void invokeCallbackFinished() {
+ invokeCallback((callback) -> callback.onCallbackFinished());
+ }
+
+ private void invokeCallback(EndpointConsumer consumer) {
try {
- mServiceToken.onCallbackFinished();
+ consumer.accept(mServiceToken);
+ } catch (IllegalStateException e) {
+ // It's possible to hit this exception if the endpoint was unregistered
+ // while processing the callback. It's not a fatal error so we just log
+ // a warning.
+ Log.w(TAG, "IllegalStateException while calling callback", e);
} catch (RemoteException e) {
e.rethrowFromSystemServer();
}
@@ -369,11 +359,6 @@ public class HubEndpoint {
/** @hide */
public void register(IContextHubService service) {
- // TODO(b/378974199): Consider refactor these assertions
- if (mServiceToken != null) {
- // Already registered
- return;
- }
try {
IContextHubEndpoint serviceToken =
service.registerEndpoint(
@@ -391,13 +376,6 @@ public class HubEndpoint {
/** @hide */
public void unregister() {
- IContextHubEndpoint serviceToken = mServiceToken;
- // TODO(b/378974199): Consider refactor these assertions
- if (serviceToken == null) {
- // Not yet registered
- return;
- }
-
try {
synchronized (mLock) {
// Don't call HubEndpointSession.close() here.
@@ -410,20 +388,11 @@ public class HubEndpoint {
} catch (RemoteException e) {
Log.e(TAG, "unregisterEndpoint: failed to unregister endpoint", e);
e.rethrowFromSystemServer();
- } finally {
- mServiceToken = null;
- mAssignedHubEndpointInfo = null;
}
}
/** @hide */
public void openSession(HubEndpointInfo destinationInfo, @Nullable String serviceDescriptor) {
- // TODO(b/378974199): Consider refactor these assertions
- if (mServiceToken == null || mAssignedHubEndpointInfo == null) {
- // No longer registered?
- return;
- }
-
HubEndpointSession newSession;
try {
synchronized (mLock) {
@@ -449,13 +418,6 @@ public class HubEndpoint {
/** @hide */
public void closeSession(HubEndpointSession session) {
- IContextHubEndpoint serviceToken = mServiceToken;
- // TODO(b/378974199): Consider refactor these assertions
- if (serviceToken == null || mAssignedHubEndpointInfo == null) {
- // Not registered
- return;
- }
-
synchronized (mLock) {
if (!mActiveSessions.contains(session.getId())) {
// Already closed?
@@ -466,8 +428,7 @@ public class HubEndpoint {
}
try {
- // Oneway notification to system service
- serviceToken.closeSession(session.getId(), REASON_CLOSE_ENDPOINT_SESSION_REQUESTED);
+ mServiceToken.closeSession(session.getId(), REASON_CLOSE_ENDPOINT_SESSION_REQUESTED);
} catch (RemoteException e) {
Log.e(TAG, "closeSession: failed to close session " + session, e);
e.rethrowFromSystemServer();
@@ -478,14 +439,8 @@ public class HubEndpoint {
HubEndpointSession session,
HubMessage message,
@Nullable IContextHubTransactionCallback transactionCallback) {
- IContextHubEndpoint serviceToken = mServiceToken;
- if (serviceToken == null) {
- // Not registered
- return;
- }
-
try {
- serviceToken.sendMessage(session.getId(), message, transactionCallback);
+ mServiceToken.sendMessage(session.getId(), message, transactionCallback);
} catch (RemoteException e) {
Log.e(TAG, "sendMessage: failed to send message session=" + session, e);
e.rethrowFromSystemServer();
diff --git a/core/java/android/hardware/devicestate/feature/flags.aconfig b/core/java/android/hardware/devicestate/feature/flags.aconfig
index 6230f4dbf6f4..44d662f2a4fc 100644
--- a/core/java/android/hardware/devicestate/feature/flags.aconfig
+++ b/core/java/android/hardware/devicestate/feature/flags.aconfig
@@ -38,4 +38,16 @@ flag {
description: "Enables Rear Display Mode V2, where the inner display shows the user a UI affordance for exiting the state"
bug: "372486634"
is_fixed_read_only: true
-} \ No newline at end of file
+}
+
+flag {
+ name: "device_state_configuration_flag"
+ is_exported: true
+ namespace: "windowing_sdk"
+ description: "Re-add flag parsing for device_state_configuration.xml configuration for devices that didn't update vendor images."
+ bug: "388366842"
+ is_fixed_read_only: true
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
diff --git a/core/java/android/hardware/display/VirtualDisplayConfig.java b/core/java/android/hardware/display/VirtualDisplayConfig.java
index 72570553f78a..2a9ee7f07934 100644
--- a/core/java/android/hardware/display/VirtualDisplayConfig.java
+++ b/core/java/android/hardware/display/VirtualDisplayConfig.java
@@ -237,10 +237,9 @@ public final class VirtualDisplayConfig implements Parcelable {
* @see Builder#setHomeSupported
* @hide
*/
- @FlaggedApi(android.companion.virtual.flags.Flags.FLAG_VDM_CUSTOM_HOME)
@SystemApi
public boolean isHomeSupported() {
- return android.companion.virtual.flags.Flags.vdmCustomHome() && mIsHomeSupported;
+ return mIsHomeSupported;
}
/**
@@ -605,7 +604,6 @@ public final class VirtualDisplayConfig implements Parcelable {
* @see DisplayManager#VIRTUAL_DISPLAY_FLAG_OWN_CONTENT_ONLY
* @hide
*/
- @FlaggedApi(android.companion.virtual.flags.Flags.FLAG_VDM_CUSTOM_HOME)
@SystemApi
@NonNull
public Builder setHomeSupported(boolean isHomeSupported) {
diff --git a/core/java/android/hardware/input/VirtualStylus.java b/core/java/android/hardware/input/VirtualStylus.java
index 4b79bc482c7b..32aac2efb3c1 100644
--- a/core/java/android/hardware/input/VirtualStylus.java
+++ b/core/java/android/hardware/input/VirtualStylus.java
@@ -16,11 +16,9 @@
package android.hardware.input;
-import android.annotation.FlaggedApi;
import android.annotation.NonNull;
import android.annotation.SystemApi;
import android.companion.virtual.IVirtualDevice;
-import android.companion.virtual.flags.Flags;
import android.os.IBinder;
import android.os.RemoteException;
import android.util.Log;
@@ -34,7 +32,6 @@ import android.util.Log;
*
* @hide
*/
-@FlaggedApi(Flags.FLAG_VIRTUAL_STYLUS)
@SystemApi
public class VirtualStylus extends VirtualInputDevice {
/** @hide */
diff --git a/core/java/android/hardware/input/VirtualStylusButtonEvent.java b/core/java/android/hardware/input/VirtualStylusButtonEvent.java
index 8fcf561bedcd..9fe725a627b4 100644
--- a/core/java/android/hardware/input/VirtualStylusButtonEvent.java
+++ b/core/java/android/hardware/input/VirtualStylusButtonEvent.java
@@ -16,11 +16,9 @@
package android.hardware.input;
-import android.annotation.FlaggedApi;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.SystemApi;
-import android.companion.virtual.flags.Flags;
import android.os.Parcel;
import android.os.Parcelable;
import android.os.SystemClock;
@@ -35,7 +33,6 @@ import java.lang.annotation.RetentionPolicy;
*
* @hide
*/
-@FlaggedApi(Flags.FLAG_VIRTUAL_STYLUS)
@SystemApi
public final class VirtualStylusButtonEvent implements Parcelable {
/** @hide */
@@ -128,7 +125,6 @@ public final class VirtualStylusButtonEvent implements Parcelable {
/**
* Builder for {@link VirtualStylusButtonEvent}.
*/
- @FlaggedApi(Flags.FLAG_VIRTUAL_STYLUS)
public static final class Builder {
@Action
diff --git a/core/java/android/hardware/input/VirtualStylusConfig.java b/core/java/android/hardware/input/VirtualStylusConfig.java
index 64cf1f56d8bc..3c56023fa6d3 100644
--- a/core/java/android/hardware/input/VirtualStylusConfig.java
+++ b/core/java/android/hardware/input/VirtualStylusConfig.java
@@ -16,11 +16,9 @@
package android.hardware.input;
-import android.annotation.FlaggedApi;
import android.annotation.IntRange;
import android.annotation.NonNull;
import android.annotation.SystemApi;
-import android.companion.virtual.flags.Flags;
import android.os.Parcel;
import android.os.Parcelable;
@@ -29,7 +27,6 @@ import android.os.Parcelable;
*
* @hide
*/
-@FlaggedApi(Flags.FLAG_VIRTUAL_STYLUS)
@SystemApi
public final class VirtualStylusConfig extends VirtualTouchDeviceConfig implements Parcelable {
@@ -68,7 +65,6 @@ public final class VirtualStylusConfig extends VirtualTouchDeviceConfig implemen
/**
* Builder for creating a {@link VirtualStylusConfig}.
*/
- @FlaggedApi(Flags.FLAG_VIRTUAL_STYLUS)
public static final class Builder extends VirtualTouchDeviceConfig.Builder<Builder> {
/**
diff --git a/core/java/android/hardware/input/VirtualStylusMotionEvent.java b/core/java/android/hardware/input/VirtualStylusMotionEvent.java
index 0ac6f3aa3e15..fa0ff4f7eeab 100644
--- a/core/java/android/hardware/input/VirtualStylusMotionEvent.java
+++ b/core/java/android/hardware/input/VirtualStylusMotionEvent.java
@@ -16,12 +16,10 @@
package android.hardware.input;
-import android.annotation.FlaggedApi;
import android.annotation.IntDef;
import android.annotation.IntRange;
import android.annotation.NonNull;
import android.annotation.SystemApi;
-import android.companion.virtual.flags.Flags;
import android.os.Parcel;
import android.os.Parcelable;
import android.os.SystemClock;
@@ -38,7 +36,6 @@ import java.lang.annotation.RetentionPolicy;
*
* @hide
*/
-@FlaggedApi(Flags.FLAG_VIRTUAL_STYLUS)
@SystemApi
public final class VirtualStylusMotionEvent implements Parcelable {
private static final int TILT_MIN = -90;
@@ -209,7 +206,6 @@ public final class VirtualStylusMotionEvent implements Parcelable {
/**
* Builder for {@link VirtualStylusMotionEvent}.
*/
- @FlaggedApi(Flags.FLAG_VIRTUAL_STYLUS)
public static final class Builder {
@ToolType
diff --git a/core/java/android/hardware/input/input_framework.aconfig b/core/java/android/hardware/input/input_framework.aconfig
index 7887c15a72ff..62126963cba4 100644
--- a/core/java/android/hardware/input/input_framework.aconfig
+++ b/core/java/android/hardware/input/input_framework.aconfig
@@ -178,6 +178,13 @@ flag {
}
flag {
+ name: "enable_display_color_inversion_key_gestures"
+ namespace: "input"
+ description: "Adds key gestures for display color inversion for accessibility needs"
+ bug: "383730505"
+}
+
+flag {
name: "enable_talkback_and_magnifier_key_gestures"
namespace: "input"
description: "Adds key gestures for talkback and magnifier"
diff --git a/core/java/android/os/IpcDataCache.java b/core/java/android/os/IpcDataCache.java
index a2e9314f6436..240bc4f74820 100644
--- a/core/java/android/os/IpcDataCache.java
+++ b/core/java/android/os/IpcDataCache.java
@@ -658,4 +658,74 @@ public class IpcDataCache<Query, Result> extends PropertyInvalidatedCache<Query,
}
});
}
+
+ /**
+ * The following APIs are exposed to support testing. They only forward the superclass but
+ * that means the superclass does not have to expose the APIs itself.
+ */
+
+ /**
+ * Stop disabling local caches with the same name as <this>. Any caches that are currently
+ * disabled remain disabled (the "disabled" setting is sticky). However, new caches with this
+ * name will not be disabled. It is not an error if the cache name is not found in the list
+ * of disabled caches.
+ * @hide
+ */
+ @TestApi
+ @Override
+ public final void forgetDisableLocal() {
+ super.forgetDisableLocal();
+ }
+
+ /**
+ * Return whether a cache instance is disabled.
+ * @hide
+ */
+ @TestApi
+ @Override
+ public final boolean isDisabled() {
+ return super.isDisabled();
+ }
+
+ /**
+ * This is an obsolete synonym for {@link #isDisabled()}.
+ * @hide
+ */
+ @TestApi
+ public boolean getDisabledState() {
+ return isDisabled();
+ }
+
+ /**
+ * Disable the use of this cache in this process. This method is used internally and during
+ * testing. To disable a cache in normal code, use disableProcessLocal().
+ * @hide
+ */
+ @TestApi
+ @Override
+ public final void disableInstance() {
+ super.disableInstance();
+ }
+
+ /**
+ * Disable all caches that use the property as the current cache.
+ * @hide
+ */
+ @TestApi
+ @Override
+ public final void disableSystemWide() {
+ super.disableSystemWide();
+ }
+
+ /**
+ * 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. The purpose is solely to
+ * ensure that test clients do not forget to use the test mode properly, even though the
+ * current logic does not care.
+ * @hide
+ */
+ @TestApi
+ public static void setTestMode(boolean mode) {
+ PropertyInvalidatedCache.setTestMode(mode);
+ }
}
diff --git a/core/java/android/os/Looper.java b/core/java/android/os/Looper.java
index 012590510714..2fe4871e08dd 100644
--- a/core/java/android/os/Looper.java
+++ b/core/java/android/os/Looper.java
@@ -70,6 +70,13 @@ public final class Looper {
private static final String TAG = "Looper";
+ private static class NoImagePreloadHolder {
+ // Enable/Disable verbose logging with a system prop. e.g.
+ // adb shell 'setprop log.looper.slow.verbose false && stop && start'
+ private static final boolean sVerboseLogging =
+ SystemProperties.getBoolean("log.looper.slow.verbose", false);
+ }
+
// sThreadLocal.get() will return null unless you've called prepare().
@UnsupportedAppUsage
static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
@@ -246,17 +253,21 @@ public final class Looper {
}
}
if (logSlowDelivery) {
+ boolean slow = false;
+
+ if (!me.mSlowDeliveryDetected || NoImagePreloadHolder.sVerboseLogging) {
+ slow = showSlowLog(slowDeliveryThresholdMs, msg.when, dispatchStart,
+ "delivery", msg);
+ }
if (me.mSlowDeliveryDetected) {
- if ((dispatchStart - msg.when) <= 10) {
+ if (!slow && (dispatchStart - msg.when) <= 10) {
Slog.w(TAG, "Drained");
me.mSlowDeliveryDetected = false;
}
- } else {
- if (showSlowLog(slowDeliveryThresholdMs, msg.when, dispatchStart, "delivery",
- msg)) {
- // Once we write a slow delivery log, suppress until the queue drains.
- me.mSlowDeliveryDetected = true;
- }
+ } else if (slow) {
+ // A slow delivery is detected, suppressing further logs unless verbose logging
+ // is enabled.
+ me.mSlowDeliveryDetected = true;
}
}
if (logSlowDispatch) {
@@ -322,6 +333,23 @@ public final class Looper {
@android.ravenwood.annotation.RavenwoodReplace
private static int getThresholdOverride() {
+ // Allow overriding the threshold for all processes' main looper with a system prop.
+ // e.g. adb shell 'setprop log.looper.any.main.slow 1 && stop && start'
+ if (myLooper() == getMainLooper()) {
+ final int globalOverride = SystemProperties.getInt("log.looper.any.main.slow", -1);
+ if (globalOverride >= 0) {
+ return globalOverride;
+ }
+ }
+
+ // Allow overriding the threshold for all threads within a process with a system prop.
+ // e.g. adb shell 'setprop log.looper.1000.any.slow 1 && stop && start'
+ final int processOverride = SystemProperties.getInt("log.looper."
+ + Process.myUid() + ".any.slow", -1);
+ if (processOverride >= 0) {
+ return processOverride;
+ }
+
return SystemProperties.getInt("log.looper."
+ Process.myUid() + "."
+ Thread.currentThread().getName()
diff --git a/core/java/android/os/health/SystemHealthManager.java b/core/java/android/os/health/SystemHealthManager.java
index febbfca56bfb..9d0e221bd9e7 100644
--- a/core/java/android/os/health/SystemHealthManager.java
+++ b/core/java/android/os/health/SystemHealthManager.java
@@ -344,11 +344,7 @@ public class SystemHealthManager {
|| !mHintManagerClientData.supportInfo.headroom.isCpuSupported) {
throw new UnsupportedOperationException();
}
- try {
- return mHintManager.getCpuHeadroomMinIntervalMillis();
- } catch (RemoteException re) {
- throw re.rethrowFromSystemServer();
- }
+ return mHintManagerClientData.supportInfo.headroom.cpuMinIntervalMillis;
}
/**
@@ -366,11 +362,7 @@ public class SystemHealthManager {
|| !mHintManagerClientData.supportInfo.headroom.isGpuSupported) {
throw new UnsupportedOperationException();
}
- try {
- return mHintManager.getGpuHeadroomMinIntervalMillis();
- } catch (RemoteException re) {
- throw re.rethrowFromSystemServer();
- }
+ return mHintManagerClientData.supportInfo.headroom.gpuMinIntervalMillis;
}
/**
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index baaaa464a4cf..73d1e1701eec 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -1254,21 +1254,6 @@ public final class Settings {
"android.settings.TEMPERATURE_UNIT_SETTINGS";
/**
- * Activity Action: Show numbering system configuration settings.
- * <p>
- * Input: Nothing.
- * <p>
- * Output: After calling {@link android.app.Activity#startActivityForResult}, the callback
- * {@code onActivityResult} will have resultCode {@link android.app.Activity#RESULT_OK} if
- * the numbering system settings page is suitable to show on the UI. Otherwise, the result is
- * set to {@link android.app.Activity#RESULT_CANCELED}.
- */
- @FlaggedApi(Flags.FLAG_SYSTEM_REGIONAL_PREFERENCES_API_ENABLED)
- @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
- public static final String ACTION_NUMBERING_SYSTEM_SETTINGS =
- "android.settings.NUMBERING_SYSTEM_SETTINGS";
-
- /**
* Activity Action: Show measurement system configuration settings.
* <p>
* Input: Nothing.
@@ -12884,6 +12869,19 @@ public final class Settings {
*/
public static final String DISABLE_SECURE_WINDOWS = "disable_secure_windows";
+ /**
+ * Controls if the adaptive authentication feature should be disabled, which
+ * will attempt to lock the device after a number of consecutive authentication
+ * attempts fail.
+ *
+ * This can only be disabled on debuggable builds. Set to 1 to disable or 0 for the
+ * normal behavior.
+ *
+ * @hide
+ */
+ public static final String DISABLE_ADAPTIVE_AUTH_LIMIT_LOCK =
+ "disable_adaptive_auth_limit_lock";
+
/** @hide */
public static final int PRIVATE_SPACE_AUTO_LOCK_ON_DEVICE_LOCK = 0;
/** @hide */
diff --git a/core/java/android/security/flags.aconfig b/core/java/android/security/flags.aconfig
index 4a9e945e62a9..a5586227cbb3 100644
--- a/core/java/android/security/flags.aconfig
+++ b/core/java/android/security/flags.aconfig
@@ -155,4 +155,11 @@ flag {
description: "Feature flag to add the privileged flag to the SUBSCRIBE_TO_KEYGUARD_LOCKED_STATE permission"
bug: "380120712"
is_fixed_read_only: true
-} \ No newline at end of file
+}
+
+flag {
+ name: "disable_adaptive_auth_counter_lock"
+ namespace: "biometrics"
+ description: "Flag to allow an adb secure setting to disable the adaptive auth lock"
+ bug: "371057865"
+}
diff --git a/core/java/android/service/notification/StatusBarNotification.java b/core/java/android/service/notification/StatusBarNotification.java
index 146c2b6fa46e..105fa3ffd4cd 100644
--- a/core/java/android/service/notification/StatusBarNotification.java
+++ b/core/java/android/service/notification/StatusBarNotification.java
@@ -30,13 +30,17 @@ import android.metrics.LogMaker;
import android.os.Build;
import android.os.Parcel;
import android.os.Parcelable;
+import android.os.Trace;
import android.os.UserHandle;
+import android.util.ArrayMap;
import com.android.internal.logging.InstanceId;
import com.android.internal.logging.nano.MetricsProto;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
+import static com.android.window.flags.Flags.enablePerDisplayPackageContextCacheInStatusbarNotif;
import java.util.ArrayList;
+import java.util.Map;
/**
* Class encapsulating a Notification. Sent by the NotificationManagerService to clients including
@@ -69,7 +73,15 @@ public class StatusBarNotification implements Parcelable {
// A small per-notification ID, used for statsd logging.
private InstanceId mInstanceId; // Not final, see setInstanceId()
+ /**
+ * @deprecated This field is only used when
+ * {@link enablePerDisplayPackageContextCacheInStatusbarNotif}
+ * is disabled.
+ */
+ @Deprecated
private Context mContext; // used for inflation & icon expansion
+ // Maps display id to context used for remote view content inflation and status bar icon.
+ private final Map<Integer, Context> mContextForDisplayId = new ArrayMap<>();
/** @hide */
public StatusBarNotification(String pkg, String opPkg, int id,
@@ -453,7 +465,11 @@ public class StatusBarNotification implements Parcelable {
* @hide
*/
public void clearPackageContext() {
- mContext = null;
+ if (enablePerDisplayPackageContextCacheInStatusbarNotif()) {
+ mContextForDisplayId.clear();
+ } else {
+ mContext = null;
+ }
}
/**
@@ -475,21 +491,42 @@ public class StatusBarNotification implements Parcelable {
*/
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
public Context getPackageContext(Context context) {
- if (mContext == null) {
- try {
- ApplicationInfo ai = context.getPackageManager()
- .getApplicationInfoAsUser(pkg, PackageManager.MATCH_UNINSTALLED_PACKAGES,
- getNormalizedUserId());
- mContext = context.createApplicationContext(ai,
- Context.CONTEXT_RESTRICTED);
- } catch (PackageManager.NameNotFoundException e) {
- mContext = null;
+ if (enablePerDisplayPackageContextCacheInStatusbarNotif()) {
+ if (context == null) return null;
+ return mContextForDisplayId.computeIfAbsent(context.getDisplayId(),
+ (displayId) -> createPackageContext(context));
+ } else {
+ if (mContext == null) {
+ try {
+ ApplicationInfo ai = context.getPackageManager()
+ .getApplicationInfoAsUser(pkg,
+ PackageManager.MATCH_UNINSTALLED_PACKAGES,
+ getNormalizedUserId());
+ mContext = context.createApplicationContext(ai,
+ Context.CONTEXT_RESTRICTED);
+ } catch (PackageManager.NameNotFoundException e) {
+ mContext = null;
+ }
+ }
+ if (mContext == null) {
+ mContext = context;
}
+ return mContext;
}
- if (mContext == null) {
- mContext = context;
+ }
+
+ private Context createPackageContext(Context context) {
+ try {
+ Trace.beginSection("StatusBarNotification#createPackageContext");
+ ApplicationInfo ai = context.getPackageManager()
+ .getApplicationInfoAsUser(pkg, PackageManager.MATCH_UNINSTALLED_PACKAGES,
+ getNormalizedUserId());
+ return context.createApplicationContext(ai, Context.CONTEXT_RESTRICTED);
+ } catch (PackageManager.NameNotFoundException e) {
+ return context;
+ } finally {
+ Trace.endSection();
}
- return mContext;
}
/**
diff --git a/core/java/android/service/settings/preferences/SettingsPreferenceValue.java b/core/java/android/service/settings/preferences/SettingsPreferenceValue.java
index eea93b321e47..2e661b475dc9 100644
--- a/core/java/android/service/settings/preferences/SettingsPreferenceValue.java
+++ b/core/java/android/service/settings/preferences/SettingsPreferenceValue.java
@@ -34,8 +34,7 @@ import java.lang.annotation.RetentionPolicy;
* This objects represents a value that can be used for a particular settings preference.
* <p>The data type for the value will correspond to {@link #getType}. For possible types, see
* constants below, such as {@link #TYPE_BOOLEAN} and {@link #TYPE_STRING}.
- * Depending on the type, the corresponding getter will contain its value. All other getters will
- * return default values (boolean returns false, String returns null) so they should not be used.
+ * Depending on the type, the corresponding getter will contain its value.
* <p>See documentation on the constants for which getter method should be used.
*/
@FlaggedApi(Flags.FLAG_SETTINGS_CATALYST)
@@ -43,12 +42,7 @@ public final class SettingsPreferenceValue implements Parcelable {
@Type
private final int mType;
- private final boolean mBooleanValue;
- private final int mIntValue;
- private final long mLongValue;
- private final double mDoubleValue;
- @Nullable
- private final String mStringValue;
+ private final @Nullable Object mValue;
/**
* Returns the type indicator for Preference value.
@@ -59,39 +53,39 @@ public final class SettingsPreferenceValue implements Parcelable {
}
/**
- * Returns the boolean value for Preference if type is {@link #TYPE_BOOLEAN}.
+ * Returns the boolean value for Preference, the type must be {@link #TYPE_BOOLEAN}.
*/
public boolean getBooleanValue() {
- return mBooleanValue;
+ return (boolean) mValue;
}
/**
- * Returns the int value for Preference if type is {@link #TYPE_INT}.
+ * Returns the int value for Preference, the type must be {@link #TYPE_INT}.
*/
public int getIntValue() {
- return mIntValue;
+ return (int) mValue;
}
/**
- * Returns the long value for Preference if type is {@link #TYPE_LONG}.
+ * Returns the long value for Preference, the type must be {@link #TYPE_LONG}.
*/
public long getLongValue() {
- return mLongValue;
+ return (long) mValue;
}
/**
- * Returns the double value for Preference if type is {@link #TYPE_DOUBLE}.
+ * Returns the double value for Preference, the type must be {@link #TYPE_DOUBLE}.
*/
public double getDoubleValue() {
- return mDoubleValue;
+ return (double) mValue;
}
/**
- * Returns the string value for Preference if type is {@link #TYPE_STRING}.
+ * Returns the string value for Preference, the type must be {@link #TYPE_STRING}.
*/
@Nullable
public String getStringValue() {
- return mStringValue;
+ return (String) mValue;
}
/** @hide */
@@ -115,34 +109,47 @@ public final class SettingsPreferenceValue implements Parcelable {
public static final int TYPE_STRING = 3;
/** Value is of type int. Access via {@link #getIntValue}. */
public static final int TYPE_INT = 4;
+ /** Max type value. */
+ private static final int MAX_TYPE_VALUE = TYPE_INT;
private SettingsPreferenceValue(@NonNull Builder builder) {
mType = builder.mType;
- mBooleanValue = builder.mBooleanValue;
- mLongValue = builder.mLongValue;
- mDoubleValue = builder.mDoubleValue;
- mStringValue = builder.mStringValue;
- mIntValue = builder.mIntValue;
+ mValue = builder.mValue;
}
private SettingsPreferenceValue(@NonNull Parcel in) {
mType = in.readInt();
- mBooleanValue = in.readBoolean();
- mLongValue = in.readLong();
- mDoubleValue = in.readDouble();
- mStringValue = in.readString8();
- mIntValue = in.readInt();
+ if (mType == TYPE_BOOLEAN) {
+ mValue = in.readBoolean();
+ } else if (mType == TYPE_LONG) {
+ mValue = in.readLong();
+ } else if (mType == TYPE_DOUBLE) {
+ mValue = in.readDouble();
+ } else if (mType == TYPE_STRING) {
+ mValue = in.readString();
+ } else if (mType == TYPE_INT) {
+ mValue = in.readInt();
+ } else {
+ // throw exception immediately, further read to Parcel may be invalid
+ throw new IllegalStateException("Unknown type: " + mType);
+ }
}
/** @hide */
@Override
public void writeToParcel(@NonNull Parcel dest, int flags) {
dest.writeInt(mType);
- dest.writeBoolean(mBooleanValue);
- dest.writeLong(mLongValue);
- dest.writeDouble(mDoubleValue);
- dest.writeString8(mStringValue);
- dest.writeInt(mIntValue);
+ if (mType == TYPE_BOOLEAN) {
+ dest.writeBoolean(getBooleanValue());
+ } else if (mType == TYPE_LONG) {
+ dest.writeLong(getLongValue());
+ } else if (mType == TYPE_DOUBLE) {
+ dest.writeDouble(getDoubleValue());
+ } else if (mType == TYPE_STRING) {
+ dest.writeString(getStringValue());
+ } else if (mType == TYPE_INT) {
+ dest.writeInt(getIntValue());
+ }
}
/** @hide */
@@ -174,17 +181,16 @@ public final class SettingsPreferenceValue implements Parcelable {
public static final class Builder {
@Type
private final int mType;
- private boolean mBooleanValue;
- private long mLongValue;
- private double mDoubleValue;
- private String mStringValue;
- private int mIntValue;
+ private @Nullable Object mValue;
/**
* Create Builder instance.
* @param type type indicator for preference value
*/
public Builder(@Type int type) {
+ if (type < 0 || type > MAX_TYPE_VALUE) {
+ throw new IllegalArgumentException("Unknown type: " + type);
+ }
mType = type;
}
@@ -194,7 +200,8 @@ public final class SettingsPreferenceValue implements Parcelable {
@SuppressLint("MissingGetterMatchingBuilder")
@NonNull
public Builder setBooleanValue(boolean booleanValue) {
- mBooleanValue = booleanValue;
+ checkType(TYPE_BOOLEAN);
+ mValue = booleanValue;
return this;
}
@@ -203,7 +210,8 @@ public final class SettingsPreferenceValue implements Parcelable {
*/
@NonNull
public Builder setIntValue(int intValue) {
- mIntValue = intValue;
+ checkType(TYPE_INT);
+ mValue = intValue;
return this;
}
@@ -212,7 +220,8 @@ public final class SettingsPreferenceValue implements Parcelable {
*/
@NonNull
public Builder setLongValue(long longValue) {
- mLongValue = longValue;
+ checkType(TYPE_LONG);
+ mValue = longValue;
return this;
}
@@ -221,7 +230,8 @@ public final class SettingsPreferenceValue implements Parcelable {
*/
@NonNull
public Builder setDoubleValue(double doubleValue) {
- mDoubleValue = doubleValue;
+ checkType(TYPE_DOUBLE);
+ mValue = doubleValue;
return this;
}
@@ -230,10 +240,17 @@ public final class SettingsPreferenceValue implements Parcelable {
*/
@NonNull
public Builder setStringValue(@Nullable String stringValue) {
- mStringValue = stringValue;
+ checkType(TYPE_STRING);
+ mValue = stringValue;
return this;
}
+ private void checkType(int type) {
+ if (mType != type) {
+ throw new IllegalArgumentException("Type is: " + mType);
+ }
+ }
+
/**
* Constructs an immutable {@link SettingsPreferenceValue} object.
*/
diff --git a/core/java/android/text/Layout.java b/core/java/android/text/Layout.java
index 3c53506990d1..323d83b92143 100644
--- a/core/java/android/text/Layout.java
+++ b/core/java/android/text/Layout.java
@@ -1066,6 +1066,12 @@ public abstract class Layout {
var hasBgColorChanged = newBackground != bgPaint.getColor();
if (lineNum != mLastLineNum || hasBgColorChanged) {
+ // Skip processing if the character is a space or a tap to avoid
+ // rendering an abrupt, empty rectangle.
+ if (Character.isWhitespace(mText.charAt(index))) {
+ return;
+ }
+
// Draw what we have so far, then reset the rect and update its color
drawRect();
mLineBackground.set(left, top, right, bottom);
diff --git a/core/java/android/view/InsetsAnimationControlImpl.java b/core/java/android/view/InsetsAnimationControlImpl.java
index 4fead2ad5246..6decd6d3a603 100644
--- a/core/java/android/view/InsetsAnimationControlImpl.java
+++ b/core/java/android/view/InsetsAnimationControlImpl.java
@@ -112,6 +112,7 @@ public class InsetsAnimationControlImpl implements InternalInsetsAnimationContro
private Insets mPendingInsets;
private float mPendingFraction;
private boolean mFinished;
+ private boolean mCancelling;
private boolean mCancelled;
private boolean mShownOnFinish;
private float mCurrentAlpha = 1.0f;
@@ -371,7 +372,7 @@ public class InsetsAnimationControlImpl implements InternalInsetsAnimationContro
mPendingInsets = mLayoutInsetsDuringAnimation == LAYOUT_INSETS_DURING_ANIMATION_SHOWN
? mShownInsets : mHiddenInsets;
mPendingAlpha = 1f;
- mPendingFraction = 1f;
+ mCancelling = true;
applyChangeInsets(null);
mCancelled = true;
mListener.onCancelled(mReadyDispatched ? this : null);
@@ -488,15 +489,15 @@ public class InsetsAnimationControlImpl implements InternalInsetsAnimationContro
return;
}
- final boolean visible = mPendingFraction == 0
- // The first frame of ANIMATION_TYPE_SHOW should be invisible since it is
- // animated from the hidden state.
- ? mAnimationType != ANIMATION_TYPE_SHOW
- : mPendingFraction < 1f || (mFinished
- ? mShownOnFinish
- // If the animation is cancelled, mFinished and mShownOnFinish are not set.
+ final boolean visible = mFinished
+ ? mShownOnFinish
+ : (mCancelling
+ // If the animation is being cancelled, mShownOnFinish is not valid.
// Here uses mLayoutInsetsDuringAnimation to decide if it should be visible.
- : mLayoutInsetsDuringAnimation == LAYOUT_INSETS_DURING_ANIMATION_SHOWN);
+ ? mLayoutInsetsDuringAnimation == LAYOUT_INSETS_DURING_ANIMATION_SHOWN
+ // The first frame of ANIMATION_TYPE_SHOW should be invisible since it is
+ // animated from the hidden state.
+ : (mAnimationType != ANIMATION_TYPE_SHOW || mPendingFraction != 0));
// TODO: Implement behavior when inset spans over multiple types
for (int i = controls.size() - 1; i >= 0; i--) {
diff --git a/core/java/android/view/SurfaceControl.java b/core/java/android/view/SurfaceControl.java
index e665c08c63e4..d7cf3e827695 100644
--- a/core/java/android/view/SurfaceControl.java
+++ b/core/java/android/view/SurfaceControl.java
@@ -4865,7 +4865,7 @@ public final class SurfaceControl implements Parcelable {
/**
* @hide
*/
- public Transaction setDesintationFrame(SurfaceControl sc, @NonNull Rect destinationFrame) {
+ public Transaction setDestinationFrame(SurfaceControl sc, @NonNull Rect destinationFrame) {
checkPreconditions(sc);
nativeSetDestinationFrame(mNativeObject, sc.mNativeObject,
destinationFrame.left, destinationFrame.top, destinationFrame.right,
@@ -4876,7 +4876,7 @@ public final class SurfaceControl implements Parcelable {
/**
* @hide
*/
- public Transaction setDesintationFrame(SurfaceControl sc, int width, int height) {
+ public Transaction setDestinationFrame(SurfaceControl sc, int width, int height) {
checkPreconditions(sc);
nativeSetDestinationFrame(mNativeObject, sc.mNativeObject, 0, 0, width, height);
return this;
diff --git a/core/java/android/view/SurfaceView.java b/core/java/android/view/SurfaceView.java
index b0051cefb21b..780e76122e8a 100644
--- a/core/java/android/view/SurfaceView.java
+++ b/core/java/android/view/SurfaceView.java
@@ -1125,7 +1125,7 @@ public class SurfaceView extends View implements ViewRootImpl.SurfaceChangedCall
}
}
- surfaceUpdateTransaction.setDesintationFrame(mBlastSurfaceControl, mSurfaceWidth,
+ surfaceUpdateTransaction.setDestinationFrame(mBlastSurfaceControl, mSurfaceWidth,
mSurfaceHeight);
if (isHardwareAccelerated()) {
diff --git a/core/java/android/window/flags/lse_desktop_experience.aconfig b/core/java/android/window/flags/lse_desktop_experience.aconfig
index be0b4fea459c..b4e7675402b9 100644
--- a/core/java/android/window/flags/lse_desktop_experience.aconfig
+++ b/core/java/android/window/flags/lse_desktop_experience.aconfig
@@ -463,6 +463,13 @@ flag {
}
flag {
+ name: "enable_connected_displays_pip"
+ namespace: "lse_desktop_experience"
+ description: "Enables PiP features in connected displays."
+ bug: "362721131"
+}
+
+flag {
name: "reparent_window_token_api"
namespace: "lse_desktop_experience"
description: "Allows to reparent a window token to a different display"
@@ -531,6 +538,16 @@ flag {
}
flag {
+ name: "enable_per_display_package_context_cache_in_statusbar_notif"
+ namespace: "lse_desktop_experience"
+ description: "Enables per-display package context caching in StatusBarNotification"
+ bug: "388886443"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
+
+flag {
name: "enable_desktop_wallpaper_activity_for_system_user"
namespace: "lse_desktop_experience"
description: "Enables starting DesktopWallpaperActivity on system user."
diff --git a/core/java/com/android/internal/accessibility/dialog/AccessibilityTargetHelper.java b/core/java/com/android/internal/accessibility/dialog/AccessibilityTargetHelper.java
index cf3a54b8437f..b187eb42366f 100644
--- a/core/java/com/android/internal/accessibility/dialog/AccessibilityTargetHelper.java
+++ b/core/java/com/android/internal/accessibility/dialog/AccessibilityTargetHelper.java
@@ -181,7 +181,8 @@ public final class AccessibilityTargetHelper {
final InvisibleToggleAllowListingFeatureTarget magnification =
new InvisibleToggleAllowListingFeatureTarget(context,
shortcutType,
- isShortcutContained(context, shortcutType, MAGNIFICATION_CONTROLLER_NAME),
+ isShortcutContained(
+ context, shortcutType, MAGNIFICATION_CONTROLLER_NAME),
MAGNIFICATION_CONTROLLER_NAME,
uid,
context.getString(R.string.accessibility_magnification_chooser_text),
diff --git a/core/java/com/android/internal/accessibility/util/ShortcutUtils.java b/core/java/com/android/internal/accessibility/util/ShortcutUtils.java
index 14ca0f8cae69..fc3c48d19b68 100644
--- a/core/java/com/android/internal/accessibility/util/ShortcutUtils.java
+++ b/core/java/com/android/internal/accessibility/util/ShortcutUtils.java
@@ -25,6 +25,7 @@ import static com.android.internal.accessibility.AccessibilityShortcutController
import static com.android.internal.accessibility.common.ShortcutConstants.AccessibilityFragmentType.INVISIBLE_TOGGLE;
import static com.android.internal.accessibility.common.ShortcutConstants.SERVICES_SEPARATOR;
import static com.android.internal.accessibility.common.ShortcutConstants.USER_SHORTCUT_TYPES;
+import static com.android.internal.accessibility.common.ShortcutConstants.UserShortcutType.DEFAULT;
import static com.android.internal.accessibility.common.ShortcutConstants.UserShortcutType.GESTURE;
import static com.android.internal.accessibility.common.ShortcutConstants.UserShortcutType.HARDWARE;
import static com.android.internal.accessibility.common.ShortcutConstants.UserShortcutType.KEY_GESTURE;
@@ -157,19 +158,42 @@ public final class ShortcutUtils {
}
/**
- * Returns if a {@code shortcutType} shortcut contains {@code componentId}.
+ * Returns if a {@code shortcutType} shortcut contains {@code componentName}.
*
* @param context The current context.
* @param shortcutType The preferred shortcut type user selected.
- * @param componentId The component id that need to be checked.
- * @return {@code true} if a component id is contained.
+ * @param componentName The component that need to be checked.
+ * @return {@code true} if the shortcut contains {@code componentName}.
*/
- public static boolean isShortcutContained(Context context, @UserShortcutType int shortcutType,
- @NonNull String componentId) {
- final AccessibilityManager am = (AccessibilityManager) context.getSystemService(
- Context.ACCESSIBILITY_SERVICE);
- final List<String> requiredTargets = am.getAccessibilityShortcutTargets(shortcutType);
- return requiredTargets.contains(componentId);
+ @SuppressLint("MissingPermission")
+ public static boolean isShortcutContained(
+ Context context, @UserShortcutType int shortcutType, @NonNull String componentName) {
+ AccessibilityManager manager = context.getSystemService(AccessibilityManager.class);
+ if (manager != null) {
+ return manager
+ .getAccessibilityShortcutTargets(shortcutType).contains(componentName);
+ } else {
+ return false;
+ }
+ }
+
+ /**
+ * Returns every shortcut type that currently has the provided componentName as a target.
+ * Types are returned as a singular flag integer.
+ * If none have the componentName, returns {@link UserShortcutType#DEFAULT}
+ */
+ public static int getEnabledShortcutTypes(
+ Context context, String componentName) {
+ final AccessibilityManager am = context.getSystemService(AccessibilityManager.class);
+ if (am == null) return DEFAULT;
+
+ int shortcutTypes = DEFAULT;
+ for (int shortcutType : USER_SHORTCUT_TYPES) {
+ if (am.getAccessibilityShortcutTargets(shortcutType).contains(componentName)) {
+ shortcutTypes |= shortcutType;
+ }
+ }
+ return shortcutTypes;
}
/**
@@ -229,8 +253,7 @@ public final class ShortcutUtils {
*/
public static void updateInvisibleToggleAccessibilityServiceEnableState(
Context context, Set<String> componentNames, int userId) {
- final AccessibilityManager am = (AccessibilityManager) context.getSystemService(
- Context.ACCESSIBILITY_SERVICE);
+ final AccessibilityManager am = context.getSystemService(AccessibilityManager.class);
if (am == null) return;
final List<AccessibilityServiceInfo> installedServices =
diff --git a/core/java/com/android/internal/widget/NotificationProgressBar.java b/core/java/com/android/internal/widget/NotificationProgressBar.java
index 904b73f41e70..1b770207f2cb 100644
--- a/core/java/com/android/internal/widget/NotificationProgressBar.java
+++ b/core/java/com/android/internal/widget/NotificationProgressBar.java
@@ -836,11 +836,16 @@ public final class NotificationProgressBar extends ProgressBar implements
} else if (part instanceof Point point) {
final float pointWidth = 2 * pointRadius;
float start = x - pointRadius;
- if (start < 0) start = 0;
- float end = start + pointWidth;
- if (end > totalWidth) {
+ float end = x + pointRadius;
+ // Only shift the points right at the start/end.
+ // For the points close to the start/end, the segment minimum width requirement
+ // would take care of shifting them to be within the bounds.
+ if (x == 0) {
+ start = 0;
+ end = pointWidth;
+ } else if (x == totalWidth) {
+ start = totalWidth - pointWidth;
end = totalWidth;
- if (totalWidth > pointWidth) start = totalWidth - pointWidth;
}
drawableParts.add(new DrawablePoint(start, end, point.mColor));
@@ -853,7 +858,7 @@ public final class NotificationProgressBar extends ProgressBar implements
private static float getSegStartOffset(Part prevPart, float pointRadius, float segPointGap,
float startX) {
if (!(prevPart instanceof Point)) return 0F;
- final float pointOffset = (startX < pointRadius) ? (pointRadius - startX) : 0;
+ final float pointOffset = (startX == 0) ? pointRadius : 0;
return pointOffset + pointRadius + segPointGap;
}
@@ -869,9 +874,7 @@ public final class NotificationProgressBar extends ProgressBar implements
return segSegGap;
}
- final float pointWidth = 2 * pointRadius;
- final float pointOffset = (endX + pointRadius > totalWidth && totalWidth > pointWidth)
- ? (endX + pointRadius - totalWidth) : 0;
+ final float pointOffset = (endX == totalWidth) ? pointRadius : 0;
return segPointGap + pointRadius + pointOffset;
}
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index 8372aecf0d27..8bf61bbcf55e 100644
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -7590,7 +7590,7 @@
<!-- Minimum required drawing width. The drawing width refers to the width after
the original segments have been adjusted for the neighboring Points and gaps. This is
enforced by stretching the segments that are too short. -->
- <attr name="minWidth" format="dimension" />
+ <attr name="minWidth" />
<!-- Height of the solid segments. -->
<attr name="height" />
<!-- Height of the faded segments. -->
diff --git a/core/tests/coretests/src/android/app/NotificationManagerTest.java b/core/tests/coretests/src/android/app/NotificationManagerTest.java
index 3d6e1225bd92..18ba6a16bf72 100644
--- a/core/tests/coretests/src/android/app/NotificationManagerTest.java
+++ b/core/tests/coretests/src/android/app/NotificationManagerTest.java
@@ -19,6 +19,7 @@ package android.app;
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.atLeast;
@@ -269,8 +270,9 @@ public class NotificationManagerTest {
// It doesn't matter what the returned contents are, as long as we return a channel.
// This setup must set up getNotificationChannels(), as that's the method called.
- when(mNotificationManager.mBackendService.getNotificationChannels(any(), any(),
- anyInt())).thenReturn(new ParceledListSlice<>(List.of(exampleChannel())));
+ when(mNotificationManager.mBackendService.getOrCreateNotificationChannels(any(), any(),
+ anyInt(), anyBoolean())).thenReturn(
+ new ParceledListSlice<>(List.of(exampleChannel())));
// ask for the same channel 100 times without invalidating the cache
for (int i = 0; i < 100; i++) {
@@ -282,7 +284,7 @@ public class NotificationManagerTest {
NotificationChannel unused = mNotificationManager.getNotificationChannel("id");
verify(mNotificationManager.mBackendService, times(2))
- .getNotificationChannels(any(), any(), anyInt());
+ .getOrCreateNotificationChannels(any(), any(), anyInt(), anyBoolean());
}
@Test
@@ -295,23 +297,24 @@ public class NotificationManagerTest {
NotificationChannel c2 = new NotificationChannel("id2", "name2",
NotificationManager.IMPORTANCE_NONE);
- when(mNotificationManager.mBackendService.getNotificationChannels(any(), any(),
- anyInt())).thenReturn(new ParceledListSlice<>(List.of(c1, c2)));
+ when(mNotificationManager.mBackendService.getOrCreateNotificationChannels(any(), any(),
+ anyInt(), anyBoolean())).thenReturn(new ParceledListSlice<>(List.of(c1, c2)));
assertThat(mNotificationManager.getNotificationChannel("id1")).isEqualTo(c1);
assertThat(mNotificationManager.getNotificationChannel("id2")).isEqualTo(c2);
assertThat(mNotificationManager.getNotificationChannel("id3")).isNull();
verify(mNotificationManager.mBackendService, times(1))
- .getNotificationChannels(any(), any(), anyInt());
+ .getOrCreateNotificationChannels(any(), any(), anyInt(), anyBoolean());
}
@Test
@EnableFlags(Flags.FLAG_NM_BINDER_PERF_CACHE_CHANNELS)
public void getNotificationChannels_cachedUntilInvalidated() throws Exception {
NotificationManager.invalidateNotificationChannelCache();
- when(mNotificationManager.mBackendService.getNotificationChannels(any(), any(),
- anyInt())).thenReturn(new ParceledListSlice<>(List.of(exampleChannel())));
+ when(mNotificationManager.mBackendService.getOrCreateNotificationChannels(any(), any(),
+ anyInt(), anyBoolean())).thenReturn(
+ new ParceledListSlice<>(List.of(exampleChannel())));
// ask for channels 100 times without invalidating the cache
for (int i = 0; i < 100; i++) {
@@ -323,7 +326,7 @@ public class NotificationManagerTest {
List<NotificationChannel> res = mNotificationManager.getNotificationChannels();
verify(mNotificationManager.mBackendService, times(2))
- .getNotificationChannels(any(), any(), anyInt());
+ .getOrCreateNotificationChannels(any(), any(), anyInt(), anyBoolean());
assertThat(res).containsExactlyElementsIn(List.of(exampleChannel()));
}
@@ -341,8 +344,9 @@ public class NotificationManagerTest {
NotificationChannel c2 = new NotificationChannel("other", "name2",
NotificationManager.IMPORTANCE_DEFAULT);
- when(mNotificationManager.mBackendService.getNotificationChannels(any(), any(), anyInt()))
- .thenReturn(new ParceledListSlice<>(List.of(c1, conv1, c2)));
+ when(mNotificationManager.mBackendService.getOrCreateNotificationChannels(any(), any(),
+ anyInt(), anyBoolean())).thenReturn(
+ new ParceledListSlice<>(List.of(c1, conv1, c2)));
// Lookup for channel c1 and c2: returned as expected
assertThat(mNotificationManager.getNotificationChannel("id")).isEqualTo(c1);
@@ -359,9 +363,9 @@ public class NotificationManagerTest {
// Lookup of a nonexistent channel is null
assertThat(mNotificationManager.getNotificationChannel("id3")).isNull();
- // All of that should have been one call to getNotificationChannels()
+ // All of that should have been one call to getOrCreateNotificationChannels()
verify(mNotificationManager.mBackendService, times(1))
- .getNotificationChannels(any(), any(), anyInt());
+ .getOrCreateNotificationChannels(any(), any(), anyInt(), anyBoolean());
}
@Test
@@ -381,12 +385,12 @@ public class NotificationManagerTest {
NotificationChannel channel3 = channel1.copy();
channel3.setName("name3");
- when(mNotificationManager.mBackendService.getNotificationChannels(any(), eq(pkg1),
- eq(userId))).thenReturn(new ParceledListSlice<>(List.of(channel1)));
- when(mNotificationManager.mBackendService.getNotificationChannels(any(), eq(pkg2),
- eq(userId))).thenReturn(new ParceledListSlice<>(List.of(channel2)));
- when(mNotificationManager.mBackendService.getNotificationChannels(any(), eq(pkg1),
- eq(userId1))).thenReturn(new ParceledListSlice<>(List.of(channel3)));
+ when(mNotificationManager.mBackendService.getOrCreateNotificationChannels(any(), eq(pkg1),
+ eq(userId), anyBoolean())).thenReturn(new ParceledListSlice<>(List.of(channel1)));
+ when(mNotificationManager.mBackendService.getOrCreateNotificationChannels(any(), eq(pkg2),
+ eq(userId), anyBoolean())).thenReturn(new ParceledListSlice<>(List.of(channel2)));
+ when(mNotificationManager.mBackendService.getOrCreateNotificationChannels(any(), eq(pkg1),
+ eq(userId1), anyBoolean())).thenReturn(new ParceledListSlice<>(List.of(channel3)));
// set our context to pretend to be from package 1 and userId 0
mContext.setParameters(pkg1, pkg1, userId);
@@ -402,7 +406,7 @@ public class NotificationManagerTest {
// Those should have been three different calls
verify(mNotificationManager.mBackendService, times(3))
- .getNotificationChannels(any(), any(), anyInt());
+ .getOrCreateNotificationChannels(any(), any(), anyInt(), anyBoolean());
}
private Notification exampleNotification() {
diff --git a/core/tests/coretests/src/android/os/SystemHealthManagerUnitTest.java b/core/tests/coretests/src/android/os/SystemHealthManagerUnitTest.java
new file mode 100644
index 000000000000..1f9d4278b12e
--- /dev/null
+++ b/core/tests/coretests/src/android/os/SystemHealthManagerUnitTest.java
@@ -0,0 +1,155 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.os;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.hardware.power.CpuHeadroomResult;
+import android.hardware.power.GpuHeadroomResult;
+import android.hardware.power.SupportInfo;
+import android.os.health.SystemHealthManager;
+import android.platform.test.annotations.DisabledOnRavenwood;
+
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+
+import com.android.internal.app.IBatteryStats;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+@RunWith(AndroidJUnit4.class)
+@DisabledOnRavenwood(blockedBy = SystemHealthManager.class)
+public class SystemHealthManagerUnitTest {
+ @Mock
+ private IBatteryStats mBatteryStats;
+ @Mock
+ private IPowerStatsService mPowerStats;
+ @Mock
+ private IHintManager mHintManager;
+ private SystemHealthManager mSystemHealthManager;
+
+ @Before
+ public void setUp() throws Exception {
+ MockitoAnnotations.initMocks(this);
+ IHintManager.HintManagerClientData clientData = new IHintManager.HintManagerClientData();
+ clientData.supportInfo = new SupportInfo();
+ clientData.maxCpuHeadroomThreads = 10;
+ clientData.supportInfo.headroom = new SupportInfo.HeadroomSupportInfo();
+ clientData.supportInfo.headroom.isCpuSupported = true;
+ clientData.supportInfo.headroom.isGpuSupported = true;
+ clientData.supportInfo.headroom.cpuMinCalculationWindowMillis = 45;
+ clientData.supportInfo.headroom.cpuMaxCalculationWindowMillis = 9999;
+ clientData.supportInfo.headroom.gpuMinCalculationWindowMillis = 46;
+ clientData.supportInfo.headroom.gpuMaxCalculationWindowMillis = 9998;
+ clientData.supportInfo.headroom.cpuMinIntervalMillis = 999;
+ clientData.supportInfo.headroom.gpuMinIntervalMillis = 998;
+ when(mHintManager.getClientData()).thenReturn(clientData);
+ mSystemHealthManager = new SystemHealthManager(mBatteryStats, mPowerStats, mHintManager);
+ }
+
+ @Test
+ public void testHeadroomParamsValueRange() {
+ assertEquals(999, mSystemHealthManager.getCpuHeadroomMinIntervalMillis());
+ assertEquals(998, mSystemHealthManager.getGpuHeadroomMinIntervalMillis());
+ assertEquals(45, (int) mSystemHealthManager.getCpuHeadroomCalculationWindowRange().first);
+ assertEquals(9999,
+ (int) mSystemHealthManager.getCpuHeadroomCalculationWindowRange().second);
+ assertEquals(46, (int) mSystemHealthManager.getGpuHeadroomCalculationWindowRange().first);
+ assertEquals(9998,
+ (int) mSystemHealthManager.getGpuHeadroomCalculationWindowRange().second);
+ assertEquals(10, (int) mSystemHealthManager.getMaxCpuHeadroomTidsSize());
+ }
+
+ @Test
+ public void testGetCpuHeadroom() throws RemoteException, InterruptedException {
+ final CpuHeadroomParams params1 = null;
+ final CpuHeadroomParamsInternal internalParams1 = new CpuHeadroomParamsInternal();
+
+ final CpuHeadroomParams params2 = new CpuHeadroomParams.Builder()
+ .setCalculationWindowMillis(100)
+ .build();
+ final CpuHeadroomParamsInternal internalParams2 = new CpuHeadroomParamsInternal();
+ internalParams2.calculationWindowMillis = 100;
+
+ final CpuHeadroomParams params3 = new CpuHeadroomParams.Builder()
+ .setCalculationType(CpuHeadroomParams.CPU_HEADROOM_CALCULATION_TYPE_AVERAGE)
+ .build();
+ final CpuHeadroomParamsInternal internalParams3 = new CpuHeadroomParamsInternal();
+ internalParams3.calculationType =
+ (byte) CpuHeadroomParams.CPU_HEADROOM_CALCULATION_TYPE_AVERAGE;
+
+ final CpuHeadroomParams params4 = new CpuHeadroomParams.Builder()
+ .setTids(1000, 1001)
+ .build();
+ final CpuHeadroomParamsInternal internalParams4 = new CpuHeadroomParamsInternal();
+ internalParams4.tids = new int[]{1000, 1001};
+
+ when(mHintManager.getCpuHeadroom(internalParams1)).thenReturn(
+ CpuHeadroomResult.globalHeadroom(99f));
+ when(mHintManager.getCpuHeadroom(internalParams2)).thenReturn(
+ CpuHeadroomResult.globalHeadroom(98f));
+ when(mHintManager.getCpuHeadroom(internalParams3)).thenReturn(
+ CpuHeadroomResult.globalHeadroom(97f));
+ when(mHintManager.getCpuHeadroom(internalParams4)).thenReturn(null);
+
+ assertEquals(99f, mSystemHealthManager.getCpuHeadroom(params1), 0.001f);
+ assertEquals(98f, mSystemHealthManager.getCpuHeadroom(params2), 0.001f);
+ assertEquals(97f, mSystemHealthManager.getCpuHeadroom(params3), 0.001f);
+ assertTrue(Float.isNaN(mSystemHealthManager.getCpuHeadroom(params4)));
+ verify(mHintManager, times(1)).getCpuHeadroom(internalParams1);
+ verify(mHintManager, times(1)).getCpuHeadroom(internalParams2);
+ verify(mHintManager, times(1)).getCpuHeadroom(internalParams3);
+ verify(mHintManager, times(1)).getCpuHeadroom(internalParams4);
+ }
+
+ @Test
+ public void testGetGpuHeadroom() throws RemoteException, InterruptedException {
+ final GpuHeadroomParams params1 = null;
+ final GpuHeadroomParamsInternal internalParams1 = new GpuHeadroomParamsInternal();
+ final GpuHeadroomParams params2 = new GpuHeadroomParams.Builder()
+ .setCalculationWindowMillis(100)
+ .build();
+ final GpuHeadroomParamsInternal internalParams2 = new GpuHeadroomParamsInternal();
+ internalParams2.calculationWindowMillis = 100;
+ final GpuHeadroomParams params3 = new GpuHeadroomParams.Builder()
+ .setCalculationType(GpuHeadroomParams.GPU_HEADROOM_CALCULATION_TYPE_AVERAGE)
+ .build();
+ final GpuHeadroomParamsInternal internalParams3 = new GpuHeadroomParamsInternal();
+ internalParams3.calculationType =
+ (byte) GpuHeadroomParams.GPU_HEADROOM_CALCULATION_TYPE_AVERAGE;
+
+ when(mHintManager.getGpuHeadroom(internalParams1)).thenReturn(
+ GpuHeadroomResult.globalHeadroom(99f));
+ when(mHintManager.getGpuHeadroom(internalParams2)).thenReturn(
+ GpuHeadroomResult.globalHeadroom(98f));
+ when(mHintManager.getGpuHeadroom(internalParams3)).thenReturn(null);
+
+ assertEquals(99f, mSystemHealthManager.getGpuHeadroom(params1), 0.001f);
+ assertEquals(98f, mSystemHealthManager.getGpuHeadroom(params2), 0.001f);
+ assertTrue(Float.isNaN(mSystemHealthManager.getGpuHeadroom(params3)));
+ verify(mHintManager, times(1)).getGpuHeadroom(internalParams1);
+ verify(mHintManager, times(1)).getGpuHeadroom(internalParams2);
+ verify(mHintManager, times(1)).getGpuHeadroom(internalParams3);
+ }
+}
diff --git a/core/tests/coretests/src/android/service/notification/StatusBarNotificationTest.java b/core/tests/coretests/src/android/service/notification/StatusBarNotificationTest.java
index 504240812559..7a4cc7f98a1a 100644
--- a/core/tests/coretests/src/android/service/notification/StatusBarNotificationTest.java
+++ b/core/tests/coretests/src/android/service/notification/StatusBarNotificationTest.java
@@ -22,6 +22,7 @@ import static junit.framework.Assert.assertNotSame;
import static junit.framework.Assert.assertNull;
import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertSame;
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.mock;
@@ -35,14 +36,19 @@ import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.metrics.LogMaker;
import android.os.UserHandle;
+import android.platform.test.annotations.EnableFlags;
+import android.platform.test.flag.junit.SetFlagsRule;
+import android.view.Display;
import androidx.test.InstrumentationRegistry;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
+import com.android.window.flags.Flags;
import org.junit.Before;
+import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
@@ -52,11 +58,16 @@ import org.mockito.MockitoAnnotations;
@SmallTest
public class StatusBarNotificationTest {
+ @Rule
+ public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
+
private final Context mMockContext = mock(Context.class);
@Mock
private Context mRealContext;
@Mock
private PackageManager mPm;
+ @Mock
+ private Context mSecondaryDisplayContext;
private static final String PKG = "com.example.o";
private static final int UID = 9583;
@@ -80,6 +91,10 @@ public class StatusBarNotificationTest {
InstrumentationRegistry.getContext().getResources());
when(mMockContext.getPackageManager()).thenReturn(mPm);
when(mMockContext.getApplicationInfo()).thenReturn(new ApplicationInfo());
+ when(mMockContext.getDisplayId()).thenReturn(Display.DEFAULT_DISPLAY);
+ when(mSecondaryDisplayContext.getPackageManager()).thenReturn(mPm);
+ when(mSecondaryDisplayContext.getApplicationInfo()).thenReturn(new ApplicationInfo());
+ when(mSecondaryDisplayContext.getDisplayId()).thenReturn(2);
when(mPm.getApplicationLabel(any())).thenReturn("");
mRealContext = InstrumentationRegistry.getContext();
@@ -221,6 +236,24 @@ public class StatusBarNotificationTest {
}
@Test
+ @EnableFlags(Flags.FLAG_ENABLE_PER_DISPLAY_PACKAGE_CONTEXT_CACHE_IN_STATUSBAR_NOTIF)
+ public void testGetPackageContext_multipleDisplaysCase() {
+ String pkg = "com.android.systemui";
+ int uid = 1000;
+ Notification notification = getNotificationBuilder(GROUP_ID_1, CHANNEL_ID).build();
+ StatusBarNotification sbn = new StatusBarNotification(
+ pkg, pkg, ID, TAG, uid, uid, notification, UserHandle.ALL, null, UID);
+ Context defaultContext = sbn.getPackageContext(mRealContext);
+ Context secondaryContext = sbn.getPackageContext(mSecondaryDisplayContext);
+ assertNotSame(mRealContext, defaultContext);
+ assertNotSame(defaultContext, secondaryContext);
+
+ // Let's make sure it caches it:
+ assertSame(defaultContext, sbn.getPackageContext(mRealContext));
+ assertSame(secondaryContext, sbn.getPackageContext(mSecondaryDisplayContext));
+ }
+
+ @Test
public void testGetUidFromKey() {
StatusBarNotification sbn = getNotification("pkg", null, "channel");
diff --git a/core/tests/coretests/src/android/text/LayoutTest.java b/core/tests/coretests/src/android/text/LayoutTest.java
index c7d85d4b9b76..9e78af57b470 100644
--- a/core/tests/coretests/src/android/text/LayoutTest.java
+++ b/core/tests/coretests/src/android/text/LayoutTest.java
@@ -1024,6 +1024,55 @@ public class LayoutTest {
expect.that(backgroundCommands.size()).isEqualTo(backgroundRectsDrawn);
}
+ @Test
+ @RequiresFlagsEnabled(FLAG_HIGH_CONTRAST_TEXT_SMALL_TEXT_RECT)
+ public void highContrastTextEnabled_testWhitespaceText_DrawsBackgroundsWithAdjacentLetters() {
+ mTextPaint.setColor(Color.BLACK);
+ SpannableString spannedText = new SpannableString("Test\tTap and Space");
+
+ // Set the entire text to white initially
+ spannedText.setSpan(
+ new ForegroundColorSpan(Color.WHITE),
+ /* start= */ 0,
+ /* end= */ spannedText.length(),
+ Spanned.SPAN_INCLUSIVE_EXCLUSIVE
+ );
+
+ // Find the whitespace character and set its color to black
+ for (int i = 0; i < spannedText.length(); i++) {
+ if (Character.isWhitespace(spannedText.charAt(i))) {
+ spannedText.setSpan(
+ new ForegroundColorSpan(Color.BLACK),
+ i,
+ i + 1,
+ Spanned.SPAN_INCLUSIVE_EXCLUSIVE
+ );
+ }
+ }
+
+ Layout layout = new StaticLayout(spannedText, mTextPaint, mWidth,
+ mAlign, mSpacingMult, mSpacingAdd, /* includePad= */ false);
+
+ MockCanvas c = new MockCanvas(/* width= */ 256, /* height= */ 256);
+ c.setHighContrastTextEnabled(true);
+ layout.draw(
+ c,
+ /* highlightPaths= */ null,
+ /* highlightPaints= */ null,
+ /* selectionPath= */ null,
+ /* selectionPaint= */ null,
+ /* cursorOffsetVertical= */ 0
+ );
+
+ List<MockCanvas.DrawCommand> drawCommands = c.getDrawCommands();
+ for (int i = 0; i < drawCommands.size(); i++) {
+ MockCanvas.DrawCommand drawCommand = drawCommands.get(i);
+ if (drawCommand.rect != null) {
+ expect.that(removeAlpha(drawCommand.paint.getColor())).isEqualTo(Color.BLACK);
+ }
+ }
+ }
+
private int removeAlpha(int color) {
return Color.rgb(
Color.red(color),
diff --git a/core/tests/coretests/src/com/android/internal/accessibility/util/ShortcutUtilsTest.java b/core/tests/coretests/src/com/android/internal/accessibility/util/ShortcutUtilsTest.java
index 1a9af6b55eed..d1555e389c2d 100644
--- a/core/tests/coretests/src/com/android/internal/accessibility/util/ShortcutUtilsTest.java
+++ b/core/tests/coretests/src/com/android/internal/accessibility/util/ShortcutUtilsTest.java
@@ -20,8 +20,8 @@ import static android.provider.Settings.Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATI
import static android.provider.Settings.Secure.ACCESSIBILITY_SHORTCUT_TARGET_MAGNIFICATION_CONTROLLER;
import static com.android.internal.accessibility.common.ShortcutConstants.SERVICES_SEPARATOR;
-import static com.android.internal.accessibility.common.ShortcutConstants.UserShortcutType.GESTURE;
-import static com.android.internal.accessibility.common.ShortcutConstants.UserShortcutType.KEY_GESTURE;
+import static com.android.internal.accessibility.common.ShortcutConstants.USER_SHORTCUT_TYPES;
+import static com.android.internal.accessibility.common.ShortcutConstants.UserShortcutType.DEFAULT;
import static com.android.internal.accessibility.common.ShortcutConstants.UserShortcutType.SOFTWARE;
import static com.google.common.truth.Truth.assertThat;
@@ -42,19 +42,22 @@ import android.testing.TestableContext;
import android.view.accessibility.AccessibilityManager;
import android.view.accessibility.IAccessibilityManager;
-import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.platform.app.InstrumentationRegistry;
import com.android.internal.accessibility.AccessibilityShortcutController;
import com.android.internal.accessibility.TestUtils;
import com.android.internal.accessibility.common.ShortcutConstants;
+import com.google.testing.junit.testparameterinjector.TestParameter;
+import com.google.testing.junit.testparameterinjector.TestParameterInjector;
+
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
+import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import java.util.StringJoiner;
@@ -62,7 +65,7 @@ import java.util.StringJoiner;
/**
* Unit Tests for {@link com.android.internal.accessibility.util.ShortcutUtils}
*/
-@RunWith(AndroidJUnit4.class)
+@RunWith(TestParameterInjector.class)
public class ShortcutUtilsTest {
private static final Set<String> ONE_COMPONENT = Set.of(
new ComponentName("pkg", "serv").flattenToString());
@@ -99,38 +102,19 @@ public class ShortcutUtilsTest {
}
@Test
- public void getShortcutTargets_softwareShortcutNoService_emptyResult() {
- assertThat(
- ShortcutUtils.getShortcutTargetsFromSettings(
- mContext, SOFTWARE, mDefaultUserId)
- ).isEmpty();
- }
-
- @Test
- public void getShortcutTargets_volumeKeyShortcutNoService_emptyResult() {
- assertThat(
- ShortcutUtils.getShortcutTargetsFromSettings(
- mContext, ShortcutConstants.UserShortcutType.HARDWARE,
- mDefaultUserId)
- ).isEmpty();
- }
-
- @Test
- public void getShortcutTargets_gestureShortcutNoService_emptyResult() {
- assertThat(
- ShortcutUtils.getShortcutTargetsFromSettings(
- mContext, GESTURE, mDefaultUserId)
- ).isEmpty();
- }
+ public void getShortcutTargets_noService_emptyResult(
+ @TestParameter(valuesProvider = ShortcutTypeValueProvider.class) int shortcutType) {
+ Settings.Secure.putStringForUser(
+ mContext.getContentResolver(),
+ ShortcutUtils.convertToKey(shortcutType), "", mContext.getUserId());
- @Test
- public void getShortcutTargets_keyGestureShortcutNoService_emptyResult() {
assertThat(
ShortcutUtils.getShortcutTargetsFromSettings(
- mContext, KEY_GESTURE, mDefaultUserId)
+ mContext, shortcutType, mDefaultUserId)
).isEmpty();
}
+ // TODO 385186274: Parameterize this test.
@Test
public void getShortcutTargets_softwareShortcut1Service_return1Service() {
setupShortcutTargets(ONE_COMPONENT, Settings.Secure.ACCESSIBILITY_BUTTON_TARGETS);
@@ -143,6 +127,7 @@ public class ShortcutUtilsTest {
).containsExactlyElementsIn(ONE_COMPONENT);
}
+ // TODO 385186274: Parameterize this test.
@Test
public void getShortcutTargets_volumeShortcut2Service_return2Service() {
setupShortcutTargets(ONE_COMPONENT, Settings.Secure.ACCESSIBILITY_BUTTON_TARGETS);
@@ -155,6 +140,7 @@ public class ShortcutUtilsTest {
).containsExactlyElementsIn(TWO_COMPONENTS);
}
+ // TODO 385186274: Parameterize this test.
@Test
public void getShortcutTargets_tripleTapShortcut_magnificationDisabled_emptyResult() {
enableTripleTapShortcutForMagnification(/* enable= */ false);
@@ -168,6 +154,7 @@ public class ShortcutUtilsTest {
).isEmpty();
}
+ // TODO 385186274: Parameterize this test.
@Test
public void getShortcutTargets_tripleTapShortcut_magnificationEnabled_returnMagnification() {
setupShortcutTargets(ONE_COMPONENT, Settings.Secure.ACCESSIBILITY_BUTTON_TARGETS);
@@ -181,6 +168,7 @@ public class ShortcutUtilsTest {
).containsExactly(ACCESSIBILITY_SHORTCUT_TARGET_MAGNIFICATION_CONTROLLER);
}
+ // TODO 385186274: Parameterize this test.
@Test
public void updateAccessibilityServiceStateIfNeeded_alwaysOnServiceOn_noShortcuts_serviceTurnedOff() {
setupA11yServiceAndShortcutState(
@@ -195,6 +183,7 @@ public class ShortcutUtilsTest {
assertA11yServiceState(ALWAYS_ON_SERVICE_COMPONENT_NAME, /* enabled= */ false);
}
+ // TODO 385186274: Parameterize this test.
@Test
public void updateAccessibilityServiceStateIfNeeded_alwaysOnServiceOnForBothUsers_noShortcutsForGuestUser_serviceTurnedOffForGuestUserOnly() {
// setup arbitrary userId by add 10 to the default user id
@@ -218,6 +207,7 @@ public class ShortcutUtilsTest {
ALWAYS_ON_SERVICE_COMPONENT_NAME, /* enabled= */ true, mDefaultUserId);
}
+ // TODO 385186274: Parameterize this test.
@Test
public void updateAccessibilityServiceStateIfNeeded_alwaysOnServiceOn_hasShortcut_serviceKeepsOn() {
setupA11yServiceAndShortcutState(
@@ -232,6 +222,7 @@ public class ShortcutUtilsTest {
assertA11yServiceState(ALWAYS_ON_SERVICE_COMPONENT_NAME, /* enabled= */ true);
}
+ // TODO 385186274: Parameterize this test.
@Test
public void updateAccessibilityServiceStateIfNeeded_alwaysOnServiceOff_noShortcuts_serviceKeepsOff() {
setupA11yServiceAndShortcutState(
@@ -246,6 +237,7 @@ public class ShortcutUtilsTest {
assertA11yServiceState(ALWAYS_ON_SERVICE_COMPONENT_NAME, /* enabled= */ false);
}
+ // TODO 385186274: Parameterize this test.
@Test
public void updateAccessibilityServiceStateIfNeeded_alwaysOnServiceOff_hasShortcuts_serviceTurnsOn() {
setupA11yServiceAndShortcutState(
@@ -260,6 +252,7 @@ public class ShortcutUtilsTest {
assertA11yServiceState(ALWAYS_ON_SERVICE_COMPONENT_NAME, /* enabled= */ true);
}
+ // TODO 385186274: Parameterize this test.
@Test
public void updateAccessibilityServiceStateIfNeeded_standardA11yServiceOn_noShortcuts_serviceKeepsOn() {
setupA11yServiceAndShortcutState(
@@ -274,6 +267,7 @@ public class ShortcutUtilsTest {
assertA11yServiceState(STANDARD_SERVICE_COMPONENT_NAME, /* enabled= */ true);
}
+ // TODO 385186274: Parameterize this test.
@Test
public void updateAccessibilityServiceStateIfNeeded_standardA11yServiceOn_hasShortcuts_serviceKeepsOn() {
setupA11yServiceAndShortcutState(
@@ -288,6 +282,7 @@ public class ShortcutUtilsTest {
assertA11yServiceState(STANDARD_SERVICE_COMPONENT_NAME, /* enabled= */ true);
}
+ // TODO 385186274: Parameterize this test.
@Test
public void updateAccessibilityServiceStateIfNeeded_standardA11yServiceOff_noShortcuts_serviceKeepsOff() {
setupA11yServiceAndShortcutState(
@@ -302,6 +297,7 @@ public class ShortcutUtilsTest {
assertA11yServiceState(STANDARD_SERVICE_COMPONENT_NAME, /* enabled= */ false);
}
+ // TODO 385186274: Parameterize this test.
@Test
public void updateAccessibilityServiceStateIfNeeded_standardA11yServiceOff_hasShortcuts_serviceKeepsOff() {
setupA11yServiceAndShortcutState(
@@ -316,6 +312,37 @@ public class ShortcutUtilsTest {
assertA11yServiceState(STANDARD_SERVICE_COMPONENT_NAME, /* enabled= */ false);
}
+ @Test
+ public void getEnabledShortcutTypes_oneShortcut_returnsExpectedType(
+ @TestParameter(valuesProvider = ShortcutTypeValueProvider.class) int shortcutType)
+ throws RemoteException {
+ clearMockShortcutTypes();
+ assertThat(ShortcutUtils.getEnabledShortcutTypes(
+ mContext, STANDARD_SERVICE_COMPONENT_NAME)).isEqualTo(DEFAULT);
+ mockShortcutType(shortcutType, STANDARD_SERVICE_COMPONENT_NAME);
+ assertThat(ShortcutUtils.getEnabledShortcutTypes(
+ mContext, STANDARD_SERVICE_COMPONENT_NAME)).isEqualTo(shortcutType);
+
+ }
+
+ @Test
+ public void getEnabledShortcutTypes_twoShortcuts_returnsExpectedTypes(
+ @TestParameter(valuesProvider = ShortcutTypeValueProvider.class) int shortcutType1,
+ @TestParameter(valuesProvider = ShortcutTypeValueProvider.class) int shortcutType2
+ ) throws RemoteException {
+ if (shortcutType1 == shortcutType2) {
+ return;
+ }
+ clearMockShortcutTypes();
+ assertThat(ShortcutUtils.getEnabledShortcutTypes(
+ mContext, STANDARD_SERVICE_COMPONENT_NAME)).isEqualTo(DEFAULT);
+ mockShortcutType(shortcutType1, STANDARD_SERVICE_COMPONENT_NAME);
+ mockShortcutType(shortcutType2, STANDARD_SERVICE_COMPONENT_NAME);
+ assertThat(ShortcutUtils.getEnabledShortcutTypes(
+ mContext, STANDARD_SERVICE_COMPONENT_NAME)).isEqualTo(
+ shortcutType1 | shortcutType2);
+ }
+
private void setupShortcutTargets(Set<String> components, String shortcutSettingsKey) {
final StringJoiner stringJoiner = new StringJoiner(String.valueOf(SERVICES_SEPARATOR));
for (String target : components) {
@@ -403,4 +430,29 @@ public class ShortcutUtilsTest {
add ? a11yServiceComponentName : "",
userId);
}
+
+ private void clearMockShortcutTypes() throws RemoteException {
+ for (int shortcutType : ShortcutConstants.USER_SHORTCUT_TYPES) {
+ when(mAccessibilityManagerService
+ .getAccessibilityShortcutTargets(shortcutType)).thenReturn(List.of());
+ }
+ }
+
+ private void mockShortcutType(int shortcutType, String componentName)
+ throws RemoteException {
+ when(mAccessibilityManagerService.getAccessibilityShortcutTargets(shortcutType))
+ .thenReturn(List.of(componentName));
+ }
+
+ static final class ShortcutTypeValueProvider implements
+ TestParameter.TestParameterValuesProvider {
+ @Override
+ public List<Integer> provideValues() {
+ List<Integer> values = new ArrayList<>();
+ for (int shortcutType: USER_SHORTCUT_TYPES) {
+ values.add(shortcutType);
+ }
+ return values;
+ }
+ }
}
diff --git a/core/tests/coretests/src/com/android/internal/widget/NotificationProgressBarTest.java b/core/tests/coretests/src/com/android/internal/widget/NotificationProgressBarTest.java
index 5df2c1279eb8..9818e19cea02 100644
--- a/core/tests/coretests/src/com/android/internal/widget/NotificationProgressBarTest.java
+++ b/core/tests/coretests/src/com/android/internal/widget/NotificationProgressBarTest.java
@@ -336,10 +336,14 @@ public class NotificationProgressBarTest {
progress, progressMax);
List<Part> expectedParts = new ArrayList<>(
- List.of(new Segment(0.15f, Color.BLUE), new Point(Color.RED),
- new Segment(0.10f, Color.BLUE), new Point(Color.BLUE),
- new Segment(0.35f, Color.BLUE), new Point(Color.BLUE),
- new Segment(0.15f, Color.BLUE), new Point(Color.YELLOW),
+ List.of(new Segment(0.15f, Color.BLUE),
+ new Point(Color.RED),
+ new Segment(0.10f, Color.BLUE),
+ new Point(Color.BLUE),
+ new Segment(0.35f, Color.BLUE),
+ new Point(Color.BLUE),
+ new Segment(0.15f, Color.BLUE),
+ new Point(Color.YELLOW),
new Segment(0.25f, Color.BLUE)));
assertThat(parts).isEqualTo(expectedParts);
@@ -408,11 +412,16 @@ public class NotificationProgressBarTest {
progress, progressMax);
List<Part> expectedParts = new ArrayList<>(
- List.of(new Segment(0.15f, Color.RED), new Point(Color.RED),
- new Segment(0.10f, Color.RED), new Point(Color.BLUE),
- new Segment(0.25f, Color.RED), new Segment(0.10f, Color.GREEN),
- new Point(Color.BLUE), new Segment(0.15f, Color.GREEN),
- new Point(Color.YELLOW), new Segment(0.25f, Color.GREEN)));
+ List.of(new Segment(0.15f, Color.RED),
+ new Point(Color.RED),
+ new Segment(0.10f, Color.RED),
+ new Point(Color.BLUE),
+ new Segment(0.25f, Color.RED),
+ new Segment(0.10f, Color.GREEN),
+ new Point(Color.BLUE),
+ new Segment(0.15f, Color.GREEN),
+ new Point(Color.YELLOW),
+ new Segment(0.25f, Color.GREEN)));
assertThat(parts).isEqualTo(expectedParts);
@@ -464,6 +473,158 @@ public class NotificationProgressBarTest {
}
@Test
+ public void processAndConvertToParts_multipleSegmentsWithPointsAtStartAndEnd() {
+ List<ProgressStyle.Segment> segments = new ArrayList<>();
+ segments.add(new ProgressStyle.Segment(50).setColor(Color.RED));
+ segments.add(new ProgressStyle.Segment(50).setColor(Color.GREEN));
+ List<ProgressStyle.Point> points = new ArrayList<>();
+ points.add(new ProgressStyle.Point(0).setColor(Color.RED));
+ points.add(new ProgressStyle.Point(25).setColor(Color.BLUE));
+ points.add(new ProgressStyle.Point(60).setColor(Color.BLUE));
+ points.add(new ProgressStyle.Point(100).setColor(Color.YELLOW));
+ int progress = 60;
+ int progressMax = 100;
+
+ List<Part> parts = NotificationProgressBar.processAndConvertToViewParts(segments, points,
+ progress, progressMax);
+
+ List<Part> expectedParts = new ArrayList<>(
+ List.of(new Point(Color.RED),
+ new Segment(0.25f, Color.RED),
+ new Point(Color.BLUE),
+ new Segment(0.25f, Color.RED),
+ new Segment(0.10f, Color.GREEN),
+ new Point(Color.BLUE),
+ new Segment(0.4f, Color.GREEN),
+ new Point(Color.YELLOW)));
+
+ assertThat(parts).isEqualTo(expectedParts);
+
+ float drawableWidth = 300;
+ float segSegGap = 4;
+ float segPointGap = 4;
+ float pointRadius = 6;
+ boolean hasTrackerIcon = true;
+ List<DrawablePart> drawableParts = NotificationProgressBar.processAndConvertToDrawableParts(
+ parts, drawableWidth, segSegGap, segPointGap, pointRadius, hasTrackerIcon);
+
+ List<DrawablePart> expectedDrawableParts = new ArrayList<>(
+ List.of(new DrawablePoint(0, 12, Color.RED),
+ new DrawableSegment(16, 65, Color.RED),
+ new DrawablePoint(69, 81, Color.BLUE),
+ new DrawableSegment(85, 146, Color.RED),
+ new DrawableSegment(150, 170, Color.GREEN),
+ new DrawablePoint(174, 186, Color.BLUE),
+ new DrawableSegment(190, 284, Color.GREEN),
+ new DrawablePoint(288, 300, Color.YELLOW)));
+
+ assertThat(drawableParts).isEqualTo(expectedDrawableParts);
+
+ float segmentMinWidth = 16;
+ boolean isStyledByProgress = true;
+
+ Pair<List<DrawablePart>, Float> p = NotificationProgressBar.maybeStretchAndRescaleSegments(
+ parts, drawableParts, segmentMinWidth, pointRadius, (float) progress / progressMax,
+ 300, isStyledByProgress, hasTrackerIcon ? 0 : segSegGap);
+
+ // Colors with 40% opacity
+ int fadedGreen = 0x6600FF00;
+ int fadedYellow = 0x66FFFF00;
+ expectedDrawableParts = new ArrayList<>(
+ List.of(new DrawablePoint(0, 12, Color.RED),
+ new DrawableSegment(16, 65, Color.RED),
+ new DrawablePoint(69, 81, Color.BLUE),
+ new DrawableSegment(85, 146, Color.RED),
+ new DrawableSegment(150, 170, Color.GREEN),
+ new DrawablePoint(174, 186, Color.BLUE),
+ new DrawableSegment(190, 284, fadedGreen, true),
+ new DrawablePoint(288, 300, fadedYellow)));
+
+ assertThat(p.second).isEqualTo(180);
+ assertThat(p.first).isEqualTo(expectedDrawableParts);
+ }
+
+ // The points are so close to start/end that they would go out of bounds without the minimum
+ // segment width requirement.
+ @Test
+ public void processAndConvertToParts_multipleSegmentsWithPointsNearStartAndEnd() {
+ List<ProgressStyle.Segment> segments = new ArrayList<>();
+ segments.add(new ProgressStyle.Segment(50).setColor(Color.RED));
+ segments.add(new ProgressStyle.Segment(50).setColor(Color.GREEN));
+ List<ProgressStyle.Point> points = new ArrayList<>();
+ points.add(new ProgressStyle.Point(1).setColor(Color.RED));
+ points.add(new ProgressStyle.Point(25).setColor(Color.BLUE));
+ points.add(new ProgressStyle.Point(60).setColor(Color.BLUE));
+ points.add(new ProgressStyle.Point(99).setColor(Color.YELLOW));
+ int progress = 60;
+ int progressMax = 100;
+
+ List<Part> parts = NotificationProgressBar.processAndConvertToViewParts(segments, points,
+ progress, progressMax);
+
+ List<Part> expectedParts = new ArrayList<>(
+ List.of(new Segment(0.01f, Color.RED),
+ new Point(Color.RED),
+ new Segment(0.24f, Color.RED),
+ new Point(Color.BLUE),
+ new Segment(0.25f, Color.RED),
+ new Segment(0.10f, Color.GREEN),
+ new Point(Color.BLUE),
+ new Segment(0.39f, Color.GREEN),
+ new Point(Color.YELLOW),
+ new Segment(0.01f, Color.GREEN)));
+
+ assertThat(parts).isEqualTo(expectedParts);
+
+ float drawableWidth = 300;
+ float segSegGap = 4;
+ float segPointGap = 4;
+ float pointRadius = 6;
+ boolean hasTrackerIcon = true;
+ List<DrawablePart> drawableParts = NotificationProgressBar.processAndConvertToDrawableParts(
+ parts, drawableWidth, segSegGap, segPointGap, pointRadius, hasTrackerIcon);
+
+ List<DrawablePart> expectedDrawableParts = new ArrayList<>(
+ List.of(new DrawableSegment(0, -7, Color.RED),
+ new DrawablePoint(-3, 9, Color.RED),
+ new DrawableSegment(13, 65, Color.RED),
+ new DrawablePoint(69, 81, Color.BLUE),
+ new DrawableSegment(85, 146, Color.RED),
+ new DrawableSegment(150, 170, Color.GREEN),
+ new DrawablePoint(174, 186, Color.BLUE),
+ new DrawableSegment(190, 287, Color.GREEN),
+ new DrawablePoint(291, 303, Color.YELLOW),
+ new DrawableSegment(307, 300, Color.GREEN)));
+
+ assertThat(drawableParts).isEqualTo(expectedDrawableParts);
+
+ float segmentMinWidth = 16;
+ boolean isStyledByProgress = true;
+
+ Pair<List<DrawablePart>, Float> p = NotificationProgressBar.maybeStretchAndRescaleSegments(
+ parts, drawableParts, segmentMinWidth, pointRadius, (float) progress / progressMax,
+ 300, isStyledByProgress, hasTrackerIcon ? 0 : segSegGap);
+
+ // Colors with 40% opacity
+ int fadedGreen = 0x6600FF00;
+ int fadedYellow = 0x66FFFF00;
+ expectedDrawableParts = new ArrayList<>(
+ List.of(new DrawableSegment(0, 16, Color.RED),
+ new DrawablePoint(20, 32, Color.RED),
+ new DrawableSegment(36, 78.02409F, Color.RED),
+ new DrawablePoint(82.02409F, 94.02409F, Color.BLUE),
+ new DrawableSegment(98.02409F, 146.55421F, Color.RED),
+ new DrawableSegment(150.55421F, 169.44579F, Color.GREEN),
+ new DrawablePoint(173.44579F, 185.44579F, Color.BLUE),
+ new DrawableSegment(189.44579F, 264, fadedGreen, true),
+ new DrawablePoint(268, 280, fadedYellow),
+ new DrawableSegment(284, 300, fadedGreen, true)));
+
+ assertThat(p.second).isEqualTo(179.44579F);
+ assertThat(p.first).isEqualTo(expectedDrawableParts);
+ }
+
+ @Test
public void processAndConvertToParts_multipleSegmentsWithPoints_notStyledByProgress() {
List<ProgressStyle.Segment> segments = new ArrayList<>();
segments.add(new ProgressStyle.Segment(50).setColor(Color.RED));
diff --git a/data/etc/preinstalled-packages-platform.xml b/data/etc/preinstalled-packages-platform.xml
index 782327713fdc..3b4014867ef7 100644
--- a/data/etc/preinstalled-packages-platform.xml
+++ b/data/etc/preinstalled-packages-platform.xml
@@ -102,7 +102,7 @@ Changes to the whitelist during system updates can result in installing addition
to pre-existing users, but cannot uninstall pre-existing system packages from pre-existing users.
-->
<config>
- <!-- Bluetooth (com.android.btservices apex) - visible on the sharesheet -->
+ <!-- Bluetooth (com.android.bt apex) - visible on the sharesheet -->
<install-in-user-type package="com.android.bluetooth">
<install-in user-type="SYSTEM" />
<install-in user-type="FULL" />
@@ -134,4 +134,9 @@ to pre-existing users, but cannot uninstall pre-existing system packages from pr
<install-in-user-type package="com.android.avatarpicker">
<install-in user-type="FULL" />
</install-in-user-type>
+
+ <!-- Users Widget (Users widget)-->
+ <install-in-user-type package="com.android.multiuser">
+ <install-in user-type="FULL" />
+ </install-in-user-type>
</config>
diff --git a/libs/WindowManager/Shell/Android.bp b/libs/WindowManager/Shell/Android.bp
index 957d1b835ec2..bcb6c4f555f7 100644
--- a/libs/WindowManager/Shell/Android.bp
+++ b/libs/WindowManager/Shell/Android.bp
@@ -170,9 +170,9 @@ android_library {
"res",
],
static_libs: [
+ "//frameworks/base/packages/SystemUI/aconfig:com_android_systemui_flags_lib",
"//frameworks/libs/systemui:com_android_systemui_shared_flags_lib",
"//frameworks/libs/systemui:iconloader_base",
- "//packages/apps/Car/SystemUI/aconfig:com_android_systemui_car_flags_lib",
"PlatformAnimationLib",
"WindowManager-Shell-lite-proto",
"WindowManager-Shell-proto",
diff --git a/libs/WindowManager/Shell/AndroidManifest.xml b/libs/WindowManager/Shell/AndroidManifest.xml
index 4f1cd9780f8b..9c15319585b7 100644
--- a/libs/WindowManager/Shell/AndroidManifest.xml
+++ b/libs/WindowManager/Shell/AndroidManifest.xml
@@ -32,7 +32,7 @@
<activity
android:name=".desktopmode.DesktopWallpaperActivity"
android:excludeFromRecents="true"
- android:launchMode="singleInstance"
+ android:launchMode="singleInstancePerTask"
android:showForAllUsers="true"
android:theme="@style/DesktopWallpaperTheme" />
diff --git a/libs/WindowManager/Shell/aconfig/Android.bp b/libs/WindowManager/Shell/aconfig/Android.bp
index 7f8f57b172ff..f8da7fa86cff 100644
--- a/libs/WindowManager/Shell/aconfig/Android.bp
+++ b/libs/WindowManager/Shell/aconfig/Android.bp
@@ -4,6 +4,7 @@ aconfig_declarations {
container: "system",
srcs: [
"multitasking.aconfig",
+ "automotive.aconfig",
],
}
diff --git a/libs/WindowManager/Shell/aconfig/automotive.aconfig b/libs/WindowManager/Shell/aconfig/automotive.aconfig
new file mode 100644
index 000000000000..2f25aa460ec1
--- /dev/null
+++ b/libs/WindowManager/Shell/aconfig/automotive.aconfig
@@ -0,0 +1,11 @@
+# proto-file: build/make/tools/aconfig/aconfig_protos/protos/aconfig.proto
+
+package: "com.android.wm.shell"
+container: "system"
+
+flag {
+ name: "enable_auto_task_stack_controller"
+ namespace: "multitasking"
+ description: "Enables auto task stack controller to manage task stacks on automotive"
+ bug: "384082238"
+}
diff --git a/libs/WindowManager/Shell/aconfig/multitasking.aconfig b/libs/WindowManager/Shell/aconfig/multitasking.aconfig
index 688bf83224aa..73e7223a3aea 100644
--- a/libs/WindowManager/Shell/aconfig/multitasking.aconfig
+++ b/libs/WindowManager/Shell/aconfig/multitasking.aconfig
@@ -104,6 +104,13 @@ flag {
}
flag {
+ name: "enable_create_any_bubble"
+ namespace: "multitasking"
+ description: "Enable UI affordances to create bubbles via launcher app icons"
+ bug: "385220199"
+}
+
+flag {
name: "only_reuse_bubbled_task_when_launched_from_bubble"
namespace: "multitasking"
description: "Allow reusing bubbled tasks for new activities only when launching from bubbles"
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/appzoomout/AppZoomOutController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/appzoomout/AppZoomOutController.java
index 8cd7b0f48003..10023c9dba40 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/appzoomout/AppZoomOutController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/appzoomout/AppZoomOutController.java
@@ -16,7 +16,9 @@
package com.android.wm.shell.appzoomout;
+import static android.app.WindowConfiguration.ROTATION_UNDEFINED;
import static android.view.Display.DEFAULT_DISPLAY;
+import static com.android.systemui.Flags.spatialModelAppPushback;
import android.app.ActivityManager;
import android.app.WindowConfiguration;
@@ -92,7 +94,9 @@ public class AppZoomOutController implements RemoteCallable<AppZoomOutController
mDisplayAreaOrganizer = displayAreaOrganizer;
mMainExecutor = mainExecutor;
- shellInit.addInitCallback(this::onInit, this);
+ if (spatialModelAppPushback()) {
+ shellInit.addInitCallback(this::onInit, this);
+ }
}
private void onInit() {
@@ -100,6 +104,7 @@ public class AppZoomOutController implements RemoteCallable<AppZoomOutController
mDisplayController.addDisplayWindowListener(mDisplaysChangedListener);
mDisplayController.addDisplayChangingController(this);
+ updateDisplayLayout(mContext.getDisplayId());
mDisplayAreaOrganizer.registerOrganizer();
}
@@ -135,7 +140,9 @@ public class AppZoomOutController implements RemoteCallable<AppZoomOutController
public void onDisplayChange(int displayId, int fromRotation, int toRotation,
@Nullable DisplayAreaInfo newDisplayAreaInfo, WindowContainerTransaction wct) {
// TODO: verify if there is synchronization issues.
- mDisplayAreaOrganizer.onRotateDisplay(mContext, toRotation);
+ if (toRotation != ROTATION_UNDEFINED) {
+ mDisplayAreaOrganizer.onRotateDisplay(mContext, toRotation);
+ }
}
@Override
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/automotive/AutoTaskStackControllerImpl.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/automotive/AutoTaskStackControllerImpl.kt
index f8f284238a98..8171312762ef 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/automotive/AutoTaskStackControllerImpl.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/automotive/AutoTaskStackControllerImpl.kt
@@ -33,7 +33,7 @@ import android.view.WindowManager.TRANSIT_CHANGE
import android.window.TransitionInfo
import android.window.TransitionRequestInfo
import android.window.WindowContainerTransaction
-import com.android.systemui.car.Flags.autoTaskStackWindowing
+import com.android.wm.shell.Flags.enableAutoTaskStackController
import com.android.wm.shell.RootTaskDisplayAreaOrganizer
import com.android.wm.shell.ShellTaskOrganizer
import com.android.wm.shell.common.ShellExecutor
@@ -66,7 +66,7 @@ class AutoTaskStackControllerImpl @Inject constructor(
private val defaultRootTaskPerDisplay = mutableMapOf<Int, Int>()
init {
- if (!autoTaskStackWindowing()) {
+ if (!enableAutoTaskStackController()) {
throw IllegalStateException("Failed to initialize" +
"AutoTaskStackController as the auto_task_stack_windowing TS flag is disabled.")
} else {
@@ -220,7 +220,7 @@ class AutoTaskStackControllerImpl @Inject constructor(
displayId: Int,
listener: RootTaskStackListener
) {
- if (!autoTaskStackWindowing()) {
+ if (!enableAutoTaskStackController()) {
Slog.e(
TAG, "Failed to create root task stack as the " +
"auto_task_stack_windowing TS flag is disabled."
@@ -236,7 +236,7 @@ class AutoTaskStackControllerImpl @Inject constructor(
}
override fun setDefaultRootTaskStackOnDisplay(displayId: Int, rootTaskStackId: Int?) {
- if (!autoTaskStackWindowing()) {
+ if (!enableAutoTaskStackController()) {
Slog.e(
TAG, "Failed to set default root task stack as the " +
"auto_task_stack_windowing TS flag is disabled."
@@ -280,7 +280,7 @@ class AutoTaskStackControllerImpl @Inject constructor(
}
override fun startTransition(transaction: AutoTaskStackTransaction): IBinder? {
- if (!autoTaskStackWindowing()) {
+ if (!enableAutoTaskStackController()) {
Slog.e(
TAG, "Failed to start transaction as the " +
"auto_task_stack_windowing TS flag is disabled."
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/ComponentUtils.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/common/ComponentUtils.kt
new file mode 100644
index 000000000000..67592e60e954
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/ComponentUtils.kt
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.wm.shell.common
+
+import android.app.PendingIntent
+import android.content.ComponentName
+import android.content.Intent
+import com.android.wm.shell.ShellTaskOrganizer
+
+/** Utils to obtain [ComponentName]s. */
+object ComponentUtils {
+ /** Retrieves the package name from an [Intent]. */
+ @JvmStatic
+ fun getPackageName(intent: Intent?): String? = intent?.component?.packageName
+
+ /** Retrieves the package name from a [PendingIntent]. */
+ @JvmStatic
+ fun getPackageName(pendingIntent: PendingIntent?): String? =
+ getPackageName(pendingIntent?.intent)
+
+ /** Retrieves the package name from a [taskId]. */
+ @JvmStatic
+ fun getPackageName(taskId: Int, taskOrganizer: ShellTaskOrganizer): String? {
+ val taskInfo = taskOrganizer.getRunningTaskInfo(taskId)
+ return getPackageName(taskInfo?.baseIntent)
+ }
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayImeController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayImeController.java
index eb1e72790a25..c9890a5b4963 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayImeController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayImeController.java
@@ -447,8 +447,10 @@ public class DisplayImeController implements DisplayController.OnDisplaysChanged
}
}
- private int imeTop(float surfaceOffset) {
- return mImeFrame.top + (int) surfaceOffset;
+ private int imeTop(float surfaceOffset, float surfacePositionY) {
+ // surfaceOffset is already offset by the surface's top inset, so we need to subtract
+ // the top inset so that the return value is in screen coordinates.
+ return mImeFrame.top + (int) (surfaceOffset - surfacePositionY);
}
private boolean calcIsFloating(InsetsSource imeSource) {
@@ -581,7 +583,7 @@ public class DisplayImeController implements DisplayController.OnDisplaysChanged
final float alpha = (mAnimateAlpha || isFloating)
? (value - hiddenY) / (shownY - hiddenY) : 1f;
t.setAlpha(animatingLeash, alpha);
- dispatchPositionChanged(mDisplayId, imeTop(value), t);
+ dispatchPositionChanged(mDisplayId, imeTop(value, defaultY), t);
t.apply();
mTransactionPool.release(t);
});
@@ -600,11 +602,12 @@ public class DisplayImeController implements DisplayController.OnDisplaysChanged
t.setPosition(animatingLeash, x, value);
if (DEBUG) {
Slog.d(TAG, "onAnimationStart d:" + mDisplayId + " top:"
- + imeTop(hiddenY) + "->" + imeTop(shownY)
+ + imeTop(hiddenY, defaultY) + "->" + imeTop(shownY, defaultY)
+ " showing:" + (mAnimationDirection == DIRECTION_SHOW));
}
- int flags = dispatchStartPositioning(mDisplayId, imeTop(hiddenY),
- imeTop(shownY), mAnimationDirection == DIRECTION_SHOW, isFloating, t);
+ int flags = dispatchStartPositioning(mDisplayId, imeTop(hiddenY, defaultY),
+ imeTop(shownY, defaultY), mAnimationDirection == DIRECTION_SHOW,
+ isFloating, t);
mAnimateAlpha = (flags & ImePositionProcessor.IME_ANIMATION_NO_ALPHA) == 0;
final float alpha = (mAnimateAlpha || isFloating)
? (value - hiddenY) / (shownY - hiddenY)
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitScreenUtils.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitScreenUtils.java
index 9113c0a53178..83e5e31bd125 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitScreenUtils.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitScreenUtils.java
@@ -27,14 +27,10 @@ import static com.android.wm.shell.shared.split.SplitScreenConstants.SPLIT_POSIT
import static com.android.wm.shell.shared.split.SplitScreenConstants.SPLIT_POSITION_UNDEFINED;
import android.app.ActivityManager;
-import android.app.PendingIntent;
-import android.content.Intent;
import android.content.res.Configuration;
import android.content.res.Resources;
import android.graphics.Rect;
-import androidx.annotation.Nullable;
-
import com.android.internal.util.ArrayUtils;
import com.android.wm.shell.Flags;
import com.android.wm.shell.ShellTaskOrganizer;
@@ -65,31 +61,6 @@ public class SplitScreenUtils {
&& ArrayUtils.contains(CONTROLLED_WINDOWING_MODES, taskInfo.getWindowingMode());
}
- /** Retrieve package name from an intent */
- @Nullable
- public static String getPackageName(Intent intent) {
- if (intent == null || intent.getComponent() == null) {
- return null;
- }
- return intent.getComponent().getPackageName();
- }
-
- /** Retrieve package name from a PendingIntent */
- @Nullable
- public static String getPackageName(PendingIntent pendingIntent) {
- if (pendingIntent == null || pendingIntent.getIntent() == null) {
- return null;
- }
- return getPackageName(pendingIntent.getIntent());
- }
-
- /** Retrieve package name from a taskId */
- @Nullable
- public static String getPackageName(int taskId, ShellTaskOrganizer taskOrganizer) {
- final ActivityManager.RunningTaskInfo taskInfo = taskOrganizer.getRunningTaskInfo(taskId);
- return taskInfo != null ? getPackageName(taskInfo.baseIntent) : null;
- }
-
/** Retrieve user id from a taskId */
public static int getUserId(int taskId, ShellTaskOrganizer taskOrganizer) {
final ActivityManager.RunningTaskInfo taskInfo = taskOrganizer.getRunningTaskInfo(taskId);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java
index e8add56619c4..ac510f89b905 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java
@@ -946,8 +946,7 @@ public abstract class WMShellModule {
FocusTransitionObserver focusTransitionObserver,
DesktopModeEventLogger desktopModeEventLogger,
DesktopModeUiEventLogger desktopModeUiEventLogger,
- WindowDecorTaskResourceLoader taskResourceLoader,
- RecentsTransitionHandler recentsTransitionHandler
+ WindowDecorTaskResourceLoader taskResourceLoader
) {
if (!DesktopModeStatus.canEnterDesktopModeOrShowAppHandle(context)) {
return Optional.empty();
@@ -963,7 +962,7 @@ public abstract class WMShellModule {
desktopTasksLimiter, appHandleEducationController, appToWebEducationController,
windowDecorCaptionHandleRepository, activityOrientationChangeHandler,
focusTransitionObserver, desktopModeEventLogger, desktopModeUiEventLogger,
- taskResourceLoader, recentsTransitionHandler));
+ taskResourceLoader));
}
@WMSingleton
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt
index 73d15270c811..f38957e48dbf 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
@@ -552,7 +552,11 @@ class DesktopTasksController(
)
val wct = WindowContainerTransaction()
exitSplitIfApplicable(wct, taskInfo)
- moveHomeTask(wct, toTop = true)
+ if (Flags.enablePerDisplayDesktopWallpaperActivity()) {
+ moveHomeTask(wct, toTop = true, taskInfo.displayId)
+ } else {
+ moveHomeTask(wct, toTop = true)
+ }
val taskIdToMinimize =
bringDesktopAppsToFrontBeforeShowingNewTask(taskInfo.displayId, wct, taskInfo.taskId)
addMoveToDesktopChanges(wct, taskInfo)
@@ -1309,11 +1313,15 @@ class DesktopTasksController(
): Int? {
logV("bringDesktopAppsToFront, newTaskId=%d", newTaskIdInFront)
// Move home to front, ensures that we go back home when all desktop windows are closed
- moveHomeTask(wct, toTop = true)
+ if (Flags.enablePerDisplayDesktopWallpaperActivity()) {
+ moveHomeTask(wct, toTop = true, displayId)
+ } else {
+ moveHomeTask(wct, toTop = true)
+ }
// Currently, we only handle the desktop on the default display really.
if (
- (displayId == DEFAULT_DISPLAY || Flags.enableBugFixesForSecondaryDisplay()) &&
+ (displayId == DEFAULT_DISPLAY || Flags.enablePerDisplayDesktopWallpaperActivity()) &&
ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY.isTrue()
) {
// Add translucent wallpaper activity to show the wallpaper underneath
@@ -1359,9 +1367,13 @@ class DesktopTasksController(
return taskIdToMinimize
}
- private fun moveHomeTask(wct: WindowContainerTransaction, toTop: Boolean) {
+ private fun moveHomeTask(
+ wct: WindowContainerTransaction,
+ toTop: Boolean,
+ displayId: Int = DEFAULT_DISPLAY,
+ ) {
shellTaskOrganizer
- .getRunningTasks(context.displayId)
+ .getRunningTasks(displayId)
.firstOrNull { task -> task.activityType == ACTIVITY_TYPE_HOME }
?.let { homeTask -> wct.reorder(homeTask.getToken(), /* onTop= */ toTop) }
}
@@ -1370,12 +1382,19 @@ class DesktopTasksController(
logV("addWallpaperActivity")
if (Flags.enableDesktopWallpaperActivityForSystemUser()) {
val intent = Intent(context, DesktopWallpaperActivity::class.java)
+ if (
+ desktopWallpaperActivityTokenProvider.getToken(displayId) == null &&
+ Flags.enablePerDisplayDesktopWallpaperActivity()
+ ) {
+ intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
+ intent.addFlags(Intent.FLAG_ACTIVITY_MULTIPLE_TASK)
+ }
val options =
ActivityOptions.makeBasic().apply {
launchWindowingMode = WINDOWING_MODE_FULLSCREEN
pendingIntentBackgroundActivityStartMode =
ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_ALLOW_ALWAYS
- if (Flags.enableBugFixesForSecondaryDisplay()) {
+ if (Flags.enablePerDisplayDesktopWallpaperActivity()) {
launchDisplayId = displayId
}
}
@@ -1391,13 +1410,20 @@ class DesktopTasksController(
val userHandle = UserHandle.of(userId)
val userContext = context.createContextAsUser(userHandle, /* flags= */ 0)
val intent = Intent(userContext, DesktopWallpaperActivity::class.java)
+ if (
+ desktopWallpaperActivityTokenProvider.getToken(displayId) == null &&
+ Flags.enablePerDisplayDesktopWallpaperActivity()
+ ) {
+ intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
+ intent.addFlags(Intent.FLAG_ACTIVITY_MULTIPLE_TASK)
+ }
intent.putExtra(Intent.EXTRA_USER_HANDLE, userId)
val options =
ActivityOptions.makeBasic().apply {
launchWindowingMode = WINDOWING_MODE_FULLSCREEN
pendingIntentBackgroundActivityStartMode =
ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_ALLOW_ALWAYS
- if (Flags.enableBugFixesForSecondaryDisplay()) {
+ if (Flags.enablePerDisplayDesktopWallpaperActivity()) {
launchDisplayId = displayId
}
}
@@ -1414,8 +1440,8 @@ class DesktopTasksController(
}
}
- private fun removeWallpaperActivity(wct: WindowContainerTransaction) {
- desktopWallpaperActivityTokenProvider.getToken()?.let { token ->
+ private fun removeWallpaperActivity(wct: WindowContainerTransaction, displayId: Int) {
+ desktopWallpaperActivityTokenProvider.getToken(displayId)?.let { token ->
logV("removeWallpaperActivity")
if (Flags.enableDesktopWallpaperActivityForSystemUser()) {
wct.reorder(token, /* onTop= */ false)
@@ -1440,9 +1466,6 @@ class DesktopTasksController(
if (!taskRepository.isOnlyVisibleNonClosingTask(taskId, displayId)) {
return
}
- if (displayId != DEFAULT_DISPLAY) {
- return
- }
} else if (
Flags.enableDesktopWindowingPip() &&
taskRepository.isMinimizedPipPresentInDisplay(displayId) &&
@@ -1457,7 +1480,7 @@ class DesktopTasksController(
desktopModeEnterExitTransitionListener?.onExitDesktopModeTransitionStarted(
FULLSCREEN_ANIMATION_DURATION
)
- removeWallpaperActivity(wct)
+ removeWallpaperActivity(wct, displayId)
}
fun releaseVisualIndicator() {
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 b9a65fee7d4d..14c8429766cc 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
@@ -60,7 +60,9 @@ class DesktopTasksTransitionObserver(
shellInit: ShellInit,
) : Transitions.TransitionObserver {
- private var transitionToCloseWallpaper: IBinder? = null
+ data class CloseWallpaperTransition(val transition: IBinder, val displayId: Int)
+
+ private var transitionToCloseWallpaper: CloseWallpaperTransition? = null
/* Pending PiP transition and its associated display id and task id. */
private var pendingPipTransitionAndPipTask: Triple<IBinder, Int, Int>? = null
private var currentProfileId: Int
@@ -248,9 +250,10 @@ class DesktopTasksTransitionObserver(
desktopRepository.getVisibleTaskCount(taskInfo.displayId) == 0 &&
change.mode == TRANSIT_CLOSE &&
taskInfo.windowingMode == WINDOWING_MODE_FREEFORM &&
- desktopWallpaperActivityTokenProvider.getToken() != null
+ desktopWallpaperActivityTokenProvider.getToken(taskInfo.displayId) != null
) {
- transitionToCloseWallpaper = transition
+ transitionToCloseWallpaper =
+ CloseWallpaperTransition(transition, taskInfo.displayId)
currentProfileId = taskInfo.userId
}
}
@@ -265,25 +268,28 @@ class DesktopTasksTransitionObserver(
}
override fun onTransitionFinished(transition: IBinder, aborted: Boolean) {
+ val lastSeenTransitionToCloseWallpaper = transitionToCloseWallpaper
// TODO: b/332682201 Update repository state
- if (transitionToCloseWallpaper == transition) {
+ if (lastSeenTransitionToCloseWallpaper?.transition == transition) {
// TODO: b/362469671 - Handle merging the animation when desktop is also closing.
- desktopWallpaperActivityTokenProvider.getToken()?.let { wallpaperActivityToken ->
- if (Flags.enableDesktopWallpaperActivityForSystemUser()) {
- transitions.startTransition(
- TRANSIT_TO_BACK,
- WindowContainerTransaction()
- .reorder(wallpaperActivityToken, /* onTop= */ false),
- null,
- )
- } else {
- transitions.startTransition(
- TRANSIT_CLOSE,
- WindowContainerTransaction().removeTask(wallpaperActivityToken),
- null,
- )
+ desktopWallpaperActivityTokenProvider
+ .getToken(lastSeenTransitionToCloseWallpaper.displayId)
+ ?.let { wallpaperActivityToken ->
+ if (Flags.enableDesktopWallpaperActivityForSystemUser()) {
+ transitions.startTransition(
+ TRANSIT_TO_BACK,
+ WindowContainerTransaction()
+ .reorder(wallpaperActivityToken, /* onTop= */ false),
+ null,
+ )
+ } else {
+ transitions.startTransition(
+ TRANSIT_CLOSE,
+ WindowContainerTransaction().removeTask(wallpaperActivityToken),
+ null,
+ )
+ }
}
- }
transitionToCloseWallpaper = null
} else if (pendingPipTransitionAndPipTask?.first == transition) {
val desktopRepository = desktopUserRepositories.getProfile(currentProfileId)
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java
index bd676ce69cfe..bba778d9f438 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java
@@ -68,12 +68,12 @@ import androidx.annotation.Nullable;
import com.android.internal.protolog.ProtoLog;
import com.android.wm.shell.R;
import com.android.wm.shell.ShellTaskOrganizer;
+import com.android.wm.shell.common.ComponentUtils;
import com.android.wm.shell.common.pip.PipBoundsAlgorithm;
import com.android.wm.shell.common.pip.PipBoundsState;
import com.android.wm.shell.common.pip.PipDisplayLayoutState;
import com.android.wm.shell.common.pip.PipMenuController;
import com.android.wm.shell.common.pip.PipUtils;
-import com.android.wm.shell.common.split.SplitScreenUtils;
import com.android.wm.shell.protolog.ShellProtoLogGroup;
import com.android.wm.shell.shared.TransitionUtil;
import com.android.wm.shell.shared.pip.PipContentOverlay;
@@ -1359,7 +1359,7 @@ public class PipTransition extends PipTransitionController {
public boolean isPackageActiveInPip(@Nullable String packageName) {
final TaskInfo inPipTask = mPipOrganizer.getTaskInfo();
return packageName != null && inPipTask != null && mPipOrganizer.isInPip()
- && packageName.equals(SplitScreenUtils.getPackageName(inPipTask.baseIntent));
+ && packageName.equals(ComponentUtils.getPackageName(inPipTask.baseIntent));
}
private void updatePipForUnhandledTransition(@NonNull TransitionInfo.Change pipChange,
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransitionController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransitionController.java
index 6e7740dc13e3..91150b0070ce 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransitionController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransitionController.java
@@ -129,6 +129,20 @@ public abstract class PipTransitionController implements Transitions.TransitionH
}
/**
+ * Called when the Shell wants to start an exit-via-expand from Pip transition/animation.
+ */
+ public void startExpandTransition(WindowContainerTransaction out) {
+ // Default implementation does nothing.
+ }
+
+ /**
+ * Called when the Shell wants to start a remove Pip transition/animation.
+ */
+ public void startRemoveTransition(boolean withFadeout) {
+ // Default implementation does nothing.
+ }
+
+ /**
* Called when the Shell wants to start resizing Pip transition/animation.
*
* @param duration the suggested duration for resize animation.
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipMotionHelper.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipMotionHelper.java
index 9babe9e9e4eb..e22cd37ab4b7 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipMotionHelper.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipMotionHelper.java
@@ -344,13 +344,22 @@ public class PipMotionHelper implements PipAppOpsListener.Callback,
*/
@Override
public void dismissPip() {
+ dismissPip(true /* withFadeout */);
+ }
+
+ /**
+ * Dismisses the pinned stack.
+ *
+ * @param withFadeout should animate with fadeout for the removal
+ */
+ public void dismissPip(boolean withFadeout) {
if (DEBUG) {
ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
"%s: removePip: callers=\n%s", TAG, Debug.getCallers(5, " "));
}
cancelPhysicsAnimation();
mMenuController.hideMenu(ANIM_TYPE_DISMISS, false /* resize */);
- mPipScheduler.scheduleRemovePip();
+ mPipScheduler.scheduleRemovePip(withFadeout);
}
/** Sets the movement bounds to use to constrain PIP position animations. */
@@ -473,7 +482,7 @@ public class PipMotionHelper implements PipAppOpsListener.Callback,
mPipBoundsState.getMovementBounds().bottom + getBounds().height() * 2,
0,
mSpringConfig)
- .withEndActions(this::dismissPip);
+ .withEndActions(() -> dismissPip(false /* withFadeout */));
startBoundsAnimator(
getBounds().left /* toX */, getBounds().bottom + getBounds().height() /* toY */);
@@ -772,7 +781,6 @@ public class PipMotionHelper implements PipAppOpsListener.Callback,
case PipTransitionState.EXITING_PIP:
// We need to force finish any local animators if about to leave PiP, to avoid
// breaking the state (e.g. leashes are cleaned up upon exit).
- if (!mPipBoundsState.getMotionBoundsState().isInMotion()) break;
cancelPhysicsAnimation();
settlePipBoundsAfterPhysicsAnimation(false /* animatingAfter */);
break;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipScheduler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipScheduler.java
index ea8dac982703..ed532cad0523 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipScheduler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipScheduler.java
@@ -19,9 +19,6 @@ package com.android.wm.shell.pip2.phone;
import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
-import static com.android.wm.shell.transition.Transitions.TRANSIT_EXIT_PIP;
-import static com.android.wm.shell.transition.Transitions.TRANSIT_REMOVE_PIP;
-
import android.content.Context;
import android.graphics.Matrix;
import android.graphics.Rect;
@@ -112,19 +109,6 @@ public class PipScheduler {
return wct;
}
- @Nullable
- private WindowContainerTransaction getRemovePipTransaction() {
- WindowContainerToken pipTaskToken = mPipTransitionState.getPipTaskToken();
- if (pipTaskToken == null) {
- return null;
- }
- WindowContainerTransaction wct = new WindowContainerTransaction();
- wct.setBounds(pipTaskToken, null);
- wct.setWindowingMode(pipTaskToken, WINDOWING_MODE_UNDEFINED);
- wct.reorder(pipTaskToken, false);
- return wct;
- }
-
/**
* Schedules exit PiP via expand transition.
*/
@@ -133,21 +117,16 @@ public class PipScheduler {
if (!mPipTransitionState.isInPip()) return;
WindowContainerTransaction wct = getExitPipViaExpandTransaction();
if (wct != null) {
- mPipTransitionController.startExitTransition(TRANSIT_EXIT_PIP, wct,
- null /* destinationBounds */);
+ mPipTransitionController.startExpandTransition(wct);
}
});
}
/** Schedules remove PiP transition. */
- public void scheduleRemovePip() {
+ public void scheduleRemovePip(boolean withFadeout) {
mMainExecutor.execute(() -> {
if (!mPipTransitionState.isInPip()) return;
- WindowContainerTransaction wct = getRemovePipTransaction();
- if (wct != null) {
- mPipTransitionController.startExitTransition(TRANSIT_REMOVE_PIP, wct,
- null /* destinationBounds */);
- }
+ mPipTransitionController.startRemoveTransition(withFadeout);
});
}
@@ -216,9 +195,11 @@ public class PipScheduler {
* @param degrees the angle to rotate the bounds to.
*/
public void scheduleUserResizePip(Rect toBounds, float degrees) {
- if (toBounds.isEmpty()) {
+ if (toBounds.isEmpty() || !mPipTransitionState.isInPip()) {
ProtoLog.w(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
- "%s: Attempted to user resize PIP to empty bounds, aborting.", TAG);
+ "%s: Attempted to user resize PIP in invalid state, aborting;"
+ + "toBounds=%s, mPipTransitionState=%s",
+ TAG, toBounds, mPipTransitionState);
return;
}
SurfaceControl leash = mPipTransitionState.getPinnedTaskLeash();
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTransition.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTransition.java
index 38015ca6d45f..4902455cae16 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTransition.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTransition.java
@@ -18,6 +18,7 @@ package com.android.wm.shell.pip2.phone;
import static android.app.WindowConfiguration.ROTATION_UNDEFINED;
import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
+import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
import static android.view.Surface.ROTATION_0;
import static android.view.Surface.ROTATION_270;
import static android.view.Surface.ROTATION_90;
@@ -57,12 +58,12 @@ import androidx.annotation.Nullable;
import com.android.internal.util.Preconditions;
import com.android.window.flags.Flags;
import com.android.wm.shell.ShellTaskOrganizer;
+import com.android.wm.shell.common.ComponentUtils;
import com.android.wm.shell.common.pip.PipBoundsAlgorithm;
import com.android.wm.shell.common.pip.PipBoundsState;
import com.android.wm.shell.common.pip.PipDisplayLayoutState;
import com.android.wm.shell.common.pip.PipMenuController;
import com.android.wm.shell.common.pip.PipUtils;
-import com.android.wm.shell.common.split.SplitScreenUtils;
import com.android.wm.shell.desktopmode.DesktopRepository;
import com.android.wm.shell.desktopmode.DesktopUserRepositories;
import com.android.wm.shell.desktopmode.desktopwallpaperactivity.DesktopWallpaperActivityTokenProvider;
@@ -126,6 +127,7 @@ public class PipTransition extends PipTransitionController implements
@Nullable
private IBinder mResizeTransition;
private int mBoundsChangeDuration = BOUNDS_CHANGE_JUMPCUT_DURATION;
+ private boolean mPendingRemoveWithFadeout;
//
@@ -184,15 +186,19 @@ public class PipTransition extends PipTransitionController implements
//
@Override
- public void startExitTransition(int type, WindowContainerTransaction out,
- @Nullable Rect destinationBounds) {
- if (out == null) {
- return;
- }
- IBinder transition = mTransitions.startTransition(type, out, this);
- if (type == TRANSIT_EXIT_PIP) {
- mExitViaExpandTransition = transition;
- }
+ public void startExpandTransition(WindowContainerTransaction out) {
+ if (out == null) return;
+ mPipTransitionState.setState(PipTransitionState.EXITING_PIP);
+ mExitViaExpandTransition = mTransitions.startTransition(TRANSIT_EXIT_PIP, out, this);
+ }
+
+ @Override
+ public void startRemoveTransition(boolean withFadeout) {
+ final WindowContainerTransaction wct = getRemovePipTransaction();
+ if (wct == null) return;
+ mPipTransitionState.setState(PipTransitionState.EXITING_PIP);
+ mPendingRemoveWithFadeout = withFadeout;
+ mTransitions.startTransition(TRANSIT_REMOVE_PIP, wct, this);
}
@Override
@@ -284,7 +290,6 @@ public class PipTransition extends PipTransitionController implements
finishCallback);
} else if (transition == mExitViaExpandTransition) {
mExitViaExpandTransition = null;
- mPipTransitionState.setState(PipTransitionState.EXITING_PIP);
return startExpandAnimation(info, startTransaction, finishTransaction, finishCallback);
} else if (transition == mResizeTransition) {
mResizeTransition = null;
@@ -690,11 +695,19 @@ public class PipTransition extends PipTransitionController implements
TransitionInfo.Change pipChange = getChangeByToken(info,
mPipTransitionState.getPipTaskToken());
mFinishCallback = finishCallback;
- PipAlphaAnimator animator = new PipAlphaAnimator(mContext, pipChange.getLeash(),
- startTransaction, PipAlphaAnimator.FADE_OUT);
+
finishTransaction.setAlpha(pipChange.getLeash(), 0f);
- animator.setAnimationEndCallback(this::finishTransition);
- animator.start();
+ if (mPendingRemoveWithFadeout) {
+ PipAlphaAnimator animator = new PipAlphaAnimator(mContext, pipChange.getLeash(),
+ startTransaction, PipAlphaAnimator.FADE_OUT);
+ animator.setAnimationEndCallback(this::finishTransition);
+ animator.start();
+ } else {
+ // Jumpcut to a faded-out PiP if no fadeout animation was requested.
+ startTransaction.setAlpha(pipChange.getLeash(), 0f);
+ startTransaction.apply();
+ finishTransition();
+ }
return true;
}
@@ -824,6 +837,19 @@ public class PipTransition extends PipTransitionController implements
return wct;
}
+ @Nullable
+ private WindowContainerTransaction getRemovePipTransaction() {
+ WindowContainerToken pipTaskToken = mPipTransitionState.getPipTaskToken();
+ if (pipTaskToken == null) {
+ return null;
+ }
+ WindowContainerTransaction wct = new WindowContainerTransaction();
+ wct.setBounds(pipTaskToken, null);
+ wct.setWindowingMode(pipTaskToken, WINDOWING_MODE_UNDEFINED);
+ wct.reorder(pipTaskToken, false);
+ return wct;
+ }
+
private boolean isAutoEnterInButtonNavigation(@NonNull TransitionRequestInfo requestInfo) {
final ActivityManager.RunningTaskInfo pipTask = requestInfo.getPipChange() != null
? requestInfo.getPipChange().getTaskInfo() : null;
@@ -1000,6 +1026,7 @@ public class PipTransition extends PipTransitionController implements
}
mPipTransitionState.setPinnedTaskLeash(null);
mPipTransitionState.setPipTaskInfo(null);
+ mPendingRemoveWithFadeout = false;
break;
}
}
@@ -1008,6 +1035,6 @@ public class PipTransition extends PipTransitionController implements
public boolean isPackageActiveInPip(@Nullable String packageName) {
final TaskInfo inPipTask = mPipTransitionState.getPipTaskInfo();
return packageName != null && inPipTask != null && mPipTransitionState.isInPip()
- && packageName.equals(SplitScreenUtils.getPackageName(inPipTask.baseIntent));
+ && packageName.equals(ComponentUtils.getPackageName(inPipTask.baseIntent));
}
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTransitionState.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTransitionState.java
index 6f9f40a487c7..8805cbb0dfbd 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTransitionState.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTransitionState.java
@@ -27,7 +27,9 @@ import android.window.WindowContainerToken;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
+import com.android.internal.protolog.ProtoLog;
import com.android.internal.util.Preconditions;
+import com.android.wm.shell.protolog.ShellProtoLogGroup;
import com.android.wm.shell.shared.annotations.ShellMainThread;
import java.io.PrintWriter;
@@ -201,6 +203,13 @@ public class PipTransitionState {
Preconditions.checkArgument(extra != null && !extra.isEmpty(),
"No extra bundle for " + stateToString(state) + " state.");
}
+ if (!shouldTransitionToState(state)) {
+ ProtoLog.v(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
+ "%s: Attempted to transition to an invalid state=%s, while in %s",
+ TAG, stateToString(state), this);
+ return;
+ }
+
if (mState != state) {
final int prevState = mState;
mState = state;
@@ -374,6 +383,17 @@ public class PipTransitionState {
return ++mPrevCustomState;
}
+ private boolean shouldTransitionToState(@TransitionState int newState) {
+ switch (newState) {
+ case SCHEDULED_BOUNDS_CHANGE:
+ // Allow scheduling bounds change only while in PiP, except for if another bounds
+ // change was scheduled but hasn't started playing yet.
+ return isInPip();
+ default:
+ return true;
+ }
+ }
+
private static String stateToString(int state) {
switch (state) {
case UNDEFINED: return "undefined";
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/recents/IRecentsAnimationRunner.aidl b/libs/WindowManager/Shell/src/com/android/wm/shell/recents/IRecentsAnimationRunner.aidl
index 8cdb8c4512a9..32c79a2d02de 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/recents/IRecentsAnimationRunner.aidl
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/recents/IRecentsAnimationRunner.aidl
@@ -17,10 +17,9 @@
package com.android.wm.shell.recents;
import android.graphics.Rect;
-import android.os.Bundle;
import android.view.RemoteAnimationTarget;
import android.window.TaskSnapshot;
-import android.window.TransitionInfo;
+import android.os.Bundle;
import com.android.wm.shell.recents.IRecentsAnimationController;
@@ -58,8 +57,7 @@ oneway interface IRecentsAnimationRunner {
*/
void onAnimationStart(in IRecentsAnimationController controller,
in RemoteAnimationTarget[] apps, in RemoteAnimationTarget[] wallpapers,
- in Rect homeContentInsets, in Rect minimizedHomeBounds, in Bundle extras,
- in TransitionInfo info) = 2;
+ in Rect homeContentInsets, in Rect minimizedHomeBounds, in Bundle extras) = 2;
/**
* Called when the task of an activity that has been started while the recents animation
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentTasksController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentTasksController.java
index d6f9183a4281..975b65023f20 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentTasksController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentTasksController.java
@@ -103,6 +103,7 @@ public class RecentTasksController implements TaskStackListenerCallback,
private final RecentTasksImpl mImpl = new RecentTasksImpl();
private final ActivityTaskManager mActivityTaskManager;
private final TaskStackTransitionObserver mTaskStackTransitionObserver;
+ private final RecentsShellCommandHandler mRecentsShellCommandHandler;
private RecentsTransitionHandler mTransitionHandler = null;
private IRecentTasksListener mListener;
private final boolean mPcFeatureEnabled;
@@ -167,6 +168,7 @@ public class RecentTasksController implements TaskStackListenerCallback,
mDesktopUserRepositories = desktopUserRepositories;
mTaskStackTransitionObserver = taskStackTransitionObserver;
mMainExecutor = mainExecutor;
+ mRecentsShellCommandHandler = new RecentsShellCommandHandler(this);
shellInit.addInitCallback(this::onInit, this);
}
@@ -183,6 +185,7 @@ public class RecentTasksController implements TaskStackListenerCallback,
mShellController.addExternalInterface(IRecentTasks.DESCRIPTOR,
this::createExternalInterface, this);
mShellCommandHandler.addDumpCallback(this::dump, this);
+ mShellCommandHandler.addCommandCallback("recents", mRecentsShellCommandHandler, this);
mUserId = ActivityManager.getCurrentUser();
mDesktopUserRepositories.ifPresent(
desktopUserRepositories ->
@@ -656,6 +659,11 @@ public class RecentTasksController implements TaskStackListenerCallback,
return mActivityTaskManager.removeTask(taskId);
}
+ /** Removes all recent tasks that are visible. */
+ public void removeAllVisibleRecentTasks() throws RemoteException {
+ ActivityTaskManager.getService().removeAllVisibleRecentTasks();
+ }
+
public void dump(@NonNull PrintWriter pw, String prefix) {
final String innerPrefix = prefix + " ";
pw.println(prefix + TAG);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentsShellCommandHandler.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentsShellCommandHandler.kt
new file mode 100644
index 000000000000..f786e079ed93
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentsShellCommandHandler.kt
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.recents
+
+import android.os.RemoteException
+import com.android.wm.shell.sysui.ShellCommandHandler.ShellCommandActionHandler
+import java.io.PrintWriter
+
+class RecentsShellCommandHandler(
+ private val recentTasksController: RecentTasksController
+) : ShellCommandActionHandler {
+ override fun onShellCommand(args: Array<out String>, pw: PrintWriter): Boolean {
+ when (args[0]) {
+ "clearAll" -> return runClearAll(pw)
+ else -> {
+ pw.println("Invalid command: " + args[0])
+ return false
+ }
+ }
+ }
+
+ override fun printShellCommandHelp(pw: PrintWriter, prefix: String) {
+ pw.println("${prefix}clearAll")
+ pw.println("$prefix Clears all visible recent tasks.")
+ }
+
+ private fun runClearAll(pw: PrintWriter): Boolean {
+ try {
+ recentTasksController.removeAllVisibleRecentTasks()
+ } catch (e: RemoteException) {
+ pw.println("Exception while removing visible recent tasks:")
+ e.printStackTrace(pw)
+ return false
+ }
+ return true
+ }
+} \ No newline at end of file
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentsTransitionHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentsTransitionHandler.java
index aeccd86e122c..db582aa30f6a 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentsTransitionHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentsTransitionHandler.java
@@ -587,8 +587,7 @@ public class RecentsTransitionHandler implements Transitions.TransitionHandler,
mListener.onAnimationStart(this,
apps.toArray(new RemoteAnimationTarget[apps.size()]),
new RemoteAnimationTarget[0],
- new Rect(0, 0, 0, 0), new Rect(), new Bundle(),
- null);
+ new Rect(0, 0, 0, 0), new Rect(), new Bundle());
for (int i = 0; i < mStateListeners.size(); i++) {
mStateListeners.get(i).onTransitionStateChanged(TRANSITION_STATE_ANIMATING);
}
@@ -819,7 +818,7 @@ public class RecentsTransitionHandler implements Transitions.TransitionHandler,
mListener.onAnimationStart(this,
apps.toArray(new RemoteAnimationTarget[apps.size()]),
wallpapers.toArray(new RemoteAnimationTarget[wallpapers.size()]),
- new Rect(0, 0, 0, 0), new Rect(), b, info);
+ new Rect(0, 0, 0, 0), new Rect(), b);
for (int i = 0; i < mStateListeners.size(); i++) {
mStateListeners.get(i).onTransitionStateChanged(TRANSITION_STATE_ANIMATING);
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java
index c724135aeced..9e88a260ac44 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java
@@ -82,6 +82,7 @@ import com.android.launcher3.icons.IconProvider;
import com.android.wm.shell.R;
import com.android.wm.shell.RootTaskDisplayAreaOrganizer;
import com.android.wm.shell.ShellTaskOrganizer;
+import com.android.wm.shell.common.ComponentUtils;
import com.android.wm.shell.common.DisplayController;
import com.android.wm.shell.common.DisplayImeController;
import com.android.wm.shell.common.DisplayInsetsController;
@@ -682,7 +683,7 @@ public class SplitScreenController implements SplitDragPolicy.Starter,
final String packageName1 = shortcutInfo.getPackage();
// NOTE: This doesn't correctly pull out packageName2 if taskId is referring to a task in
// recents that hasn't launched and is not being organized
- final String packageName2 = SplitScreenUtils.getPackageName(taskId, mTaskOrganizer);
+ final String packageName2 = ComponentUtils.getPackageName(taskId, mTaskOrganizer);
final int userId1 = shortcutInfo.getUserId();
final int userId2 = SplitScreenUtils.getUserId(taskId, mTaskOrganizer);
if (samePackage(packageName1, packageName2, userId1, userId2)) {
@@ -727,10 +728,10 @@ public class SplitScreenController implements SplitDragPolicy.Starter,
@SplitPosition int splitPosition, @PersistentSnapPosition int snapPosition,
@Nullable RemoteTransition remoteTransition, InstanceId instanceId) {
Intent fillInIntent = null;
- final String packageName1 = SplitScreenUtils.getPackageName(pendingIntent);
+ final String packageName1 = ComponentUtils.getPackageName(pendingIntent);
// NOTE: This doesn't correctly pull out packageName2 if taskId is referring to a task in
// recents that hasn't launched and is not being organized
- final String packageName2 = SplitScreenUtils.getPackageName(taskId, mTaskOrganizer);
+ final String packageName2 = ComponentUtils.getPackageName(taskId, mTaskOrganizer);
final int userId2 = SplitScreenUtils.getUserId(taskId, mTaskOrganizer);
boolean setSecondIntentMultipleTask = false;
if (samePackage(packageName1, packageName2, userId1, userId2)) {
@@ -766,8 +767,8 @@ public class SplitScreenController implements SplitDragPolicy.Starter,
InstanceId instanceId) {
Intent fillInIntent1 = null;
Intent fillInIntent2 = null;
- final String packageName1 = SplitScreenUtils.getPackageName(pendingIntent1);
- final String packageName2 = SplitScreenUtils.getPackageName(pendingIntent2);
+ final String packageName1 = ComponentUtils.getPackageName(pendingIntent1);
+ final String packageName2 = ComponentUtils.getPackageName(pendingIntent2);
final ActivityOptions activityOptions1 = options1 != null
? ActivityOptions.fromBundle(options1) : ActivityOptions.makeBasic();
final ActivityOptions activityOptions2 = options2 != null
@@ -835,7 +836,7 @@ public class SplitScreenController implements SplitDragPolicy.Starter,
if (fillInIntent == null) fillInIntent = new Intent();
fillInIntent.addFlags(FLAG_ACTIVITY_NO_USER_ACTION);
- final String packageName1 = SplitScreenUtils.getPackageName(intent);
+ final String packageName1 = ComponentUtils.getPackageName(intent);
final String packageName2 = getPackageName(reverseSplitPosition(position), hideTaskToken);
final int userId2 = getUserId(reverseSplitPosition(position), hideTaskToken);
final ComponentName component = intent.getIntent().getComponent();
@@ -900,7 +901,7 @@ public class SplitScreenController implements SplitDragPolicy.Starter,
}
}
- return taskInfo != null ? SplitScreenUtils.getPackageName(taskInfo.baseIntent) : null;
+ return taskInfo != null ? ComponentUtils.getPackageName(taskInfo.baseIntent) : null;
}
/**
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
index b6bd879c75eb..c27ef295db51 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
@@ -131,6 +131,7 @@ import com.android.internal.protolog.ProtoLog;
import com.android.launcher3.icons.IconProvider;
import com.android.wm.shell.R;
import com.android.wm.shell.ShellTaskOrganizer;
+import com.android.wm.shell.common.ComponentUtils;
import com.android.wm.shell.common.DisplayController;
import com.android.wm.shell.common.DisplayImeController;
import com.android.wm.shell.common.DisplayInsetsController;
@@ -140,7 +141,6 @@ import com.android.wm.shell.common.SyncTransactionQueue;
import com.android.wm.shell.common.split.OffscreenTouchZone;
import com.android.wm.shell.common.split.SplitDecorManager;
import com.android.wm.shell.common.split.SplitLayout;
-import com.android.wm.shell.common.split.SplitScreenUtils;
import com.android.wm.shell.common.split.SplitState;
import com.android.wm.shell.common.split.SplitWindowManager;
import com.android.wm.shell.desktopmode.DesktopTasksController;
@@ -3536,12 +3536,12 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
if (mSplitRequest.mActivateTaskId == taskInfo.taskId) {
return mSplitRequest.mActivatePosition;
}
- final String packageName1 = SplitScreenUtils.getPackageName(mSplitRequest.mStartIntent);
- final String basePackageName = SplitScreenUtils.getPackageName(taskInfo.baseIntent);
+ final String packageName1 = ComponentUtils.getPackageName(mSplitRequest.mStartIntent);
+ final String basePackageName = ComponentUtils.getPackageName(taskInfo.baseIntent);
if (packageName1 != null && packageName1.equals(basePackageName)) {
return mSplitRequest.mActivatePosition;
}
- final String packageName2 = SplitScreenUtils.getPackageName(mSplitRequest.mStartIntent2);
+ final String packageName2 = ComponentUtils.getPackageName(mSplitRequest.mStartIntent2);
if (packageName2 != null && packageName2.equals(basePackageName)) {
return mSplitRequest.mActivatePosition;
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/taskview/TaskViewTaskController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/taskview/TaskViewTaskController.java
index 8dd14983e122..5c7dd078ee45 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/taskview/TaskViewTaskController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/taskview/TaskViewTaskController.java
@@ -536,9 +536,12 @@ public class TaskViewTaskController implements ShellTaskOrganizer.TaskListener {
}
return;
}
+ // Cache it to avoid NPE and make sure to remove it from recents history.
+ // mTaskToken can be cleared in onTaskVanished() when the task is removed.
+ final WindowContainerToken taskToken = mTaskToken;
mShellExecutor.execute(() -> {
WindowContainerTransaction wct = new WindowContainerTransaction();
- wct.removeTask(mTaskToken);
+ wct.removeTask(taskToken);
mTaskViewTransitions.closeTaskView(wct, this);
});
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultMixedHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultMixedHandler.java
index 41c0a11827bb..2177986bccd5 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultMixedHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultMixedHandler.java
@@ -43,7 +43,7 @@ import com.android.internal.protolog.ProtoLog;
import com.android.window.flags.Flags;
import com.android.wm.shell.ShellTaskOrganizer;
import com.android.wm.shell.activityembedding.ActivityEmbeddingController;
-import com.android.wm.shell.common.split.SplitScreenUtils;
+import com.android.wm.shell.common.ComponentUtils;
import com.android.wm.shell.desktopmode.DesktopTasksController;
import com.android.wm.shell.keyguard.KeyguardTransitionHandler;
import com.android.wm.shell.pip.PipTransitionController;
@@ -645,7 +645,7 @@ public class DefaultMixedHandler implements MixedTransitionHandler,
// task enter split.
if (mPipHandler != null) {
return mPipHandler
- .isPackageActiveInPip(SplitScreenUtils.getPackageName(intent.getIntent()));
+ .isPackageActiveInPip(ComponentUtils.getPackageName(intent.getIntent()));
}
return false;
}
@@ -657,7 +657,7 @@ public class DefaultMixedHandler implements MixedTransitionHandler,
// task enter split.
if (mPipHandler != null) {
return mPipHandler.isPackageActiveInPip(
- SplitScreenUtils.getPackageName(taskId, shellTaskOrganizer));
+ ComponentUtils.getPackageName(taskId, shellTaskOrganizer));
}
return false;
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java
index 429e0564dd2c..67dae283345a 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java
@@ -126,8 +126,6 @@ import com.android.wm.shell.desktopmode.common.ToggleTaskSizeUtilsKt;
import com.android.wm.shell.desktopmode.education.AppHandleEducationController;
import com.android.wm.shell.desktopmode.education.AppToWebEducationController;
import com.android.wm.shell.freeform.FreeformTaskTransitionStarter;
-import com.android.wm.shell.recents.RecentsTransitionHandler;
-import com.android.wm.shell.recents.RecentsTransitionStateListener;
import com.android.wm.shell.shared.FocusTransitionListener;
import com.android.wm.shell.shared.annotations.ShellBackgroundThread;
import com.android.wm.shell.shared.annotations.ShellMainThread;
@@ -159,10 +157,8 @@ import kotlinx.coroutines.MainCoroutineDispatcher;
import java.io.PrintWriter;
import java.util.ArrayList;
-import java.util.HashSet;
import java.util.List;
import java.util.Optional;
-import java.util.Set;
import java.util.function.Supplier;
/**
@@ -251,7 +247,6 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel,
private final DesktopModeEventLogger mDesktopModeEventLogger;
private final DesktopModeUiEventLogger mDesktopModeUiEventLogger;
private final WindowDecorTaskResourceLoader mTaskResourceLoader;
- private final RecentsTransitionHandler mRecentsTransitionHandler;
public DesktopModeWindowDecorViewModel(
Context context,
@@ -287,8 +282,7 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel,
FocusTransitionObserver focusTransitionObserver,
DesktopModeEventLogger desktopModeEventLogger,
DesktopModeUiEventLogger desktopModeUiEventLogger,
- WindowDecorTaskResourceLoader taskResourceLoader,
- RecentsTransitionHandler recentsTransitionHandler) {
+ WindowDecorTaskResourceLoader taskResourceLoader) {
this(
context,
shellExecutor,
@@ -329,8 +323,7 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel,
focusTransitionObserver,
desktopModeEventLogger,
desktopModeUiEventLogger,
- taskResourceLoader,
- recentsTransitionHandler);
+ taskResourceLoader);
}
@VisibleForTesting
@@ -374,8 +367,7 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel,
FocusTransitionObserver focusTransitionObserver,
DesktopModeEventLogger desktopModeEventLogger,
DesktopModeUiEventLogger desktopModeUiEventLogger,
- WindowDecorTaskResourceLoader taskResourceLoader,
- RecentsTransitionHandler recentsTransitionHandler) {
+ WindowDecorTaskResourceLoader taskResourceLoader) {
mContext = context;
mMainExecutor = shellExecutor;
mMainHandler = mainHandler;
@@ -444,7 +436,6 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel,
mDesktopModeEventLogger = desktopModeEventLogger;
mDesktopModeUiEventLogger = desktopModeUiEventLogger;
mTaskResourceLoader = taskResourceLoader;
- mRecentsTransitionHandler = recentsTransitionHandler;
shellInit.addInitCallback(this::onInit, this);
}
@@ -459,10 +450,6 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel,
new DesktopModeOnTaskResizeAnimationListener());
mDesktopTasksController.setOnTaskRepositionAnimationListener(
new DesktopModeOnTaskRepositionAnimationListener());
- if (Flags.enableDesktopRecentsTransitionsCornersBugfix()) {
- mRecentsTransitionHandler.addTransitionStateListener(
- new DesktopModeRecentsTransitionStateListener());
- }
mDisplayController.addDisplayChangingController(mOnDisplayChangingListener);
try {
mWindowManager.registerSystemGestureExclusionListener(mGestureExclusionListener,
@@ -1872,38 +1859,6 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel,
}
}
- private class DesktopModeRecentsTransitionStateListener
- implements RecentsTransitionStateListener {
- final Set<Integer> mAnimatingTaskIds = new HashSet<>();
-
- @Override
- public void onTransitionStateChanged(int state) {
- switch (state) {
- case RecentsTransitionStateListener.TRANSITION_STATE_REQUESTED:
- for (int n = 0; n < mWindowDecorByTaskId.size(); n++) {
- int taskId = mWindowDecorByTaskId.keyAt(n);
- mAnimatingTaskIds.add(taskId);
- setIsRecentsTransitionRunningForTask(taskId, true);
- }
- return;
- case RecentsTransitionStateListener.TRANSITION_STATE_NOT_RUNNING:
- // No Recents transition running - clean up window decorations
- for (int taskId : mAnimatingTaskIds) {
- setIsRecentsTransitionRunningForTask(taskId, false);
- }
- mAnimatingTaskIds.clear();
- return;
- default:
- }
- }
-
- private void setIsRecentsTransitionRunningForTask(int taskId, boolean isRecentsRunning) {
- final DesktopModeWindowDecoration decoration = mWindowDecorByTaskId.get(taskId);
- if (decoration == null) return;
- decoration.setIsRecentsTransitionRunning(isRecentsRunning);
- }
- }
-
private class DragEventListenerImpl
implements DragPositioningCallbackUtility.DragEventListener {
@Override
@@ -2014,7 +1969,9 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel,
Supplier<SurfaceControl.Transaction> transactionFactory,
Handler handler) {
final TaskPositioner taskPositioner = DesktopModeStatus.isVeiledResizeEnabled()
- ? new VeiledResizeTaskPositioner(
+ // TODO(b/383632995): Update when the flag is launched.
+ ? (Flags.enableConnectedDisplaysWindowDrag()
+ ? new MultiDisplayVeiledResizeTaskPositioner(
taskOrganizer,
windowDecoration,
displayController,
@@ -2022,6 +1979,14 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel,
transitions,
interactionJankMonitor,
handler)
+ : new VeiledResizeTaskPositioner(
+ taskOrganizer,
+ windowDecoration,
+ displayController,
+ dragEventListener,
+ transitions,
+ interactionJankMonitor,
+ handler))
: new FluidResizeTaskPositioner(
taskOrganizer,
transitions,
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecoration.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecoration.java
index 39a989ce7c7f..4ac89546c9c7 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecoration.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecoration.java
@@ -204,7 +204,6 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin
private final MultiInstanceHelper mMultiInstanceHelper;
private final WindowDecorCaptionHandleRepository mWindowDecorCaptionHandleRepository;
private final DesktopUserRepositories mDesktopUserRepositories;
- private boolean mIsRecentsTransitionRunning = false;
private Runnable mLoadAppInfoRunnable;
private Runnable mSetAppInfoRunnable;
@@ -499,7 +498,7 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin
applyStartTransactionOnDraw, shouldSetTaskVisibilityPositionAndCrop,
mIsStatusBarVisible, mIsKeyguardVisibleAndOccluded, inFullImmersive,
mDisplayController.getInsetsState(taskInfo.displayId), hasGlobalFocus,
- displayExclusionRegion, mIsRecentsTransitionRunning);
+ displayExclusionRegion);
final WindowDecorLinearLayout oldRootView = mResult.mRootView;
final SurfaceControl oldDecorationSurface = mDecorationContainerSurface;
@@ -870,8 +869,7 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin
boolean inFullImmersiveMode,
@NonNull InsetsState displayInsetsState,
boolean hasGlobalFocus,
- @NonNull Region displayExclusionRegion,
- boolean shouldIgnoreCornerRadius) {
+ @NonNull Region displayExclusionRegion) {
final int captionLayoutId = getDesktopModeWindowDecorLayoutId(taskInfo.getWindowingMode());
final boolean isAppHeader =
captionLayoutId == R.layout.desktop_mode_app_header;
@@ -1008,19 +1006,13 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin
relayoutParams.mWindowDecorConfig = windowDecorConfig;
if (DesktopModeStatus.useRoundedCorners()) {
- relayoutParams.mCornerRadius = shouldIgnoreCornerRadius ? INVALID_CORNER_RADIUS :
- getCornerRadius(context, relayoutParams.mLayoutResId);
+ relayoutParams.mCornerRadius = taskInfo.getWindowingMode() == WINDOWING_MODE_FREEFORM
+ ? loadDimensionPixelSize(context.getResources(),
+ R.dimen.desktop_windowing_freeform_rounded_corner_radius)
+ : INVALID_CORNER_RADIUS;
}
}
- private static int getCornerRadius(@NonNull Context context, int layoutResId) {
- if (layoutResId == R.layout.desktop_mode_app_header) {
- return loadDimensionPixelSize(context.getResources(),
- R.dimen.desktop_windowing_freeform_rounded_corner_radius);
- }
- return INVALID_CORNER_RADIUS;
- }
-
/**
* If task has focused window decor, return the caption id of the fullscreen caption size
* resource. Otherwise, return ID_NULL and caption width be set to task width.
@@ -1748,17 +1740,6 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin
}
/**
- * Declares whether a Recents transition is currently active.
- *
- * <p> When a Recents transition is active we allow that transition to take ownership of the
- * corner radius of its task surfaces, so each window decoration should stop updating the corner
- * radius of its task surface during that time.
- */
- void setIsRecentsTransitionRunning(boolean isRecentsTransitionRunning) {
- mIsRecentsTransitionRunning = isRecentsTransitionRunning;
- }
-
- /**
* Called when there is a {@link MotionEvent#ACTION_HOVER_EXIT} on the maximize window button.
*/
void onMaximizeButtonHoverExit() {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/MultiDisplayVeiledResizeTaskPositioner.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/MultiDisplayVeiledResizeTaskPositioner.kt
new file mode 100644
index 000000000000..8dc921c986ce
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/MultiDisplayVeiledResizeTaskPositioner.kt
@@ -0,0 +1,293 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.wm.shell.windowdecor
+
+import android.graphics.PointF
+import android.graphics.Rect
+import android.os.Handler
+import android.os.IBinder
+import android.os.Looper
+import android.view.Choreographer
+import android.view.Surface
+import android.view.SurfaceControl
+import android.view.WindowManager
+import android.window.TransitionInfo
+import android.window.TransitionRequestInfo
+import android.window.WindowContainerTransaction
+import com.android.internal.jank.Cuj
+import com.android.internal.jank.InteractionJankMonitor
+import com.android.wm.shell.ShellTaskOrganizer
+import com.android.wm.shell.common.DisplayController
+import com.android.wm.shell.shared.annotations.ShellMainThread
+import com.android.wm.shell.transition.Transitions
+import java.util.concurrent.TimeUnit
+import java.util.function.Supplier
+
+/**
+ * A task positioner that also takes into account resizing a
+ * [com.android.wm.shell.windowdecor.ResizeVeil] and dragging move across multiple displays.
+ * - If the drag is resizing the task, we resize the veil instead.
+ * - If the drag is repositioning, we consider multi-display topology if needed, and update in the
+ * typical manner.
+ */
+class MultiDisplayVeiledResizeTaskPositioner(
+ private val taskOrganizer: ShellTaskOrganizer,
+ private val desktopWindowDecoration: DesktopModeWindowDecoration,
+ private val displayController: DisplayController,
+ dragEventListener: DragPositioningCallbackUtility.DragEventListener,
+ private val transactionSupplier: Supplier<SurfaceControl.Transaction>,
+ private val transitions: Transitions,
+ private val interactionJankMonitor: InteractionJankMonitor,
+ @ShellMainThread private val handler: Handler,
+) : TaskPositioner, Transitions.TransitionHandler {
+ private val dragEventListeners =
+ mutableListOf<DragPositioningCallbackUtility.DragEventListener>()
+ private val stableBounds = Rect()
+ private val taskBoundsAtDragStart = Rect()
+ private val repositionStartPoint = PointF()
+ private val repositionTaskBounds = Rect()
+ private val isResizing: Boolean
+ get() =
+ (ctrlType and DragPositioningCallback.CTRL_TYPE_TOP) != 0 ||
+ (ctrlType and DragPositioningCallback.CTRL_TYPE_BOTTOM) != 0 ||
+ (ctrlType and DragPositioningCallback.CTRL_TYPE_LEFT) != 0 ||
+ (ctrlType and DragPositioningCallback.CTRL_TYPE_RIGHT) != 0
+
+ @DragPositioningCallback.CtrlType private var ctrlType = 0
+ private var isResizingOrAnimatingResize = false
+ @Surface.Rotation private var rotation = 0
+
+ constructor(
+ taskOrganizer: ShellTaskOrganizer,
+ windowDecoration: DesktopModeWindowDecoration,
+ displayController: DisplayController,
+ dragEventListener: DragPositioningCallbackUtility.DragEventListener,
+ transitions: Transitions,
+ interactionJankMonitor: InteractionJankMonitor,
+ @ShellMainThread handler: Handler,
+ ) : this(
+ taskOrganizer,
+ windowDecoration,
+ displayController,
+ dragEventListener,
+ Supplier<SurfaceControl.Transaction> { SurfaceControl.Transaction() },
+ transitions,
+ interactionJankMonitor,
+ handler,
+ )
+
+ init {
+ dragEventListeners.add(dragEventListener)
+ }
+
+ override fun onDragPositioningStart(ctrlType: Int, displayId: Int, x: Float, y: Float): Rect {
+ this.ctrlType = ctrlType
+ taskBoundsAtDragStart.set(
+ desktopWindowDecoration.mTaskInfo.configuration.windowConfiguration.bounds
+ )
+ repositionStartPoint[x] = y
+ if (isResizing) {
+ // Capture CUJ for re-sizing window in DW mode.
+ interactionJankMonitor.begin(
+ createLongTimeoutJankConfigBuilder(Cuj.CUJ_DESKTOP_MODE_RESIZE_WINDOW)
+ )
+ if (!desktopWindowDecoration.mHasGlobalFocus) {
+ val wct = WindowContainerTransaction()
+ wct.reorder(
+ desktopWindowDecoration.mTaskInfo.token,
+ /* onTop= */ true,
+ /* includingParents= */ true,
+ )
+ taskOrganizer.applyTransaction(wct)
+ }
+ }
+ for (dragEventListener in dragEventListeners) {
+ dragEventListener.onDragStart(desktopWindowDecoration.mTaskInfo.taskId)
+ }
+ repositionTaskBounds.set(taskBoundsAtDragStart)
+ val rotation =
+ desktopWindowDecoration.mTaskInfo.configuration.windowConfiguration.displayRotation
+ if (stableBounds.isEmpty || this.rotation != rotation) {
+ this.rotation = rotation
+ displayController
+ .getDisplayLayout(desktopWindowDecoration.mDisplay.displayId)!!
+ .getStableBounds(stableBounds)
+ }
+ return Rect(repositionTaskBounds)
+ }
+
+ override fun onDragPositioningMove(displayId: Int, x: Float, y: Float): Rect {
+ check(Looper.myLooper() == handler.looper) {
+ "This method must run on the shell main thread."
+ }
+ val delta = DragPositioningCallbackUtility.calculateDelta(x, y, repositionStartPoint)
+ if (
+ isResizing &&
+ DragPositioningCallbackUtility.changeBounds(
+ ctrlType,
+ repositionTaskBounds,
+ taskBoundsAtDragStart,
+ stableBounds,
+ delta,
+ displayController,
+ desktopWindowDecoration,
+ )
+ ) {
+ if (!isResizingOrAnimatingResize) {
+ for (dragEventListener in dragEventListeners) {
+ dragEventListener.onDragMove(desktopWindowDecoration.mTaskInfo.taskId)
+ }
+ desktopWindowDecoration.showResizeVeil(repositionTaskBounds)
+ isResizingOrAnimatingResize = true
+ } else {
+ desktopWindowDecoration.updateResizeVeil(repositionTaskBounds)
+ }
+ } else if (ctrlType == DragPositioningCallback.CTRL_TYPE_UNDEFINED) {
+ // Begin window drag CUJ instrumentation only when drag position moves.
+ interactionJankMonitor.begin(
+ createLongTimeoutJankConfigBuilder(Cuj.CUJ_DESKTOP_MODE_DRAG_WINDOW)
+ )
+ val t = transactionSupplier.get()
+ DragPositioningCallbackUtility.setPositionOnDrag(
+ desktopWindowDecoration,
+ repositionTaskBounds,
+ taskBoundsAtDragStart,
+ repositionStartPoint,
+ t,
+ x,
+ y,
+ )
+ t.setFrameTimeline(Choreographer.getInstance().vsyncId)
+ t.apply()
+ }
+ return Rect(repositionTaskBounds)
+ }
+
+ override fun onDragPositioningEnd(displayId: Int, x: Float, y: Float): Rect {
+ val delta = DragPositioningCallbackUtility.calculateDelta(x, y, repositionStartPoint)
+ if (isResizing) {
+ if (taskBoundsAtDragStart != repositionTaskBounds) {
+ DragPositioningCallbackUtility.changeBounds(
+ ctrlType,
+ repositionTaskBounds,
+ taskBoundsAtDragStart,
+ stableBounds,
+ delta,
+ displayController,
+ desktopWindowDecoration,
+ )
+ desktopWindowDecoration.updateResizeVeil(repositionTaskBounds)
+ val wct = WindowContainerTransaction()
+ wct.setBounds(desktopWindowDecoration.mTaskInfo.token, repositionTaskBounds)
+ transitions.startTransition(WindowManager.TRANSIT_CHANGE, wct, this)
+ } else {
+ // If bounds haven't changed, perform necessary veil reset here as startAnimation
+ // won't be called.
+ resetVeilIfVisible()
+ }
+ interactionJankMonitor.end(Cuj.CUJ_DESKTOP_MODE_RESIZE_WINDOW)
+ } else {
+ DragPositioningCallbackUtility.updateTaskBounds(
+ repositionTaskBounds,
+ taskBoundsAtDragStart,
+ repositionStartPoint,
+ x,
+ y,
+ )
+ interactionJankMonitor.end(Cuj.CUJ_DESKTOP_MODE_DRAG_WINDOW)
+ }
+
+ ctrlType = DragPositioningCallback.CTRL_TYPE_UNDEFINED
+ taskBoundsAtDragStart.setEmpty()
+ repositionStartPoint[0f] = 0f
+ return Rect(repositionTaskBounds)
+ }
+
+ private fun resetVeilIfVisible() {
+ if (isResizingOrAnimatingResize) {
+ desktopWindowDecoration.hideResizeVeil()
+ isResizingOrAnimatingResize = false
+ }
+ }
+
+ private fun createLongTimeoutJankConfigBuilder(@Cuj.CujType cujType: Int) =
+ InteractionJankMonitor.Configuration.Builder.withSurface(
+ cujType,
+ desktopWindowDecoration.mContext,
+ desktopWindowDecoration.mTaskSurface,
+ handler,
+ )
+ .setTimeout(LONG_CUJ_TIMEOUT_MS)
+
+ override fun startAnimation(
+ transition: IBinder,
+ info: TransitionInfo,
+ startTransaction: SurfaceControl.Transaction,
+ finishTransaction: SurfaceControl.Transaction,
+ finishCallback: Transitions.TransitionFinishCallback,
+ ): Boolean {
+ for (change in info.changes) {
+ val sc = change.leash
+ val endBounds = change.endAbsBounds
+ val endPosition = change.endRelOffset
+ startTransaction
+ .setWindowCrop(sc, endBounds.width(), endBounds.height())
+ .setPosition(sc, endPosition.x.toFloat(), endPosition.y.toFloat())
+ finishTransaction
+ .setWindowCrop(sc, endBounds.width(), endBounds.height())
+ .setPosition(sc, endPosition.x.toFloat(), endPosition.y.toFloat())
+ }
+
+ startTransaction.apply()
+ resetVeilIfVisible()
+ ctrlType = DragPositioningCallback.CTRL_TYPE_UNDEFINED
+ finishCallback.onTransitionFinished(null /* wct */)
+ isResizingOrAnimatingResize = false
+ interactionJankMonitor.end(Cuj.CUJ_DESKTOP_MODE_DRAG_WINDOW)
+ return true
+ }
+
+ /**
+ * We should never reach this as this handler's transitions are only started from shell
+ * explicitly.
+ */
+ override fun handleRequest(
+ transition: IBinder,
+ request: TransitionRequestInfo,
+ ): WindowContainerTransaction? {
+ return null
+ }
+
+ override fun isResizingOrAnimating() = isResizingOrAnimatingResize
+
+ override fun addDragEventListener(
+ dragEventListener: DragPositioningCallbackUtility.DragEventListener
+ ) {
+ dragEventListeners.add(dragEventListener)
+ }
+
+ override fun removeDragEventListener(
+ dragEventListener: DragPositioningCallbackUtility.DragEventListener
+ ) {
+ dragEventListeners.remove(dragEventListener)
+ }
+
+ companion object {
+ // Timeout used for resize and drag CUJs, this is longer than the default timeout to avoid
+ // timing out in the middle of a resize or drag action.
+ private val LONG_CUJ_TIMEOUT_MS = TimeUnit.SECONDS.toMillis(/* duration= */ 10L)
+ }
+}
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 e011cc08903b..d2c79d76e6c1 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
@@ -53,7 +53,12 @@ import java.util.function.Supplier;
* {@link com.android.wm.shell.windowdecor.ResizeVeil}.
* If the drag is resizing the task, we resize the veil instead.
* If the drag is repositioning, we update in the typical manner.
+ * <p>
+ * @deprecated This class will be replaced by
+ * {@link com.android.wm.shell.windowdecor.MultiDisplayVeiledResizeTaskPositioner}.
+ * TODO(b/383632995): Remove this class after MultiDisplayVeiledResizeTaskPositioner is launched.
*/
+@Deprecated
public class VeiledResizeTaskPositioner implements TaskPositioner, Transitions.TransitionHandler {
// Timeout used for resize and drag CUJs, this is longer than the default timeout to avoid
// timing out in the middle of a resize or drag action.
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/WindowDecoration.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/WindowDecoration.java
index fa7183ad0fd8..5d1bedb85b5e 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/WindowDecoration.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/WindowDecoration.java
@@ -967,4 +967,4 @@ public abstract class WindowDecoration<T extends View & TaskFocusStateConsumer>
return Objects.hash(mToken, mOwner, mFrame, Arrays.hashCode(mBoundingRects), mFlags);
}
}
-}
+} \ No newline at end of file
diff --git a/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/functional/EnterDesktopWithDragExistingWindowsTest.kt b/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/functional/EnterDesktopWithDragExistingWindowsTest.kt
new file mode 100644
index 000000000000..2b26bbfb68cb
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/functional/EnterDesktopWithDragExistingWindowsTest.kt
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.functional
+
+import android.platform.test.annotations.Postsubmit
+import com.android.wm.shell.scenarios.EnterDesktopWithDragExistingWindows
+import org.junit.runner.RunWith
+import org.junit.runners.BlockJUnit4ClassRunner
+
+/* Functional test for [EnterDesktopWithDragExistingWindows]. */
+@RunWith(BlockJUnit4ClassRunner::class)
+@Postsubmit
+class EnterDesktopWithDragExistingWindowsTest : EnterDesktopWithDragExistingWindows()
diff --git a/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/EnterDesktopWithAppHandleMenuExistingWindows.kt b/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/EnterDesktopWithAppHandleMenuExistingWindows.kt
new file mode 100644
index 000000000000..2de0830dedb5
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/EnterDesktopWithAppHandleMenuExistingWindows.kt
@@ -0,0 +1,70 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.scenarios
+
+import android.platform.test.annotations.Postsubmit
+import android.app.Instrumentation
+import android.tools.traces.parsers.WindowManagerStateHelper
+import androidx.test.platform.app.InstrumentationRegistry
+import androidx.test.uiautomator.UiDevice
+import com.android.launcher3.tapl.LauncherInstrumentation
+import com.android.server.wm.flicker.helpers.DesktopModeAppHelper
+import com.android.server.wm.flicker.helpers.SimpleAppHelper
+import com.android.server.wm.flicker.helpers.ImeAppHelper
+import com.android.server.wm.flicker.helpers.NewTasksAppHelper
+import com.android.window.flags.Flags
+import org.junit.After
+import org.junit.Assume
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.BlockJUnit4ClassRunner
+
+@RunWith(BlockJUnit4ClassRunner::class)
+@Postsubmit
+open class EnterDesktopWithAppHandleMenuExistingWindows {
+
+ private val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation()
+ private val tapl = LauncherInstrumentation()
+ private val wmHelper = WindowManagerStateHelper(instrumentation)
+ private val device = UiDevice.getInstance(instrumentation)
+ private val imeApp = ImeAppHelper(instrumentation)
+ private val newTaskApp = NewTasksAppHelper(instrumentation)
+ private val testApp = DesktopModeAppHelper(SimpleAppHelper(instrumentation))
+
+ @Before
+ fun setup() {
+ Assume.assumeTrue(Flags.enableDesktopWindowingMode() && tapl.isTablet)
+ testApp.enterDesktopMode(wmHelper, device)
+ imeApp.launchViaIntent(wmHelper)
+ newTaskApp.launchViaIntent(wmHelper)
+ testApp.launchViaIntent(wmHelper)
+ testApp.exitDesktopWithDragToTopDragZone(wmHelper, device)
+ }
+
+ @Test
+ open fun reenterDesktopWithAppHandleMenu() {
+ testApp.enterDesktopModeFromAppHandleMenu(wmHelper, device)
+ }
+
+ @After
+ fun teardown() {
+ testApp.exit(wmHelper)
+ newTaskApp.exit(wmHelper)
+ imeApp.exit(wmHelper)
+ }
+} \ No newline at end of file
diff --git a/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/EnterDesktopWithDragExistingWindows.kt b/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/EnterDesktopWithDragExistingWindows.kt
new file mode 100644
index 000000000000..814478af67c1
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/EnterDesktopWithDragExistingWindows.kt
@@ -0,0 +1,74 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.scenarios
+
+import android.tools.NavBar
+import android.tools.Rotation
+import android.tools.flicker.rules.ChangeDisplayOrientationRule
+import com.android.server.wm.flicker.helpers.ImeAppHelper
+import com.android.server.wm.flicker.helpers.NewTasksAppHelper
+import com.android.window.flags.Flags
+import com.android.wm.shell.Utils
+import org.junit.After
+import org.junit.Assume
+import org.junit.Before
+import org.junit.Ignore
+import org.junit.Rule
+import org.junit.Test
+
+@Ignore("Test Base Class")
+abstract class EnterDesktopWithDragExistingWindows
+constructor(
+ val rotation: Rotation = Rotation.ROTATION_0,
+ isResizeable: Boolean = true,
+ isLandscapeApp: Boolean = true,
+) : DesktopScenarioCustomAppTestBase(isResizeable, isLandscapeApp) {
+
+ @Rule
+ @JvmField
+ val testSetupRule = Utils.testSetupRule(NavBar.MODE_GESTURAL, rotation)
+ private val imeApp = ImeAppHelper(instrumentation)
+ private val newTaskApp = NewTasksAppHelper(instrumentation)
+
+ @Before
+ fun setup() {
+ Assume.assumeTrue(Flags.enableDesktopWindowingMode() && tapl.isTablet)
+ tapl.setEnableRotation(true)
+ tapl.setExpectedRotation(rotation.value)
+ ChangeDisplayOrientationRule.setRotation(rotation)
+ tapl.enableTransientTaskbar(false)
+
+ testApp.enterDesktopMode(wmHelper, device)
+ imeApp.launchViaIntent(wmHelper)
+ newTaskApp.launchViaIntent(wmHelper)
+ testApp.launchViaIntent(wmHelper)
+ testApp.exitDesktopWithDragToTopDragZone(wmHelper, device)
+ }
+
+ @Test
+ open fun reenterDesktopWithDrag() {
+ // By default this method uses drag to desktop
+ testApp.enterDesktopMode(wmHelper, device)
+ }
+
+ @After
+ fun teardown() {
+ testApp.exit(wmHelper)
+ newTaskApp.exit(wmHelper)
+ imeApp.exit(wmHelper)
+ }
+} \ No newline at end of file
diff --git a/libs/WindowManager/Shell/tests/e2e/splitscreen/scenarios/src/com/android/wm/shell/scenarios/CopyContentInSplit.kt b/libs/WindowManager/Shell/tests/e2e/splitscreen/scenarios/src/com/android/wm/shell/scenarios/CopyContentInSplit.kt
index ba4654260864..31d89f92f744 100644
--- a/libs/WindowManager/Shell/tests/e2e/splitscreen/scenarios/src/com/android/wm/shell/scenarios/CopyContentInSplit.kt
+++ b/libs/WindowManager/Shell/tests/e2e/splitscreen/scenarios/src/com/android/wm/shell/scenarios/CopyContentInSplit.kt
@@ -24,6 +24,7 @@ import androidx.test.platform.app.InstrumentationRegistry
import androidx.test.uiautomator.UiDevice
import com.android.launcher3.tapl.LauncherInstrumentation
import com.android.wm.shell.Utils
+import com.android.wm.shell.flicker.utils.RecentTasksUtils
import com.android.wm.shell.flicker.utils.SplitScreenUtils
import org.junit.After
import org.junit.Before
@@ -61,7 +62,6 @@ constructor(val rotation: Rotation = Rotation.ROTATION_0) {
@After
fun teardown() {
- primaryApp.exit(wmHelper)
- secondaryApp.exit(wmHelper)
+ RecentTasksUtils.clearAllVisibleRecentTasks(instrumentation)
}
}
diff --git a/libs/WindowManager/Shell/tests/e2e/splitscreen/scenarios/src/com/android/wm/shell/scenarios/DismissSplitScreenByDivider.kt b/libs/WindowManager/Shell/tests/e2e/splitscreen/scenarios/src/com/android/wm/shell/scenarios/DismissSplitScreenByDivider.kt
index d774a31220da..1af6cac39085 100644
--- a/libs/WindowManager/Shell/tests/e2e/splitscreen/scenarios/src/com/android/wm/shell/scenarios/DismissSplitScreenByDivider.kt
+++ b/libs/WindowManager/Shell/tests/e2e/splitscreen/scenarios/src/com/android/wm/shell/scenarios/DismissSplitScreenByDivider.kt
@@ -24,6 +24,7 @@ import androidx.test.platform.app.InstrumentationRegistry
import androidx.test.uiautomator.UiDevice
import com.android.launcher3.tapl.LauncherInstrumentation
import com.android.wm.shell.Utils
+import com.android.wm.shell.flicker.utils.RecentTasksUtils
import com.android.wm.shell.flicker.utils.SplitScreenUtils
import org.junit.After
import org.junit.Before
@@ -74,7 +75,6 @@ constructor(val rotation: Rotation = Rotation.ROTATION_0) {
@After
fun teardown() {
- primaryApp.exit(wmHelper)
- secondaryApp.exit(wmHelper)
+ RecentTasksUtils.clearAllVisibleRecentTasks(instrumentation)
}
}
diff --git a/libs/WindowManager/Shell/tests/e2e/splitscreen/scenarios/src/com/android/wm/shell/scenarios/DismissSplitScreenByGoHome.kt b/libs/WindowManager/Shell/tests/e2e/splitscreen/scenarios/src/com/android/wm/shell/scenarios/DismissSplitScreenByGoHome.kt
index 5aa161911a9a..8ad8c7bd7a7f 100644
--- a/libs/WindowManager/Shell/tests/e2e/splitscreen/scenarios/src/com/android/wm/shell/scenarios/DismissSplitScreenByGoHome.kt
+++ b/libs/WindowManager/Shell/tests/e2e/splitscreen/scenarios/src/com/android/wm/shell/scenarios/DismissSplitScreenByGoHome.kt
@@ -24,6 +24,7 @@ import androidx.test.platform.app.InstrumentationRegistry
import androidx.test.uiautomator.UiDevice
import com.android.launcher3.tapl.LauncherInstrumentation
import com.android.wm.shell.Utils
+import com.android.wm.shell.flicker.utils.RecentTasksUtils
import com.android.wm.shell.flicker.utils.SplitScreenUtils
import org.junit.After
import org.junit.Before
@@ -60,7 +61,6 @@ constructor(val rotation: Rotation = Rotation.ROTATION_0) {
@After
fun teardown() {
- primaryApp.exit(wmHelper)
- secondaryApp.exit(wmHelper)
+ RecentTasksUtils.clearAllVisibleRecentTasks(instrumentation)
}
}
diff --git a/libs/WindowManager/Shell/tests/e2e/splitscreen/scenarios/src/com/android/wm/shell/scenarios/DragDividerToResize.kt b/libs/WindowManager/Shell/tests/e2e/splitscreen/scenarios/src/com/android/wm/shell/scenarios/DragDividerToResize.kt
index 668f3678bb38..da0ace472153 100644
--- a/libs/WindowManager/Shell/tests/e2e/splitscreen/scenarios/src/com/android/wm/shell/scenarios/DragDividerToResize.kt
+++ b/libs/WindowManager/Shell/tests/e2e/splitscreen/scenarios/src/com/android/wm/shell/scenarios/DragDividerToResize.kt
@@ -24,6 +24,7 @@ import androidx.test.platform.app.InstrumentationRegistry
import androidx.test.uiautomator.UiDevice
import com.android.launcher3.tapl.LauncherInstrumentation
import com.android.wm.shell.Utils
+import com.android.wm.shell.flicker.utils.RecentTasksUtils
import com.android.wm.shell.flicker.utils.SplitScreenUtils
import org.junit.After
import org.junit.Before
@@ -61,7 +62,6 @@ constructor(val rotation: Rotation = Rotation.ROTATION_0) {
@After
fun teardown() {
- primaryApp.exit(wmHelper)
- secondaryApp.exit(wmHelper)
+ RecentTasksUtils.clearAllVisibleRecentTasks(instrumentation)
}
}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/utils/RecentTasksUtils.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/utils/RecentTasksUtils.kt
new file mode 100644
index 000000000000..aa262f9dfd6a
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/utils/RecentTasksUtils.kt
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.flicker.utils
+
+import android.app.Instrumentation
+
+object RecentTasksUtils {
+ fun clearAllVisibleRecentTasks(instrumentation: Instrumentation) {
+ instrumentation.uiAutomation.executeShellCommand(
+ "dumpsys activity service SystemUIService WMShell recents clearAll"
+ )
+ }
+} \ No newline at end of file
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksControllerTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksControllerTest.kt
index 95ed8b4d4511..f549b0563827 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
@@ -601,7 +601,32 @@ class DesktopTasksControllerTest : ShellTestCase() {
}
@Test
+ @EnableFlags(
+ Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY,
+ Flags.FLAG_ENABLE_PER_DISPLAY_DESKTOP_WALLPAPER_ACTIVITY,
+ )
+ fun showDesktopApps_onSecondaryDisplay_desktopWallpaperEnabled_perDisplayWallpaperEnabled_shouldShowWallpaper() {
+ val homeTask = setUpHomeTask(SECOND_DISPLAY)
+ val task1 = setUpFreeformTask(SECOND_DISPLAY)
+ val task2 = setUpFreeformTask(SECOND_DISPLAY)
+ markTaskHidden(task1)
+ markTaskHidden(task2)
+
+ controller.showDesktopApps(SECOND_DISPLAY, RemoteTransition(TestRemoteTransition()))
+
+ val wct =
+ getLatestWct(type = TRANSIT_TO_FRONT, handlerClass = OneShotRemoteHandler::class.java)
+ assertThat(wct.hierarchyOps).hasSize(4)
+ // Expect order to be from bottom: home, wallpaperIntent, task1, task2
+ wct.assertReorderAt(index = 0, homeTask)
+ wct.assertPendingIntentAt(index = 1, desktopWallpaperIntent)
+ wct.assertReorderAt(index = 2, task1)
+ wct.assertReorderAt(index = 3, task2)
+ }
+
+ @Test
@EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY)
+ @DisableFlags(Flags.FLAG_ENABLE_PER_DISPLAY_DESKTOP_WALLPAPER_ACTIVITY)
fun showDesktopApps_onSecondaryDisplay_desktopWallpaperEnabled_shouldNotShowWallpaper() {
val homeTask = setUpHomeTask(SECOND_DISPLAY)
val task1 = setUpFreeformTask(SECOND_DISPLAY)
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip2/phone/PipSchedulerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip2/phone/PipSchedulerTest.java
index c42f6c35bcb0..aef44a40fa0f 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip2/phone/PipSchedulerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip2/phone/PipSchedulerTest.java
@@ -16,14 +16,10 @@
package com.android.wm.shell.pip2.phone;
-import static com.android.wm.shell.transition.Transitions.TRANSIT_EXIT_PIP;
-import static com.android.wm.shell.transition.Transitions.TRANSIT_REMOVE_PIP;
-
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
-import static org.mockito.ArgumentMatchers.isNull;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.when;
import static org.mockito.kotlin.MatchersKt.eq;
@@ -124,12 +120,13 @@ public class PipSchedulerTest {
.setCallsite("PipSchedulerTest")
.build();
when(mMockPipTransitionState.getPinnedTaskLeash()).thenReturn(testLeash);
+ // PiP is in a valid state by default.
+ when(mMockPipTransitionState.isInPip()).thenReturn(true);
}
@Test
public void scheduleExitPipViaExpand_nullTaskToken_noop() {
setNullPipTaskToken();
- when(mMockPipTransitionState.isInPip()).thenReturn(true);
mPipScheduler.scheduleExitPipViaExpand();
@@ -137,14 +134,12 @@ public class PipSchedulerTest {
assertNotNull(mRunnableArgumentCaptor.getValue());
mRunnableArgumentCaptor.getValue().run();
- verify(mMockPipTransitionController, never())
- .startExitTransition(eq(TRANSIT_EXIT_PIP), any(), isNull());
+ verify(mMockPipTransitionController, never()).startExpandTransition(any());
}
@Test
public void scheduleExitPipViaExpand_exitTransitionCalled() {
setMockPipTaskToken();
- when(mMockPipTransitionState.isInPip()).thenReturn(true);
mPipScheduler.scheduleExitPipViaExpand();
@@ -152,23 +147,21 @@ public class PipSchedulerTest {
assertNotNull(mRunnableArgumentCaptor.getValue());
mRunnableArgumentCaptor.getValue().run();
- verify(mMockPipTransitionController, times(1))
- .startExitTransition(eq(TRANSIT_EXIT_PIP), any(), isNull());
+ verify(mMockPipTransitionController, times(1)).startExpandTransition(any());
}
@Test
public void removePipAfterAnimation() {
setMockPipTaskToken();
- when(mMockPipTransitionState.isInPip()).thenReturn(true);
- mPipScheduler.scheduleRemovePip();
+ mPipScheduler.scheduleRemovePip(true /* withFadeout */);
verify(mMockMainExecutor, times(1)).execute(mRunnableArgumentCaptor.capture());
assertNotNull(mRunnableArgumentCaptor.getValue());
mRunnableArgumentCaptor.getValue().run();
verify(mMockPipTransitionController, times(1))
- .startExitTransition(eq(TRANSIT_REMOVE_PIP), any(), isNull());
+ .startRemoveTransition(true /* withFadeout */);
}
@Test
@@ -192,7 +185,6 @@ public class PipSchedulerTest {
@Test
public void scheduleAnimateResizePip_boundsConfig_setsConfigAtEnd() {
setMockPipTaskToken();
- when(mMockPipTransitionState.isInPip()).thenReturn(true);
mPipScheduler.scheduleAnimateResizePip(TEST_BOUNDS, true);
@@ -233,7 +225,6 @@ public class PipSchedulerTest {
@Test
public void scheduleAnimateResizePip_resizeTransition() {
setMockPipTaskToken();
- when(mMockPipTransitionState.isInPip()).thenReturn(true);
mPipScheduler.scheduleAnimateResizePip(TEST_BOUNDS, true, TEST_RESIZE_DURATION);
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip2/phone/PipTransitionStateTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip2/phone/PipTransitionStateTest.java
index 571ae93e1aec..fa9b5903bc3c 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip2/phone/PipTransitionStateTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip2/phone/PipTransitionStateTest.java
@@ -110,4 +110,22 @@ public class PipTransitionStateTest extends ShellTestCase {
mPipTransitionState.setState(PipTransitionState.ENTERED_PIP);
mPipTransitionState.removePipTransitionStateChangedListener(mStateChangedListener);
}
+
+ @Test
+ public void testBoundsChangeState_notInPip() {
+ Bundle extra = new Bundle();
+ extra.putParcelable(EXTRA_ENTRY_KEY, mEmptyParcelable);
+
+ mPipTransitionState.setState(PipTransitionState.UNDEFINED);
+ mPipTransitionState.setState(PipTransitionState.SCHEDULED_BOUNDS_CHANGE, extra);
+ Assert.assertEquals(PipTransitionState.UNDEFINED, mPipTransitionState.getState());
+
+ mPipTransitionState.setState(PipTransitionState.ENTERING_PIP, extra);
+ mPipTransitionState.setState(PipTransitionState.SCHEDULED_BOUNDS_CHANGE, extra);
+ Assert.assertEquals(PipTransitionState.ENTERING_PIP, mPipTransitionState.getState());
+
+ mPipTransitionState.setState(PipTransitionState.EXITING_PIP);
+ mPipTransitionState.setState(PipTransitionState.SCHEDULED_BOUNDS_CHANGE, extra);
+ Assert.assertEquals(PipTransitionState.EXITING_PIP, mPipTransitionState.getState());
+ }
}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/recents/RecentsTransitionHandlerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/recents/RecentsTransitionHandlerTest.java
index ab43119b14c0..894d238b7e15 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/recents/RecentsTransitionHandlerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/recents/RecentsTransitionHandlerTest.java
@@ -169,7 +169,7 @@ public class RecentsTransitionHandlerTest extends ShellTestCase {
final IResultReceiver finishCallback = mock(IResultReceiver.class);
final IBinder transition = startRecentsTransition(/* synthetic= */ true, runner);
- verify(runner).onAnimationStart(any(), any(), any(), any(), any(), any(), any());
+ verify(runner).onAnimationStart(any(), any(), any(), any(), any(), any());
// Finish and verify no transition remains and that the provided finish callback is called
mRecentsTransitionHandler.findController(transition).finish(true /* toHome */,
@@ -184,7 +184,7 @@ public class RecentsTransitionHandlerTest extends ShellTestCase {
final IRecentsAnimationRunner runner = mock(IRecentsAnimationRunner.class);
final IBinder transition = startRecentsTransition(/* synthetic= */ true, runner);
- verify(runner).onAnimationStart(any(), any(), any(), any(), any(), any(), any());
+ verify(runner).onAnimationStart(any(), any(), any(), any(), any(), any());
mRecentsTransitionHandler.findController(transition).cancel("test");
mMainExecutor.flushAll();
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModelTests.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModelTests.kt
index 79e9b9c8cd77..ffe8e7135513 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModelTests.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModelTests.kt
@@ -59,12 +59,11 @@ import com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn
import com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession
import com.android.window.flags.Flags
import com.android.wm.shell.R
+import com.android.wm.shell.desktopmode.common.ToggleTaskSizeInteraction
import com.android.wm.shell.desktopmode.DesktopImmersiveController
import com.android.wm.shell.desktopmode.DesktopModeEventLogger.Companion.InputMethod
import com.android.wm.shell.desktopmode.DesktopModeEventLogger.Companion.ResizeTrigger
import com.android.wm.shell.desktopmode.DesktopTasksController.SnapPosition
-import com.android.wm.shell.desktopmode.common.ToggleTaskSizeInteraction
-import com.android.wm.shell.recents.RecentsTransitionStateListener
import com.android.wm.shell.shared.desktopmode.DesktopModeStatus
import com.android.wm.shell.shared.desktopmode.DesktopModeTransitionSource
import com.android.wm.shell.splitscreen.SplitScreenController
@@ -540,8 +539,7 @@ class DesktopModeWindowDecorViewModelTests : DesktopModeWindowDecorViewModelTest
onLeftSnapClickListenerCaptor.value.invoke()
verify(mockDesktopTasksController, never())
- .snapToHalfScreen(
- eq(decor.mTaskInfo), any(), eq(currentBounds), eq(SnapPosition.LEFT),
+ .snapToHalfScreen(eq(decor.mTaskInfo), any(), eq(currentBounds), eq(SnapPosition.LEFT),
eq(ResizeTrigger.MAXIMIZE_BUTTON),
eq(InputMethod.UNKNOWN_INPUT_METHOD),
eq(decor),
@@ -618,12 +616,11 @@ class DesktopModeWindowDecorViewModelTests : DesktopModeWindowDecorViewModelTest
onRightSnapClickListenerCaptor.value.invoke()
verify(mockDesktopTasksController, never())
- .snapToHalfScreen(
- eq(decor.mTaskInfo), any(), eq(currentBounds), eq(SnapPosition.RIGHT),
+ .snapToHalfScreen(eq(decor.mTaskInfo), any(), eq(currentBounds), eq(SnapPosition.RIGHT),
eq(ResizeTrigger.MAXIMIZE_BUTTON),
eq(InputMethod.UNKNOWN_INPUT_METHOD),
eq(decor),
- )
+ )
}
@Test
@@ -1226,49 +1223,6 @@ class DesktopModeWindowDecorViewModelTests : DesktopModeWindowDecorViewModelTest
verify(task2, never()).onExclusionRegionChanged(newRegion)
}
- @Test
- @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_RECENTS_TRANSITIONS_CORNERS_BUGFIX)
- fun testRecentsTransitionStateListener_requestedState_setsTransitionRunning() {
- val task = createTask(windowingMode = WINDOWING_MODE_FREEFORM)
- val decoration = setUpMockDecorationForTask(task)
- onTaskOpening(task, SurfaceControl())
-
- desktopModeRecentsTransitionStateListener.onTransitionStateChanged(
- RecentsTransitionStateListener.TRANSITION_STATE_REQUESTED)
-
- verify(decoration).setIsRecentsTransitionRunning(true)
- }
-
- @Test
- @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_RECENTS_TRANSITIONS_CORNERS_BUGFIX)
- fun testRecentsTransitionStateListener_nonRunningState_setsTransitionNotRunning() {
- val task = createTask(windowingMode = WINDOWING_MODE_FREEFORM)
- val decoration = setUpMockDecorationForTask(task)
- onTaskOpening(task, SurfaceControl())
- desktopModeRecentsTransitionStateListener.onTransitionStateChanged(
- RecentsTransitionStateListener.TRANSITION_STATE_REQUESTED)
-
- desktopModeRecentsTransitionStateListener.onTransitionStateChanged(
- RecentsTransitionStateListener.TRANSITION_STATE_NOT_RUNNING)
-
- verify(decoration).setIsRecentsTransitionRunning(false)
- }
-
- @Test
- @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_RECENTS_TRANSITIONS_CORNERS_BUGFIX)
- fun testRecentsTransitionStateListener_requestedAndAnimating_setsTransitionRunningOnce() {
- val task = createTask(windowingMode = WINDOWING_MODE_FREEFORM)
- val decoration = setUpMockDecorationForTask(task)
- onTaskOpening(task, SurfaceControl())
-
- desktopModeRecentsTransitionStateListener.onTransitionStateChanged(
- RecentsTransitionStateListener.TRANSITION_STATE_REQUESTED)
- desktopModeRecentsTransitionStateListener.onTransitionStateChanged(
- RecentsTransitionStateListener.TRANSITION_STATE_ANIMATING)
-
- verify(decoration, times(1)).setIsRecentsTransitionRunning(true)
- }
-
private fun createOpenTaskDecoration(
@WindowingMode windowingMode: Int,
taskSurface: SurfaceControl = SurfaceControl(),
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModelTestsBase.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModelTestsBase.kt
index 8af8285d031c..b5e8cebc1277 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModelTestsBase.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModelTestsBase.kt
@@ -40,7 +40,6 @@ import android.view.SurfaceControl
import android.view.WindowInsets.Type.statusBars
import com.android.dx.mockito.inline.extended.StaticMockitoSession
import com.android.internal.jank.InteractionJankMonitor
-import com.android.window.flags.Flags
import com.android.wm.shell.RootTaskDisplayAreaOrganizer
import com.android.wm.shell.ShellTaskOrganizer
import com.android.wm.shell.ShellTestCase
@@ -66,8 +65,6 @@ import com.android.wm.shell.desktopmode.WindowDecorCaptionHandleRepository
import com.android.wm.shell.desktopmode.education.AppHandleEducationController
import com.android.wm.shell.desktopmode.education.AppToWebEducationController
import com.android.wm.shell.freeform.FreeformTaskTransitionStarter
-import com.android.wm.shell.recents.RecentsTransitionHandler
-import com.android.wm.shell.recents.RecentsTransitionStateListener
import com.android.wm.shell.splitscreen.SplitScreenController
import com.android.wm.shell.sysui.ShellCommandHandler
import com.android.wm.shell.sysui.ShellController
@@ -154,7 +151,6 @@ open class DesktopModeWindowDecorViewModelTestsBase : ShellTestCase() {
protected val mockFocusTransitionObserver = mock<FocusTransitionObserver>()
protected val mockCaptionHandleRepository = mock<WindowDecorCaptionHandleRepository>()
protected val mockDesktopRepository: DesktopRepository = mock<DesktopRepository>()
- protected val mockRecentsTransitionHandler = mock<RecentsTransitionHandler>()
protected val motionEvent = mock<MotionEvent>()
val displayLayout = mock<DisplayLayout>()
protected lateinit var spyContext: TestableContext
@@ -168,7 +164,6 @@ open class DesktopModeWindowDecorViewModelTestsBase : ShellTestCase() {
protected lateinit var mockitoSession: StaticMockitoSession
protected lateinit var shellInit: ShellInit
internal lateinit var desktopModeOnInsetsChangedListener: DesktopModeOnInsetsChangedListener
- protected lateinit var desktopModeRecentsTransitionStateListener: RecentsTransitionStateListener
protected lateinit var displayChangingListener:
DisplayChangeController.OnDisplayChangingListener
internal lateinit var desktopModeOnKeyguardChangedListener: DesktopModeKeyguardChangeListener
@@ -224,8 +219,7 @@ open class DesktopModeWindowDecorViewModelTestsBase : ShellTestCase() {
mockFocusTransitionObserver,
desktopModeEventLogger,
mock<DesktopModeUiEventLogger>(),
- mock<WindowDecorTaskResourceLoader>(),
- mockRecentsTransitionHandler,
+ mock<WindowDecorTaskResourceLoader>()
)
desktopModeWindowDecorViewModel.setSplitScreenController(mockSplitScreenController)
whenever(mockDisplayController.getDisplayLayout(any())).thenReturn(mockDisplayLayout)
@@ -262,13 +256,6 @@ open class DesktopModeWindowDecorViewModelTestsBase : ShellTestCase() {
verify(displayInsetsController)
.addGlobalInsetsChangedListener(insetsChangedCaptor.capture())
desktopModeOnInsetsChangedListener = insetsChangedCaptor.firstValue
- val recentsTransitionStateListenerCaptor = argumentCaptor<RecentsTransitionStateListener>()
- if (Flags.enableDesktopRecentsTransitionsCornersBugfix()) {
- verify(mockRecentsTransitionHandler)
- .addTransitionStateListener(recentsTransitionStateListenerCaptor.capture())
- desktopModeRecentsTransitionStateListener =
- recentsTransitionStateListenerCaptor.firstValue
- }
val keyguardChangedCaptor =
argumentCaptor<DesktopModeKeyguardChangeListener>()
verify(mockShellController).addKeyguardChangeListener(keyguardChangedCaptor.capture())
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorationTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorationTests.java
index 9ea5fd6e1abe..6b02aeffd42a 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorationTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorationTests.java
@@ -169,7 +169,6 @@ public class DesktopModeWindowDecorationTests extends ShellTestCase {
private static final boolean DEFAULT_IS_KEYGUARD_VISIBLE_AND_OCCLUDED = false;
private static final boolean DEFAULT_IS_IN_FULL_IMMERSIVE_MODE = false;
private static final boolean DEFAULT_HAS_GLOBAL_FOCUS = true;
- private static final boolean DEFAULT_SHOULD_IGNORE_CORNER_RADIUS = false;
@Rule public final SetFlagsRule mSetFlagsRule = new SetFlagsRule(DEVICE_DEFAULT);
@@ -397,31 +396,6 @@ public class DesktopModeWindowDecorationTests extends ShellTestCase {
}
@Test
- public void updateRelayoutParams_shouldIgnoreCornerRadius_roundedCornersNotSet() {
- final ActivityManager.RunningTaskInfo taskInfo = createTaskInfo(/* visible= */ true);
- taskInfo.configuration.windowConfiguration.setWindowingMode(WINDOWING_MODE_FREEFORM);
- fillRoundedCornersResources(/* fillValue= */ 30);
- RelayoutParams relayoutParams = new RelayoutParams();
-
- DesktopModeWindowDecoration.updateRelayoutParams(
- relayoutParams,
- mTestableContext,
- taskInfo,
- mMockSplitScreenController,
- DEFAULT_APPLY_START_TRANSACTION_ON_DRAW,
- DEFAULT_SHOULD_SET_TASK_POSITIONING_AND_CROP,
- DEFAULT_IS_STATUSBAR_VISIBLE,
- DEFAULT_IS_KEYGUARD_VISIBLE_AND_OCCLUDED,
- DEFAULT_IS_IN_FULL_IMMERSIVE_MODE,
- new InsetsState(),
- DEFAULT_HAS_GLOBAL_FOCUS,
- mExclusionRegion,
- /* shouldIgnoreCornerRadius= */ true);
-
- assertThat(relayoutParams.mCornerRadius).isEqualTo(INVALID_CORNER_RADIUS);
- }
-
- @Test
@EnableFlags(Flags.FLAG_ENABLE_APP_HEADER_WITH_TASK_DENSITY)
public void updateRelayoutParams_appHeader_usesTaskDensity() {
final int systemDensity = mTestableContext.getOrCreateTestableResources().getResources()
@@ -660,8 +634,7 @@ public class DesktopModeWindowDecorationTests extends ShellTestCase {
/* inFullImmersiveMode */ true,
insetsState,
DEFAULT_HAS_GLOBAL_FOCUS,
- mExclusionRegion,
- DEFAULT_SHOULD_IGNORE_CORNER_RADIUS);
+ mExclusionRegion);
// Takes status bar inset as padding, ignores caption bar inset.
assertThat(relayoutParams.mCaptionTopPadding).isEqualTo(50);
@@ -686,8 +659,7 @@ public class DesktopModeWindowDecorationTests extends ShellTestCase {
/* inFullImmersiveMode */ true,
new InsetsState(),
DEFAULT_HAS_GLOBAL_FOCUS,
- mExclusionRegion,
- DEFAULT_SHOULD_IGNORE_CORNER_RADIUS);
+ mExclusionRegion);
assertThat(relayoutParams.mIsInsetSource).isFalse();
}
@@ -711,8 +683,7 @@ public class DesktopModeWindowDecorationTests extends ShellTestCase {
DEFAULT_IS_IN_FULL_IMMERSIVE_MODE,
new InsetsState(),
DEFAULT_HAS_GLOBAL_FOCUS,
- mExclusionRegion,
- DEFAULT_SHOULD_IGNORE_CORNER_RADIUS);
+ mExclusionRegion);
// Header is always shown because it's assumed the status bar is always visible.
assertThat(relayoutParams.mIsCaptionVisible).isTrue();
@@ -736,8 +707,7 @@ public class DesktopModeWindowDecorationTests extends ShellTestCase {
DEFAULT_IS_IN_FULL_IMMERSIVE_MODE,
new InsetsState(),
DEFAULT_HAS_GLOBAL_FOCUS,
- mExclusionRegion,
- DEFAULT_SHOULD_IGNORE_CORNER_RADIUS);
+ mExclusionRegion);
assertThat(relayoutParams.mIsCaptionVisible).isTrue();
}
@@ -760,8 +730,7 @@ public class DesktopModeWindowDecorationTests extends ShellTestCase {
DEFAULT_IS_IN_FULL_IMMERSIVE_MODE,
new InsetsState(),
DEFAULT_HAS_GLOBAL_FOCUS,
- mExclusionRegion,
- DEFAULT_SHOULD_IGNORE_CORNER_RADIUS);
+ mExclusionRegion);
assertThat(relayoutParams.mIsCaptionVisible).isFalse();
}
@@ -784,8 +753,7 @@ public class DesktopModeWindowDecorationTests extends ShellTestCase {
DEFAULT_IS_IN_FULL_IMMERSIVE_MODE,
new InsetsState(),
DEFAULT_HAS_GLOBAL_FOCUS,
- mExclusionRegion,
- DEFAULT_SHOULD_IGNORE_CORNER_RADIUS);
+ mExclusionRegion);
assertThat(relayoutParams.mIsCaptionVisible).isFalse();
}
@@ -809,8 +777,7 @@ public class DesktopModeWindowDecorationTests extends ShellTestCase {
/* inFullImmersiveMode */ true,
new InsetsState(),
DEFAULT_HAS_GLOBAL_FOCUS,
- mExclusionRegion,
- DEFAULT_SHOULD_IGNORE_CORNER_RADIUS);
+ mExclusionRegion);
assertThat(relayoutParams.mIsCaptionVisible).isTrue();
@@ -826,8 +793,7 @@ public class DesktopModeWindowDecorationTests extends ShellTestCase {
/* inFullImmersiveMode */ true,
new InsetsState(),
DEFAULT_HAS_GLOBAL_FOCUS,
- mExclusionRegion,
- DEFAULT_SHOULD_IGNORE_CORNER_RADIUS);
+ mExclusionRegion);
assertThat(relayoutParams.mIsCaptionVisible).isFalse();
}
@@ -851,8 +817,7 @@ public class DesktopModeWindowDecorationTests extends ShellTestCase {
/* inFullImmersiveMode */ true,
new InsetsState(),
DEFAULT_HAS_GLOBAL_FOCUS,
- mExclusionRegion,
- DEFAULT_SHOULD_IGNORE_CORNER_RADIUS);
+ mExclusionRegion);
assertThat(relayoutParams.mIsCaptionVisible).isFalse();
}
@@ -1515,8 +1480,7 @@ public class DesktopModeWindowDecorationTests extends ShellTestCase {
DEFAULT_IS_IN_FULL_IMMERSIVE_MODE,
new InsetsState(),
DEFAULT_HAS_GLOBAL_FOCUS,
- mExclusionRegion,
- DEFAULT_SHOULD_IGNORE_CORNER_RADIUS);
+ mExclusionRegion);
}
private DesktopModeWindowDecoration createWindowDecoration(
diff --git a/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
new file mode 100644
index 000000000000..f179cac32244
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/MultiDisplayVeiledResizeTaskPositionerTest.kt
@@ -0,0 +1,611 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.wm.shell.windowdecor
+
+import android.app.ActivityManager
+import android.app.WindowConfiguration
+import android.content.Context
+import android.content.res.Resources
+import android.graphics.Point
+import android.graphics.Rect
+import android.os.Handler
+import android.os.IBinder
+import android.os.Looper
+import android.testing.AndroidTestingRunner
+import android.view.Display
+import android.view.Surface.ROTATION_0
+import android.view.Surface.ROTATION_270
+import android.view.Surface.ROTATION_90
+import android.view.SurfaceControl
+import android.view.SurfaceControl.Transaction
+import android.view.WindowManager.TRANSIT_CHANGE
+import android.window.TransitionInfo
+import android.window.WindowContainerToken
+import androidx.test.filters.SmallTest
+import androidx.test.internal.runner.junit4.statement.UiThreadStatement.runOnUiThread
+import com.android.internal.jank.InteractionJankMonitor
+import com.android.wm.shell.ShellTaskOrganizer
+import com.android.wm.shell.ShellTestCase
+import com.android.wm.shell.common.DisplayController
+import com.android.wm.shell.common.DisplayLayout
+import com.android.wm.shell.transition.Transitions
+import com.android.wm.shell.transition.Transitions.TransitionFinishCallback
+import com.android.wm.shell.windowdecor.DragPositioningCallback.CTRL_TYPE_BOTTOM
+import com.android.wm.shell.windowdecor.DragPositioningCallback.CTRL_TYPE_RIGHT
+import com.android.wm.shell.windowdecor.DragPositioningCallback.CTRL_TYPE_TOP
+import com.android.wm.shell.windowdecor.DragPositioningCallback.CTRL_TYPE_UNDEFINED
+import java.util.function.Supplier
+import junit.framework.Assert
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.Mock
+import org.mockito.Mockito.any
+import org.mockito.Mockito.argThat
+import org.mockito.Mockito.eq
+import org.mockito.Mockito.mock
+import org.mockito.Mockito.never
+import org.mockito.Mockito.times
+import org.mockito.Mockito.verify
+import org.mockito.Mockito.`when` as whenever
+import org.mockito.Mockito.`when`
+import org.mockito.MockitoAnnotations
+
+/**
+ * Tests for [MultiDisplayVeiledResizeTaskPositioner].
+ *
+ * Build/Install/Run: atest WMShellUnitTests:MultiDisplayVeiledResizeTaskPositionerTest
+ */
+@SmallTest
+@RunWith(AndroidTestingRunner::class)
+class MultiDisplayVeiledResizeTaskPositionerTest : ShellTestCase() {
+
+ @Mock private lateinit var mockShellTaskOrganizer: ShellTaskOrganizer
+ @Mock private lateinit var mockDesktopWindowDecoration: DesktopModeWindowDecoration
+ @Mock
+ private lateinit var mockDragEventListener: DragPositioningCallbackUtility.DragEventListener
+
+ @Mock private lateinit var taskToken: WindowContainerToken
+ @Mock private lateinit var taskBinder: IBinder
+
+ @Mock private lateinit var mockDisplayController: DisplayController
+ @Mock private lateinit var mockDisplayLayout: DisplayLayout
+ @Mock private lateinit var mockDisplay: Display
+ @Mock private lateinit var mockTransactionFactory: Supplier<SurfaceControl.Transaction>
+ @Mock private lateinit var mockTransaction: SurfaceControl.Transaction
+ @Mock private lateinit var mockTransitionBinder: IBinder
+ @Mock private lateinit var mockTransitionInfo: TransitionInfo
+ @Mock private lateinit var mockFinishCallback: TransitionFinishCallback
+ @Mock private lateinit var mockTransitions: Transitions
+ @Mock private lateinit var mockContext: Context
+ @Mock private lateinit var mockResources: Resources
+ @Mock private lateinit var mockInteractionJankMonitor: InteractionJankMonitor
+ private val mainHandler = Handler(Looper.getMainLooper())
+
+ private lateinit var taskPositioner: MultiDisplayVeiledResizeTaskPositioner
+
+ @Before
+ fun setUp() {
+ MockitoAnnotations.initMocks(this)
+
+ mockDesktopWindowDecoration.mDisplay = mockDisplay
+ mockDesktopWindowDecoration.mDecorWindowContext = mockContext
+ whenever(mockContext.getResources()).thenReturn(mockResources)
+ whenever(taskToken.asBinder()).thenReturn(taskBinder)
+ whenever(mockDisplayController.getDisplayLayout(DISPLAY_ID)).thenReturn(mockDisplayLayout)
+ whenever(mockDisplayLayout.densityDpi()).thenReturn(DENSITY_DPI)
+ whenever(mockDisplayLayout.getStableBounds(any())).thenAnswer { i ->
+ if (
+ mockDesktopWindowDecoration.mTaskInfo.configuration.windowConfiguration
+ .displayRotation == ROTATION_90 ||
+ mockDesktopWindowDecoration.mTaskInfo.configuration.windowConfiguration
+ .displayRotation == ROTATION_270
+ ) {
+ (i.arguments.first() as Rect).set(STABLE_BOUNDS_LANDSCAPE)
+ } else {
+ (i.arguments.first() as Rect).set(STABLE_BOUNDS_PORTRAIT)
+ }
+ }
+ `when`(mockTransactionFactory.get()).thenReturn(mockTransaction)
+ mockDesktopWindowDecoration.mTaskInfo =
+ ActivityManager.RunningTaskInfo().apply {
+ taskId = TASK_ID
+ token = taskToken
+ minWidth = MIN_WIDTH
+ minHeight = MIN_HEIGHT
+ defaultMinSize = DEFAULT_MIN
+ displayId = DISPLAY_ID
+ configuration.windowConfiguration.setBounds(STARTING_BOUNDS)
+ configuration.windowConfiguration.displayRotation = ROTATION_90
+ isResizeable = true
+ }
+ `when`(mockDesktopWindowDecoration.calculateValidDragArea()).thenReturn(VALID_DRAG_AREA)
+ mockDesktopWindowDecoration.mDisplay = mockDisplay
+ whenever(mockDisplay.displayId).thenAnswer { DISPLAY_ID }
+
+ taskPositioner =
+ MultiDisplayVeiledResizeTaskPositioner(
+ mockShellTaskOrganizer,
+ mockDesktopWindowDecoration,
+ mockDisplayController,
+ mockDragEventListener,
+ mockTransactionFactory,
+ mockTransitions,
+ mockInteractionJankMonitor,
+ mainHandler,
+ )
+ }
+
+ @Test
+ fun testDragResize_noMove_doesNotShowResizeVeil() = runOnUiThread {
+ taskPositioner.onDragPositioningStart(
+ CTRL_TYPE_TOP or CTRL_TYPE_RIGHT,
+ DISPLAY_ID,
+ STARTING_BOUNDS.left.toFloat(),
+ STARTING_BOUNDS.top.toFloat(),
+ )
+ verify(mockDesktopWindowDecoration, never()).showResizeVeil(STARTING_BOUNDS)
+
+ taskPositioner.onDragPositioningEnd(
+ DISPLAY_ID,
+ STARTING_BOUNDS.left.toFloat(),
+ STARTING_BOUNDS.top.toFloat(),
+ )
+
+ verify(mockTransitions, never())
+ .startTransition(
+ eq(TRANSIT_CHANGE),
+ argThat { wct ->
+ return@argThat wct.changes.any { (token, change) ->
+ token == taskBinder &&
+ (change.windowSetMask and WindowConfiguration.WINDOW_CONFIG_BOUNDS) !=
+ 0 &&
+ change.configuration.windowConfiguration.bounds == STARTING_BOUNDS
+ }
+ },
+ eq(taskPositioner),
+ )
+ verify(mockDesktopWindowDecoration, never()).hideResizeVeil()
+ }
+
+ @Test
+ fun testDragResize_movesTask_doesNotShowResizeVeil() = runOnUiThread {
+ taskPositioner.onDragPositioningStart(
+ CTRL_TYPE_UNDEFINED,
+ DISPLAY_ID,
+ STARTING_BOUNDS.left.toFloat(),
+ STARTING_BOUNDS.top.toFloat(),
+ )
+
+ taskPositioner.onDragPositioningMove(
+ DISPLAY_ID,
+ STARTING_BOUNDS.left.toFloat() + 60,
+ STARTING_BOUNDS.top.toFloat() + 100,
+ )
+ val rectAfterMove = Rect(STARTING_BOUNDS)
+ rectAfterMove.left += 60
+ rectAfterMove.right += 60
+ rectAfterMove.top += 100
+ rectAfterMove.bottom += 100
+ verify(mockTransaction)
+ .setPosition(any(), eq(rectAfterMove.left.toFloat()), eq(rectAfterMove.top.toFloat()))
+
+ val endBounds =
+ taskPositioner.onDragPositioningEnd(
+ DISPLAY_ID,
+ STARTING_BOUNDS.left.toFloat() + 70,
+ STARTING_BOUNDS.top.toFloat() + 20,
+ )
+ val rectAfterEnd = Rect(STARTING_BOUNDS)
+ rectAfterEnd.left += 70
+ rectAfterEnd.right += 70
+ rectAfterEnd.top += 20
+ rectAfterEnd.bottom += 20
+
+ verify(mockDesktopWindowDecoration, never()).showResizeVeil(any())
+ verify(mockDesktopWindowDecoration, never()).hideResizeVeil()
+ Assert.assertEquals(rectAfterEnd, endBounds)
+ }
+
+ @Test
+ fun testDragResize_resize_boundsUpdateOnEnd() = runOnUiThread {
+ taskPositioner.onDragPositioningStart(
+ CTRL_TYPE_RIGHT or CTRL_TYPE_TOP,
+ DISPLAY_ID,
+ STARTING_BOUNDS.right.toFloat(),
+ STARTING_BOUNDS.top.toFloat(),
+ )
+
+ taskPositioner.onDragPositioningMove(
+ DISPLAY_ID,
+ STARTING_BOUNDS.right.toFloat() + 10,
+ STARTING_BOUNDS.top.toFloat() + 10,
+ )
+
+ val rectAfterMove = Rect(STARTING_BOUNDS)
+ rectAfterMove.right += 10
+ rectAfterMove.top += 10
+ verify(mockDesktopWindowDecoration).showResizeVeil(rectAfterMove)
+ verify(mockShellTaskOrganizer, never())
+ .applyTransaction(
+ argThat { wct ->
+ return@argThat wct.changes.any { (token, change) ->
+ token == taskBinder &&
+ (change.windowSetMask and WindowConfiguration.WINDOW_CONFIG_BOUNDS) !=
+ 0 &&
+ change.configuration.windowConfiguration.bounds == rectAfterMove
+ }
+ }
+ )
+
+ taskPositioner.onDragPositioningEnd(
+ DISPLAY_ID,
+ STARTING_BOUNDS.right.toFloat() + 20,
+ STARTING_BOUNDS.top.toFloat() + 20,
+ )
+ val rectAfterEnd = Rect(rectAfterMove)
+ rectAfterEnd.right += 10
+ rectAfterEnd.top += 10
+ verify(mockDesktopWindowDecoration).updateResizeVeil(any())
+ verify(mockTransitions)
+ .startTransition(
+ eq(TRANSIT_CHANGE),
+ argThat { wct ->
+ return@argThat wct.changes.any { (token, change) ->
+ token == taskBinder &&
+ (change.windowSetMask and WindowConfiguration.WINDOW_CONFIG_BOUNDS) !=
+ 0 &&
+ change.configuration.windowConfiguration.bounds == rectAfterEnd
+ }
+ },
+ eq(taskPositioner),
+ )
+ }
+
+ @Test
+ fun testDragResize_noEffectiveMove_skipsTransactionOnEnd() = runOnUiThread {
+ taskPositioner.onDragPositioningStart(
+ DISPLAY_ID,
+ CTRL_TYPE_TOP or CTRL_TYPE_RIGHT,
+ STARTING_BOUNDS.left.toFloat(),
+ STARTING_BOUNDS.top.toFloat(),
+ )
+
+ taskPositioner.onDragPositioningMove(
+ DISPLAY_ID,
+ STARTING_BOUNDS.left.toFloat(),
+ STARTING_BOUNDS.top.toFloat(),
+ )
+
+ taskPositioner.onDragPositioningEnd(
+ DISPLAY_ID,
+ STARTING_BOUNDS.left.toFloat() + 10,
+ STARTING_BOUNDS.top.toFloat() + 10,
+ )
+
+ verify(mockTransitions, never())
+ .startTransition(
+ eq(TRANSIT_CHANGE),
+ argThat { wct ->
+ return@argThat wct.changes.any { (token, change) ->
+ token == taskBinder &&
+ (change.windowSetMask and WindowConfiguration.WINDOW_CONFIG_BOUNDS) !=
+ 0 &&
+ change.configuration.windowConfiguration.bounds == STARTING_BOUNDS
+ }
+ },
+ eq(taskPositioner),
+ )
+
+ verify(mockShellTaskOrganizer, never())
+ .applyTransaction(
+ argThat { wct ->
+ return@argThat wct.changes.any { (token, change) ->
+ token == taskBinder &&
+ ((change.windowSetMask and WindowConfiguration.WINDOW_CONFIG_BOUNDS) !=
+ 0)
+ }
+ }
+ )
+ }
+
+ @Test
+ fun testDragResize_drag_setBoundsNotRunIfDragEndsInDisallowedEndArea() = runOnUiThread {
+ taskPositioner.onDragPositioningStart(
+ CTRL_TYPE_UNDEFINED, // drag
+ DISPLAY_ID,
+ STARTING_BOUNDS.left.toFloat(),
+ STARTING_BOUNDS.top.toFloat(),
+ )
+
+ val newX = STARTING_BOUNDS.left.toFloat() + 5
+ val newY = DISALLOWED_AREA_FOR_END_BOUNDS_HEIGHT.toFloat() - 1
+ taskPositioner.onDragPositioningMove(DISPLAY_ID, newX, newY)
+
+ taskPositioner.onDragPositioningEnd(DISPLAY_ID, newX, newY)
+
+ verify(mockShellTaskOrganizer, never())
+ .applyTransaction(
+ argThat { wct ->
+ return@argThat wct.changes.any { (token, change) ->
+ token == taskBinder &&
+ ((change.windowSetMask and WindowConfiguration.WINDOW_CONFIG_BOUNDS) !=
+ 0)
+ }
+ }
+ )
+ }
+
+ @Test
+ fun testDragResize_resize_resizingTaskReorderedToTopWhenNotFocused() = runOnUiThread {
+ mockDesktopWindowDecoration.mHasGlobalFocus = false
+ taskPositioner.onDragPositioningStart(
+ CTRL_TYPE_RIGHT, // Resize right
+ DISPLAY_ID,
+ STARTING_BOUNDS.left.toFloat(),
+ STARTING_BOUNDS.top.toFloat(),
+ )
+
+ // Verify task is reordered to top
+ verify(mockShellTaskOrganizer)
+ .applyTransaction(
+ argThat { wct ->
+ return@argThat wct.hierarchyOps.any { hierarchyOps ->
+ hierarchyOps.container == taskBinder && hierarchyOps.toTop
+ }
+ }
+ )
+ }
+
+ @Test
+ fun testDragResize_resize_resizingTaskNotReorderedToTopWhenFocused() = runOnUiThread {
+ mockDesktopWindowDecoration.mHasGlobalFocus = true
+ taskPositioner.onDragPositioningStart(
+ CTRL_TYPE_RIGHT, // Resize right
+ DISPLAY_ID,
+ STARTING_BOUNDS.left.toFloat(),
+ STARTING_BOUNDS.top.toFloat(),
+ )
+
+ // Verify task is not reordered to top
+ verify(mockShellTaskOrganizer, never())
+ .applyTransaction(
+ argThat { wct ->
+ return@argThat wct.hierarchyOps.any { hierarchyOps ->
+ hierarchyOps.container == taskBinder && hierarchyOps.toTop
+ }
+ }
+ )
+ }
+
+ @Test
+ fun testDragResize_drag_draggedTaskNotReorderedToTop() = runOnUiThread {
+ mockDesktopWindowDecoration.mHasGlobalFocus = false
+ taskPositioner.onDragPositioningStart(
+ CTRL_TYPE_UNDEFINED, // drag
+ DISPLAY_ID,
+ STARTING_BOUNDS.left.toFloat(),
+ STARTING_BOUNDS.top.toFloat(),
+ )
+
+ // Verify task is not reordered to top since task is already brought to top before dragging
+ // begins
+ verify(mockShellTaskOrganizer, never())
+ .applyTransaction(
+ argThat { wct ->
+ return@argThat wct.hierarchyOps.any { hierarchyOps ->
+ hierarchyOps.container == taskBinder && hierarchyOps.toTop
+ }
+ }
+ )
+ }
+
+ @Test
+ fun testDragResize_drag_updatesStableBoundsOnRotate() = runOnUiThread {
+ // Test landscape stable bounds
+ performDrag(
+ STARTING_BOUNDS.right.toFloat(),
+ STARTING_BOUNDS.bottom.toFloat(),
+ STARTING_BOUNDS.right.toFloat() + 2000,
+ STARTING_BOUNDS.bottom.toFloat() + 2000,
+ CTRL_TYPE_RIGHT or CTRL_TYPE_BOTTOM,
+ )
+ val rectAfterDrag = Rect(STARTING_BOUNDS)
+ rectAfterDrag.right += 2000
+ rectAfterDrag.bottom = STABLE_BOUNDS_LANDSCAPE.bottom
+ // First drag; we should fetch stable bounds.
+ verify(mockDisplayLayout, times(1)).getStableBounds(any())
+ verify(mockTransitions)
+ .startTransition(
+ eq(TRANSIT_CHANGE),
+ argThat { wct ->
+ return@argThat wct.changes.any { (token, change) ->
+ token == taskBinder &&
+ (change.windowSetMask and WindowConfiguration.WINDOW_CONFIG_BOUNDS) !=
+ 0 &&
+ change.configuration.windowConfiguration.bounds == rectAfterDrag
+ }
+ },
+ eq(taskPositioner),
+ )
+ // Drag back to starting bounds.
+ performDrag(
+ STARTING_BOUNDS.right.toFloat() + 2000,
+ STARTING_BOUNDS.bottom.toFloat(),
+ STARTING_BOUNDS.right.toFloat(),
+ STARTING_BOUNDS.bottom.toFloat(),
+ CTRL_TYPE_RIGHT or CTRL_TYPE_BOTTOM,
+ )
+
+ // Display did not rotate; we should use previous stable bounds
+ verify(mockDisplayLayout, times(1)).getStableBounds(any())
+
+ // Rotate the screen to portrait
+ mockDesktopWindowDecoration.mTaskInfo.apply {
+ configuration.windowConfiguration.displayRotation = ROTATION_0
+ }
+ // Test portrait stable bounds
+ performDrag(
+ STARTING_BOUNDS.right.toFloat(),
+ STARTING_BOUNDS.bottom.toFloat(),
+ STARTING_BOUNDS.right.toFloat() + 2000,
+ STARTING_BOUNDS.bottom.toFloat() + 2000,
+ CTRL_TYPE_RIGHT or CTRL_TYPE_BOTTOM,
+ )
+ rectAfterDrag.right = STABLE_BOUNDS_PORTRAIT.right
+ rectAfterDrag.bottom = STARTING_BOUNDS.bottom + 2000
+
+ verify(mockTransitions)
+ .startTransition(
+ eq(TRANSIT_CHANGE),
+ argThat { wct ->
+ return@argThat wct.changes.any { (token, change) ->
+ token == taskBinder &&
+ (change.windowSetMask and WindowConfiguration.WINDOW_CONFIG_BOUNDS) !=
+ 0 &&
+ change.configuration.windowConfiguration.bounds == rectAfterDrag
+ }
+ },
+ eq(taskPositioner),
+ )
+ // Display has rotated; we expect a new stable bounds.
+ verify(mockDisplayLayout, times(2)).getStableBounds(any())
+ }
+
+ @Test
+ fun testIsResizingOrAnimatingResizeSet() = runOnUiThread {
+ Assert.assertFalse(taskPositioner.isResizingOrAnimating)
+
+ taskPositioner.onDragPositioningStart(
+ CTRL_TYPE_TOP or CTRL_TYPE_RIGHT,
+ DISPLAY_ID,
+ STARTING_BOUNDS.left.toFloat(),
+ STARTING_BOUNDS.top.toFloat(),
+ )
+
+ taskPositioner.onDragPositioningMove(
+ DISPLAY_ID,
+ STARTING_BOUNDS.left.toFloat() - 20,
+ STARTING_BOUNDS.top.toFloat() - 20,
+ )
+
+ // isResizingOrAnimating should be set to true after move during a resize
+ Assert.assertTrue(taskPositioner.isResizingOrAnimating)
+ verify(mockDragEventListener, times(1)).onDragMove(eq(TASK_ID))
+
+ taskPositioner.onDragPositioningEnd(
+ DISPLAY_ID,
+ STARTING_BOUNDS.left.toFloat(),
+ STARTING_BOUNDS.top.toFloat(),
+ )
+
+ // isResizingOrAnimating should be not be set till false until after transition animation
+ Assert.assertTrue(taskPositioner.isResizingOrAnimating)
+ }
+
+ @Test
+ fun testIsResizingOrAnimatingResizeResetAfterStartAnimation() = runOnUiThread {
+ performDrag(
+ STARTING_BOUNDS.left.toFloat(),
+ STARTING_BOUNDS.top.toFloat(),
+ STARTING_BOUNDS.left.toFloat() - 20,
+ STARTING_BOUNDS.top.toFloat() - 20,
+ CTRL_TYPE_TOP or CTRL_TYPE_RIGHT,
+ )
+
+ taskPositioner.startAnimation(
+ mockTransitionBinder,
+ mockTransitionInfo,
+ mockTransaction,
+ mockTransaction,
+ mockFinishCallback,
+ )
+
+ // isResizingOrAnimating should be set to false until after transition successfully consumed
+ Assert.assertFalse(taskPositioner.isResizingOrAnimating)
+ }
+
+ @Test
+ fun testStartAnimation_useEndRelOffset() = runOnUiThread {
+ val changeMock = mock(TransitionInfo.Change::class.java)
+ val startTransaction = mock(Transaction::class.java)
+ val finishTransaction = mock(Transaction::class.java)
+ val point = Point(10, 20)
+ val bounds = Rect(1, 2, 3, 4)
+ `when`(changeMock.leash).thenReturn(mock(SurfaceControl::class.java))
+ `when`(changeMock.endRelOffset).thenReturn(point)
+ `when`(changeMock.endAbsBounds).thenReturn(bounds)
+ `when`(mockTransitionInfo.changes).thenReturn(listOf(changeMock))
+ `when`(startTransaction.setWindowCrop(any(), eq(bounds.width()), eq(bounds.height())))
+ .thenReturn(startTransaction)
+ `when`(finishTransaction.setWindowCrop(any(), eq(bounds.width()), eq(bounds.height())))
+ .thenReturn(finishTransaction)
+
+ taskPositioner.startAnimation(
+ mockTransitionBinder,
+ mockTransitionInfo,
+ startTransaction,
+ finishTransaction,
+ mockFinishCallback,
+ )
+
+ verify(startTransaction).setPosition(any(), eq(point.x.toFloat()), eq(point.y.toFloat()))
+ verify(finishTransaction).setPosition(any(), eq(point.x.toFloat()), eq(point.y.toFloat()))
+ verify(changeMock).endRelOffset
+ }
+
+ private fun performDrag(startX: Float, startY: Float, endX: Float, endY: Float, ctrlType: Int) {
+ taskPositioner.onDragPositioningStart(ctrlType, DISPLAY_ID, startX, startY)
+ taskPositioner.onDragPositioningMove(DISPLAY_ID, endX, endY)
+
+ taskPositioner.onDragPositioningEnd(DISPLAY_ID, endX, endY)
+ }
+
+ companion object {
+ private const val TASK_ID = 5
+ private const val MIN_WIDTH = 10
+ private const val MIN_HEIGHT = 10
+ private const val DENSITY_DPI = 20
+ private const val DEFAULT_MIN = 40
+ private const val DISPLAY_ID = 1
+ private const val NAVBAR_HEIGHT = 50
+ private const val CAPTION_HEIGHT = 50
+ private const val DISALLOWED_AREA_FOR_END_BOUNDS_HEIGHT = 10
+ private val DISPLAY_BOUNDS = Rect(0, 0, 2400, 1600)
+ private val STARTING_BOUNDS = Rect(100, 100, 200, 200)
+ private val STABLE_BOUNDS_LANDSCAPE =
+ Rect(
+ DISPLAY_BOUNDS.left,
+ DISPLAY_BOUNDS.top + CAPTION_HEIGHT,
+ DISPLAY_BOUNDS.right,
+ DISPLAY_BOUNDS.bottom - NAVBAR_HEIGHT,
+ )
+ private val STABLE_BOUNDS_PORTRAIT =
+ Rect(
+ DISPLAY_BOUNDS.top,
+ DISPLAY_BOUNDS.left + CAPTION_HEIGHT,
+ DISPLAY_BOUNDS.bottom,
+ DISPLAY_BOUNDS.right - NAVBAR_HEIGHT,
+ )
+ private val VALID_DRAG_AREA =
+ Rect(
+ DISPLAY_BOUNDS.left - 100,
+ STABLE_BOUNDS_LANDSCAPE.top,
+ DISPLAY_BOUNDS.right - 100,
+ DISPLAY_BOUNDS.bottom - 100,
+ )
+ }
+}
diff --git a/location/java/android/location/Geocoder.java b/location/java/android/location/Geocoder.java
index cdde7c66e268..9746dab4151c 100644
--- a/location/java/android/location/Geocoder.java
+++ b/location/java/android/location/Geocoder.java
@@ -83,8 +83,11 @@ public final class Geocoder {
* succeed.
*/
public static boolean isPresent() {
- ILocationManager lm = Objects.requireNonNull(ILocationManager.Stub.asInterface(
- ServiceManager.getService(Context.LOCATION_SERVICE)));
+ ILocationManager lm = ILocationManager.Stub.asInterface(
+ ServiceManager.getService(Context.LOCATION_SERVICE));
+ if (lm == null) {
+ return false;
+ }
try {
return lm.isGeocodeAvailable();
} catch (RemoteException e) {
diff --git a/media/java/android/media/quality/MediaQualityManager.java b/media/java/android/media/quality/MediaQualityManager.java
index 191b938b2e26..aeb028ccd0a6 100644
--- a/media/java/android/media/quality/MediaQualityManager.java
+++ b/media/java/android/media/quality/MediaQualityManager.java
@@ -54,14 +54,17 @@ public final class MediaQualityManager {
private final IMediaQualityManager mService;
private final Context mContext;
private final UserHandle mUserHandle;
- private final Object mLock = new Object();
- // @GuardedBy("mLock")
+ private final Object mPpLock = new Object();
+ private final Object mSpLock = new Object();
+ private final Object mAbLock = new Object();
+ private final Object mApLock = new Object();
+ // @GuardedBy("mPpLock")
private final List<PictureProfileCallbackRecord> mPpCallbackRecords = new ArrayList<>();
- // @GuardedBy("mLock")
+ // @GuardedBy("mSpLock")
private final List<SoundProfileCallbackRecord> mSpCallbackRecords = new ArrayList<>();
- // @GuardedBy("mLock")
+ // @GuardedBy("mAbLock")
private final List<AmbientBacklightCallbackRecord> mAbCallbackRecords = new ArrayList<>();
- // @GuardedBy("mLock")
+ // @GuardedBy("mApLock")
private final List<ActiveProcessingPictureListenerRecord> mApListenerRecords =
new ArrayList<>();
@@ -82,7 +85,7 @@ public final class MediaQualityManager {
IPictureProfileCallback ppCallback = new IPictureProfileCallback.Stub() {
@Override
public void onPictureProfileAdded(String profileId, PictureProfile profile) {
- synchronized (mLock) {
+ synchronized (mPpLock) {
for (PictureProfileCallbackRecord record : mPpCallbackRecords) {
// TODO: filter callback record
record.postPictureProfileAdded(profileId, profile);
@@ -91,7 +94,7 @@ public final class MediaQualityManager {
}
@Override
public void onPictureProfileUpdated(String profileId, PictureProfile profile) {
- synchronized (mLock) {
+ synchronized (mPpLock) {
for (PictureProfileCallbackRecord record : mPpCallbackRecords) {
// TODO: filter callback record
record.postPictureProfileUpdated(profileId, profile);
@@ -100,7 +103,7 @@ public final class MediaQualityManager {
}
@Override
public void onPictureProfileRemoved(String profileId, PictureProfile profile) {
- synchronized (mLock) {
+ synchronized (mPpLock) {
for (PictureProfileCallbackRecord record : mPpCallbackRecords) {
// TODO: filter callback record
record.postPictureProfileRemoved(profileId, profile);
@@ -110,7 +113,7 @@ public final class MediaQualityManager {
@Override
public void onParameterCapabilitiesChanged(
String profileId, List<ParameterCapability> caps) {
- synchronized (mLock) {
+ synchronized (mPpLock) {
for (PictureProfileCallbackRecord record : mPpCallbackRecords) {
// TODO: filter callback record
record.postParameterCapabilitiesChanged(profileId, caps);
@@ -119,7 +122,7 @@ public final class MediaQualityManager {
}
@Override
public void onError(String profileId, int err) {
- synchronized (mLock) {
+ synchronized (mPpLock) {
for (PictureProfileCallbackRecord record : mPpCallbackRecords) {
// TODO: filter callback record
record.postError(profileId, err);
@@ -130,7 +133,7 @@ public final class MediaQualityManager {
ISoundProfileCallback spCallback = new ISoundProfileCallback.Stub() {
@Override
public void onSoundProfileAdded(String profileId, SoundProfile profile) {
- synchronized (mLock) {
+ synchronized (mSpLock) {
for (SoundProfileCallbackRecord record : mSpCallbackRecords) {
// TODO: filter callback record
record.postSoundProfileAdded(profileId, profile);
@@ -139,7 +142,7 @@ public final class MediaQualityManager {
}
@Override
public void onSoundProfileUpdated(String profileId, SoundProfile profile) {
- synchronized (mLock) {
+ synchronized (mSpLock) {
for (SoundProfileCallbackRecord record : mSpCallbackRecords) {
// TODO: filter callback record
record.postSoundProfileUpdated(profileId, profile);
@@ -148,7 +151,7 @@ public final class MediaQualityManager {
}
@Override
public void onSoundProfileRemoved(String profileId, SoundProfile profile) {
- synchronized (mLock) {
+ synchronized (mSpLock) {
for (SoundProfileCallbackRecord record : mSpCallbackRecords) {
// TODO: filter callback record
record.postSoundProfileRemoved(profileId, profile);
@@ -158,7 +161,7 @@ public final class MediaQualityManager {
@Override
public void onParameterCapabilitiesChanged(
String profileId, List<ParameterCapability> caps) {
- synchronized (mLock) {
+ synchronized (mSpLock) {
for (SoundProfileCallbackRecord record : mSpCallbackRecords) {
// TODO: filter callback record
record.postParameterCapabilitiesChanged(profileId, caps);
@@ -167,7 +170,7 @@ public final class MediaQualityManager {
}
@Override
public void onError(String profileId, int err) {
- synchronized (mLock) {
+ synchronized (mSpLock) {
for (SoundProfileCallbackRecord record : mSpCallbackRecords) {
// TODO: filter callback record
record.postError(profileId, err);
@@ -178,7 +181,7 @@ public final class MediaQualityManager {
IAmbientBacklightCallback abCallback = new IAmbientBacklightCallback.Stub() {
@Override
public void onAmbientBacklightEvent(AmbientBacklightEvent event) {
- synchronized (mLock) {
+ synchronized (mAbLock) {
for (AmbientBacklightCallbackRecord record : mAbCallbackRecords) {
record.postAmbientBacklightEvent(event);
}
@@ -205,7 +208,7 @@ public final class MediaQualityManager {
@NonNull PictureProfileCallback callback) {
Preconditions.checkNotNull(callback);
Preconditions.checkNotNull(executor);
- synchronized (mLock) {
+ synchronized (mPpLock) {
mPpCallbackRecords.add(new PictureProfileCallbackRecord(callback, executor));
}
}
@@ -215,7 +218,7 @@ public final class MediaQualityManager {
*/
public void unregisterPictureProfileCallback(@NonNull final PictureProfileCallback callback) {
Preconditions.checkNotNull(callback);
- synchronized (mLock) {
+ synchronized (mPpLock) {
for (Iterator<PictureProfileCallbackRecord> it = mPpCallbackRecords.iterator();
it.hasNext(); ) {
PictureProfileCallbackRecord record = it.next();
@@ -416,7 +419,7 @@ public final class MediaQualityManager {
@NonNull SoundProfileCallback callback) {
Preconditions.checkNotNull(callback);
Preconditions.checkNotNull(executor);
- synchronized (mLock) {
+ synchronized (mSpLock) {
mSpCallbackRecords.add(new SoundProfileCallbackRecord(callback, executor));
}
}
@@ -426,7 +429,7 @@ public final class MediaQualityManager {
*/
public void unregisterSoundProfileCallback(@NonNull final SoundProfileCallback callback) {
Preconditions.checkNotNull(callback);
- synchronized (mLock) {
+ synchronized (mSpLock) {
for (Iterator<SoundProfileCallbackRecord> it = mSpCallbackRecords.iterator();
it.hasNext(); ) {
SoundProfileCallbackRecord record = it.next();
@@ -785,7 +788,7 @@ public final class MediaQualityManager {
@NonNull AmbientBacklightCallback callback) {
Preconditions.checkNotNull(callback);
Preconditions.checkNotNull(executor);
- synchronized (mLock) {
+ synchronized (mAbLock) {
mAbCallbackRecords.add(new AmbientBacklightCallbackRecord(callback, executor));
}
}
@@ -797,7 +800,7 @@ public final class MediaQualityManager {
public void unregisterAmbientBacklightCallback(
@NonNull final AmbientBacklightCallback callback) {
Preconditions.checkNotNull(callback);
- synchronized (mLock) {
+ synchronized (mAbLock) {
for (Iterator<AmbientBacklightCallbackRecord> it = mAbCallbackRecords.iterator();
it.hasNext(); ) {
AmbientBacklightCallbackRecord record = it.next();
@@ -1128,7 +1131,7 @@ public final class MediaQualityManager {
@NonNull Consumer<List<ActiveProcessingPicture>> listener) {
Preconditions.checkNotNull(listener);
Preconditions.checkNotNull(executor);
- synchronized (mLock) {
+ synchronized (mApLock) {
mApListenerRecords.add(
new ActiveProcessingPictureListenerRecord(listener, executor, false));
}
@@ -1147,7 +1150,7 @@ public final class MediaQualityManager {
@NonNull Consumer<List<ActiveProcessingPicture>> listener) {
Preconditions.checkNotNull(listener);
Preconditions.checkNotNull(executor);
- synchronized (mLock) {
+ synchronized (mApLock) {
mApListenerRecords.add(
new ActiveProcessingPictureListenerRecord(listener, executor, true));
}
@@ -1160,7 +1163,7 @@ public final class MediaQualityManager {
public void removeActiveProcessingPictureListener(
@NonNull Consumer<List<ActiveProcessingPicture>> listener) {
Preconditions.checkNotNull(listener);
- synchronized (mLock) {
+ synchronized (mApLock) {
for (Iterator<ActiveProcessingPictureListenerRecord> it = mApListenerRecords.iterator();
it.hasNext(); ) {
ActiveProcessingPictureListenerRecord record = it.next();
diff --git a/media/tests/MediaRouter/Android.bp b/media/tests/MediaRouter/Android.bp
index e4f88a65ed1a..e63c59d53f2a 100644
--- a/media/tests/MediaRouter/Android.bp
+++ b/media/tests/MediaRouter/Android.bp
@@ -9,7 +9,7 @@ package {
android_test {
name: "mediaroutertest",
- team: "trendy_team_android_media_solutions",
+ team: "trendy_team_android_media_better_together",
srcs: ["**/*.java"],
diff --git a/native/android/TEST_MAPPING b/native/android/TEST_MAPPING
index be84574e6a2c..70560a84b88e 100644
--- a/native/android/TEST_MAPPING
+++ b/native/android/TEST_MAPPING
@@ -16,9 +16,7 @@
{
"name": "CtsOsTestCases_cts_performancehintmanagertest",
"file_patterns": ["performance_hint.cpp"]
- }
- ],
- "postsubmit": [
+ },
{
"name": "CtsThermalTestCases",
"file_patterns": ["thermal.cpp"]
diff --git a/native/android/libandroid.map.txt b/native/android/libandroid.map.txt
index b30b779b57b5..49cbd7181d77 100644
--- a/native/android/libandroid.map.txt
+++ b/native/android/libandroid.map.txt
@@ -421,6 +421,7 @@ LIBANDROID {
LIBANDROID_PLATFORM {
global:
AThermal_setIThermalServiceForTesting;
+ ASystemHealth_setIHintManagerForTesting;
APerformanceHint_setIHintManagerForTesting;
APerformanceHint_sendHint;
APerformanceHint_getThreadIds;
diff --git a/native/android/system_health.cpp b/native/android/system_health.cpp
index 5c07ac7bfccc..1b43e71c7bf0 100644
--- a/native/android/system_health.cpp
+++ b/native/android/system_health.cpp
@@ -14,6 +14,8 @@
* limitations under the License.
*/
+#define LOG_TAG "system_health"
+
#include <aidl/android/hardware/power/CpuHeadroomParams.h>
#include <aidl/android/hardware/power/GpuHeadroomParams.h>
#include <aidl/android/os/CpuHeadroomParamsInternal.h>
@@ -23,6 +25,17 @@
#include <android/system_health.h>
#include <binder/IServiceManager.h>
#include <binder/Status.h>
+#include <system_health_private.h>
+
+#include <list>
+#include <map>
+#include <memory>
+#include <mutex>
+#include <optional>
+#include <utility>
+
+#include "android-base/thread_annotations.h"
+#include "utils/SystemClock.h"
using namespace android;
using namespace aidl::android::os;
@@ -55,9 +68,20 @@ private:
IHintManager::HintManagerClientData mClientData;
};
+static std::shared_ptr<IHintManager>* gIHintManagerForTesting = nullptr;
+static std::shared_ptr<ASystemHealthManager> gSystemHealthManagerForTesting = nullptr;
+
ASystemHealthManager* ASystemHealthManager::getInstance() {
static std::once_flag creationFlag;
static ASystemHealthManager* instance = nullptr;
+ if (gSystemHealthManagerForTesting) {
+ return gSystemHealthManagerForTesting.get();
+ }
+ if (gIHintManagerForTesting) {
+ gSystemHealthManagerForTesting =
+ std::shared_ptr<ASystemHealthManager>(create(*gIHintManagerForTesting));
+ return gSystemHealthManagerForTesting.get();
+ }
std::call_once(creationFlag, []() { instance = create(nullptr); });
return instance;
}
@@ -121,7 +145,8 @@ int ASystemHealthManager::getCpuHeadroom(const ACpuHeadroomParams* params, float
}
return EPIPE;
}
- *outHeadroom = res->get<hal::CpuHeadroomResult::Tag::globalHeadroom>();
+ *outHeadroom = res ? res->get<hal::CpuHeadroomResult::Tag::globalHeadroom>()
+ : std::numeric_limits<float>::quiet_NaN();
return OK;
}
@@ -155,37 +180,20 @@ int ASystemHealthManager::getGpuHeadroom(const AGpuHeadroomParams* params, float
}
return EPIPE;
}
- *outHeadroom = res->get<hal::GpuHeadroomResult::Tag::globalHeadroom>();
+ *outHeadroom = res ? res->get<hal::GpuHeadroomResult::Tag::globalHeadroom>()
+ : std::numeric_limits<float>::quiet_NaN();
return OK;
}
int ASystemHealthManager::getCpuHeadroomMinIntervalMillis(int64_t* outMinIntervalMillis) {
if (!mClientData.supportInfo.headroom.isCpuSupported) return ENOTSUP;
- int64_t minIntervalMillis = 0;
- ::ndk::ScopedAStatus ret = mHintManager->getCpuHeadroomMinIntervalMillis(&minIntervalMillis);
- if (!ret.isOk()) {
- ALOGE("ASystemHealth_getCpuHeadroomMinIntervalMillis fails: %s", ret.getMessage());
- if (ret.getExceptionCode() == EX_UNSUPPORTED_OPERATION) {
- return ENOTSUP;
- }
- return EPIPE;
- }
- *outMinIntervalMillis = minIntervalMillis;
+ *outMinIntervalMillis = mClientData.supportInfo.headroom.cpuMinIntervalMillis;
return OK;
}
int ASystemHealthManager::getGpuHeadroomMinIntervalMillis(int64_t* outMinIntervalMillis) {
if (!mClientData.supportInfo.headroom.isGpuSupported) return ENOTSUP;
- int64_t minIntervalMillis = 0;
- ::ndk::ScopedAStatus ret = mHintManager->getGpuHeadroomMinIntervalMillis(&minIntervalMillis);
- if (!ret.isOk()) {
- ALOGE("ASystemHealth_getGpuHeadroomMinIntervalMillis fails: %s", ret.getMessage());
- if (ret.getExceptionCode() == EX_UNSUPPORTED_OPERATION) {
- return ENOTSUP;
- }
- return EPIPE;
- }
- *outMinIntervalMillis = minIntervalMillis;
+ *outMinIntervalMillis = mClientData.supportInfo.headroom.gpuMinIntervalMillis;
return OK;
}
@@ -298,7 +306,6 @@ void ACpuHeadroomParams_setTids(ACpuHeadroomParams* _Nonnull params, const int*
size_t tidsSize) {
LOG_ALWAYS_FATAL_IF(tids == nullptr, "%s: tids should not be null", __FUNCTION__);
params->tids.resize(tidsSize);
- params->tids.clear();
for (int i = 0; i < (int)tidsSize; ++i) {
LOG_ALWAYS_FATAL_IF(tids[i] <= 0, "ACpuHeadroomParams_setTids: Invalid non-positive tid %d",
tids[i]);
@@ -355,3 +362,10 @@ void ACpuHeadroomParams_destroy(ACpuHeadroomParams* _Nullable params) {
void AGpuHeadroomParams_destroy(AGpuHeadroomParams* _Nullable params) {
delete params;
}
+
+void ASystemHealth_setIHintManagerForTesting(void* iManager) {
+ if (iManager == nullptr) {
+ gSystemHealthManagerForTesting = nullptr;
+ }
+ gIHintManagerForTesting = static_cast<std::shared_ptr<IHintManager>*>(iManager);
+}
diff --git a/native/android/tests/system_health/Android.bp b/native/android/tests/system_health/Android.bp
new file mode 100644
index 000000000000..30aeb77375ad
--- /dev/null
+++ b/native/android/tests/system_health/Android.bp
@@ -0,0 +1,66 @@
+// Copyright (C) 2024 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
+cc_test {
+ name: "NativeSystemHealthUnitTestCases",
+
+ multilib: {
+ lib32: {
+ suffix: "32",
+ },
+ lib64: {
+ suffix: "64",
+ },
+ },
+
+ srcs: ["NativeSystemHealthUnitTest.cpp"],
+
+ shared_libs: [
+ "libandroid",
+ "libbinder",
+ "libbinder_ndk",
+ "liblog",
+ "libpowermanager",
+ "libutils",
+ ],
+
+ static_libs: [
+ "libbase",
+ "libgmock",
+ "libgtest",
+ ],
+ stl: "c++_shared",
+
+ test_suites: [
+ "device-tests",
+ ],
+
+ cflags: [
+ "-Wall",
+ "-Werror",
+ ],
+
+ header_libs: [
+ "libandroid_headers_private",
+ ],
+}
diff --git a/native/android/tests/system_health/NativeSystemHealthUnitTest.cpp b/native/android/tests/system_health/NativeSystemHealthUnitTest.cpp
new file mode 100644
index 000000000000..3f08fc66e392
--- /dev/null
+++ b/native/android/tests/system_health/NativeSystemHealthUnitTest.cpp
@@ -0,0 +1,231 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "NativeSystemHealthUnitTest"
+
+#include <aidl/android/os/IHintManager.h>
+#include <android/binder_manager.h>
+#include <android/binder_status.h>
+#include <android/system_health.h>
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+#include <system_health_private.h>
+
+#include <memory>
+#include <optional>
+#include <vector>
+
+using namespace std::chrono_literals;
+namespace hal = aidl::android::hardware::power;
+using aidl::android::os::CpuHeadroomParamsInternal;
+using aidl::android::os::GpuHeadroomParamsInternal;
+using aidl::android::os::IHintManager;
+using aidl::android::os::IHintSession;
+using aidl::android::os::SessionCreationConfig;
+using ndk::ScopedAStatus;
+using ndk::SpAIBinder;
+
+using namespace android;
+using namespace testing;
+
+class MockIHintManager : public IHintManager {
+public:
+ MOCK_METHOD(ScopedAStatus, createHintSessionWithConfig,
+ (const SpAIBinder& token, hal::SessionTag tag,
+ const SessionCreationConfig& creationConfig, hal::SessionConfig* config,
+ IHintManager::SessionCreationReturn* _aidl_return),
+ (override));
+ MOCK_METHOD(ScopedAStatus, setHintSessionThreads,
+ (const std::shared_ptr<IHintSession>& _, const ::std::vector<int32_t>& tids),
+ (override));
+ MOCK_METHOD(ScopedAStatus, getHintSessionThreadIds,
+ (const std::shared_ptr<IHintSession>& _, ::std::vector<int32_t>* tids), (override));
+ MOCK_METHOD(ScopedAStatus, getSessionChannel,
+ (const ::ndk::SpAIBinder& in_token,
+ std::optional<hal::ChannelConfig>* _aidl_return),
+ (override));
+ MOCK_METHOD(ScopedAStatus, closeSessionChannel, (), (override));
+ MOCK_METHOD(ScopedAStatus, getCpuHeadroom,
+ (const CpuHeadroomParamsInternal& _,
+ std::optional<hal::CpuHeadroomResult>* _aidl_return),
+ (override));
+ MOCK_METHOD(ScopedAStatus, getCpuHeadroomMinIntervalMillis, (int64_t*), (override));
+ MOCK_METHOD(ScopedAStatus, getGpuHeadroom,
+ (const GpuHeadroomParamsInternal& _,
+ std::optional<hal::GpuHeadroomResult>* _aidl_return),
+ (override));
+ MOCK_METHOD(ScopedAStatus, getGpuHeadroomMinIntervalMillis, (int64_t* _aidl_return),
+ (override));
+ MOCK_METHOD(ScopedAStatus, passSessionManagerBinder, (const SpAIBinder& sessionManager));
+ MOCK_METHOD(ScopedAStatus, registerClient,
+ (const std::shared_ptr<aidl::android::os::IHintManager::IHintManagerClient>& _,
+ aidl::android::os::IHintManager::HintManagerClientData* _aidl_return),
+ (override));
+ MOCK_METHOD(ScopedAStatus, getClientData,
+ (aidl::android::os::IHintManager::HintManagerClientData * _aidl_return),
+ (override));
+ MOCK_METHOD(SpAIBinder, asBinder, (), (override));
+ MOCK_METHOD(bool, isRemote, (), (override));
+};
+
+class NativeSystemHealthUnitTest : public Test {
+public:
+ void SetUp() override {
+ mMockIHintManager = ndk::SharedRefBase::make<NiceMock<MockIHintManager>>();
+ ASystemHealth_setIHintManagerForTesting(&mMockIHintManager);
+ ON_CALL(*mMockIHintManager, getClientData(_))
+ .WillByDefault(
+ DoAll(SetArgPointee<0>(mClientData), [] { return ScopedAStatus::ok(); }));
+ }
+
+ void TearDown() override {
+ ASystemHealth_setIHintManagerForTesting(nullptr);
+ }
+
+ IHintManager::HintManagerClientData mClientData{
+ .powerHalVersion = 6,
+ .maxCpuHeadroomThreads = 10,
+ .supportInfo{.headroom{
+ .isCpuSupported = true,
+ .isGpuSupported = true,
+ .cpuMinIntervalMillis = 999,
+ .gpuMinIntervalMillis = 998,
+ .cpuMinCalculationWindowMillis = 45,
+ .cpuMaxCalculationWindowMillis = 9999,
+ .gpuMinCalculationWindowMillis = 46,
+ .gpuMaxCalculationWindowMillis = 9998,
+ }},
+ };
+
+ std::shared_ptr<NiceMock<MockIHintManager>> mMockIHintManager = nullptr;
+};
+
+TEST_F(NativeSystemHealthUnitTest, headroomParamsValueRange) {
+ int64_t minIntervalMillis = 0;
+ int minCalculationWindowMillis = 0;
+ int maxCalculationWindowMillis = 0;
+ ASSERT_EQ(OK, ASystemHealth_getCpuHeadroomMinIntervalMillis(&minIntervalMillis));
+ ASSERT_EQ(OK,
+ ASystemHealth_getCpuHeadroomCalculationWindowRange(&minCalculationWindowMillis,
+ &maxCalculationWindowMillis));
+ ASSERT_EQ(minIntervalMillis, mClientData.supportInfo.headroom.cpuMinIntervalMillis);
+ ASSERT_EQ(minCalculationWindowMillis,
+ mClientData.supportInfo.headroom.cpuMinCalculationWindowMillis);
+ ASSERT_EQ(maxCalculationWindowMillis,
+ mClientData.supportInfo.headroom.cpuMaxCalculationWindowMillis);
+
+ ASSERT_EQ(OK, ASystemHealth_getGpuHeadroomMinIntervalMillis(&minIntervalMillis));
+ ASSERT_EQ(OK,
+ ASystemHealth_getGpuHeadroomCalculationWindowRange(&minCalculationWindowMillis,
+ &maxCalculationWindowMillis));
+ ASSERT_EQ(minIntervalMillis, mClientData.supportInfo.headroom.gpuMinIntervalMillis);
+ ASSERT_EQ(minCalculationWindowMillis,
+ mClientData.supportInfo.headroom.gpuMinCalculationWindowMillis);
+ ASSERT_EQ(maxCalculationWindowMillis,
+ mClientData.supportInfo.headroom.gpuMaxCalculationWindowMillis);
+}
+
+TEST_F(NativeSystemHealthUnitTest, getCpuHeadroom) {
+ CpuHeadroomParamsInternal internalParams1;
+ ACpuHeadroomParams* params2 = ACpuHeadroomParams_create();
+ ACpuHeadroomParams_setCalculationWindowMillis(params2, 200);
+ CpuHeadroomParamsInternal internalParams2;
+ internalParams2.calculationWindowMillis = 200;
+ ACpuHeadroomParams* params3 = ACpuHeadroomParams_create();
+ ACpuHeadroomParams_setCalculationType(params3, ACPU_HEADROOM_CALCULATION_TYPE_AVERAGE);
+ CpuHeadroomParamsInternal internalParams3;
+ internalParams3.calculationType = hal::CpuHeadroomParams::CalculationType::AVERAGE;
+ ACpuHeadroomParams* params4 = ACpuHeadroomParams_create();
+ int tids[3] = {1, 2, 3};
+ ACpuHeadroomParams_setTids(params4, tids, 3);
+ CpuHeadroomParamsInternal internalParams4;
+ internalParams4.tids = {1, 2, 3};
+
+ EXPECT_CALL(*mMockIHintManager, getCpuHeadroom(internalParams1, _))
+ .Times(Exactly(1))
+ .WillOnce(DoAll(SetArgPointee<1>(hal::CpuHeadroomResult::make<
+ hal::CpuHeadroomResult::globalHeadroom>(1.0f)),
+ [] { return ScopedAStatus::ok(); }));
+ EXPECT_CALL(*mMockIHintManager, getCpuHeadroom(internalParams2, _))
+ .Times(Exactly(1))
+ .WillOnce(DoAll(SetArgPointee<1>(hal::CpuHeadroomResult::make<
+ hal::CpuHeadroomResult::globalHeadroom>(2.0f)),
+ [] { return ScopedAStatus::ok(); }));
+ EXPECT_CALL(*mMockIHintManager, getCpuHeadroom(internalParams3, _))
+ .Times(Exactly(1))
+ .WillOnce(DoAll(SetArgPointee<1>(std::nullopt), [] { return ScopedAStatus::ok(); }));
+ EXPECT_CALL(*mMockIHintManager, getCpuHeadroom(internalParams4, _))
+ .Times(Exactly(1))
+ .WillOnce(DoAll(SetArgPointee<1>(hal::CpuHeadroomResult::make<
+ hal::CpuHeadroomResult::globalHeadroom>(4.0f)),
+ [] { return ScopedAStatus::ok(); }));
+
+ float headroom1 = 0.0f;
+ float headroom2 = 0.0f;
+ float headroom3 = 0.0f;
+ float headroom4 = 0.0f;
+ ASSERT_EQ(OK, ASystemHealth_getCpuHeadroom(nullptr, &headroom1));
+ ASSERT_EQ(OK, ASystemHealth_getCpuHeadroom(params2, &headroom2));
+ ASSERT_EQ(OK, ASystemHealth_getCpuHeadroom(params3, &headroom3));
+ ASSERT_EQ(OK, ASystemHealth_getCpuHeadroom(params4, &headroom4));
+ ASSERT_EQ(1.0f, headroom1);
+ ASSERT_EQ(2.0f, headroom2);
+ ASSERT_TRUE(isnan(headroom3));
+ ASSERT_EQ(4.0f, headroom4);
+
+ ACpuHeadroomParams_destroy(params2);
+ ACpuHeadroomParams_destroy(params3);
+ ACpuHeadroomParams_destroy(params4);
+}
+
+TEST_F(NativeSystemHealthUnitTest, getGpuHeadroom) {
+ GpuHeadroomParamsInternal internalParams1;
+ AGpuHeadroomParams* params2 = AGpuHeadroomParams_create();
+ AGpuHeadroomParams_setCalculationWindowMillis(params2, 200);
+ GpuHeadroomParamsInternal internalParams2;
+ internalParams2.calculationWindowMillis = 200;
+ AGpuHeadroomParams* params3 = AGpuHeadroomParams_create();
+ AGpuHeadroomParams_setCalculationType(params3, AGPU_HEADROOM_CALCULATION_TYPE_AVERAGE);
+ GpuHeadroomParamsInternal internalParams3;
+ internalParams3.calculationType = hal::GpuHeadroomParams::CalculationType::AVERAGE;
+
+ EXPECT_CALL(*mMockIHintManager, getGpuHeadroom(internalParams1, _))
+ .Times(Exactly(1))
+ .WillOnce(DoAll(SetArgPointee<1>(hal::GpuHeadroomResult::make<
+ hal::GpuHeadroomResult::globalHeadroom>(1.0f)),
+ [] { return ScopedAStatus::ok(); }));
+ EXPECT_CALL(*mMockIHintManager, getGpuHeadroom(internalParams2, _))
+ .Times(Exactly(1))
+ .WillOnce(DoAll(SetArgPointee<1>(hal::GpuHeadroomResult::make<
+ hal::GpuHeadroomResult::globalHeadroom>(2.0f)),
+ [] { return ScopedAStatus::ok(); }));
+ EXPECT_CALL(*mMockIHintManager, getGpuHeadroom(internalParams3, _))
+ .Times(Exactly(1))
+ .WillOnce(DoAll(SetArgPointee<1>(std::nullopt), [] { return ScopedAStatus::ok(); }));
+
+ float headroom1 = 0.0f;
+ float headroom2 = 0.0f;
+ float headroom3 = 0.0f;
+ ASSERT_EQ(OK, ASystemHealth_getGpuHeadroom(nullptr, &headroom1));
+ ASSERT_EQ(OK, ASystemHealth_getGpuHeadroom(params2, &headroom2));
+ ASSERT_EQ(OK, ASystemHealth_getGpuHeadroom(params3, &headroom3));
+ ASSERT_EQ(1.0f, headroom1);
+ ASSERT_EQ(2.0f, headroom2);
+ ASSERT_TRUE(isnan(headroom3));
+
+ AGpuHeadroomParams_destroy(params2);
+ AGpuHeadroomParams_destroy(params3);
+}
diff --git a/packages/CrashRecovery/framework/Android.bp b/packages/CrashRecovery/framework/Android.bp
index 1a3446ec56de..5dd42bb633e5 100644
--- a/packages/CrashRecovery/framework/Android.bp
+++ b/packages/CrashRecovery/framework/Android.bp
@@ -1,8 +1,8 @@
filegroup {
name: "framework-crashrecovery-sources",
srcs: [
- "java/**/*.java",
"java/**/*.aidl",
+ "java/**/*.java",
],
path: "java",
visibility: [
@@ -12,11 +12,14 @@ filegroup {
java_sdk_library {
name: "framework-platformcrashrecovery",
- srcs: [":framework-crashrecovery-sources"],
+ srcs: [
+ ":framework-crashrecovery-module-sources",
+ ":framework-crashrecovery-sources",
+ ],
defaults: ["framework-non-updatable-unbundled-defaults"],
permitted_packages: [
- "android.service.watchdog",
"android.crashrecovery",
+ "android.service.watchdog",
],
static_libs: ["android.crashrecovery.flags-aconfig-java"],
aidl: {
diff --git a/packages/CrashRecovery/services/module/java/com/android/server/RescueParty.java b/packages/CrashRecovery/services/module/java/com/android/server/RescueParty.java
index 40bc5f78a9c6..846da194b3c3 100644
--- a/packages/CrashRecovery/services/module/java/com/android/server/RescueParty.java
+++ b/packages/CrashRecovery/services/module/java/com/android/server/RescueParty.java
@@ -29,18 +29,13 @@ import android.content.pm.PackageManager;
import android.content.pm.VersionedPackage;
import android.crashrecovery.flags.Flags;
import android.os.Build;
-import android.os.Environment;
import android.os.PowerManager;
import android.os.RecoverySystem;
import android.os.SystemClock;
import android.os.SystemProperties;
-import android.os.UserHandle;
-import android.provider.DeviceConfig;
import android.provider.Settings;
import android.sysprop.CrashRecoveryProperties;
import android.text.TextUtils;
-import android.util.ArraySet;
-import android.util.ArrayUtils;
import android.util.EventLog;
import android.util.FileUtils;
import android.util.Log;
@@ -56,10 +51,7 @@ import com.android.server.crashrecovery.proto.CrashRecoveryStatsLog;
import java.io.File;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
-import java.util.ArrayList;
import java.util.HashMap;
-import java.util.HashSet;
-import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.TimeUnit;
@@ -241,87 +233,11 @@ public class RescueParty {
CrashRecoveryProperties.maxRescueLevelAttempted(level);
}
- private static Set<String> getPresetNamespacesForPackages(List<String> packageNames) {
- Set<String> resultSet = new ArraySet<String>();
- if (!Flags.deprecateFlagsAndSettingsResets()) {
- try {
- String flagVal = DeviceConfig.getString(NAMESPACE_CONFIGURATION,
- NAMESPACE_TO_PACKAGE_MAPPING_FLAG, "");
- String[] mappingEntries = flagVal.split(",");
- for (int i = 0; i < mappingEntries.length; i++) {
- if (TextUtils.isEmpty(mappingEntries[i])) {
- continue;
- }
- String[] splitEntry = mappingEntries[i].split(":");
- if (splitEntry.length != 2) {
- throw new RuntimeException("Invalid mapping entry: " + mappingEntries[i]);
- }
- String namespace = splitEntry[0];
- String packageName = splitEntry[1];
-
- if (packageNames.contains(packageName)) {
- resultSet.add(namespace);
- }
- }
- } catch (Exception e) {
- resultSet.clear();
- Slog.e(TAG, "Failed to read preset package to namespaces mapping.", e);
- } finally {
- return resultSet;
- }
- } else {
- return resultSet;
- }
- }
-
@VisibleForTesting
static long getElapsedRealtime() {
return SystemClock.elapsedRealtime();
}
- private static class RescuePartyMonitorCallback implements DeviceConfig.MonitorCallback {
- Context mContext;
-
- RescuePartyMonitorCallback(Context context) {
- this.mContext = context;
- }
-
- public void onNamespaceUpdate(@NonNull String updatedNamespace) {
- if (!Flags.deprecateFlagsAndSettingsResets()) {
- startObservingPackages(mContext, updatedNamespace);
- }
- }
-
- public void onDeviceConfigAccess(@NonNull String callingPackage,
- @NonNull String namespace) {
-
- if (!Flags.deprecateFlagsAndSettingsResets()) {
- RescuePartyObserver.getInstance(mContext).recordDeviceConfigAccess(
- callingPackage,
- namespace);
- }
- }
- }
-
- private static void startObservingPackages(Context context, @NonNull String updatedNamespace) {
- if (!Flags.deprecateFlagsAndSettingsResets()) {
- RescuePartyObserver rescuePartyObserver = RescuePartyObserver.getInstance(context);
- Set<String> callingPackages = rescuePartyObserver.getCallingPackagesSet(
- updatedNamespace);
- if (callingPackages == null) {
- return;
- }
- List<String> callingPackageList = new ArrayList<>();
- callingPackageList.addAll(callingPackages);
- Slog.i(TAG, "Starting to observe: " + callingPackageList + ", updated namespace: "
- + updatedNamespace);
- PackageWatchdog.getInstance(context).startExplicitHealthCheck(
- callingPackageList,
- DEFAULT_OBSERVING_DURATION_MS,
- rescuePartyObserver);
- }
- }
-
private static int getMaxRescueLevel(boolean mayPerformReboot) {
if (Flags.recoverabilityDetection()) {
if (!mayPerformReboot
@@ -849,34 +765,6 @@ public class RescueParty {
}
}
- private synchronized void recordDeviceConfigAccess(@NonNull String callingPackage,
- @NonNull String namespace) {
- if (!Flags.deprecateFlagsAndSettingsResets()) {
- // Record it in calling packages to namespace map
- Set<String> namespaceSet = mCallingPackageNamespaceSetMap.get(callingPackage);
- if (namespaceSet == null) {
- namespaceSet = new ArraySet<>();
- mCallingPackageNamespaceSetMap.put(callingPackage, namespaceSet);
- }
- namespaceSet.add(namespace);
- // Record it in namespace to calling packages map
- Set<String> callingPackageSet = mNamespaceCallingPackageSetMap.get(namespace);
- if (callingPackageSet == null) {
- callingPackageSet = new ArraySet<>();
- }
- callingPackageSet.add(callingPackage);
- mNamespaceCallingPackageSetMap.put(namespace, callingPackageSet);
- }
- }
-
- private synchronized Set<String> getAffectedNamespaceSet(String failedPackage) {
- return mCallingPackageNamespaceSetMap.get(failedPackage);
- }
-
- private synchronized Set<String> getAllAffectedNamespaceSet() {
- return new HashSet<String>(mNamespaceCallingPackageSetMap.keySet());
- }
-
private synchronized Set<String> getCallingPackagesSet(String namespace) {
return mNamespaceCallingPackageSetMap.get(namespace);
}
@@ -894,26 +782,6 @@ public class RescueParty {
return now < lastResetTime + TimeUnit.MINUTES.toMillis(throttleDurationMin);
}
- private static int[] getAllUserIds() {
- int systemUserId = UserHandle.SYSTEM.getIdentifier();
- int[] userIds = { systemUserId };
- try {
- for (File file : FileUtils.listFilesOrEmpty(
- Environment.getDataSystemDeviceProtectedDirectory())) {
- try {
- final int userId = Integer.parseInt(file.getName());
- if (userId != systemUserId) {
- userIds = ArrayUtils.appendInt(userIds, userId);
- }
- } catch (NumberFormatException ignored) {
- }
- }
- } catch (Throwable t) {
- Slog.w(TAG, "Trouble discovering users", t);
- }
- return userIds;
- }
-
/**
* Hacky test to check if the device has an active USB connection, which is
* a good proxy for someone doing local development work.
diff --git a/packages/CrashRecovery/services/module/java/com/android/util/ArrayUtils.java b/packages/CrashRecovery/services/module/java/com/android/util/ArrayUtils.java
index 0b7b98603419..29ff7cced897 100644
--- a/packages/CrashRecovery/services/module/java/com/android/util/ArrayUtils.java
+++ b/packages/CrashRecovery/services/module/java/com/android/util/ArrayUtils.java
@@ -16,13 +16,8 @@
package android.util;
-import android.annotation.NonNull;
import android.annotation.Nullable;
-import java.io.File;
-import java.util.List;
-import java.util.Objects;
-
/**
* Copied over from frameworks/base/core/java/com/android/internal/util/ArrayUtils.java
*
@@ -30,25 +25,6 @@ import java.util.Objects;
*/
public class ArrayUtils {
private ArrayUtils() { /* cannot be instantiated */ }
- public static final File[] EMPTY_FILE = new File[0];
-
-
- /**
- * Return first index of {@code value} in {@code array}, or {@code -1} if
- * not found.
- */
- public static <T> int indexOf(@Nullable T[] array, T value) {
- if (array == null) return -1;
- for (int i = 0; i < array.length; i++) {
- if (Objects.equals(array[i], value)) return i;
- }
- return -1;
- }
-
- /** @hide */
- public static @NonNull File[] defeatNullable(@Nullable File[] val) {
- return (val != null) ? val : EMPTY_FILE;
- }
/**
* Checks if given array is null or has zero elements.
@@ -63,53 +39,4 @@ public class ArrayUtils {
public static boolean isEmpty(@Nullable byte[] array) {
return array == null || array.length == 0;
}
-
- /**
- * Converts from List of bytes to byte array
- * @param list
- * @return byte[]
- */
- public static byte[] toPrimitive(List<byte[]> list) {
- if (list.size() == 0) {
- return new byte[0];
- }
- int byteLen = list.get(0).length;
- byte[] array = new byte[list.size() * byteLen];
- for (int i = 0; i < list.size(); i++) {
- for (int j = 0; j < list.get(i).length; j++) {
- array[i * byteLen + j] = list.get(i)[j];
- }
- }
- return array;
- }
-
- /**
- * Adds value to given array if not already present, providing set-like
- * behavior.
- */
- public static @NonNull int[] appendInt(@Nullable int[] cur, int val) {
- return appendInt(cur, val, false);
- }
-
- /**
- * Adds value to given array.
- */
- public static @NonNull int[] appendInt(@Nullable int[] cur, int val,
- boolean allowDuplicates) {
- if (cur == null) {
- return new int[] { val };
- }
- final int n = cur.length;
- if (!allowDuplicates) {
- for (int i = 0; i < n; i++) {
- if (cur[i] == val) {
- return cur;
- }
- }
- }
- int[] ret = new int[n + 1];
- System.arraycopy(cur, 0, ret, 0, n);
- ret[n] = val;
- return ret;
- }
}
diff --git a/packages/CrashRecovery/services/module/java/com/android/util/FileUtils.java b/packages/CrashRecovery/services/module/java/com/android/util/FileUtils.java
index 9c73feeffb6c..d60a9b9847ca 100644
--- a/packages/CrashRecovery/services/module/java/com/android/util/FileUtils.java
+++ b/packages/CrashRecovery/services/module/java/com/android/util/FileUtils.java
@@ -16,7 +16,6 @@
package android.util;
-import android.annotation.NonNull;
import android.annotation.Nullable;
import java.io.BufferedInputStream;
@@ -115,14 +114,4 @@ public class FileUtils {
}
return false;
}
-
- /**
- * List the files in the directory or return empty file.
- *
- * @hide
- */
- public static @NonNull File[] listFilesOrEmpty(@Nullable File dir) {
- return (dir != null) ? ArrayUtils.defeatNullable(dir.listFiles())
- : ArrayUtils.EMPTY_FILE;
- }
}
diff --git a/packages/CrashRecovery/services/module/java/com/android/util/XmlUtils.java b/packages/CrashRecovery/services/module/java/com/android/util/XmlUtils.java
index 50823f5c9c34..488b531c2b8a 100644
--- a/packages/CrashRecovery/services/module/java/com/android/util/XmlUtils.java
+++ b/packages/CrashRecovery/services/module/java/com/android/util/XmlUtils.java
@@ -16,21 +16,10 @@
package android.util;
-import android.annotation.NonNull;
-import android.system.ErrnoException;
-import android.system.Os;
-
-import com.android.modules.utils.TypedXmlPullParser;
-
-import libcore.util.XmlObjectFactory;
-
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
-import java.io.BufferedInputStream;
-import java.io.FileInputStream;
import java.io.IOException;
-import java.io.InputStream;
/**
* Bits and pieces copied from hidden API of
@@ -40,8 +29,6 @@ import java.io.InputStream;
*/
public class XmlUtils {
- private static final String STRING_ARRAY_SEPARATOR = ":";
-
/** @hide */
public static final void beginDocument(XmlPullParser parser, String firstElementName)
throws XmlPullParserException, IOException {
@@ -76,44 +63,4 @@ public class XmlUtils {
}
}
}
-
- private static XmlPullParser newPullParser() {
- try {
- XmlPullParser parser = XmlObjectFactory.newXmlPullParser();
- parser.setFeature(XmlPullParser.FEATURE_PROCESS_DOCDECL, true);
- parser.setFeature(XmlPullParser.FEATURE_PROCESS_NAMESPACES, true);
- return parser;
- } catch (XmlPullParserException e) {
- throw new AssertionError();
- }
- }
-
- /** @hide */
- public static @NonNull TypedXmlPullParser resolvePullParser(@NonNull InputStream in)
- throws IOException {
- final byte[] magic = new byte[4];
- if (in instanceof FileInputStream) {
- try {
- Os.pread(((FileInputStream) in).getFD(), magic, 0, magic.length, 0);
- } catch (ErrnoException e) {
- throw e.rethrowAsIOException();
- }
- } else {
- if (!in.markSupported()) {
- in = new BufferedInputStream(in);
- }
- in.mark(8);
- in.read(magic);
- in.reset();
- }
-
- final TypedXmlPullParser xml;
- xml = (TypedXmlPullParser) newPullParser();
- try {
- xml.setInput(in, "UTF_8");
- } catch (XmlPullParserException e) {
- throw new IOException(e);
- }
- return xml;
- }
}
diff --git a/packages/SettingsLib/CardPreference/res/drawable/settingslib_card_preference_background.xml b/packages/SettingsLib/CardPreference/res/drawable/settingslib_card_preference_background.xml
new file mode 100644
index 000000000000..1d57c1617495
--- /dev/null
+++ b/packages/SettingsLib/CardPreference/res/drawable/settingslib_card_preference_background.xml
@@ -0,0 +1,28 @@
+<?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.
+-->
+
+<ripple xmlns:android="http://schemas.android.com/apk/res/android"
+ android:color="?android:colorControlHighlight">
+ <item>
+ <shape android:shape="rectangle">
+ <solid
+ android:color="@color/settingslib_materialColorSecondaryContainer" />
+ <corners
+ android:radius="@dimen/settingslib_expressive_radius_extralarge3" />
+ </shape>
+ </item>
+</ripple> \ No newline at end of file
diff --git a/packages/SettingsLib/CardPreference/res/layout/settingslib_expressive_preference_card.xml b/packages/SettingsLib/CardPreference/res/layout/settingslib_expressive_preference_card.xml
index 9018baca79e7..4ce106e56822 100644
--- a/packages/SettingsLib/CardPreference/res/layout/settingslib_expressive_preference_card.xml
+++ b/packages/SettingsLib/CardPreference/res/layout/settingslib_expressive_preference_card.xml
@@ -14,9 +14,13 @@
See the License for the specific language governing permissions and
limitations under the License.
-->
-<com.google.android.material.card.MaterialCardView
+<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
- style="@style/SettingsLibCardStyle">
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:paddingEnd="?android:attr/listPreferredItemPaddingEnd"
+ android:paddingStart="?android:attr/listPreferredItemPaddingStart"
+ android:paddingVertical="@dimen/settingslib_expressive_space_extrasmall4">
<LinearLayout
android:id="@+id/card_container"
@@ -24,10 +28,10 @@
android:layout_height="wrap_content"
android:baselineAligned="false"
android:minHeight="@dimen/settingslib_expressive_space_large3"
- android:paddingStart="@dimen/settingslib_expressive_space_small1"
- android:paddingEnd="@dimen/settingslib_expressive_space_small1"
+ android:paddingHorizontal="@dimen/settingslib_expressive_space_medium1"
android:orientation="horizontal"
- android:gravity="center_vertical">
+ android:gravity="center_vertical"
+ android:background="@drawable/settingslib_card_preference_background">
<LinearLayout
android:id="@+id/icon_frame"
@@ -35,15 +39,13 @@
android:layout_height="wrap_content"
android:minWidth="@dimen/settingslib_expressive_space_medium3"
android:minHeight="@dimen/settingslib_expressive_space_medium3"
- android:gravity="center"
- android:orientation="horizontal">
-
+ android:gravity="center">
<ImageView
android:id="@android:id/icon"
android:layout_width="@dimen/settingslib_expressive_space_medium3"
android:layout_height="@dimen/settingslib_expressive_space_medium3"
- android:scaleType="centerInside"/>
-
+ android:scaleType="centerInside"
+ android:importantForAccessibility="no"/>
</LinearLayout>
<LinearLayout
@@ -54,19 +56,16 @@
android:paddingHorizontal="@dimen/settingslib_expressive_space_small1"
android:paddingVertical="@dimen/settingslib_expressive_space_small2"
android:orientation="vertical">
-
<TextView
android:id="@android:id/title"
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:textAppearance="@style/TextAppearance.CardTitle.SettingsLib"/>
-
+ android:textAppearance="@style/TextAppearance.CardTitle.SettingsLib" />
<TextView
android:id="@android:id/summary"
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:textAppearance="@style/TextAppearance.CardSummary.SettingsLib"/>
-
+ android:textAppearance="@style/TextAppearance.CardSummary.SettingsLib" />
</LinearLayout>
<ImageView
@@ -75,9 +74,9 @@
android:layout_height="@dimen/settingslib_expressive_space_medium4"
android:padding="@dimen/settingslib_expressive_space_extrasmall4"
android:layout_gravity="center"
+ android:contentDescription="@string/settingslib_dismiss_button_content_description"
android:src="@drawable/settingslib_expressive_icon_close"
- android:background="?android:attr/selectableItemBackground" />
+ android:tint="@color/settingslib_materialColorOnSecondary" />
</LinearLayout>
-
-</com.google.android.material.card.MaterialCardView> \ No newline at end of file
+</LinearLayout> \ No newline at end of file
diff --git a/packages/SettingsLib/CardPreference/res/values/styles_expressive.xml b/packages/SettingsLib/CardPreference/res/values/styles_expressive.xml
index 287b13fa0d50..e7d4a0013896 100644
--- a/packages/SettingsLib/CardPreference/res/values/styles_expressive.xml
+++ b/packages/SettingsLib/CardPreference/res/values/styles_expressive.xml
@@ -18,11 +18,11 @@
<resources>
<style name="TextAppearance.CardTitle.SettingsLib"
parent="@style/TextAppearance.SettingsLib.TitleMedium.Emphasized">
- <item name="android:textColor">@color/settingslib_materialColorOnPrimary</item>
+ <item name="android:textColor">@color/settingslib_materialColorOnSecondaryContainer</item>
</style>
<style name="TextAppearance.CardSummary.SettingsLib"
parent="@style/TextAppearance.SettingsLib.LabelMedium">
- <item name="android:textColor">@color/settingslib_materialColorOnSecondary</item>
+ <item name="android:textColor">@color/settingslib_materialColorOnSecondaryContainer</item>
</style>
</resources> \ No newline at end of file
diff --git a/packages/SettingsLib/Metadata/processor/src/com/android/settingslib/metadata/PreferenceScreenAnnotationProcessor.kt b/packages/SettingsLib/Metadata/processor/src/com/android/settingslib/metadata/PreferenceScreenAnnotationProcessor.kt
index 14e3b87cf325..38b641336547 100644
--- a/packages/SettingsLib/Metadata/processor/src/com/android/settingslib/metadata/PreferenceScreenAnnotationProcessor.kt
+++ b/packages/SettingsLib/Metadata/processor/src/com/android/settingslib/metadata/PreferenceScreenAnnotationProcessor.kt
@@ -114,15 +114,7 @@ class PreferenceScreenAnnotationProcessor : AbstractProcessor() {
private fun generateCode(outputPkg: String, outputClass: String, outputFun: String) {
// sort by screen keys to make the output deterministic and naturally fit to FixedArrayMap
screens.sort()
- val javaFileObject =
- try {
- processingEnv.filer.createSourceFile("$outputPkg.$outputClass")
- } catch (e: Exception) {
- // quick fix: gradle runs this processor twice unexpectedly
- warn("cannot createSourceFile: $e")
- return
- }
- javaFileObject.openWriter().use {
+ processingEnv.filer.createSourceFile("$outputPkg.$outputClass").openWriter().use {
it.write("package $outputPkg;\n\n")
it.write("import $PACKAGE.FixedArrayMap;\n")
it.write("import $PACKAGE.FixedArrayMap.OrderedInitializer;\n")
diff --git a/packages/SettingsLib/SettingsTheme/res/values/strings.xml b/packages/SettingsLib/SettingsTheme/res/values/strings.xml
index c36dcb88b9fe..f3f077edc91d 100644
--- a/packages/SettingsLib/SettingsTheme/res/values/strings.xml
+++ b/packages/SettingsLib/SettingsTheme/res/values/strings.xml
@@ -21,4 +21,6 @@
<string name="settingslib_expressive_text_expand">Expand</string>
<!-- text of button to indicate user the textView is collapsable [CHAR LIMIT=NONE] -->
<string name="settingslib_expressive_text_collapse">Collapse</string>
+ <!-- Content description of the dismiss button for accessibility (not shown on the screen). [CHAR LIMIT=NONE] -->
+ <string name="settingslib_dismiss_button_content_description">Dismiss</string>
</resources> \ No newline at end of file
diff --git a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/framework/common/BytesFormatter.kt b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/framework/common/BytesFormatter.kt
index 5b7e2a86135a..e6cc8a80ee38 100644
--- a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/framework/common/BytesFormatter.kt
+++ b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/framework/common/BytesFormatter.kt
@@ -24,6 +24,7 @@ import android.icu.text.NumberFormat
import android.icu.text.UnicodeSet
import android.icu.text.UnicodeSetSpanner
import android.icu.util.Measure
+import android.text.BidiFormatter
import android.text.format.Formatter
import android.text.format.Formatter.RoundedBytesResult
import java.math.BigDecimal
@@ -40,11 +41,17 @@ class BytesFormatter(resources: Resources) {
constructor(context: Context) : this(context.resources)
private val locale = resources.configuration.locales[0]
+ private val bidiFormatter = BidiFormatter.getInstance(locale)
fun format(bytes: Long, useCase: UseCase): String {
val rounded = RoundedBytesResult.roundBytes(bytes, useCase.flag)
val numberFormatter = getNumberFormatter(rounded.fractionDigits)
- return numberFormatter.formatRoundedBytesResult(rounded)
+ val formattedString = numberFormatter.formatRoundedBytesResult(rounded)
+ return if (useCase == UseCase.FileSize) {
+ formattedString.bidiWrap()
+ } else {
+ formattedString
+ }
}
fun formatWithUnits(bytes: Long, useCase: UseCase): Result {
@@ -74,6 +81,14 @@ class BytesFormatter(resources: Resources) {
}
}
+ /** Wraps the source string in bidi formatting characters in RTL locales. */
+ private fun String.bidiWrap(): String =
+ if (bidiFormatter.isRtlContext) {
+ bidiFormatter.unicodeWrap(this)
+ } else {
+ this
+ }
+
private companion object {
fun String.removeFirst(removed: String): String =
SPACES_AND_CONTROLS.trim(replaceFirst(removed, "")).toString()
diff --git a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/model/app/AppStorageRepository.kt b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/model/app/AppStorageRepository.kt
new file mode 100644
index 000000000000..6fd470c1e7aa
--- /dev/null
+++ b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/model/app/AppStorageRepository.kt
@@ -0,0 +1,92 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settingslib.spaprivileged.model.app
+
+import android.content.Context
+import android.content.pm.ApplicationInfo
+import android.util.Log
+import com.android.settingslib.spaprivileged.framework.common.BytesFormatter
+import com.android.settingslib.spaprivileged.framework.common.storageStatsManager
+
+/** A repository interface for accessing and formatting app storage information. */
+interface AppStorageRepository {
+ /**
+ * Formats the size of an application into a human-readable string.
+ *
+ * This function retrieves the total size of the application, including APK file and its
+ * associated data.
+ *
+ * This function takes an [ApplicationInfo] object as input and returns a formatted string
+ * representing the size of the application. The size is formatted in units like kB, MB, GB,
+ * etc.
+ *
+ * @param app The [ApplicationInfo] object representing the application.
+ * @return A formatted string representing the size of the application.
+ */
+ fun formatSize(app: ApplicationInfo): String
+
+ /**
+ * Formats the size about an application into a human-readable string.
+ *
+ * @param sizeBytes The size in bytes to format.
+ * @return A formatted string representing the size about application.
+ */
+ fun formatSizeBytes(sizeBytes: Long): String
+
+ /**
+ * Calculates the size of an application in bytes.
+ *
+ * This function retrieves the total size of the application, including APK file and its
+ * associated data.
+ *
+ * @param app The [ApplicationInfo] object representing the application.
+ * @return The total size of the application in bytes, or null if the size could not be
+ * determined.
+ */
+ fun calculateSizeBytes(app: ApplicationInfo): Long?
+}
+
+class AppStorageRepositoryImpl(context: Context) : AppStorageRepository {
+ private val storageStatsManager = context.storageStatsManager
+ private val bytesFormatter = BytesFormatter(context)
+
+ override fun formatSize(app: ApplicationInfo): String {
+ val sizeBytes = calculateSizeBytes(app)
+ return if (sizeBytes != null) formatSizeBytes(sizeBytes) else ""
+ }
+
+ override fun formatSizeBytes(sizeBytes: Long): String =
+ bytesFormatter.format(sizeBytes, BytesFormatter.UseCase.FileSize)
+
+ override fun calculateSizeBytes(app: ApplicationInfo): Long? =
+ try {
+ val stats =
+ storageStatsManager.queryStatsForPackage(
+ app.storageUuid,
+ app.packageName,
+ app.userHandle,
+ )
+ stats.codeBytes + stats.dataBytes
+ } catch (e: Exception) {
+ Log.w(TAG, "Failed to query stats", e)
+ null
+ }
+
+ companion object {
+ private const val TAG = "AppStorageRepository"
+ }
+}
diff --git a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/AppStorageSize.kt b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/AppStorageSize.kt
index 7a4f81cc1321..7c98e9cd813b 100644
--- a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/AppStorageSize.kt
+++ b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/AppStorageSize.kt
@@ -16,42 +16,30 @@
package com.android.settingslib.spaprivileged.template.app
-import android.content.Context
import android.content.pm.ApplicationInfo
-import android.text.format.Formatter
-import android.util.Log
+import androidx.annotation.VisibleForTesting
import androidx.compose.runtime.Composable
import androidx.compose.runtime.State
import androidx.compose.runtime.remember
-import androidx.compose.ui.platform.LocalContext
import androidx.lifecycle.compose.collectAsStateWithLifecycle
-import com.android.settingslib.spaprivileged.framework.common.storageStatsManager
+import com.android.settingslib.spa.framework.compose.rememberContext
import com.android.settingslib.spaprivileged.framework.compose.placeholder
-import com.android.settingslib.spaprivileged.model.app.userHandle
+import com.android.settingslib.spaprivileged.model.app.AppStorageRepository
+import com.android.settingslib.spaprivileged.model.app.AppStorageRepositoryImpl
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.flow
import kotlinx.coroutines.flow.flowOn
-private const val TAG = "AppStorageSize"
-
@Composable
-fun ApplicationInfo.getStorageSize(): State<String> {
- val context = LocalContext.current
- return remember(this) {
- flow {
- val sizeBytes = calculateSizeBytes(context)
- this.emit(if (sizeBytes != null) Formatter.formatFileSize(context, sizeBytes) else "")
- }.flowOn(Dispatchers.IO)
- }.collectAsStateWithLifecycle(initialValue = placeholder())
-}
+fun ApplicationInfo.getStorageSize(): State<String> =
+ getStorageSize(rememberContext(::AppStorageRepositoryImpl))
-fun ApplicationInfo.calculateSizeBytes(context: Context): Long? {
- val storageStatsManager = context.storageStatsManager
- return try {
- val stats = storageStatsManager.queryStatsForPackage(storageUuid, packageName, userHandle)
- stats.codeBytes + stats.dataBytes
- } catch (e: Exception) {
- Log.w(TAG, "Failed to query stats: $e")
- null
- }
+@VisibleForTesting
+@Composable
+fun ApplicationInfo.getStorageSize(appStorageRepository: AppStorageRepository): State<String> {
+ val app = this
+ return remember(app) {
+ flow { emit(appStorageRepository.formatSize(app)) }.flowOn(Dispatchers.Default)
+ }
+ .collectAsStateWithLifecycle(initialValue = placeholder())
}
diff --git a/packages/SettingsLib/SpaPrivileged/tests/src/com/android/settingslib/spaprivileged/model/app/AppStorageRepositoryTest.kt b/packages/SettingsLib/SpaPrivileged/tests/src/com/android/settingslib/spaprivileged/model/app/AppStorageRepositoryTest.kt
new file mode 100644
index 000000000000..e8ec974bb0b8
--- /dev/null
+++ b/packages/SettingsLib/SpaPrivileged/tests/src/com/android/settingslib/spaprivileged/model/app/AppStorageRepositoryTest.kt
@@ -0,0 +1,94 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settingslib.spaprivileged.model.app
+
+import android.app.usage.StorageStats
+import android.app.usage.StorageStatsManager
+import android.content.Context
+import android.content.pm.ApplicationInfo
+import android.content.pm.PackageManager.NameNotFoundException
+import androidx.test.core.app.ApplicationProvider
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import com.android.settingslib.spaprivileged.framework.common.storageStatsManager
+import com.google.common.truth.Truth.assertThat
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.kotlin.doReturn
+import org.mockito.kotlin.doThrow
+import org.mockito.kotlin.mock
+import org.mockito.kotlin.spy
+import org.mockito.kotlin.stub
+import java.util.UUID
+
+@RunWith(AndroidJUnit4::class)
+class AppStorageRepositoryTest {
+ private val app = ApplicationInfo().apply { storageUuid = UUID.randomUUID() }
+
+ private val mockStorageStatsManager =
+ mock<StorageStatsManager> {
+ on { queryStatsForPackage(app.storageUuid, app.packageName, app.userHandle) } doReturn
+ STATS
+ }
+
+ private val context: Context =
+ spy(ApplicationProvider.getApplicationContext()) {
+ on { storageStatsManager } doReturn mockStorageStatsManager
+ }
+
+ private val repository = AppStorageRepositoryImpl(context)
+
+ @Test
+ fun calculateSizeBytes() {
+ val sizeBytes = repository.calculateSizeBytes(app)
+
+ assertThat(sizeBytes).isEqualTo(120)
+ }
+
+ @Test
+ fun formatSize() {
+ val fileSize = repository.formatSize(app)
+
+ assertThat(fileSize).isEqualTo("120 byte")
+ }
+
+ @Test
+ fun formatSize_throwException() {
+ mockStorageStatsManager.stub {
+ on { queryStatsForPackage(app.storageUuid, app.packageName, app.userHandle) } doThrow
+ NameNotFoundException()
+ }
+
+ val fileSize = repository.formatSize(app)
+
+ assertThat(fileSize).isEqualTo("")
+ }
+
+ @Test
+ fun formatSizeBytes() {
+ val fileSize = repository.formatSizeBytes(120)
+
+ assertThat(fileSize).isEqualTo("120 byte")
+ }
+
+ companion object {
+ private val STATS =
+ StorageStats().apply {
+ codeBytes = 100
+ dataBytes = 20
+ }
+ }
+}
diff --git a/packages/SettingsLib/SpaPrivileged/tests/src/com/android/settingslib/spaprivileged/template/app/AppStorageSizeTest.kt b/packages/SettingsLib/SpaPrivileged/tests/src/com/android/settingslib/spaprivileged/template/app/AppStorageSizeTest.kt
index 60f3d0ce1be3..4f42c8254c39 100644
--- a/packages/SettingsLib/SpaPrivileged/tests/src/com/android/settingslib/spaprivileged/template/app/AppStorageSizeTest.kt
+++ b/packages/SettingsLib/SpaPrivileged/tests/src/com/android/settingslib/spaprivileged/template/app/AppStorageSizeTest.kt
@@ -16,98 +16,37 @@
package com.android.settingslib.spaprivileged.template.app
-import android.app.usage.StorageStats
-import android.app.usage.StorageStatsManager
-import android.content.Context
import android.content.pm.ApplicationInfo
-import android.content.pm.PackageManager.NameNotFoundException
-import androidx.compose.runtime.CompositionLocalProvider
-import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.test.junit4.createComposeRule
-import androidx.test.core.app.ApplicationProvider
import androidx.test.ext.junit.runners.AndroidJUnit4
import com.android.settingslib.spa.framework.compose.stateOf
-import com.android.settingslib.spaprivileged.framework.common.storageStatsManager
-import com.android.settingslib.spaprivileged.model.app.userHandle
-import java.util.UUID
-import org.junit.Before
+import com.android.settingslib.spaprivileged.model.app.AppStorageRepository
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
-import org.mockito.Mock
-import org.mockito.Spy
-import org.mockito.junit.MockitoJUnit
-import org.mockito.junit.MockitoRule
-import org.mockito.kotlin.whenever
+import org.mockito.kotlin.doReturn
+import org.mockito.kotlin.mock
+import java.util.UUID
@RunWith(AndroidJUnit4::class)
class AppStorageSizeTest {
- @get:Rule
- val mockito: MockitoRule = MockitoJUnit.rule()
-
- @get:Rule
- val composeTestRule = createComposeRule()
+ @get:Rule val composeTestRule = createComposeRule()
- @Spy
- private val context: Context = ApplicationProvider.getApplicationContext()
-
- @Mock
- private lateinit var storageStatsManager: StorageStatsManager
-
- private val app = ApplicationInfo().apply {
- storageUuid = UUID.randomUUID()
- }
+ private val app = ApplicationInfo().apply { storageUuid = UUID.randomUUID() }
- @Before
- fun setUp() {
- whenever(context.storageStatsManager).thenReturn(storageStatsManager)
- whenever(
- storageStatsManager.queryStatsForPackage(
- app.storageUuid,
- app.packageName,
- app.userHandle,
- )
- ).thenReturn(STATS)
- }
+ private val mockAppStorageRepository =
+ mock<AppStorageRepository> { on { formatSize(app) } doReturn SIZE }
@Test
fun getStorageSize() {
var storageSize = stateOf("")
- composeTestRule.setContent {
- CompositionLocalProvider(LocalContext provides context) {
- storageSize = app.getStorageSize()
- }
- }
-
- composeTestRule.waitUntil { storageSize.value == "120 B" }
- }
-
- @Test
- fun getStorageSize_throwException() {
- var storageSize = stateOf("Computing")
- whenever(
- storageStatsManager.queryStatsForPackage(
- app.storageUuid,
- app.packageName,
- app.userHandle,
- )
- ).thenThrow(NameNotFoundException())
-
- composeTestRule.setContent {
- CompositionLocalProvider(LocalContext provides context) {
- storageSize = app.getStorageSize()
- }
- }
+ composeTestRule.setContent { storageSize = app.getStorageSize(mockAppStorageRepository) }
- composeTestRule.waitUntil { storageSize.value == "" }
+ composeTestRule.waitUntil { storageSize.value == SIZE }
}
- companion object {
- private val STATS = StorageStats().apply {
- codeBytes = 100
- dataBytes = 20
- cacheBytes = 3
- }
+ private companion object {
+ const val SIZE = "120 kB"
}
}
diff --git a/packages/SettingsLib/aconfig/settingslib.aconfig b/packages/SettingsLib/aconfig/settingslib.aconfig
index bbe08f254283..d94450b1cabd 100644
--- a/packages/SettingsLib/aconfig/settingslib.aconfig
+++ b/packages/SettingsLib/aconfig/settingslib.aconfig
@@ -219,3 +219,13 @@ flag {
purpose: PURPOSE_BUGFIX
}
}
+
+flag {
+ name: "adopt_primary_group_management_api"
+ namespace: "cross_device_experiences"
+ description: "Adopt Bluetooth LE broadcast primary group management APIs"
+ bug: "381946931"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
diff --git a/packages/SettingsLib/search/processor-src/com/android/settingslib/search/IndexableProcessor.java b/packages/SettingsLib/search/processor-src/com/android/settingslib/search/IndexableProcessor.java
index fa43915deb6a..5b25d0dc04e1 100644
--- a/packages/SettingsLib/search/processor-src/com/android/settingslib/search/IndexableProcessor.java
+++ b/packages/SettingsLib/search/processor-src/com/android/settingslib/search/IndexableProcessor.java
@@ -35,7 +35,6 @@ import javax.annotation.processing.ProcessingEnvironment;
import javax.annotation.processing.RoundEnvironment;
import javax.annotation.processing.SupportedAnnotationTypes;
import javax.annotation.processing.SupportedOptions;
-import javax.annotation.processing.SupportedSourceVersion;
import javax.lang.model.SourceVersion;
import javax.lang.model.element.Element;
import javax.lang.model.element.Modifier;
@@ -48,7 +47,6 @@ import javax.tools.Diagnostic.Kind;
* Annotation processor for {@link SearchIndexable} that generates {@link SearchIndexableResources}
* subclasses.
*/
-@SupportedSourceVersion(SourceVersion.RELEASE_17)
@SupportedOptions(IndexableProcessor.PACKAGE_KEY)
@SupportedAnnotationTypes({"com.android.settingslib.search.SearchIndexable"})
public class IndexableProcessor extends AbstractProcessor {
@@ -69,6 +67,11 @@ public class IndexableProcessor extends AbstractProcessor {
private boolean mRanOnce;
@Override
+ public SourceVersion getSupportedSourceVersion() {
+ return SourceVersion.latestSupported();
+ }
+
+ @Override
public boolean process(Set<? extends TypeElement> annotations,
RoundEnvironment roundEnvironment) {
if (mRanOnce) {
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CsipDeviceManager.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CsipDeviceManager.java
index 4b7cb36f2753..bf86911ee683 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CsipDeviceManager.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CsipDeviceManager.java
@@ -134,6 +134,8 @@ public class CsipDeviceManager {
// Do nothing if GroupId has been assigned
if (!isValidGroupId(cachedDevice.getGroupId())) {
final int newGroupId = getBaseGroupId(cachedDevice.getDevice());
+ log("updateCsipDevices: propose new group id " + newGroupId + " for device "
+ + cachedDevice.getDevice());
// Do nothing if there is no GroupId on Bluetooth device
if (isValidGroupId(newGroupId)) {
cachedDevice.setGroupId(newGroupId);
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothProfileManager.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothProfileManager.java
index 7c24df9e9019..ff5e9e657213 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothProfileManager.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothProfileManager.java
@@ -358,6 +358,9 @@ public class LocalBluetoothProfileManager {
&& mProfile instanceof CsipSetCoordinatorProfile;
if (isAshaProfile && (newState == BluetoothProfile.STATE_CONNECTED)) {
+ if (DEBUG) {
+ Log.d(TAG, "onReceive, hearing aid profile connected, check hisyncid");
+ }
// Check if the HiSyncID has being initialized
if (cachedDevice.getHiSyncId() == BluetoothHearingAid.HI_SYNC_ID_INVALID) {
long newHiSyncId = getHearingAidProfile().getHiSyncId(cachedDevice.getDevice());
@@ -375,7 +378,9 @@ public class LocalBluetoothProfileManager {
}
if (isHapClientOrLeAudioProfile && newState == BluetoothProfile.STATE_CONNECTED) {
-
+ if (DEBUG) {
+ Log.d(TAG, "onReceive, hap/lea profile connected, check hearing aid info");
+ }
// Checks if both profiles are connected to the device. Hearing aid info need
// to be retrieved from these profiles separately.
if (cachedDevice.isConnectedLeAudioHearingAidDevice()) {
@@ -389,10 +394,16 @@ public class LocalBluetoothProfileManager {
}
if (isCsipProfile && (newState == BluetoothProfile.STATE_CONNECTED)) {
+ if (DEBUG) {
+ Log.d(TAG, "onReceive, csip profile connected, check group id");
+ }
// Check if the GroupID has being initialized
if (cachedDevice.getGroupId() == BluetoothCsipSetCoordinator.GROUP_ID_INVALID) {
final Map<Integer, ParcelUuid> groupIdMap = getCsipSetCoordinatorProfile()
.getGroupUuidMapByDevice(cachedDevice.getDevice());
+ if (DEBUG) {
+ Log.d(TAG, "csip group uuid map = " + groupIdMap);
+ }
if (groupIdMap != null) {
for (Map.Entry<Integer, ParcelUuid> entry: groupIdMap.entrySet()) {
if (entry.getValue().equals(BluetoothUuid.CAP)) {
@@ -431,6 +442,9 @@ public class LocalBluetoothProfileManager {
mProfile.getProfileId());
}
if (needDispatchProfileConnectionState) {
+ if (DEBUG) {
+ Log.d(TAG, "needDispatchProfileConnectionState");
+ }
cachedDevice.refresh();
mEventManager.dispatchProfileConnectionStateChanged(cachedDevice, newState,
mProfile.getProfileId());
diff --git a/packages/SettingsLib/src/com/android/settingslib/notification/data/repository/FakeZenModeRepository.kt b/packages/SettingsLib/src/com/android/settingslib/notification/data/repository/FakeZenModeRepository.kt
index 9aaefe47fda2..58c7907f77de 100644
--- a/packages/SettingsLib/src/com/android/settingslib/notification/data/repository/FakeZenModeRepository.kt
+++ b/packages/SettingsLib/src/com/android/settingslib/notification/data/repository/FakeZenModeRepository.kt
@@ -37,8 +37,9 @@ class FakeZenModeRepository : ZenModeRepository {
override val globalZenMode: StateFlow<Int>
get() = mutableZenMode.asStateFlow()
- private val mutableModesFlow: MutableStateFlow<List<ZenMode>> =
+ private val mutableModesFlow: MutableStateFlow<List<ZenMode>> by lazy {
MutableStateFlow(listOf(TestModeBuilder.MANUAL_DND))
+ }
override val modes: Flow<List<ZenMode>>
get() = mutableModesFlow.asStateFlow()
diff --git a/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java b/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java
index b0309a8fa5a5..6681c014f2e0 100644
--- a/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java
+++ b/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java
@@ -454,5 +454,6 @@ public class SecureSettingsValidators {
VALIDATORS.put(Secure.MANDATORY_BIOMETRICS_REQUIREMENTS_SATISFIED,
new InclusiveIntegerRangeValidator(0, 1));
VALIDATORS.put(Secure.ADVANCED_PROTECTION_MODE, BOOLEAN_VALIDATOR);
+ VALIDATORS.put(Secure.DISABLE_ADAPTIVE_AUTH_LIMIT_LOCK, BOOLEAN_VALIDATOR);
}
}
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
index cb656bdd5d54..9b75a47d92ac 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
@@ -1869,7 +1869,7 @@ public class SettingsProvider extends ContentProvider {
}
case MUTATION_OPERATION_RESET -> {
return mSettingsRegistry.resetSettingsLocked(SETTINGS_TYPE_SECURE,
- UserHandle.USER_SYSTEM, callingPackage, mode, tag);
+ owningUserId, callingPackage, mode, tag);
}
}
}
diff --git a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
index cbdb36fff98c..9aad5d5f8367 100644
--- a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
+++ b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
@@ -689,6 +689,7 @@ public class SettingsBackupTest {
Settings.Secure.DEFAULT_DEVICE_INPUT_METHOD,
Settings.Secure.DEVICE_PAIRED,
Settings.Secure.DIALER_DEFAULT_APPLICATION,
+ Settings.Secure.DISABLE_ADAPTIVE_AUTH_LIMIT_LOCK,
Settings.Secure.DISABLED_PRINT_SERVICES,
Settings.Secure.DISABLE_SECURE_WINDOWS,
Settings.Secure.DISABLED_SYSTEM_INPUT_METHODS,
diff --git a/packages/SystemUI/Android.bp b/packages/SystemUI/Android.bp
index 6b2449fdaa49..a935aacacf95 100644
--- a/packages/SystemUI/Android.bp
+++ b/packages/SystemUI/Android.bp
@@ -423,6 +423,7 @@ android_library {
],
manifest: "AndroidManifest-res.xml",
flags_packages: [
+ "android.app.flags-aconfig",
"com_android_systemui_flags",
],
}
@@ -519,6 +520,7 @@ android_library {
"androidx.activity_activity-compose",
"androidx.compose.animation_animation-graphics",
"androidx.lifecycle_lifecycle-viewmodel-compose",
+ "kairos",
],
libs: [
"keepanno-annotations",
@@ -739,6 +741,7 @@ android_library {
"PlatformMotionTesting",
"SystemUICustomizationTestUtils",
"androidx.compose.runtime_runtime",
+ "kairos",
"kosmos",
"testables",
"androidx.test.rules",
diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml
index 51ea5298fbb8..0075f85af8ed 100644
--- a/packages/SystemUI/AndroidManifest.xml
+++ b/packages/SystemUI/AndroidManifest.xml
@@ -547,6 +547,12 @@
android:permission="android.permission.BIND_WALLPAPER"
android:exported="true" />
+ <service android:name=".wallpapers.GradientColorWallpaper"
+ android:featureFlag="android.app.enable_connected_displays_wallpaper"
+ android:singleUser="true"
+ android:permission="android.permission.BIND_WALLPAPER"
+ android:exported="true" />
+
<activity android:name=".tuner.TunerActivity"
android:enabled="false"
android:icon="@drawable/tuner"
diff --git a/packages/SystemUI/accessibility/accessibilitymenu/AndroidManifest.xml b/packages/SystemUI/accessibility/accessibilitymenu/AndroidManifest.xml
index 0f210e7e5e7b..b40a11469172 100644
--- a/packages/SystemUI/accessibility/accessibilitymenu/AndroidManifest.xml
+++ b/packages/SystemUI/accessibility/accessibilitymenu/AndroidManifest.xml
@@ -20,7 +20,10 @@
<uses-permission android:name="android.permission.CONTROL_DISPLAY_BRIGHTNESS"/>
<uses-permission android:name="android.permission.MANAGE_USERS"/>
- <application android:supportsRtl="true">
+ <application
+ android:supportsRtl="true"
+ android:allowBackup="true"
+ android:restoreAnyVersion="true">
<service
android:name="com.android.systemui.accessibility.accessibilitymenu.AccessibilityMenuService"
android:exported="false"
diff --git a/packages/SystemUI/aconfig/Android.bp b/packages/SystemUI/aconfig/Android.bp
index 1858c80ca901..088ec136f24e 100644
--- a/packages/SystemUI/aconfig/Android.bp
+++ b/packages/SystemUI/aconfig/Android.bp
@@ -23,6 +23,7 @@ package {
default_team: "trendy_team_system_ui_please_use_a_more_specific_subteam_if_possible_",
default_visibility: [
"//visibility:override",
+ "//frameworks/base/libs/WindowManager/Shell:__subpackages__",
"//frameworks/base/packages/SystemUI:__subpackages__",
"//frameworks/libs/systemui/tracinglib:__subpackages__",
"//frameworks/base/services/accessibility:__subpackages__",
diff --git a/packages/SystemUI/aconfig/accessibility.aconfig b/packages/SystemUI/aconfig/accessibility.aconfig
index b5eba08c8f87..fb21be4c3bd1 100644
--- a/packages/SystemUI/aconfig/accessibility.aconfig
+++ b/packages/SystemUI/aconfig/accessibility.aconfig
@@ -38,6 +38,16 @@ flag {
}
flag {
+ name: "floating_menu_display_cutout_support"
+ namespace: "accessibility"
+ description: "Makes FAB properly react to and avoid DisplayCutouts."
+ bug: "384399408"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
+
+flag {
name: "floating_menu_drag_to_hide"
namespace: "accessibility"
description: "Allows users to hide the FAB then use notification to dismiss or bring it back."
@@ -125,3 +135,13 @@ flag {
description: "Update hearing device icon in floating menu according to the connection status."
bug: "357882387"
}
+
+flag {
+ name: "floating_menu_notify_targets_changed_on_strict_diff"
+ namespace: "accessibility"
+ description: "Only notify listeners that the list of accessibility targets has changed if the lists are not identical."
+ bug: "376473165"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
diff --git a/packages/SystemUI/aconfig/systemui.aconfig b/packages/SystemUI/aconfig/systemui.aconfig
index a0d7fd28f5f8..153f89284587 100644
--- a/packages/SystemUI/aconfig/systemui.aconfig
+++ b/packages/SystemUI/aconfig/systemui.aconfig
@@ -382,6 +382,13 @@ flag {
}
flag {
+ name: "status_bar_mobile_icon_kairos"
+ namespace: "systemui"
+ description: "Refactors the mobile connection icon in the status bar to use the Kairos library"
+ bug: "383172066"
+}
+
+flag {
name: "status_bar_monochrome_icons_fix"
namespace: "systemui"
description: "Fixes the status bar icon size when drawing InsetDrawables (ie. monochrome icons)"
@@ -1912,3 +1919,13 @@ flag {
description: "Special UI treatment for magic actions"
bug: "383567383"
}
+
+flag {
+ name: "show_audio_sharing_slider_in_volume_panel"
+ namespace: "cross_device_experiences"
+ description: "Show two sliders in volume panel when audio sharing."
+ bug: "336183611"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
diff --git a/packages/SystemUI/animation/lib/src/com/android/systemui/animation/ViewUIComponent.java b/packages/SystemUI/animation/lib/src/com/android/systemui/animation/ViewUIComponent.java
index 2e8f92839fa6..4b8610884b05 100644
--- a/packages/SystemUI/animation/lib/src/com/android/systemui/animation/ViewUIComponent.java
+++ b/packages/SystemUI/animation/lib/src/com/android/systemui/animation/ViewUIComponent.java
@@ -185,16 +185,24 @@ public class ViewUIComponent implements UIComponent {
return;
}
ViewGroup.LayoutParams params = mView.getLayoutParams();
- if (params == null || params.width == 0 || params.height == 0) {
+ if (params == null) {
// layout pass didn't happen.
logD("draw: skipped - no layout");
return;
}
+
+ final Rect realBounds = getRealBounds();
+ if (realBounds.width() == 0 || realBounds.height() == 0) {
+ // bad bounds.
+ logD("draw: skipped - zero bounds");
+ return;
+ }
+
+
Canvas canvas = mSurface.lockHardwareCanvas();
// Clear the canvas first.
canvas.drawColor(0, PorterDuff.Mode.CLEAR);
if (mVisibleOverride) {
- Rect realBounds = getRealBounds();
Rect renderBounds = getBounds();
canvas.translate(renderBounds.left, renderBounds.top);
canvas.scale(
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/BouncerContent.kt b/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/BouncerContent.kt
index 0054a4c899ec..439968590dad 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/BouncerContent.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/BouncerContent.kt
@@ -168,7 +168,7 @@ private fun StandardLayout(viewModel: BouncerSceneContentViewModel, modifier: Mo
LocalWindowSizeClass.current.heightSizeClass == WindowHeightSizeClass.Expanded
FoldAware(
- modifier = modifier.padding(start = 32.dp, top = 92.dp, end = 32.dp, bottom = 48.dp),
+ modifier = modifier.padding(top = 92.dp, bottom = 48.dp),
viewModel = viewModel,
aboveFold = {
Column(
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/PatternBouncer.kt b/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/PatternBouncer.kt
index 8321238b28b1..3d0354a578f7 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/PatternBouncer.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/PatternBouncer.kt
@@ -24,6 +24,8 @@ import androidx.compose.foundation.Canvas
import androidx.compose.foundation.gestures.awaitEachGesture
import androidx.compose.foundation.gestures.awaitFirstDown
import androidx.compose.foundation.gestures.detectDragGestures
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.width
import androidx.compose.material3.MaterialTheme
@@ -35,6 +37,7 @@ import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.runtime.setValue
+import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clipToBounds
import androidx.compose.ui.geometry.Offset
@@ -45,6 +48,7 @@ import androidx.compose.ui.layout.onGloballyPositioned
import androidx.compose.ui.platform.LocalDensity
import androidx.compose.ui.platform.LocalView
import androidx.compose.ui.res.integerResource
+import androidx.compose.ui.unit.DpSize
import androidx.compose.ui.unit.dp
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import com.android.compose.animation.Easings
@@ -212,23 +216,27 @@ fun PatternBouncer(
var gridCoordinates: LayoutCoordinates? by remember { mutableStateOf(null) }
var offset: Offset by remember { mutableStateOf(Offset.Zero) }
var scale: Float by remember { mutableStateOf(1f) }
+ // This is the size of the drawing area, in dips.
+ val dotDrawingArea =
+ remember(colCount, rowCount) {
+ DpSize(
+ // Because the width also includes spacing to the left and right of the leftmost and
+ // rightmost dots in the grid and because UX mocks specify the width without that
+ // spacing, the actual width needs to be defined slightly bigger than the UX mock
+ // width.
+ width = (262 * colCount / 2).dp,
+ // Because the height also includes spacing above and below the topmost and
+ // bottommost
+ // dots in the grid and because UX mocks specify the height without that spacing,
+ // the
+ // actual height needs to be defined slightly bigger than the UX mock height.
+ height = (262 * rowCount / 2).dp,
+ )
+ }
- Canvas(
- modifier
- .sysuiResTag("bouncer_pattern_root")
- // Because the width also includes spacing to the left and right of the leftmost and
- // rightmost dots in the grid and because UX mocks specify the width without that
- // spacing, the actual width needs to be defined slightly bigger than the UX mock width.
- .width((262 * colCount / 2).dp)
- // Because the height also includes spacing above and below the topmost and bottommost
- // dots in the grid and because UX mocks specify the height without that spacing, the
- // actual height needs to be defined slightly bigger than the UX mock height.
- .height((262 * rowCount / 2).dp)
- // Need to clip to bounds to make sure that the lines don't follow the input pointer
- // when it leaves the bounds of the dot grid.
- .clipToBounds()
- .onGloballyPositioned { coordinates -> gridCoordinates = coordinates }
- .thenIf(isInputEnabled) {
+ Box(
+ modifier =
+ modifier.fillMaxWidth().thenIf(isInputEnabled) {
Modifier.pointerInput(Unit) {
awaitEachGesture {
awaitFirstDown()
@@ -257,105 +265,125 @@ fun PatternBouncer(
inputPosition = change.position
change.position.minus(offset).div(scale).let {
viewModel.onDrag(
- xPx = it.x,
+ xPx =
+ it.x -
+ ((size.width - dotDrawingArea.width.roundToPx()) / 2),
yPx = it.y,
- containerSizePx = size.width,
+ containerSizePx = dotDrawingArea.width.roundToPx(),
)
}
}
}
}
- .motionTestValues {
- entryAnimationCompleted exportAs entryCompleted
- dotAppearFadeInAnimatables.map { it.value.value } exportAs dotAppearFadeIn
- dotAppearMoveUpAnimatables.map { it.value.value } exportAs dotAppearMoveUp
- dotScalingAnimatables.map { it.value.value } exportAs dotScaling
- }
) {
- gridCoordinates?.let { nonNullCoordinates ->
- val containerSize = nonNullCoordinates.size
- if (containerSize.width <= 0 || containerSize.height <= 0) {
- return@let
- }
+ Canvas(
+ Modifier.sysuiResTag("bouncer_pattern_root")
+ .width(dotDrawingArea.width)
+ .height(dotDrawingArea.height)
+ // Need to clip to bounds to make sure that the lines don't follow the input pointer
+ // when it leaves the bounds of the dot grid.
+ .clipToBounds()
+ .align(Alignment.Center)
+ .onGloballyPositioned { coordinates -> gridCoordinates = coordinates }
+ .motionTestValues {
+ entryAnimationCompleted exportAs entryCompleted
+ dotAppearFadeInAnimatables.map { it.value.value } exportAs dotAppearFadeIn
+ dotAppearMoveUpAnimatables.map { it.value.value } exportAs dotAppearMoveUp
+ dotScalingAnimatables.map { it.value.value } exportAs dotScaling
+ }
+ ) {
+ gridCoordinates?.let { nonNullCoordinates ->
+ val containerSize = nonNullCoordinates.size
+ if (containerSize.width <= 0 || containerSize.height <= 0) {
+ return@let
+ }
- val horizontalSpacing = containerSize.width.toFloat() / colCount
- val verticalSpacing = containerSize.height.toFloat() / rowCount
- val spacing = min(horizontalSpacing, verticalSpacing)
- val horizontalOffset =
- offset(
- availableSize = containerSize.width,
- spacingPerDot = spacing,
- dotCount = colCount,
- isCentered = true,
- )
- val verticalOffset =
- offset(
- availableSize = containerSize.height,
- spacingPerDot = spacing,
- dotCount = rowCount,
- isCentered = centerDotsVertically,
- )
- offset = Offset(horizontalOffset, verticalOffset)
- scale = (colCount * spacing) / containerSize.width
+ val horizontalSpacing = containerSize.width.toFloat() / colCount
+ val verticalSpacing = containerSize.height.toFloat() / rowCount
+ val spacing = min(horizontalSpacing, verticalSpacing)
+ val horizontalOffset =
+ offset(
+ availableSize = containerSize.width,
+ spacingPerDot = spacing,
+ dotCount = colCount,
+ isCentered = true,
+ )
+ val verticalOffset =
+ offset(
+ availableSize = containerSize.height,
+ spacingPerDot = spacing,
+ dotCount = rowCount,
+ isCentered = centerDotsVertically,
+ )
+ offset = Offset(horizontalOffset, verticalOffset)
+ scale = (colCount * spacing) / containerSize.width
- if (isAnimationEnabled) {
- // Draw lines between dots.
- selectedDots.forEachIndexed { index, dot ->
- if (index > 0) {
- val previousDot = selectedDots[index - 1]
- val lineFadeOutAnimationProgress =
- lineFadeOutAnimatables[previousDot]!!.value
- val startLerp = 1 - lineFadeOutAnimationProgress
- val from =
- pixelOffset(previousDot, spacing, horizontalOffset, verticalOffset)
- val to = pixelOffset(dot, spacing, horizontalOffset, verticalOffset)
- val lerpedFrom =
- Offset(
- x = from.x + (to.x - from.x) * startLerp,
- y = from.y + (to.y - from.y) * startLerp,
+ if (isAnimationEnabled) {
+ // Draw lines between dots.
+ selectedDots.forEachIndexed { index, dot ->
+ if (index > 0) {
+ val previousDot = selectedDots[index - 1]
+ val lineFadeOutAnimationProgress =
+ lineFadeOutAnimatables[previousDot]!!.value
+ val startLerp = 1 - lineFadeOutAnimationProgress
+ val from =
+ pixelOffset(previousDot, spacing, horizontalOffset, verticalOffset)
+ val to = pixelOffset(dot, spacing, horizontalOffset, verticalOffset)
+ val lerpedFrom =
+ Offset(
+ x = from.x + (to.x - from.x) * startLerp,
+ y = from.y + (to.y - from.y) * startLerp,
+ )
+ drawLine(
+ start = lerpedFrom,
+ end = to,
+ cap = StrokeCap.Round,
+ alpha = lineFadeOutAnimationProgress * lineAlpha(spacing),
+ color = lineColor,
+ strokeWidth = lineStrokeWidth,
)
- drawLine(
- start = lerpedFrom,
- end = to,
- cap = StrokeCap.Round,
- alpha = lineFadeOutAnimationProgress * lineAlpha(spacing),
- color = lineColor,
- strokeWidth = lineStrokeWidth,
- )
+ }
}
- }
- // Draw the line between the most recently-selected dot and the input pointer
- // position.
- inputPosition?.let { lineEnd ->
- currentDot?.let { dot ->
- val from = pixelOffset(dot, spacing, horizontalOffset, verticalOffset)
- val lineLength =
- sqrt((from.y - lineEnd.y).pow(2) + (from.x - lineEnd.x).pow(2))
- drawLine(
- start = from,
- end = lineEnd,
- cap = StrokeCap.Round,
- alpha = lineAlpha(spacing, lineLength),
- color = lineColor,
- strokeWidth = lineStrokeWidth,
- )
+ // Draw the line between the most recently-selected dot and the input pointer
+ // position.
+ inputPosition?.let { lineEnd ->
+ currentDot?.let { dot ->
+ val from = pixelOffset(dot, spacing, horizontalOffset, verticalOffset)
+ val lineLength =
+ sqrt((from.y - lineEnd.y).pow(2) + (from.x - lineEnd.x).pow(2))
+ drawLine(
+ start = from,
+ end = lineEnd,
+ cap = StrokeCap.Round,
+ alpha = lineAlpha(spacing, lineLength),
+ color = lineColor,
+ strokeWidth = lineStrokeWidth,
+ )
+ }
}
}
- }
- // Draw each dot on the grid.
- dots.forEach { dot ->
- val initialOffset = checkNotNull(dotAppearMaxOffsetPixels[dot])
- val appearOffset =
- (1 - checkNotNull(dotAppearMoveUpAnimatables[dot]).value) * initialOffset
- drawCircle(
- center =
- pixelOffset(dot, spacing, horizontalOffset, verticalOffset + appearOffset),
- color =
- dotColor.copy(alpha = checkNotNull(dotAppearFadeInAnimatables[dot]).value),
- radius = dotRadius * checkNotNull(dotScalingAnimatables[dot]).value,
- )
+ // Draw each dot on the grid.
+ dots.forEach { dot ->
+ val initialOffset = checkNotNull(dotAppearMaxOffsetPixels[dot])
+ val appearOffset =
+ (1 - checkNotNull(dotAppearMoveUpAnimatables[dot]).value) * initialOffset
+ drawCircle(
+ center =
+ pixelOffset(
+ dot,
+ spacing,
+ horizontalOffset,
+ verticalOffset + appearOffset,
+ ),
+ color =
+ dotColor.copy(
+ alpha = checkNotNull(dotAppearFadeInAnimatables[dot]).value
+ ),
+ radius = dotRadius * checkNotNull(dotScalingAnimatables[dot]).value,
+ )
+ }
}
}
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/floatingmenu/MenuListViewTouchHandlerTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/floatingmenu/MenuListViewTouchHandlerTest.java
index 56a97bb34172..fff6def52803 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/floatingmenu/MenuListViewTouchHandlerTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/floatingmenu/MenuListViewTouchHandlerTest.java
@@ -28,6 +28,7 @@ import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
+import android.graphics.PointF;
import android.platform.test.annotations.DisableFlags;
import android.platform.test.annotations.EnableFlags;
import android.testing.TestableLooper;
@@ -210,7 +211,7 @@ public class MenuListViewTouchHandlerTest extends SysuiTestCase {
mTouchHandler.onInterceptTouchEvent(mStubListView, stubMoveEvent);
mTouchHandler.onInterceptTouchEvent(mStubListView, stubUpEvent);
- verify(mMenuAnimationController).flingMenuThenSpringToEdge(anyFloat(), anyFloat(),
+ verify(mMenuAnimationController).flingMenuThenSpringToEdge(any(PointF.class), anyFloat(),
anyFloat());
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/floatingmenu/MenuViewTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/floatingmenu/MenuViewTest.java
index 5ff7bd063427..737170f6a665 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/floatingmenu/MenuViewTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/floatingmenu/MenuViewTest.java
@@ -18,17 +18,25 @@ package com.android.systemui.accessibility.floatingmenu;
import static android.app.UiModeManager.MODE_NIGHT_YES;
+import static com.android.internal.accessibility.AccessibilityShortcutController.MAGNIFICATION_COMPONENT_NAME;
+import static com.android.internal.accessibility.AccessibilityShortcutController.MAGNIFICATION_CONTROLLER_NAME;
+
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.Mockito.doNothing;
+import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import android.app.UiModeManager;
+import android.content.Context;
import android.content.res.Configuration;
import android.graphics.Rect;
import android.graphics.drawable.GradientDrawable;
+import android.platform.test.annotations.DisableFlags;
import android.platform.test.annotations.EnableFlags;
import android.testing.TestableLooper;
import android.view.WindowManager;
@@ -37,6 +45,8 @@ import android.view.accessibility.AccessibilityManager;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
+import com.android.internal.accessibility.common.ShortcutConstants;
+import com.android.internal.accessibility.dialog.AccessibilityTarget;
import com.android.settingslib.bluetooth.HearingAidDeviceManager;
import com.android.systemui.Flags;
import com.android.systemui.Prefs;
@@ -54,6 +64,9 @@ import org.mockito.Mock;
import org.mockito.junit.MockitoJUnit;
import org.mockito.junit.MockitoRule;
+import java.util.ArrayList;
+import java.util.List;
+
/** Tests for {@link MenuView}. */
@RunWith(AndroidJUnit4.class)
@TestableLooper.RunWithLooper(setAsMainLooper = true)
@@ -63,17 +76,24 @@ public class MenuViewTest extends SysuiTestCase {
private int mNightMode;
private UiModeManager mUiModeManager;
private MenuView mMenuView;
+ private MenuView mMenuViewSpy;
private String mLastPosition;
private MenuViewAppearance mStubMenuViewAppearance;
+ private MenuViewModel mMenuViewModel;
+ private final List<String> mShortcutTargets = new ArrayList<>();
@Rule
public MockitoRule mockito = MockitoJUnit.rule();
@Mock
private AccessibilityManager mAccessibilityManager;
+
@Mock
private HearingAidDeviceManager mHearingAidDeviceManager;
+ @Mock
+ private MenuView.OnTargetFeaturesChangeListener mOnTargetFeaturesChangeListener;
+
private SysuiTestableContext mSpyContext;
@Before
@@ -91,22 +111,38 @@ public class MenuViewTest extends SysuiTestCase {
mSpyContext = spy(mContext);
doNothing().when(mSpyContext).startActivity(any());
+ mContext.addMockSystemService(Context.ACCESSIBILITY_SERVICE, mAccessibilityManager);
+ mShortcutTargets.add(MAGNIFICATION_CONTROLLER_NAME);
+ doReturn(mShortcutTargets)
+ .when(mAccessibilityManager)
+ .getAccessibilityShortcutTargets(anyInt());
+
final SecureSettings secureSettings = TestUtils.mockSecureSettings(mContext);
- final MenuViewModel stubMenuViewModel = new MenuViewModel(mContext, mAccessibilityManager,
- secureSettings, mHearingAidDeviceManager);
+ mMenuViewModel =
+ new MenuViewModel(
+ mContext, mAccessibilityManager, secureSettings, mHearingAidDeviceManager);
final WindowManager stubWindowManager = mContext.getSystemService(WindowManager.class);
mStubMenuViewAppearance = new MenuViewAppearance(mSpyContext, stubWindowManager);
- mMenuView = spy(new MenuView(mSpyContext, stubMenuViewModel, mStubMenuViewAppearance,
- secureSettings));
+ mMenuView =
+ new MenuView(mSpyContext, mMenuViewModel, mStubMenuViewAppearance, secureSettings);
+ mMenuView.setOnTargetFeaturesChangeListener(mOnTargetFeaturesChangeListener);
mLastPosition = Prefs.getString(mSpyContext,
Prefs.Key.ACCESSIBILITY_FLOATING_MENU_POSITION, /* defaultValue= */ null);
+
+ mMenuViewSpy =
+ spy(
+ new MenuView(
+ mSpyContext,
+ mMenuViewModel,
+ mStubMenuViewAppearance,
+ secureSettings));
}
@Test
public void onConfigurationChanged_updateViewModel() {
- mMenuView.onConfigurationChanged(/* newConfig= */ null);
+ mMenuViewSpy.onConfigurationChanged(/* newConfig= */ null);
- verify(mMenuView).loadLayoutResources();
+ verify(mMenuViewSpy).loadLayoutResources();
}
@Test
@@ -179,6 +215,75 @@ public class MenuViewTest extends SysuiTestCase {
assertThat(radiiAnimator.isStarted()).isTrue();
}
+ @Test
+ @DisableFlags(Flags.FLAG_FLOATING_MENU_NOTIFY_TARGETS_CHANGED_ON_STRICT_DIFF)
+ public void onTargetFeaturesChanged_listenerCalled_flagDisabled() {
+ // Call show() to start observing the target features change listener.
+ mMenuView.show();
+
+ // The target features change listener should be called when the observer is added.
+ verify(mOnTargetFeaturesChangeListener, times(1)).onChange(any());
+
+ // When the target features list changes, the listener should be called.
+ mMenuViewModel.onTargetFeaturesChanged(
+ List.of(
+ new TestAccessibilityTarget(mContext, 123),
+ new TestAccessibilityTarget(mContext, 456)));
+ verify(mOnTargetFeaturesChangeListener, times(2)).onChange(any());
+
+ // Double check that when the target features list changes, the listener should be called.
+ List<AccessibilityTarget> newFeaturesList =
+ List.of(
+ new TestAccessibilityTarget(mContext, 123),
+ new TestAccessibilityTarget(mContext, 789),
+ new TestAccessibilityTarget(mContext, 456));
+ mMenuViewModel.onTargetFeaturesChanged(newFeaturesList);
+ verify(mOnTargetFeaturesChangeListener, times(3)).onChange(any());
+
+ // When the target features list doesn't change, the listener will still be called.
+ mMenuViewModel.onTargetFeaturesChanged(newFeaturesList);
+ verify(mOnTargetFeaturesChangeListener, times(4)).onChange(any());
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_FLOATING_MENU_NOTIFY_TARGETS_CHANGED_ON_STRICT_DIFF)
+ public void onTargetFeaturesChanged_listenerCalled_flagEnabled() {
+ // Call show() to start observing the target features change listener.
+ mMenuView.show();
+
+ // The target features change listener should be called when the observer is added.
+ verify(mOnTargetFeaturesChangeListener, times(1)).onChange(any());
+
+ // When the target features list changes, the listener should be called.
+ mMenuViewModel.onTargetFeaturesChanged(
+ List.of(
+ new TestAccessibilityTarget(mContext, 123),
+ new TestAccessibilityTarget(mContext, 456)));
+ verify(mOnTargetFeaturesChangeListener, times(2)).onChange(any());
+
+ // Double check that when the target features list changes, the listener should be called.
+ List<AccessibilityTarget> newFeaturesList =
+ List.of(
+ new TestAccessibilityTarget(mContext, 123),
+ new TestAccessibilityTarget(mContext, 789),
+ new TestAccessibilityTarget(mContext, 456));
+ mMenuViewModel.onTargetFeaturesChanged(newFeaturesList);
+ verify(mOnTargetFeaturesChangeListener, times(3)).onChange(any());
+
+ // When the target features list doesn't change, the listener should not be called again.
+ mMenuViewModel.onTargetFeaturesChanged(newFeaturesList);
+ verify(mOnTargetFeaturesChangeListener, times(3)).onChange(any());
+
+ // When the target features list changes order (but the UIDs of the targets don't change),
+ // the listener should be called.
+ mMenuViewModel.onTargetFeaturesChanged(
+ List.of(
+ new TestAccessibilityTarget(mContext, 789),
+ new TestAccessibilityTarget(mContext, 123),
+ new TestAccessibilityTarget(mContext, 456)));
+ verify(mOnTargetFeaturesChangeListener, times(4)).onChange(any());
+ }
+
private InstantInsetLayerDrawable getMenuViewInsetLayer() {
return (InstantInsetLayerDrawable) mMenuView.getBackground();
}
@@ -196,6 +301,23 @@ public class MenuViewTest extends SysuiTestCase {
return radiiAnimator;
}
+ /** Simplified AccessibilityTarget for testing MenuView. */
+ private static class TestAccessibilityTarget extends AccessibilityTarget {
+ TestAccessibilityTarget(Context context, int uid) {
+ // Set fields unused by tests to defaults that allow test compilation.
+ super(
+ context,
+ ShortcutConstants.UserShortcutType.SOFTWARE,
+ 0,
+ false,
+ MAGNIFICATION_COMPONENT_NAME.flattenToString(),
+ uid,
+ null,
+ null,
+ null);
+ }
+ }
+
@After
public void tearDown() throws Exception {
mUiModeManager.setNightMode(mNightMode);
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/hearingaid/HearingDevicesDialogDelegateTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/hearingaid/HearingDevicesDialogDelegateTest.java
index 43d0d69c428f..e4539b75f317 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/hearingaid/HearingDevicesDialogDelegateTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/hearingaid/HearingDevicesDialogDelegateTest.java
@@ -66,7 +66,6 @@ import com.android.settingslib.bluetooth.LocalBluetoothAdapter;
import com.android.settingslib.bluetooth.LocalBluetoothManager;
import com.android.settingslib.bluetooth.LocalBluetoothProfileManager;
import com.android.settingslib.bluetooth.VolumeControlProfile;
-import com.android.systemui.Flags;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.animation.DialogTransitionAnimator;
import com.android.systemui.bluetooth.qsdialog.DeviceItem;
@@ -227,7 +226,6 @@ public class HearingDevicesDialogDelegateTest extends SysuiTestCase {
}
@Test
- @EnableFlags(Flags.FLAG_HEARING_DEVICES_DIALOG_RELATED_TOOLS)
public void showDialog_noLiveCaption_noRelatedToolsInConfig_relatedToolLayoutGone() {
mContext.getOrCreateTestableResources().addOverride(
R.array.config_quickSettingsHearingDevicesRelatedToolName, new String[]{});
@@ -239,7 +237,6 @@ public class HearingDevicesDialogDelegateTest extends SysuiTestCase {
}
@Test
- @EnableFlags(Flags.FLAG_HEARING_DEVICES_DIALOG_RELATED_TOOLS)
public void showDialog_hasLiveCaption_noRelatedToolsInConfig_showOneRelatedTool() {
when(mPackageManager.queryIntentActivities(
eq(LIVE_CAPTION_INTENT), anyInt())).thenReturn(
@@ -254,7 +251,6 @@ public class HearingDevicesDialogDelegateTest extends SysuiTestCase {
}
@Test
- @EnableFlags(Flags.FLAG_HEARING_DEVICES_DIALOG_RELATED_TOOLS)
public void showDialog_hasLiveCaption_oneRelatedToolInConfig_showTwoRelatedTools()
throws PackageManager.NameNotFoundException {
when(mPackageManager.queryIntentActivities(eq(LIVE_CAPTION_INTENT), anyInt()))
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/clipboardoverlay/ClipboardImageLoaderTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/clipboardoverlay/ClipboardImageLoaderTest.kt
index 791f1f2e1f26..6fdeb2b8ebb0 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/clipboardoverlay/ClipboardImageLoaderTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/clipboardoverlay/ClipboardImageLoaderTest.kt
@@ -17,14 +17,17 @@ package com.android.systemui.clipboardoverlay
import android.content.ContentResolver
import android.content.Context
+import android.content.pm.UserInfo
import android.net.Uri
+import android.platform.test.annotations.DisableFlags
+import android.platform.test.annotations.EnableFlags
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
+import com.android.systemui.Flags.FLAG_CLIPBOARD_OVERLAY_MULTIUSER
import com.android.systemui.SysuiTestCase
-import com.android.systemui.util.mockito.whenever
+import com.android.systemui.settings.FakeUserTracker
import java.io.IOException
import kotlinx.coroutines.CoroutineScope
-import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.test.StandardTestDispatcher
import kotlinx.coroutines.test.runTest
import org.junit.Assert.assertNull
@@ -36,44 +39,90 @@ import org.mockito.ArgumentMatchers.eq
import org.mockito.Mock
import org.mockito.Mockito.verify
import org.mockito.MockitoAnnotations
+import org.mockito.kotlin.whenever
@SmallTest
-@OptIn(ExperimentalCoroutinesApi::class)
@RunWith(AndroidJUnit4::class)
class ClipboardImageLoaderTest : SysuiTestCase() {
@Mock private lateinit var mockContext: Context
@Mock private lateinit var mockContentResolver: ContentResolver
+ @Mock private lateinit var mockSecondaryContentResolver: ContentResolver
private lateinit var clipboardImageLoader: ClipboardImageLoader
+ private var fakeUserTracker: FakeUserTracker =
+ FakeUserTracker(userContentResolverProvider = { mockContentResolver })
+
+ private val userInfos = listOf(UserInfo(0, "system", 0), UserInfo(50, "secondary", 0))
@Before
fun setup() {
MockitoAnnotations.initMocks(this)
+
+ fakeUserTracker.set(userInfos, 0)
}
@Test
@Throws(IOException::class)
+ @DisableFlags(FLAG_CLIPBOARD_OVERLAY_MULTIUSER)
+ fun test_imageLoadSuccess_legacy() = runTest {
+ val testDispatcher = StandardTestDispatcher(this.testScheduler)
+ fakeUserTracker =
+ FakeUserTracker(userContentResolverProvider = { mockSecondaryContentResolver })
+ fakeUserTracker.set(userInfos, 1)
+
+ clipboardImageLoader =
+ ClipboardImageLoader(
+ mockContext,
+ fakeUserTracker,
+ testDispatcher,
+ CoroutineScope(testDispatcher),
+ )
+ val testUri = Uri.parse("testUri")
+ whenever<ContentResolver?>(mockContext.contentResolver)
+ .thenReturn(mockSecondaryContentResolver)
+ whenever(mockContext.resources).thenReturn(context.resources)
+
+ clipboardImageLoader.load(testUri)
+
+ verify(mockSecondaryContentResolver).loadThumbnail(eq(testUri), any(), any())
+ }
+
+ @Test
+ @Throws(IOException::class)
+ @EnableFlags(FLAG_CLIPBOARD_OVERLAY_MULTIUSER)
fun test_imageLoadSuccess() = runTest {
val testDispatcher = StandardTestDispatcher(this.testScheduler)
+ fakeUserTracker =
+ FakeUserTracker(userContentResolverProvider = { mockSecondaryContentResolver })
+ fakeUserTracker.set(userInfos, 1)
+
clipboardImageLoader =
- ClipboardImageLoader(mockContext, testDispatcher, CoroutineScope(testDispatcher))
+ ClipboardImageLoader(
+ mockContext,
+ fakeUserTracker,
+ testDispatcher,
+ CoroutineScope(testDispatcher),
+ )
val testUri = Uri.parse("testUri")
- whenever(mockContext.contentResolver).thenReturn(mockContentResolver)
whenever(mockContext.resources).thenReturn(context.resources)
clipboardImageLoader.load(testUri)
- verify(mockContentResolver).loadThumbnail(eq(testUri), any(), any())
+ verify(mockSecondaryContentResolver).loadThumbnail(eq(testUri), any(), any())
}
- @OptIn(ExperimentalCoroutinesApi::class)
@Test
@Throws(IOException::class)
fun test_imageLoadFailure() = runTest {
val testDispatcher = StandardTestDispatcher(this.testScheduler)
clipboardImageLoader =
- ClipboardImageLoader(mockContext, testDispatcher, CoroutineScope(testDispatcher))
+ ClipboardImageLoader(
+ mockContext,
+ fakeUserTracker,
+ testDispatcher,
+ CoroutineScope(testDispatcher),
+ )
val testUri = Uri.parse("testUri")
whenever(mockContext.contentResolver).thenReturn(mockContentResolver)
whenever(mockContext.resources).thenReturn(context.resources)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/domain/autoaddable/A11yShortcutAutoAddableListTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/domain/autoaddable/A11yShortcutAutoAddableListTest.kt
index 16f30feb7e3b..9c630ebb0ad2 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/domain/autoaddable/A11yShortcutAutoAddableListTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/domain/autoaddable/A11yShortcutAutoAddableListTest.kt
@@ -17,9 +17,6 @@
package com.android.systemui.qs.pipeline.domain.autoaddable
import android.content.ComponentName
-import android.platform.test.annotations.DisableFlags
-import android.platform.test.annotations.EnableFlags
-import android.view.accessibility.Flags
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.internal.accessibility.AccessibilityShortcutController
@@ -43,44 +40,35 @@ class A11yShortcutAutoAddableListTest : SysuiTestCase() {
object : A11yShortcutAutoAddable.Factory {
override fun create(
spec: TileSpec,
- componentName: ComponentName
+ componentName: ComponentName,
): A11yShortcutAutoAddable {
return A11yShortcutAutoAddable(mock(), mock(), spec, componentName)
}
}
@Test
- @DisableFlags(Flags.FLAG_A11Y_QS_SHORTCUT)
- fun getA11yShortcutAutoAddables_withA11yQsShortcutFlagOff_emptyResult() {
- val autoAddables = A11yShortcutAutoAddableList.getA11yShortcutAutoAddables(factory)
-
- assertThat(autoAddables).isEmpty()
- }
-
- @Test
- @EnableFlags(Flags.FLAG_A11Y_QS_SHORTCUT)
- fun getA11yShortcutAutoAddables_withA11yQsShortcutFlagOn_correctAutoAddables() {
+ fun getA11yShortcutAutoAddables_correctAutoAddables() {
val expected =
setOf(
factory.create(
TileSpec.create(ColorCorrectionTile.TILE_SPEC),
- AccessibilityShortcutController.DALTONIZER_COMPONENT_NAME
+ AccessibilityShortcutController.DALTONIZER_COMPONENT_NAME,
),
factory.create(
TileSpec.create(ColorInversionTile.TILE_SPEC),
- AccessibilityShortcutController.COLOR_INVERSION_COMPONENT_NAME
+ AccessibilityShortcutController.COLOR_INVERSION_COMPONENT_NAME,
),
factory.create(
TileSpec.create(OneHandedModeTile.TILE_SPEC),
- AccessibilityShortcutController.ONE_HANDED_COMPONENT_NAME
+ AccessibilityShortcutController.ONE_HANDED_COMPONENT_NAME,
),
factory.create(
TileSpec.create(ReduceBrightColorsTile.TILE_SPEC),
- AccessibilityShortcutController.REDUCE_BRIGHT_COLORS_COMPONENT_NAME
+ AccessibilityShortcutController.REDUCE_BRIGHT_COLORS_COMPONENT_NAME,
),
factory.create(
TileSpec.create(HearingDevicesTile.TILE_SPEC),
- AccessibilityShortcutController.ACCESSIBILITY_HEARING_AIDS_COMPONENT_NAME
+ AccessibilityShortcutController.ACCESSIBILITY_HEARING_AIDS_COMPONENT_NAME,
),
)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/domain/autoaddable/ReduceBrightColorsAutoAddableTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/domain/autoaddable/ReduceBrightColorsAutoAddableTest.kt
deleted file mode 100644
index d0699aa12a43..000000000000
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/domain/autoaddable/ReduceBrightColorsAutoAddableTest.kt
+++ /dev/null
@@ -1,121 +0,0 @@
-/*
- * Copyright (C) 2023 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.qs.pipeline.domain.autoaddable
-
-import android.platform.test.annotations.DisableFlags
-import android.platform.test.annotations.EnableFlags
-import android.view.accessibility.Flags
-import androidx.test.ext.junit.runners.AndroidJUnit4
-import androidx.test.filters.SmallTest
-import com.android.systemui.SysuiTestCase
-import com.android.systemui.coroutines.collectLastValue
-import com.android.systemui.qs.ReduceBrightColorsController
-import com.android.systemui.qs.pipeline.domain.model.AutoAddSignal
-import com.android.systemui.qs.pipeline.domain.model.AutoAddTracking
-import com.android.systemui.qs.pipeline.shared.TileSpec
-import com.android.systemui.qs.tiles.ReduceBrightColorsTile
-import com.android.systemui.util.mockito.capture
-import com.google.common.truth.Truth.assertThat
-import kotlinx.coroutines.ExperimentalCoroutinesApi
-import kotlinx.coroutines.test.TestResult
-import kotlinx.coroutines.test.TestScope
-import kotlinx.coroutines.test.runCurrent
-import kotlinx.coroutines.test.runTest
-import org.junit.Before
-import org.junit.Test
-import org.junit.runner.RunWith
-import org.mockito.ArgumentCaptor
-import org.mockito.Captor
-import org.mockito.Mock
-import org.mockito.Mockito.verify
-import org.mockito.MockitoAnnotations
-
-@OptIn(ExperimentalCoroutinesApi::class)
-@SmallTest
-@RunWith(AndroidJUnit4::class)
-class ReduceBrightColorsAutoAddableTest : SysuiTestCase() {
-
- @Mock private lateinit var reduceBrightColorsController: ReduceBrightColorsController
- @Captor
- private lateinit var reduceBrightColorsListenerCaptor:
- ArgumentCaptor<ReduceBrightColorsController.Listener>
-
- private lateinit var underTest: ReduceBrightColorsAutoAddable
-
- @Before
- fun setUp() {
- MockitoAnnotations.initMocks(this)
- }
-
- @Test
- fun notAvailable_strategyDisabled() =
- testWithFeatureAvailability(available = false) {
- assertThat(underTest.autoAddTracking).isEqualTo(AutoAddTracking.Disabled)
- }
-
- @Test
- @DisableFlags(Flags.FLAG_A11Y_QS_SHORTCUT)
- fun available_strategyIfNotAdded() =
- testWithFeatureAvailability(available = true) {
- assertThat(underTest.autoAddTracking).isEqualTo(AutoAddTracking.IfNotAdded(SPEC))
- }
-
- @Test
- @DisableFlags(Flags.FLAG_A11Y_QS_SHORTCUT)
- fun activated_addSignal() = testWithFeatureAvailability {
- val signal by collectLastValue(underTest.autoAddSignal(0))
- runCurrent()
-
- verify(reduceBrightColorsController).addCallback(capture(reduceBrightColorsListenerCaptor))
-
- reduceBrightColorsListenerCaptor.value.onActivated(true)
-
- assertThat(signal).isEqualTo(AutoAddSignal.Add(SPEC))
- }
-
- @Test
- @DisableFlags(Flags.FLAG_A11Y_QS_SHORTCUT)
- fun notActivated_noSignal() = testWithFeatureAvailability {
- val signal by collectLastValue(underTest.autoAddSignal(0))
- runCurrent()
-
- verify(reduceBrightColorsController).addCallback(capture(reduceBrightColorsListenerCaptor))
-
- reduceBrightColorsListenerCaptor.value.onActivated(false)
-
- assertThat(signal).isNull()
- }
-
- @Test
- @EnableFlags(Flags.FLAG_A11Y_QS_SHORTCUT)
- fun available_a11yQsShortcutFlagEnabled_strategyDisabled() =
- testWithFeatureAvailability(available = true) {
- assertThat(underTest.autoAddTracking).isEqualTo(AutoAddTracking.Disabled)
- }
-
- private fun testWithFeatureAvailability(
- available: Boolean = true,
- body: suspend TestScope.() -> TestResult
- ) = runTest {
- underTest = ReduceBrightColorsAutoAddable(reduceBrightColorsController, available)
- body()
- }
-
- companion object {
- private val SPEC by lazy { TileSpec.create(ReduceBrightColorsTile.TILE_SPEC) }
- }
-}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/domain/interactor/AccessibilityTilesInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/domain/interactor/AccessibilityTilesInteractorTest.kt
index 3faab5048fb4..6cd627c1d058 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/domain/interactor/AccessibilityTilesInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/domain/interactor/AccessibilityTilesInteractorTest.kt
@@ -19,9 +19,6 @@ package com.android.systemui.qs.pipeline.domain.interactor
import android.content.Context
import android.content.pm.UserInfo
import android.os.UserHandle
-import android.platform.test.annotations.DisableFlags
-import android.platform.test.annotations.EnableFlags
-import android.view.accessibility.Flags
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
@@ -50,21 +47,9 @@ import org.mockito.Mockito.mock
@RunWith(AndroidJUnit4::class)
@OptIn(ExperimentalCoroutinesApi::class)
class AccessibilityTilesInteractorTest : SysuiTestCase() {
- private val USER_0_INFO =
- UserInfo(
- 0,
- "zero",
- "",
- UserInfo.FLAG_ADMIN or UserInfo.FLAG_FULL,
- )
+ private val USER_0_INFO = UserInfo(0, "zero", "", UserInfo.FLAG_ADMIN or UserInfo.FLAG_FULL)
- private val USER_1_INFO =
- UserInfo(
- 1,
- "one",
- "",
- UserInfo.FLAG_ADMIN or UserInfo.FLAG_FULL,
- )
+ private val USER_1_INFO = UserInfo(1, "one", "", UserInfo.FLAG_ADMIN or UserInfo.FLAG_FULL)
private val USER_0_TILES = listOf(TileSpec.create(ColorInversionTile.TILE_SPEC))
private val USER_1_TILES = listOf(TileSpec.create(ColorCorrectionTile.TILE_SPEC))
@@ -94,20 +79,7 @@ class AccessibilityTilesInteractorTest : SysuiTestCase() {
}
@Test
- @DisableFlags(Flags.FLAG_A11Y_QS_SHORTCUT)
- fun currentTilesChanged_a11yQsShortcutFlagOff_nothingHappen() =
- testScope.runTest {
- underTest = createInteractor()
-
- setTiles(USER_0_TILES)
- runCurrent()
-
- assertThat(a11yQsShortcutsRepository.notifyA11yManagerTilesChangedRequests).isEmpty()
- }
-
- @Test
- @EnableFlags(Flags.FLAG_A11Y_QS_SHORTCUT)
- fun currentTilesChanged_a11yQsShortcutFlagOn_notifyAccessibilityRepository() =
+ fun currentTilesChanged_notifyAccessibilityRepository() =
testScope.runTest {
underTest = createInteractor()
@@ -123,8 +95,7 @@ class AccessibilityTilesInteractorTest : SysuiTestCase() {
}
@Test
- @EnableFlags(Flags.FLAG_A11Y_QS_SHORTCUT)
- fun userChanged_a11yQsShortcutFlagOn_notifyAccessibilityRepositoryWithCorrectTilesAndUser() =
+ fun userChanged_notifyAccessibilityRepositoryWithCorrectTilesAndUser() =
testScope.runTest {
underTest = createInteractor()
setTiles(USER_0_TILES)
@@ -163,7 +134,7 @@ class AccessibilityTilesInteractorTest : SysuiTestCase() {
return AccessibilityTilesInteractor(
a11yQsShortcutsRepository,
testDispatcher,
- testScope.backgroundScope
+ testScope.backgroundScope,
)
.apply { init(currentTilesInteractor) }
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/HearingDevicesTileTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/HearingDevicesTileTest.java
index 7d41a20e628f..307b87a74dad 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/HearingDevicesTileTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/HearingDevicesTileTest.java
@@ -27,8 +27,6 @@ import static org.mockito.Mockito.when;
import android.content.Intent;
import android.os.Handler;
-import android.platform.test.annotations.DisableFlags;
-import android.platform.test.annotations.EnableFlags;
import android.provider.Settings;
import android.service.quicksettings.Tile;
import android.testing.TestableLooper;
@@ -38,7 +36,6 @@ import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
import com.android.internal.logging.MetricsLogger;
-import com.android.systemui.Flags;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.accessibility.hearingaid.HearingDevicesChecker;
import com.android.systemui.accessibility.hearingaid.HearingDevicesDialogManager;
@@ -124,18 +121,6 @@ public class HearingDevicesTileTest extends SysuiTestCase {
}
@Test
- @EnableFlags(Flags.FLAG_HEARING_AIDS_QS_TILE_DIALOG)
- public void isAvailable_flagEnabled_true() {
- assertThat(mTile.isAvailable()).isTrue();
- }
-
- @Test
- @DisableFlags(Flags.FLAG_HEARING_AIDS_QS_TILE_DIALOG)
- public void isAvailable_flagDisabled_false() {
- assertThat(mTile.isAvailable()).isFalse();
- }
-
- @Test
public void longClick_expectedAction() {
mTile.longClick(null);
mTestableLooper.processAllMessages();
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/hearingdevices/domain/interactor/HearingDevicesTileDataInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/hearingdevices/domain/interactor/HearingDevicesTileDataInteractorTest.kt
index 1dfa2cd26491..9099d3d911bc 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/hearingdevices/domain/interactor/HearingDevicesTileDataInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/hearingdevices/domain/interactor/HearingDevicesTileDataInteractorTest.kt
@@ -17,12 +17,9 @@
package com.android.systemui.qs.tiles.impl.hearingdevices.domain.interactor
import android.os.UserHandle
-import android.platform.test.annotations.DisableFlags
-import android.platform.test.annotations.EnableFlags
import android.platform.test.annotations.EnabledOnRavenwood
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
-import com.android.systemui.Flags
import com.android.systemui.SysuiTestCase
import com.android.systemui.accessibility.hearingaid.HearingDevicesChecker
import com.android.systemui.coroutines.collectLastValue
@@ -66,24 +63,14 @@ class HearingDevicesTileDataInteractorTest : SysuiTestCase() {
underTest = HearingDevicesTileDataInteractor(testScope.testScheduler, controller, checker)
}
- @EnableFlags(Flags.FLAG_HEARING_AIDS_QS_TILE_DIALOG)
@Test
- fun availability_flagEnabled_returnTrue() =
+ fun availability_returnTrue() =
testScope.runTest {
val availability by collectLastValue(underTest.availability(testUser))
assertThat(availability).isTrue()
}
- @DisableFlags(Flags.FLAG_HEARING_AIDS_QS_TILE_DIALOG)
- @Test
- fun availability_flagDisabled_returnFalse() =
- testScope.runTest {
- val availability by collectLastValue(underTest.availability(testUser))
-
- assertThat(availability).isFalse()
- }
-
@Test
fun tileData_bluetoothStateChanged_dataMatchesChecker() =
testScope.runTest {
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/display/StatusBarTouchShadeDisplayPolicyTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/display/StatusBarTouchShadeDisplayPolicyTest.kt
index fd9f5f02ee62..20dfd3e11947 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/display/StatusBarTouchShadeDisplayPolicyTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/display/StatusBarTouchShadeDisplayPolicyTest.kt
@@ -18,6 +18,7 @@ package com.android.systemui.shade.display
import android.view.Display
import android.view.Display.TYPE_EXTERNAL
+import android.view.MotionEvent
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
@@ -28,11 +29,16 @@ import com.android.systemui.display.data.repository.displayRepository
import com.android.systemui.keyguard.data.repository.fakeKeyguardRepository
import com.android.systemui.kosmos.testScope
import com.android.systemui.kosmos.useUnconfinedTestDispatcher
+import com.android.systemui.shade.domain.interactor.notificationElement
+import com.android.systemui.shade.domain.interactor.qsElement
+import com.android.systemui.shade.domain.interactor.shadeInteractor
import com.android.systemui.testKosmos
import com.google.common.truth.Truth.assertThat
import kotlin.test.Test
import kotlinx.coroutines.test.runTest
import org.junit.runner.RunWith
+import org.mockito.kotlin.doReturn
+import org.mockito.kotlin.mock
@SmallTest
@RunWith(AndroidJUnit4::class)
@@ -50,9 +56,19 @@ class StatusBarTouchShadeDisplayPolicyTest : SysuiTestCase() {
keyguardRepository,
testScope.backgroundScope,
shadeOnDefaultDisplayWhenLocked = shadeOnDefaultDisplayWhenLocked,
+ shadeInteractor = { kosmos.shadeInteractor },
+ { kosmos.qsElement },
+ { kosmos.notificationElement },
)
}
+ private fun createMotionEventForDisplay(displayId: Int, xCoordinate: Float = 0f): MotionEvent {
+ return mock<MotionEvent> {
+ on { getX() } doReturn xCoordinate
+ on { getDisplayId() } doReturn displayId
+ }
+ }
+
@Test
fun displayId_defaultToDefaultDisplay() {
val underTest = createUnderTest()
@@ -67,7 +83,7 @@ class StatusBarTouchShadeDisplayPolicyTest : SysuiTestCase() {
val displayId by collectLastValue(underTest.displayId)
displayRepository.addDisplays(display(id = 2, type = TYPE_EXTERNAL))
- underTest.onStatusBarTouched(2)
+ underTest.onStatusBarTouched(createMotionEventForDisplay(2), STATUS_BAR_WIDTH)
assertThat(displayId).isEqualTo(2)
}
@@ -79,7 +95,7 @@ class StatusBarTouchShadeDisplayPolicyTest : SysuiTestCase() {
val displayIds by collectValues(underTest.displayId)
assertThat(displayIds).isEqualTo(listOf(Display.DEFAULT_DISPLAY))
- underTest.onStatusBarTouched(2)
+ underTest.onStatusBarTouched(createMotionEventForDisplay(2), STATUS_BAR_WIDTH)
// Never set, as 2 was not a display according to the repository.
assertThat(displayIds).isEqualTo(listOf(Display.DEFAULT_DISPLAY))
@@ -92,7 +108,7 @@ class StatusBarTouchShadeDisplayPolicyTest : SysuiTestCase() {
val displayId by collectLastValue(underTest.displayId)
displayRepository.addDisplays(display(id = 2, type = TYPE_EXTERNAL))
- underTest.onStatusBarTouched(2)
+ underTest.onStatusBarTouched(createMotionEventForDisplay(2), STATUS_BAR_WIDTH)
assertThat(displayId).isEqualTo(2)
@@ -108,7 +124,7 @@ class StatusBarTouchShadeDisplayPolicyTest : SysuiTestCase() {
val displayId by collectLastValue(underTest.displayId)
displayRepository.addDisplays(display(id = 2, type = TYPE_EXTERNAL))
- underTest.onStatusBarTouched(2)
+ underTest.onStatusBarTouched(createMotionEventForDisplay(2), STATUS_BAR_WIDTH)
assertThat(displayId).isEqualTo(2)
@@ -124,7 +140,7 @@ class StatusBarTouchShadeDisplayPolicyTest : SysuiTestCase() {
val displayId by collectLastValue(underTest.displayId)
displayRepository.addDisplays(display(id = 2, type = TYPE_EXTERNAL))
- underTest.onStatusBarTouched(2)
+ underTest.onStatusBarTouched(createMotionEventForDisplay(2), STATUS_BAR_WIDTH)
assertThat(displayId).isEqualTo(2)
@@ -136,4 +152,48 @@ class StatusBarTouchShadeDisplayPolicyTest : SysuiTestCase() {
assertThat(displayId).isEqualTo(2)
}
+
+ @Test
+ fun onStatusBarTouched_leftSide_intentSetToNotifications() =
+ testScope.runTest {
+ val underTest = createUnderTest(shadeOnDefaultDisplayWhenLocked = true)
+
+ underTest.onStatusBarTouched(
+ createMotionEventForDisplay(2, STATUS_BAR_WIDTH * 0.1f),
+ STATUS_BAR_WIDTH,
+ )
+
+ assertThat(underTest.consumeExpansionIntent()).isEqualTo(kosmos.notificationElement)
+ }
+
+ @Test
+ fun onStatusBarTouched_rightSide_intentSetToQs() =
+ testScope.runTest {
+ val underTest = createUnderTest(shadeOnDefaultDisplayWhenLocked = true)
+
+ underTest.onStatusBarTouched(
+ createMotionEventForDisplay(2, STATUS_BAR_WIDTH * 0.95f),
+ STATUS_BAR_WIDTH,
+ )
+
+ assertThat(underTest.consumeExpansionIntent()).isEqualTo(kosmos.qsElement)
+ }
+
+ @Test
+ fun onStatusBarTouched_nullAfterConsumed() =
+ testScope.runTest {
+ val underTest = createUnderTest(shadeOnDefaultDisplayWhenLocked = true)
+
+ underTest.onStatusBarTouched(
+ createMotionEventForDisplay(2, STATUS_BAR_WIDTH * 0.1f),
+ STATUS_BAR_WIDTH,
+ )
+ assertThat(underTest.consumeExpansionIntent()).isEqualTo(kosmos.notificationElement)
+
+ assertThat(underTest.consumeExpansionIntent()).isNull()
+ }
+
+ companion object {
+ private const val STATUS_BAR_WIDTH = 100
+ }
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/domain/interactor/ShadeExpandedStateInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/domain/interactor/ShadeExpandedStateInteractorTest.kt
index 58396e7cef82..8aa8a50afcd4 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/domain/interactor/ShadeExpandedStateInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/domain/interactor/ShadeExpandedStateInteractorTest.kt
@@ -22,8 +22,6 @@ import com.android.systemui.SysuiTestCase
import com.android.systemui.flags.EnableSceneContainer
import com.android.systemui.kosmos.testScope
import com.android.systemui.kosmos.useUnconfinedTestDispatcher
-import com.android.systemui.shade.domain.interactor.ShadeExpandedStateInteractorImpl.NotificationElement
-import com.android.systemui.shade.domain.interactor.ShadeExpandedStateInteractorImpl.QSElement
import com.android.systemui.shade.shadeTestUtil
import com.android.systemui.testKosmos
import com.google.common.truth.Truth.assertThat
@@ -52,7 +50,7 @@ class ShadeExpandedStateInteractorTest : SysuiTestCase() {
val element = currentlyExpandedElement.value
- assertThat(element).isInstanceOf(QSElement::class.java)
+ assertThat(element).isInstanceOf(QSShadeElement::class.java)
}
@Test
@@ -62,7 +60,7 @@ class ShadeExpandedStateInteractorTest : SysuiTestCase() {
val element = underTest.currentlyExpandedElement.value
- assertThat(element).isInstanceOf(NotificationElement::class.java)
+ assertThat(element).isInstanceOf(NotificationShadeElement::class.java)
}
@Test
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/FakeHomeStatusBarViewModel.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/FakeHomeStatusBarViewModel.kt
index 6feada1c9769..937f333b0065 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/FakeHomeStatusBarViewModel.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/FakeHomeStatusBarViewModel.kt
@@ -46,6 +46,8 @@ class FakeHomeStatusBarViewModel(
override val isHomeStatusBarAllowedByScene = MutableStateFlow(false)
+ override val shouldHomeStatusBarBeVisible = MutableStateFlow(false)
+
override val shouldShowOperatorNameView = MutableStateFlow(false)
override val isClockVisible =
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/HomeStatusBarViewModelImplTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/HomeStatusBarViewModelImplTest.kt
index e95bc3378423..be4af868b740 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/HomeStatusBarViewModelImplTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/HomeStatusBarViewModelImplTest.kt
@@ -575,6 +575,98 @@ class HomeStatusBarViewModelImplTest : SysuiTestCase() {
}
@Test
+ fun shouldHomeStatusBarBeVisible_keyguardNotGone_noHun_false() =
+ kosmos.runTest {
+ // Do not transition from keyguard. i.e., we don't call transitionKeyguardToGone()
+
+ // Nothing disabled
+ fakeDisableFlagsRepository.disableFlags.value =
+ DisableFlagsModel(DISABLE_NONE, DISABLE2_NONE)
+
+ val latest by collectLastValue(underTest.shouldHomeStatusBarBeVisible)
+ assertThat(latest).isFalse()
+ }
+
+ @Test
+ fun shouldHomeStatusBarBeVisible_keyguardNotGone_hun_true() =
+ kosmos.runTest {
+ // Keyguard gone
+ transitionKeyguardToGone()
+
+ // Nothing disabled
+ fakeDisableFlagsRepository.disableFlags.value =
+ DisableFlagsModel(DISABLE_NONE, DISABLE2_NONE)
+
+ // there is an active HUN
+ headsUpNotificationRepository.setNotifications(
+ UnconfinedFakeHeadsUpRowRepository(
+ key = "key",
+ pinnedStatus = MutableStateFlow(PinnedStatus.PinnedByUser),
+ )
+ )
+
+ val latest by collectLastValue(underTest.shouldHomeStatusBarBeVisible)
+ assertThat(latest).isTrue()
+ }
+
+ @Test
+ fun shouldHomeStatusBarBeVisible_keyguardGone_noHun_notInCamera_true() =
+ kosmos.runTest {
+ // Keyguard gone
+ transitionKeyguardToGone()
+
+ // Nothing disabled
+ fakeDisableFlagsRepository.disableFlags.value =
+ DisableFlagsModel(DISABLE_NONE, DISABLE2_NONE)
+
+ val latest by collectLastValue(underTest.shouldHomeStatusBarBeVisible)
+ assertThat(latest).isTrue()
+ }
+
+ @Test
+ fun shouldHomeStatusBarBeVisible_keyguardGone_hun_notInCamera_true() =
+ kosmos.runTest {
+ // Keyguard gone
+ transitionKeyguardToGone()
+
+ // Nothing disabled
+ fakeDisableFlagsRepository.disableFlags.value =
+ DisableFlagsModel(DISABLE_NONE, DISABLE2_NONE)
+
+ // there is an active HUN
+ headsUpNotificationRepository.setNotifications(
+ UnconfinedFakeHeadsUpRowRepository(
+ key = "key",
+ pinnedStatus = MutableStateFlow(PinnedStatus.PinnedByUser),
+ )
+ )
+
+ val latest by collectLastValue(underTest.shouldHomeStatusBarBeVisible)
+ assertThat(latest).isTrue()
+ }
+
+ @Test
+ fun shouldHomeStatusBarBeVisible_keyguardGone_noHun_inCamera_false() =
+ kosmos.runTest {
+ // Keyguard gone
+ transitionKeyguardToGone()
+
+ // Nothing disabled
+ fakeDisableFlagsRepository.disableFlags.value =
+ DisableFlagsModel(DISABLE_NONE, DISABLE2_NONE)
+
+ fakeKeyguardTransitionRepository.sendTransitionSteps(
+ from = KeyguardState.LOCKSCREEN,
+ to = KeyguardState.OCCLUDED,
+ testScope = testScope,
+ )
+ kosmos.keyguardInteractor.onCameraLaunchDetected(CAMERA_LAUNCH_SOURCE_POWER_DOUBLE_TAP)
+
+ val latest by collectLastValue(underTest.shouldHomeStatusBarBeVisible)
+ assertThat(latest).isFalse()
+ }
+
+ @Test
fun isClockVisible_allowedByDisableFlags_visible() =
kosmos.runTest {
val latest by collectLastValue(underTest.isClockVisible)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/StatusBarOperatorNameViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/StatusBarOperatorNameViewModelTest.kt
index 20cc85f08b01..8608b0bf2f0b 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/StatusBarOperatorNameViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/StatusBarOperatorNameViewModelTest.kt
@@ -39,14 +39,12 @@ class StatusBarOperatorNameViewModelTest : SysuiTestCase() {
kosmos.runTest {
val intr1 = fakeMobileIconsInteractor.getMobileConnectionInteractorForSubId(1)
val intr2 = fakeMobileIconsInteractor.getMobileConnectionInteractorForSubId(2)
- val invalidIntr = fakeMobileIconsInteractor.getMobileConnectionInteractorForSubId(-1)
// GIVEN default data subId is 1
fakeMobileIconsInteractor.defaultDataSubId.value = 1
intr1.carrierName.value = "Test Name 1"
intr2.carrierName.value = "Test Name 2"
- invalidIntr.carrierName.value = "default network name"
val latest by collectLastValue(underTest.operatorName)
@@ -56,8 +54,19 @@ class StatusBarOperatorNameViewModelTest : SysuiTestCase() {
assertThat(latest).isEqualTo("Test Name 2")
- fakeMobileIconsInteractor.defaultDataSubId.value = -1
+ fakeMobileIconsInteractor.defaultDataSubId.value = null
- assertThat(latest).isEqualTo("default network name")
+ assertThat(latest).isNull()
+ }
+
+ @Test
+ fun operatorName_noDefaultDataSubId_null() =
+ kosmos.runTest {
+ // GIVEN defaultDataSubId is null
+ fakeMobileIconsInteractor.defaultDataSubId.value = null
+
+ val latest by collectLastValue(underTest.operatorName)
+
+ assertThat(latest).isNull()
}
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/wallpapers/GradientColorWallpaperTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/wallpapers/GradientColorWallpaperTest.kt
new file mode 100644
index 000000000000..ba6ea9f5e8bb
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/wallpapers/GradientColorWallpaperTest.kt
@@ -0,0 +1,85 @@
+/*
+ * 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.wallpapers
+
+import android.content.Context
+import android.graphics.Canvas
+import android.graphics.Paint
+import android.graphics.Rect
+import android.graphics.RectF
+import android.service.wallpaper.WallpaperService.Engine
+import android.testing.TestableLooper.RunWithLooper
+import android.view.Surface
+import android.view.SurfaceHolder
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.ArgumentMatchers.anyInt
+import org.mockito.Mock
+import org.mockito.Mockito.spy
+import org.mockito.MockitoAnnotations
+import org.mockito.kotlin.any
+import org.mockito.kotlin.verify
+import org.mockito.kotlin.whenever
+
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+@RunWithLooper
+class GradientColorWallpaperTest : SysuiTestCase() {
+
+ @Mock private lateinit var surfaceHolder: SurfaceHolder
+
+ @Mock private lateinit var surface: Surface
+
+ @Mock private lateinit var canvas: Canvas
+
+ @Mock private lateinit var mockContext: Context
+
+ @Before
+ fun setUp() {
+ MockitoAnnotations.initMocks(this)
+
+ whenever(surfaceHolder.surface).thenReturn(surface)
+ whenever(surfaceHolder.surfaceFrame).thenReturn(surfaceFrame)
+ whenever(surface.lockHardwareCanvas()).thenReturn(canvas)
+ whenever(mockContext.getColor(anyInt())).thenReturn(1)
+ }
+
+ private fun createGradientColorWallpaperEngine(): Engine {
+ val gradientColorWallpaper = GradientColorWallpaper()
+ val engine = spy(gradientColorWallpaper.onCreateEngine())
+ whenever(engine.displayContext).thenReturn(mockContext)
+ return engine
+ }
+
+ @Test
+ fun onSurfaceRedrawNeeded_shouldDrawInCanvas() {
+ val engine = createGradientColorWallpaperEngine()
+ engine.onCreate(surfaceHolder)
+
+ engine.onSurfaceRedrawNeeded(surfaceHolder)
+
+ verify(canvas).drawRect(any<RectF>(), any<Paint>())
+ }
+
+ private companion object {
+ val surfaceFrame = Rect(0, 0, 100, 100)
+ }
+}
diff --git a/packages/SystemUI/res/layout/qs_footer_impl.xml b/packages/SystemUI/res/layout/qs_footer_impl.xml
index b3f32a2863a8..5e8a8a570466 100644
--- a/packages/SystemUI/res/layout/qs_footer_impl.xml
+++ b/packages/SystemUI/res/layout/qs_footer_impl.xml
@@ -34,13 +34,14 @@
android:layout_height="match_parent"
android:layout_gravity="center_vertical">
- <TextView
+ <com.android.systemui.qs.BuildTextView
android:id="@+id/build"
android:layout_width="0dp"
android:layout_height="match_parent"
android:paddingEnd="4dp"
android:layout_weight="1"
- android:clickable="true"
+ android:marqueeRepeatLimit="1"
+ android:clickable="false"
android:ellipsize="marquee"
android:focusable="true"
android:gravity="center_vertical"
diff --git a/packages/SystemUI/res/layout/volume_dialog_slider.xml b/packages/SystemUI/res/layout/volume_dialog_slider.xml
index 6eb7b730e105..c5f468e731f5 100644
--- a/packages/SystemUI/res/layout/volume_dialog_slider.xml
+++ b/packages/SystemUI/res/layout/volume_dialog_slider.xml
@@ -14,8 +14,8 @@
limitations under the License.
-->
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="0dp"
- android:layout_height="0dp"
+ android:layout_width="@dimen/volume_dialog_slider_width"
+ android:layout_height="match_parent"
android:maxHeight="@dimen/volume_dialog_slider_height">
<com.google.android.material.slider.Slider
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index a8ee60973586..e785af8e36c9 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -3815,7 +3815,7 @@
user what action they need to take in the customization dialog to assign a new custom shortcut.
The shortcut customize dialog allows users to add/remove custom shortcuts
[CHAR LIMIT=NONE] -->
- <string name="shortcut_customize_mode_add_shortcut_description">Press key to assign shortcut</string>
+ <string name="shortcut_customize_mode_add_shortcut_description">To create this shortcut, press the Action key and one or more other keys together</string>
<!-- Sub title at the top of the remove custom shortcut dialog. Explains to the user what action
they're about to take when they click remove shortcut. The shortcut customize dialog allows
users to add/remove custom shortcuts
@@ -3912,7 +3912,7 @@
<!-- Error message displayed when the user select a key combination that is already in use while
assigning a new custom key combination to a shortcut in shortcut helper. The helper is a
component that shows the user which keyboard shortcuts they can use. [CHAR LIMIT=NONE] -->
- <string name="shortcut_customizer_key_combination_in_use_error_message">Key combination already in use. Try another key.</string>
+ <string name="shortcut_customizer_key_combination_in_use_error_message">Key combination already in use. Try another combination.</string>
<!-- Generic error message displayed when the user selected key combination cannot be used as
custom keyboard shortcut in shortcut helper. The helper is a component that shows the user
which keyboard shortcuts they can use and allows users to customize their keyboard
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/RecentsAnimationListener.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/RecentsAnimationListener.java
index ff6bcdb150f8..51892aac606a 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/RecentsAnimationListener.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/RecentsAnimationListener.java
@@ -19,7 +19,6 @@ package com.android.systemui.shared.system;
import android.graphics.Rect;
import android.os.Bundle;
import android.view.RemoteAnimationTarget;
-import android.window.TransitionInfo;
import com.android.systemui.shared.recents.model.ThumbnailData;
@@ -31,7 +30,7 @@ public interface RecentsAnimationListener {
*/
void onAnimationStart(RecentsAnimationControllerCompat controller,
RemoteAnimationTarget[] apps, RemoteAnimationTarget[] wallpapers,
- Rect homeContentInsets, Rect minimizedHomeBounds, Bundle extras, TransitionInfo info);
+ Rect homeContentInsets, Rect minimizedHomeBounds, Bundle extras);
/**
* Called when the animation into Recents was canceled. This call is made on the binder thread.
diff --git a/packages/SystemUI/src/com/android/systemui/KairosActivatable.kt b/packages/SystemUI/src/com/android/systemui/KairosActivatable.kt
new file mode 100644
index 000000000000..5e29ba91ce42
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/KairosActivatable.kt
@@ -0,0 +1,212 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui
+
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Application
+import com.android.systemui.kairos.BuildScope
+import com.android.systemui.kairos.Events
+import com.android.systemui.kairos.EventsLoop
+import com.android.systemui.kairos.ExperimentalKairosApi
+import com.android.systemui.kairos.Incremental
+import com.android.systemui.kairos.IncrementalLoop
+import com.android.systemui.kairos.KairosNetwork
+import com.android.systemui.kairos.State
+import com.android.systemui.kairos.StateLoop
+import com.android.systemui.kairos.launchKairosNetwork
+import com.android.systemui.kairos.launchScope
+import dagger.Binds
+import dagger.Module
+import dagger.Provides
+import dagger.multibindings.ClassKey
+import dagger.multibindings.IntoMap
+import dagger.multibindings.Multibinds
+import javax.inject.Inject
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.launch
+
+/**
+ * A Kairos-powered class that needs late-initialization within a Kairos [BuildScope].
+ *
+ * If your class is a [SysUISingleton], you can leverage Dagger to automatically initialize your
+ * instance after SystemUI has initialized:
+ * ```kotlin
+ * class MyClass : KairosActivatable { ... }
+ *
+ * @dagger.Module
+ * interface MyModule {
+ * @Binds
+ * @IntoSet
+ * fun bindKairosActivatable(impl: MyClass): KairosActivatable
+ * }
+ * ```
+ *
+ * Alternatively, you can utilize Dagger's [dagger.assisted.AssistedInject]:
+ * ```kotlin
+ * class MyClass @AssistedInject constructor(...) : KairosActivatable {
+ * @AssistedFactory
+ * interface Factory {
+ * fun create(...): MyClass
+ * }
+ * }
+ *
+ * // When you need an instance:
+ *
+ * class OtherClass @Inject constructor(
+ * private val myClassFactory: MyClass.Factory,
+ * ) {
+ * fun BuildScope.foo() {
+ * val myClass = activated { myClassFactory.create() }
+ * ...
+ * }
+ * }
+ * ```
+ *
+ * @see activated
+ */
+@ExperimentalKairosApi
+fun interface KairosActivatable {
+ /** Initializes any Kairos fields that require a [BuildScope] in order to be constructed. */
+ fun BuildScope.activate()
+}
+
+/** Constructs [KairosActivatable] instances. */
+@ExperimentalKairosApi
+fun interface KairosActivatableFactory<T : KairosActivatable> {
+ fun BuildScope.create(): T
+}
+
+/** Instantiates, [activates][KairosActivatable.activate], and returns a [KairosActivatable]. */
+@ExperimentalKairosApi
+fun <T : KairosActivatable> BuildScope.activated(factory: KairosActivatableFactory<T>): T =
+ factory.run { create() }.apply { activate() }
+
+/**
+ * Utilities for defining [State] and [Events] from a constructor without a provided [BuildScope].
+ * These instances are not active until the builder is [activated][activate]; while you can
+ * immediately use them with other Kairos APIs, the Kairos transaction will be suspended until
+ * initialization is complete.
+ *
+ * ```kotlin
+ * class MyRepository(private val dataSource: DataSource) : KairosBuilder by kairosBuilder() {
+ * val dataSourceEvent = buildEvents<SomeData> {
+ * // inside this lambda, we have access to a BuildScope, which can be used to create
+ * // new inputs to the Kairos network
+ * dataSource.someDataFlow.toEvents()
+ * }
+ * }
+ * ```
+ */
+@ExperimentalKairosApi
+interface KairosBuilder : KairosActivatable {
+ /**
+ * Returns a forward-reference to a [State] that will be instantiated when this builder is
+ * [activated][activate].
+ */
+ fun <R> buildState(block: BuildScope.() -> State<R>): State<R>
+
+ /**
+ * Returns a forward-reference to an [Events] that will be instantiated when this builder is
+ * [activated][activate].
+ */
+ fun <R> buildEvents(block: BuildScope.() -> Events<R>): Events<R>
+
+ fun <K, V> buildIncremental(block: BuildScope.() -> Incremental<K, V>): Incremental<K, V>
+
+ /** Defers [block] until this builder is [activated][activate]. */
+ fun onActivated(block: BuildScope.() -> Unit)
+}
+
+/** Returns an [KairosBuilder] that can only be [activated][KairosActivatable.activate] once. */
+@ExperimentalKairosApi fun kairosBuilder(): KairosBuilder = KairosBuilderImpl()
+
+@OptIn(ExperimentalKairosApi::class)
+private class KairosBuilderImpl @Inject constructor() : KairosBuilder {
+
+ // TODO: atomic?
+ // TODO: are two lists really necessary?
+ private var _builds: MutableList<KairosActivatable>? = mutableListOf()
+ private var _startables: MutableList<KairosActivatable>? = mutableListOf()
+
+ private val startables
+ get() = checkNotNull(_startables) { "Kairos network has already been initialized" }
+
+ private val builds
+ get() = checkNotNull(_builds) { "Kairos network has already been initialized" }
+
+ override fun <R> buildState(block: BuildScope.() -> State<R>): State<R> =
+ StateLoop<R>().apply { builds.add { loopback = block() } }
+
+ override fun <R> buildEvents(block: BuildScope.() -> Events<R>): Events<R> =
+ EventsLoop<R>().apply { builds.add { loopback = block() } }
+
+ override fun <K, V> buildIncremental(
+ block: BuildScope.() -> Incremental<K, V>
+ ): Incremental<K, V> = IncrementalLoop<K, V>().apply { builds.add { loopback = block() } }
+
+ override fun onActivated(block: BuildScope.() -> Unit) {
+ startables.add { block() }
+ }
+
+ override fun BuildScope.activate() {
+ builds.forEach { it.run { activate() } }
+ _builds = null
+ deferredBuildScopeAction {
+ startables.forEach { it.run { activate() } }
+ _startables = null
+ }
+ }
+}
+
+/** Initializes [KairosActivatables][KairosActivatable] after SystemUI is initialized. */
+@SysUISingleton
+@ExperimentalKairosApi
+class KairosCoreStartable
+@Inject
+constructor(
+ @Application private val appScope: CoroutineScope,
+ private val kairosNetwork: KairosNetwork,
+ private val activatables: dagger.Lazy<Set<@JvmSuppressWildcards KairosActivatable>>,
+) : CoreStartable {
+ override fun start() {
+ appScope.launch {
+ kairosNetwork.activateSpec {
+ for (activatable in activatables.get()) {
+ launchScope { activatable.run { activate() } }
+ }
+ }
+ }
+ }
+}
+
+@Module
+@ExperimentalKairosApi
+interface KairosCoreStartableModule {
+ @Binds
+ @IntoMap
+ @ClassKey(KairosCoreStartable::class)
+ fun bindCoreStartable(impl: KairosCoreStartable): CoreStartable
+
+ @Multibinds fun kairosActivatables(): Set<@JvmSuppressWildcards KairosActivatable>
+
+ companion object {
+ @Provides
+ @SysUISingleton
+ fun provideKairosNetwork(@Application scope: CoroutineScope): KairosNetwork =
+ scope.launchKairosNetwork()
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/MagnificationImpl.java b/packages/SystemUI/src/com/android/systemui/accessibility/MagnificationImpl.java
index 5b433464c1c6..5cba464fc24c 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/MagnificationImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/MagnificationImpl.java
@@ -609,15 +609,12 @@ public class MagnificationImpl implements Magnification, CommandQueue.Callbacks
final WindowMagnificationController windowMagnificationController =
mWindowMagnificationControllerSupplier.get(displayId);
if (windowMagnificationController != null) {
- boolean isWindowMagnifierActivated = windowMagnificationController.isActivated();
- if (isWindowMagnifierActivated) {
- windowMagnificationController.updateDragHandleResourcesIfNeeded(shown);
- }
+ windowMagnificationController.updateDragHandleResourcesIfNeeded(shown);
if (shown) {
mA11yLogger.logWithPosition(
MagnificationSettingsEvent.MAGNIFICATION_SETTINGS_PANEL_OPENED,
- isWindowMagnifierActivated
+ windowMagnificationController.isActivated()
? ACCESSIBILITY_MAGNIFICATION_MODE_WINDOW
: ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN
);
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationController.java b/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationController.java
index 08d3e17c03d7..1587ab16fc38 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationController.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationController.java
@@ -1519,12 +1519,12 @@ class WindowMagnificationController implements View.OnTouchListener, SurfaceHold
}
void updateDragHandleResourcesIfNeeded(boolean settingsPanelIsShown) {
+ mSettingsPanelVisibility = settingsPanelIsShown;
+
if (!isActivated()) {
return;
}
- mSettingsPanelVisibility = settingsPanelIsShown;
-
mDragView.setBackground(mContext.getResources().getDrawable(settingsPanelIsShown
? R.drawable.accessibility_window_magnification_drag_handle_background_change_inset
: R.drawable.accessibility_window_magnification_drag_handle_background_inset));
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuAnimationController.java b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuAnimationController.java
index 030d147e21c3..edbede8fa865 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuAnimationController.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuAnimationController.java
@@ -24,6 +24,7 @@ import android.graphics.Rect;
import android.os.Handler;
import android.os.Looper;
import android.util.Log;
+import android.view.DisplayCutout;
import android.view.View;
import android.view.animation.Animation;
import android.view.animation.OvershootInterpolator;
@@ -197,7 +198,7 @@ class MenuAnimationController {
constrainPositionAndUpdate(position, /* writeToPosition = */ true);
}
- void flingMenuThenSpringToEdge(float x, float velocityX, float velocityY) {
+ void flingMenuThenSpringToEdge(PointF position, float velocityX, float velocityY) {
final boolean shouldMenuFlingLeft = isOnLeftSide()
? velocityX < ESCAPE_VELOCITY
: velocityX < -ESCAPE_VELOCITY;
@@ -205,9 +206,17 @@ class MenuAnimationController {
final Rect draggableBounds = mMenuView.getMenuDraggableBounds();
final float finalPositionX = shouldMenuFlingLeft
? draggableBounds.left : draggableBounds.right;
-
+ final DisplayCutout displayCutout = mMenuViewAppearance.getDisplayCutout();
+ final float finalPositionY =
+ (displayCutout == null) ? position.y
+ : mMenuViewAppearance.avoidVerticalDisplayCutout(
+ position.y, draggableBounds,
+ shouldMenuFlingLeft
+ ? displayCutout.getBoundingRectLeft()
+ : displayCutout.getBoundingRectRight()
+ );
final float minimumVelocityToReachEdge =
- (finalPositionX - x) * (FLING_FRICTION_SCALAR * DEFAULT_FRICTION);
+ (finalPositionX - position.x) * (FLING_FRICTION_SCALAR * DEFAULT_FRICTION);
final float startXVelocity = shouldMenuFlingLeft
? Math.min(minimumVelocityToReachEdge, velocityX)
@@ -219,11 +228,19 @@ class MenuAnimationController {
createSpringForce(),
finalPositionX);
- flingThenSpringMenuWith(DynamicAnimation.TRANSLATION_Y,
- velocityY,
- FLING_FRICTION_SCALAR,
- createSpringForce(),
- /* finalPosition= */ null);
+ if (com.android.systemui.Flags.floatingMenuDisplayCutoutSupport()) {
+ flingThenSpringMenuWith(DynamicAnimation.TRANSLATION_Y,
+ velocityY,
+ FLING_FRICTION_SCALAR,
+ createSpringForce(),
+ (finalPositionY != position.y) ? finalPositionY : null);
+ } else {
+ flingThenSpringMenuWith(DynamicAnimation.TRANSLATION_Y,
+ velocityY,
+ FLING_FRICTION_SCALAR,
+ createSpringForce(),
+ /* finalPosition= */ null);
+ }
}
private void flingThenSpringMenuWith(DynamicAnimation.ViewProperty property, float velocity,
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuInfoRepository.java b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuInfoRepository.java
index a1cb0367421b..bb6ab51aa56a 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuInfoRepository.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuInfoRepository.java
@@ -266,7 +266,7 @@ class MenuInfoRepository {
mSecureSettings.getUriFor(Settings.Secure.ACCESSIBILITY_BUTTON_TARGETS),
/* notifyForDescendants */ false, mMenuTargetFeaturesContentObserver,
UserHandle.USER_CURRENT);
- if (!com.android.systemui.Flags.floatingMenuNarrowTargetContentObserver()) {
+ if (com.android.systemui.Flags.floatingMenuNotifyTargetsChangedOnStrictDiff()) {
mSecureSettings.registerContentObserverForUserSync(
mSecureSettings.getUriFor(ENABLED_ACCESSIBILITY_SERVICES),
/* notifyForDescendants */ false,
@@ -287,7 +287,7 @@ class MenuInfoRepository {
UserHandle.USER_CURRENT);
mContext.registerComponentCallbacks(mComponentCallbacks);
- if (!com.android.systemui.Flags.floatingMenuNarrowTargetContentObserver()) {
+ if (com.android.systemui.Flags.floatingMenuNotifyTargetsChangedOnStrictDiff()) {
mAccessibilityManager.addAccessibilityServicesStateChangeListener(
mA11yServicesStateChangeListener);
}
@@ -317,7 +317,7 @@ class MenuInfoRepository {
mContext.getContentResolver().unregisterContentObserver(mMenuFadeOutContentObserver);
mContext.unregisterComponentCallbacks(mComponentCallbacks);
- if (!com.android.systemui.Flags.floatingMenuNarrowTargetContentObserver()) {
+ if (com.android.systemui.Flags.floatingMenuNotifyTargetsChangedOnStrictDiff()) {
mAccessibilityManager.removeAccessibilityServicesStateChangeListener(
mA11yServicesStateChangeListener);
}
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuListViewTouchHandler.java b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuListViewTouchHandler.java
index 9511e3769a8d..aca020d235be 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuListViewTouchHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuListViewTouchHandler.java
@@ -105,7 +105,8 @@ class MenuListViewTouchHandler implements RecyclerView.OnItemTouchListener {
if (mDragToInteractAnimationController.maybeConsumeUpMotionEvent(motionEvent)
== empty) {
mVelocityTracker.computeCurrentVelocity(VELOCITY_UNIT_SECONDS);
- mMenuAnimationController.flingMenuThenSpringToEdge(endX,
+ mMenuAnimationController.flingMenuThenSpringToEdge(
+ new PointF(endX, mMenuTranslationDown.y + dy),
mVelocityTracker.getXVelocity(), mVelocityTracker.getYVelocity());
mMenuAnimationController.fadeOutIfEnabled();
}
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuView.java b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuView.java
index 3f49010aaaab..ae39b72b2585 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuView.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuView.java
@@ -284,13 +284,36 @@ class MenuView extends FrameLayout implements
onEdgeChanged();
onPositionChanged();
- if (mFeaturesChangeListener != null) {
+ boolean shouldSendFeatureChangeNotification =
+ com.android.systemui.Flags.floatingMenuNotifyTargetsChangedOnStrictDiff()
+ ? !areFeatureListsIdentical(targetFeatures, newTargetFeatures)
+ : true;
+ if (mFeaturesChangeListener != null && shouldSendFeatureChangeNotification) {
mFeaturesChangeListener.onChange(newTargetFeatures);
}
mMenuAnimationController.fadeOutIfEnabled();
}
+ /**
+ * Returns true if the given feature lists are identical lists, i.e. the same list of {@link
+ * AccessibilityTarget} (equality checked via UID) in the same order.
+ */
+ private boolean areFeatureListsIdentical(
+ List<AccessibilityTarget> currentFeatures, List<AccessibilityTarget> newFeatures) {
+ if (currentFeatures.size() != newFeatures.size()) {
+ return false;
+ }
+
+ for (int i = 0; i < currentFeatures.size(); i++) {
+ if (currentFeatures.get(i).getUid() != newFeatures.get(i).getUid()) {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
private void onMenuFadeEffectInfoChanged(MenuFadeEffectInfo fadeEffectInfo) {
mMenuAnimationController.updateOpacityWith(fadeEffectInfo.isFadeEffectEnabled(),
fadeEffectInfo.getOpacity());
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuViewAppearance.java b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuViewAppearance.java
index a700cbef2e16..bd3dfe049587 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuViewAppearance.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuViewAppearance.java
@@ -28,12 +28,14 @@ import android.graphics.Insets;
import android.graphics.PointF;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
+import android.view.DisplayCutout;
import android.view.WindowInsets;
import android.view.WindowManager;
import android.view.WindowMetrics;
import androidx.annotation.DimenRes;
+import com.android.internal.annotations.VisibleForTesting;
import com.android.systemui.res.R;
import java.lang.annotation.Retention;
@@ -291,7 +293,7 @@ class MenuViewAppearance {
final WindowMetrics windowMetrics = mWindowManager.getCurrentWindowMetrics();
final WindowInsets windowInsets = windowMetrics.getWindowInsets();
final Insets insets = windowInsets.getInsetsIgnoringVisibility(
- WindowInsets.Type.systemBars() | WindowInsets.Type.displayCutout());
+ WindowInsets.Type.systemBars());
final Rect bounds = new Rect(windowMetrics.getBounds());
bounds.left += insets.left;
@@ -302,6 +304,37 @@ class MenuViewAppearance {
return bounds;
}
+ DisplayCutout getDisplayCutout() {
+ return mWindowManager.getCurrentWindowMetrics().getWindowInsets().getDisplayCutout();
+ }
+
+ float avoidVerticalDisplayCutout(float y, Rect bounds, Rect cutout) {
+ int menuHeight = calculateActualMenuHeight();
+ return avoidVerticalDisplayCutout(y, menuHeight, bounds, cutout);
+ }
+
+ @VisibleForTesting
+ public static float avoidVerticalDisplayCutout(
+ float y, float menuHeight, Rect bounds, Rect cutout) {
+ if (cutout.top > y + menuHeight || cutout.bottom < y) {
+ return y;
+ }
+
+ boolean topAvailable = cutout.top - bounds.top >= menuHeight;
+ boolean bottomAvailable = bounds.bottom - cutout.bottom >= menuHeight;
+ boolean topOrBottom;
+ if (!topAvailable && !bottomAvailable) {
+ return y;
+ } else if (topAvailable && !bottomAvailable) {
+ topOrBottom = true;
+ } else if (!topAvailable && bottomAvailable) {
+ topOrBottom = false;
+ } else {
+ topOrBottom = y + menuHeight * 0.5f < cutout.centerY();
+ }
+ return (topOrBottom) ? cutout.top - menuHeight : cutout.bottom;
+ }
+
boolean isMenuOnLeftSide() {
return mPercentagePosition.getPercentageX() < 0.5f;
}
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuViewLayer.java b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuViewLayer.java
index 7a674e2fa6f1..81095220b4a6 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuViewLayer.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuViewLayer.java
@@ -21,17 +21,12 @@ import static android.view.WindowInsets.Type.ime;
import static androidx.core.view.WindowInsetsCompat.Type;
import static com.android.internal.accessibility.AccessibilityShortcutController.ACCESSIBILITY_BUTTON_COMPONENT_NAME;
-import static com.android.internal.accessibility.common.ShortcutConstants.AccessibilityFragmentType.INVISIBLE_TOGGLE;
-import static com.android.internal.accessibility.common.ShortcutConstants.UserShortcutType.HARDWARE;
import static com.android.internal.accessibility.common.ShortcutConstants.UserShortcutType.SOFTWARE;
-import static com.android.internal.accessibility.util.AccessibilityUtils.getAccessibilityServiceFragmentType;
-import static com.android.internal.accessibility.util.AccessibilityUtils.setAccessibilityServiceState;
import static com.android.systemui.accessibility.floatingmenu.MenuMessageView.Index;
import static com.android.systemui.accessibility.floatingmenu.MenuNotificationFactory.ACTION_DELETE;
import static com.android.systemui.accessibility.floatingmenu.MenuNotificationFactory.ACTION_UNDO;
import static com.android.systemui.util.PluralMessageFormaterKt.icuMessageFormat;
-import android.accessibilityservice.AccessibilityServiceInfo;
import android.annotation.IntDef;
import android.annotation.StringDef;
import android.annotation.SuppressLint;
@@ -39,7 +34,6 @@ import android.app.NotificationManager;
import android.app.StatusBarManager;
import android.content.BroadcastReceiver;
import android.content.ComponentCallbacks;
-import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
@@ -174,46 +168,13 @@ class MenuViewLayer extends FrameLayout implements
final Runnable mDismissMenuAction = new Runnable() {
@Override
public void run() {
- if (android.view.accessibility.Flags.a11yQsShortcut()) {
- mAccessibilityManager.enableShortcutsForTargets(
- /* enable= */ false,
- ShortcutConstants.UserShortcutType.SOFTWARE,
- new ArraySet<>(
- mAccessibilityManager.getAccessibilityShortcutTargets(SOFTWARE)),
- mSecureSettings.getRealUserHandle(UserHandle.USER_CURRENT)
- );
- } else {
- mSecureSettings.putStringForUser(
- Settings.Secure.ACCESSIBILITY_BUTTON_TARGETS, /* value= */ "",
- UserHandle.USER_CURRENT);
-
- final List<ComponentName> hardwareKeyShortcutComponents =
- mAccessibilityManager.getAccessibilityShortcutTargets(HARDWARE)
- .stream()
- .map(ComponentName::unflattenFromString)
- .toList();
-
- // Should disable the corresponding service when the fragment type is
- // INVISIBLE_TOGGLE, which will enable service when the shortcut is on.
- final List<AccessibilityServiceInfo> serviceInfoList =
- mAccessibilityManager.getEnabledAccessibilityServiceList(
- AccessibilityServiceInfo.FEEDBACK_ALL_MASK);
- serviceInfoList.forEach(info -> {
- if (getAccessibilityServiceFragmentType(info) != INVISIBLE_TOGGLE) {
- return;
- }
-
- final ComponentName serviceComponentName = info.getComponentName();
- if (hardwareKeyShortcutComponents.contains(serviceComponentName)) {
- return;
- }
-
- setAccessibilityServiceState(
- getContext(), serviceComponentName, /* enabled= */ false,
- mSecureSettings.getRealUserHandle(UserHandle.USER_CURRENT));
- });
- }
-
+ mAccessibilityManager.enableShortcutsForTargets(
+ /* enable= */ false,
+ ShortcutConstants.UserShortcutType.SOFTWARE,
+ new ArraySet<>(
+ mAccessibilityManager.getAccessibilityShortcutTargets(SOFTWARE)),
+ mSecureSettings.getRealUserHandle(UserHandle.USER_CURRENT)
+ );
mFloatingMenu.hide();
}
};
@@ -516,7 +477,7 @@ class MenuViewLayer extends FrameLayout implements
return;
}
mMenuAnimationController.flingMenuThenSpringToEdge(
- mMenuView.getMenuPosition().x, 100f, 0f);
+ mMenuView.getMenuPosition(), 100f, 0f);
Intent intent = getIntentForEditScreen();
PackageManager packageManager = getContext().getPackageManager();
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/hearingaid/HearingDevicesDialogDelegate.java b/packages/SystemUI/src/com/android/systemui/accessibility/hearingaid/HearingDevicesDialogDelegate.java
index 73aabc3cf95a..438184d4d2d6 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/hearingaid/HearingDevicesDialogDelegate.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/hearingaid/HearingDevicesDialogDelegate.java
@@ -286,9 +286,7 @@ public class HearingDevicesDialogDelegate implements SystemUIDialog.Delegate,
if (com.android.settingslib.flags.Flags.hearingDevicesAmbientVolumeControl()) {
setupAmbientControls();
}
- if (com.android.systemui.Flags.hearingDevicesDialogRelatedTools()) {
- setupRelatedToolsView(dialog);
- }
+ setupRelatedToolsView(dialog);
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/hearingaid/HearingDevicesDialogReceiver.java b/packages/SystemUI/src/com/android/systemui/accessibility/hearingaid/HearingDevicesDialogReceiver.java
index 02e65fd9031b..90b8fa0177b7 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/hearingaid/HearingDevicesDialogReceiver.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/hearingaid/HearingDevicesDialogReceiver.java
@@ -22,8 +22,6 @@ import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
-import com.android.systemui.Flags;
-
import javax.inject.Inject;
/**
@@ -43,10 +41,6 @@ public class HearingDevicesDialogReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
- if (!Flags.hearingAidsQsTileDialog()) {
- return;
- }
-
if (ACTION.equals(intent.getAction())) {
mDialogManager.showDialog(/* expandable= */ null, LAUNCH_SOURCE_A11Y);
}
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/qs/QSAccessibilityModule.kt b/packages/SystemUI/src/com/android/systemui/accessibility/qs/QSAccessibilityModule.kt
index 610e3f8a8c84..fb47d429e271 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/qs/QSAccessibilityModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/qs/QSAccessibilityModule.kt
@@ -411,7 +411,7 @@ interface QSAccessibilityModule {
stateInteractor: HearingDevicesTileDataInteractor,
userActionInteractor: HearingDevicesTileUserActionInteractor,
): QSTileViewModel {
- return if (Flags.hearingAidsQsTileDialog() && Flags.qsNewTilesFuture()) {
+ return if (Flags.qsNewTilesFuture()) {
factory.create(
TileSpec.create(HEARING_DEVICES_TILE_SPEC),
userActionInteractor,
diff --git a/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardImageLoader.kt b/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardImageLoader.kt
index 8814a1298e44..c0ad3b8c97c9 100644
--- a/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardImageLoader.kt
+++ b/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardImageLoader.kt
@@ -20,15 +20,17 @@ import android.graphics.Bitmap
import android.net.Uri
import android.util.Log
import android.util.Size
-import com.android.systemui.res.R
+import com.android.app.tracing.coroutines.launchTraced as launch
+import com.android.systemui.Flags.clipboardOverlayMultiuser
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.dagger.qualifiers.Background
+import com.android.systemui.res.R
+import com.android.systemui.settings.UserTracker
import java.io.IOException
import java.util.function.Consumer
import javax.inject.Inject
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.CoroutineScope
-import com.android.app.tracing.coroutines.launchTraced as launch
import kotlinx.coroutines.withContext
import kotlinx.coroutines.withTimeoutOrNull
@@ -36,8 +38,9 @@ class ClipboardImageLoader
@Inject
constructor(
private val context: Context,
+ private val userTracker: UserTracker,
@Background private val bgDispatcher: CoroutineDispatcher,
- @Application private val mainScope: CoroutineScope
+ @Application private val mainScope: CoroutineScope,
) {
private val TAG: String = "ClipboardImageLoader"
@@ -46,7 +49,15 @@ constructor(
withContext(bgDispatcher) {
try {
val size = context.resources.getDimensionPixelSize(R.dimen.overlay_x_scale)
- context.contentResolver.loadThumbnail(uri, Size(size, size * 4), null)
+ if (clipboardOverlayMultiuser()) {
+ userTracker.userContentResolver.loadThumbnail(
+ uri,
+ Size(size, size * 4),
+ null,
+ )
+ } else {
+ context.contentResolver.loadThumbnail(uri, Size(size, size * 4), null)
+ }
} catch (e: IOException) {
Log.e(TAG, "Thumbnail loading failed!", e)
null
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java
index 7ebe52f3bd58..c02784dfab1b 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java
@@ -31,6 +31,7 @@ import com.android.systemui.BootCompleteCache;
import com.android.systemui.BootCompleteCacheImpl;
import com.android.systemui.CameraProtectionModule;
import com.android.systemui.CoreStartable;
+import com.android.systemui.KairosCoreStartableModule;
import com.android.systemui.SystemUISecondaryUserService;
import com.android.systemui.activity.ActivityManagerModule;
import com.android.systemui.ambient.dagger.AmbientModule;
@@ -232,6 +233,7 @@ import javax.inject.Named;
FlagsModule.class,
FlagDependenciesModule.class,
FooterActionsModule.class,
+ KairosCoreStartableModule.class,
GestureModule.class,
InputMethodModule.class,
KeyEventRepositoryModule.class,
diff --git a/packages/SystemUI/src/com/android/systemui/inputdevice/tutorial/ui/composable/ActionTutorialContent.kt b/packages/SystemUI/src/com/android/systemui/inputdevice/tutorial/ui/composable/ActionTutorialContent.kt
index c40adfe6baf8..08e0a9d52faa 100644
--- a/packages/SystemUI/src/com/android/systemui/inputdevice/tutorial/ui/composable/ActionTutorialContent.kt
+++ b/packages/SystemUI/src/com/android/systemui/inputdevice/tutorial/ui/composable/ActionTutorialContent.kt
@@ -33,6 +33,7 @@ import androidx.compose.foundation.layout.safeDrawingPadding
import androidx.compose.foundation.layout.width
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
+import androidx.compose.material3.windowsizeclass.WindowHeightSizeClass
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
@@ -45,11 +46,13 @@ import androidx.compose.ui.graphics.graphicsLayer
import androidx.compose.ui.platform.LocalConfiguration
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp
+import com.android.compose.windowsizeclass.LocalWindowSizeClass
import com.android.systemui.inputdevice.tutorial.ui.composable.TutorialActionState.Error
import com.android.systemui.inputdevice.tutorial.ui.composable.TutorialActionState.Finished
import com.android.systemui.inputdevice.tutorial.ui.composable.TutorialActionState.InProgress
import com.android.systemui.inputdevice.tutorial.ui.composable.TutorialActionState.InProgressAfterError
import com.android.systemui.inputdevice.tutorial.ui.composable.TutorialActionState.NotStarted
+import com.android.systemui.keyboard.shortcut.ui.composable.hasCompactWindowSize
sealed interface TutorialActionState {
data object NotStarted : TutorialActionState
@@ -82,18 +85,25 @@ fun ActionTutorialContent(
) {
Column(
verticalArrangement = Arrangement.Center,
- modifier =
- Modifier.fillMaxSize()
- .background(config.colors.background)
- .safeDrawingPadding()
- .padding(start = 48.dp, top = 100.dp, end = 48.dp, bottom = 8.dp),
+ modifier = Modifier.fillMaxSize().background(config.colors.background).safeDrawingPadding(),
) {
+ val isCompactWindow = hasCompactWindowSize()
when (LocalConfiguration.current.orientation) {
Configuration.ORIENTATION_LANDSCAPE -> {
- HorizontalDescriptionAndAnimation(actionState, config, Modifier.weight(1f))
+ HorizontalDescriptionAndAnimation(
+ actionState,
+ config,
+ isCompactWindow,
+ Modifier.weight(1f),
+ )
}
else -> {
- VerticalDescriptionAndAnimation(actionState, config, Modifier.weight(1f))
+ VerticalDescriptionAndAnimation(
+ actionState,
+ config,
+ isCompactWindow,
+ Modifier.weight(1f),
+ )
}
}
val buttonAlpha by animateFloatAsState(if (actionState is Finished) 1f else 0f)
@@ -109,11 +119,15 @@ fun ActionTutorialContent(
private fun HorizontalDescriptionAndAnimation(
actionState: TutorialActionState,
config: TutorialScreenConfig,
+ isCompactWindow: Boolean,
modifier: Modifier = Modifier,
) {
- Row(modifier = modifier.fillMaxWidth()) {
- TutorialDescription(actionState, config, modifier = Modifier.weight(1f))
- Spacer(modifier = Modifier.width(70.dp))
+ Row(
+ modifier =
+ modifier.fillMaxWidth().padding(start = 48.dp, top = 100.dp, end = 48.dp, bottom = 8.dp)
+ ) {
+ TutorialDescription(actionState, config, isCompactWindow, modifier = Modifier.weight(1f))
+ Spacer(modifier = Modifier.width(24.dp))
TutorialAnimation(actionState, config, modifier = Modifier.weight(1f))
}
}
@@ -122,20 +136,25 @@ private fun HorizontalDescriptionAndAnimation(
private fun VerticalDescriptionAndAnimation(
actionState: TutorialActionState,
config: TutorialScreenConfig,
+ isCompactWindow: Boolean,
modifier: Modifier = Modifier,
) {
- Column(modifier = modifier.fillMaxWidth().padding(horizontal = 40.dp, vertical = 40.dp)) {
- Spacer(modifier = Modifier.weight(0.1f))
+ val horizontalPadding = if (isCompactWindow) 24.dp else 96.dp
+ // Represents the majority of tablets in portrait - we need extra spacer at the top and bottom
+ val isTablet = LocalWindowSizeClass.current.heightSizeClass == WindowHeightSizeClass.Expanded
+ Column(
+ modifier =
+ modifier.fillMaxWidth().padding(start = 0.dp, top = 100.dp, end = 0.dp, bottom = 8.dp)
+ ) {
+ if (isTablet) Spacer(modifier = Modifier.weight(0.3f))
TutorialDescription(
actionState,
config,
- modifier =
- Modifier.weight(0.2f)
- // extra padding to better align with animation which has embedded padding
- .padding(horizontal = 15.dp),
+ isCompactWindow,
+ modifier = Modifier.weight(1f).padding(horizontal = horizontalPadding),
)
- Spacer(modifier = Modifier.width(70.dp))
- TutorialAnimation(actionState, config, modifier = Modifier.weight(1f))
+ TutorialAnimation(actionState, config, modifier = Modifier.weight(1.8f).fillMaxWidth())
+ if (isTablet) Spacer(modifier = Modifier.weight(0.3f))
}
}
@@ -143,6 +162,7 @@ private fun VerticalDescriptionAndAnimation(
fun TutorialDescription(
actionState: TutorialActionState,
config: TutorialScreenConfig,
+ isCompactWindow: Boolean,
modifier: Modifier = Modifier,
) {
val focusRequester = remember { FocusRequester() }
@@ -159,7 +179,9 @@ fun TutorialDescription(
Column(verticalArrangement = Arrangement.Top, modifier = modifier) {
Text(
text = stringResource(id = titleTextId),
- style = MaterialTheme.typography.displayLarge,
+ style =
+ if (isCompactWindow) MaterialTheme.typography.headlineLarge
+ else MaterialTheme.typography.displayMedium,
color = config.colors.title,
modifier = Modifier.focusRequester(focusRequester).focusable(),
)
diff --git a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/composable/ShortcutHelper.kt b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/composable/ShortcutHelper.kt
index ba31d08c9c1b..bf60c9a52417 100644
--- a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/composable/ShortcutHelper.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/composable/ShortcutHelper.kt
@@ -105,6 +105,7 @@ import androidx.compose.ui.semantics.role
import androidx.compose.ui.semantics.semantics
import androidx.compose.ui.text.SpanStyle
import androidx.compose.ui.text.buildAnnotatedString
+import androidx.compose.ui.text.style.Hyphens
import androidx.compose.ui.text.withStyle
import androidx.compose.ui.unit.Density
import androidx.compose.ui.unit.Dp
@@ -969,7 +970,7 @@ private fun CategoryItemTwoPane(
Text(
fontSize = 18.sp,
color = colors.textColor(selected).value,
- style = MaterialTheme.typography.titleSmall,
+ style = MaterialTheme.typography.titleSmall.copy(hyphens = Hyphens.Auto),
text = label,
)
}
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/animation/ColorSchemeTransition.kt b/packages/SystemUI/src/com/android/systemui/media/controls/ui/animation/ColorSchemeTransition.kt
index 21407f3bd6d4..02b403786768 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/animation/ColorSchemeTransition.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/animation/ColorSchemeTransition.kt
@@ -27,6 +27,7 @@ import android.graphics.drawable.RippleDrawable
import com.android.internal.R
import com.android.internal.annotations.VisibleForTesting
import com.android.settingslib.Utils
+import com.android.systemui.Flags
import com.android.systemui.media.controls.ui.view.MediaViewHolder
import com.android.systemui.monet.ColorScheme
import com.android.systemui.surfaceeffects.loadingeffect.LoadingEffect
@@ -51,7 +52,7 @@ interface ColorTransition {
open class AnimatingColorTransition(
private val defaultColor: Int,
private val extractColor: (ColorScheme) -> Int,
- private val applyColor: (Int) -> Unit
+ private val applyColor: (Int) -> Unit,
) : AnimatorUpdateListener, ColorTransition {
private val argbEvaluator = ArgbEvaluator()
@@ -105,35 +106,52 @@ internal constructor(
private val mediaViewHolder: MediaViewHolder,
private val multiRippleController: MultiRippleController,
private val turbulenceNoiseController: TurbulenceNoiseController,
- animatingColorTransitionFactory: AnimatingColorTransitionFactory
+ animatingColorTransitionFactory: AnimatingColorTransitionFactory,
) {
constructor(
context: Context,
mediaViewHolder: MediaViewHolder,
multiRippleController: MultiRippleController,
- turbulenceNoiseController: TurbulenceNoiseController
+ turbulenceNoiseController: TurbulenceNoiseController,
) : this(
context,
mediaViewHolder,
multiRippleController,
turbulenceNoiseController,
- ::AnimatingColorTransition
+ ::AnimatingColorTransition,
)
+
var loadingEffect: LoadingEffect? = null
- val bgColor = context.getColor(com.google.android.material.R.color.material_dynamic_neutral20)
+ val bgColor =
+ if (Flags.mediaControlsUiUpdate()) {
+ context.getColor(R.color.materialColorOnSurface)
+ } else {
+ context.getColor(com.google.android.material.R.color.material_dynamic_neutral20)
+ }
+
+ val textColor = context.getColor(R.color.materialColorInverseOnSurface)
+ val buttonBgColor = context.getColor(R.color.materialColorPrimary)
+ val insideButtonColor = context.getColor(R.color.materialColorOnPrimary)
+
val surfaceColor =
animatingColorTransitionFactory(bgColor, ::surfaceFromScheme) { surfaceColor ->
val colorList = ColorStateList.valueOf(surfaceColor)
- mediaViewHolder.seamlessIcon.imageTintList = colorList
- mediaViewHolder.seamlessText.setTextColor(surfaceColor)
mediaViewHolder.albumView.backgroundTintList = colorList
mediaViewHolder.gutsViewHolder.setSurfaceColor(surfaceColor)
+
+ if (Flags.mediaControlsUiUpdate()) return@animatingColorTransitionFactory
+ mediaViewHolder.seamlessIcon.imageTintList = colorList
+ mediaViewHolder.seamlessText.setTextColor(surfaceColor)
}
val accentPrimary =
animatingColorTransitionFactory(
- loadDefaultColor(R.attr.textColorPrimary),
- ::accentPrimaryFromScheme
+ if (Flags.mediaControlsUiUpdate()) {
+ buttonBgColor
+ } else {
+ loadDefaultColor(R.attr.textColorPrimary)
+ },
+ ::accentPrimaryFromScheme,
) { accentPrimary ->
val accentColorList = ColorStateList.valueOf(accentPrimary)
mediaViewHolder.actionPlayPause.backgroundTintList = accentColorList
@@ -145,8 +163,12 @@ internal constructor(
val accentSecondary =
animatingColorTransitionFactory(
- loadDefaultColor(R.attr.textColorPrimary),
- ::accentSecondaryFromScheme
+ if (Flags.mediaControlsUiUpdate()) {
+ buttonBgColor
+ } else {
+ loadDefaultColor(R.attr.textColorPrimary)
+ },
+ ::accentSecondaryFromScheme,
) { accentSecondary ->
val colorList = ColorStateList.valueOf(accentSecondary)
(mediaViewHolder.seamlessButton.background as? RippleDrawable)?.let {
@@ -157,7 +179,11 @@ internal constructor(
val colorSeamless =
animatingColorTransitionFactory(
- loadDefaultColor(R.attr.textColorPrimary),
+ if (Flags.mediaControlsUiUpdate()) {
+ buttonBgColor
+ } else {
+ loadDefaultColor(R.attr.textColorPrimary)
+ },
{ colorScheme: ColorScheme ->
// A1-100 dark in dark theme, A1-200 in light theme
if (
@@ -170,13 +196,17 @@ internal constructor(
{ seamlessColor: Int ->
val accentColorList = ColorStateList.valueOf(seamlessColor)
mediaViewHolder.seamlessButton.backgroundTintList = accentColorList
- }
+ },
)
val textPrimary =
animatingColorTransitionFactory(
- loadDefaultColor(R.attr.textColorPrimary),
- ::textPrimaryFromScheme
+ if (Flags.mediaControlsUiUpdate()) {
+ textColor
+ } else {
+ loadDefaultColor(R.attr.textColorPrimary)
+ },
+ ::textPrimaryFromScheme,
) { textPrimary ->
mediaViewHolder.titleText.setTextColor(textPrimary)
val textColorList = ColorStateList.valueOf(textPrimary)
@@ -192,25 +222,41 @@ internal constructor(
val textPrimaryInverse =
animatingColorTransitionFactory(
- loadDefaultColor(R.attr.textColorPrimaryInverse),
- ::textPrimaryInverseFromScheme
+ if (Flags.mediaControlsUiUpdate()) {
+ insideButtonColor
+ } else {
+ loadDefaultColor(R.attr.textColorPrimaryInverse)
+ },
+ ::textPrimaryInverseFromScheme,
) { textPrimaryInverse ->
- mediaViewHolder.actionPlayPause.imageTintList =
- ColorStateList.valueOf(textPrimaryInverse)
+ val colorList = ColorStateList.valueOf(textPrimaryInverse)
+ mediaViewHolder.actionPlayPause.imageTintList = colorList
+
+ if (!Flags.mediaControlsUiUpdate()) return@animatingColorTransitionFactory
+ mediaViewHolder.seamlessIcon.imageTintList = colorList
+ mediaViewHolder.seamlessText.setTextColor(textPrimaryInverse)
}
val textSecondary =
animatingColorTransitionFactory(
- loadDefaultColor(R.attr.textColorSecondary),
- ::textSecondaryFromScheme
+ if (Flags.mediaControlsUiUpdate()) {
+ textColor
+ } else {
+ loadDefaultColor(R.attr.textColorSecondary)
+ },
+ ::textSecondaryFromScheme,
) { textSecondary ->
mediaViewHolder.artistText.setTextColor(textSecondary)
}
val textTertiary =
animatingColorTransitionFactory(
- loadDefaultColor(R.attr.textColorTertiary),
- ::textTertiaryFromScheme
+ if (Flags.mediaControlsUiUpdate()) {
+ textColor
+ } else {
+ loadDefaultColor(R.attr.textColorTertiary)
+ },
+ ::textTertiaryFromScheme,
) { textTertiary ->
mediaViewHolder.seekBar.progressBackgroundTintList =
ColorStateList.valueOf(textTertiary)
diff --git a/packages/SystemUI/src/com/android/systemui/qs/BuildTextView.kt b/packages/SystemUI/src/com/android/systemui/qs/BuildTextView.kt
new file mode 100644
index 000000000000..1f864e9d7b8b
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/BuildTextView.kt
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.qs
+
+import android.content.Context
+import android.util.AttributeSet
+import android.view.accessibility.AccessibilityNodeInfo
+import com.android.systemui.res.R
+import com.android.systemui.util.DelayableMarqueeTextView
+
+class BuildTextView
+@JvmOverloads
+constructor(
+ context: Context,
+ attrs: AttributeSet? = null,
+ defStyleAttr: Int = 0,
+ defStyleRes: Int = 0,
+) : DelayableMarqueeTextView(context, attrs, defStyleAttr, defStyleRes) {
+
+ override fun onInitializeAccessibilityNodeInfo(info: AccessibilityNodeInfo?) {
+ super.onInitializeAccessibilityNodeInfo(info)
+ // Clear selected state so it's not announced by accessibility, but we can still marquee.
+ info?.isSelected = false
+ info?.addAction(
+ AccessibilityNodeInfo.AccessibilityAction(
+ AccessibilityNodeInfo.AccessibilityAction.ACTION_LONG_CLICK.id,
+ resources.getString(R.string.copy_to_clipboard_a11y_action),
+ )
+ )
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/pipeline/dagger/BaseAutoAddableModule.kt b/packages/SystemUI/src/com/android/systemui/qs/pipeline/dagger/BaseAutoAddableModule.kt
index e1ec338cec6f..a6edb586776b 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/pipeline/dagger/BaseAutoAddableModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/pipeline/dagger/BaseAutoAddableModule.kt
@@ -27,7 +27,6 @@ import com.android.systemui.qs.pipeline.domain.autoaddable.DataSaverAutoAddable
import com.android.systemui.qs.pipeline.domain.autoaddable.DeviceControlsAutoAddable
import com.android.systemui.qs.pipeline.domain.autoaddable.HotspotAutoAddable
import com.android.systemui.qs.pipeline.domain.autoaddable.NightDisplayAutoAddable
-import com.android.systemui.qs.pipeline.domain.autoaddable.ReduceBrightColorsAutoAddable
import com.android.systemui.qs.pipeline.domain.autoaddable.WalletAutoAddable
import com.android.systemui.qs.pipeline.domain.autoaddable.WorkTileAutoAddable
import com.android.systemui.qs.pipeline.domain.model.AutoAddable
@@ -45,11 +44,11 @@ interface BaseAutoAddableModule {
@ElementsIntoSet
fun providesAutoAddableSetting(
@Main resources: Resources,
- autoAddableSettingFactory: AutoAddableSetting.Factory
+ autoAddableSettingFactory: AutoAddableSetting.Factory,
): Set<AutoAddable> {
return AutoAddableSettingList.parseSettingsResource(
resources,
- autoAddableSettingFactory
+ autoAddableSettingFactory,
)
.toSet()
}
@@ -75,10 +74,6 @@ interface BaseAutoAddableModule {
@Binds @IntoSet fun bindNightDisplayAutoAddable(impl: NightDisplayAutoAddable): AutoAddable
- @Binds
- @IntoSet
- fun bindReduceBrightColorsAutoAddable(impl: ReduceBrightColorsAutoAddable): AutoAddable
-
@Binds @IntoSet fun bindWalletAutoAddable(impl: WalletAutoAddable): AutoAddable
@Binds @IntoSet fun bindWorkModeAutoAddable(impl: WorkTileAutoAddable): AutoAddable
diff --git a/packages/SystemUI/src/com/android/systemui/qs/pipeline/domain/autoaddable/A11yShortcutAutoAddableList.kt b/packages/SystemUI/src/com/android/systemui/qs/pipeline/domain/autoaddable/A11yShortcutAutoAddableList.kt
index a0c9737de0ee..791339c75eeb 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/pipeline/domain/autoaddable/A11yShortcutAutoAddableList.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/pipeline/domain/autoaddable/A11yShortcutAutoAddableList.kt
@@ -16,7 +16,6 @@
package com.android.systemui.qs.pipeline.domain.autoaddable
-import android.view.accessibility.Flags
import com.android.internal.accessibility.AccessibilityShortcutController
import com.android.systemui.qs.pipeline.domain.model.AutoAddable
import com.android.systemui.qs.pipeline.shared.TileSpec
@@ -33,31 +32,27 @@ object A11yShortcutAutoAddableList {
* accessibility features with shortcut options
*/
fun getA11yShortcutAutoAddables(factory: A11yShortcutAutoAddable.Factory): Set<AutoAddable> {
- return if (Flags.a11yQsShortcut()) {
- setOf(
- factory.create(
- TileSpec.create(ColorCorrectionTile.TILE_SPEC),
- AccessibilityShortcutController.DALTONIZER_COMPONENT_NAME
- ),
- factory.create(
- TileSpec.create(ColorInversionTile.TILE_SPEC),
- AccessibilityShortcutController.COLOR_INVERSION_COMPONENT_NAME
- ),
- factory.create(
- TileSpec.create(OneHandedModeTile.TILE_SPEC),
- AccessibilityShortcutController.ONE_HANDED_COMPONENT_NAME
- ),
- factory.create(
- TileSpec.create(ReduceBrightColorsTile.TILE_SPEC),
- AccessibilityShortcutController.REDUCE_BRIGHT_COLORS_COMPONENT_NAME
- ),
- factory.create(
- TileSpec.create(HearingDevicesTile.TILE_SPEC),
- AccessibilityShortcutController.ACCESSIBILITY_HEARING_AIDS_COMPONENT_NAME
- )
- )
- } else {
- emptySet()
- }
+ return setOf(
+ factory.create(
+ TileSpec.create(ColorCorrectionTile.TILE_SPEC),
+ AccessibilityShortcutController.DALTONIZER_COMPONENT_NAME,
+ ),
+ factory.create(
+ TileSpec.create(ColorInversionTile.TILE_SPEC),
+ AccessibilityShortcutController.COLOR_INVERSION_COMPONENT_NAME,
+ ),
+ factory.create(
+ TileSpec.create(OneHandedModeTile.TILE_SPEC),
+ AccessibilityShortcutController.ONE_HANDED_COMPONENT_NAME,
+ ),
+ factory.create(
+ TileSpec.create(ReduceBrightColorsTile.TILE_SPEC),
+ AccessibilityShortcutController.REDUCE_BRIGHT_COLORS_COMPONENT_NAME,
+ ),
+ factory.create(
+ TileSpec.create(HearingDevicesTile.TILE_SPEC),
+ AccessibilityShortcutController.ACCESSIBILITY_HEARING_AIDS_COMPONENT_NAME,
+ ),
+ )
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/pipeline/domain/autoaddable/ReduceBrightColorsAutoAddable.kt b/packages/SystemUI/src/com/android/systemui/qs/pipeline/domain/autoaddable/ReduceBrightColorsAutoAddable.kt
deleted file mode 100644
index bde682074780..000000000000
--- a/packages/SystemUI/src/com/android/systemui/qs/pipeline/domain/autoaddable/ReduceBrightColorsAutoAddable.kt
+++ /dev/null
@@ -1,72 +0,0 @@
-/*
- * Copyright (C) 2023 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.qs.pipeline.domain.autoaddable
-
-import android.view.accessibility.Flags
-import com.android.systemui.dagger.SysUISingleton
-import com.android.systemui.qs.ReduceBrightColorsController
-import com.android.systemui.qs.dagger.QSFlagsModule.RBC_AVAILABLE
-import com.android.systemui.qs.pipeline.domain.model.AutoAddSignal
-import com.android.systemui.qs.pipeline.domain.model.AutoAddTracking
-import com.android.systemui.qs.pipeline.shared.TileSpec
-import com.android.systemui.qs.tiles.ReduceBrightColorsTile
-import javax.inject.Inject
-import javax.inject.Named
-import kotlinx.coroutines.channels.ProducerScope
-
-/**
- * [AutoAddable] for [ReduceBrightColorsTile.TILE_SPEC].
- *
- * It will send a signal to add the tile when reduce bright colors is enabled.
- */
-@SysUISingleton
-class ReduceBrightColorsAutoAddable
-@Inject
-constructor(
- controller: ReduceBrightColorsController,
- @Named(RBC_AVAILABLE) private var available: Boolean,
-) :
- CallbackControllerAutoAddable<
- ReduceBrightColorsController.Listener,
- ReduceBrightColorsController,
- >(controller) {
-
- override val spec: TileSpec
- get() = TileSpec.create(ReduceBrightColorsTile.TILE_SPEC)
-
- override fun ProducerScope<AutoAddSignal>.getCallback(): ReduceBrightColorsController.Listener {
- return object : ReduceBrightColorsController.Listener {
- override fun onActivated(activated: Boolean) {
- if (activated && available) {
- sendAdd()
- }
- }
- }
- }
-
- override val autoAddTracking
- get() =
- if (Flags.a11yQsShortcut()) {
- AutoAddTracking.Disabled
- } else if (available) {
- super.autoAddTracking
- } else {
- AutoAddTracking.Disabled
- }
-
- override val description = "ReduceBrightColorsAutoAddable ($autoAddTracking)"
-}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/pipeline/domain/interactor/AccessibilityTilesInteractor.kt b/packages/SystemUI/src/com/android/systemui/qs/pipeline/domain/interactor/AccessibilityTilesInteractor.kt
index 56c3e0e648bf..481d1e3c28bd 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/pipeline/domain/interactor/AccessibilityTilesInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/pipeline/domain/interactor/AccessibilityTilesInteractor.kt
@@ -17,7 +17,7 @@
package com.android.systemui.qs.pipeline.domain.interactor
import android.content.Context
-import android.view.accessibility.Flags
+import com.android.app.tracing.coroutines.launchTraced as launch
import com.android.systemui.accessibility.data.repository.AccessibilityQsShortcutsRepository
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
@@ -30,7 +30,6 @@ import javax.inject.Inject
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.collectLatest
-import com.android.app.tracing.coroutines.launchTraced as launch
/** Observe the tiles in the QS Panel and perform accessibility related actions */
@SysUISingleton
@@ -48,10 +47,7 @@ constructor(
if (!initialized.compareAndSet(/* expectedValue= */ false, /* newValue= */ true)) {
return
}
-
- if (Flags.a11yQsShortcut()) {
- startObservingTiles(currentTilesInteractor)
- }
+ startObservingTiles(currentTilesInteractor)
}
private fun startObservingTiles(currentTilesInteractor: CurrentTilesInteractor) {
@@ -63,14 +59,11 @@ constructor(
.collectLatest {
a11yQsShortcutsRepository.notifyAccessibilityManagerTilesChanged(
it.userContext,
- it.currentTileSpecs
+ it.currentTileSpecs,
)
}
}
}
- private data class Data(
- val currentTileSpecs: List<TileSpec>,
- val userContext: Context,
- )
+ private data class Data(val currentTileSpecs: List<TileSpec>, val userContext: Context)
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/HearingDevicesTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/HearingDevicesTile.java
index 74563fff8775..22b742f2f05b 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/HearingDevicesTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/HearingDevicesTile.java
@@ -29,7 +29,6 @@ import android.widget.Button;
import androidx.annotation.Nullable;
import com.android.internal.logging.MetricsLogger;
-import com.android.systemui.Flags;
import com.android.systemui.accessibility.hearingaid.HearingDevicesChecker;
import com.android.systemui.accessibility.hearingaid.HearingDevicesDialogManager;
import com.android.systemui.animation.Expandable;
@@ -138,9 +137,4 @@ public class HearingDevicesTile extends QSTileImpl<BooleanState> {
public CharSequence getTileLabel() {
return mContext.getString(R.string.quick_settings_hearing_devices_label);
}
-
- @Override
- public boolean isAvailable() {
- return Flags.hearingAidsQsTileDialog();
- }
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/hearingdevices/domain/interactor/HearingDevicesTileDataInteractor.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/hearingdevices/domain/interactor/HearingDevicesTileDataInteractor.kt
index ec0a4e9db896..33b7feb49b09 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/hearingdevices/domain/interactor/HearingDevicesTileDataInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/hearingdevices/domain/interactor/HearingDevicesTileDataInteractor.kt
@@ -17,7 +17,6 @@
package com.android.systemui.qs.tiles.impl.hearingdevices.domain.interactor
import android.os.UserHandle
-import com.android.systemui.Flags
import com.android.systemui.accessibility.hearingaid.HearingDevicesChecker
import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.qs.tiles.base.interactor.DataUpdateTrigger
@@ -62,8 +61,7 @@ constructor(
.flowOn(backgroundContext)
.distinctUntilChanged()
- override fun availability(user: UserHandle): Flow<Boolean> =
- flowOf(Flags.hearingAidsQsTileDialog())
+ override fun availability(user: UserHandle): Flow<Boolean> = flowOf(true)
private fun getModel() =
HearingDevicesTileModel(
diff --git a/packages/SystemUI/src/com/android/systemui/shade/display/ShadeDisplayPolicy.kt b/packages/SystemUI/src/com/android/systemui/shade/display/ShadeDisplayPolicy.kt
index 17b5e5b584b4..d53f9f7ec595 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/display/ShadeDisplayPolicy.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/display/ShadeDisplayPolicy.kt
@@ -16,6 +16,7 @@
package com.android.systemui.shade.display
+import com.android.systemui.shade.domain.interactor.ShadeExpandedStateInteractor.ShadeElement
import dagger.Binds
import dagger.Module
import dagger.multibindings.IntoSet
@@ -33,11 +34,33 @@ interface ShadeDisplayPolicy {
val displayId: StateFlow<Int>
}
+/** Return the latest element the user intended to expand in the shade (notifications or QS). */
+interface ShadeExpansionIntent {
+ /**
+ * Returns the latest element the user intended to expand in the shade (notifications or QS).
+ *
+ * When the shade moves to a different display (e.g., due to a touch on the status bar of an
+ * external display), it's first collapsed and then re-expanded on the target display.
+ *
+ * If the user was trying to open a specific element (QS or notifications) when the shade was on
+ * the original display, that intention might be lost during the collapse/re-expand transition.
+ * This is used to preserve the user's intention, ensuring the correct element is expanded on
+ * the target display.
+ *
+ * Note that the expansion intent is kept for a very short amount of time (ideally, just a bit
+ * above the time it takes for the shade to collapse)
+ */
+ fun consumeExpansionIntent(): ShadeElement?
+}
+
@Module
interface ShadeDisplayPolicyModule {
@Binds fun provideDefaultPolicy(impl: StatusBarTouchShadeDisplayPolicy): ShadeDisplayPolicy
+ @Binds
+ fun provideShadeExpansionIntent(impl: StatusBarTouchShadeDisplayPolicy): ShadeExpansionIntent
+
@IntoSet
@Binds
fun provideDefaultDisplayPolicyToSet(impl: DefaultDisplayShadePolicy): ShadeDisplayPolicy
diff --git a/packages/SystemUI/src/com/android/systemui/shade/display/StatusBarTouchShadeDisplayPolicy.kt b/packages/SystemUI/src/com/android/systemui/shade/display/StatusBarTouchShadeDisplayPolicy.kt
index 30b086f03d66..91020aa7bdb0 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/display/StatusBarTouchShadeDisplayPolicy.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/display/StatusBarTouchShadeDisplayPolicy.kt
@@ -18,16 +18,25 @@ package com.android.systemui.shade.display
import android.util.Log
import android.view.Display
+import android.view.MotionEvent
import com.android.app.tracing.coroutines.launchTraced
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.display.data.repository.DisplayRepository
import com.android.systemui.keyguard.data.repository.KeyguardRepository
import com.android.systemui.shade.ShadeOnDefaultDisplayWhenLocked
+import com.android.systemui.shade.domain.interactor.NotificationShadeElement
+import com.android.systemui.shade.domain.interactor.QSShadeElement
+import com.android.systemui.shade.domain.interactor.ShadeExpandedStateInteractor.ShadeElement
+import com.android.systemui.shade.domain.interactor.ShadeInteractor
import com.android.systemui.shade.shared.flag.ShadeWindowGoesAround
+import dagger.Lazy
+import java.util.concurrent.atomic.AtomicReference
import javax.inject.Inject
+import kotlin.time.Duration.Companion.seconds
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Job
+import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.StateFlow
@@ -49,14 +58,20 @@ class StatusBarTouchShadeDisplayPolicy
constructor(
displayRepository: DisplayRepository,
keyguardRepository: KeyguardRepository,
- @Background val backgroundScope: CoroutineScope,
- @ShadeOnDefaultDisplayWhenLocked val shadeOnDefaultDisplayWhenLocked: Boolean,
-) : ShadeDisplayPolicy {
+ @Background private val backgroundScope: CoroutineScope,
+ @ShadeOnDefaultDisplayWhenLocked private val shadeOnDefaultDisplayWhenLocked: Boolean,
+ private val shadeInteractor: Lazy<ShadeInteractor>,
+ private val qsShadeElement: Lazy<QSShadeElement>,
+ private val notificationElement: Lazy<NotificationShadeElement>,
+) : ShadeDisplayPolicy, ShadeExpansionIntent {
override val name: String = "status_bar_latest_touch"
private val currentDisplayId = MutableStateFlow(Display.DEFAULT_DISPLAY)
private val availableDisplayIds: StateFlow<Set<Int>> = displayRepository.displayIds
+ private var latestIntent = AtomicReference<ShadeElement?>()
+ private var timeoutJob: Job? = null
+
override val displayId: StateFlow<Int> =
if (shadeOnDefaultDisplayWhenLocked) {
keyguardRepository.isKeyguardShowing
@@ -75,8 +90,29 @@ constructor(
private var removalListener: Job? = null
/** Called when the status bar on the given display is touched. */
- fun onStatusBarTouched(statusBarDisplayId: Int) {
+ fun onStatusBarTouched(event: MotionEvent, statusBarWidth: Int) {
ShadeWindowGoesAround.isUnexpectedlyInLegacyMode()
+ updateShadeDisplayIfNeeded(event)
+ updateExpansionIntent(event, statusBarWidth)
+ }
+
+ override fun consumeExpansionIntent(): ShadeElement? {
+ return latestIntent.getAndSet(null)
+ }
+
+ private fun updateExpansionIntent(event: MotionEvent, statusBarWidth: Int) {
+ val element = classifyStatusBarEvent(event, statusBarWidth)
+ latestIntent.set(element)
+ timeoutJob?.cancel()
+ timeoutJob =
+ backgroundScope.launchTraced("StatusBarTouchDisplayPolicy#intentTimeout") {
+ delay(EXPANSION_INTENT_EXPIRY)
+ latestIntent.set(null)
+ }
+ }
+
+ private fun updateShadeDisplayIfNeeded(event: MotionEvent) {
+ val statusBarDisplayId = event.displayId
if (statusBarDisplayId !in availableDisplayIds.value) {
Log.e(TAG, "Got touch on unknown display $statusBarDisplayId")
return
@@ -90,6 +126,17 @@ constructor(
}
}
+ private fun classifyStatusBarEvent(
+ motionEvent: MotionEvent,
+ statusbarWidth: Int,
+ ): ShadeElement {
+ val xPercentage = motionEvent.x / statusbarWidth
+ val threshold = shadeInteractor.get().getTopEdgeSplitFraction()
+ return if (xPercentage < threshold) {
+ notificationElement.get()
+ } else qsShadeElement.get()
+ }
+
private fun monitorDisplayRemovals(): Job {
return backgroundScope.launchTraced("StatusBarTouchDisplayPolicy#monitorDisplayRemovals") {
currentDisplayId.subscriptionCount
@@ -112,5 +159,6 @@ constructor(
private companion object {
const val TAG = "StatusBarTouchDisplayPolicy"
+ val EXPANSION_INTENT_EXPIRY = 2.seconds
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeDisplaysInteractor.kt b/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeDisplaysInteractor.kt
index 691a383cb338..f67d33122063 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeDisplaysInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeDisplaysInteractor.kt
@@ -30,6 +30,7 @@ import com.android.systemui.shade.ShadeDisplayChangeLatencyTracker
import com.android.systemui.shade.ShadeTraceLogger.logMoveShadeWindowTo
import com.android.systemui.shade.ShadeTraceLogger.traceReparenting
import com.android.systemui.shade.data.repository.ShadeDisplaysRepository
+import com.android.systemui.shade.display.ShadeExpansionIntent
import com.android.systemui.shade.shared.flag.ShadeWindowGoesAround
import com.android.window.flags.Flags
import java.util.Optional
@@ -49,6 +50,7 @@ constructor(
@Main private val mainThreadContext: CoroutineContext,
private val shadeDisplayChangeLatencyTracker: ShadeDisplayChangeLatencyTracker,
shadeExpandedInteractor: Optional<ShadeExpandedStateInteractor>,
+ private val shadeExpansionIntent: ShadeExpansionIntent,
) : CoreStartable {
private val shadeExpandedInteractor =
@@ -90,10 +92,7 @@ constructor(
withContext(mainThreadContext) {
traceReparenting {
shadeDisplayChangeLatencyTracker.onShadeDisplayChanging(destinationId)
- val expandedElement = shadeExpandedInteractor.currentlyExpandedElement.value
- expandedElement?.collapse(reason = "Shade window move")
- reparentToDisplayId(id = destinationId)
- expandedElement?.expand(reason = "Shade window move")
+ collapseAndExpandShadeIfNeeded { reparentToDisplayId(id = destinationId) }
checkContextDisplayMatchesExpected(destinationId)
}
}
@@ -106,6 +105,18 @@ constructor(
}
}
+ private suspend fun collapseAndExpandShadeIfNeeded(wrapped: () -> Unit) {
+ val previouslyExpandedElement = shadeExpandedInteractor.currentlyExpandedElement.value
+ previouslyExpandedElement?.collapse(reason = COLLAPSE_EXPAND_REASON)
+
+ wrapped()
+
+ // If the user was trying to expand a specific shade element, let's make sure to expand
+ // that one. Otherwise, we can just re-expand the previous expanded element.
+ shadeExpansionIntent.consumeExpansionIntent()?.expand(COLLAPSE_EXPAND_REASON)
+ ?: previouslyExpandedElement?.expand(reason = COLLAPSE_EXPAND_REASON)
+ }
+
private fun checkContextDisplayMatchesExpected(destinationId: Int) {
if (shadeContext.displayId != destinationId) {
Log.wtf(
@@ -125,5 +136,6 @@ constructor(
private companion object {
const val TAG = "ShadeDisplaysInteractor"
+ const val COLLAPSE_EXPAND_REASON = "Shade window move"
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeExpandedStateInteractor.kt b/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeExpandedStateInteractor.kt
index dd3abeec5a72..c1ea71e8e757 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeExpandedStateInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeExpandedStateInteractor.kt
@@ -24,6 +24,7 @@ import com.android.systemui.shade.domain.interactor.ShadeExpandedStateInteractor
import com.android.systemui.shade.shared.flag.DualShade
import com.android.systemui.util.kotlin.Utils.Companion.combineState
import javax.inject.Inject
+import kotlin.coroutines.CoroutineContext
import kotlin.time.Duration.Companion.seconds
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.MutableStateFlow
@@ -47,7 +48,7 @@ interface ShadeExpandedStateInteractor {
val currentlyExpandedElement: StateFlow<ShadeElement?>
/** An element from the shade window that can be expanded or collapsed. */
- abstract class ShadeElement {
+ sealed class ShadeElement {
/** Expands the shade element, returning when the expansion is done */
abstract suspend fun expand(reason: String)
@@ -60,13 +61,12 @@ interface ShadeExpandedStateInteractor {
class ShadeExpandedStateInteractorImpl
@Inject
constructor(
- private val shadeInteractor: ShadeInteractor,
+ shadeInteractor: ShadeInteractor,
@Background private val bgScope: CoroutineScope,
+ private val notificationElement: NotificationShadeElement,
+ private val qsElement: QSShadeElement,
) : ShadeExpandedStateInteractor {
- private val notificationElement = NotificationElement()
- private val qsElement = QSElement()
-
override val currentlyExpandedElement: StateFlow<ShadeElement?> =
if (SceneContainerFlag.isEnabled) {
combineState(
@@ -84,35 +84,47 @@ constructor(
} else {
MutableStateFlow(null)
}
+}
- inner class NotificationElement : ShadeElement() {
- override suspend fun expand(reason: String) {
- shadeInteractor.expandNotificationsShade(reason)
- shadeInteractor.shadeExpansion.waitUntil(1f)
- }
+private suspend fun StateFlow<Float>.waitUntil(f: Float, coroutineContext: CoroutineContext) {
+ // it's important to not do this in the main thread otherwise it will block any rendering.
+ withContext(coroutineContext) {
+ withTimeout(1.seconds) { traceWaitForExpansion(expansion = f) { first { it == f } } }
+ }
+}
- override suspend fun collapse(reason: String) {
- shadeInteractor.collapseNotificationsShade(reason)
- shadeInteractor.shadeExpansion.waitUntil(0f)
- }
+@SysUISingleton
+class NotificationShadeElement
+@Inject
+constructor(
+ private val shadeInteractor: ShadeInteractor,
+ @Background private val bgContext: CoroutineContext,
+) : ShadeElement() {
+ override suspend fun expand(reason: String) {
+ shadeInteractor.expandNotificationsShade(reason)
+ shadeInteractor.shadeExpansion.waitUntil(1f, bgContext)
}
- inner class QSElement : ShadeElement() {
- override suspend fun expand(reason: String) {
- shadeInteractor.expandQuickSettingsShade(reason)
- shadeInteractor.qsExpansion.waitUntil(1f)
- }
+ override suspend fun collapse(reason: String) {
+ shadeInteractor.collapseNotificationsShade(reason)
+ shadeInteractor.shadeExpansion.waitUntil(0f, bgContext)
+ }
+}
- override suspend fun collapse(reason: String) {
- shadeInteractor.collapseQuickSettingsShade(reason)
- shadeInteractor.qsExpansion.waitUntil(0f)
- }
+@SysUISingleton
+class QSShadeElement
+@Inject
+constructor(
+ private val shadeInteractor: ShadeInteractor,
+ @Background private val bgContext: CoroutineContext,
+) : ShadeElement() {
+ override suspend fun expand(reason: String) {
+ shadeInteractor.expandQuickSettingsShade(reason)
+ shadeInteractor.qsExpansion.waitUntil(1f, bgContext)
}
- private suspend fun StateFlow<Float>.waitUntil(f: Float) {
- // it's important to not do this in the main thread otherwise it will block any rendering.
- withContext(bgScope.coroutineContext) {
- withTimeout(1.seconds) { traceWaitForExpansion(expansion = f) { first { it == f } } }
- }
+ override suspend fun collapse(reason: String) {
+ shadeInteractor.collapseQuickSettingsShade(reason)
+ shadeInteractor.qsExpansion.waitUntil(0f, bgContext)
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/core/StatusBarConntectedDisplays.kt b/packages/SystemUI/src/com/android/systemui/statusbar/core/StatusBarConnectedDisplays.kt
index 54a18f764406..06474b01287d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/core/StatusBarConntectedDisplays.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/core/StatusBarConnectedDisplays.kt
@@ -16,9 +16,9 @@
package com.android.systemui.statusbar.core
-import com.android.systemui.Flags
import com.android.systemui.flags.FlagToken
import com.android.systemui.flags.RefactorFlagUtils
+import com.android.systemui.shared.Flags
/** Helper for reading or using the status bar connected displays flag state. */
@Suppress("NOTHING_TO_INLINE")
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/core/StatusBarRootModernization.kt b/packages/SystemUI/src/com/android/systemui/statusbar/core/StatusBarRootModernization.kt
index 057213fa4b18..3c30f3cbec85 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/core/StatusBarRootModernization.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/core/StatusBarRootModernization.kt
@@ -25,6 +25,9 @@ object StatusBarRootModernization {
/** Aconfig flag for removing the fragment */
const val FLAG_NAME = Flags.FLAG_STATUS_BAR_ROOT_MODERNIZATION
+ /** Shows a "compose->bar" text in the status bar for debug purposes */
+ const val SHOW_DISAMBIGUATION = false
+
/** A token used for dependency declaration */
val token: FlagToken
get() = FlagToken(FLAG_NAME, isEnabled)
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
index 76591ac4e453..7b55e83a0a99 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
@@ -1245,15 +1245,19 @@ public class NotificationStackScrollLayout
@Override
public void setHeadsUpTop(float headsUpTop) {
if (SceneContainerFlag.isUnexpectedlyInLegacyMode()) return;
- mAmbientState.setHeadsUpTop(headsUpTop);
- requestChildrenUpdate();
+ if (mAmbientState.getHeadsUpTop() != headsUpTop) {
+ mAmbientState.setHeadsUpTop(headsUpTop);
+ requestChildrenUpdate();
+ }
}
@Override
public void setHeadsUpBottom(float headsUpBottom) {
if (SceneContainerFlag.isUnexpectedlyInLegacyMode()) return;
- mAmbientState.setHeadsUpBottom(headsUpBottom);
- mStateAnimator.setHeadsUpAppearHeightBottom(Math.round(headsUpBottom));
+ if (mAmbientState.getHeadsUpBottom() != headsUpBottom) {
+ mAmbientState.setHeadsUpBottom(headsUpBottom);
+ mStateAnimator.setHeadsUpAppearHeightBottom(Math.round(headsUpBottom));
+ }
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewController.kt
index aa1308931f99..3f44f7bdef90 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewController.kt
@@ -234,7 +234,7 @@ private constructor(
)
}
if (ShadeWindowGoesAround.isEnabled && event.action == MotionEvent.ACTION_DOWN) {
- lazyStatusBarShadeDisplayPolicy.get().onStatusBarTouched(context.displayId)
+ lazyStatusBarShadeDisplayPolicy.get().onStatusBarTouched(event, mView.width)
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/CarrierConfigRepository.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/CarrierConfigRepository.kt
index 30c529a9034a..3e7094a0b5e8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/CarrierConfigRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/CarrierConfigRepository.kt
@@ -34,6 +34,9 @@ interface CarrierConfigRepository {
*/
suspend fun startObservingCarrierConfigUpdates()
- /** Gets a cached [SystemUiCarrierConfig], or creates a new one which will track the defaults */
- fun getOrCreateConfigForSubId(subId: Int): SystemUiCarrierConfig
+ /**
+ * Gets a cached [SystemUiCarrierConfig], or creates a new one which will track the defaults. A
+ * null [maybeSubId] will return the default carrier config.
+ */
+ fun getOrCreateConfigForSubId(maybeSubId: Int?): SystemUiCarrierConfig
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/CarrierConfigRepositoryImpl.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/CarrierConfigRepositoryImpl.kt
index 9a97f19f6593..b32037992501 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/CarrierConfigRepositoryImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/CarrierConfigRepositoryImpl.kt
@@ -20,6 +20,7 @@ import android.content.IntentFilter
import android.os.PersistableBundle
import android.telephony.CarrierConfigManager
import android.telephony.SubscriptionManager
+import android.telephony.SubscriptionManager.INVALID_SUBSCRIPTION_ID
import android.util.SparseArray
import androidx.annotation.VisibleForTesting
import androidx.core.util.getOrElse
@@ -51,7 +52,7 @@ constructor(
private val defaultConfig: PersistableBundle by lazy { CarrierConfigManager.getDefaultConfig() }
// Used for logging the default config in the dumpsys
private val defaultConfigForLogs: SystemUiCarrierConfig by lazy {
- SystemUiCarrierConfig(-1, defaultConfig)
+ SystemUiCarrierConfig(INVALID_SUBSCRIPTION_ID, defaultConfig)
}
private val configs = SparseArray<SystemUiCarrierConfig>()
@@ -89,7 +90,10 @@ constructor(
configToUpdate.processNewCarrierConfig(config)
}
- override fun getOrCreateConfigForSubId(subId: Int): SystemUiCarrierConfig {
+ override fun getOrCreateConfigForSubId(maybeSubId: Int?): SystemUiCarrierConfig {
+ // See CarrierConfigManager#getConfigForSubId(), passing INVALID_SUBSCRIPTION_ID yields
+ // the default carrier config
+ val subId = maybeSubId ?: INVALID_SUBSCRIPTION_ID
return configs.getOrElse(subId) {
val config = SystemUiCarrierConfig(subId, defaultConfig)
val carrierConfig = carrierConfigManager?.getConfigForSubId(subId)
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/MobileConnectionsRepository.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/MobileConnectionsRepository.kt
index 32e9c85bea81..09a626940c79 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/MobileConnectionsRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/MobileConnectionsRepository.kt
@@ -48,8 +48,8 @@ interface MobileConnectionsRepository {
*/
val activeSubChangedInGroupEvent: Flow<Unit>
- /** Tracks [SubscriptionManager.getDefaultDataSubscriptionId] */
- val defaultDataSubId: StateFlow<Int>
+ /** Tracks [SubscriptionManager.getDefaultDataSubscriptionId]. Null if there is no default */
+ val defaultDataSubId: StateFlow<Int?>
/**
* True if the default network connection is a mobile-like connection and false otherwise.
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/MobileRepositorySwitcher.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/MobileRepositorySwitcher.kt
index fc766915e4ef..252ebe6a32b0 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/MobileRepositorySwitcher.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/MobileRepositorySwitcher.kt
@@ -164,7 +164,7 @@ constructor(
override fun getIsAnySimSecure(): Boolean = activeRepo.value.getIsAnySimSecure()
- override val defaultDataSubId: StateFlow<Int> =
+ override val defaultDataSubId: StateFlow<Int?> =
activeRepo
.flatMapLatest { it.defaultDataSubId }
.stateIn(scope, SharingStarted.WhileSubscribed(), realRepository.defaultDataSubId.value)
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/demo/DemoMobileConnectionsRepository.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/demo/DemoMobileConnectionsRepository.kt
index 936954f3b484..b608e53b4bce 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/demo/DemoMobileConnectionsRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/demo/DemoMobileConnectionsRepository.kt
@@ -166,7 +166,7 @@ constructor(
private fun <K, V> Map<K, V>.reverse() = entries.associateBy({ it.value }) { it.key }
// TODO(b/261029387): add a command for this value
- override val defaultDataSubId = MutableStateFlow(INVALID_SUBSCRIPTION_ID)
+ override val defaultDataSubId: MutableStateFlow<Int?> = MutableStateFlow(null)
// TODO(b/261029387): not yet supported
override val mobileIsDefault: StateFlow<Boolean> = MutableStateFlow(true)
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionsRepositoryImpl.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionsRepositoryImpl.kt
index b762751ecbb4..aa6da61958e0 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionsRepositoryImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionsRepositoryImpl.kt
@@ -249,7 +249,7 @@ constructor(
tableLogger,
LOGGING_PREFIX,
columnName = "activeSubId",
- initialValue = INVALID_SUBSCRIPTION_ID,
+ initialValue = null,
)
.stateIn(scope, started = SharingStarted.WhileSubscribed(), null)
@@ -264,22 +264,31 @@ constructor(
}
.stateIn(scope, SharingStarted.WhileSubscribed(), null)
- override val defaultDataSubId: StateFlow<Int> =
+ override val defaultDataSubId: StateFlow<Int?> =
broadcastDispatcher
.broadcastFlow(
IntentFilter(TelephonyManager.ACTION_DEFAULT_DATA_SUBSCRIPTION_CHANGED)
) { intent, _ ->
- intent.getIntExtra(PhoneConstants.SUBSCRIPTION_KEY, INVALID_SUBSCRIPTION_ID)
+ val subId =
+ intent.getIntExtra(PhoneConstants.SUBSCRIPTION_KEY, INVALID_SUBSCRIPTION_ID)
+ if (subId == INVALID_SUBSCRIPTION_ID) {
+ null
+ } else {
+ subId
+ }
}
.distinctUntilChanged()
.logDiffsForTable(
tableLogger,
LOGGING_PREFIX,
columnName = "defaultSubId",
- initialValue = INVALID_SUBSCRIPTION_ID,
+ initialValue = null,
)
- .onStart { emit(subscriptionManagerProxy.getDefaultDataSubscriptionId()) }
- .stateIn(scope, SharingStarted.WhileSubscribed(), INVALID_SUBSCRIPTION_ID)
+ .onStart {
+ val subId = subscriptionManagerProxy.getDefaultDataSubscriptionId()
+ emit(if (subId == INVALID_SUBSCRIPTION_ID) null else subId)
+ }
+ .stateIn(scope, SharingStarted.WhileSubscribed(), null)
private val carrierConfigChangedEvent =
broadcastDispatcher
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconsInteractor.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconsInteractor.kt
index 78731faa6167..be56461a96ee 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconsInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconsInteractor.kt
@@ -72,7 +72,7 @@ interface MobileIconsInteractor {
val filteredSubscriptions: Flow<List<SubscriptionModel>>
/** Subscription ID of the current default data subscription */
- val defaultDataSubId: Flow<Int>
+ val defaultDataSubId: Flow<Int?>
/**
* The current list of [MobileIconInteractor]s associated with the current list of
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/composable/StatusBarRoot.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/composable/StatusBarRoot.kt
index 71e19188f309..b1cc208e9b43 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/composable/StatusBarRoot.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/composable/StatusBarRoot.kt
@@ -32,6 +32,7 @@ import androidx.compose.ui.draw.alpha
import androidx.compose.ui.platform.ComposeView
import androidx.compose.ui.platform.ViewCompositionStrategy
import androidx.compose.ui.viewinterop.AndroidView
+import androidx.core.view.isVisible
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import com.android.app.tracing.coroutines.launchTraced as launch
import com.android.compose.theme.PlatformTheme
@@ -39,6 +40,7 @@ import com.android.keyguard.AlphaOptimizedLinearLayout
import com.android.systemui.plugins.DarkIconDispatcher
import com.android.systemui.res.R
import com.android.systemui.statusbar.chips.ui.compose.OngoingActivityChips
+import com.android.systemui.statusbar.core.StatusBarRootModernization
import com.android.systemui.statusbar.data.repository.DarkIconDispatcherStore
import com.android.systemui.statusbar.events.domain.interactor.SystemStatusEventAnimationInteractor
import com.android.systemui.statusbar.featurepods.popups.StatusBarPopupChips
@@ -142,10 +144,14 @@ fun StatusBarRoot(
Box(Modifier.fillMaxSize()) {
// TODO(b/364360986): remove this before rolling the flag forward
- Disambiguation(viewModel = statusBarViewModel)
+ if (StatusBarRootModernization.SHOW_DISAMBIGUATION) {
+ Disambiguation(viewModel = statusBarViewModel)
+ }
Row(Modifier.fillMaxSize()) {
val scope = rememberCoroutineScope()
+ val visible =
+ statusBarViewModel.shouldHomeStatusBarBeVisible.collectAsStateWithLifecycle(false)
AndroidView(
factory = { context ->
val inflater = LayoutInflater.from(context)
@@ -280,7 +286,12 @@ fun StatusBarRoot(
}
onViewCreated(phoneStatusBarView)
phoneStatusBarView
- }
+ },
+ update = { view ->
+ // Show or hide the entire status bar. This is important so that we aren't
+ // visible when first inflated
+ view.isVisible = visible.value
+ },
)
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/HomeStatusBarViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/HomeStatusBarViewModel.kt
index d9d9a29ee2b6..6ff4354fcc46 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/HomeStatusBarViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/HomeStatusBarViewModel.kt
@@ -80,6 +80,9 @@ import kotlinx.coroutines.flow.stateIn
* so that it's all in one place and easily testable outside of the fragment.
*/
interface HomeStatusBarViewModel {
+ /** Should the entire status bar be hidden? */
+ val shouldHomeStatusBarBeVisible: Flow<Boolean>
+
/**
* True if the device is currently transitioning from lockscreen to occluded and false
* otherwise.
@@ -271,10 +274,12 @@ constructor(
isHomeScreenStatusBarAllowedLegacy
}
- private val shouldHomeStatusBarBeVisible =
- combine(isHomeStatusBarAllowed, keyguardInteractor.isSecureCameraActive) {
+ override val shouldHomeStatusBarBeVisible =
+ combine(
isHomeStatusBarAllowed,
- isSecureCameraActive ->
+ keyguardInteractor.isSecureCameraActive,
+ headsUpNotificationInteractor.statusBarHeadsUpStatus,
+ ) { isHomeStatusBarAllowed, isSecureCameraActive, headsUpState ->
// When launching the camera over the lockscreen, the status icons would typically
// become visible momentarily before animating out, since we're not yet aware that the
// launching camera activity is fullscreen. Even once the activity finishes launching,
@@ -282,7 +287,7 @@ constructor(
// tells us to hide them.
// To ensure that this high-visibility animation is smooth, keep the icons hidden during
// a camera launch. See b/257292822.
- isHomeStatusBarAllowed && !isSecureCameraActive
+ headsUpState.isPinned || (isHomeStatusBarAllowed && !isSecureCameraActive)
}
private val isAnyChipVisible =
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/StatusBarOperatorNameViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/StatusBarOperatorNameViewModel.kt
index 7ae74c3bfb65..0b83c4e3dea3 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/StatusBarOperatorNameViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/StatusBarOperatorNameViewModel.kt
@@ -22,6 +22,7 @@ import javax.inject.Inject
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.flatMapLatest
+import kotlinx.coroutines.flow.flowOf
/**
* View model for the operator name (aka carrier name) of the carrier for the default data
@@ -34,6 +35,10 @@ class StatusBarOperatorNameViewModel
constructor(mobileIconsInteractor: MobileIconsInteractor) {
val operatorName: Flow<String?> =
mobileIconsInteractor.defaultDataSubId.flatMapLatest {
- mobileIconsInteractor.getMobileConnectionInteractorForSubId(it).carrierName
+ if (it == null) {
+ flowOf(null)
+ } else {
+ mobileIconsInteractor.getMobileConnectionInteractorForSubId(it).carrierName
+ }
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/composable/TutorialSelectionScreen.kt b/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/composable/TutorialSelectionScreen.kt
index 66a900bd72d8..c8a58400069e 100644
--- a/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/composable/TutorialSelectionScreen.kt
+++ b/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/composable/TutorialSelectionScreen.kt
@@ -218,9 +218,11 @@ private fun TutorialButton(
verticalArrangement = Arrangement.Center,
horizontalAlignment = Alignment.CenterHorizontally,
) {
+ // contentDescription is set to null because the icon is decorative and we don't want to
+ // repeat the text twice
Icon(
imageVector = icon,
- contentDescription = text,
+ contentDescription = null,
modifier = Modifier.width(30.dp).height(30.dp),
tint = iconColor,
)
diff --git a/packages/SystemUI/src/com/android/systemui/util/DelayableMarqueeTextView.kt b/packages/SystemUI/src/com/android/systemui/util/DelayableMarqueeTextView.kt
index ef9340a332dc..ffaddfbb9b15 100644
--- a/packages/SystemUI/src/com/android/systemui/util/DelayableMarqueeTextView.kt
+++ b/packages/SystemUI/src/com/android/systemui/util/DelayableMarqueeTextView.kt
@@ -20,11 +20,13 @@ import android.content.Context
import android.util.AttributeSet
import com.android.systemui.res.R
-class DelayableMarqueeTextView @JvmOverloads constructor(
+open class DelayableMarqueeTextView
+@JvmOverloads
+constructor(
context: Context,
attrs: AttributeSet? = null,
defStyleAttr: Int = 0,
- defStyleRes: Int = 0
+ defStyleRes: Int = 0,
) : SafeMarqueeTextView(context, attrs, defStyleAttr, defStyleRes) {
var marqueeDelay: Long = DEFAULT_MARQUEE_DELAY
@@ -39,16 +41,20 @@ class DelayableMarqueeTextView @JvmOverloads constructor(
}
init {
- val typedArray = context.theme.obtainStyledAttributes(
+ val typedArray =
+ context.theme.obtainStyledAttributes(
attrs,
R.styleable.DelayableMarqueeTextView,
defStyleAttr,
- defStyleRes
- )
- marqueeDelay = typedArray.getInteger(
- R.styleable.DelayableMarqueeTextView_marqueeDelay,
- DEFAULT_MARQUEE_DELAY.toInt()
- ).toLong()
+ defStyleRes,
+ )
+ marqueeDelay =
+ typedArray
+ .getInteger(
+ R.styleable.DelayableMarqueeTextView_marqueeDelay,
+ DEFAULT_MARQUEE_DELAY.toInt(),
+ )
+ .toLong()
typedArray.recycle()
}
diff --git a/packages/SystemUI/src/com/android/systemui/volume/dialog/ringer/ui/binder/VolumeDialogRingerViewBinder.kt b/packages/SystemUI/src/com/android/systemui/volume/dialog/ringer/ui/binder/VolumeDialogRingerViewBinder.kt
index 96630ca36b97..908249dbca08 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/dialog/ringer/ui/binder/VolumeDialogRingerViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/dialog/ringer/ui/binder/VolumeDialogRingerViewBinder.kt
@@ -144,7 +144,6 @@ constructor(private val viewModel: VolumeDialogRingerDrawerViewModel) {
ringerState.orientation,
)
}
-
is RingerDrawerState.Closed -> {
if (
uiModel.selectedButton.ringerMode ==
@@ -189,7 +188,6 @@ constructor(private val viewModel: VolumeDialogRingerDrawerViewModel) {
}
}
}
-
is RingerDrawerState.Open -> {
drawerContainer.animateAndBindDrawerButtons(
viewModel,
@@ -220,7 +218,6 @@ constructor(private val viewModel: VolumeDialogRingerDrawerViewModel) {
}
}
}
-
is RingerViewModelState.Unavailable -> {
drawerContainer.visibility = View.GONE
volumeDialogBackgroundView.setBackgroundResource(
@@ -251,7 +248,7 @@ constructor(private val viewModel: VolumeDialogRingerDrawerViewModel) {
.requireViewById<ImageButton>(R.id.volume_drawer_button)
val previousIndex =
uiModel.availableButtons.indexOfFirst {
- it?.ringerMode == uiModel.drawerState.previousMode
+ it.ringerMode == uiModel.drawerState.previousMode
}
val unselectedButton =
getChildAt(count - previousIndex)
@@ -306,20 +303,18 @@ constructor(private val viewModel: VolumeDialogRingerDrawerViewModel) {
) {
val count = uiModel.availableButtons.size
uiModel.availableButtons.fastForEachIndexed { index, ringerButton ->
- ringerButton?.let {
- val view = getChildAt(count - index)
- val isOpen = uiModel.drawerState is RingerDrawerState.Open
- if (index == uiModel.currentButtonIndex) {
- view.bindDrawerButton(
- if (isOpen) it else uiModel.selectedButton,
- viewModel,
- isOpen,
- isSelected = true,
- isAnimated = isAnimated,
- )
- } else {
- view.bindDrawerButton(it, viewModel, isOpen, isAnimated = isAnimated)
- }
+ val view = getChildAt(count - index)
+ val isOpen = uiModel.drawerState is RingerDrawerState.Open
+ if (index == uiModel.currentButtonIndex) {
+ view.bindDrawerButton(
+ if (isOpen) ringerButton else uiModel.selectedButton,
+ viewModel,
+ isOpen,
+ isSelected = true,
+ isAnimated = isAnimated,
+ )
+ } else {
+ view.bindDrawerButton(ringerButton, viewModel, isOpen, isAnimated = isAnimated)
}
}
onAnimationEnd?.run()
diff --git a/packages/SystemUI/src/com/android/systemui/volume/dialog/ringer/ui/viewmodel/RingerViewModel.kt b/packages/SystemUI/src/com/android/systemui/volume/dialog/ringer/ui/viewmodel/RingerViewModel.kt
index 96d4f62416bf..8613610ad1bf 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/dialog/ringer/ui/viewmodel/RingerViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/dialog/ringer/ui/viewmodel/RingerViewModel.kt
@@ -19,7 +19,7 @@ package com.android.systemui.volume.dialog.ringer.ui.viewmodel
/** Models volume dialog ringer */
data class RingerViewModel(
/** List of the available buttons according to the available modes */
- val availableButtons: List<RingerButtonViewModel?>,
+ val availableButtons: List<RingerButtonViewModel>,
/** The index of the currently selected button */
val currentButtonIndex: Int,
/** Currently selected button. */
diff --git a/packages/SystemUI/src/com/android/systemui/volume/dialog/ringer/ui/viewmodel/VolumeDialogRingerDrawerViewModel.kt b/packages/SystemUI/src/com/android/systemui/volume/dialog/ringer/ui/viewmodel/VolumeDialogRingerDrawerViewModel.kt
index eec64d9a2f86..07c4de0ac0c4 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/dialog/ringer/ui/viewmodel/VolumeDialogRingerDrawerViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/dialog/ringer/ui/viewmodel/VolumeDialogRingerDrawerViewModel.kt
@@ -50,7 +50,9 @@ import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.flowOn
+import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.map
+import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.flow.stateIn
import kotlinx.coroutines.launch
@@ -106,9 +108,25 @@ constructor(
.setUsage(AudioAttributes.USAGE_ASSISTANCE_SONIFICATION)
.build()
+ init {
+ ringerViewModel
+ .onEach { viewModelState ->
+ when (viewModelState) {
+ is RingerViewModelState.Available ->
+ volumeDialogLogger.onRingerDrawerAvailable(
+ viewModelState.uiModel.availableButtons.map { it.ringerMode }
+ )
+ is RingerViewModelState.Unavailable ->
+ volumeDialogLogger.onRingerDrawerUnavailable()
+ }
+ }
+ .launchIn(coroutineScope)
+ }
+
fun onRingerButtonClicked(ringerMode: RingerMode, isSelectedButton: Boolean = false) {
if (drawerState.value is RingerDrawerState.Open && !isSelectedButton) {
Events.writeEvent(Events.EVENT_RINGER_TOGGLE, ringerMode.value)
+ volumeDialogLogger.onRingerModeChanged(ringerMode)
provideTouchFeedback(ringerMode)
maybeShowToast(ringerMode)
ringerInteractor.setRingerMode(ringerMode)
@@ -159,7 +177,9 @@ constructor(
RingerViewModelState.Available(
RingerViewModel(
availableButtons =
- availableModes.map { mode -> toButtonViewModel(mode, isZenMuted) },
+ availableModes.mapNotNull { mode ->
+ toButtonViewModel(mode, isZenMuted)
+ },
currentButtonIndex = currentIndex,
selectedButton = it,
drawerState = drawerState,
@@ -219,7 +239,6 @@ constructor(
hintLabelResId = R.string.volume_ringer_hint_unmute,
ringerMode = ringerMode,
)
-
availableModes.contains(RingerMode(RINGER_MODE_VIBRATE)) ->
RingerButtonViewModel(
imageResId = R.drawable.ic_speaker_on,
@@ -232,7 +251,6 @@ constructor(
hintLabelResId = R.string.volume_ringer_hint_vibrate,
ringerMode = ringerMode,
)
-
else ->
RingerButtonViewModel(
imageResId = R.drawable.ic_speaker_on,
@@ -269,17 +287,14 @@ constructor(
null
}
}
-
RINGER_MODE_SILENT ->
applicationContext.getString(
internalR.string.volume_dialog_ringer_guidance_silent
)
-
RINGER_MODE_VIBRATE ->
applicationContext.getString(
internalR.string.volume_dialog_ringer_guidance_vibrate
)
-
else ->
applicationContext.getString(
internalR.string.volume_dialog_ringer_guidance_vibrate
diff --git a/packages/SystemUI/src/com/android/systemui/volume/dialog/shared/VolumeDialogLogger.kt b/packages/SystemUI/src/com/android/systemui/volume/dialog/shared/VolumeDialogLogger.kt
index 9a3aa7e3d79f..cccf090fae85 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/dialog/shared/VolumeDialogLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/dialog/shared/VolumeDialogLogger.kt
@@ -45,6 +45,52 @@ class VolumeDialogLogger @Inject constructor(@VolumeLog private val logBuffer: L
)
}
+ fun onVolumeSliderAdjustmentFinished(volume: Int, stream: Int) {
+ logBuffer.log(
+ TAG,
+ LogLevel.DEBUG,
+ {
+ int1 = volume
+ int2 = stream
+ },
+ { "Volume adjusted: volume=$int1 stream=$int2" },
+ )
+ }
+
+ fun onVolumeSlidersUpdated(primaryStream: Int, floating: Collection<Int>) {
+ logBuffer.log(
+ TAG,
+ LogLevel.DEBUG,
+ {
+ int1 = primaryStream
+ str1 = floating.joinToString(",") { it.toString() }
+ },
+ { "Showing streams: primary=$int1 floating=$str1" },
+ )
+ }
+
+ fun onRingerModeChanged(ringerMode: RingerMode) {
+ logBuffer.log(
+ TAG,
+ LogLevel.DEBUG,
+ { int1 = ringerMode.value },
+ { "Ringer mode changed to: $int1" },
+ )
+ }
+
+ fun onRingerDrawerAvailable(modes: List<RingerMode>) {
+ logBuffer.log(
+ TAG,
+ LogLevel.DEBUG,
+ { str1 = modes.joinToString(",") { it.value.toString() } },
+ { "Ringer drawer available with modes: $str1" },
+ )
+ }
+
+ fun onRingerDrawerUnavailable() {
+ logBuffer.log(TAG, LogLevel.DEBUG, {}, { "Ringer drawer unavailable" })
+ }
+
fun onCurrentRingerModeIsUnsupported(ringerMode: RingerMode) {
logBuffer.log(
TAG,
diff --git a/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/VolumeDialogSliderViewBinder.kt b/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/VolumeDialogSliderViewBinder.kt
index 3b964fdec1b8..d40302408dd6 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/VolumeDialogSliderViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/VolumeDialogSliderViewBinder.kt
@@ -27,6 +27,7 @@ import com.android.systemui.volume.dialog.sliders.ui.viewmodel.VolumeDialogSlide
import com.android.systemui.volume.dialog.sliders.ui.viewmodel.VolumeDialogSliderStateModel
import com.android.systemui.volume.dialog.sliders.ui.viewmodel.VolumeDialogSliderViewModel
import com.google.android.material.slider.Slider
+import com.google.android.material.slider.Slider.OnSliderTouchListener
import javax.inject.Inject
import kotlin.math.roundToInt
import kotlinx.coroutines.CoroutineScope
@@ -68,6 +69,15 @@ constructor(
sliderView.addOnChangeListener { _, value, fromUser ->
viewModel.setStreamVolume(value.roundToInt(), fromUser)
}
+ sliderView.addOnSliderTouchListener(
+ object : OnSliderTouchListener {
+ override fun onStartTrackingTouch(slider: Slider) {}
+
+ override fun onStopTrackingTouch(slider: Slider) {
+ viewModel.onStreamChangeFinished(slider.value.roundToInt())
+ }
+ }
+ )
viewModel.isDisabledByZenMode.onEach { sliderView.isEnabled = !it }.launchIn(this)
viewModel.state
diff --git a/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/viewmodel/VolumeDialogSliderIconProvider.kt b/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/viewmodel/VolumeDialogSliderIconProvider.kt
index 71fe22ba4b01..9cf02f26c9f7 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/viewmodel/VolumeDialogSliderIconProvider.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/viewmodel/VolumeDialogSliderIconProvider.kt
@@ -82,10 +82,6 @@ constructor(
ringerMode: RingerMode?,
): Int {
val isStreamOffline = level == 0 || isMuted
- when (ringerMode?.value) {
- AudioManager.RINGER_MODE_VIBRATE -> return R.drawable.ic_volume_ringer_vibrate
- AudioManager.RINGER_MODE_SILENT -> return R.drawable.ic_ring_volume_off
- }
if (isRoutedToBluetooth) {
return if (stream == AudioManager.STREAM_VOICE_CALL) {
R.drawable.ic_volume_bt_sco
@@ -98,29 +94,39 @@ constructor(
}
}
+ val isLevelLow = level < (levelMax + levelMin) / 2
return if (isStreamOffline) {
+ val ringerOfflineIcon =
+ when (ringerMode?.value) {
+ AudioManager.RINGER_MODE_VIBRATE -> return R.drawable.ic_volume_ringer_vibrate
+ AudioManager.RINGER_MODE_SILENT -> return R.drawable.ic_ring_volume_off
+ else -> null
+ }
when (stream) {
AudioManager.STREAM_MUSIC -> R.drawable.ic_volume_media_mute
- AudioManager.STREAM_NOTIFICATION -> R.drawable.ic_volume_ringer_mute
+ AudioManager.STREAM_NOTIFICATION ->
+ ringerOfflineIcon ?: R.drawable.ic_volume_ringer_mute
+ AudioManager.STREAM_RING -> ringerOfflineIcon ?: R.drawable.ic_volume_ringer_vibrate
AudioManager.STREAM_ALARM -> R.drawable.ic_volume_alarm_mute
AudioManager.STREAM_SYSTEM -> R.drawable.ic_volume_system_mute
else -> null
- } ?: getIconForStream(stream)
- } else {
- if (level < (levelMax + levelMin) / 2) {
- // This icon is different on TV
- R.drawable.ic_volume_media_low
- } else {
- getIconForStream(stream)
}
- }
+ } else {
+ null
+ } ?: getIconForStream(stream = stream, isLevelLow = isLevelLow)
}
@DrawableRes
- private fun getIconForStream(stream: Int): Int {
+ private fun getIconForStream(stream: Int, isLevelLow: Boolean): Int {
return when (stream) {
AudioManager.STREAM_ACCESSIBILITY -> R.drawable.ic_volume_accessibility
- AudioManager.STREAM_MUSIC -> R.drawable.ic_volume_media
+ AudioManager.STREAM_MUSIC ->
+ if (isLevelLow) {
+ // This icon is different on TV
+ R.drawable.ic_volume_media_low
+ } else {
+ R.drawable.ic_volume_media
+ }
AudioManager.STREAM_RING -> R.drawable.ic_ring_volume
AudioManager.STREAM_NOTIFICATION -> R.drawable.ic_volume_ringer
AudioManager.STREAM_ALARM -> R.drawable.ic_alarm
@@ -135,7 +141,9 @@ constructor(
* affect the [stream]
*/
private fun ringerModeForStream(stream: Int): Flow<RingerMode?> {
- return if (stream == AudioManager.STREAM_RING) {
+ return if (
+ stream == AudioManager.STREAM_RING || stream == AudioManager.STREAM_NOTIFICATION
+ ) {
audioVolumeInteractor.ringerMode
} else {
flowOf(null)
diff --git a/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/viewmodel/VolumeDialogSliderViewModel.kt b/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/viewmodel/VolumeDialogSliderViewModel.kt
index d999910675b0..89dd0352afa7 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/viewmodel/VolumeDialogSliderViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/viewmodel/VolumeDialogSliderViewModel.kt
@@ -20,9 +20,11 @@ import com.android.systemui.util.time.SystemClock
import com.android.systemui.volume.Events
import com.android.systemui.volume.dialog.dagger.scope.VolumeDialog
import com.android.systemui.volume.dialog.domain.interactor.VolumeDialogVisibilityInteractor
+import com.android.systemui.volume.dialog.shared.VolumeDialogLogger
import com.android.systemui.volume.dialog.shared.model.VolumeDialogStreamModel
import com.android.systemui.volume.dialog.sliders.dagger.VolumeDialogSliderScope
import com.android.systemui.volume.dialog.sliders.domain.interactor.VolumeDialogSliderInteractor
+import com.android.systemui.volume.dialog.sliders.domain.model.VolumeDialogSliderType
import javax.inject.Inject
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.ExperimentalCoroutinesApi
@@ -60,6 +62,8 @@ constructor(
@VolumeDialog private val coroutineScope: CoroutineScope,
private val volumeDialogSliderIconProvider: VolumeDialogSliderIconProvider,
private val systemClock: SystemClock,
+ private val sliderType: VolumeDialogSliderType,
+ private val logger: VolumeDialogLogger,
) {
private val userVolumeUpdates = MutableStateFlow<VolumeUpdate?>(null)
@@ -110,6 +114,10 @@ constructor(
}
}
+ fun onStreamChangeFinished(volume: Int) {
+ logger.onVolumeSliderAdjustmentFinished(volume = volume, stream = sliderType.audioStream)
+ }
+
private fun getTimestampMillis(): Long = systemClock.uptimeMillis()
private data class VolumeUpdate(val newVolumeLevel: Int, val timestampMillis: Long)
diff --git a/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/viewmodel/VolumeDialogSlidersViewModel.kt b/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/viewmodel/VolumeDialogSlidersViewModel.kt
index d8e6aec026c6..344dadcce229 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/viewmodel/VolumeDialogSlidersViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/viewmodel/VolumeDialogSlidersViewModel.kt
@@ -18,6 +18,7 @@ package com.android.systemui.volume.dialog.sliders.ui.viewmodel
import com.android.systemui.volume.dialog.dagger.scope.VolumeDialog
import com.android.systemui.volume.dialog.dagger.scope.VolumeDialogScope
+import com.android.systemui.volume.dialog.shared.VolumeDialogLogger
import com.android.systemui.volume.dialog.sliders.dagger.VolumeDialogSliderComponent
import com.android.systemui.volume.dialog.sliders.domain.interactor.VolumeDialogSlidersInteractor
import javax.inject.Inject
@@ -33,13 +34,18 @@ class VolumeDialogSlidersViewModel
@Inject
constructor(
@VolumeDialog coroutineScope: CoroutineScope,
- private val slidersInteractor: VolumeDialogSlidersInteractor,
+ slidersInteractor: VolumeDialogSlidersInteractor,
private val sliderComponentFactory: VolumeDialogSliderComponent.Factory,
+ private val volumeDialogLogger: VolumeDialogLogger,
) {
val sliders: Flow<VolumeDialogSliderUiModel> =
slidersInteractor.sliders
.map { slidersModel ->
+ volumeDialogLogger.onVolumeSlidersUpdated(
+ slidersModel.slider.audioStream,
+ slidersModel.floatingSliders.map { it.audioStream },
+ )
VolumeDialogSliderUiModel(
sliderComponent = sliderComponentFactory.create(slidersModel.slider),
floatingSliderComponent =
diff --git a/packages/SystemUI/src/com/android/systemui/volume/panel/component/volume/slider/ui/viewmodel/AudioStreamSliderViewModel.kt b/packages/SystemUI/src/com/android/systemui/volume/panel/component/volume/slider/ui/viewmodel/AudioStreamSliderViewModel.kt
index 5b8d9b045475..328d1245e4a5 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/panel/component/volume/slider/ui/viewmodel/AudioStreamSliderViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/panel/component/volume/slider/ui/viewmodel/AudioStreamSliderViewModel.kt
@@ -65,8 +65,6 @@ constructor(
) : SliderViewModel {
private val volumeChanges = MutableStateFlow<Int?>(null)
- private val streamsAffectedByRing =
- setOf(AudioManager.STREAM_RING, AudioManager.STREAM_NOTIFICATION)
private val audioStream = audioStreamWrapper.audioStream
private val iconsByStream =
mapOf(
@@ -175,9 +173,9 @@ constructor(
null
},
a11yStateDescription =
- if (volume == volumeRange.first) {
+ if (isMuted) {
context.getString(
- if (audioStream.value in streamsAffectedByRing) {
+ if (isAffectedByRingerMode) {
if (ringerMode.value == AudioManager.RINGER_MODE_VIBRATE) {
R.string.volume_panel_hint_vibrate
} else {
@@ -226,8 +224,8 @@ constructor(
private fun AudioStreamModel.getIcon(ringerMode: RingerMode): Icon {
val iconRes =
- if (isAffectedByMute && isMuted) {
- if (audioStream.value in streamsAffectedByRing) {
+ if (isMuted) {
+ if (isAffectedByRingerMode) {
if (ringerMode.value == AudioManager.RINGER_MODE_VIBRATE) {
R.drawable.ic_volume_ringer_vibrate
} else {
diff --git a/packages/SystemUI/src/com/android/systemui/wallpapers/GradientColorWallpaper.kt b/packages/SystemUI/src/com/android/systemui/wallpapers/GradientColorWallpaper.kt
new file mode 100644
index 000000000000..c1fb0e80cafc
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/wallpapers/GradientColorWallpaper.kt
@@ -0,0 +1,62 @@
+/*
+ * 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.wallpapers
+
+import android.graphics.Canvas
+import android.graphics.Paint
+import android.service.wallpaper.WallpaperService
+import android.util.Log
+import android.view.SurfaceHolder
+import androidx.core.graphics.toRectF
+
+/** A wallpaper that shows a static gradient color image wallpaper. */
+class GradientColorWallpaper : WallpaperService() {
+
+ override fun onCreateEngine(): Engine = GradientColorWallpaperEngine()
+
+ inner class GradientColorWallpaperEngine : Engine() {
+ init {
+ setShowForAllUsers(true)
+ }
+
+ override fun onSurfaceRedrawNeeded(surfaceHolder: SurfaceHolder) {
+ drawFrameInternal(surfaceHolder)
+ }
+
+ private fun drawFrameInternal(surfaceHolder: SurfaceHolder) {
+ val context = displayContext ?: return
+ val surface = surfaceHolder.surface
+ var canvas: Canvas? = null
+ try {
+ canvas = surface.lockHardwareCanvas()
+ val destRectF = surfaceHolder.surfaceFrame.toRectF()
+ val toColor = context.getColor(com.android.internal.R.color.materialColorPrimary)
+
+ // TODO(b/384519696): Draw the actual gradient color wallpaper instead.
+ canvas.drawRect(destRectF, Paint().apply { color = toColor })
+ } catch (exception: IllegalStateException) {
+ Log.d(TAG, "Fail to draw in the canvas", exception)
+ } finally {
+ canvas?.let { surface.unlockCanvasAndPost(it) }
+ }
+ }
+ }
+
+ private companion object {
+ const val TAG = "GradientColorWallpaper"
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuAnimationControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuAnimationControllerTest.java
index 9f6ad56335d7..c14cb87064ec 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuAnimationControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuAnimationControllerTest.java
@@ -194,7 +194,7 @@ public class MenuAnimationControllerTest extends SysuiTestCase {
final Runnable onSpringAnimationsEndCallback = mock(Runnable.class);
mMenuAnimationController.setSpringAnimationsEndAction(onSpringAnimationsEndCallback);
- mMenuAnimationController.flingMenuThenSpringToEdge(/* x= */ 0, /* velocityX= */
+ mMenuAnimationController.flingMenuThenSpringToEdge(new PointF(), /* velocityX= */
100, /* velocityY= */ 100);
mMenuAnimationController.mPositionAnimations.values()
.forEach(animation -> verify((FlingAnimation) animation).addEndListener(
@@ -212,7 +212,7 @@ public class MenuAnimationControllerTest extends SysuiTestCase {
final Runnable onSpringAnimationsEndCallback = mock(Runnable.class);
mMenuAnimationController.setSpringAnimationsEndAction(onSpringAnimationsEndCallback);
- mMenuAnimationController.flingMenuThenSpringToEdge(/* x= */ 0, /* velocityX= */
+ mMenuAnimationController.flingMenuThenSpringToEdge(new PointF(), /* velocityX= */
200, /* velocityY= */ 200);
mMenuAnimationController.mPositionAnimations.values()
.forEach(animation -> verify((FlingAnimation) animation).addEndListener(
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuViewAppearanceTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuViewAppearanceTest.java
new file mode 100644
index 000000000000..146488b523ad
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuViewAppearanceTest.java
@@ -0,0 +1,72 @@
+/*
+ * 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.accessibility.floatingmenu;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.graphics.Rect;
+import android.testing.TestableLooper;
+
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import androidx.test.filters.SmallTest;
+
+import com.android.systemui.SysuiTestCase;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/** Tests for {@link MenuViewAppearanceTest}. */
+@RunWith(AndroidJUnit4.class)
+@TestableLooper.RunWithLooper(setAsMainLooper = true)
+@SmallTest
+public class MenuViewAppearanceTest extends SysuiTestCase {
+ static final Rect DRAGGABLE_BOUNDS = new Rect(0, 0, 10, 10);
+ static final int MENU_HEIGHT = 1;
+
+ @Test
+ public void avoidVerticalDisplayCutout_roomAbove_placesAbove() {
+ final int y = 2;
+ final Rect cutout = new Rect(0, 3, 0, 10);
+
+ final float end_y = MenuViewAppearance.avoidVerticalDisplayCutout(
+ y, MENU_HEIGHT, DRAGGABLE_BOUNDS, cutout);
+
+ assertThat(end_y + MENU_HEIGHT).isAtMost(cutout.top);
+ }
+
+ @Test
+ public void avoidVerticalDisplayCutout_roomBelow_placesBelow() {
+ final int y = 2;
+ final Rect cutout = new Rect(0, 0, 0, 5);
+
+ final float end_y = MenuViewAppearance.avoidVerticalDisplayCutout(
+ y, MENU_HEIGHT, DRAGGABLE_BOUNDS, cutout);
+
+ assertThat(end_y).isAtLeast(cutout.bottom);
+ }
+
+ @Test
+ public void avoidVerticalDisplayCutout_noRoom_noChange() {
+ final int y = 2;
+ final Rect cutout = new Rect(0, 0, 0, 10);
+
+ final float end_y = MenuViewAppearance.avoidVerticalDisplayCutout(
+ y, MENU_HEIGHT, DRAGGABLE_BOUNDS, cutout);
+
+ assertThat(end_y).isEqualTo(end_y);
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuViewLayerTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuViewLayerTest.java
index 1500340c9d89..01fc868c716b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuViewLayerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuViewLayerTest.java
@@ -232,7 +232,6 @@ public class MenuViewLayerTest extends SysuiTestCase {
}
@Test
- @EnableFlags(android.view.accessibility.Flags.FLAG_A11Y_QS_SHORTCUT)
public void triggerDismissMenuAction_callsA11yManagerEnableShortcutsForTargets() {
final List<String> stubShortcutTargets = new ArrayList<>();
stubShortcutTargets.add(TEST_SELECT_TO_SPEAK_COMPONENT_NAME.flattenToString());
@@ -249,45 +248,6 @@ public class MenuViewLayerTest extends SysuiTestCase {
}
@Test
- @DisableFlags(android.view.accessibility.Flags.FLAG_A11Y_QS_SHORTCUT)
- public void triggerDismissMenuAction_matchA11yButtonTargetsResult() {
- mMenuViewLayer.mDismissMenuAction.run();
- verify(mSecureSettings).putStringForUser(
- Settings.Secure.ACCESSIBILITY_BUTTON_TARGETS, /* value= */ "",
- UserHandle.USER_CURRENT);
- }
-
- @Test
- @DisableFlags(android.view.accessibility.Flags.FLAG_A11Y_QS_SHORTCUT)
- public void triggerDismissMenuAction_matchEnabledA11yServicesResult() {
- setupEnabledAccessibilityServiceList();
-
- mMenuViewLayer.mDismissMenuAction.run();
- final String value = Settings.Secure.getStringForUser(mSpyContext.getContentResolver(),
- Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES,
- mSecureSettings.getRealUserHandle(UserHandle.USER_CURRENT));
-
- assertThat(value).isEqualTo("");
- }
-
- @Test
- @DisableFlags(android.view.accessibility.Flags.FLAG_A11Y_QS_SHORTCUT)
- public void triggerDismissMenuAction_hasHardwareKeyShortcut_keepEnabledStatus() {
- setupEnabledAccessibilityServiceList();
- final List<String> stubShortcutTargets = new ArrayList<>();
- stubShortcutTargets.add(TEST_SELECT_TO_SPEAK_COMPONENT_NAME.flattenToString());
- when(mStubAccessibilityManager.getAccessibilityShortcutTargets(
- ShortcutConstants.UserShortcutType.HARDWARE)).thenReturn(stubShortcutTargets);
-
- mMenuViewLayer.mDismissMenuAction.run();
- final String value = Settings.Secure.getStringForUser(mSpyContext.getContentResolver(),
- Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES,
- mSecureSettings.getRealUserHandle(UserHandle.USER_CURRENT));
-
- assertThat(value).isEqualTo(TEST_SELECT_TO_SPEAK_COMPONENT_NAME.flattenToString());
- }
-
- @Test
@EnableFlags(Flags.FLAG_FLOATING_MENU_DRAG_TO_EDIT)
public void onEditAction_startsActivity() {
mockActivityQuery(true);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewControllerTest.kt
index 243be3dc142d..437ccb6a9821 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewControllerTest.kt
@@ -37,7 +37,6 @@ import android.widget.FrameLayout
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import androidx.test.platform.app.InstrumentationRegistry
-import com.android.systemui.Flags as AconfigFlags
import com.android.systemui.SysuiTestCase
import com.android.systemui.SysuiTestableContext
import com.android.systemui.battery.BatteryMeterView
@@ -52,7 +51,9 @@ import com.android.systemui.shade.ShadeViewController
import com.android.systemui.shade.StatusBarLongPressGestureDetector
import com.android.systemui.shade.display.StatusBarTouchShadeDisplayPolicy
import com.android.systemui.shade.domain.interactor.PanelExpansionInteractor
+import com.android.systemui.shade.shared.flag.ShadeWindowGoesAround
import com.android.systemui.statusbar.CommandQueue
+import com.android.systemui.statusbar.core.StatusBarConnectedDisplays
import com.android.systemui.statusbar.data.repository.fakeStatusBarContentInsetsProviderStore
import com.android.systemui.statusbar.policy.Clock
import com.android.systemui.statusbar.policy.ConfigurationController
@@ -62,7 +63,6 @@ import com.android.systemui.unfold.SysUIUnfoldComponent
import com.android.systemui.unfold.config.UnfoldTransitionConfig
import com.android.systemui.unfold.util.ScopedUnfoldTransitionProgressProvider
import com.android.systemui.user.ui.viewmodel.StatusBarUserChipViewModel
-import com.android.systemui.util.mockito.any
import com.android.systemui.util.mockito.argumentCaptor
import com.android.systemui.util.mockito.whenever
import com.android.systemui.util.view.ViewUtil
@@ -74,7 +74,6 @@ import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.ArgumentCaptor
-import org.mockito.ArgumentMatchers.eq
import org.mockito.Mock
import org.mockito.Mockito.mock
import org.mockito.Mockito.never
@@ -82,6 +81,8 @@ import org.mockito.Mockito.spy
import org.mockito.Mockito.verify
import org.mockito.Mockito.`when`
import org.mockito.MockitoAnnotations
+import org.mockito.kotlin.any
+import org.mockito.kotlin.eq
@SmallTest
@RunWith(AndroidJUnit4::class)
@@ -287,7 +288,7 @@ class PhoneStatusBarViewControllerTest : SysuiTestCase() {
}
@Test
- @DisableFlags(AconfigFlags.FLAG_STATUS_BAR_CONNECTED_DISPLAYS)
+ @DisableFlags(StatusBarConnectedDisplays.FLAG_NAME)
fun handleTouchEventFromStatusBar_touchOnPrimaryDisplay_statusBarConnectedDisplaysDisabled_shadeReceivesEvent() {
`when`(centralSurfacesImpl.commandQueuePanelsEnabled).thenReturn(true)
`when`(shadeViewController.isViewEnabled).thenReturn(true)
@@ -299,10 +300,7 @@ class PhoneStatusBarViewControllerTest : SysuiTestCase() {
}
@Test
- @EnableFlags(
- AconfigFlags.FLAG_STATUS_BAR_CONNECTED_DISPLAYS,
- AconfigFlags.FLAG_SHADE_WINDOW_GOES_AROUND,
- )
+ @EnableFlags(StatusBarConnectedDisplays.FLAG_NAME, ShadeWindowGoesAround.FLAG_NAME)
fun handleTouchEventFromStatusBar_touchOnPrimaryDisplay_statusBarConnectedDisplaysEnabled_shadeWindowGoesAroundEnabled_shadeReceivesEvent() {
`when`(centralSurfacesImpl.commandQueuePanelsEnabled).thenReturn(true)
`when`(shadeViewController.isViewEnabled).thenReturn(true)
@@ -314,8 +312,8 @@ class PhoneStatusBarViewControllerTest : SysuiTestCase() {
}
@Test
- @EnableFlags(AconfigFlags.FLAG_STATUS_BAR_CONNECTED_DISPLAYS)
- @DisableFlags(AconfigFlags.FLAG_SHADE_WINDOW_GOES_AROUND)
+ @EnableFlags(StatusBarConnectedDisplays.FLAG_NAME)
+ @DisableFlags(ShadeWindowGoesAround.FLAG_NAME)
fun handleTouchEventFromStatusBar_touchOnPrimaryDisplay_statusBarConnectedDisplaysEnabled_shadeWindowGoesAroundDisabled_shadeReceivesEvent() {
`when`(centralSurfacesImpl.commandQueuePanelsEnabled).thenReturn(true)
`when`(shadeViewController.isViewEnabled).thenReturn(true)
@@ -327,7 +325,7 @@ class PhoneStatusBarViewControllerTest : SysuiTestCase() {
}
@Test
- @DisableFlags(AconfigFlags.FLAG_STATUS_BAR_CONNECTED_DISPLAYS)
+ @DisableFlags(StatusBarConnectedDisplays.FLAG_NAME)
fun handleTouchEventFromStatusBar_touchOnSecondaryDisplay_statusBarConnectedDisplaysDisabled_shadeReceivesEvent() {
`when`(centralSurfacesImpl.commandQueuePanelsEnabled).thenReturn(true)
`when`(shadeViewController.isViewEnabled).thenReturn(true)
@@ -339,10 +337,7 @@ class PhoneStatusBarViewControllerTest : SysuiTestCase() {
}
@Test
- @EnableFlags(
- AconfigFlags.FLAG_STATUS_BAR_CONNECTED_DISPLAYS,
- AconfigFlags.FLAG_SHADE_WINDOW_GOES_AROUND,
- )
+ @EnableFlags(StatusBarConnectedDisplays.FLAG_NAME, ShadeWindowGoesAround.FLAG_NAME)
fun handleTouchEventFromStatusBar_touchOnSecondaryDisplay_statusBarConnectedDisplaysEnabled_shadeWindowGoesAroundEnabled_shadeReceivesEvent() {
`when`(centralSurfacesImpl.commandQueuePanelsEnabled).thenReturn(true)
`when`(shadeViewController.isViewEnabled).thenReturn(true)
@@ -354,8 +349,8 @@ class PhoneStatusBarViewControllerTest : SysuiTestCase() {
}
@Test
- @EnableFlags(AconfigFlags.FLAG_STATUS_BAR_CONNECTED_DISPLAYS)
- @DisableFlags(AconfigFlags.FLAG_SHADE_WINDOW_GOES_AROUND)
+ @EnableFlags(StatusBarConnectedDisplays.FLAG_NAME)
+ @DisableFlags(ShadeWindowGoesAround.FLAG_NAME)
fun handleTouchEventFromStatusBar_touchOnSecondaryDisplay_statusBarConnectedDisplaysEnabled_shadeWindowGoesAroundDisabled_shadeDoesNotReceiveEvent() {
`when`(centralSurfacesImpl.commandQueuePanelsEnabled).thenReturn(true)
`when`(shadeViewController.isViewEnabled).thenReturn(true)
@@ -441,27 +436,30 @@ class PhoneStatusBarViewControllerTest : SysuiTestCase() {
}
@Test
- @EnableFlags(AconfigFlags.FLAG_SHADE_WINDOW_GOES_AROUND)
+ @EnableFlags(ShadeWindowGoesAround.FLAG_NAME)
fun onTouch_actionDown_propagatesToDisplayPolicy() {
- controller.onTouch(MotionEvent.obtain(0L, 0L, MotionEvent.ACTION_DOWN, 0f, 0f, 0))
+ val event = MotionEvent.obtain(0L, 0L, MotionEvent.ACTION_DOWN, 0f, 0f, 0)
+ controller.onTouch(event)
- verify(statusBarTouchShadeDisplayPolicy).onStatusBarTouched(eq(mContext.displayId))
+ verify(statusBarTouchShadeDisplayPolicy).onStatusBarTouched(eq(event), any())
}
@Test
- @EnableFlags(AconfigFlags.FLAG_SHADE_WINDOW_GOES_AROUND)
+ @EnableFlags(ShadeWindowGoesAround.FLAG_NAME)
fun onTouch_actionUp_notPropagatesToDisplayPolicy() {
- controller.onTouch(MotionEvent.obtain(0L, 0L, MotionEvent.ACTION_UP, 0f, 0f, 0))
+ val event = MotionEvent.obtain(0L, 0L, MotionEvent.ACTION_UP, 0f, 0f, 0)
+ controller.onTouch(event)
- verify(statusBarTouchShadeDisplayPolicy, never()).onStatusBarTouched(any())
+ verify(statusBarTouchShadeDisplayPolicy, never()).onStatusBarTouched(any(), any())
}
@Test
- @DisableFlags(AconfigFlags.FLAG_SHADE_WINDOW_GOES_AROUND)
+ @DisableFlags(ShadeWindowGoesAround.FLAG_NAME)
fun onTouch_shadeWindowGoesAroundDisabled_notPropagatesToDisplayPolicy() {
- controller.onTouch(MotionEvent.obtain(0L, 0L, MotionEvent.ACTION_DOWN, 0f, 0f, 0))
+ val event = MotionEvent.obtain(0L, 0L, MotionEvent.ACTION_DOWN, 0f, 0f, 0)
+ controller.onTouch(event)
- verify(statusBarTouchShadeDisplayPolicy, never()).onStatusBarTouched(any())
+ verify(statusBarTouchShadeDisplayPolicy, never()).onStatusBarTouched(eq(event), any())
}
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionsRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionsRepositoryTest.kt
index d7456dfb33c3..6c60f55a3904 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionsRepositoryTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionsRepositoryTest.kt
@@ -829,7 +829,7 @@ class MobileConnectionsRepositoryTest : SysuiTestCase() {
testScope.runTest {
val latest by collectLastValue(underTest.defaultDataSubId)
- assertThat(latest).isEqualTo(INVALID_SUBSCRIPTION_ID)
+ assertThat(latest).isEqualTo(null)
val intent2 =
Intent(TelephonyManager.ACTION_DEFAULT_DATA_SUBSCRIPTION_CHANGED)
@@ -856,6 +856,31 @@ class MobileConnectionsRepositoryTest : SysuiTestCase() {
}
@Test
+ fun defaultDataSubId_filtersOutInvalidSubIds() =
+ testScope.runTest {
+ subscriptionManagerProxy.defaultDataSubId = INVALID_SUBSCRIPTION_ID
+ val latest by collectLastValue(underTest.defaultDataSubId)
+
+ assertThat(latest).isNull()
+ }
+
+ @Test
+ fun defaultDataSubId_filtersOutInvalidSubIds_fromValidToInvalid() =
+ testScope.runTest {
+ subscriptionManagerProxy.defaultDataSubId = 2
+ val latest by collectLastValue(underTest.defaultDataSubId)
+
+ assertThat(latest).isEqualTo(2)
+
+ val intent =
+ Intent(TelephonyManager.ACTION_DEFAULT_DATA_SUBSCRIPTION_CHANGED)
+ .putExtra(PhoneConstants.SUBSCRIPTION_KEY, INVALID_SUBSCRIPTION_ID)
+ fakeBroadcastDispatcher.sendIntentToMatchingReceiversOnly(context, intent)
+
+ assertThat(latest).isNull()
+ }
+
+ @Test
fun defaultDataSubId_fetchesCurrentOnRestart() =
testScope.runTest {
subscriptionManagerProxy.defaultDataSubId = 2
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/shade/data/repository/ShadeDisplaysRepositoryKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/shade/data/repository/ShadeDisplaysRepositoryKosmos.kt
index 636cb37adf03..aaef27d257c5 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/shade/data/repository/ShadeDisplaysRepositoryKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/shade/data/repository/ShadeDisplaysRepositoryKosmos.kt
@@ -23,7 +23,11 @@ import com.android.systemui.kosmos.testScope
import com.android.systemui.shade.display.AnyExternalShadeDisplayPolicy
import com.android.systemui.shade.display.DefaultDisplayShadePolicy
import com.android.systemui.shade.display.ShadeDisplayPolicy
+import com.android.systemui.shade.display.ShadeExpansionIntent
import com.android.systemui.shade.display.StatusBarTouchShadeDisplayPolicy
+import com.android.systemui.shade.domain.interactor.notificationElement
+import com.android.systemui.shade.domain.interactor.qsElement
+import com.android.systemui.shade.domain.interactor.shadeInteractor
import com.android.systemui.util.settings.fakeGlobalSettings
val Kosmos.defaultShadeDisplayPolicy: DefaultDisplayShadePolicy by
@@ -37,16 +41,20 @@ val Kosmos.anyExternalShadeDisplayPolicy: AnyExternalShadeDisplayPolicy by
)
}
-val Kosmos.focusBasedShadeDisplayPolicy: StatusBarTouchShadeDisplayPolicy by
+val Kosmos.statusBarTouchShadeDisplayPolicy: StatusBarTouchShadeDisplayPolicy by
Kosmos.Fixture {
StatusBarTouchShadeDisplayPolicy(
displayRepository = displayRepository,
backgroundScope = testScope.backgroundScope,
keyguardRepository = keyguardRepository,
shadeOnDefaultDisplayWhenLocked = false,
+ shadeInteractor = { shadeInteractor },
+ notificationElement = { notificationElement },
+ qsShadeElement = { qsElement },
)
}
-
+val Kosmos.shadeExpansionIntent: ShadeExpansionIntent by
+ Kosmos.Fixture { statusBarTouchShadeDisplayPolicy }
val Kosmos.shadeDisplaysRepository: MutableShadeDisplaysRepository by
Kosmos.Fixture {
ShadeDisplaysRepositoryImpl(
@@ -62,7 +70,7 @@ val Kosmos.shadeDisplayPolicies: Set<ShadeDisplayPolicy> by
setOf(
defaultShadeDisplayPolicy,
anyExternalShadeDisplayPolicy,
- focusBasedShadeDisplayPolicy,
+ statusBarTouchShadeDisplayPolicy,
)
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/shade/domain/interactor/ShadeDisplaysInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/shade/domain/interactor/ShadeDisplaysInteractorKosmos.kt
index 6e44df833582..923de2dcbf68 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/shade/domain/interactor/ShadeDisplaysInteractorKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/shade/domain/interactor/ShadeDisplaysInteractorKosmos.kt
@@ -23,6 +23,7 @@ import com.android.systemui.kosmos.testScope
import com.android.systemui.shade.ShadeDisplayChangeLatencyTracker
import com.android.systemui.shade.ShadeWindowLayoutParams
import com.android.systemui.shade.data.repository.fakeShadeDisplaysRepository
+import com.android.systemui.shade.data.repository.shadeExpansionIntent
import java.util.Optional
import org.mockito.kotlin.any
import org.mockito.kotlin.mock
@@ -49,5 +50,6 @@ val Kosmos.shadeDisplaysInteractor by
testScope.backgroundScope.coroutineContext,
mockedShadeDisplayChangeLatencyTracker,
Optional.of(shadeExpandedStateInteractor),
+ shadeExpansionIntent,
)
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/shade/domain/interactor/ShadeInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/shade/domain/interactor/ShadeInteractorKosmos.kt
index 1dc7229a6506..32a30502a370 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/shade/domain/interactor/ShadeInteractorKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/shade/domain/interactor/ShadeInteractorKosmos.kt
@@ -31,7 +31,6 @@ import com.android.systemui.statusbar.phone.dozeParameters
import com.android.systemui.statusbar.policy.data.repository.userSetupRepository
import com.android.systemui.statusbar.policy.domain.interactor.deviceProvisioningInteractor
import com.android.systemui.user.domain.interactor.userSwitcherInteractor
-import org.mockito.kotlin.mock
var Kosmos.baseShadeInteractor: BaseShadeInteractor by
Kosmos.Fixture {
@@ -73,7 +72,19 @@ val Kosmos.shadeInteractorImpl by
shadeModeInteractor = shadeModeInteractor,
)
}
-var Kosmos.mockShadeInteractor: ShadeInteractor by Kosmos.Fixture { mock() }
+var Kosmos.notificationElement: NotificationShadeElement by
+ Kosmos.Fixture {
+ NotificationShadeElement(shadeInteractor, testScope.backgroundScope.coroutineContext)
+ }
+var Kosmos.qsElement: QSShadeElement by
+ Kosmos.Fixture { QSShadeElement(shadeInteractor, testScope.backgroundScope.coroutineContext) }
val Kosmos.shadeExpandedStateInteractor by
- Kosmos.Fixture { ShadeExpandedStateInteractorImpl(shadeInteractor, testScope.backgroundScope) }
+ Kosmos.Fixture {
+ ShadeExpandedStateInteractorImpl(
+ shadeInteractor,
+ testScope.backgroundScope,
+ notificationElement,
+ qsElement,
+ )
+ }
val Kosmos.fakeShadeExpandedStateInteractor by Kosmos.Fixture { FakeShadeExpandedStateInteractor() }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/FakeCarrierConfigRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/FakeCarrierConfigRepository.kt
index adf6ca1d1710..eebfca79efe3 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/FakeCarrierConfigRepository.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/FakeCarrierConfigRepository.kt
@@ -17,6 +17,7 @@
package com.android.systemui.statusbar.pipeline.mobile.data.repository
import android.os.PersistableBundle
+import android.telephony.SubscriptionManager.INVALID_SUBSCRIPTION_ID
import com.android.systemui.statusbar.pipeline.mobile.data.model.SystemUiCarrierConfig
class FakeCarrierConfigRepository : CarrierConfigRepository {
@@ -24,8 +25,12 @@ class FakeCarrierConfigRepository : CarrierConfigRepository {
val configsById = mutableMapOf<Int, SystemUiCarrierConfig>()
- override fun getOrCreateConfigForSubId(subId: Int): SystemUiCarrierConfig =
- configsById.getOrPut(subId) { SystemUiCarrierConfig(subId, createDefaultTestConfig()) }
+ override fun getOrCreateConfigForSubId(maybeSubId: Int?): SystemUiCarrierConfig {
+ val subId = maybeSubId ?: INVALID_SUBSCRIPTION_ID
+ return configsById.getOrPut(subId) {
+ SystemUiCarrierConfig(subId, createDefaultTestConfig())
+ }
+ }
}
val CarrierConfigRepository.fake
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/FakeMobileIconsInteractor.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/FakeMobileIconsInteractor.kt
index 3b8adb4a8307..352f6cf011e1 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/FakeMobileIconsInteractor.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/FakeMobileIconsInteractor.kt
@@ -55,7 +55,7 @@ class FakeMobileIconsInteractor(
override val filteredSubscriptions = MutableStateFlow<List<SubscriptionModel>>(listOf())
- override val defaultDataSubId = MutableStateFlow(DEFAULT_DATA_SUB_ID)
+ override val defaultDataSubId: MutableStateFlow<Int?> = MutableStateFlow(DEFAULT_DATA_SUB_ID)
private val _activeDataConnectionHasDataEnabled = MutableStateFlow(false)
override val activeDataConnectionHasDataEnabled = _activeDataConnectionHasDataEnabled
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/volume/dialog/sliders/dagger/VolumeDialogSliderComponentKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/volume/dialog/sliders/dagger/VolumeDialogSliderComponentKosmos.kt
index 4f79f7b4b41a..cb38cc3576ad 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/volume/dialog/sliders/dagger/VolumeDialogSliderComponentKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/volume/dialog/sliders/dagger/VolumeDialogSliderComponentKosmos.kt
@@ -18,6 +18,7 @@ package com.android.systemui.volume.dialog.sliders.dagger
import android.content.applicationContext
import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.testDispatcher
import com.android.systemui.kosmos.testScope
import com.android.systemui.plugins.volumeDialogController
import com.android.systemui.statusbar.policy.data.repository.zenModeRepository
@@ -74,6 +75,7 @@ private fun Kosmos.setupVolumeDialogSliderComponent(
volumeDialogSliderType = type
applicationContext = parentKosmos.applicationContext
testScope = parentKosmos.testScope
+ testDispatcher = parentKosmos.testDispatcher
volumeDialogController = parentKosmos.volumeDialogController
mediaControllerInteractor = parentKosmos.mediaControllerInteractor
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/volume/dialog/sliders/ui/viewmodel/VolumeDialogSliderViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/volume/dialog/sliders/ui/viewmodel/VolumeDialogSliderViewModelKosmos.kt
index 63cd440a8633..b26081c40c38 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/volume/dialog/sliders/ui/viewmodel/VolumeDialogSliderViewModelKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/volume/dialog/sliders/ui/viewmodel/VolumeDialogSliderViewModelKosmos.kt
@@ -20,15 +20,19 @@ import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.applicationCoroutineScope
import com.android.systemui.util.time.systemClock
import com.android.systemui.volume.dialog.domain.interactor.volumeDialogVisibilityInteractor
+import com.android.systemui.volume.dialog.shared.volumeDialogLogger
import com.android.systemui.volume.dialog.sliders.domain.interactor.volumeDialogSliderInteractor
+import com.android.systemui.volume.dialog.sliders.domain.model.volumeDialogSliderType
val Kosmos.volumeDialogSliderViewModel by
Kosmos.Fixture {
VolumeDialogSliderViewModel(
+ sliderType = volumeDialogSliderType,
interactor = volumeDialogSliderInteractor,
visibilityInteractor = volumeDialogVisibilityInteractor,
coroutineScope = applicationCoroutineScope,
volumeDialogSliderIconProvider = volumeDialogSliderIconProvider,
systemClock = systemClock,
+ logger = volumeDialogLogger,
)
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/volume/dialog/sliders/ui/viewmodel/VolumeDialogSlidersViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/volume/dialog/sliders/ui/viewmodel/VolumeDialogSlidersViewModelKosmos.kt
index 5531f7608b69..8fb60fdd5ab1 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/volume/dialog/sliders/ui/viewmodel/VolumeDialogSlidersViewModelKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/volume/dialog/sliders/ui/viewmodel/VolumeDialogSlidersViewModelKosmos.kt
@@ -18,6 +18,7 @@ package com.android.systemui.volume.dialog.sliders.ui.viewmodel
import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.applicationCoroutineScope
+import com.android.systemui.volume.dialog.shared.volumeDialogLogger
import com.android.systemui.volume.dialog.sliders.dagger.volumeDialogSliderComponentFactory
import com.android.systemui.volume.dialog.sliders.domain.interactor.volumeDialogSlidersInteractor
@@ -27,5 +28,6 @@ val Kosmos.volumeDialogSlidersViewModel by
applicationCoroutineScope,
volumeDialogSlidersInteractor,
volumeDialogSliderComponentFactory,
+ volumeDialogLogger,
)
}
diff --git a/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/BuildScope.kt b/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/BuildScope.kt
index bd2173cd2393..c9fae70353f1 100644
--- a/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/BuildScope.kt
+++ b/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/BuildScope.kt
@@ -100,7 +100,7 @@ interface BuildScope : HasNetwork, StateScope {
* observation of new emissions. It will however *not* cancel any running effects from previous
* emissions. To achieve this behavior, use [launchScope] or [asyncScope] to create a child
* build scope:
- * ``` kotlin
+ * ```
* val job = launchScope {
* events.observe { x ->
* launchEffect { longRunningEffect(x) }
@@ -141,7 +141,7 @@ interface BuildScope : HasNetwork, StateScope {
*
* By default, [builder] is only running while the returned [Events] is being
* [observed][observe]. If you want it to run at all times, simply add a no-op observer:
- * ``` kotlin
+ * ```
* events { ... }.apply { observe() }
* ```
*/
@@ -158,7 +158,7 @@ interface BuildScope : HasNetwork, StateScope {
*
* By default, [builder] is only running while the returned [Events] is being
* [observed][observe]. If you want it to run at all times, simply add a no-op observer:
- * ``` kotlin
+ * ```
* events { ... }.apply { observe() }
* ```
*
@@ -196,7 +196,7 @@ interface BuildScope : HasNetwork, StateScope {
* outside of the current Kairos transaction; when [transform] returns, the returned value is
* emitted from the result [Events] in a new transaction.
*
- * ``` kotlin
+ * ```
* fun <A, B> Events<A>.mapAsyncLatest(transform: suspend (A) -> B): Events<B> =
* mapLatestBuild { a -> asyncEvent { transform(a) } }.flatten()
* ```
@@ -571,7 +571,7 @@ interface BuildScope : HasNetwork, StateScope {
/**
* Shorthand for:
- * ``` kotlin
+ * ```
* flow.toEvents().holdState(initialValue)
* ```
*/
@@ -579,7 +579,7 @@ interface BuildScope : HasNetwork, StateScope {
/**
* Shorthand for:
- * ``` kotlin
+ * ```
* flow.scan(initialValue, operation).toEvents().holdState(initialValue)
* ```
*/
@@ -588,7 +588,7 @@ interface BuildScope : HasNetwork, StateScope {
/**
* Shorthand for:
- * ``` kotlin
+ * ```
* flow.scan(initialValue) { a, f -> f(a) }.toEvents().holdState(initialValue)
* ```
*/
@@ -665,7 +665,7 @@ interface BuildScope : HasNetwork, StateScope {
* be used to make further modifications to the Kairos network, and/or perform side-effects via
* [effect].
*
- * ``` kotlin
+ * ```
* fun <A> State<A>.observeBuild(block: BuildScope.(A) -> Unit = {}): Job = launchScope {
* block(sample())
* changes.observeBuild(block)
@@ -698,7 +698,7 @@ interface BuildScope : HasNetwork, StateScope {
* outside of the current Kairos transaction; when it completes, the returned [Events] emits in a
* new transaction.
*
- * ``` kotlin
+ * ```
* fun <A> BuildScope.asyncEvent(block: suspend () -> A): Events<A> =
* events { emit(block()) }.apply { observe() }
* ```
@@ -719,7 +719,7 @@ fun <A> BuildScope.asyncEvent(block: suspend () -> A): Events<A> =
* executed if this [BuildScope] is still active by that time. It can be deactivated due to a
* -Latest combinator, for example.
*
- * ``` kotlin
+ * ```
* fun BuildScope.effect(
* context: CoroutineContext = EmptyCoroutineContext,
* block: EffectScope.() -> Unit,
@@ -740,7 +740,7 @@ fun BuildScope.effect(
* done because the current [BuildScope] might be deactivated within this transaction, perhaps due
* to a -Latest combinator. If this happens, then the coroutine will never actually be started.
*
- * ``` kotlin
+ * ```
* fun BuildScope.launchEffect(block: suspend KairosScope.() -> Unit): Job =
* effect { effectCoroutineScope.launch { block() } }
* ```
@@ -757,7 +757,7 @@ fun BuildScope.launchEffect(block: suspend KairosCoroutineScope.() -> Unit): Job
* to a -Latest combinator. If this happens, then the coroutine will never actually be started.
*
* Shorthand for:
- * ``` kotlin
+ * ```
* fun <R> BuildScope.asyncEffect(block: suspend KairosScope.() -> R): Deferred<R> =
* CompletableDeferred<R>.apply {
* effect { effectCoroutineScope.launch { complete(block()) } }
@@ -789,7 +789,7 @@ fun BuildScope.launchScope(block: BuildSpec<*>): Job = asyncScope(block).second
*
* By default, [builder] is only running while the returned [Events] is being
* [observed][BuildScope.observe]. If you want it to run at all times, simply add a no-op observer:
- * ``` kotlin
+ * ```
* events { ... }.apply { observe() }
* ```
*
@@ -813,7 +813,7 @@ fun <In, Out> BuildScope.coalescingEvents(
*
* By default, [builder] is only running while the returned [Events] is being
* [observed][BuildScope.observe]. If you want it to run at all times, simply add a no-op observer:
- * ``` kotlin
+ * ```
* events { ... }.apply { observe() }
* ```
*
diff --git a/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/Events.kt b/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/Events.kt
index 8f468c153743..1a13773d71bf 100644
--- a/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/Events.kt
+++ b/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/Events.kt
@@ -105,7 +105,7 @@ class EventsLoop<A> : Events<A>() {
*
* Useful for recursive definitions.
*
- * ``` kotlin
+ * ```
* fun <A> Lazy<Events<A>>.defer() = deferredEvents { value }
* ```
*
@@ -122,7 +122,7 @@ class EventsLoop<A> : Events<A>() {
*
* Useful for recursive definitions.
*
- * ``` kotlin
+ * ```
* fun <A> DeferredValue<Events<A>>.defer() = deferredEvents { get() }
* ```
*
@@ -160,7 +160,7 @@ fun <A, B> Events<A>.mapMaybe(transform: TransactionScope.(A) -> Maybe<B>): Even
* Returns an [Events] that contains only the non-null results of applying [transform] to each value
* of the original [Events].
*
- * ``` kotlin
+ * ```
* fun <A> Events<A>.mapNotNull(transform: TransactionScope.(A) -> B?): Events<B> =
* mapMaybe { if (it == null) absent else present(it) }
* ```
@@ -201,7 +201,7 @@ fun <A, B> Events<A>.mapCheap(transform: TransactionScope.(A) -> B): Events<B> =
* Returns an [Events] that invokes [action] before each value of the original [Events] is emitted.
* Useful for logging and debugging.
*
- * ``` kotlin
+ * ```
* fun <A> Events<A>.onEach(action: TransactionScope.(A) -> Unit): Events<A> =
* map { it.also { action(it) } }
* ```
@@ -220,7 +220,7 @@ fun <A> Events<A>.onEach(action: TransactionScope.(A) -> Unit): Events<A> = map
* Splits an [Events] of pairs into a pair of [Events], where each returned [Events] emits half of
* the original.
*
- * ``` kotlin
+ * ```
* fun <A, B> Events<Pair<A, B>>.unzip(): Pair<Events<A>, Events<B>> {
* val lefts = map { it.first }
* val rights = map { it.second }
diff --git a/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/Filter.kt b/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/Filter.kt
index 8ca5ac8652db..d412b849e441 100644
--- a/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/Filter.kt
+++ b/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/Filter.kt
@@ -29,7 +29,7 @@ fun <A> Events<A>.filter(state: State<Boolean>): Events<A> = filter { state.samp
/**
* Returns an [Events] containing only values of the original [Events] that are not null.
*
- * ``` kotlin
+ * ```
* fun <A> Events<A?>.filterNotNull(): Events<A> = mapNotNull { it }
* ```
*
@@ -41,7 +41,7 @@ fun <A> Events<A?>.filterNotNull(): Events<A> = mapCheap { it.toMaybe() }.filter
/**
* Returns an [Events] containing only values of the original [Events] that are instances of [A].
*
- * ``` kotlin
+ * ```
* inline fun <reified A> Events<*>.filterIsInstance(): Events<A> =
* mapNotNull { it as? A }
* ```
@@ -55,7 +55,7 @@ inline fun <reified A> Events<*>.filterIsInstance(): Events<A> =
/**
* Returns an [Events] containing only values of the original [Events] that are present.
*
- * ``` kotlin
+ * ```
* fun <A> Events<Maybe<A>>.filterPresent(): Events<A> = mapMaybe { it }
* ```
*
@@ -69,7 +69,7 @@ fun <A> Events<Maybe<A>>.filterPresent(): Events<A> =
* Returns an [Events] containing only values of the original [Events] that satisfy the given
* [predicate].
*
- * ``` kotlin
+ * ```
* fun <A> Events<A>.filter(predicate: TransactionScope.(A) -> Boolean): Events<A> =
* mapMaybe { if (predicate(it)) present(it) else absent }
* ```
diff --git a/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/GroupBy.kt b/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/GroupBy.kt
index 45da34ac9ae6..27fc1b4cf54c 100644
--- a/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/GroupBy.kt
+++ b/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/GroupBy.kt
@@ -63,7 +63,7 @@ fun <K, A> Events<Map<K, A>>.groupByKey(numKeys: Int? = null): GroupedEvents<K,
* downstream [Events]. The downstream [Events] are associated with a [key][K], which is derived
* from each emission of the original [Events] via [extractKey].
*
- * ``` kotlin
+ * ```
* fun <K, A> Events<A>.groupBy(
* numKeys: Int? = null,
* extractKey: TransactionScope.(A) -> K,
@@ -108,7 +108,7 @@ class GroupedEvents<in K, out A> internal constructor(internal val impl: DemuxIm
* Using this is equivalent to `upstream.filter(predicate) to upstream.filter { !predicate(it) }`
* but is more efficient; specifically, [partition] will only invoke [predicate] once per element.
*
- * ``` kotlin
+ * ```
* fun <A> Events<A>.partition(
* predicate: TransactionScope.(A) -> Boolean
* ): Pair<Events<A>, Events<A>> =
@@ -133,7 +133,7 @@ fun <A> Events<A>.partition(
* [First]s and once for [Second]s, but is slightly more efficient; specifically, the
* [filterIsInstance] check is only performed once per element.
*
- * ``` kotlin
+ * ```
* fun <A, B> Events<Either<A, B>>.partitionEither(): Pair<Events<A>, Events<B>> =
* map { it.asThese() }.partitionThese()
* ```
diff --git a/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/Incremental.kt b/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/Incremental.kt
index d88ae3b81349..02941bdded30 100644
--- a/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/Incremental.kt
+++ b/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/Incremental.kt
@@ -62,7 +62,7 @@ fun <K, V> incrementalOf(value: Map<K, V>): Incremental<K, V> {
*
* Useful for recursive definitions.
*
- * ``` kotlin
+ * ```
* fun <A> Lazy<Incremental<K, V>>.defer() = deferredIncremental { value }
* ```
*/
@@ -78,7 +78,7 @@ fun <K, V> Lazy<Incremental<K, V>>.defer(): Incremental<K, V> = deferInline { va
*
* Useful for recursive definitions.
*
- * ``` kotlin
+ * ```
* fun <A> DeferredValue<Incremental<K, V>>.defer() = deferredIncremental { get() }
* ```
*/
diff --git a/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/Merge.kt b/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/Merge.kt
index de9dca43b5d5..cc4ce53ca40a 100644
--- a/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/Merge.kt
+++ b/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/Merge.kt
@@ -33,7 +33,7 @@ import com.android.systemui.kairos.util.map
* function is used to combine coincident emissions to produce the result value to be emitted by the
* merged [Events].
*
- * ``` kotlin
+ * ```
* fun <A> Events<A>.mergeWith(
* other: Events<A>,
* transformCoincidence: TransactionScope.(A, A) -> A = { a, _ -> a },
@@ -62,7 +62,7 @@ fun <A> Events<A>.mergeWith(
* Merges the given [Events] into a single [Events] that emits events from all. All coincident
* emissions are collected into the emitted [List], preserving the input ordering.
*
- * ``` kotlin
+ * ```
* fun <A> merge(vararg events: Events<A>): Events<List<A>> = events.asIterable().merge()
* ```
*
@@ -76,7 +76,7 @@ fun <A> merge(vararg events: Events<A>): Events<List<A>> = events.asIterable().m
* Merges the given [Events] into a single [Events] that emits events from all. In the case of
* coincident emissions, the emission from the left-most [Events] is emitted.
*
- * ``` kotlin
+ * ```
* fun <A> mergeLeft(vararg events: Events<A>): Events<A> = events.asIterable().mergeLeft()
* ```
*
@@ -92,7 +92,7 @@ fun <A> mergeLeft(vararg events: Events<A>): Events<A> = events.asIterable().mer
* function is used to combine coincident emissions to produce the result value to be emitted by the
* merged [Events].
*
- * ``` kotlin
+ * ```
* fun <A> merge(vararg events: Events<A>, transformCoincidence: (A, A) -> A): Events<A> =
* merge(*events).map { l -> l.reduce(transformCoincidence) }
* ```
@@ -117,7 +117,7 @@ fun <A> Iterable<Events<A>>.merge(): Events<List<A>> =
* coincident emissions, the emission from the left-most [Events] is emitted.
*
* Semantically equivalent to the following definition:
- * ``` kotlin
+ * ```
* fun <A> Iterable<Events<A>>.mergeLeft(): Events<A> =
* merge().mapCheap { it.first() }
* ```
@@ -135,7 +135,7 @@ fun <A> Iterable<Events<A>>.mergeLeft(): Events<A> =
* Creates a new [Events] that emits events from all given [Events]. All simultaneous emissions are
* collected into the emitted [List], preserving the input ordering.
*
- * ``` kotlin
+ * ```
* fun <A> Sequence<Events<A>>.merge(): Events<List<A>> = asIterable().merge()
* ```
*
@@ -148,7 +148,7 @@ fun <A> Iterable<Events<A>>.mergeLeft(): Events<A> =
* collected into the emitted [Map], and are given the same key of the associated [Events] in the
* input [Map].
*
- * ``` kotlin
+ * ```
* fun <K, A> Map<K, Events<A>>.merge(): Events<Map<K, A>> =
* asSequence()
* .map { (k, events) -> events.map { a -> k to a } }
@@ -173,7 +173,7 @@ fun <K, A> Map<K, Events<A>>.merge(): Events<Map<K, A>> =
* [Map.applyPatch][com.android.systemui.kairos.util.applyPatch].
*
* Conceptually this is equivalent to:
- * ``` kotlin
+ * ```
* fun <K, V> State<Map<K, V>>.mergeEventsIncrementally(): Events<Map<K, V>> =
* map { it.merge() }.switchEvents()
* ```
@@ -218,7 +218,7 @@ fun <K, V> Incremental<K, Events<V>>.mergeEventsIncrementally(): Events<Map<K, V
* [Map.applyPatch][com.android.systemui.kairos.util.applyPatch].
*
* Conceptually this is equivalent to:
- * ``` kotlin
+ * ```
* fun <K, V> State<Map<K, V>>.mergeEventsIncrementallyPromptly(): Events<Map<K, V>> =
* map { it.merge() }.switchEventsPromptly()
* ```
diff --git a/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/State.kt b/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/State.kt
index 22ca83c6a15a..e8b005ec8788 100644
--- a/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/State.kt
+++ b/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/State.kt
@@ -76,7 +76,7 @@ fun <A> stateOf(value: A): State<A> {
*
* Useful for recursive definitions.
*
- * ``` kotlin
+ * ```
* fun <A> Lazy<State<A>>.defer() = deferredState { value }
* ```
*/
@@ -91,7 +91,7 @@ fun <A> stateOf(value: A): State<A> {
*
* Useful for recursive definitions.
*
- * ``` kotlin
+ * ```
* fun <A> DeferredValue<State<A>>.defer() = deferredState { get() }
* ```
*/
@@ -150,7 +150,7 @@ fun <A, B> State<A>.mapCheapUnsafe(transform: KairosScope.(A) -> B): State<B> {
* Splits a [State] of pairs into a pair of [Events][State], where each returned [State] holds half
* of the original.
*
- * ``` kotlin
+ * ```
* fun <A, B> State<Pair<A, B>>.unzip(): Pair<State<A>, State<B>> {
* val first = map { it.first }
* val second = map { it.second }
@@ -186,7 +186,7 @@ fun <A, B> State<A>.flatMap(transform: KairosScope.(A) -> State<B>): State<B> {
/**
* Returns a [State] that behaves like the current value of the original [State].
*
- * ``` kotlin
+ * ```
* fun <A> State<State<A>>.flatten() = flatMap { it }
* ```
*
@@ -201,7 +201,7 @@ fun <A, B> State<A>.flatMap(transform: KairosScope.(A) -> State<B>): State<B> {
* recent value is used.
*
* Effectively equivalent to:
- * ``` kotlin
+ * ```
* ConflatedMutableEvents(kairosNetwork).holdState(initialValue)
* ```
*/
@@ -328,7 +328,7 @@ private inline fun <A> deferInline(crossinline block: InitScope.() -> State<A>):
* Like [changes] but also includes the old value of this [State].
*
* Shorthand for:
- * ``` kotlin
+ * ```
* stateChanges.map { WithPrev(previousValue = sample(), newValue = it) }
* ```
*/
diff --git a/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/StateScope.kt b/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/StateScope.kt
index faeffe84e2e8..8020896d228a 100644
--- a/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/StateScope.kt
+++ b/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/StateScope.kt
@@ -116,7 +116,7 @@ interface StateScope : TransactionScope {
* [Events] emitted from this, following the patch rules outlined in
* [Map.applyPatch][com.android.systemui.kairos.util.applyPatch].
*
- * ``` kotlin
+ * ```
* fun <K, V> Events<MapPatch<K, Events<V>>>.mergeEventsIncrementally(
* initialEvents: DeferredValue<Map<K, Events<V>>>,
* ): Events<Map<K, V>> =
@@ -135,7 +135,7 @@ interface StateScope : TransactionScope {
* [Events] emitted from this, following the patch rules outlined in
* [Map.applyPatch][com.android.systemui.kairos.util.applyPatch].
*
- * ``` kotlin
+ * ```
* fun <K, V> Events<MapPatch<K, Events<V>>>.mergeEventsIncrementallyPromptly(
* initialEvents: DeferredValue<Map<K, Events<V>>>,
* ): Events<Map<K, V>> =
@@ -155,7 +155,7 @@ interface StateScope : TransactionScope {
* [Events] emitted from this, following the patch rules outlined in
* [Map.applyPatch][com.android.systemui.kairos.util.applyPatch].
*
- * ``` kotlin
+ * ```
* fun <K, V> Events<MapPatch<K, Events<V>>>.mergeEventsIncrementally(
* initialEvents: Map<K, Events<V>>,
* ): Events<Map<K, V>> =
@@ -174,7 +174,7 @@ interface StateScope : TransactionScope {
* [Events] emitted from this, following the patch rules outlined in
* [Map.applyPatch][com.android.systemui.kairos.util.applyPatch].
*
- * ``` kotlin
+ * ```
* fun <K, V> Events<MapPatch<K, Events<V>>>.mergeEventsIncrementallyPromptly(
* initialEvents: Map<K, Events<V>>,
* ): Events<Map<K, V>> =
@@ -220,7 +220,7 @@ interface StateScope : TransactionScope {
* [mapLatestStateful], accumulation is not stopped with each subsequent emission of the
* original [Events].
*
- * ``` kotlin
+ * ```
* fun <A, B> Events<A>.mapStateful(transform: StateScope.(A) -> B): Events<B> =
* map { statefully { transform(it) } }.applyStatefuls()
* ```
@@ -234,7 +234,7 @@ interface StateScope : TransactionScope {
*
* Unlike [applyLatestStateful], state accumulation is not stopped with each state change.
*
- * ``` kotlin
+ * ```
* fun <A> State<Stateful<A>>.applyStatefuls(): State<A> =
* changes
* .applyStatefuls()
@@ -252,7 +252,7 @@ interface StateScope : TransactionScope {
* Returns an [Events] that acts like the most recent [Events] to be emitted from the original
* [Events].
*
- * ``` kotlin
+ * ```
* fun <A> Events<Events<A>>.flatten() = holdState(emptyEvents).switchEvents()
* ```
*
@@ -267,7 +267,7 @@ interface StateScope : TransactionScope {
* [transform] can perform state accumulation via its [StateScope] receiver. With each
* invocation of [transform], state accumulation from previous invocation is stopped.
*
- * ``` kotlin
+ * ```
* fun <A, B> Events<A>.mapLatestStateful(transform: StateScope.(A) -> B): Events<B> =
* map { statefully { transform(it) } }.applyLatestStateful()
* ```
@@ -282,7 +282,7 @@ interface StateScope : TransactionScope {
* [transform] can perform state accumulation via its [StateScope] receiver. With each
* invocation of [transform], state accumulation from previous invocation is stopped.
*
- * ``` kotlin
+ * ```
* fun <A, B> Events<A>.flatMapLatestStateful(
* transform: StateScope.(A) -> Events<B>
* ): Events<B> =
@@ -495,7 +495,7 @@ interface StateScope : TransactionScope {
*
* The optional [numKeys] argument is an optimization used to initialize the internal storage.
*
- * ``` kotlin
+ * ```
* fun <K, A, B> Events<MapPatch<K, A>>.mapLatestStatefulForKey(
* numKeys: Int? = null,
* transform: StateScope.(A) -> B,
@@ -516,7 +516,7 @@ interface StateScope : TransactionScope {
* If the original [Events] is emitting an event at this exact time, then it will be the only
* even emitted from the result [Events].
*
- * ``` kotlin
+ * ```
* fun <A> Events<A>.nextOnly(): Events<A> =
* EventsLoop<A>().apply {
* loopback = map { emptyEvents }.holdState(this@nextOnly).switchEvents()
@@ -535,7 +535,7 @@ interface StateScope : TransactionScope {
/**
* Returns an [Events] that skips the next emission of the original [Events].
*
- * ``` kotlin
+ * ```
* fun <A> Events<A>.skipNext(): Events<A> =
* nextOnly().map { this@skipNext }.holdState(emptyEvents).switchEvents()
* ```
@@ -554,7 +554,7 @@ interface StateScope : TransactionScope {
* If the original [Events] emits at the same time as [stop], then the returned [Events] will
* emit that value.
*
- * ``` kotlin
+ * ```
* fun <A> Events<A>.takeUntil(stop: Events<*>): Events<A> =
* stop.map { emptyEvents }.nextOnly().holdState(this).switchEvents()
* ```
@@ -586,7 +586,7 @@ interface StateScope : TransactionScope {
* Returns an [Events] that emits values from the original [Events] up to and including a value
* is emitted that satisfies [predicate].
*
- * ``` kotlin
+ * ```
* fun <A> Events<A>.takeUntil(predicate: TransactionScope.(A) -> Boolean): Events<A> =
* takeUntil(filter(predicate))
* ```
@@ -602,7 +602,7 @@ interface StateScope : TransactionScope {
* have been processed; this keeps the value of the [State] consistent during the entire Kairos
* transaction.
*
- * ``` kotlin
+ * ```
* fun <A, B> Events<A>.foldState(
* initialValue: B,
* transform: TransactionScope.(A, B) -> B,
@@ -630,7 +630,7 @@ interface StateScope : TransactionScope {
* have been processed; this keeps the value of the [State] consistent during the entire Kairos
* transaction.
*
- * ``` kotlin
+ * ```
* fun <A, B> Events<A>.foldStateDeferred(
* initialValue: DeferredValue<B>,
* transform: TransactionScope.(A, B) -> B,
@@ -663,7 +663,7 @@ interface StateScope : TransactionScope {
* have been processed; this keeps the value of the [State] consistent during the entire Kairos
* transaction.
*
- * ``` kotlin
+ * ```
* fun <A> Events<Stateful<A>>.holdLatestStateful(init: Stateful<A>): State<A> {
* val (changes, initApplied) = applyLatestStateful(init)
* return changes.holdStateDeferred(initApplied)
@@ -724,7 +724,7 @@ interface StateScope : TransactionScope {
* Returns an [Events] that wraps each emission of the original [Events] into an [IndexedValue],
* containing the emitted value and its index (starting from zero).
*
- * ``` kotlin
+ * ```
* fun <A> Events<A>.withIndex(): Events<IndexedValue<A>> {
* val index = fold(0) { _, oldIdx -> oldIdx + 1 }
* return sample(index) { a, idx -> IndexedValue(idx, a) }
@@ -740,7 +740,7 @@ interface StateScope : TransactionScope {
* Returns an [Events] containing the results of applying [transform] to each value of the
* original [Events] and its index (starting from zero).
*
- * ``` kotlin
+ * ```
* fun <A> Events<A>.mapIndexed(transform: TransactionScope.(Int, A) -> B): Events<B> {
* val index = foldState(0) { _, i -> i + 1 }
* return sample(index) { a, idx -> transform(idx, a) }
@@ -755,7 +755,7 @@ interface StateScope : TransactionScope {
/**
* Returns an [Events] where all subsequent repetitions of the same value are filtered out.
*
- * ``` kotlin
+ * ```
* fun <A> Events<A>.distinctUntilChanged(): Events<A> {
* val state: State<Any?> = holdState(Any())
* return filter { it != state.sample() }
@@ -774,7 +774,7 @@ interface StateScope : TransactionScope {
* Note that the returned [Events] will not emit anything until [other] has emitted at least one
* value.
*
- * ``` kotlin
+ * ```
* fun <A, B, C> Events<A>.sample(
* other: Events<B>,
* transform: TransactionScope.(A, B) -> C,
@@ -796,7 +796,7 @@ interface StateScope : TransactionScope {
* Returns a [State] that samples the [Transactional] held by the given [State] within the same
* transaction that the state changes.
*
- * ``` kotlin
+ * ```
* fun <A> State<Transactional<A>>.sampleTransactionals(): State<A> =
* changes
* .sampleTransactionals()
@@ -815,7 +815,7 @@ interface StateScope : TransactionScope {
* Note that this is less efficient than [State.map], which should be preferred if [transform]
* does not need access to [TransactionScope].
*
- * ``` kotlin
+ * ```
* fun <A, B> State<A>.mapTransactionally(transform: TransactionScope.(A) -> B): State<B> =
* map { transactionally { transform(it) } }.sampleTransactionals()
* ```
@@ -830,7 +830,7 @@ interface StateScope : TransactionScope {
* Note that this is less efficient than [combine], which should be preferred if [transform]
* does not need access to [TransactionScope].
*
- * ``` kotlin
+ * ```
* fun <A, B, Z> combineTransactionally(
* stateA: State<A>,
* stateB: State<B>,
@@ -895,7 +895,7 @@ interface StateScope : TransactionScope {
* Note that this is less efficient than [flatMap], which should be preferred if [transform]
* does not need access to [TransactionScope].
*
- * ``` kotlin
+ * ```
* fun <A, B> State<A>.flatMapTransactionally(
* transform: TransactionScope.(A) -> State<B>
* ): State<B> = map { transactionally { transform(it) } }.sampleTransactionals().flatten()
@@ -950,7 +950,7 @@ interface StateScope : TransactionScope {
* Returns an [Incremental] that reflects the state of the original [Incremental], but also adds
* / removes entries based on the state of the original's values.
*
- * ``` kotlin
+ * ```
* fun <K, V> Incremental<K, State<Maybe<V>>>.applyStateIncrementally(): Incremental<K, V> =
* mapValues { (_, v) -> v.changes }
* .mergeEventsIncrementallyPromptly()
@@ -971,7 +971,7 @@ interface StateScope : TransactionScope {
* / removes entries based on the [State] returned from applying [transform] to the original's
* entries.
*
- * ``` kotlin
+ * ```
* fun <K, V, U> Incremental<K, V>.mapIncrementalState(
* transform: KairosScope.(Map.Entry<K, V>) -> State<Maybe<U>>
* ): Incremental<K, U> = mapValues { transform(it) }.applyStateIncrementally()
@@ -986,7 +986,7 @@ interface StateScope : TransactionScope {
* / removes entries based on the [State] returned from applying [transform] to the original's
* entries, such that entries are added when that state is `true`, and removed when `false`.
*
- * ``` kotlin
+ * ```
* fun <K, V> Incremental<K, V>.filterIncrementally(
* transform: KairosScope.(Map.Entry<K, V>) -> State<Boolean>
* ): Incremental<K, V> = mapIncrementalState { entry ->
@@ -1004,7 +1004,7 @@ interface StateScope : TransactionScope {
* Returns an [Incremental] that samples the [Transactionals][Transactional] held by the
* original within the same transaction that the incremental [updates].
*
- * ``` kotlin
+ * ```
* fun <K, V> Incremental<K, Transactional<V>>.sampleTransactionals(): Incremental<K, V> =
* updates
* .map { patch -> patch.mapValues { (k, mv) -> mv.map { it.sample() } } }
@@ -1027,7 +1027,7 @@ interface StateScope : TransactionScope {
* Note that this is less efficient than [mapValues], which should be preferred if [transform]
* does not need access to [TransactionScope].
*
- * ``` kotlin
+ * ```
* fun <K, V, U> Incremental<K, V>.mapValuesTransactionally(
* transform: TransactionScope.(Map.Entry<K, V>) -> U
* ): Incremental<K, U> =
diff --git a/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/Transactional.kt b/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/Transactional.kt
index cf98821fdadb..5050511a1371 100644
--- a/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/Transactional.kt
+++ b/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/Transactional.kt
@@ -51,7 +51,7 @@ fun <A> transactionalOf(value: A): Transactional<A> =
*
* Useful for recursive definitions.
*
- * ``` kotlin
+ * ```
* fun <A> DeferredValue<Transactional<A>>.defer() = deferredTransactional { get() }
* ```
*/
@@ -67,7 +67,7 @@ fun <A> DeferredValue<Transactional<A>>.defer(): Transactional<A> = deferInline
*
* Useful for recursive definitions.
*
- * ``` kotlin
+ * ```
* fun <A> Lazy<Transactional<A>>.defer() = deferredTransactional { value }
* ```
*/
diff --git a/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/internal/store/MapK.kt b/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/internal/store/MapK.kt
index e193a4957bd0..3dbc6f00c623 100644
--- a/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/internal/store/MapK.kt
+++ b/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/internal/store/MapK.kt
@@ -21,28 +21,28 @@ package com.android.systemui.kairos.internal.store
*
* Let's say you want to write a class that is generic over both a map, and the type of data within
* the map:
- * ``` kotlin
+ * ```
* class Foo<TMap, TKey, TValue> {
* val container: TMap<TKey, TElement> // disallowed!
* }
* ```
*
* You can use `MapK` to represent the "higher-kinded" type variable `TMap`:
- * ``` kotlin
+ * ```
* class Foo<TMap, TKey, TValue> {
* val container: MapK<TMap, TKey, TValue> // OK!
* }
* ```
*
* Note that Kotlin will not let you use the generic type without parameters as `TMap`:
- * ``` kotlin
+ * ```
* val fooHk: MapK<HashMap, Int, String> // not allowed: HashMap requires two type parameters
* ```
*
* To work around this, you need to declare a special type-witness object. This object is only used
* at compile time and can be stripped out by a minifier because it's never used at runtime.
*
- * ``` kotlin
+ * ```
* class Foo<A, B> : MapK<FooWitness, A, B> { ... }
* object FooWitness
*
diff --git a/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/util/MapPatch.kt b/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/util/MapPatch.kt
index 8fe41bc20dfa..dde5d822f052 100644
--- a/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/util/MapPatch.kt
+++ b/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/util/MapPatch.kt
@@ -47,7 +47,7 @@ fun <K, V> Map<K, V>.applyPatch(patch: MapPatch<K, V>): Map<K, V> {
* Returns a [MapPatch] that, when applied, includes all of the values from the original [Map].
*
* Shorthand for:
- * ``` kotlin
+ * ```
* mapValues { (key, value) -> Maybe.present(value) }
* ```
*/
diff --git a/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/util/Maybe.kt b/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/util/Maybe.kt
index 4754bc443329..4373705597ed 100644
--- a/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/util/Maybe.kt
+++ b/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/util/Maybe.kt
@@ -69,7 +69,7 @@ object MaybeScope {
*
* This can be used instead of Kotlin's built-in nullability (`?.` and `?:`) operators when dealing
* with complex combinations of nullables:
- * ``` kotlin
+ * ```
* val aMaybe: Maybe<Any> = ...
* val bMaybe: Maybe<Any> = ...
* val result: String = maybe {
diff --git a/packages/Vcn/service-b/src/com/android/server/vcn/Vcn.java b/packages/Vcn/service-b/src/com/android/server/vcn/Vcn.java
index 369ef6ae6a3f..97f86b1bff5b 100644
--- a/packages/Vcn/service-b/src/com/android/server/vcn/Vcn.java
+++ b/packages/Vcn/service-b/src/com/android/server/vcn/Vcn.java
@@ -209,6 +209,8 @@ public class Vcn extends Handler {
this(vcnContext, subscriptionGroup, config, snapshot, vcnCallback, new Dependencies());
}
+ // WARNING: This constructor executes on the binder thread. Thread safety MUST be ensured when
+ // accessing data within this constructor and any methods called from here.
@VisibleForTesting(visibility = Visibility.PRIVATE)
public Vcn(
@NonNull VcnContext vcnContext,
diff --git a/packages/Vcn/service-b/src/com/android/server/vcn/VcnNetworkProvider.java b/packages/Vcn/service-b/src/com/android/server/vcn/VcnNetworkProvider.java
index 99c848f53c39..38fcf09145d9 100644
--- a/packages/Vcn/service-b/src/com/android/server/vcn/VcnNetworkProvider.java
+++ b/packages/Vcn/service-b/src/com/android/server/vcn/VcnNetworkProvider.java
@@ -36,7 +36,6 @@ import android.net.vcn.VcnGatewayConnectionConfig;
import android.os.Build;
import android.os.Handler;
import android.os.Looper;
-import android.util.ArraySet;
import android.util.IndentingPrintWriter;
import android.util.Slog;
@@ -46,6 +45,7 @@ import com.android.modules.utils.HandlerExecutor;
import java.util.Objects;
import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.Executor;
/**
@@ -56,12 +56,14 @@ import java.util.concurrent.Executor;
*
* @hide
*/
+// TODO(b/388919146): Implement a more generic solution to prevent concurrent modifications on
+// mListeners and mRequests
// TODO(b/374174952): Replace VANILLA_ICE_CREAM with BAKLAVA after Android B finalization
@TargetApi(Build.VERSION_CODES.VANILLA_ICE_CREAM)
public class VcnNetworkProvider extends NetworkProvider {
private static final String TAG = VcnNetworkProvider.class.getSimpleName();
- private final Set<NetworkRequestListener> mListeners = new ArraySet<>();
+ private final Set<NetworkRequestListener> mListeners = ConcurrentHashMap.newKeySet();
private final Context mContext;
private final Handler mHandler;
@@ -72,7 +74,7 @@ public class VcnNetworkProvider extends NetworkProvider {
*
* <p>NetworkRequests are immutable once created, and therefore can be used as stable keys.
*/
- private final Set<NetworkRequest> mRequests = new ArraySet<>();
+ private final Set<NetworkRequest> mRequests = ConcurrentHashMap.newKeySet();
public VcnNetworkProvider(@NonNull Context context, @NonNull Looper looper) {
this(context, looper, new Dependencies());
diff --git a/ravenwood/texts/ravenwood-framework-policies.txt b/ravenwood/texts/ravenwood-framework-policies.txt
index 80126df1b8df..26b6fe3d82ad 100644
--- a/ravenwood/texts/ravenwood-framework-policies.txt
+++ b/ravenwood/texts/ravenwood-framework-policies.txt
@@ -52,7 +52,7 @@ class android.content.BroadcastReceiver keep
class android.content.Context keep
method <init> ()V keep
method getSystemService (Ljava/lang/Class;)Ljava/lang/Object; keep
-class android.content.pm.PackageManager keep
+class android.content.pm.PackageManager
method <init> ()V keep
class android.text.ClipboardManager keep
method <init> ()V keep
diff --git a/ravenwood/tools/hoststubgen/scripts/dump-jar b/ravenwood/tools/hoststubgen/scripts/dump-jar
index 998357b70dff..02a6d25378bd 100755
--- a/ravenwood/tools/hoststubgen/scripts/dump-jar
+++ b/ravenwood/tools/hoststubgen/scripts/dump-jar
@@ -89,14 +89,33 @@ filter_output() {
# - Some other transient lines
# - Sometimes the javap shows mysterious warnings, so remove them too.
#
- # `/PATTERN-1/,/PATTERN-2/{//!d}` is a trick to delete lines between two patterns, without
- # the start and the end lines.
+ # Most conversion are simple per-line deletion or simple inline replacement with a regex.
+ #
+ # But removing the constant pool is a bit tricky. It looks like this in the output:
+ #---------------------------
+ #Constant pool:
+ # #1 = Methodref #31.#88 // java/lang/Object."<init>":()V
+ # #2 = Class #89 // java/lang/UnsupportedOperationException
+ # :
+ #{ // Or something, I'm not sure if it always ends with a "{".
+ #---------------------------
+ # i.e. we want to delete all lines from "Constant pool:" as long as the first character
+ # is a space.
+ #
+ # If we simply use '/^Constant pool:/,/^[^ ]/d', then it'll delete the "Constant pool:"
+ # line and "{" line too, but again the last line might be important, so we don't want to
+ # delete it.
+ #
+ # So we instead, use '/^Constant pool:/,/^[^ ]/{/^ /d}', which mean:
+ # between lines matching '/^Constant pool:/' and '/^[^ ]/', delete lines that start with
+ # a space. (=='/^ /d').
+ #
sed -e 's/#[0-9][0-9]*/#x/g' \
-e 's/^\( *\)[0-9][0-9]*:/\1x:/' \
- -e '/^Constant pool:/,/^[^ ]/{//!d}' \
+ -e '/^Constant pool:/,/^[^ ]/{/^ /d}' \
-e '/^ *line *[0-9][0-9]*: *[0-9][0-9]*$/d' \
- -e '/SHA-256 checksum/d' \
- -e '/Last modified/d' \
+ -e '/^ *SHA-256 checksum/d' \
+ -e '/^ *Last modified/d' \
-e '/^Classfile jar/d' \
-e '/\[warning\]/d'
else
diff --git a/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/filters/TextFileFilterPolicyParser.kt b/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/filters/TextFileFilterPolicyParser.kt
index be1b6ca93869..c5500831e21a 100644
--- a/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/filters/TextFileFilterPolicyParser.kt
+++ b/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/filters/TextFileFilterPolicyParser.kt
@@ -413,8 +413,8 @@ class TextFileFilterPolicyParser {
}
private fun parseClass(fields: Array<String>) {
- if (fields.size < 3) {
- throw ParseException("Class ('c') expects 2 fields.")
+ if (fields.size <= 1) {
+ throw ParseException("Class ('c') expects 1 or 2 fields.")
}
val className = fields[1]
@@ -424,7 +424,9 @@ class TextFileFilterPolicyParser {
// :aidl, etc?
val classType = resolveSpecialClass(className)
- if (fields[2].startsWith("!")) {
+ val policyStr = if (fields.size > 2) { fields[2] } else { "" }
+
+ if (policyStr.startsWith("!")) {
if (classType != SpecialClass.NotSpecial) {
// We could support it, but not needed at least for now.
throw ParseException(
@@ -432,10 +434,10 @@ class TextFileFilterPolicyParser {
)
}
// It's a redirection class.
- val toClass = fields[2].substring(1)
+ val toClass = policyStr.substring(1)
processor.onRedirectionClass(className, toClass)
- } else if (fields[2].startsWith("~")) {
+ } else if (policyStr.startsWith("~")) {
if (classType != SpecialClass.NotSpecial) {
// We could support it, but not needed at least for now.
throw ParseException(
@@ -443,11 +445,23 @@ class TextFileFilterPolicyParser {
)
}
// It's a class-load hook
- val callback = fields[2].substring(1)
+ val callback = policyStr.substring(1)
processor.onClassLoadHook(className, callback)
} else {
- val policy = parsePolicy(fields[2])
+ // Special case: if it's a class directive with no policy, then it encloses
+ // members, but we don't apply any policy to the class itself.
+ // This is only allowed in a not-special case.
+ if (policyStr == "") {
+ if (classType == SpecialClass.NotSpecial && superClass == null) {
+ currentClassName = className
+ processor.onSimpleClassStart(className)
+ return
+ }
+ throw ParseException("Special class or subclass directive must have a policy")
+ }
+
+ val policy = parsePolicy(policyStr)
if (!policy.isUsableWithClasses) {
throw ParseException("Class can't have policy '$policy'")
}
diff --git a/ravenwood/tools/hoststubgen/test-tiny-framework/diff-and-update-golden.sh b/ravenwood/tools/hoststubgen/test-tiny-framework/diff-and-update-golden.sh
index 8408a18142eb..23699fd1dba4 100755
--- a/ravenwood/tools/hoststubgen/test-tiny-framework/diff-and-update-golden.sh
+++ b/ravenwood/tools/hoststubgen/test-tiny-framework/diff-and-update-golden.sh
@@ -37,8 +37,7 @@ SCRIPT_NAME="${0##*/}"
GOLDEN_DIR=${GOLDEN_DIR:-golden-output}
mkdir -p $GOLDEN_DIR
-# TODO(b/388562869) We shouldn't need `--ignore-matching-lines`, but the golden files may not have the "Constant pool:" lines.
-DIFF_CMD=${DIFF_CMD:-diff -u --ignore-blank-lines --ignore-space-change --ignore-matching-lines='^\(Constant.pool:\|{\)$'}
+DIFF_CMD=${DIFF_CMD:-./tiny-framework-dump-test.py run-diff}
update=0
three_way=0
@@ -63,12 +62,10 @@ done
shift $(($OPTIND - 1))
# Build the dump files, which are the input of this test.
-run ${BUILD_CMD:=m} dump-jar tiny-framework-dump-test
-
+run ${BUILD_CMD:-m} dump-jar tiny-framework-dump-test
# Get the path to the generate text files. (not the golden files.)
# We get them from $OUT/module-info.json
-
files=(
$(python3 -c '
import sys
@@ -78,7 +75,7 @@ import json
with open(sys.argv[1], "r") as f:
data = json.load(f)
- # Equivalent to: jq -r '.["tiny-framework-dump-test"]["installed"][]'
+ # Equivalent to: jq -r '.["tiny-framework-dump-test"]["installed"][]'
for path in data["tiny-framework-dump-test"]["installed"]:
if "golden-output" in path:
diff --git a/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework-dump-test.py b/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework-dump-test.py
index c35d6d106c8d..761748265726 100755
--- a/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework-dump-test.py
+++ b/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework-dump-test.py
@@ -28,9 +28,16 @@ GOLDEN_DIRS = [
# Run diff.
def run_diff(file1, file2):
- # TODO(b/388562869) We shouldn't need `--ignore-matching-lines`, but the golden files may not have the "Constant pool:" lines.
- command = ['diff', '-u', '--ignore-blank-lines',
+ command = ['diff', '-u',
+ '--ignore-blank-lines',
'--ignore-space-change',
+
+ # Ignore the class file version.
+ '--ignore-matching-lines=^ *\(major\|minor\) version:$',
+
+ # We shouldn't need `--ignore-matching-lines`, but somehow
+ # the golden files were generated without these lines for b/388562869,
+ # so let's just ignore them.
'--ignore-matching-lines=^\(Constant.pool:\|{\)$',
file1, file2]
print(' '.join(command))
@@ -85,4 +92,13 @@ class TestWithGoldenOutput(unittest.TestCase):
if __name__ == "__main__":
+ args = sys.argv
+
+ # This script is used by diff-and-update-golden.sh too.
+ if len(args) > 1 and args[1] == "run-diff":
+ if run_diff(args[2], args[3]):
+ sys.exit(0)
+ else:
+ sys.exit(1)
+
unittest.main(verbosity=2)
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
index 8e037c3ba90c..7275881e9661 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
@@ -1841,9 +1841,6 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
public void notifyQuickSettingsTilesChanged(
@UserIdInt int userId, @NonNull List<ComponentName> tileComponentNames) {
notifyQuickSettingsTilesChanged_enforcePermission();
- if (!android.view.accessibility.Flags.a11yQsShortcut()) {
- return;
- }
if (DEBUG) {
Slog.d(LOG_TAG, TextUtils.formatSimple(
"notifyQuickSettingsTilesChanged userId: %d, tileComponentNames: %s",
@@ -2278,9 +2275,6 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
private void restoreShortcutTargets(
String newValue, @UserShortcutType int shortcutType, int userId) {
assertNoTapShortcut(shortcutType);
- if (shortcutType == QUICK_SETTINGS && !android.view.accessibility.Flags.a11yQsShortcut()) {
- return;
- }
synchronized (mLock) {
final AccessibilityUserState userState = getUserStateLocked(userId);
@@ -3894,9 +3888,6 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
*/
private void updateAccessibilityShortcutTargetsLocked(
AccessibilityUserState userState, @UserShortcutType int shortcutType) {
- if (shortcutType == QUICK_SETTINGS && !android.view.accessibility.Flags.a11yQsShortcut()) {
- return;
- }
if (shortcutType == SOFTWARE) {
// Update accessibility button availability.
for (int i = userState.mBoundServices.size() - 1; i >= 0; i--) {
@@ -4079,9 +4070,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
final List<Integer> shortcutTypes = new ArrayList<>(4);
shortcutTypes.add(HARDWARE);
shortcutTypes.add(SOFTWARE);
- if (android.view.accessibility.Flags.a11yQsShortcut()) {
- shortcutTypes.add(QUICK_SETTINGS);
- }
+ shortcutTypes.add(QUICK_SETTINGS);
if (android.provider.Flags.a11yStandaloneGestureEnabled()) {
shortcutTypes.add(GESTURE);
}
@@ -4385,13 +4374,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
private void launchAccessibilityFrameworkFeature(int displayId, ComponentName assignedTarget) {
if (assignedTarget.equals(ACCESSIBILITY_HEARING_AIDS_COMPONENT_NAME)) {
- //import com.android.systemui.Flags;
- if (com.android.systemui.Flags.hearingAidsQsTileDialog()) {
- launchHearingDevicesDialog();
- } else {
- launchAccessibilitySubSettings(displayId,
- ACCESSIBILITY_HEARING_AIDS_COMPONENT_NAME);
- }
+ launchHearingDevicesDialog();
}
}
diff --git a/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java b/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
index 1dba629b9a55..cffdfbd36532 100644
--- a/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
+++ b/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
@@ -5666,13 +5666,6 @@ class AppWidgetServiceImpl extends IAppWidgetService.Stub implements WidgetBacku
}
public boolean canAccessAppWidget(Widget widget, int uid, String packageName) {
- if (isDifferentPackageFromHost(widget.host, packageName)
- && isDifferentPackageFromProvider(widget.provider, packageName)) {
- // Apps providing AppWidget are only allowed to access widgets provided by the
- // same package. Similarly, apps hosting AppWidget are only allowed to access
- // widgets hosted by the same package.
- return false;
- }
if (isHostInPackageForUid(widget.host, uid, packageName)) {
// Apps hosting the AppWidget have access to it.
return true;
@@ -5775,19 +5768,6 @@ class AppWidgetServiceImpl extends IAppWidgetService.Stub implements WidgetBacku
&& provider.id.componentName.getPackageName().equals(packageName);
}
- private boolean isDifferentPackageFromHost(
- @Nullable final Host host, @Nullable final String packageName) {
- return packageName == null || host == null || host.id == null
- || !packageName.equals(host.id.packageName);
- }
-
- private boolean isDifferentPackageFromProvider(
- @Nullable final Provider provider, @Nullable final String packageName) {
- return packageName == null || provider == null || provider.id == null
- || provider.id.componentName == null
- || !packageName.equals(provider.id.componentName.getPackageName());
- }
-
private boolean isProfileEnabled(int profileId) {
final long identity = Binder.clearCallingIdentity();
try {
diff --git a/services/companion/java/com/android/server/companion/virtual/VirtualDeviceImpl.java b/services/companion/java/com/android/server/companion/virtual/VirtualDeviceImpl.java
index 260ea75a1f4c..5edd9d7041ba 100644
--- a/services/companion/java/com/android/server/companion/virtual/VirtualDeviceImpl.java
+++ b/services/companion/java/com/android/server/companion/virtual/VirtualDeviceImpl.java
@@ -21,7 +21,6 @@ import static android.Manifest.permission.ADD_TRUSTED_DISPLAY;
import static android.app.admin.DevicePolicyManager.NEARBY_STREAMING_ENABLED;
import static android.app.admin.DevicePolicyManager.NEARBY_STREAMING_NOT_CONTROLLED_BY_POLICY;
import static android.app.admin.DevicePolicyManager.NEARBY_STREAMING_SAME_MANAGED_ACCOUNT_ONLY;
-import static android.companion.virtual.VirtualDeviceParams.ACTIVITY_POLICY_DEFAULT_ALLOWED;
import static android.companion.virtual.VirtualDeviceParams.DEVICE_POLICY_CUSTOM;
import static android.companion.virtual.VirtualDeviceParams.DEVICE_POLICY_DEFAULT;
import static android.companion.virtual.VirtualDeviceParams.NAVIGATION_POLICY_DEFAULT_ALLOWED;
@@ -492,17 +491,10 @@ final class VirtualDeviceImpl extends IVirtualDevice.Stub
this, getDeviceId(), getPersistentDeviceId(), mParams.getName());
}
- if (Flags.dynamicPolicy()) {
- mActivityPolicyExemptions = new ArraySet<>(
- mParams.getDevicePolicy(POLICY_TYPE_ACTIVITY) == DEVICE_POLICY_DEFAULT
- ? mParams.getBlockedActivities()
- : mParams.getAllowedActivities());
- } else {
- mActivityPolicyExemptions =
- mParams.getDefaultActivityPolicy() == ACTIVITY_POLICY_DEFAULT_ALLOWED
- ? mParams.getBlockedActivities()
- : mParams.getAllowedActivities();
- }
+ mActivityPolicyExemptions = new ArraySet<>(
+ mParams.getDevicePolicy(POLICY_TYPE_ACTIVITY) == DEVICE_POLICY_DEFAULT
+ ? mParams.getBlockedActivities()
+ : mParams.getAllowedActivities());
if (Flags.vdmCustomIme() && mParams.getInputMethodComponent() != null) {
final String imeId = mParams.getInputMethodComponent().flattenToShortString();
@@ -587,12 +579,8 @@ final class VirtualDeviceImpl extends IVirtualDevice.Stub
@Override // Binder call
public @VirtualDeviceParams.DevicePolicy int getDevicePolicy(
@VirtualDeviceParams.PolicyType int policyType) {
- if (Flags.dynamicPolicy()) {
- synchronized (mVirtualDeviceLock) {
- return mDevicePolicies.get(policyType, DEVICE_POLICY_DEFAULT);
- }
- } else {
- return mParams.getDevicePolicy(policyType);
+ synchronized (mVirtualDeviceLock) {
+ return mDevicePolicies.get(policyType, DEVICE_POLICY_DEFAULT);
}
}
@@ -891,9 +879,6 @@ final class VirtualDeviceImpl extends IVirtualDevice.Stub
public void setDevicePolicy(@VirtualDeviceParams.DynamicPolicyType int policyType,
@VirtualDeviceParams.DevicePolicy int devicePolicy) {
checkCallerIsDeviceOwner();
- if (!Flags.dynamicPolicy()) {
- return;
- }
switch (policyType) {
case POLICY_TYPE_RECENTS:
@@ -924,23 +909,21 @@ final class VirtualDeviceImpl extends IVirtualDevice.Stub
}
break;
case POLICY_TYPE_CLIPBOARD:
- if (Flags.crossDeviceClipboard()) {
- if (devicePolicy == DEVICE_POLICY_CUSTOM
+ if (devicePolicy == DEVICE_POLICY_CUSTOM
&& mContext.checkCallingOrSelfPermission(ADD_TRUSTED_DISPLAY)
!= PackageManager.PERMISSION_GRANTED) {
- throw new SecurityException("Requires ADD_TRUSTED_DISPLAY permission to "
- + "set a custom clipboard policy.");
- }
- synchronized (mVirtualDeviceLock) {
- for (int i = 0; i < mVirtualDisplays.size(); i++) {
- VirtualDisplayWrapper wrapper = mVirtualDisplays.valueAt(i);
- if (!wrapper.isTrusted() && !wrapper.isMirror()) {
- throw new SecurityException("All displays must be trusted for "
- + "devices with custom clipboard policy.");
- }
+ throw new SecurityException("Requires ADD_TRUSTED_DISPLAY permission to "
+ + "set a custom clipboard policy.");
+ }
+ synchronized (mVirtualDeviceLock) {
+ for (int i = 0; i < mVirtualDisplays.size(); i++) {
+ VirtualDisplayWrapper wrapper = mVirtualDisplays.valueAt(i);
+ if (!wrapper.isTrusted() && !wrapper.isMirror()) {
+ throw new SecurityException("All displays must be trusted for "
+ + "devices with custom clipboard policy.");
}
- mDevicePolicies.put(policyType, devicePolicy);
}
+ mDevicePolicies.put(policyType, devicePolicy);
}
break;
case POLICY_TYPE_BLOCKED_ACTIVITY:
@@ -1443,15 +1426,11 @@ final class VirtualDeviceImpl extends IVirtualDevice.Stub
private GenericWindowPolicyController createWindowPolicyControllerLocked(
@NonNull Set<String> displayCategories) {
final boolean activityLaunchAllowedByDefault =
- Flags.dynamicPolicy()
- ? getDevicePolicy(POLICY_TYPE_ACTIVITY) == DEVICE_POLICY_DEFAULT
- : mParams.getDefaultActivityPolicy() == ACTIVITY_POLICY_DEFAULT_ALLOWED;
+ getDevicePolicy(POLICY_TYPE_ACTIVITY) == DEVICE_POLICY_DEFAULT;
final boolean crossTaskNavigationAllowedByDefault =
mParams.getDefaultNavigationPolicy() == NAVIGATION_POLICY_DEFAULT_ALLOWED;
final boolean showTasksInHostDeviceRecents =
getDevicePolicy(POLICY_TYPE_RECENTS) == DEVICE_POLICY_DEFAULT;
- final ComponentName homeComponent =
- Flags.vdmCustomHome() ? mParams.getHomeComponent() : null;
if (mActivityListenerAdapter == null) {
mActivityListenerAdapter = new GwpcActivityListener();
@@ -1472,7 +1451,7 @@ final class VirtualDeviceImpl extends IVirtualDevice.Stub
mActivityListenerAdapter,
displayCategories,
showTasksInHostDeviceRecents,
- homeComponent);
+ mParams.getHomeComponent());
gwpc.registerRunningAppsChangedListener(/* listener= */ this);
return gwpc;
}
diff --git a/services/companion/java/com/android/server/companion/virtual/VirtualDeviceManagerService.java b/services/companion/java/com/android/server/companion/virtual/VirtualDeviceManagerService.java
index 40726b4331e2..a60fa693350c 100644
--- a/services/companion/java/com/android/server/companion/virtual/VirtualDeviceManagerService.java
+++ b/services/companion/java/com/android/server/companion/virtual/VirtualDeviceManagerService.java
@@ -216,17 +216,14 @@ public class VirtualDeviceManagerService extends SystemService {
VIRTUAL_DEVICE_SERVICE_ORDERED_ID,
mActivityInterceptorCallback);
- if (Flags.persistentDeviceIdApi()) {
- CompanionDeviceManager cdm =
- getContext().getSystemService(CompanionDeviceManager.class);
- if (cdm != null) {
- onCdmAssociationsChanged(cdm.getAllAssociations(UserHandle.USER_ALL));
- cdm.addOnAssociationsChangedListener(getContext().getMainExecutor(),
- this::onCdmAssociationsChanged, UserHandle.USER_ALL);
- } else {
- Slog.e(TAG, "Failed to find CompanionDeviceManager. No CDM association info "
- + " will be available.");
- }
+ CompanionDeviceManager cdm = getContext().getSystemService(CompanionDeviceManager.class);
+ if (cdm != null) {
+ onCdmAssociationsChanged(cdm.getAllAssociations(UserHandle.USER_ALL));
+ cdm.addOnAssociationsChangedListener(getContext().getMainExecutor(),
+ this::onCdmAssociationsChanged, UserHandle.USER_ALL);
+ } else {
+ Slog.e(TAG, "Failed to find CompanionDeviceManager. No CDM association info "
+ + " will be available.");
}
if (android.companion.virtualdevice.flags.Flags.deviceAwareDisplayPower()) {
mStrongAuthTracker = new StrongAuthTracker(getContext());
@@ -338,14 +335,6 @@ public class VirtualDeviceManagerService extends SystemService {
final long identity = Binder.clearCallingIdentity();
try {
getContext().sendBroadcastAsUser(i, UserHandle.ALL);
-
- if (!Flags.persistentDeviceIdApi()) {
- synchronized (mVirtualDeviceManagerLock) {
- if (mVirtualDevices.size() == 0) {
- unregisterCdmAssociationListener();
- }
- }
- }
} finally {
Binder.restoreCallingIdentity(identity);
}
@@ -379,21 +368,6 @@ public class VirtualDeviceManagerService extends SystemService {
}
}
- @RequiresPermission(android.Manifest.permission.MANAGE_COMPANION_DEVICES)
- private void registerCdmAssociationListener() {
- final CompanionDeviceManager cdm = getContext().getSystemService(
- CompanionDeviceManager.class);
- cdm.addOnAssociationsChangedListener(getContext().getMainExecutor(),
- mCdmAssociationListener);
- }
-
- @RequiresPermission(android.Manifest.permission.MANAGE_COMPANION_DEVICES)
- private void unregisterCdmAssociationListener() {
- final CompanionDeviceManager cdm = getContext().getSystemService(
- CompanionDeviceManager.class);
- cdm.removeOnAssociationsChangedListener(mCdmAssociationListener);
- }
-
void onCdmAssociationsChanged(List<AssociationInfo> associations) {
ArrayMap<String, AssociationInfo> vdmAssociations = new ArrayMap<>();
for (int i = 0; i < associations.size(); ++i) {
@@ -479,9 +453,8 @@ public class VirtualDeviceManagerService extends SystemService {
AssociationInfo associationInfo = getAssociationInfo(packageName, associationId);
if (associationInfo == null) {
throw new IllegalArgumentException("No association with ID " + associationId);
- } else if (!VIRTUAL_DEVICE_COMPANION_DEVICE_PROFILES
- .contains(associationInfo.getDeviceProfile())
- && Flags.persistentDeviceIdApi()) {
+ } else if (!VIRTUAL_DEVICE_COMPANION_DEVICE_PROFILES.contains(
+ associationInfo.getDeviceProfile())) {
throw new IllegalArgumentException("Unsupported CDM Association device profile "
+ associationInfo.getDeviceProfile() + " for virtual device creation.");
}
@@ -522,14 +495,6 @@ public class VirtualDeviceManagerService extends SystemService {
Counter.logIncrement("virtual_devices.value_virtual_devices_created_count");
synchronized (mVirtualDeviceManagerLock) {
- if (!Flags.persistentDeviceIdApi() && mVirtualDevices.size() == 0) {
- final long callingId = Binder.clearCallingIdentity();
- try {
- registerCdmAssociationListener();
- } finally {
- Binder.restoreCallingIdentity(callingId);
- }
- }
mVirtualDevices.put(deviceId, virtualDevice);
}
diff --git a/services/core/java/com/android/server/am/ActivityManagerConstants.java b/services/core/java/com/android/server/am/ActivityManagerConstants.java
index 4b8770b3cd35..50876dafa819 100644
--- a/services/core/java/com/android/server/am/ActivityManagerConstants.java
+++ b/services/core/java/com/android/server/am/ActivityManagerConstants.java
@@ -277,7 +277,9 @@ final class ActivityManagerConstants extends ContentObserver {
private static final long DEFAULT_FOLLOW_UP_OOMADJ_UPDATE_WAIT_DURATION = 1000L;
/** The default value to {@link #KEY_FREEZER_CUTOFF_ADJ} */
- private static final int DEFAULT_FREEZER_CUTOFF_ADJ = ProcessList.CACHED_APP_MIN_ADJ;
+ private static final int DEFAULT_FREEZER_CUTOFF_ADJ =
+ Flags.prototypeAggressiveFreezing() ? ProcessList.HOME_APP_ADJ
+ : ProcessList.CACHED_APP_MIN_ADJ;
/**
* Same as {@link TEMPORARY_ALLOW_LIST_TYPE_FOREGROUND_SERVICE_NOT_ALLOWED}
diff --git a/services/core/java/com/android/server/am/flags.aconfig b/services/core/java/com/android/server/am/flags.aconfig
index cfcede8ee40d..d9be47193c89 100644
--- a/services/core/java/com/android/server/am/flags.aconfig
+++ b/services/core/java/com/android/server/am/flags.aconfig
@@ -270,6 +270,16 @@ flag {
}
flag {
+ name: "prototype_aggressive_freezing"
+ namespace: "backstage_power"
+ description: "Grant PROCESS_CAPABILITY_CPU_TIME to as many valid states and aggressively freeze other states"
+ bug: "370798593"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
+
+flag {
name: "add_modify_raw_oom_adj_service_level"
namespace: "backstage_power"
description: "Add a SERVICE_ADJ level to the modifyRawOomAdj method"
diff --git a/services/core/java/com/android/server/appop/DiscreteOpsDbHelper.java b/services/core/java/com/android/server/appop/DiscreteOpsDbHelper.java
index e4c36cc214e8..695032e6476c 100644
--- a/services/core/java/com/android/server/appop/DiscreteOpsDbHelper.java
+++ b/services/core/java/com/android/server/appop/DiscreteOpsDbHelper.java
@@ -110,7 +110,12 @@ class DiscreteOpsDbHelper extends SQLiteOpenHelper {
}
db.setTransactionSuccessful();
} finally {
- db.endTransaction();
+ try {
+ db.endTransaction();
+ } catch (SQLiteException exception) {
+ Slog.e(LOG_TAG, "Couldn't commit transaction when inserting discrete ops, database"
+ + " file size (bytes) : " + getDatabaseFile().length(), exception);
+ }
}
}
diff --git a/services/core/java/com/android/server/display/feature/DisplayManagerFlags.java b/services/core/java/com/android/server/display/feature/DisplayManagerFlags.java
index 4e57d6791ff6..43aa6f46da78 100644
--- a/services/core/java/com/android/server/display/feature/DisplayManagerFlags.java
+++ b/services/core/java/com/android/server/display/feature/DisplayManagerFlags.java
@@ -258,6 +258,11 @@ public class DisplayManagerFlags {
Flags::subscribeGranularDisplayEvents
);
+ private final FlagState mBaseDensityForExternalDisplays = new FlagState(
+ Flags.FLAG_BASE_DENSITY_FOR_EXTERNAL_DISPLAYS,
+ Flags::baseDensityForExternalDisplays
+ );
+
/**
* @return {@code true} if 'port' is allowed in display layout configuration file.
*/
@@ -553,6 +558,14 @@ public class DisplayManagerFlags {
}
/**
+ * @return {@code true} if the flag for base density for external displays is enabled
+ */
+ public boolean isBaseDensityForExternalDisplaysEnabled() {
+ return mBaseDensityForExternalDisplays.isEnabled();
+ }
+
+
+ /**
* dumps all flagstates
* @param pw printWriter
*/
@@ -606,6 +619,7 @@ public class DisplayManagerFlags {
pw.println(" " + mDisplayListenerPerformanceImprovementsFlagState);
pw.println(" " + mSubscribeGranularDisplayEvents);
pw.println(" " + mEnableDisplayContentModeManagementFlagState);
+ pw.println(" " + mBaseDensityForExternalDisplays);
}
private static class FlagState {
diff --git a/services/core/java/com/android/server/display/feature/display_flags.aconfig b/services/core/java/com/android/server/display/feature/display_flags.aconfig
index afae07c88f8d..00a9dcb71b4b 100644
--- a/services/core/java/com/android/server/display/feature/display_flags.aconfig
+++ b/services/core/java/com/android/server/display/feature/display_flags.aconfig
@@ -471,3 +471,11 @@ flag {
purpose: PURPOSE_BUGFIX
}
}
+
+flag {
+ name: "base_density_for_external_displays"
+ namespace: "lse_desktop_experience"
+ description: "Feature flag for setting a base density for external displays."
+ bug: "382954433"
+ is_fixed_read_only: true
+}
diff --git a/services/core/java/com/android/server/hdmi/PowerStatusMonitorActionFromPlayback.java b/services/core/java/com/android/server/hdmi/PowerStatusMonitorActionFromPlayback.java
index d05ded5367d0..a2465d1c3a5d 100644
--- a/services/core/java/com/android/server/hdmi/PowerStatusMonitorActionFromPlayback.java
+++ b/services/core/java/com/android/server/hdmi/PowerStatusMonitorActionFromPlayback.java
@@ -28,21 +28,17 @@ import com.android.internal.annotations.VisibleForTesting;
*/
public class PowerStatusMonitorActionFromPlayback extends HdmiCecFeatureAction {
private static final String TAG = "PowerStatusMonitorActionFromPlayback";
+ // State that waits for next monitoring.
+ private static final int STATE_WAIT_FOR_NEXT_MONITORING = 1;
// State that waits for <Report Power Status> once sending <Give Device Power Status>
- // to all external devices.
- private static final int STATE_WAIT_FOR_REPORT_POWER_STATUS = 1;
- // State that waits for next monitoring.
- private static final int STATE_WAIT_FOR_NEXT_MONITORING = 2;
+ // to the TV.
+ private static final int STATE_WAIT_FOR_REPORT_POWER_STATUS = 2;
+
// Monitoring interval (60s)
@VisibleForTesting
protected static final int MONITORING_INTERVAL_MS = 60000;
// Timeout once sending <Give Device Power Status>
- private static final int REPORT_POWER_STATUS_TIMEOUT_MS = 5000;
- // Maximum number of retries in case the <Give Device Power Status> failed being sent or times
- // out.
- private static final int GIVE_POWER_STATUS_FOR_SOURCE_RETRIES = 5;
- private int mPowerStatusRetries = 0;
PowerStatusMonitorActionFromPlayback(HdmiCecLocalDevice source) {
super(source);
@@ -68,11 +64,10 @@ public class PowerStatusMonitorActionFromPlayback extends HdmiCecFeatureAction {
private boolean handleReportPowerStatusFromTv(HdmiCecMessage cmd) {
int powerStatus = cmd.getParams()[0] & 0xFF;
- mState = STATE_WAIT_FOR_NEXT_MONITORING;
- addTimer(mState, MONITORING_INTERVAL_MS);
if (powerStatus == POWER_STATUS_STANDBY) {
Slog.d(TAG, "TV reported it turned off, going to sleep.");
source().getService().standby();
+ finish();
return true;
}
return false;
@@ -80,34 +75,28 @@ public class PowerStatusMonitorActionFromPlayback extends HdmiCecFeatureAction {
@Override
void handleTimerEvent(int state) {
+ if (mState != state) {
+ return;
+ }
+
switch (mState) {
case STATE_WAIT_FOR_NEXT_MONITORING:
- mPowerStatusRetries = 0;
queryPowerStatus();
break;
case STATE_WAIT_FOR_REPORT_POWER_STATUS:
- handleTimeout();
+ mState = STATE_WAIT_FOR_NEXT_MONITORING;
+ addTimer(mState, MONITORING_INTERVAL_MS);
+ break;
+ default:
break;
}
}
private void queryPowerStatus() {
sendCommand(HdmiCecMessageBuilder.buildGiveDevicePowerStatus(getSourceAddress(),
- Constants.ADDR_TV));
+ Constants.ADDR_TV));
mState = STATE_WAIT_FOR_REPORT_POWER_STATUS;
- addTimer(mState, REPORT_POWER_STATUS_TIMEOUT_MS);
- }
-
- private void handleTimeout() {
- if (mState == STATE_WAIT_FOR_REPORT_POWER_STATUS) {
- if (mPowerStatusRetries++ < GIVE_POWER_STATUS_FOR_SOURCE_RETRIES) {
- queryPowerStatus();
- } else {
- mPowerStatusRetries = 0;
- mState = STATE_WAIT_FOR_NEXT_MONITORING;
- addTimer(mState, MONITORING_INTERVAL_MS);
- }
- }
+ addTimer(mState, HdmiConfig.TIMEOUT_MS);
}
}
diff --git a/services/core/java/com/android/server/inputmethod/ImeVisibilityStateComputer.java b/services/core/java/com/android/server/inputmethod/ImeVisibilityStateComputer.java
index 281db0ae9518..5fe8318dbb3f 100644
--- a/services/core/java/com/android/server/inputmethod/ImeVisibilityStateComputer.java
+++ b/services/core/java/com/android/server/inputmethod/ImeVisibilityStateComputer.java
@@ -40,6 +40,7 @@ import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.UserIdInt;
+import android.content.pm.PackageManager;
import android.content.res.Configuration;
import android.os.Binder;
import android.os.IBinder;
@@ -58,6 +59,7 @@ import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.inputmethod.SoftInputShowHideReason;
import com.android.server.LocalServices;
+import com.android.server.pm.UserManagerInternal;
import com.android.server.wm.WindowManagerInternal;
import java.io.PrintWriter;
@@ -78,6 +80,7 @@ public final class ImeVisibilityStateComputer {
private final int mUserId;
private final InputMethodManagerService mService;
+ private final UserManagerInternal mUserManagerInternal;
private final WindowManagerInternal mWindowManagerInternal;
final InputMethodManagerService.ImeDisplayValidator mImeDisplayValidator;
@@ -188,6 +191,7 @@ public final class ImeVisibilityStateComputer {
public ImeVisibilityStateComputer(@NonNull InputMethodManagerService service,
@UserIdInt int userId) {
this(service,
+ LocalServices.getService(UserManagerInternal.class),
LocalServices.getService(WindowManagerInternal.class),
LocalServices.getService(WindowManagerInternal.class)::getDisplayImePolicy,
new ImeVisibilityPolicy(), userId);
@@ -196,12 +200,15 @@ public final class ImeVisibilityStateComputer {
@VisibleForTesting
public ImeVisibilityStateComputer(@NonNull InputMethodManagerService service,
@NonNull Injector injector) {
- this(service, injector.getWmService(), injector.getImeValidator(),
- new ImeVisibilityPolicy(), injector.getUserId());
+ this(service, injector.getUserManagerService(), injector.getWmService(),
+ injector.getImeValidator(), new ImeVisibilityPolicy(), injector.getUserId());
}
interface Injector {
@NonNull
+ UserManagerInternal getUserManagerService();
+
+ @NonNull
WindowManagerInternal getWmService();
@NonNull
@@ -212,11 +219,13 @@ public final class ImeVisibilityStateComputer {
}
private ImeVisibilityStateComputer(InputMethodManagerService service,
+ UserManagerInternal userManagerInternal,
WindowManagerInternal wmService,
InputMethodManagerService.ImeDisplayValidator imeDisplayValidator,
ImeVisibilityPolicy imePolicy, @UserIdInt int userId) {
mUserId = userId;
mService = service;
+ mUserManagerInternal = userManagerInternal;
mWindowManagerInternal = wmService;
mImeDisplayValidator = imeDisplayValidator;
mPolicy = imePolicy;
@@ -337,7 +346,16 @@ public final class ImeVisibilityStateComputer {
@GuardedBy("ImfLock.class")
int computeImeDisplayId(@NonNull ImeTargetWindowState state, int displayId) {
- final int displayToShowIme = computeImeDisplayIdForTarget(displayId, mImeDisplayValidator);
+ final int displayToShowIme;
+ final PackageManager pm = mService.mContext.getPackageManager();
+ if (pm.hasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE)
+ && mUserManagerInternal.isVisibleBackgroundFullUser(mUserId)
+ && Flags.fallbackDisplayForSecondaryUserOnSecondaryDisplay()) {
+ displayToShowIme = mService.computeImeDisplayIdForVisibleBackgroundUserOnAutomotive(
+ displayId, mUserId, mImeDisplayValidator);
+ } else {
+ displayToShowIme = computeImeDisplayIdForTarget(displayId, mImeDisplayValidator);
+ }
state.setImeDisplayId(displayToShowIme);
final boolean imeHiddenByPolicy = displayToShowIme == INVALID_DISPLAY;
mPolicy.setImeHiddenByDisplayPolicy(imeHiddenByPolicy);
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
index 1f414ac07ba3..45c7cffd462b 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
@@ -2345,8 +2345,32 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl.
* {@link WindowManager#DISPLAY_IME_POLICY_HIDE}
*/
static int computeImeDisplayIdForTarget(int displayId, @NonNull ImeDisplayValidator checker) {
- if (displayId == DEFAULT_DISPLAY || displayId == INVALID_DISPLAY) {
- return FALLBACK_DISPLAY_ID;
+ return computeImeDisplayIdForTargetInner(displayId, checker, FALLBACK_DISPLAY_ID);
+ }
+
+ /**
+ * Find the display where the IME should be shown for a visible background user.
+ *
+ * @param displayId the ID of the display where the IME client target is
+ * @param userId the ID of the user who own the IME
+ * @param checker instance of {@link ImeDisplayValidator} which is used for
+ * checking display config to adjust the final target display
+ * @return the ID of the display where the IME should be shown or
+ * {@link android.view.Display#INVALID_DISPLAY} if the display has an ImePolicy of
+ * {@link WindowManager#DISPLAY_IME_POLICY_HIDE}
+ */
+ int computeImeDisplayIdForVisibleBackgroundUserOnAutomotive(
+ int displayId, @UserIdInt int userId, @NonNull ImeDisplayValidator checker) {
+ // Visible background user can be assigned to a secondary display, not the default display.
+ // The main display assigned to the user will be used as the fallback display.
+ final int mainDisplayId = mUserManagerInternal.getMainDisplayAssignedToUser(userId);
+ return computeImeDisplayIdForTargetInner(displayId, checker, mainDisplayId);
+ }
+
+ private static int computeImeDisplayIdForTargetInner(
+ int displayId, @NonNull ImeDisplayValidator checker, int fallbackDisplayId) {
+ if (displayId == fallbackDisplayId || displayId == INVALID_DISPLAY) {
+ return fallbackDisplayId;
}
// Show IME window on fallback display when the display doesn't support system decorations
@@ -2356,9 +2380,8 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl.
return displayId;
} else if (result == DISPLAY_IME_POLICY_HIDE) {
return INVALID_DISPLAY;
- } else {
- return FALLBACK_DISPLAY_ID;
}
+ return fallbackDisplayId;
}
@GuardedBy("ImfLock.class")
diff --git a/services/core/java/com/android/server/location/contexthub/ContextHubServiceUtil.java b/services/core/java/com/android/server/location/contexthub/ContextHubServiceUtil.java
index a41194b898ac..1949d103a0d6 100644
--- a/services/core/java/com/android/server/location/contexthub/ContextHubServiceUtil.java
+++ b/services/core/java/com/android/server/location/contexthub/ContextHubServiceUtil.java
@@ -499,9 +499,11 @@ import java.util.List;
/* package */
static HubMessage createHubMessage(Message message) {
boolean isReliable = (message.flags & Message.FLAG_REQUIRES_DELIVERY_STATUS) != 0;
- return new HubMessage.Builder(message.type, message.content)
+ HubMessage outMessage = new HubMessage.Builder(message.type, message.content)
.setResponseRequired(isReliable)
.build();
+ outMessage.setMessageSequenceNumber(message.sequenceNumber);
+ return outMessage;
}
/**
diff --git a/services/core/java/com/android/server/location/fudger/LocationFudger.java b/services/core/java/com/android/server/location/fudger/LocationFudger.java
index 28e21b71dcc9..39ee0cbeacce 100644
--- a/services/core/java/com/android/server/location/fudger/LocationFudger.java
+++ b/services/core/java/com/android/server/location/fudger/LocationFudger.java
@@ -68,6 +68,17 @@ public class LocationFudger {
private static final double MAX_LATITUDE =
90.0 - (1.0 / APPROXIMATE_METERS_PER_DEGREE_AT_EQUATOR);
+ // The average edge length in km of an S2 cell, indexed by S2 levels 0 to
+ // 13. Level 13 is the highest level used for coarsening.
+ // This approximation assumes the S2 cells are squares.
+ // For density-based coarsening, we use the edge to set the accuracy of the
+ // coarsened location.
+ // The values are from http://s2geometry.io/resources/s2cell_statistics.html
+ // We take square root of the average area.
+ private static final float[] S2_CELL_AVG_EDGE_PER_LEVEL = new float[] {
+ 9220.14f, 4610.07f, 2305.04f, 1152.52f, 576.26f, 288.13f, 144.06f,
+ 72.03f, 36.02f, 20.79f, 9f, 5.05f, 2.25f, 1.13f, 0.57f};
+
private final float mAccuracyM;
private final Clock mClock;
private final Random mRandom;
@@ -194,12 +205,14 @@ public class LocationFudger {
// The new algorithm is applied if and only if (1) the flag is on, (2) the cache has been
// set, and (3) the cache has successfully queried the provider for the default coarsening
// value.
+ float accuracy = mAccuracyM;
if (Flags.populationDensityProvider() && Flags.densityBasedCoarseLocations()
&& cacheCopy != null) {
if (cacheCopy.hasDefaultValue()) {
// New algorithm that snaps to the center of a S2 cell.
int level = cacheCopy.getCoarseningLevel(latitude, longitude);
coarsened = snapToCenterOfS2Cell(latitude, longitude, level);
+ accuracy = getS2CellApproximateEdge(level);
} else {
// Try to fetch the default value. The answer won't come in time, but will be used
// for the next location to coarsen.
@@ -214,7 +227,7 @@ public class LocationFudger {
coarse.setLatitude(coarsened[LAT_INDEX]);
coarse.setLongitude(coarsened[LNG_INDEX]);
- coarse.setAccuracy(Math.max(mAccuracyM, coarse.getAccuracy()));
+ coarse.setAccuracy(Math.max(accuracy, coarse.getAccuracy()));
synchronized (this) {
mCachedFineLocation = fine;
@@ -224,6 +237,19 @@ public class LocationFudger {
return coarse;
}
+ // Returns the average edge length in meters of an S2 cell at the given
+ // level. This is computed as if the S2 cell were a square. We do not need
+ // an exact value, only a rough approximation.
+ @VisibleForTesting
+ protected float getS2CellApproximateEdge(int level) {
+ if (level < 0) {
+ level = 0;
+ } else if (level >= S2_CELL_AVG_EDGE_PER_LEVEL.length) {
+ level = S2_CELL_AVG_EDGE_PER_LEVEL.length - 1;
+ }
+ return S2_CELL_AVG_EDGE_PER_LEVEL[level] * 1000;
+ }
+
// quantize location by snapping to a grid. this is the primary means of obfuscation. it
// gives nice consistent results and is very effective at hiding the true location (as
// long as you are not sitting on a grid boundary, which the random offsets mitigate).
diff --git a/services/core/java/com/android/server/locksettings/LockSettingsService.java b/services/core/java/com/android/server/locksettings/LockSettingsService.java
index 0d0cdd83cc73..a0e543300ce7 100644
--- a/services/core/java/com/android/server/locksettings/LockSettingsService.java
+++ b/services/core/java/com/android/server/locksettings/LockSettingsService.java
@@ -137,6 +137,7 @@ import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.messages.nano.SystemMessageProto.SystemMessage;
import com.android.internal.notification.SystemNotificationChannels;
+import com.android.internal.pm.RoSystemFeatures;
import com.android.internal.util.ArrayUtils;
import com.android.internal.util.DumpUtils;
import com.android.internal.util.IndentingPrintWriter;
@@ -1325,7 +1326,7 @@ public class LockSettingsService extends ILockSettings.Stub {
mContext.enforceCallingOrSelfPermission(
Manifest.permission.MANAGE_WEAK_ESCROW_TOKEN,
"Requires MANAGE_WEAK_ESCROW_TOKEN permission.");
- if (!mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE)) {
+ if (!RoSystemFeatures.hasFeatureAutomotive(mContext)) {
throw new IllegalArgumentException(
"Weak escrow token are only for automotive devices.");
}
@@ -3613,7 +3614,7 @@ public class LockSettingsService extends ILockSettings.Stub {
}
// Escrow tokens are enabled on automotive builds.
- if (mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE)) {
+ if (RoSystemFeatures.hasFeatureAutomotive(mContext)) {
return;
}
diff --git a/services/core/java/com/android/server/media/quality/MediaQualityService.java b/services/core/java/com/android/server/media/quality/MediaQualityService.java
index 64bf961f82cc..efc1b9959c0f 100644
--- a/services/core/java/com/android/server/media/quality/MediaQualityService.java
+++ b/services/core/java/com/android/server/media/quality/MediaQualityService.java
@@ -110,7 +110,7 @@ public class MediaQualityService extends SystemService {
if ((pp.getPackageName() != null && !pp.getPackageName().isEmpty()
&& !incomingPackageEqualsCallingUidPackage(pp.getPackageName()))
&& !hasGlobalPictureQualityServicePermission()) {
- notifyPictureProfileError(null, PictureProfile.ERROR_NO_PERMISSION,
+ notifyOnPictureProfileError(null, PictureProfile.ERROR_NO_PERMISSION,
Binder.getCallingUid(), Binder.getCallingPid());
}
@@ -128,7 +128,9 @@ public class MediaQualityService extends SystemService {
Long id = db.insert(mMediaQualityDbHelper.PICTURE_QUALITY_TABLE_NAME,
null, values);
populateTempIdMap(mPictureProfileTempIdMap, id);
- pp.setProfileId(mPictureProfileTempIdMap.getValue(id));
+ String value = mPictureProfileTempIdMap.getValue(id);
+ pp.setProfileId(value);
+ notifyOnPictureProfileAdded(value, pp, Binder.getCallingUid(), Binder.getCallingPid());
return pp;
}
@@ -136,7 +138,7 @@ public class MediaQualityService extends SystemService {
public void updatePictureProfile(String id, PictureProfile pp, UserHandle user) {
Long dbId = mPictureProfileTempIdMap.getKey(id);
if (!hasPermissionToUpdatePictureProfile(dbId, pp)) {
- notifyPictureProfileError(id, PictureProfile.ERROR_NO_PERMISSION,
+ notifyOnPictureProfileError(id, PictureProfile.ERROR_NO_PERMISSION,
Binder.getCallingUid(), Binder.getCallingPid());
}
@@ -150,6 +152,8 @@ public class MediaQualityService extends SystemService {
SQLiteDatabase db = mMediaQualityDbHelper.getWritableDatabase();
db.replace(mMediaQualityDbHelper.PICTURE_QUALITY_TABLE_NAME,
null, values);
+ notifyOnPictureProfileUpdated(mPictureProfileTempIdMap.getValue(dbId),
+ getPictureProfile(dbId), Binder.getCallingUid(), Binder.getCallingPid());
}
private boolean hasPermissionToUpdatePictureProfile(Long dbId, PictureProfile toUpdate) {
@@ -164,8 +168,9 @@ public class MediaQualityService extends SystemService {
public void removePictureProfile(String id, UserHandle user) {
Long dbId = mPictureProfileTempIdMap.getKey(id);
- if (!hasPermissionToRemovePictureProfile(dbId)) {
- notifyPictureProfileError(id, PictureProfile.ERROR_NO_PERMISSION,
+ PictureProfile toDelete = getPictureProfile(dbId);
+ if (!hasPermissionToRemovePictureProfile(toDelete)) {
+ notifyOnPictureProfileError(id, PictureProfile.ERROR_NO_PERMISSION,
Binder.getCallingUid(), Binder.getCallingPid());
}
@@ -176,16 +181,20 @@ public class MediaQualityService extends SystemService {
int result = db.delete(mMediaQualityDbHelper.PICTURE_QUALITY_TABLE_NAME, selection,
selectionArgs);
if (result == 0) {
- notifyPictureProfileError(id, PictureProfile.ERROR_INVALID_ARGUMENT,
+ notifyOnPictureProfileError(id, PictureProfile.ERROR_INVALID_ARGUMENT,
Binder.getCallingUid(), Binder.getCallingPid());
}
+ notifyOnPictureProfileRemoved(mPictureProfileTempIdMap.getValue(dbId), toDelete,
+ Binder.getCallingUid(), Binder.getCallingPid());
mPictureProfileTempIdMap.remove(dbId);
}
}
- private boolean hasPermissionToRemovePictureProfile(Long dbId) {
- PictureProfile fromDb = getPictureProfile(dbId);
- return fromDb.getName().equalsIgnoreCase(getPackageOfCallingUid());
+ private boolean hasPermissionToRemovePictureProfile(PictureProfile toDelete) {
+ if (toDelete != null) {
+ return toDelete.getName().equalsIgnoreCase(getPackageOfCallingUid());
+ }
+ return false;
}
@Override
@@ -246,7 +255,7 @@ public class MediaQualityService extends SystemService {
public List<PictureProfile> getPictureProfilesByPackage(
String packageName, Bundle options, UserHandle user) {
if (!hasGlobalPictureQualityServicePermission()) {
- notifyPictureProfileError(null, PictureProfile.ERROR_NO_PERMISSION,
+ notifyOnPictureProfileError(null, PictureProfile.ERROR_NO_PERMISSION,
Binder.getCallingUid(), Binder.getCallingPid());
}
@@ -270,7 +279,7 @@ public class MediaQualityService extends SystemService {
@Override
public boolean setDefaultPictureProfile(String profileId, UserHandle user) {
if (!hasGlobalPictureQualityServicePermission()) {
- notifyPictureProfileError(profileId, PictureProfile.ERROR_NO_PERMISSION,
+ notifyOnPictureProfileError(profileId, PictureProfile.ERROR_NO_PERMISSION,
Binder.getCallingUid(), Binder.getCallingPid());
}
// TODO: pass the profile ID to MediaQuality HAL when ready.
@@ -280,7 +289,7 @@ public class MediaQualityService extends SystemService {
@Override
public List<String> getPictureProfilePackageNames(UserHandle user) {
if (!hasGlobalPictureQualityServicePermission()) {
- notifyPictureProfileError(null, PictureProfile.ERROR_NO_PERMISSION,
+ notifyOnPictureProfileError(null, PictureProfile.ERROR_NO_PERMISSION,
Binder.getCallingUid(), Binder.getCallingPid());
}
String [] column = {BaseParameters.PARAMETER_PACKAGE};
@@ -293,13 +302,31 @@ public class MediaQualityService extends SystemService {
}
@Override
- public List<PictureProfileHandle> getPictureProfileHandle(String[] id, UserHandle user) {
- return new ArrayList<>();
+ public List<PictureProfileHandle> getPictureProfileHandle(String[] ids, UserHandle user) {
+ List<PictureProfileHandle> toReturn = new ArrayList<>();
+ for (String id : ids) {
+ Long key = mPictureProfileTempIdMap.getKey(id);
+ if (key != null) {
+ toReturn.add(new PictureProfileHandle(key));
+ } else {
+ toReturn.add(null);
+ }
+ }
+ return toReturn;
}
@Override
- public List<SoundProfileHandle> getSoundProfileHandle(String[] id, UserHandle user) {
- return new ArrayList<>();
+ public List<SoundProfileHandle> getSoundProfileHandle(String[] ids, UserHandle user) {
+ List<SoundProfileHandle> toReturn = new ArrayList<>();
+ for (String id : ids) {
+ Long key = mSoundProfileTempIdMap.getKey(id);
+ if (key != null) {
+ toReturn.add(new SoundProfileHandle(key));
+ } else {
+ toReturn.add(null);
+ }
+ }
+ return toReturn;
}
@Override
@@ -307,7 +334,7 @@ public class MediaQualityService extends SystemService {
if ((sp.getPackageName() != null && !sp.getPackageName().isEmpty()
&& !incomingPackageEqualsCallingUidPackage(sp.getPackageName()))
&& !hasGlobalPictureQualityServicePermission()) {
- notifySoundProfileError(null, SoundProfile.ERROR_NO_PERMISSION,
+ notifyOnSoundProfileError(null, SoundProfile.ERROR_NO_PERMISSION,
Binder.getCallingUid(), Binder.getCallingPid());
}
SQLiteDatabase db = mMediaQualityDbHelper.getWritableDatabase();
@@ -324,7 +351,9 @@ public class MediaQualityService extends SystemService {
Long id = db.insert(mMediaQualityDbHelper.SOUND_QUALITY_TABLE_NAME,
null, values);
populateTempIdMap(mSoundProfileTempIdMap, id);
- sp.setProfileId(mSoundProfileTempIdMap.getValue(id));
+ String value = mSoundProfileTempIdMap.getValue(id);
+ sp.setProfileId(value);
+ notifyOnSoundProfileAdded(value, sp, Binder.getCallingUid(), Binder.getCallingPid());
return sp;
}
@@ -332,7 +361,7 @@ public class MediaQualityService extends SystemService {
public void updateSoundProfile(String id, SoundProfile sp, UserHandle user) {
Long dbId = mSoundProfileTempIdMap.getKey(id);
if (!hasPermissionToUpdateSoundProfile(dbId, sp)) {
- notifySoundProfileError(id, SoundProfile.ERROR_NO_PERMISSION,
+ notifyOnSoundProfileError(id, SoundProfile.ERROR_NO_PERMISSION,
Binder.getCallingUid(), Binder.getCallingPid());
}
@@ -345,6 +374,8 @@ public class MediaQualityService extends SystemService {
SQLiteDatabase db = mMediaQualityDbHelper.getWritableDatabase();
db.replace(mMediaQualityDbHelper.SOUND_QUALITY_TABLE_NAME, null, values);
+ notifyOnSoundProfileUpdated(mSoundProfileTempIdMap.getValue(dbId),
+ getSoundProfile(dbId), Binder.getCallingUid(), Binder.getCallingPid());
}
private boolean hasPermissionToUpdateSoundProfile(Long dbId, SoundProfile sp) {
@@ -358,8 +389,9 @@ public class MediaQualityService extends SystemService {
@Override
public void removeSoundProfile(String id, UserHandle user) {
Long dbId = mSoundProfileTempIdMap.getKey(id);
- if (!hasPermissionToRemoveSoundProfile(dbId)) {
- notifySoundProfileError(id, SoundProfile.ERROR_NO_PERMISSION,
+ SoundProfile toDelete = getSoundProfile(dbId);
+ if (!hasPermissionToRemoveSoundProfile(toDelete)) {
+ notifyOnSoundProfileError(id, SoundProfile.ERROR_NO_PERMISSION,
Binder.getCallingUid(), Binder.getCallingPid());
}
@@ -370,16 +402,20 @@ public class MediaQualityService extends SystemService {
int result = db.delete(mMediaQualityDbHelper.SOUND_QUALITY_TABLE_NAME, selection,
selectionArgs);
if (result == 0) {
- notifySoundProfileError(id, SoundProfile.ERROR_INVALID_ARGUMENT,
+ notifyOnSoundProfileError(id, SoundProfile.ERROR_INVALID_ARGUMENT,
Binder.getCallingUid(), Binder.getCallingPid());
}
+ notifyOnSoundProfileRemoved(mSoundProfileTempIdMap.getValue(dbId), toDelete,
+ Binder.getCallingUid(), Binder.getCallingPid());
mSoundProfileTempIdMap.remove(dbId);
}
}
- private boolean hasPermissionToRemoveSoundProfile(Long dbId) {
- SoundProfile fromDb = getSoundProfile(dbId);
- return fromDb.getName().equalsIgnoreCase(getPackageOfCallingUid());
+ private boolean hasPermissionToRemoveSoundProfile(SoundProfile toDelete) {
+ if (toDelete != null) {
+ return toDelete.getName().equalsIgnoreCase(getPackageOfCallingUid());
+ }
+ return false;
}
@Override
@@ -440,7 +476,7 @@ public class MediaQualityService extends SystemService {
public List<SoundProfile> getSoundProfilesByPackage(
String packageName, Bundle options, UserHandle user) {
if (!hasGlobalSoundQualityServicePermission()) {
- notifySoundProfileError(null, SoundProfile.ERROR_NO_PERMISSION,
+ notifyOnSoundProfileError(null, SoundProfile.ERROR_NO_PERMISSION,
Binder.getCallingUid(), Binder.getCallingPid());
}
@@ -464,7 +500,7 @@ public class MediaQualityService extends SystemService {
@Override
public boolean setDefaultSoundProfile(String profileId, UserHandle user) {
if (!hasGlobalSoundQualityServicePermission()) {
- notifySoundProfileError(profileId, SoundProfile.ERROR_NO_PERMISSION,
+ notifyOnSoundProfileError(profileId, SoundProfile.ERROR_NO_PERMISSION,
Binder.getCallingUid(), Binder.getCallingPid());
}
// TODO: pass the profile ID to MediaQuality HAL when ready.
@@ -474,7 +510,7 @@ public class MediaQualityService extends SystemService {
@Override
public List<String> getSoundProfilePackageNames(UserHandle user) {
if (!hasGlobalSoundQualityServicePermission()) {
- notifySoundProfileError(null, SoundProfile.ERROR_NO_PERMISSION,
+ notifyOnSoundProfileError(null, SoundProfile.ERROR_NO_PERMISSION,
Binder.getCallingUid(), Binder.getCallingPid());
}
String [] column = {BaseParameters.PARAMETER_NAME};
@@ -717,7 +753,35 @@ public class MediaQualityService extends SystemService {
}
}
- private void notifyPictureProfileError(String profileId, int errorCode, int uid, int pid) {
+ enum Mode {
+ ADD,
+ UPDATE,
+ REMOVE,
+ ERROR
+ }
+
+ private void notifyOnPictureProfileAdded(String profileId, PictureProfile profile,
+ int uid, int pid) {
+ notifyPictureProfileHelper(Mode.ADD, profileId, profile, null, uid, pid);
+ }
+
+ private void notifyOnPictureProfileUpdated(String profileId, PictureProfile profile,
+ int uid, int pid) {
+ notifyPictureProfileHelper(Mode.UPDATE, profileId, profile, null, uid, pid);
+ }
+
+ private void notifyOnPictureProfileRemoved(String profileId, PictureProfile profile,
+ int uid, int pid) {
+ notifyPictureProfileHelper(Mode.REMOVE, profileId, profile, null, uid, pid);
+ }
+
+ private void notifyOnPictureProfileError(String profileId, int errorCode,
+ int uid, int pid) {
+ notifyPictureProfileHelper(Mode.ERROR, profileId, null, errorCode, uid, pid);
+ }
+
+ private void notifyPictureProfileHelper(Mode mode, String profileId, PictureProfile profile,
+ Integer errorCode, int uid, int pid) {
UserState userState = getOrCreateUserStateLocked(UserHandle.USER_SYSTEM);
int n = userState.mPictureProfileCallbacks.beginBroadcast();
@@ -729,17 +793,56 @@ public class MediaQualityService extends SystemService {
.get(callback);
if (pidUid.first == pid && pidUid.second == uid) {
- userState.mPictureProfileCallbacks.getBroadcastItem(i)
- .onError(profileId, errorCode);
+ if (mode == Mode.ADD) {
+ userState.mPictureProfileCallbacks.getBroadcastItem(i)
+ .onPictureProfileAdded(profileId, profile);
+ } else if (mode == Mode.UPDATE) {
+ userState.mPictureProfileCallbacks.getBroadcastItem(i)
+ .onPictureProfileUpdated(profileId, profile);
+ } else if (mode == Mode.REMOVE) {
+ userState.mPictureProfileCallbacks.getBroadcastItem(i)
+ .onPictureProfileRemoved(profileId, profile);
+ } else if (mode == Mode.ERROR) {
+ userState.mPictureProfileCallbacks.getBroadcastItem(i)
+ .onError(profileId, errorCode);
+ }
}
} catch (RemoteException e) {
- Slog.e(TAG, "failed to report added input to callback", e);
+ if (mode == Mode.ADD) {
+ Slog.e(TAG, "Failed to report added picture profile to callback", e);
+ } else if (mode == Mode.UPDATE) {
+ Slog.e(TAG, "Failed to report updated picture profile to callback", e);
+ } else if (mode == Mode.REMOVE) {
+ Slog.e(TAG, "Failed to report removed picture profile to callback", e);
+ } else if (mode == Mode.ERROR) {
+ Slog.e(TAG, "Failed to report picture profile error to callback", e);
+ }
}
}
userState.mPictureProfileCallbacks.finishBroadcast();
}
- private void notifySoundProfileError(String profileId, int errorCode, int uid, int pid) {
+ private void notifyOnSoundProfileAdded(String profileId, SoundProfile profile,
+ int uid, int pid) {
+ notifySoundProfileHelper(Mode.ADD, profileId, profile, null, uid, pid);
+ }
+
+ private void notifyOnSoundProfileUpdated(String profileId, SoundProfile profile,
+ int uid, int pid) {
+ notifySoundProfileHelper(Mode.UPDATE, profileId, profile, null, uid, pid);
+ }
+
+ private void notifyOnSoundProfileRemoved(String profileId, SoundProfile profile,
+ int uid, int pid) {
+ notifySoundProfileHelper(Mode.REMOVE, profileId, profile, null, uid, pid);
+ }
+
+ private void notifyOnSoundProfileError(String profileId, int errorCode, int uid, int pid) {
+ notifySoundProfileHelper(Mode.ERROR, profileId, null, errorCode, uid, pid);
+ }
+
+ private void notifySoundProfileHelper(Mode mode, String profileId, SoundProfile profile,
+ Integer errorCode, int uid, int pid) {
UserState userState = getOrCreateUserStateLocked(UserHandle.USER_SYSTEM);
int n = userState.mSoundProfileCallbacks.beginBroadcast();
@@ -751,11 +854,30 @@ public class MediaQualityService extends SystemService {
.get(callback);
if (pidUid.first == pid && pidUid.second == uid) {
- userState.mSoundProfileCallbacks.getBroadcastItem(i)
- .onError(profileId, errorCode);
+ if (mode == Mode.ADD) {
+ userState.mSoundProfileCallbacks.getBroadcastItem(i)
+ .onSoundProfileAdded(profileId, profile);
+ } else if (mode == Mode.UPDATE) {
+ userState.mSoundProfileCallbacks.getBroadcastItem(i)
+ .onSoundProfileUpdated(profileId, profile);
+ } else if (mode == Mode.REMOVE) {
+ userState.mSoundProfileCallbacks.getBroadcastItem(i)
+ .onSoundProfileRemoved(profileId, profile);
+ } else if (mode == Mode.ERROR) {
+ userState.mSoundProfileCallbacks.getBroadcastItem(i)
+ .onError(profileId, errorCode);
+ }
}
} catch (RemoteException e) {
- Slog.e(TAG, "failed to report added input to callback", e);
+ if (mode == Mode.ADD) {
+ Slog.e(TAG, "Failed to report added sound profile to callback", e);
+ } else if (mode == Mode.UPDATE) {
+ Slog.e(TAG, "Failed to report updated sound profile to callback", e);
+ } else if (mode == Mode.REMOVE) {
+ Slog.e(TAG, "Failed to report removed sound profile to callback", e);
+ } else if (mode == Mode.ERROR) {
+ Slog.e(TAG, "Failed to report sound profile error to callback", e);
+ }
}
}
userState.mSoundProfileCallbacks.finishBroadcast();
@@ -812,7 +934,7 @@ public class MediaQualityService extends SystemService {
@Override
public List<String> getPictureProfileAllowList(UserHandle user) {
if (!hasGlobalPictureQualityServicePermission()) {
- notifyPictureProfileError(null, PictureProfile.ERROR_NO_PERMISSION,
+ notifyOnPictureProfileError(null, PictureProfile.ERROR_NO_PERMISSION,
Binder.getCallingUid(), Binder.getCallingPid());
}
return new ArrayList<>();
@@ -821,7 +943,7 @@ public class MediaQualityService extends SystemService {
@Override
public void setPictureProfileAllowList(List<String> packages, UserHandle user) {
if (!hasGlobalPictureQualityServicePermission()) {
- notifyPictureProfileError(null, PictureProfile.ERROR_NO_PERMISSION,
+ notifyOnPictureProfileError(null, PictureProfile.ERROR_NO_PERMISSION,
Binder.getCallingUid(), Binder.getCallingPid());
}
}
@@ -829,7 +951,7 @@ public class MediaQualityService extends SystemService {
@Override
public List<String> getSoundProfileAllowList(UserHandle user) {
if (!hasGlobalSoundQualityServicePermission()) {
- notifySoundProfileError(null, SoundProfile.ERROR_NO_PERMISSION,
+ notifyOnSoundProfileError(null, SoundProfile.ERROR_NO_PERMISSION,
Binder.getCallingUid(), Binder.getCallingPid());
}
return new ArrayList<>();
@@ -838,7 +960,7 @@ public class MediaQualityService extends SystemService {
@Override
public void setSoundProfileAllowList(List<String> packages, UserHandle user) {
if (!hasGlobalSoundQualityServicePermission()) {
- notifySoundProfileError(null, SoundProfile.ERROR_NO_PERMISSION,
+ notifyOnSoundProfileError(null, SoundProfile.ERROR_NO_PERMISSION,
Binder.getCallingUid(), Binder.getCallingPid());
}
}
@@ -851,7 +973,7 @@ public class MediaQualityService extends SystemService {
@Override
public void setAutoPictureQualityEnabled(boolean enabled, UserHandle user) {
if (!hasGlobalPictureQualityServicePermission()) {
- notifyPictureProfileError(null, PictureProfile.ERROR_NO_PERMISSION,
+ notifyOnPictureProfileError(null, PictureProfile.ERROR_NO_PERMISSION,
Binder.getCallingUid(), Binder.getCallingPid());
}
@@ -883,7 +1005,7 @@ public class MediaQualityService extends SystemService {
@Override
public void setSuperResolutionEnabled(boolean enabled, UserHandle user) {
if (!hasGlobalPictureQualityServicePermission()) {
- notifyPictureProfileError(null, PictureProfile.ERROR_NO_PERMISSION,
+ notifyOnPictureProfileError(null, PictureProfile.ERROR_NO_PERMISSION,
Binder.getCallingUid(), Binder.getCallingPid());
}
@@ -915,7 +1037,7 @@ public class MediaQualityService extends SystemService {
@Override
public void setAutoSoundQualityEnabled(boolean enabled, UserHandle user) {
if (!hasGlobalSoundQualityServicePermission()) {
- notifySoundProfileError(null, SoundProfile.ERROR_NO_PERMISSION,
+ notifyOnSoundProfileError(null, SoundProfile.ERROR_NO_PERMISSION,
Binder.getCallingUid(), Binder.getCallingPid());
}
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index 341038f878d9..32d3970ce549 100644
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -158,6 +158,7 @@ import static android.view.WindowManager.LayoutParams.TYPE_TOAST;
import static android.view.contentprotection.flags.Flags.rapidClearNotificationsByListenerAppOpEnabled;
import static com.android.internal.util.FrameworkStatsLog.DND_MODE_RULE;
+import static com.android.internal.util.FrameworkStatsLog.NOTIFICATION_BUNDLE_PREFERENCES;
import static com.android.internal.util.FrameworkStatsLog.PACKAGE_NOTIFICATION_CHANNEL_GROUP_PREFERENCES;
import static com.android.internal.util.FrameworkStatsLog.PACKAGE_NOTIFICATION_CHANNEL_PREFERENCES;
import static com.android.internal.util.FrameworkStatsLog.PACKAGE_NOTIFICATION_PREFERENCES;
@@ -362,6 +363,7 @@ import com.android.server.lights.LightsManager;
import com.android.server.notification.GroupHelper.NotificationAttributes;
import com.android.server.notification.ManagedServices.ManagedServiceInfo;
import com.android.server.notification.ManagedServices.UserProfiles;
+import com.android.server.notification.NotificationRecordLogger.NotificationPullStatsEvent;
import com.android.server.notification.NotificationRecordLogger.NotificationReportedEvent;
import com.android.server.notification.toast.CustomToastRecord;
import com.android.server.notification.toast.TextToastRecord;
@@ -2856,6 +2858,7 @@ public class NotificationManagerService extends SystemService {
mStatsManager.clearPullAtomCallback(PACKAGE_NOTIFICATION_PREFERENCES);
mStatsManager.clearPullAtomCallback(PACKAGE_NOTIFICATION_CHANNEL_PREFERENCES);
mStatsManager.clearPullAtomCallback(PACKAGE_NOTIFICATION_CHANNEL_GROUP_PREFERENCES);
+ mStatsManager.clearPullAtomCallback(NOTIFICATION_BUNDLE_PREFERENCES);
mStatsManager.clearPullAtomCallback(DND_MODE_RULE);
}
if (mAppOps != null) {
@@ -2960,6 +2963,12 @@ public class NotificationManagerService extends SystemService {
ConcurrentUtils.DIRECT_EXECUTOR,
mPullAtomCallback
);
+ mStatsManager.setPullAtomCallback(
+ NOTIFICATION_BUNDLE_PREFERENCES,
+ null, // use default PullAtomMetadata values
+ ConcurrentUtils.DIRECT_EXECUTOR,
+ mPullAtomCallback
+ );
}
private class StatsPullAtomCallbackImpl implements StatsManager.StatsPullAtomCallback {
@@ -2969,6 +2978,7 @@ public class NotificationManagerService extends SystemService {
case PACKAGE_NOTIFICATION_PREFERENCES:
case PACKAGE_NOTIFICATION_CHANNEL_PREFERENCES:
case PACKAGE_NOTIFICATION_CHANNEL_GROUP_PREFERENCES:
+ case NOTIFICATION_BUNDLE_PREFERENCES:
case DND_MODE_RULE:
return pullNotificationStates(atomTag, data);
default:
@@ -2980,8 +2990,15 @@ public class NotificationManagerService extends SystemService {
private int pullNotificationStates(int atomTag, List<StatsEvent> data) {
switch(atomTag) {
case PACKAGE_NOTIFICATION_PREFERENCES:
- mPreferencesHelper.pullPackagePreferencesStats(data,
- getAllUsersNotificationPermissions());
+ if (notificationClassificationUi()) {
+ Set<String> pkgs = mAssistants.getPackagesWithKeyTypeAdjustmentSettings();
+ mPreferencesHelper.pullPackagePreferencesStats(data,
+ getAllUsersNotificationPermissions(),
+ getPackageSpecificAdjustmentKeyTypes(pkgs));
+ } else {
+ mPreferencesHelper.pullPackagePreferencesStats(data,
+ getAllUsersNotificationPermissions());
+ }
break;
case PACKAGE_NOTIFICATION_CHANNEL_PREFERENCES:
mPreferencesHelper.pullPackageChannelPreferencesStats(data);
@@ -2989,6 +3006,11 @@ public class NotificationManagerService extends SystemService {
case PACKAGE_NOTIFICATION_CHANNEL_GROUP_PREFERENCES:
mPreferencesHelper.pullPackageChannelGroupPreferencesStats(data);
break;
+ case NOTIFICATION_BUNDLE_PREFERENCES:
+ if (notificationClassification() && notificationClassificationUi()) {
+ mAssistants.pullBundlePreferencesStats(data);
+ }
+ break;
case DND_MODE_RULE:
mZenModeHelper.pullRules(data);
break;
@@ -4961,6 +4983,12 @@ public class NotificationManagerService extends SystemService {
@Override
public ParceledListSlice<NotificationChannel> getNotificationChannels(
String callingPkg, String targetPkg, int userId) {
+ return getOrCreateNotificationChannels(callingPkg, targetPkg, userId, false);
+ }
+
+ @Override
+ public ParceledListSlice<NotificationChannel> getOrCreateNotificationChannels(
+ String callingPkg, String targetPkg, int userId, boolean createPrefsIfNeeded) {
if (canNotifyAsPackage(callingPkg, targetPkg, userId)
|| isCallingUidSystem()) {
int targetUid = -1;
@@ -4970,7 +4998,8 @@ public class NotificationManagerService extends SystemService {
/* ignore */
}
return mPreferencesHelper.getNotificationChannels(
- targetPkg, targetUid, false /* includeDeleted */, true);
+ targetPkg, targetUid, false /* includeDeleted */, true,
+ createPrefsIfNeeded);
}
throw new SecurityException("Pkg " + callingPkg
+ " cannot read channels for " + targetPkg + " in " + userId);
@@ -7481,6 +7510,24 @@ public class NotificationManagerService extends SystemService {
return allPermissions;
}
+ @VisibleForTesting
+ @FlaggedApi(android.app.Flags.FLAG_NOTIFICATION_CLASSIFICATION_UI)
+ protected @NonNull Map<String, Set<Integer>> getPackageSpecificAdjustmentKeyTypes(
+ Set<String> pkgs) {
+ ArrayMap<String, Set<Integer>> pkgToAllowedTypes = new ArrayMap<>();
+ for (String pkg : pkgs) {
+ int[] allowedTypesArray = mAssistants.getAllowedAdjustmentKeyTypesForPackage(pkg);
+ if (allowedTypesArray != null) {
+ Set<Integer> allowedTypes = new ArraySet<Integer>();
+ for (int i : allowedTypesArray) {
+ allowedTypes.add(i);
+ }
+ pkgToAllowedTypes.append(pkg, allowedTypes);
+ }
+ }
+ return pkgToAllowedTypes;
+ }
+
private void dumpJson(PrintWriter pw, @NonNull DumpFilter filter,
ArrayMap<Pair<Integer, String>, Pair<Boolean, Boolean>> pkgPermissions) {
JSONObject dump = new JSONObject();
@@ -12056,6 +12103,22 @@ public class NotificationManagerService extends SystemService {
}
@FlaggedApi(android.app.Flags.FLAG_NOTIFICATION_CLASSIFICATION_UI)
+ protected @NonNull Set<String> getPackagesWithKeyTypeAdjustmentSettings() {
+ if (notificationClassificationUi()) {
+ Set<String> packagesWithModifications = new ArraySet<String>();
+ synchronized (mLock) {
+ for (String pkg : mClassificationTypePackagesEnabledTypes.keySet()) {
+ if (mClassificationTypePackagesEnabledTypes.get(pkg) != null) {
+ packagesWithModifications.add(pkg);
+ }
+ }
+ }
+ return packagesWithModifications;
+ }
+ return new ArraySet<String>();
+ }
+
+ @FlaggedApi(android.app.Flags.FLAG_NOTIFICATION_CLASSIFICATION_UI)
protected @NonNull int[] getAllowedAdjustmentKeyTypesForPackage(String pkg) {
synchronized (mLock) {
if (notificationClassificationUi()) {
@@ -12656,6 +12719,32 @@ public class NotificationManagerService extends SystemService {
Slog.e(TAG, "unable to notify assistant (capabilities): " + info, ex);
}
}
+
+ /**
+ * Fills out {@link BundlePreferences} proto and wraps it in a {@link StatsEvent}.
+ */
+ @FlaggedApi(android.service.notification.Flags.FLAG_NOTIFICATION_CLASSIFICATION)
+ protected void pullBundlePreferencesStats(List<StatsEvent> events) {
+ boolean bundlesAllowed = true;
+ synchronized (mLock) {
+ List<String> unsupportedAdjustments = new ArrayList(
+ mNasUnsupported.getOrDefault(
+ UserHandle.getUserId(Binder.getCallingUid()),
+ new HashSet<>())
+ );
+ bundlesAllowed = !unsupportedAdjustments.contains(Adjustment.KEY_TYPE);
+ }
+
+ int[] allowedBundleTypes = getAllowedAdjustmentKeyTypes();
+
+ events.add(FrameworkStatsLog.buildStatsEvent(
+ NOTIFICATION_BUNDLE_PREFERENCES,
+ /* optional int32 event_id = 1 */
+ NotificationPullStatsEvent.NOTIFICATION_BUNDLE_PREFERENCES_PULLED.getId(),
+ /* optional bool bundles_allowed = 2 */ bundlesAllowed,
+ /* repeated android.stats.notification.BundleTypes allowed_bundle_types = 3 */
+ allowedBundleTypes));
+ }
}
/**
diff --git a/services/core/java/com/android/server/notification/NotificationRecordLogger.java b/services/core/java/com/android/server/notification/NotificationRecordLogger.java
index 3943aa583fee..6c0035b82a86 100644
--- a/services/core/java/com/android/server/notification/NotificationRecordLogger.java
+++ b/services/core/java/com/android/server/notification/NotificationRecordLogger.java
@@ -32,8 +32,6 @@ import android.service.notification.NotificationListenerService;
import android.service.notification.NotificationStats;
import android.util.Log;
-import com.android.internal.config.sysui.SystemUiSystemPropertiesFlags;
-import com.android.internal.config.sysui.SystemUiSystemPropertiesFlags.NotificationFlags;
import com.android.internal.logging.InstanceId;
import com.android.internal.logging.UiEvent;
import com.android.internal.logging.UiEventLogger;
@@ -368,6 +366,19 @@ interface NotificationRecordLogger {
}
}
+ enum NotificationPullStatsEvent implements UiEventLogger.UiEventEnum {
+ @UiEvent(doc = "Notification Bundle Preferences pulled.")
+ NOTIFICATION_BUNDLE_PREFERENCES_PULLED(2072);
+
+ private final int mId;
+ NotificationPullStatsEvent(int id) {
+ mId = id;
+ }
+ @Override public int getId() {
+ return mId;
+ }
+ }
+
/**
* A helper for extracting logging information from one or two NotificationRecords.
*/
diff --git a/services/core/java/com/android/server/notification/PreferencesHelper.java b/services/core/java/com/android/server/notification/PreferencesHelper.java
index 36eabae69b22..3b34dcd17705 100644
--- a/services/core/java/com/android/server/notification/PreferencesHelper.java
+++ b/services/core/java/com/android/server/notification/PreferencesHelper.java
@@ -16,6 +16,7 @@
package com.android.server.notification;
+import static android.app.Flags.notificationClassificationUi;
import static android.app.AppOpsManager.OP_SYSTEM_ALERT_WINDOW;
import static android.app.NotificationChannel.DEFAULT_CHANNEL_ID;
import static android.app.NotificationChannel.NEWS_ID;
@@ -1961,10 +1962,25 @@ public class PreferencesHelper implements RankingConfig {
@Override
public ParceledListSlice<NotificationChannel> getNotificationChannels(String pkg, int uid,
boolean includeDeleted, boolean includeBundles) {
+ return getNotificationChannels(pkg, uid, includeDeleted, includeBundles, false);
+ }
+
+ protected ParceledListSlice<NotificationChannel> getNotificationChannels(String pkg, int uid,
+ boolean includeDeleted, boolean includeBundles, boolean createPrefsIfNeeded) {
+ if (createPrefsIfNeeded && !android.app.Flags.nmBinderPerfCacheChannels()) {
+ Slog.wtf(TAG,
+ "getNotificationChannels called with createPrefsIfNeeded=true and flag off");
+ createPrefsIfNeeded = false;
+ }
Objects.requireNonNull(pkg);
List<NotificationChannel> channels = new ArrayList<>();
synchronized (mLock) {
- PackagePreferences r = getPackagePreferencesLocked(pkg, uid);
+ PackagePreferences r;
+ if (createPrefsIfNeeded) {
+ r = getOrCreatePackagePreferencesLocked(pkg, uid);
+ } else {
+ r = getPackagePreferencesLocked(pkg, uid);
+ }
if (r == null) {
return ParceledListSlice.emptyList();
}
@@ -2523,6 +2539,25 @@ public class PreferencesHelper implements RankingConfig {
*/
public void pullPackagePreferencesStats(List<StatsEvent> events,
ArrayMap<Pair<Integer, String>, Pair<Boolean, Boolean>> pkgPermissions) {
+ pullPackagePreferencesStats(events, pkgPermissions, new ArrayMap<String, Set<Integer>>());
+ }
+
+
+ /**
+ * Fills out {@link PackageNotificationPreferences} proto and wraps it in a {@link StatsEvent}.
+ * @param events Newly filled out StatsEvent protos are added to this list as output.
+ * @param pkgPermissions Maps from a pair representing a uid and package to a pair of booleans,
+ * where the first represents whether the notification permission was
+ * granted to that package, and the second represents whether the
+ * permission was user-set.
+ * @param pkgAdjustmentKeyTypes A map of package names that are not allowed to have their
+ * notifications classified into differently typed notification
+ * channels, and the channels that they're allowed to be
+ * classified into.
+ */
+ public void pullPackagePreferencesStats(List<StatsEvent> events,
+ ArrayMap<Pair<Integer, String>, Pair<Boolean, Boolean>> pkgPermissions,
+ @NonNull Map<String, Set<Integer>> pkgAdjustmentKeyTypes) {
Set<Pair<Integer, String>> pkgsWithPermissionsToHandle = null;
if (pkgPermissions != null) {
pkgsWithPermissionsToHandle = pkgPermissions.keySet();
@@ -2568,6 +2603,14 @@ public class PreferencesHelper implements RankingConfig {
isFsiPermissionUserSet(r.pkg, r.uid, fsiState,
currentPermissionFlags);
+ if (!notificationClassificationUi()
+ && pkgAdjustmentKeyTypes.keySet().size() > 0) {
+ Slog.w(TAG, "Pkg adjustment types improperly allowed without flag set");
+ }
+
+ int[] allowedBundleTypes =
+ getAllowedTypesForPackage(pkgAdjustmentKeyTypes, r.pkg);
+
events.add(FrameworkStatsLog.buildStatsEvent(
PACKAGE_NOTIFICATION_PREFERENCES,
/* optional int32 uid = 1 [(is_uid) = true] */ r.uid,
@@ -2576,7 +2619,9 @@ public class PreferencesHelper implements RankingConfig {
/* optional int32 user_locked_fields = 4 */ r.lockedAppFields,
/* optional bool user_set_importance = 5 */ importanceIsUserSet,
/* optional FsiState fsi_state = 6 */ fsiState,
- /* optional bool is_fsi_permission_user_set = 7 */ fsiIsUserSet));
+ /* optional bool is_fsi_permission_user_set = 7 */ fsiIsUserSet,
+ /* repeated int32 allowed_bundle_types = 8 */ allowedBundleTypes
+ ));
}
}
@@ -2587,6 +2632,10 @@ public class PreferencesHelper implements RankingConfig {
break;
}
pulledEvents++;
+
+ int[] allowedBundleTypes =
+ getAllowedTypesForPackage(pkgAdjustmentKeyTypes, p.second);
+
// Because all fields are required in FrameworkStatsLog.buildStatsEvent, we have
// to fill in default values for all the unspecified fields.
events.add(FrameworkStatsLog.buildStatsEvent(
@@ -2598,9 +2647,29 @@ public class PreferencesHelper implements RankingConfig {
/* optional int32 user_locked_fields = 4 */ DEFAULT_LOCKED_APP_FIELDS,
/* optional bool user_set_importance = 5 */ pkgPermissions.get(p).second,
/* optional FsiState fsi_state = 6 */ 0,
- /* optional bool is_fsi_permission_user_set = 7 */ false));
+ /* optional bool is_fsi_permission_user_set = 7 */ false,
+ /* repeated BundleTypes allowed_bundle_types = 8 */ allowedBundleTypes));
+ }
+ }
+ }
+
+ private int[] getAllowedTypesForPackage(@NonNull
+ Map<String, Set<Integer>> pkgAdjustmentKeyTypes,
+ String pkg) {
+ int[] allowedBundleTypes = new int[]{};
+ if (notificationClassificationUi()) {
+ if (pkgAdjustmentKeyTypes.containsKey(pkg)) {
+ // Convert from set to int[]
+ Set<Integer> types = pkgAdjustmentKeyTypes.get(pkg);
+ allowedBundleTypes = new int[types.size()];
+ int i = 0;
+ for (int val : types) {
+ allowedBundleTypes[i] = val;
+ i++;
+ }
}
}
+ return allowedBundleTypes;
}
/**
diff --git a/services/core/java/com/android/server/pm/DeletePackageHelper.java b/services/core/java/com/android/server/pm/DeletePackageHelper.java
index f6e518a4fed7..90adb6683496 100644
--- a/services/core/java/com/android/server/pm/DeletePackageHelper.java
+++ b/services/core/java/com/android/server/pm/DeletePackageHelper.java
@@ -729,10 +729,13 @@ final class DeletePackageHelper {
final String internalPackageName =
snapshot.resolveInternalPackageName(packageName, versionCode);
+ final boolean deleteAllUsers = (deleteFlags & PackageManager.DELETE_ALL_USERS) != 0;
+ final int[] users = deleteAllUsers ? mUserManagerInternal.getUserIds() : new int[]{userId};
+
if (!isOrphaned(snapshot, internalPackageName)
&& !allowSilentUninstall
- && !isCallerAllowedToSilentlyUninstall(
- snapshot, callingUid, internalPackageName, userId)) {
+ && !isCallerAllowedToSilentlyUninstall(snapshot, callingUid, internalPackageName,
+ users)) {
mPm.mHandler.post(() -> {
try {
final Intent intent = new Intent(Intent.ACTION_UNINSTALL_PACKAGE);
@@ -751,8 +754,7 @@ final class DeletePackageHelper {
});
return;
}
- final boolean deleteAllUsers = (deleteFlags & PackageManager.DELETE_ALL_USERS) != 0;
- final int[] users = deleteAllUsers ? mUserManagerInternal.getUserIds() : new int[]{userId};
+
if (UserHandle.getUserId(callingUid) != userId || (deleteAllUsers && users.length > 1)) {
mPm.mContext.enforceCallingOrSelfPermission(
android.Manifest.permission.INTERACT_ACROSS_USERS_FULL,
@@ -916,16 +918,24 @@ final class DeletePackageHelper {
}
private boolean isCallerAllowedToSilentlyUninstall(@NonNull Computer snapshot, int callingUid,
- String pkgName, int userId) {
+ String pkgName, int[] targetUserIds) {
if (PackageManagerServiceUtils.isRootOrShell(callingUid)
|| UserHandle.getAppId(callingUid) == Process.SYSTEM_UID) {
return true;
}
final int callingUserId = UserHandle.getUserId(callingUid);
+
// If the caller installed the pkgName, then allow it to silently uninstall.
- if (callingUid == snapshot.getPackageUid(
- snapshot.getInstallerPackageName(pkgName, userId), 0, callingUserId)) {
- return true;
+ for (int user : targetUserIds) {
+ try {
+ if (callingUid == snapshot.getPackageUid(
+ snapshot.getInstallerPackageName(pkgName, user), 0, callingUserId)) {
+ return true;
+ }
+ } catch (Exception ignored) {
+ // The app to be uninstalled (`pkgName`) is not installed on this `user`. Continue
+ // looking for the installerPkgName in the next user
+ }
}
// Allow package verifier to silently uninstall.
diff --git a/services/core/java/com/android/server/pm/InstallPackageHelper.java b/services/core/java/com/android/server/pm/InstallPackageHelper.java
index a8f31f90d1e0..4cca85590967 100644
--- a/services/core/java/com/android/server/pm/InstallPackageHelper.java
+++ b/services/core/java/com/android/server/pm/InstallPackageHelper.java
@@ -3039,13 +3039,14 @@ final class InstallPackageHelper {
if (android.permission.flags.Flags.enhancedConfirmationModeApisEnabled()
&& android.security.Flags.extendEcmToAllSettings()) {
final int appId = request.getAppId();
- mPm.mHandler.post(() -> {
+ // TODO: b/388960315 - Implement a long-term solution to race condition
+ mPm.mHandler.postDelayed(() -> {
for (int userId : firstUserIds) {
// MODE_DEFAULT means that the app's guardedness will be decided lazily
setAccessRestrictedSettingsMode(packageName, appId, userId,
AppOpsManager.MODE_DEFAULT);
}
- });
+ }, 1000L);
} else {
// Apply restricted settings on potentially dangerous packages. Needs to happen
// after appOpsManager is notified of the new package
diff --git a/services/core/java/com/android/server/pm/PackageSetting.java b/services/core/java/com/android/server/pm/PackageSetting.java
index fb16b862b275..a902f5ff372f 100644
--- a/services/core/java/com/android/server/pm/PackageSetting.java
+++ b/services/core/java/com/android/server/pm/PackageSetting.java
@@ -1848,8 +1848,10 @@ public class PackageSetting extends SettingBase implements PackageStateInternal
boolean manifestOverrideEnabled = (mPageSizeAppCompatFlags
& ApplicationInfo.PAGE_SIZE_APP_COMPAT_FLAG_MANIFEST_OVERRIDE_ENABLED) != 0;
boolean settingsOverrideEnabled = (mPageSizeAppCompatFlags
- & ApplicationInfo.PAGE_SIZE_APP_COMPAT_FLAG_MANIFEST_OVERRIDE_ENABLED) != 0;
- if (manifestOverrideEnabled || settingsOverrideEnabled) {
+ & ApplicationInfo.PAGE_SIZE_APP_COMPAT_FLAG_SETTINGS_OVERRIDE_ENABLED) != 0;
+ boolean settingsOverrideDisabled = (mPageSizeAppCompatFlags
+ & ApplicationInfo.PAGE_SIZE_APP_COMPAT_FLAG_SETTINGS_OVERRIDE_DISABLED) != 0;
+ if (manifestOverrideEnabled || settingsOverrideEnabled || settingsOverrideDisabled) {
return null;
}
diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java
index 81956fbb55e6..f4d4c5be035e 100644
--- a/services/core/java/com/android/server/pm/UserManagerService.java
+++ b/services/core/java/com/android/server/pm/UserManagerService.java
@@ -3312,13 +3312,18 @@ public class UserManagerService extends IUserManager.Stub {
}
}
-
-
private void sendUserInfoChangedBroadcast(@UserIdInt int userId) {
Intent changedIntent = new Intent(Intent.ACTION_USER_INFO_CHANGED);
changedIntent.putExtra(Intent.EXTRA_USER_HANDLE, userId);
changedIntent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
mContext.sendBroadcastAsUser(changedIntent, UserHandle.ALL);
+
+ // This intent allow system UI apps to refresh the content even if process was freezed.
+ Intent bgIntent = new Intent(Intent.ACTION_USER_INFO_CHANGED_BACKGROUND);
+ bgIntent.putExtra(Intent.EXTRA_USER_HANDLE, userId);
+ bgIntent.addFlags(Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND);
+ mContext.sendBroadcastAsUser(bgIntent, UserHandle.ALL,
+ Manifest.permission.MANAGE_USERS);
}
@Override
diff --git a/services/core/java/com/android/server/policy/DeviceStateProviderImpl.java b/services/core/java/com/android/server/policy/DeviceStateProviderImpl.java
index e75f852eb437..a755ee1cd0fe 100644
--- a/services/core/java/com/android/server/policy/DeviceStateProviderImpl.java
+++ b/services/core/java/com/android/server/policy/DeviceStateProviderImpl.java
@@ -45,6 +45,7 @@ import com.android.server.devicestate.DeviceStateProvider;
import com.android.server.input.InputManagerInternal;
import com.android.server.policy.devicestate.config.Conditions;
import com.android.server.policy.devicestate.config.DeviceStateConfig;
+import com.android.server.policy.devicestate.config.Flags;
import com.android.server.policy.devicestate.config.LidSwitchCondition;
import com.android.server.policy.devicestate.config.NumericRange;
import com.android.server.policy.devicestate.config.Properties;
@@ -140,7 +141,16 @@ public final class DeviceStateProviderImpl implements DeviceStateProvider,
private static final String PROPERTY_FEATURE_REAR_DISPLAY_OUTER_DEFAULT =
"com.android.server.policy.PROPERTY_FEATURE_REAR_DISPLAY_OUTER_DEFAULT";
-
+ // Deprecated flag definitions to maintain backwards compatibility.
+ private static final String FLAG_CANCEL_OVERRIDE_REQUESTS = "FLAG_CANCEL_OVERRIDE_REQUESTS";
+ private static final String FLAG_APP_INACCESSIBLE = "FLAG_APP_INACCESSIBLE";
+ private static final String FLAG_EMULATED_ONLY = "FLAG_EMULATED_ONLY";
+ private static final String FLAG_CANCEL_WHEN_REQUESTER_NOT_ON_TOP =
+ "FLAG_CANCEL_WHEN_REQUESTER_NOT_ON_TOP";
+ private static final String FLAG_UNSUPPORTED_WHEN_THERMAL_STATUS_CRITICAL =
+ "FLAG_UNSUPPORTED_WHEN_THERMAL_STATUS_CRITICAL";
+ private static final String FLAG_UNSUPPORTED_WHEN_POWER_SAVE_MODE =
+ "FLAG_UNSUPPORTED_WHEN_POWER_SAVE_MODE";
/** Interface that allows reading the device state configuration. */
interface ReadableConfig {
@@ -185,15 +195,29 @@ public final class DeviceStateProviderImpl implements DeviceStateProvider,
new HashSet<>();
Set<@DeviceState.DeviceStateProperties Integer> physicalProperties =
new HashSet<>();
- final Properties configFlags = stateConfig.getProperties();
- if (configFlags != null) {
- List<String> configPropertyStrings = configFlags.getProperty();
+ final Properties configProperties = stateConfig.getProperties();
+ if (configProperties != null) {
+ List<String> configPropertyStrings = configProperties.getProperty();
for (int i = 0; i < configPropertyStrings.size(); i++) {
final String configPropertyString = configPropertyStrings.get(i);
addPropertyByString(configPropertyString, systemProperties,
physicalProperties);
}
}
+
+ if (android.hardware.devicestate.feature.flags
+ .Flags.deviceStateConfigurationFlag()) {
+ // Parse through the deprecated flag configuration to keep compatibility.
+ final Flags configFlags = stateConfig.getFlags();
+ if (configFlags != null) {
+ List<String> configFlagStrings = configFlags.getFlag();
+ for (int i = 0; i < configFlagStrings.size(); i++) {
+ final String configFlagString = configFlagStrings.get(i);
+ addFlagByString(configFlagString, systemProperties);
+ }
+ }
+ }
+
DeviceState.Configuration deviceStateConfiguration =
new DeviceState.Configuration.Builder(state, name)
.setSystemProperties(systemProperties)
@@ -292,6 +316,34 @@ public final class DeviceStateProviderImpl implements DeviceStateProvider,
}
}
+ private static void addFlagByString(String flagString,
+ Set<@DeviceState.SystemDeviceStateProperties Integer> systemProperties) {
+ switch (flagString) {
+ case FLAG_APP_INACCESSIBLE:
+ systemProperties.add(DeviceState.PROPERTY_APP_INACCESSIBLE);
+ break;
+ case FLAG_EMULATED_ONLY:
+ systemProperties.add(DeviceState.PROPERTY_EMULATED_ONLY);
+ break;
+ case FLAG_CANCEL_OVERRIDE_REQUESTS:
+ systemProperties.add(DeviceState.PROPERTY_POLICY_CANCEL_OVERRIDE_REQUESTS);
+ break;
+ case FLAG_CANCEL_WHEN_REQUESTER_NOT_ON_TOP:
+ systemProperties.add(DeviceState.PROPERTY_POLICY_CANCEL_WHEN_REQUESTER_NOT_ON_TOP);
+ break;
+ case FLAG_UNSUPPORTED_WHEN_POWER_SAVE_MODE:
+ systemProperties.add(DeviceState.PROPERTY_POLICY_UNSUPPORTED_WHEN_POWER_SAVE_MODE);
+ break;
+ case FLAG_UNSUPPORTED_WHEN_THERMAL_STATUS_CRITICAL:
+ systemProperties.add(
+ DeviceState.PROPERTY_POLICY_UNSUPPORTED_WHEN_THERMAL_STATUS_CRITICAL);
+ break;
+ default:
+ Slog.w(TAG, "Parsed unknown flag with name: " + flagString);
+ break;
+ }
+ }
+
// Lock for internal state.
private final Object mLock = new Object();
private final Context mContext;
diff --git a/services/core/java/com/android/server/power/TEST_MAPPING b/services/core/java/com/android/server/power/TEST_MAPPING
index f67f56db3c1e..c5937e5ffb90 100644
--- a/services/core/java/com/android/server/power/TEST_MAPPING
+++ b/services/core/java/com/android/server/power/TEST_MAPPING
@@ -8,6 +8,12 @@
},
{
"name": "PowerServiceTests_server_power"
+ },
+ {
+ "name": "CtsStatsdAtomHostTestCases_statsdatom_powermanager",
+ "file_patterns": [
+ "(/|^)ThermalManagerService.java"
+ ]
}
],
"postsubmit": [
@@ -22,12 +28,6 @@
},
{
"name": "PowerServiceTests_server_power"
- },
- {
- "name": "CtsStatsdAtomHostTestCases_statsdatom_powermanager",
- "file_patterns": [
- "(/|^)ThermalManagerService.java"
- ]
}
]
}
diff --git a/services/core/java/com/android/server/security/authenticationpolicy/AuthenticationPolicyService.java b/services/core/java/com/android/server/security/authenticationpolicy/AuthenticationPolicyService.java
index 6798a6146ae0..2452dc59bea5 100644
--- a/services/core/java/com/android/server/security/authenticationpolicy/AuthenticationPolicyService.java
+++ b/services/core/java/com/android/server/security/authenticationpolicy/AuthenticationPolicyService.java
@@ -17,6 +17,7 @@
package com.android.server.security.authenticationpolicy;
import static android.Manifest.permission.MANAGE_SECURE_LOCK_DEVICE;
+import static android.security.Flags.disableAdaptiveAuthCounterLock;
import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.SOME_AUTH_REQUIRED_AFTER_ADAPTIVE_AUTH_REQUEST;
@@ -39,6 +40,7 @@ import android.os.IBinder;
import android.os.Looper;
import android.os.Message;
import android.os.SystemClock;
+import android.provider.Settings;
import android.security.authenticationpolicy.AuthenticationPolicyManager;
import android.security.authenticationpolicy.DisableSecureLockDeviceParams;
import android.security.authenticationpolicy.EnableSecureLockDeviceParams;
@@ -251,6 +253,17 @@ public class AuthenticationPolicyService extends SystemService {
return;
}
+ if (disableAdaptiveAuthCounterLock() && Build.IS_DEBUGGABLE) {
+ final boolean disabled = Settings.Secure.getIntForUser(
+ getContext().getContentResolver(),
+ Settings.Secure.DISABLE_ADAPTIVE_AUTH_LIMIT_LOCK,
+ 0 /* default */, userId) != 0;
+ if (disabled) {
+ Slog.d(TAG, "not locking (disabled by user)");
+ return;
+ }
+ }
+
//TODO: additionally consider the trust signal before locking device
lockDevice(userId);
}
diff --git a/services/core/java/com/android/server/selinux/QuotaExceededException.java b/services/core/java/com/android/server/selinux/QuotaExceededException.java
new file mode 100644
index 000000000000..26d4d827af6b
--- /dev/null
+++ b/services/core/java/com/android/server/selinux/QuotaExceededException.java
@@ -0,0 +1,23 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.server.selinux;
+
+/** An exception raised when the quota has been reached.
+ *
+ * This exception is raised in EventLogCollection.add(). See QuotaLimiter
+ * for the implementation details.
+ */
+class QuotaExceededException extends Exception {}
diff --git a/services/core/java/com/android/server/selinux/SelinuxAuditLogsCollector.java b/services/core/java/com/android/server/selinux/SelinuxAuditLogsCollector.java
index 0aa705892376..54365ff03db0 100644
--- a/services/core/java/com/android/server/selinux/SelinuxAuditLogsCollector.java
+++ b/services/core/java/com/android/server/selinux/SelinuxAuditLogsCollector.java
@@ -28,10 +28,8 @@ import com.android.server.utils.Slogf;
import java.io.IOException;
import java.time.Instant;
-import java.util.ArrayDeque;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Queue;
+import java.util.AbstractCollection;
+import java.util.Iterator;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.function.Supplier;
import java.util.regex.Matcher;
@@ -57,6 +55,7 @@ class SelinuxAuditLogsCollector {
private final Supplier<String> mAuditDomainSupplier;
private final RateLimiter mRateLimiter;
private final QuotaLimiter mQuotaLimiter;
+ private EventLogCollection mEventCollection;
@VisibleForTesting Instant mLastWrite = Instant.MIN;
@@ -69,6 +68,7 @@ class SelinuxAuditLogsCollector {
mAuditDomainSupplier = auditDomainSupplier;
mRateLimiter = rateLimiter;
mQuotaLimiter = quotaLimiter;
+ mEventCollection = new EventLogCollection();
}
SelinuxAuditLogsCollector(RateLimiter rateLimiter, QuotaLimiter quotaLimiter) {
@@ -86,75 +86,72 @@ class SelinuxAuditLogsCollector {
mStopRequested.set(stopRequested);
}
- /**
- * Collect and push SELinux audit logs for the provided {@code tagCode}.
+ /** A Collection to work around EventLog.readEvents() constraints.
+ *
+ * This collection only supports add(). Any other method inherited from
+ * Collection will throw an UnsupportedOperationException exception.
*
- * @return true if the job was completed. If the job was interrupted, return false.
+ * This collection ensures that we are processing one event at a time and
+ * avoid collecting all the event objects before processing (e.g.,
+ * ArrayList), which could lead to an OOM situation.
*/
- boolean collect(int tagCode) {
- Queue<Event> logLines = new ArrayDeque<>();
- Instant latestTimestamp = collectLogLines(tagCode, logLines);
-
- boolean quotaExceeded = writeAuditLogs(logLines);
- if (quotaExceeded) {
- Slog.w(TAG, "Too many SELinux logs in the queue, I am giving up.");
- mLastWrite = latestTimestamp; // next run we will ignore all these logs.
- logLines.clear();
+ class EventLogCollection extends AbstractCollection<Event> {
+
+ SelinuxAuditLogBuilder mAuditLogBuilder;
+ int mAuditsWritten = 0;
+ Instant mLatestTimestamp;
+
+ void reset() {
+ mAuditsWritten = 0;
+ mLatestTimestamp = mLastWrite;
+ mAuditLogBuilder = new SelinuxAuditLogBuilder(mAuditDomainSupplier.get());
}
- return logLines.isEmpty();
- }
+ int getAuditsWritten() {
+ return mAuditsWritten;
+ }
- private Instant collectLogLines(int tagCode, Queue<Event> logLines) {
- List<Event> events = new ArrayList<>();
- try {
- EventLog.readEvents(new int[] {tagCode}, events);
- } catch (IOException e) {
- Slog.e(TAG, "Error reading event logs", e);
+ Instant getLatestTimestamp() {
+ return mLatestTimestamp;
}
- Instant latestTimestamp = mLastWrite;
- for (Event event : events) {
- Instant eventTime = Instant.ofEpochSecond(0, event.getTimeNanos());
- if (eventTime.isAfter(latestTimestamp)) {
- latestTimestamp = eventTime;
+ @Override
+ public Iterator<Event> iterator() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public int size() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public boolean add(Event event) {
+ if (mStopRequested.get()) {
+ throw new IllegalStateException(new InterruptedException());
}
+
+ Instant eventTime = Instant.ofEpochSecond(/* epochSecond= */ 0, event.getTimeNanos());
if (eventTime.compareTo(mLastWrite) <= 0) {
- continue;
+ return true;
}
Object eventData = event.getData();
if (!(eventData instanceof String)) {
- continue;
+ return true;
}
- logLines.add(event);
- }
- return latestTimestamp;
- }
-
- private boolean writeAuditLogs(Queue<Event> logLines) {
- final SelinuxAuditLogBuilder auditLogBuilder =
- new SelinuxAuditLogBuilder(mAuditDomainSupplier.get());
- int auditsWritten = 0;
-
- while (!mStopRequested.get() && !logLines.isEmpty()) {
- Event event = logLines.poll();
- String logLine = (String) event.getData();
- Instant logTime = Instant.ofEpochSecond(0, event.getTimeNanos());
+ String logLine = (String) eventData;
if (!SELINUX_MATCHER.reset(logLine).matches()) {
- continue;
+ return true;
}
- auditLogBuilder.reset(SELINUX_MATCHER.group("denial"));
- final SelinuxAuditLog auditLog = auditLogBuilder.build();
+ mAuditLogBuilder.reset(SELINUX_MATCHER.group("denial"));
+ final SelinuxAuditLog auditLog = mAuditLogBuilder.build();
if (auditLog == null) {
- continue;
+ return true;
}
if (!mQuotaLimiter.acquire()) {
- if (DEBUG) {
- Slogf.d(TAG, "Running out of quota after %d logs.", auditsWritten);
- }
- return true;
+ throw new IllegalStateException(new QuotaExceededException());
}
mRateLimiter.acquire();
@@ -169,16 +166,50 @@ class SelinuxAuditLogsCollector {
auditLog.mTClass,
auditLog.mPath,
auditLog.mPermissive);
- auditsWritten++;
- if (logTime.isAfter(mLastWrite)) {
- mLastWrite = logTime;
+ mAuditsWritten++;
+ if (eventTime.isAfter(mLatestTimestamp)) {
+ mLatestTimestamp = eventTime;
+ }
+
+ return true;
+ }
+ }
+
+ /**
+ * Collect and push SELinux audit logs for the provided {@code tagCode}.
+ *
+ * @return true if the job was completed. If the job was interrupted or
+ * failed because of IOException, return false.
+ * @throws QuotaExceededException if it ran out of quota.
+ */
+ boolean collect(int tagCode) throws QuotaExceededException {
+ mEventCollection.reset();
+ try {
+ EventLog.readEvents(new int[] {tagCode}, mEventCollection);
+ } catch (IllegalStateException e) {
+ if (e.getCause() instanceof QuotaExceededException) {
+ if (DEBUG) {
+ Slogf.d(TAG, "Running out of quota after %d logs.",
+ mEventCollection.getAuditsWritten());
+ }
+ // next run we will ignore all these logs.
+ mLastWrite = mEventCollection.getLatestTimestamp();
+ throw (QuotaExceededException) e.getCause();
+ } else if (e.getCause() instanceof InterruptedException) {
+ mLastWrite = mEventCollection.getLatestTimestamp();
+ return false;
}
+ throw e;
+ } catch (IOException e) {
+ Slog.e(TAG, "Error reading event logs", e);
+ return false;
}
+ mLastWrite = mEventCollection.getLatestTimestamp();
if (DEBUG) {
- Slogf.d(TAG, "Written %d logs", auditsWritten);
+ Slogf.d(TAG, "Written %d logs", mEventCollection.getAuditsWritten());
}
- return false;
+ return true;
}
}
diff --git a/services/core/java/com/android/server/selinux/SelinuxAuditLogsJob.java b/services/core/java/com/android/server/selinux/SelinuxAuditLogsJob.java
index 0092c3797156..e55e5900f265 100644
--- a/services/core/java/com/android/server/selinux/SelinuxAuditLogsJob.java
+++ b/services/core/java/com/android/server/selinux/SelinuxAuditLogsJob.java
@@ -51,8 +51,12 @@ final class SelinuxAuditLogsJob {
return;
}
mIsRunning.set(true);
- boolean done = mAuditLogsCollector.collect(SelinuxAuditLogsService.AUDITD_TAG_CODE);
- if (done) {
+ try {
+ boolean done = mAuditLogsCollector.collect(SelinuxAuditLogsService.AUDITD_TAG_CODE);
+ if (done) {
+ jobService.jobFinished(params, /* wantsReschedule= */ false);
+ }
+ } catch (QuotaExceededException e) {
jobService.jobFinished(params, /* wantsReschedule= */ false);
}
mIsRunning.set(false);
diff --git a/services/core/java/com/android/server/statusbar/StatusBarManagerService.java b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
index a19a3422af06..b7b4cc0b6861 100644
--- a/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
+++ b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
@@ -95,7 +95,6 @@ import android.view.WindowInsets;
import android.view.WindowInsets.Type.InsetsType;
import android.view.WindowInsetsController.Appearance;
import android.view.WindowInsetsController.Behavior;
-import android.view.accessibility.Flags;
import com.android.internal.R;
import com.android.internal.annotations.GuardedBy;
@@ -981,16 +980,12 @@ public class StatusBarManagerService extends IStatusBarService.Stub implements D
@Override
public void addQsTileToFrontOrEnd(ComponentName tile, boolean end) {
- if (Flags.a11yQsShortcut()) {
- StatusBarManagerService.this.addQsTileToFrontOrEnd(tile, end);
- }
+ StatusBarManagerService.this.addQsTileToFrontOrEnd(tile, end);
}
@Override
public void removeQsTile(ComponentName tile) {
- if (Flags.a11yQsShortcut()) {
- StatusBarManagerService.this.remTile(tile);
- }
+ StatusBarManagerService.this.remTile(tile);
}
};
@@ -1098,19 +1093,7 @@ public class StatusBarManagerService extends IStatusBarService.Stub implements D
}
public void addTile(ComponentName component) {
- if (Flags.a11yQsShortcut()) {
- addQsTileToFrontOrEnd(component, false);
- } else {
- enforceStatusBarOrShell();
- enforceValidCallingUser();
-
- if (mBar != null) {
- try {
- mBar.addQsTile(component);
- } catch (RemoteException ex) {
- }
- }
- }
+ addQsTileToFrontOrEnd(component, false);
}
private void addQsTileToFrontOrEnd(ComponentName tile, boolean end) {
diff --git a/services/core/java/com/android/server/timezonedetector/Environment.java b/services/core/java/com/android/server/timezonedetector/Environment.java
new file mode 100644
index 000000000000..795fb02373ff
--- /dev/null
+++ b/services/core/java/com/android/server/timezonedetector/Environment.java
@@ -0,0 +1,80 @@
+/*
+ * Copyright 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.timezonedetector;
+
+import android.annotation.CurrentTimeMillisLong;
+import android.annotation.ElapsedRealtimeLong;
+import android.annotation.NonNull;
+
+import com.android.server.SystemTimeZone;
+
+import java.io.PrintWriter;
+
+/**
+ * Used by the time zone detector code to interact with device state besides that available from
+ * {@link ServiceConfigAccessor}. It can be faked for testing.
+ */
+public interface Environment {
+
+ /**
+ * Returns the device's currently configured time zone. May return an empty string.
+ */
+ @NonNull
+ String getDeviceTimeZone();
+
+ /**
+ * Returns the confidence of the device's current time zone.
+ */
+ @SystemTimeZone.TimeZoneConfidence
+ int getDeviceTimeZoneConfidence();
+
+ /**
+ * Sets the device's time zone, associated confidence, and records a debug log entry.
+ */
+ void setDeviceTimeZoneAndConfidence(
+ @NonNull String zoneId, @SystemTimeZone.TimeZoneConfidence int confidence,
+ @NonNull String logInfo);
+
+ /**
+ * Returns the time according to the elapsed realtime clock, the same as {@link
+ * android.os.SystemClock#elapsedRealtime()}.
+ */
+ @ElapsedRealtimeLong
+ long elapsedRealtimeMillis();
+
+ /**
+ * Returns the current time in milliseconds, the same as
+ * {@link java.lang.System#currentTimeMillis()}.
+ */
+ @CurrentTimeMillisLong
+ long currentTimeMillis();
+
+ /**
+ * Adds a standalone entry to the time zone debug log.
+ */
+ void addDebugLogEntry(@NonNull String logMsg);
+
+ /**
+ * Dumps the time zone debug log to the supplied {@link PrintWriter}.
+ */
+ void dumpDebugLog(PrintWriter printWriter);
+
+ /**
+ * Requests that the supplied runnable be invoked asynchronously.
+ */
+ void runAsync(@NonNull Runnable runnable);
+}
diff --git a/services/core/java/com/android/server/timezonedetector/EnvironmentImpl.java b/services/core/java/com/android/server/timezonedetector/EnvironmentImpl.java
index 449b41a09c51..8491b4818c2e 100644
--- a/services/core/java/com/android/server/timezonedetector/EnvironmentImpl.java
+++ b/services/core/java/com/android/server/timezonedetector/EnvironmentImpl.java
@@ -16,6 +16,7 @@
package com.android.server.timezonedetector;
+import android.annotation.CurrentTimeMillisLong;
import android.annotation.ElapsedRealtimeLong;
import android.annotation.NonNull;
import android.os.Handler;
@@ -31,9 +32,9 @@ import java.io.PrintWriter;
import java.util.Objects;
/**
- * The real implementation of {@link TimeZoneDetectorStrategyImpl.Environment}.
+ * The real implementation of {@link Environment}.
*/
-final class EnvironmentImpl implements TimeZoneDetectorStrategyImpl.Environment {
+final class EnvironmentImpl implements Environment {
private static final String TIMEZONE_PROPERTY = "persist.sys.timezone";
@@ -69,6 +70,11 @@ final class EnvironmentImpl implements TimeZoneDetectorStrategyImpl.Environment
}
@Override
+ public @CurrentTimeMillisLong long currentTimeMillis() {
+ return System.currentTimeMillis();
+ }
+
+ @Override
public void addDebugLogEntry(@NonNull String logMsg) {
SystemTimeZone.addDebugLogEntry(logMsg);
}
diff --git a/services/core/java/com/android/server/timezonedetector/NotifyingTimeZoneChangeListener.java b/services/core/java/com/android/server/timezonedetector/NotifyingTimeZoneChangeListener.java
index 2e73829ca143..cf85a9a6a706 100644
--- a/services/core/java/com/android/server/timezonedetector/NotifyingTimeZoneChangeListener.java
+++ b/services/core/java/com/android/server/timezonedetector/NotifyingTimeZoneChangeListener.java
@@ -29,6 +29,7 @@ import static com.android.server.timezonedetector.TimeZoneDetectorStrategy.ORIGI
import android.annotation.DurationMillisLong;
import android.annotation.IntDef;
+import android.annotation.NonNull;
import android.annotation.RequiresPermission;
import android.annotation.UserIdInt;
import android.app.ActivityManagerInternal;
@@ -44,7 +45,6 @@ import android.icu.text.DateFormat;
import android.icu.text.SimpleDateFormat;
import android.icu.util.TimeZone;
import android.os.Handler;
-import android.os.SystemClock;
import android.os.UserHandle;
import android.util.IndentingPrintWriter;
import android.util.Log;
@@ -153,6 +153,9 @@ public class NotifyingTimeZoneChangeListener implements TimeZoneChangeListener {
}
};
+ @NonNull
+ private final Environment mEnvironment;
+
private final Object mConfigurationLock = new Object();
@GuardedBy("mConfigurationLock")
private ConfigurationInternal mConfigurationInternal;
@@ -170,12 +173,14 @@ public class NotifyingTimeZoneChangeListener implements TimeZoneChangeListener {
/** Create and initialise a new {@code TimeZoneChangeTrackerImpl} */
@RequiresPermission("android.permission.INTERACT_ACROSS_USERS_FULL")
public static NotifyingTimeZoneChangeListener create(Handler handler, Context context,
- ServiceConfigAccessor serviceConfigAccessor) {
+ ServiceConfigAccessor serviceConfigAccessor,
+ @NonNull Environment environment) {
NotifyingTimeZoneChangeListener changeTracker =
new NotifyingTimeZoneChangeListener(handler,
context,
serviceConfigAccessor,
- context.getSystemService(NotificationManager.class));
+ context.getSystemService(NotificationManager.class),
+ environment);
// Pretend there was an update to initialize configuration.
changeTracker.handleConfigurationUpdate();
@@ -184,9 +189,9 @@ public class NotifyingTimeZoneChangeListener implements TimeZoneChangeListener {
}
@VisibleForTesting
- NotifyingTimeZoneChangeListener(
- Handler handler, Context context, ServiceConfigAccessor serviceConfigAccessor,
- NotificationManager notificationManager) {
+ NotifyingTimeZoneChangeListener(Handler handler, Context context,
+ ServiceConfigAccessor serviceConfigAccessor, NotificationManager notificationManager,
+ @NonNull Environment environment) {
mHandler = Objects.requireNonNull(handler);
mContext = Objects.requireNonNull(context);
mServiceConfigAccessor = Objects.requireNonNull(serviceConfigAccessor);
@@ -194,6 +199,7 @@ public class NotifyingTimeZoneChangeListener implements TimeZoneChangeListener {
this::handleConfigurationUpdate);
mActivityManagerInternal = LocalServices.getService(ActivityManagerInternal.class);
mNotificationManager = notificationManager;
+ mEnvironment = Objects.requireNonNull(environment);
}
@RequiresPermission("android.permission.INTERACT_ACROSS_USERS_FULL")
@@ -420,7 +426,7 @@ public class NotifyingTimeZoneChangeListener implements TimeZoneChangeListener {
if (!changeEvent.getOldZoneId().equals(lastChangeEvent.getNewZoneId())) {
int changeEventId = mNextChangeEventId.getAndIncrement();
TimeZoneChangeEvent syntheticChangeEvent = new TimeZoneChangeEvent(
- SystemClock.elapsedRealtime(), System.currentTimeMillis(),
+ mEnvironment.elapsedRealtimeMillis(), mEnvironment.currentTimeMillis(),
ORIGIN_UNKNOWN, UserHandle.USER_NULL, lastChangeEvent.getNewZoneId(),
changeEvent.getOldZoneId(), 0, "Synthetic");
TimeZoneChangeRecord syntheticTrackedChangeEvent =
diff --git a/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorStrategyImpl.java b/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorStrategyImpl.java
index b2b06b0af5fa..042d81ab6885 100644
--- a/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorStrategyImpl.java
+++ b/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorStrategyImpl.java
@@ -25,7 +25,6 @@ import static android.app.timezonedetector.TelephonyTimeZoneSuggestion.QUALITY_S
import static com.android.server.SystemTimeZone.TIME_ZONE_CONFIDENCE_HIGH;
import static com.android.server.SystemTimeZone.TIME_ZONE_CONFIDENCE_LOW;
-import android.annotation.ElapsedRealtimeLong;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.RequiresPermission;
@@ -54,7 +53,6 @@ import com.android.server.SystemTimeZone.TimeZoneConfidence;
import com.android.server.flags.Flags;
import com.android.server.timezonedetector.ConfigurationInternal.DetectionMode;
-import java.io.PrintWriter;
import java.time.Duration;
import java.util.ArrayList;
import java.util.List;
@@ -67,55 +65,6 @@ import java.util.Objects;
*/
public final class TimeZoneDetectorStrategyImpl implements TimeZoneDetectorStrategy {
- /**
- * Used by {@link TimeZoneDetectorStrategyImpl} to interact with device state besides that
- * available from {@link #mServiceConfigAccessor}. It can be faked for testing.
- */
- @VisibleForTesting
- public interface Environment {
-
- /**
- * Returns the device's currently configured time zone. May return an empty string.
- */
- @NonNull
- String getDeviceTimeZone();
-
- /**
- * Returns the confidence of the device's current time zone.
- */
- @TimeZoneConfidence
- int getDeviceTimeZoneConfidence();
-
- /**
- * Sets the device's time zone, associated confidence, and records a debug log entry.
- */
- void setDeviceTimeZoneAndConfidence(
- @NonNull String zoneId, @TimeZoneConfidence int confidence,
- @NonNull String logInfo);
-
- /**
- * Returns the time according to the elapsed realtime clock, the same as {@link
- * android.os.SystemClock#elapsedRealtime()}.
- */
- @ElapsedRealtimeLong
- long elapsedRealtimeMillis();
-
- /**
- * Adds a standalone entry to the time zone debug log.
- */
- void addDebugLogEntry(@NonNull String logMsg);
-
- /**
- * Dumps the time zone debug log to the supplied {@link PrintWriter}.
- */
- void dumpDebugLog(PrintWriter printWriter);
-
- /**
- * Requests that the supplied runnable be invoked asynchronously.
- */
- void runAsync(@NonNull Runnable runnable);
- }
-
private static final String LOG_TAG = TimeZoneDetectorService.TAG;
private static final boolean DBG = TimeZoneDetectorService.DBG;
@@ -263,10 +212,10 @@ public final class TimeZoneDetectorStrategyImpl implements TimeZoneDetectorStrat
public static TimeZoneDetectorStrategyImpl create(
@NonNull Context context, @NonNull Handler handler,
@NonNull ServiceConfigAccessor serviceConfigAccessor) {
-
Environment environment = new EnvironmentImpl(handler);
TimeZoneChangeListener changeEventTracker =
- NotifyingTimeZoneChangeListener.create(handler, context, serviceConfigAccessor);
+ NotifyingTimeZoneChangeListener.create(handler, context, serviceConfigAccessor,
+ environment);
return new TimeZoneDetectorStrategyImpl(
serviceConfigAccessor, environment, changeEventTracker);
}
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index d31aed2aee37..dca65a183f18 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -90,10 +90,6 @@ import static android.content.pm.ActivityInfo.RESIZE_MODE_RESIZEABLE_VIA_SDK_VER
import static android.content.pm.ActivityInfo.RESIZE_MODE_UNRESIZEABLE;
import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_BEHIND;
import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSET;
-import static android.content.pm.ActivityInfo.SIZE_CHANGES_SUPPORTED_METADATA;
-import static android.content.pm.ActivityInfo.SIZE_CHANGES_SUPPORTED_OVERRIDE;
-import static android.content.pm.ActivityInfo.SIZE_CHANGES_UNSUPPORTED_METADATA;
-import static android.content.pm.ActivityInfo.SIZE_CHANGES_UNSUPPORTED_OVERRIDE;
import static android.content.res.Configuration.ASSETS_SEQ_UNDEFINED;
import static android.content.res.Configuration.EMPTY;
import static android.content.res.Configuration.ORIENTATION_LANDSCAPE;
@@ -1272,8 +1268,9 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
pw.println(prefix + "manifestMinAspectRatio="
+ info.getManifestMinAspectRatio());
}
- pw.println(prefix + "supportsSizeChanges="
- + ActivityInfo.sizeChangesSupportModeToString(supportsSizeChanges()));
+ pw.println(
+ prefix + "supportsSizeChanges=" + ActivityInfo.sizeChangesSupportModeToString(
+ mAppCompatController.getSizeCompatModePolicy().supportsSizeChanges()));
if (info.configChanges != 0) {
pw.println(prefix + "configChanges=0x" + Integer.toHexString(info.configChanges));
}
@@ -3232,7 +3229,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
return false;
}
// If the user preference respects aspect ratio, then it becomes non-resizable.
- return mAppCompatController.getAppCompatAspectRatioOverrides()
+ return mAppCompatController.getAspectRatioOverrides()
.userPreferenceCompatibleWithNonResizability();
}
@@ -6581,7 +6578,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
mTaskSupervisor.mStoppingActivities.remove(this);
if (getDisplayArea().allResumedActivitiesComplete()) {
// Construct the compat environment at a relatively stable state if needed.
- mAppCompatController.getAppCompatSizeCompatModePolicy().updateAppCompatDisplayInsets();
+ mAppCompatController.getSizeCompatModePolicy().updateAppCompatDisplayInsets();
mRootWindowContainer.executeAppTransitionForAllDisplay();
}
@@ -8222,7 +8219,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
!= getRequestedConfigurationOrientation(false /*forDisplay */)) {
// Do not change the requested configuration now, because this will be done when setting
// the orientation below with the new mAppCompatDisplayInsets
- mAppCompatController.getAppCompatSizeCompatModePolicy().clearSizeCompatModeAttributes();
+ mAppCompatController.getSizeCompatModePolicy().clearSizeCompatModeAttributes();
}
ProtoLog.v(WM_DEBUG_ORIENTATION,
"Setting requested orientation %s for %s",
@@ -8366,7 +8363,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
@Nullable
AppCompatDisplayInsets getAppCompatDisplayInsets() {
- return mAppCompatController.getAppCompatSizeCompatModePolicy().getAppCompatDisplayInsets();
+ return mAppCompatController.getSizeCompatModePolicy().getAppCompatDisplayInsets();
}
/**
@@ -8374,31 +8371,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
* density than its parent or its bounds don't fit in parent naturally.
*/
boolean inSizeCompatMode() {
- final AppCompatSizeCompatModePolicy scmPolicy = mAppCompatController
- .getAppCompatSizeCompatModePolicy();
- if (scmPolicy.isInSizeCompatModeForBounds()) {
- return true;
- }
- if (getAppCompatDisplayInsets() == null || !shouldCreateAppCompatDisplayInsets()
- // The orientation is different from parent when transforming.
- || isFixedRotationTransforming()) {
- return false;
- }
- final Rect appBounds = getConfiguration().windowConfiguration.getAppBounds();
- if (appBounds == null) {
- // The app bounds hasn't been computed yet.
- return false;
- }
- final WindowContainer parent = getParent();
- if (parent == null) {
- // The parent of detached Activity can be null.
- return false;
- }
- final Configuration parentConfig = parent.getConfiguration();
- // Although colorMode, screenLayout, smallestScreenWidthDp are also fixed, generally these
- // fields should be changed with density and bounds, so here only compares the most
- // significant field.
- return parentConfig.densityDpi != getConfiguration().densityDpi;
+ return mAppCompatController.getSizeCompatModePolicy().inSizeCompatMode();
}
/**
@@ -8412,67 +8385,12 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
* aspect ratio.
*/
boolean shouldCreateAppCompatDisplayInsets() {
- if (mAppCompatController.getAppCompatAspectRatioOverrides().hasFullscreenOverride()) {
- // If the user has forced the applications aspect ratio to be fullscreen, don't use size
- // compatibility mode in any situation. The user has been warned and therefore accepts
- // the risk of the application misbehaving.
- return false;
- }
- switch (supportsSizeChanges()) {
- case SIZE_CHANGES_SUPPORTED_METADATA:
- case SIZE_CHANGES_SUPPORTED_OVERRIDE:
- return false;
- case SIZE_CHANGES_UNSUPPORTED_OVERRIDE:
- return true;
- default:
- // Fall through
- }
- // Use root activity's info for tasks in multi-window mode, or fullscreen tasks in freeform
- // task display areas, to ensure visual consistency across activity launches and exits in
- // the same task.
- final TaskDisplayArea tda = getTaskDisplayArea();
- if (inMultiWindowMode() || (tda != null && tda.inFreeformWindowingMode())) {
- final ActivityRecord root = task != null ? task.getRootActivity() : null;
- if (root != null && root != this && !root.shouldCreateAppCompatDisplayInsets()) {
- // If the root activity doesn't use size compatibility mode, the activities above
- // are forced to be the same for consistent visual appearance.
- return false;
- }
- }
- return !isResizeable() && (info.isFixedOrientation() || hasFixedAspectRatio())
- // The configuration of non-standard type should be enforced by system.
- // {@link WindowConfiguration#ACTIVITY_TYPE_STANDARD} is set when this activity is
- // added to a task, but this function is called when resolving the launch params, at
- // which point, the activity type is still undefined if it will be standard.
- // For other non-standard types, the type is set in the constructor, so this should
- // not be a problem.
- && isActivityTypeStandardOrUndefined();
- }
-
- /**
- * Returns whether the activity supports size changes.
- */
- @ActivityInfo.SizeChangesSupportMode
- private int supportsSizeChanges() {
- final AppCompatResizeOverrides resizeOverrides = mAppCompatController.getResizeOverrides();
- if (resizeOverrides.shouldOverrideForceNonResizeApp()) {
- return SIZE_CHANGES_UNSUPPORTED_OVERRIDE;
- }
-
- if (info.supportsSizeChanges) {
- return SIZE_CHANGES_SUPPORTED_METADATA;
- }
-
- if (resizeOverrides.shouldOverrideForceResizeApp()) {
- return SIZE_CHANGES_SUPPORTED_OVERRIDE;
- }
-
- return SIZE_CHANGES_UNSUPPORTED_METADATA;
+ return mAppCompatController.getSizeCompatModePolicy().shouldCreateAppCompatDisplayInsets();
}
@Override
boolean hasSizeCompatBounds() {
- return mAppCompatController.getAppCompatSizeCompatModePolicy().hasSizeCompatBounds();
+ return mAppCompatController.getSizeCompatModePolicy().hasSizeCompatBounds();
}
@Override
@@ -8491,7 +8409,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
@Override
float getCompatScale() {
// We need to invoke {#getCompatScale()} only if the CompatScale is not available.
- return mAppCompatController.getAppCompatSizeCompatModePolicy()
+ return mAppCompatController.getSizeCompatModePolicy()
.getCompatScaleIfAvailable(ActivityRecord.super::getCompatScale);
}
@@ -8518,7 +8436,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
newParentConfiguration = mTmpConfig;
}
- mAppCompatController.getAppCompatAspectRatioPolicy().reset();
+ mAppCompatController.getAspectRatioPolicy().reset();
mIsEligibleForFixedOrientationLetterbox = false;
mResolveConfigHint.resolveTmpOverrides(mDisplayContent, newParentConfiguration,
isFixedRotationTransforming());
@@ -8549,15 +8467,15 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
// If activity in fullscreen mode is letterboxed because of fixed orientation then bounds
// are already calculated in resolveFixedOrientationConfiguration.
// Don't apply aspect ratio if app is overridden to fullscreen by device user/manufacturer.
- if (!mAppCompatController.getAppCompatAspectRatioPolicy()
+ if (!mAppCompatController.getAspectRatioPolicy()
.isLetterboxedForFixedOrientationAndAspectRatio()
- && !mAppCompatController.getAppCompatAspectRatioOverrides()
+ && !mAppCompatController.getAspectRatioOverrides()
.hasFullscreenOverride()) {
resolveAspectRatioRestriction(newParentConfiguration);
}
final AppCompatDisplayInsets appCompatDisplayInsets = getAppCompatDisplayInsets();
final AppCompatSizeCompatModePolicy scmPolicy =
- mAppCompatController.getAppCompatSizeCompatModePolicy();
+ mAppCompatController.getSizeCompatModePolicy();
if (appCompatDisplayInsets != null) {
scmPolicy.resolveSizeCompatModeConfiguration(newParentConfiguration,
appCompatDisplayInsets, mTmpBounds);
@@ -8586,7 +8504,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
// Fixed orientation letterboxing is possible on both large screen devices
// with ignoreOrientationRequest enabled and on phones in split screen even with
// ignoreOrientationRequest disabled.
- && (mAppCompatController.getAppCompatAspectRatioPolicy()
+ && (mAppCompatController.getAspectRatioPolicy()
.isLetterboxedForFixedOrientationAndAspectRatio()
// Limiting check for aspect ratio letterboxing to devices with enabled
// ignoreOrientationRequest. This avoids affecting phones where apps may
@@ -8595,7 +8513,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
// accurate on phones shouldn't make the big difference and is expected
// to be already well-tested by apps.
|| (isIgnoreOrientationRequest
- && mAppCompatController.getAppCompatAspectRatioPolicy().isAspectRatioApplied()))) {
+ && mAppCompatController.getAspectRatioPolicy().isAspectRatioApplied()))) {
// TODO(b/264034555): Use mDisplayContent to calculate smallestScreenWidthDp from all
// rotations and only re-calculate if parent bounds have non-orientation size change.
resolvedConfig.smallestScreenWidthDp =
@@ -8707,7 +8625,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
return mAppCompatController.getTransparentPolicy().getInheritedAppCompatState();
}
final AppCompatSizeCompatModePolicy scmPolicy = mAppCompatController
- .getAppCompatSizeCompatModePolicy();
+ .getSizeCompatModePolicy();
if (scmPolicy.isInSizeCompatModeForBounds()) {
return APP_COMPAT_STATE_CHANGED__STATE__LETTERBOXED_FOR_SIZE_COMPAT_MODE;
}
@@ -8715,13 +8633,13 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
// letterboxed for fixed orientation. Aspect ratio restrictions are also applied if
// present. But this doesn't return true when the activity is letterboxed only because
// of aspect ratio restrictions.
- if (mAppCompatController.getAppCompatAspectRatioPolicy()
- .isLetterboxedForFixedOrientationAndAspectRatio()) {
+ final AppCompatAspectRatioPolicy aspectRatioPolicy =
+ mAppCompatController.getAspectRatioPolicy();
+ if (aspectRatioPolicy.isLetterboxedForFixedOrientationAndAspectRatio()) {
return APP_COMPAT_STATE_CHANGED__STATE__LETTERBOXED_FOR_FIXED_ORIENTATION;
}
// Letterbox for limited aspect ratio.
- if (mAppCompatController.getAppCompatAspectRatioPolicy()
- .isLetterboxedForAspectRatioOnly()) {
+ if (aspectRatioPolicy.isLetterboxedForAspectRatioOnly()) {
return APP_COMPAT_STATE_CHANGED__STATE__LETTERBOXED_FOR_ASPECT_RATIO;
}
@@ -8745,7 +8663,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
return;
}
final AppCompatSizeCompatModePolicy scmPolicy =
- mAppCompatController.getAppCompatSizeCompatModePolicy();
+ mAppCompatController.getSizeCompatModePolicy();
final Rect screenResolvedBounds = scmPolicy.replaceResolvedBoundsIfNeeded(resolvedBounds);
final Rect parentAppBounds = mResolveConfigHint.mParentAppBoundsOverride;
final Rect parentBounds = newParentConfiguration.windowConfiguration.getBounds();
@@ -8843,7 +8761,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
final Configuration resolvedConfig = getResolvedOverrideConfiguration();
final Rect resolvedBounds = resolvedConfig.windowConfiguration.getBounds();
final AppCompatSizeCompatModePolicy scmPolicy =
- mAppCompatController.getAppCompatSizeCompatModePolicy();
+ mAppCompatController.getSizeCompatModePolicy();
return scmPolicy.replaceResolvedBoundsIfNeeded(resolvedBounds);
}
@@ -8997,7 +8915,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
}
final AppCompatDisplayInsets appCompatDisplayInsets = getAppCompatDisplayInsets();
final AppCompatSizeCompatModePolicy scmPolicy =
- mAppCompatController.getAppCompatSizeCompatModePolicy();
+ mAppCompatController.getSizeCompatModePolicy();
if (appCompatDisplayInsets != null
&& !appCompatDisplayInsets.mIsInFixedOrientationOrAspectRatioLetterbox) {
@@ -9048,8 +8966,10 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
final Rect prevResolvedBounds = new Rect(resolvedBounds);
resolvedBounds.set(containingBounds);
- mAppCompatController.getAppCompatAspectRatioPolicy()
- .applyDesiredAspectRatio(newParentConfig, parentBounds, resolvedBounds,
+ final AppCompatAspectRatioPolicy aspectRatioPolicy = mAppCompatController
+ .getAspectRatioPolicy();
+
+ aspectRatioPolicy.applyDesiredAspectRatio(newParentConfig, parentBounds, resolvedBounds,
containingBoundsWithInsets, containingBounds);
if (appCompatDisplayInsets != null) {
@@ -9078,8 +8998,8 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
// for comparison with size compat app bounds in {@link resolveSizeCompatModeConfiguration}.
mResolveConfigHint.mTmpCompatInsets = appCompatDisplayInsets;
computeConfigByResolveHint(getResolvedOverrideConfiguration(), newParentConfig);
- mAppCompatController.getAppCompatAspectRatioPolicy()
- .setLetterboxBoundsForFixedOrientationAndAspectRatio(new Rect(resolvedBounds));
+ aspectRatioPolicy.setLetterboxBoundsForFixedOrientationAndAspectRatio(
+ new Rect(resolvedBounds));
}
/**
@@ -9096,8 +9016,9 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
// Use tmp bounds to calculate aspect ratio so we can know whether the activity should use
// restricted size (resolved bounds may be the requested override bounds).
mTmpBounds.setEmpty();
- mAppCompatController.getAppCompatAspectRatioPolicy()
- .applyAspectRatioForLetterbox(mTmpBounds, parentAppBounds, parentBounds);
+ final AppCompatAspectRatioPolicy aspectRatioPolicy = mAppCompatController
+ .getAspectRatioPolicy();
+ aspectRatioPolicy.applyAspectRatioForLetterbox(mTmpBounds, parentAppBounds, parentBounds);
// If the out bounds is not empty, it means the activity cannot fill parent's app bounds,
// then they should be aligned later in #updateResolvedBoundsPosition()
if (!mTmpBounds.isEmpty()) {
@@ -9108,8 +9029,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
// restrict, the bounds should be the requested override bounds.
mResolveConfigHint.mTmpOverrideDisplayInfo = getFixedRotationTransformDisplayInfo();
computeConfigByResolveHint(resolvedConfig, newParentConfiguration);
- mAppCompatController.getAppCompatAspectRatioPolicy()
- .setLetterboxBoundsForAspectRatio(new Rect(resolvedBounds));
+ aspectRatioPolicy.setLetterboxBoundsForAspectRatio(new Rect(resolvedBounds));
}
}
@@ -9118,7 +9038,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
// TODO(b/268458693): Refactor configuration inheritance in case of translucent activities
final Rect superBounds = super.getBounds();
final AppCompatSizeCompatModePolicy scmPolicy =
- mAppCompatController.getAppCompatSizeCompatModePolicy();
+ mAppCompatController.getSizeCompatModePolicy();
return mAppCompatController.getTransparentPolicy().findOpaqueNotFinishingActivityBelow()
.map(ActivityRecord::getBounds)
.orElseGet(() -> scmPolicy.getAppSizeCompatBoundsIfAvailable(superBounds));
@@ -9359,18 +9279,11 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
* Returns the min aspect ratio of this activity.
*/
float getMinAspectRatio() {
- return mAppCompatController.getAppCompatAspectRatioPolicy().getMinAspectRatio();
+ return mAppCompatController.getAspectRatioPolicy().getMinAspectRatio();
}
float getMaxAspectRatio() {
- return mAppCompatController.getAppCompatAspectRatioPolicy().getMaxAspectRatio();
- }
-
- /**
- * Returns true if the activity has maximum or minimum aspect ratio.
- */
- private boolean hasFixedAspectRatio() {
- return getMaxAspectRatio() != 0 || getMinAspectRatio() != 0;
+ return mAppCompatController.getAspectRatioPolicy().getMaxAspectRatio();
}
/**
@@ -9452,7 +9365,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
if (mVisibleRequested) {
// Calling from here rather than resolveOverrideConfiguration to ensure that this is
// called after full config is updated in ConfigurationContainer#onConfigurationChanged.
- mAppCompatController.getAppCompatSizeCompatModePolicy().updateAppCompatDisplayInsets();
+ mAppCompatController.getSizeCompatModePolicy().updateAppCompatDisplayInsets();
}
// Short circuit: if the two full configurations are equal (the common case), then there is
@@ -9792,7 +9705,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
// Reset the existing override configuration so it can be updated according to the latest
// configuration.
- mAppCompatController.getAppCompatSizeCompatModePolicy().clearSizeCompatMode();
+ mAppCompatController.getSizeCompatModePolicy().clearSizeCompatMode();
if (!attachedToProcess()) {
return;
@@ -10222,29 +10135,27 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
proto.write(LAST_DROP_INPUT_MODE, mLastDropInputMode);
proto.write(OVERRIDE_ORIENTATION, getOverrideOrientation());
proto.write(SHOULD_SEND_COMPAT_FAKE_FOCUS, shouldSendCompatFakeFocus());
+ final AppCompatCameraOverrides cameraOverrides =
+ mAppCompatController.getAppCompatCameraOverrides();
proto.write(SHOULD_FORCE_ROTATE_FOR_CAMERA_COMPAT,
- mAppCompatController.getAppCompatCameraOverrides()
- .shouldForceRotateForCameraCompat());
+ cameraOverrides.shouldForceRotateForCameraCompat());
proto.write(SHOULD_REFRESH_ACTIVITY_FOR_CAMERA_COMPAT,
- mAppCompatController.getAppCompatCameraOverrides()
- .shouldRefreshActivityForCameraCompat());
+ cameraOverrides.shouldRefreshActivityForCameraCompat());
proto.write(SHOULD_REFRESH_ACTIVITY_VIA_PAUSE_FOR_CAMERA_COMPAT,
- mAppCompatController.getAppCompatCameraOverrides()
- .shouldRefreshActivityViaPauseForCameraCompat());
+ cameraOverrides.shouldRefreshActivityViaPauseForCameraCompat());
+ final AppCompatAspectRatioOverrides aspectRatioOverrides =
+ mAppCompatController.getAspectRatioOverrides();
proto.write(SHOULD_OVERRIDE_MIN_ASPECT_RATIO,
- mAppCompatController.getAppCompatAspectRatioOverrides()
- .shouldOverrideMinAspectRatio());
+ aspectRatioOverrides.shouldOverrideMinAspectRatio());
proto.write(SHOULD_IGNORE_ORIENTATION_REQUEST_LOOP,
mAppCompatController.getOrientationOverrides()
.shouldIgnoreOrientationRequestLoop());
proto.write(SHOULD_OVERRIDE_FORCE_RESIZE_APP,
mAppCompatController.getResizeOverrides().shouldOverrideForceResizeApp());
proto.write(SHOULD_ENABLE_USER_ASPECT_RATIO_SETTINGS,
- mAppCompatController.getAppCompatAspectRatioOverrides()
- .shouldEnableUserAspectRatioSettings());
+ aspectRatioOverrides.shouldEnableUserAspectRatioSettings());
proto.write(IS_USER_FULLSCREEN_OVERRIDE_ENABLED,
- mAppCompatController.getAppCompatAspectRatioOverrides()
- .isUserFullscreenOverrideEnabled());
+ aspectRatioOverrides.isUserFullscreenOverrideEnabled());
}
@Override
diff --git a/services/core/java/com/android/server/wm/ActivityStarter.java b/services/core/java/com/android/server/wm/ActivityStarter.java
index 0ab2ffe3e298..bdbd0d15d982 100644
--- a/services/core/java/com/android/server/wm/ActivityStarter.java
+++ b/services/core/java/com/android/server/wm/ActivityStarter.java
@@ -1964,7 +1964,6 @@ class ActivityStarter {
if (mLastStartActivityRecord != null) {
targetTaskTop.mLaunchSourceType = mLastStartActivityRecord.mLaunchSourceType;
}
- targetTaskTop.mTransitionController.collect(targetTaskTop);
recordTransientLaunchIfNeeded(targetTaskTop);
// Recycle the target task for this launch.
startResult =
diff --git a/services/core/java/com/android/server/wm/AppCompatAspectRatioPolicy.java b/services/core/java/com/android/server/wm/AppCompatAspectRatioPolicy.java
index 6a0de98c0ffa..4ecd0bec9880 100644
--- a/services/core/java/com/android/server/wm/AppCompatAspectRatioPolicy.java
+++ b/services/core/java/com/android/server/wm/AppCompatAspectRatioPolicy.java
@@ -76,7 +76,7 @@ class AppCompatAspectRatioPolicy {
private float getDesiredAspectRatio(@NonNull Configuration newParentConfig,
@NonNull Rect parentBounds) {
final float letterboxAspectRatioOverride =
- mAppCompatOverrides.getAppCompatAspectRatioOverrides()
+ mAppCompatOverrides.getAspectRatioOverrides()
.getFixedOrientationLetterboxAspectRatio(newParentConfig);
// Aspect ratio as suggested by the system. Apps requested mix/max aspect ratio will
// be respected in #applyAspectRatio.
@@ -127,7 +127,7 @@ class AppCompatAspectRatioPolicy {
}
final AppCompatAspectRatioOverrides aspectRatioOverrides =
- mAppCompatOverrides.getAppCompatAspectRatioOverrides();
+ mAppCompatOverrides.getAspectRatioOverrides();
if (aspectRatioOverrides.shouldApplyUserMinAspectRatioOverride()) {
return aspectRatioOverrides.getUserMinAspectRatio();
}
@@ -215,6 +215,13 @@ class AppCompatAspectRatioPolicy {
mAppCompatAspectRatioState.mLetterboxBoundsForAspectRatio = bounds;
}
+ /**
+ * Returns true if the activity has maximum or minimum aspect ratio.
+ */
+ boolean hasFixedAspectRatio() {
+ return getMaxAspectRatio() != 0 || getMinAspectRatio() != 0;
+ }
+
private boolean isParentFullscreenPortrait() {
final WindowContainer<?> parent = mActivityRecord.getParent();
return parent != null
diff --git a/services/core/java/com/android/server/wm/AppCompatController.java b/services/core/java/com/android/server/wm/AppCompatController.java
index 6d0e8eacd438..1a6c9a1a8797 100644
--- a/services/core/java/com/android/server/wm/AppCompatController.java
+++ b/services/core/java/com/android/server/wm/AppCompatController.java
@@ -31,11 +31,11 @@ class AppCompatController {
@NonNull
private final AppCompatOrientationPolicy mOrientationPolicy;
@NonNull
- private final AppCompatAspectRatioPolicy mAppCompatAspectRatioPolicy;
+ private final AppCompatAspectRatioPolicy mAspectRatioPolicy;
@NonNull
private final AppCompatReachabilityPolicy mReachabilityPolicy;
@NonNull
- private final DesktopAppCompatAspectRatioPolicy mDesktopAppCompatAspectRatioPolicy;
+ private final DesktopAppCompatAspectRatioPolicy mDesktopAspectRatioPolicy;
@NonNull
private final AppCompatOverrides mAppCompatOverrides;
@NonNull
@@ -43,7 +43,7 @@ class AppCompatController {
@NonNull
private final AppCompatLetterboxPolicy mAppCompatLetterboxPolicy;
@NonNull
- private final AppCompatSizeCompatModePolicy mAppCompatSizeCompatModePolicy;
+ private final AppCompatSizeCompatModePolicy mSizeCompatModePolicy;
AppCompatController(@NonNull WindowManagerService wmService,
@NonNull ActivityRecord activityRecord) {
@@ -56,15 +56,15 @@ class AppCompatController {
mAppCompatOverrides = new AppCompatOverrides(activityRecord, packageManager,
wmService.mAppCompatConfiguration, optPropBuilder, mAppCompatDeviceStateQuery);
mOrientationPolicy = new AppCompatOrientationPolicy(activityRecord, mAppCompatOverrides);
- mAppCompatAspectRatioPolicy = new AppCompatAspectRatioPolicy(activityRecord,
+ mAspectRatioPolicy = new AppCompatAspectRatioPolicy(activityRecord,
mTransparentPolicy, mAppCompatOverrides);
mReachabilityPolicy = new AppCompatReachabilityPolicy(activityRecord,
wmService.mAppCompatConfiguration);
mAppCompatLetterboxPolicy = new AppCompatLetterboxPolicy(activityRecord,
wmService.mAppCompatConfiguration);
- mDesktopAppCompatAspectRatioPolicy = new DesktopAppCompatAspectRatioPolicy(activityRecord,
+ mDesktopAspectRatioPolicy = new DesktopAppCompatAspectRatioPolicy(activityRecord,
mAppCompatOverrides, mTransparentPolicy, wmService.mAppCompatConfiguration);
- mAppCompatSizeCompatModePolicy = new AppCompatSizeCompatModePolicy(activityRecord,
+ mSizeCompatModePolicy = new AppCompatSizeCompatModePolicy(activityRecord,
mAppCompatOverrides);
}
@@ -79,13 +79,13 @@ class AppCompatController {
}
@NonNull
- AppCompatAspectRatioPolicy getAppCompatAspectRatioPolicy() {
- return mAppCompatAspectRatioPolicy;
+ AppCompatAspectRatioPolicy getAspectRatioPolicy() {
+ return mAspectRatioPolicy;
}
@NonNull
- DesktopAppCompatAspectRatioPolicy getDesktopAppCompatAspectRatioPolicy() {
- return mDesktopAppCompatAspectRatioPolicy;
+ DesktopAppCompatAspectRatioPolicy getDesktopAspectRatioPolicy() {
+ return mDesktopAspectRatioPolicy;
}
@NonNull
@@ -99,8 +99,8 @@ class AppCompatController {
}
@NonNull
- AppCompatAspectRatioOverrides getAppCompatAspectRatioOverrides() {
- return mAppCompatOverrides.getAppCompatAspectRatioOverrides();
+ AppCompatAspectRatioOverrides getAspectRatioOverrides() {
+ return mAppCompatOverrides.getAspectRatioOverrides();
}
@NonNull
@@ -139,14 +139,14 @@ class AppCompatController {
}
@NonNull
- AppCompatSizeCompatModePolicy getAppCompatSizeCompatModePolicy() {
- return mAppCompatSizeCompatModePolicy;
+ AppCompatSizeCompatModePolicy getSizeCompatModePolicy() {
+ return mSizeCompatModePolicy;
}
void dump(@NonNull PrintWriter pw, @NonNull String prefix) {
getTransparentPolicy().dump(pw, prefix);
getAppCompatLetterboxPolicy().dump(pw, prefix);
- getAppCompatSizeCompatModePolicy().dump(pw, prefix);
+ getSizeCompatModePolicy().dump(pw, prefix);
}
}
diff --git a/services/core/java/com/android/server/wm/AppCompatOrientationOverrides.java b/services/core/java/com/android/server/wm/AppCompatOrientationOverrides.java
index af83668f1188..a49bec0ba2f3 100644
--- a/services/core/java/com/android/server/wm/AppCompatOrientationOverrides.java
+++ b/services/core/java/com/android/server/wm/AppCompatOrientationOverrides.java
@@ -144,7 +144,7 @@ class AppCompatOrientationOverrides {
mOrientationOverridesState.updateOrientationRequestLoopState();
return mOrientationOverridesState.shouldIgnoreRequestInLoop()
- && !mActivityRecord.mAppCompatController.getAppCompatAspectRatioPolicy()
+ && !mActivityRecord.mAppCompatController.getAspectRatioPolicy()
.isLetterboxedForFixedOrientationAndAspectRatio();
}
diff --git a/services/core/java/com/android/server/wm/AppCompatOrientationPolicy.java b/services/core/java/com/android/server/wm/AppCompatOrientationPolicy.java
index fc758ef90995..6202f8070dd4 100644
--- a/services/core/java/com/android/server/wm/AppCompatOrientationPolicy.java
+++ b/services/core/java/com/android/server/wm/AppCompatOrientationPolicy.java
@@ -55,7 +55,7 @@ class AppCompatOrientationPolicy {
@ActivityInfo.ScreenOrientation
int overrideOrientationIfNeeded(@ActivityInfo.ScreenOrientation int candidate) {
final AppCompatAspectRatioOverrides aspectRatioOverrides =
- mAppCompatOverrides.getAppCompatAspectRatioOverrides();
+ mAppCompatOverrides.getAspectRatioOverrides();
// Ignore all orientation requests of activities for eligible virtual displays.
if (aspectRatioOverrides.shouldIgnoreActivitySizeRestrictionsForDisplay()) {
return SCREEN_ORIENTATION_USER;
diff --git a/services/core/java/com/android/server/wm/AppCompatOverrides.java b/services/core/java/com/android/server/wm/AppCompatOverrides.java
index 9fb54db23d55..f8002a589eef 100644
--- a/services/core/java/com/android/server/wm/AppCompatOverrides.java
+++ b/services/core/java/com/android/server/wm/AppCompatOverrides.java
@@ -31,7 +31,7 @@ public class AppCompatOverrides {
@NonNull
private final AppCompatCameraOverrides mAppCompatCameraOverrides;
@NonNull
- private final AppCompatAspectRatioOverrides mAppCompatAspectRatioOverrides;
+ private final AppCompatAspectRatioOverrides mAspectRatioOverrides;
@NonNull
private final AppCompatFocusOverrides mAppCompatFocusOverrides;
@NonNull
@@ -52,7 +52,7 @@ public class AppCompatOverrides {
appCompatConfiguration, optPropBuilder, mAppCompatCameraOverrides);
mReachabilityOverrides = new AppCompatReachabilityOverrides(activityRecord,
appCompatConfiguration, appCompatDeviceStateQuery);
- mAppCompatAspectRatioOverrides = new AppCompatAspectRatioOverrides(activityRecord,
+ mAspectRatioOverrides = new AppCompatAspectRatioOverrides(activityRecord,
appCompatConfiguration, optPropBuilder, appCompatDeviceStateQuery,
mReachabilityOverrides);
mAppCompatFocusOverrides = new AppCompatFocusOverrides(activityRecord,
@@ -74,8 +74,8 @@ public class AppCompatOverrides {
}
@NonNull
- AppCompatAspectRatioOverrides getAppCompatAspectRatioOverrides() {
- return mAppCompatAspectRatioOverrides;
+ AppCompatAspectRatioOverrides getAspectRatioOverrides() {
+ return mAspectRatioOverrides;
}
@NonNull
diff --git a/services/core/java/com/android/server/wm/AppCompatSizeCompatModePolicy.java b/services/core/java/com/android/server/wm/AppCompatSizeCompatModePolicy.java
index d278dc3d1be7..f48ef4fa8f5f 100644
--- a/services/core/java/com/android/server/wm/AppCompatSizeCompatModePolicy.java
+++ b/services/core/java/com/android/server/wm/AppCompatSizeCompatModePolicy.java
@@ -18,6 +18,10 @@ package com.android.server.wm;
import static android.app.WindowConfiguration.ROTATION_UNDEFINED;
import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
+import static android.content.pm.ActivityInfo.SIZE_CHANGES_SUPPORTED_METADATA;
+import static android.content.pm.ActivityInfo.SIZE_CHANGES_SUPPORTED_OVERRIDE;
+import static android.content.pm.ActivityInfo.SIZE_CHANGES_UNSUPPORTED_METADATA;
+import static android.content.pm.ActivityInfo.SIZE_CHANGES_UNSUPPORTED_OVERRIDE;
import static android.content.res.Configuration.ORIENTATION_UNDEFINED;
import static com.android.server.wm.DesktopModeHelper.canEnterDesktopMode;
@@ -82,7 +86,8 @@ class AppCompatSizeCompatModePolicy {
}
/**
- * @return The {@code true} if the current instance has {@link #mAppCompatDisplayInsets} without
+ * @return The {@code true} if the current instance has
+ * {@link AppCompatSizeCompatModePolicy#mAppCompatDisplayInsets} without
* considering the inheritance implemented in {@link #getAppCompatDisplayInsets()}
*/
boolean hasAppCompatDisplayInsetsWithoutInheritance() {
@@ -199,7 +204,7 @@ class AppCompatSizeCompatModePolicy {
// activity will be displayed within them even if it is in size compat mode. They should be
// saved here before resolved bounds are overridden below.
final AppCompatAspectRatioPolicy aspectRatioPolicy = mActivityRecord.mAppCompatController
- .getAppCompatAspectRatioPolicy();
+ .getAspectRatioPolicy();
final boolean useResolvedBounds = aspectRatioPolicy.isAspectRatioApplied();
final Rect containerBounds = useResolvedBounds
? new Rect(resolvedBounds)
@@ -244,8 +249,7 @@ class AppCompatSizeCompatModePolicy {
resolvedBounds.set(containingBounds);
// The size of floating task is fixed (only swap), so the aspect ratio is already correct.
if (!appCompatDisplayInsets.mIsFloating) {
- mActivityRecord.mAppCompatController.getAppCompatAspectRatioPolicy()
- .applyAspectRatioForLetterbox(resolvedBounds, containingAppBounds,
+ aspectRatioPolicy.applyAspectRatioForLetterbox(resolvedBounds, containingAppBounds,
containingBounds);
}
@@ -359,7 +363,7 @@ class AppCompatSizeCompatModePolicy {
}
final Rect letterboxedContainerBounds = mActivityRecord.mAppCompatController
- .getAppCompatAspectRatioPolicy().getLetterboxedContainerBounds();
+ .getAspectRatioPolicy().getLetterboxedContainerBounds();
// The role of AppCompatDisplayInsets is like the override bounds.
mAppCompatDisplayInsets =
@@ -368,6 +372,112 @@ class AppCompatSizeCompatModePolicy {
.mUseOverrideInsetsForConfig);
}
+ /**
+ * @return {@code true} if this activity is in size compatibility mode that uses the different
+ * density than its parent or its bounds don't fit in parent naturally.
+ */
+ boolean inSizeCompatMode() {
+ if (isInSizeCompatModeForBounds()) {
+ return true;
+ }
+ if (getAppCompatDisplayInsets() == null || !shouldCreateAppCompatDisplayInsets()
+ // The orientation is different from parent when transforming.
+ || mActivityRecord.isFixedRotationTransforming()) {
+ return false;
+ }
+ final Rect appBounds = mActivityRecord.getConfiguration().windowConfiguration
+ .getAppBounds();
+ if (appBounds == null) {
+ // The app bounds hasn't been computed yet.
+ return false;
+ }
+ final WindowContainer<?> parent = mActivityRecord.getParent();
+ if (parent == null) {
+ // The parent of detached Activity can be null.
+ return false;
+ }
+ final Configuration parentConfig = parent.getConfiguration();
+ // Although colorMode, screenLayout, smallestScreenWidthDp are also fixed, generally these
+ // fields should be changed with density and bounds, so here only compares the most
+ // significant field.
+ return parentConfig.densityDpi != mActivityRecord.getConfiguration().densityDpi;
+ }
+
+ /**
+ * Indicates the activity will keep the bounds and screen configuration when it was first
+ * launched, no matter how its parent changes.
+ *
+ * <p>If {@true}, then {@link AppCompatDisplayInsets} will be created in {@link
+ * ActivityRecord#resolveOverrideConfiguration} to "freeze" activity bounds and insets.
+ *
+ * @return {@code true} if this activity is declared as non-resizable and fixed orientation or
+ * aspect ratio.
+ */
+ boolean shouldCreateAppCompatDisplayInsets() {
+ if (mActivityRecord.mAppCompatController.getAspectRatioOverrides()
+ .hasFullscreenOverride()) {
+ // If the user has forced the applications aspect ratio to be fullscreen, don't use size
+ // compatibility mode in any situation. The user has been warned and therefore accepts
+ // the risk of the application misbehaving.
+ return false;
+ }
+ switch (supportsSizeChanges()) {
+ case SIZE_CHANGES_SUPPORTED_METADATA:
+ case SIZE_CHANGES_SUPPORTED_OVERRIDE:
+ return false;
+ case SIZE_CHANGES_UNSUPPORTED_OVERRIDE:
+ return true;
+ default:
+ // Fall through
+ }
+ // Use root activity's info for tasks in multi-window mode, or fullscreen tasks in freeform
+ // task display areas, to ensure visual consistency across activity launches and exits in
+ // the same task.
+ final TaskDisplayArea tda = mActivityRecord.getTaskDisplayArea();
+ if (mActivityRecord.inMultiWindowMode() || (tda != null && tda.inFreeformWindowingMode())) {
+ final Task task = mActivityRecord.getTask();
+ final ActivityRecord root = task != null ? task.getRootActivity() : null;
+ if (root != null && root != mActivityRecord
+ && !root.shouldCreateAppCompatDisplayInsets()) {
+ // If the root activity doesn't use size compatibility mode, the activities above
+ // are forced to be the same for consistent visual appearance.
+ return false;
+ }
+ }
+ final AppCompatAspectRatioPolicy aspectRatioPolicy = mActivityRecord.mAppCompatController
+ .getAspectRatioPolicy();
+ return !mActivityRecord.isResizeable() && (mActivityRecord.info.isFixedOrientation()
+ || aspectRatioPolicy.hasFixedAspectRatio())
+ // The configuration of non-standard type should be enforced by system.
+ // {@link WindowConfiguration#ACTIVITY_TYPE_STANDARD} is set when this activity is
+ // added to a task, but this function is called when resolving the launch params, at
+ // which point, the activity type is still undefined if it will be standard.
+ // For other non-standard types, the type is set in the constructor, so this should
+ // not be a problem.
+ && mActivityRecord.isActivityTypeStandardOrUndefined();
+ }
+
+ /**
+ * Returns whether the activity supports size changes.
+ */
+ @ActivityInfo.SizeChangesSupportMode
+ int supportsSizeChanges() {
+ final AppCompatResizeOverrides resizeOverrides = mAppCompatOverrides.getResizeOverrides();
+ if (resizeOverrides.shouldOverrideForceNonResizeApp()) {
+ return SIZE_CHANGES_UNSUPPORTED_OVERRIDE;
+ }
+
+ if (mActivityRecord.info.supportsSizeChanges) {
+ return SIZE_CHANGES_SUPPORTED_METADATA;
+ }
+
+ if (resizeOverrides.shouldOverrideForceResizeApp()) {
+ return SIZE_CHANGES_SUPPORTED_OVERRIDE;
+ }
+
+ return SIZE_CHANGES_UNSUPPORTED_METADATA;
+ }
+
private boolean isInSizeCompatModeForBounds(final @NonNull Rect appBounds,
final @NonNull Rect containerBounds) {
diff --git a/services/core/java/com/android/server/wm/AppCompatUtils.java b/services/core/java/com/android/server/wm/AppCompatUtils.java
index e28dddc496e1..1ab0868b37d1 100644
--- a/services/core/java/com/android/server/wm/AppCompatUtils.java
+++ b/services/core/java/com/android/server/wm/AppCompatUtils.java
@@ -156,7 +156,7 @@ final class AppCompatUtils {
.getAppCompatLetterboxOverrides().isLetterboxEducationEnabled());
final AppCompatAspectRatioOverrides aspectRatioOverrides =
- top.mAppCompatController.getAppCompatAspectRatioOverrides();
+ top.mAppCompatController.getAspectRatioOverrides();
appCompatTaskInfo.setUserFullscreenOverrideEnabled(
aspectRatioOverrides.shouldApplyUserFullscreenOverride());
appCompatTaskInfo.setSystemFullscreenOverrideEnabled(
@@ -206,7 +206,7 @@ final class AppCompatUtils {
appCompatTaskInfo.cameraCompatTaskInfo.freeformCameraCompatMode =
AppCompatCameraPolicy.getCameraCompatFreeformMode(top);
appCompatTaskInfo.setHasMinAspectRatioOverride(top.mAppCompatController
- .getDesktopAppCompatAspectRatioPolicy().hasMinAspectRatioOverride(task));
+ .getDesktopAspectRatioPolicy().hasMinAspectRatioOverride(task));
}
/**
@@ -222,7 +222,7 @@ final class AppCompatUtils {
return "SIZE_COMPAT_MODE";
}
final AppCompatAspectRatioPolicy aspectRatioPolicy = activityRecord.mAppCompatController
- .getAppCompatAspectRatioPolicy();
+ .getAspectRatioPolicy();
if (aspectRatioPolicy.isLetterboxedForFixedOrientationAndAspectRatio()) {
return "FIXED_ORIENTATION";
}
diff --git a/services/core/java/com/android/server/wm/BackNavigationController.java b/services/core/java/com/android/server/wm/BackNavigationController.java
index 37575f00363e..ab32e54b92dd 100644
--- a/services/core/java/com/android/server/wm/BackNavigationController.java
+++ b/services/core/java/com/android/server/wm/BackNavigationController.java
@@ -36,6 +36,7 @@ import static com.android.server.wm.BackNavigationProto.SHOW_WALLPAPER;
import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_PREDICT_BACK;
import static com.android.server.wm.WindowContainer.SYNC_STATE_NONE;
+import android.annotation.BinderThread;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.Context;
@@ -44,6 +45,7 @@ import android.content.res.ResourceId;
import android.graphics.Point;
import android.graphics.Rect;
import android.os.Bundle;
+import android.os.IBinder;
import android.os.RemoteCallback;
import android.os.RemoteException;
import android.os.SystemProperties;
@@ -705,12 +707,34 @@ class BackNavigationController {
private WindowState mNavigatingWindow;
private RemoteCallback mObserver;
+ private final IBinder.DeathRecipient mListenerDeathRecipient =
+ new IBinder.DeathRecipient() {
+ @Override
+ @BinderThread
+ public void binderDied() {
+ synchronized (mWindowManagerService.mGlobalLock) {
+ stopMonitorForRemote();
+ stopMonitorTransition();
+ }
+ }
+ };
+
void startMonitor(@NonNull WindowState window, @NonNull RemoteCallback observer) {
mNavigatingWindow = window;
mObserver = observer;
+ try {
+ mObserver.getInterface().asBinder().linkToDeath(mListenerDeathRecipient,
+ 0 /* flags */);
+ } catch (RemoteException r) {
+ Slog.e(TAG, "Failed to link to death");
+ }
}
void stopMonitorForRemote() {
+ if (mObserver != null) {
+ mObserver.getInterface().asBinder().unlinkToDeath(mListenerDeathRecipient,
+ 0 /* flags */);
+ }
mObserver = null;
}
diff --git a/services/core/java/com/android/server/wm/DesktopAppCompatAspectRatioPolicy.java b/services/core/java/com/android/server/wm/DesktopAppCompatAspectRatioPolicy.java
index 43855aa3d247..fee5566af484 100644
--- a/services/core/java/com/android/server/wm/DesktopAppCompatAspectRatioPolicy.java
+++ b/services/core/java/com/android/server/wm/DesktopAppCompatAspectRatioPolicy.java
@@ -189,7 +189,7 @@ public class DesktopAppCompatAspectRatioPolicy {
final ActivityInfo info = mActivityRecord.info;
final AppCompatAspectRatioOverrides aspectRatioOverrides =
- mAppCompatOverrides.getAppCompatAspectRatioOverrides();
+ mAppCompatOverrides.getAspectRatioOverrides();
if (shouldApplyUserMinAspectRatioOverride(task)) {
return aspectRatioOverrides.getUserMinAspectRatio();
}
@@ -266,7 +266,7 @@ public class DesktopAppCompatAspectRatioPolicy {
return false;
}
- final int userAspectRatioCode = mAppCompatOverrides.getAppCompatAspectRatioOverrides()
+ final int userAspectRatioCode = mAppCompatOverrides.getAspectRatioOverrides()
.getUserMinAspectRatioOverrideCode();
return userAspectRatioCode != USER_MIN_ASPECT_RATIO_UNSET
@@ -281,7 +281,7 @@ public class DesktopAppCompatAspectRatioPolicy {
// We use mBooleanPropertyAllowUserAspectRatioOverride to allow apps to opt-out which has
// effect only if explicitly false. If mBooleanPropertyAllowUserAspectRatioOverride is null,
// the current app doesn't opt-out so the first part of the predicate is true.
- return mAppCompatOverrides.getAppCompatAspectRatioOverrides()
+ return mAppCompatOverrides.getAspectRatioOverrides()
.getAllowUserAspectRatioOverridePropertyValue()
&& mAppCompatConfiguration.isUserAppAspectRatioSettingsEnabled()
&& task.mDisplayContent.getIgnoreOrientationRequest();
diff --git a/services/core/java/com/android/server/wm/DesktopModeBoundsCalculator.java b/services/core/java/com/android/server/wm/DesktopModeBoundsCalculator.java
index fcf88d395f1c..0106a64660fe 100644
--- a/services/core/java/com/android/server/wm/DesktopModeBoundsCalculator.java
+++ b/services/core/java/com/android/server/wm/DesktopModeBoundsCalculator.java
@@ -104,7 +104,7 @@ public final class DesktopModeBoundsCalculator {
if (!DesktopModeFlags.ENABLE_WINDOWING_DYNAMIC_INITIAL_BOUNDS.isTrue()) {
return centerInScreen(idealSize, screenBounds);
}
- if (activity.mAppCompatController.getAppCompatAspectRatioOverrides()
+ if (activity.mAppCompatController.getAspectRatioOverrides()
.hasFullscreenOverride()) {
// If the activity has a fullscreen override applied, it should be treated as
// resizeable and match the device orientation. Thus the ideal size can be
@@ -112,7 +112,7 @@ public final class DesktopModeBoundsCalculator {
return centerInScreen(idealSize, screenBounds);
}
final DesktopAppCompatAspectRatioPolicy desktopAppCompatAspectRatioPolicy =
- activity.mAppCompatController.getDesktopAppCompatAspectRatioPolicy();
+ activity.mAppCompatController.getDesktopAspectRatioPolicy();
float appAspectRatio = desktopAppCompatAspectRatioPolicy.calculateAspectRatio(task);
final float tdaWidth = stableBounds.width();
final float tdaHeight = stableBounds.height();
@@ -190,7 +190,7 @@ public final class DesktopModeBoundsCalculator {
@NonNull ActivityRecord activity, @NonNull Task task) {
final int activityOrientation = activity.getOverrideOrientation();
final DesktopAppCompatAspectRatioPolicy desktopAppCompatAspectRatioPolicy =
- activity.mAppCompatController.getDesktopAppCompatAspectRatioPolicy();
+ activity.mAppCompatController.getDesktopAspectRatioPolicy();
if (desktopAppCompatAspectRatioPolicy.shouldApplyUserMinAspectRatioOverride(task)
&& (!isFixedOrientation(activityOrientation)
|| activityOrientation == SCREEN_ORIENTATION_LOCKED)) {
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index d32c31f1c1c7..5435d8f164da 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -193,7 +193,6 @@ import android.os.Message;
import android.os.PowerManager;
import android.os.RemoteCallbackList;
import android.os.RemoteException;
-import android.os.SystemClock;
import android.os.Trace;
import android.os.UserHandle;
import android.os.UserManager;
@@ -2187,12 +2186,6 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
}
}
- /** Returns {@code true} if the screen rotation animation needs to wait for the window. */
- boolean shouldSyncRotationChange(WindowState w) {
- final AsyncRotationController controller = mAsyncRotationController;
- return controller == null || !controller.isAsync(w);
- }
-
void notifyInsetsChanged(Consumer<WindowState> dispatchInsetsChanged) {
if (mFixedRotationLaunchingApp != null) {
// The insets state of fixed rotation app is a rotated copy. Make sure the visibilities
@@ -2279,10 +2272,6 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
if (!shellTransitions) {
forAllWindows(w -> {
w.seamlesslyRotateIfAllowed(transaction, oldRotation, rotation, rotateSeamlessly);
- if (!rotateSeamlessly && w.mHasSurface) {
- ProtoLog.v(WM_DEBUG_ORIENTATION, "Set mOrientationChanging of %s", w);
- w.setOrientationChanging(true);
- }
}, true /* traverseTopToBottom */);
mPinnedTaskController.startSeamlessRotationIfNeeded(transaction, oldRotation, rotation);
if (!mDisplayRotation.hasSeamlessRotatingWindow()) {
@@ -5083,15 +5072,6 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
Slog.w(TAG_WM, "Window freeze timeout expired.");
mWmService.mWindowsFreezingScreen = WINDOWS_FREEZING_SCREENS_TIMEOUT;
- forAllWindows(w -> {
- if (!w.getOrientationChanging()) {
- return;
- }
- w.orientationChangeTimedOut();
- w.mLastFreezeDuration = (int)(SystemClock.elapsedRealtime()
- - mWmService.mDisplayFreezeTime);
- Slog.w(TAG_WM, "Force clearing orientation change: " + w);
- }, true /* traverseTopToBottom */);
mWmService.mWindowPlacerLocked.performSurfacePlacement();
}
diff --git a/services/core/java/com/android/server/wm/DragDropController.java b/services/core/java/com/android/server/wm/DragDropController.java
index db058cafe5fe..fa748d3a22a5 100644
--- a/services/core/java/com/android/server/wm/DragDropController.java
+++ b/services/core/java/com/android/server/wm/DragDropController.java
@@ -212,7 +212,7 @@ class DragDropController {
surface = null;
mDragState.mPid = callerPid;
mDragState.mUid = callerUid;
- mDragState.mOriginalAlpha = alpha;
+ mDragState.mStartDragAlpha = alpha;
mDragState.mAnimatedScale = callingWin.mGlobalScale;
mDragState.mToken = dragToken;
mDragState.mStartDragDisplayContent = displayContent;
@@ -287,7 +287,7 @@ class DragDropController {
}
final SurfaceControl.Transaction transaction = mDragState.mTransaction;
- transaction.setAlpha(surfaceControl, mDragState.mOriginalAlpha);
+ transaction.setAlpha(surfaceControl, mDragState.mStartDragAlpha);
transaction.show(surfaceControl);
displayContent.reparentToOverlay(transaction, surfaceControl);
mDragState.updateDragSurfaceLocked(true /* keepHandling */,
diff --git a/services/core/java/com/android/server/wm/DragState.java b/services/core/java/com/android/server/wm/DragState.java
index d48b9b4a5d10..69f32cb7b8ea 100644
--- a/services/core/java/com/android/server/wm/DragState.java
+++ b/services/core/java/com/android/server/wm/DragState.java
@@ -82,6 +82,7 @@ import java.util.concurrent.CompletableFuture;
class DragState {
private static final long MIN_ANIMATION_DURATION_MS = 195;
private static final long MAX_ANIMATION_DURATION_MS = 375;
+ private static final float DIFFERENT_DISPLAY_RETURN_ANIMATION_SCALE = 0.75f;
private static final int DRAG_FLAGS_URI_ACCESS = View.DRAG_FLAG_GLOBAL_URI_READ |
View.DRAG_FLAG_GLOBAL_URI_WRITE;
@@ -114,8 +115,9 @@ class DragState {
boolean mDragResult;
boolean mRelinquishDragSurfaceToDropTarget;
float mAnimatedScale = 1.0f;
- float mOriginalAlpha;
- float mOriginalDisplayX, mOriginalDisplayY;
+ float mStartDragAlpha;
+ // Coords are in display coordinates space.
+ float mStartDragDisplayX, mStartDragDisplayY;
float mCurrentDisplayX, mCurrentDisplayY;
float mThumbOffsetX, mThumbOffsetY;
InputInterceptor mInputInterceptor;
@@ -497,8 +499,8 @@ class DragState {
*/
void broadcastDragStartedLocked(final float touchX, final float touchY) {
Trace.instant(TRACE_TAG_WINDOW_MANAGER, "DragDropController#DRAG_STARTED");
- mOriginalDisplayX = mCurrentDisplayX = touchX;
- mOriginalDisplayY = mCurrentDisplayY = touchY;
+ mStartDragDisplayX = mCurrentDisplayX = touchX;
+ mStartDragDisplayY = mCurrentDisplayY = touchY;
// Cache a base-class instance of the clip metadata so that parceling
// works correctly in calling out to the apps.
@@ -809,21 +811,32 @@ class DragState {
mCurrentDisplayY),
PropertyValuesHolder.ofFloat(ANIMATED_PROPERTY_SCALE, mAnimatedScale,
mAnimatedScale),
- PropertyValuesHolder.ofFloat(ANIMATED_PROPERTY_ALPHA, mOriginalAlpha, 0f));
+ PropertyValuesHolder.ofFloat(ANIMATED_PROPERTY_ALPHA, mStartDragAlpha, 0f));
+ duration = MIN_ANIMATION_DURATION_MS;
+ } else if (Flags.enableConnectedDisplaysDnd() && mCurrentDisplayContent.getDisplayId()
+ != mStartDragDisplayContent.getDisplayId()) {
+ animator = ValueAnimator.ofPropertyValuesHolder(
+ PropertyValuesHolder.ofFloat(ANIMATED_PROPERTY_X,
+ mCurrentDisplayX - mThumbOffsetX, mCurrentDisplayX - mThumbOffsetX),
+ PropertyValuesHolder.ofFloat(ANIMATED_PROPERTY_Y,
+ mCurrentDisplayY - mThumbOffsetY, mCurrentDisplayY - mThumbOffsetY),
+ PropertyValuesHolder.ofFloat(ANIMATED_PROPERTY_SCALE, mAnimatedScale,
+ DIFFERENT_DISPLAY_RETURN_ANIMATION_SCALE * mAnimatedScale),
+ PropertyValuesHolder.ofFloat(ANIMATED_PROPERTY_ALPHA, mStartDragAlpha, 0f));
duration = MIN_ANIMATION_DURATION_MS;
} else {
animator = ValueAnimator.ofPropertyValuesHolder(
PropertyValuesHolder.ofFloat(ANIMATED_PROPERTY_X,
- mCurrentDisplayX - mThumbOffsetX, mOriginalDisplayX - mThumbOffsetX),
+ mCurrentDisplayX - mThumbOffsetX, mStartDragDisplayX - mThumbOffsetX),
PropertyValuesHolder.ofFloat(ANIMATED_PROPERTY_Y,
- mCurrentDisplayY - mThumbOffsetY, mOriginalDisplayY - mThumbOffsetY),
+ mCurrentDisplayY - mThumbOffsetY, mStartDragDisplayY - mThumbOffsetY),
PropertyValuesHolder.ofFloat(ANIMATED_PROPERTY_SCALE, mAnimatedScale,
mAnimatedScale),
- PropertyValuesHolder.ofFloat(ANIMATED_PROPERTY_ALPHA, mOriginalAlpha,
- mOriginalAlpha / 2));
+ PropertyValuesHolder.ofFloat(ANIMATED_PROPERTY_ALPHA, mStartDragAlpha,
+ mStartDragAlpha / 2));
- final float translateX = mOriginalDisplayX - mCurrentDisplayX;
- final float translateY = mOriginalDisplayY - mCurrentDisplayY;
+ final float translateX = mStartDragDisplayX - mCurrentDisplayX;
+ final float translateY = mStartDragDisplayY - mCurrentDisplayY;
// Adjust the duration to the travel distance.
final double travelDistance = Math.sqrt(
translateX * translateX + translateY * translateY);
@@ -853,7 +866,7 @@ class DragState {
mCurrentDisplayY),
PropertyValuesHolder.ofFloat(ANIMATED_PROPERTY_SCALE, mAnimatedScale,
mAnimatedScale),
- PropertyValuesHolder.ofFloat(ANIMATED_PROPERTY_ALPHA, mOriginalAlpha, 0f));
+ PropertyValuesHolder.ofFloat(ANIMATED_PROPERTY_ALPHA, mStartDragAlpha, 0f));
} else {
animator = ValueAnimator.ofPropertyValuesHolder(
PropertyValuesHolder.ofFloat(ANIMATED_PROPERTY_X,
@@ -861,7 +874,7 @@ class DragState {
PropertyValuesHolder.ofFloat(ANIMATED_PROPERTY_Y,
mCurrentDisplayY - mThumbOffsetY, mCurrentDisplayY),
PropertyValuesHolder.ofFloat(ANIMATED_PROPERTY_SCALE, mAnimatedScale, 0),
- PropertyValuesHolder.ofFloat(ANIMATED_PROPERTY_ALPHA, mOriginalAlpha, 0));
+ PropertyValuesHolder.ofFloat(ANIMATED_PROPERTY_ALPHA, mStartDragAlpha, 0));
}
final AnimationListener listener = new AnimationListener();
diff --git a/services/core/java/com/android/server/wm/LaunchParamsController.java b/services/core/java/com/android/server/wm/LaunchParamsController.java
index d3c3d2834124..ba1401ab3978 100644
--- a/services/core/java/com/android/server/wm/LaunchParamsController.java
+++ b/services/core/java/com/android/server/wm/LaunchParamsController.java
@@ -79,7 +79,8 @@ class LaunchParamsController {
* @param result The resulting params.
*/
void calculate(Task task, WindowLayout layout, ActivityRecord activity, ActivityRecord source,
- ActivityOptions options, @Nullable Request request, int phase, LaunchParams result) {
+ ActivityOptions options, @Nullable Request request,
+ @LaunchParamsModifier.Phase int phase, LaunchParams result) {
result.reset();
if (task != null || activity != null) {
diff --git a/services/core/java/com/android/server/wm/RootWindowContainer.java b/services/core/java/com/android/server/wm/RootWindowContainer.java
index 57fe0bb4937e..7a3eb67bf94e 100644
--- a/services/core/java/com/android/server/wm/RootWindowContainer.java
+++ b/services/core/java/com/android/server/wm/RootWindowContainer.java
@@ -152,7 +152,6 @@ import com.android.server.LocalServices;
import com.android.server.am.ActivityManagerService;
import com.android.server.am.AppTimeTracker;
import com.android.server.am.UserState;
-import com.android.server.display.feature.DisplayManagerFlags;
import com.android.server.pm.UserManagerInternal;
import com.android.server.policy.PermissionPolicyInternal;
import com.android.server.policy.WindowManagerPolicy;
@@ -206,7 +205,6 @@ class RootWindowContainer extends WindowContainer<DisplayContent>
// For seamless rotation cases this always stays true, as the windows complete their orientation
// changes 1 by 1 without disturbing global state.
boolean mOrientationChangeComplete = true;
- boolean mWallpaperActionPending = false;
private final Handler mHandler;
@@ -1101,10 +1099,6 @@ class RootWindowContainer extends WindowContainer<DisplayContent>
}
}
- if ((bulkUpdateParams & SET_WALLPAPER_ACTION_PENDING) != 0) {
- mWallpaperActionPending = true;
- }
-
return doRequest;
}
@@ -1541,20 +1535,18 @@ class RootWindowContainer extends WindowContainer<DisplayContent>
ActivityInfo aInfo = resolveHomeActivity(userId, homeIntent);
boolean lookForSecondaryHomeActivityInPrimaryHomePackage = aInfo != null;
- if (android.companion.virtual.flags.Flags.vdmCustomHome()) {
- // Resolve the externally set home activity for this display, if any. If it is unset or
- // we fail to resolve it, fallback to the default secondary home activity.
- final ComponentName customHomeComponent =
- taskDisplayArea.getDisplayContent() != null
- ? taskDisplayArea.getDisplayContent().getCustomHomeComponent()
- : null;
- if (customHomeComponent != null) {
- homeIntent.setComponent(customHomeComponent);
- ActivityInfo customHomeActivityInfo = resolveHomeActivity(userId, homeIntent);
- if (customHomeActivityInfo != null) {
- aInfo = customHomeActivityInfo;
- lookForSecondaryHomeActivityInPrimaryHomePackage = false;
- }
+ // Resolve the externally set home activity for this display, if any. If it is unset or
+ // we fail to resolve it, fallback to the default secondary home activity.
+ final ComponentName customHomeComponent =
+ taskDisplayArea.getDisplayContent() != null
+ ? taskDisplayArea.getDisplayContent().getCustomHomeComponent()
+ : null;
+ if (customHomeComponent != null) {
+ homeIntent.setComponent(customHomeComponent);
+ ActivityInfo customHomeActivityInfo = resolveHomeActivity(userId, homeIntent);
+ if (customHomeActivityInfo != null) {
+ aInfo = customHomeActivityInfo;
+ lookForSecondaryHomeActivityInPrimaryHomePackage = false;
}
}
diff --git a/services/core/java/com/android/server/wm/SnapshotPersistQueue.java b/services/core/java/com/android/server/wm/SnapshotPersistQueue.java
index 3eb13c52cca6..5e1d7928e96d 100644
--- a/services/core/java/com/android/server/wm/SnapshotPersistQueue.java
+++ b/services/core/java/com/android/server/wm/SnapshotPersistQueue.java
@@ -231,8 +231,13 @@ class SnapshotPersistQueue {
if (next.isReady(mUserManagerInternal)) {
isReadyToWrite = true;
next.onDequeuedLocked();
- } else {
+ } else if (!mShutdown) {
mWriteQueue.addLast(next);
+ } else {
+ // User manager is locked and device is shutting down, skip writing
+ // this item.
+ next.onDequeuedLocked();
+ next = null;
}
}
}
diff --git a/services/core/java/com/android/server/wm/TaskLaunchParamsModifier.java b/services/core/java/com/android/server/wm/TaskLaunchParamsModifier.java
index c39671d76929..e3a5b66b83fd 100644
--- a/services/core/java/com/android/server/wm/TaskLaunchParamsModifier.java
+++ b/services/core/java/com/android/server/wm/TaskLaunchParamsModifier.java
@@ -96,9 +96,10 @@ class TaskLaunchParamsModifier implements LaunchParamsModifier {
}
@Override
+ @Result
public int onCalculate(@Nullable Task task, @Nullable ActivityInfo.WindowLayout layout,
@Nullable ActivityRecord activity, @Nullable ActivityRecord source,
- @Nullable ActivityOptions options, @Nullable Request request, int phase,
+ @Nullable ActivityOptions options, @Nullable Request request, @Phase int phase,
LaunchParams currentParams, LaunchParams outParams) {
initLogBuilder(task, activity);
final int result = calculate(task, layout, activity, source, options, request, phase,
@@ -107,9 +108,10 @@ class TaskLaunchParamsModifier implements LaunchParamsModifier {
return result;
}
+ @Result
private int calculate(@Nullable Task task, @Nullable ActivityInfo.WindowLayout layout,
@Nullable ActivityRecord activity, @Nullable ActivityRecord source,
- @Nullable ActivityOptions options, @Nullable Request request, int phase,
+ @Nullable ActivityOptions options, @Nullable Request request, @Phase int phase,
LaunchParams currentParams, LaunchParams outParams) {
final ActivityRecord root;
if (task != null) {
diff --git a/services/core/java/com/android/server/wm/TransparentPolicy.java b/services/core/java/com/android/server/wm/TransparentPolicy.java
index edd99243c3ef..88ea0730ab00 100644
--- a/services/core/java/com/android/server/wm/TransparentPolicy.java
+++ b/services/core/java/com/android/server/wm/TransparentPolicy.java
@@ -204,7 +204,7 @@ class TransparentPolicy {
return true;
}
final AppCompatSizeCompatModePolicy scmPolicy = mActivityRecord.mAppCompatController
- .getAppCompatSizeCompatModePolicy();
+ .getSizeCompatModePolicy();
if (mActivityRecord.getTask() == null || mActivityRecord.fillsParent()
|| scmPolicy.hasAppCompatDisplayInsetsWithoutInheritance()) {
return true;
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index e4ef3d186bdb..bf23e757fee5 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -331,6 +331,7 @@ import android.window.WindowContextInfo;
import com.android.internal.R;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.annotations.VisibleForTesting.Visibility;
import com.android.internal.os.IResultReceiver;
import com.android.internal.os.TransferPipe;
import com.android.internal.policy.IKeyguardDismissCallback;
@@ -600,6 +601,7 @@ public class WindowManagerService extends IWindowManager.Stub
final boolean mLimitedAlphaCompositing;
final int mMaxUiWidth;
+ @NonNull
@VisibleForTesting
WindowManagerPolicy mPolicy;
@@ -1054,13 +1056,16 @@ public class WindowManagerService extends IWindowManager.Stub
private boolean mAnimationsDisabled = false;
boolean mPointerLocationEnabled = false;
+ @NonNull
final AppCompatConfiguration mAppCompatConfiguration;
private boolean mIsIgnoreOrientationRequestDisabled;
+ @NonNull
final InputManagerService mInputManager;
final DisplayManagerInternal mDisplayManagerInternal;
final DisplayManager mDisplayManager;
+ @NonNull
final ActivityTaskManagerService mAtmService;
/** Indicates whether this device supports wide color gamut / HDR rendering */
@@ -1116,7 +1121,9 @@ public class WindowManagerService extends IWindowManager.Stub
static WindowManagerThreadPriorityBooster sThreadPriorityBooster =
new WindowManagerThreadPriorityBooster();
+ @NonNull
Supplier<SurfaceControl.Builder> mSurfaceControlFactory;
+ @NonNull
Supplier<SurfaceControl.Transaction> mTransactionFactory;
private final SurfaceControl.Transaction mTransaction;
@@ -1188,9 +1195,11 @@ public class WindowManagerService extends IWindowManager.Stub
private volatile boolean mDisableSecureWindows = false;
- public static WindowManagerService main(final Context context, final InputManagerService im,
- final boolean showBootMsgs, WindowManagerPolicy policy,
- ActivityTaskManagerService atm) {
+ /** Creates an instance of the WindowManagerService for the system server. */
+ public static WindowManagerService main(@NonNull final Context context,
+ @NonNull final InputManagerService im, final boolean showBootMsgs,
+ @NonNull final WindowManagerPolicy policy,
+ @NonNull final ActivityTaskManagerService atm) {
// Using SysUI context to have access to Material colors extracted from Wallpaper.
final AppCompatConfiguration appCompat = new AppCompatConfiguration(
ActivityThread.currentActivityThread().getSystemUiContext());
@@ -1204,15 +1213,19 @@ public class WindowManagerService extends IWindowManager.Stub
/**
* Creates and returns an instance of the WindowManagerService. This call allows the caller
- * to override factories that can be used to stub native calls during test.
+ * to override factories that can be used to stub native calls during test. Tests should use
+ * {@link WindowManagerServiceTestSupport} instead of calling this directly to ensure
+ * proper initialization and cleanup of dependencies.
*/
- @VisibleForTesting
- public static WindowManagerService main(final Context context, final InputManagerService im,
- final boolean showBootMsgs, WindowManagerPolicy policy, ActivityTaskManagerService atm,
- DisplayWindowSettingsProvider displayWindowSettingsProvider,
- Supplier<SurfaceControl.Transaction> transactionFactory,
- Supplier<SurfaceControl.Builder> surfaceControlFactory,
- AppCompatConfiguration appCompat) {
+ @VisibleForTesting(visibility = Visibility.PRIVATE)
+ static WindowManagerService main(@NonNull final Context context,
+ @NonNull final InputManagerService im, boolean showBootMsgs,
+ @NonNull final WindowManagerPolicy policy,
+ @NonNull final ActivityTaskManagerService atm,
+ @NonNull final DisplayWindowSettingsProvider displayWindowSettingsProvider,
+ @NonNull final Supplier<SurfaceControl.Transaction> transactionFactory,
+ @NonNull final Supplier<SurfaceControl.Builder> surfaceControlFactory,
+ @NonNull final AppCompatConfiguration appCompat) {
final WindowManagerService[] wms = new WindowManagerService[1];
DisplayThread.getHandler().runWithScissors(() ->
@@ -1238,12 +1251,13 @@ public class WindowManagerService extends IWindowManager.Stub
new WindowManagerShellCommand(this).exec(this, in, out, err, args, callback, result);
}
- private WindowManagerService(Context context, InputManagerService inputManager,
- boolean showBootMsgs, WindowManagerPolicy policy, ActivityTaskManagerService atm,
- DisplayWindowSettingsProvider displayWindowSettingsProvider,
- Supplier<SurfaceControl.Transaction> transactionFactory,
- Supplier<SurfaceControl.Builder> surfaceControlFactory,
- AppCompatConfiguration appCompat) {
+ private WindowManagerService(@NonNull Context context,
+ @NonNull InputManagerService inputManager, boolean showBootMsgs,
+ @NonNull WindowManagerPolicy policy, @NonNull ActivityTaskManagerService atm,
+ @NonNull DisplayWindowSettingsProvider displayWindowSettingsProvider,
+ @NonNull Supplier<SurfaceControl.Transaction> transactionFactory,
+ @NonNull Supplier<SurfaceControl.Builder> surfaceControlFactory,
+ @NonNull AppCompatConfiguration appCompat) {
installLock(this, INDEX_WINDOW);
mGlobalLock = atm.getGlobalLock();
mAtmService = atm;
@@ -6393,11 +6407,6 @@ public class WindowManagerService extends IWindowManager.Stub
if (mFrozenDisplayId != INVALID_DISPLAY && mFrozenDisplayId == w.getDisplayId()
&& mWindowsFreezingScreen != WINDOWS_FREEZING_SCREENS_TIMEOUT) {
ProtoLog.v(WM_DEBUG_ORIENTATION, "Changing surface while display frozen: %s", w);
- // WindowsState#reportResized won't tell invisible requested window to redraw,
- // so do not set it as changing orientation to avoid affecting draw state.
- if (w.isVisibleRequested()) {
- w.setOrientationChanging(true);
- }
if (mWindowsFreezingScreen == WINDOWS_FREEZING_SCREENS_NONE) {
mWindowsFreezingScreen = WINDOWS_FREEZING_SCREENS_ACTIVE;
// XXX should probably keep timeout from
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index 85e3d89730a3..da58470edc1e 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -148,7 +148,6 @@ import static com.android.server.wm.WindowManagerService.MY_PID;
import static com.android.server.wm.WindowManagerService.UPDATE_FOCUS_NORMAL;
import static com.android.server.wm.WindowManagerService.UPDATE_FOCUS_REMOVING_FOCUS;
import static com.android.server.wm.WindowManagerService.UPDATE_FOCUS_WILL_PLACE_SURFACES;
-import static com.android.server.wm.WindowManagerService.WINDOWS_FREEZING_SCREENS_TIMEOUT;
import static com.android.server.wm.WindowStateAnimator.COMMIT_DRAW_PENDING;
import static com.android.server.wm.WindowStateAnimator.DRAW_PENDING;
import static com.android.server.wm.WindowStateAnimator.HAS_DRAWN;
@@ -592,27 +591,10 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
/** Completely remove from window manager after exit animation? */
boolean mRemoveOnExit;
- /**
- * Set when the orientation is changing and this window has not yet
- * been updated for the new orientation.
- */
- private boolean mOrientationChanging;
-
/** The time when the window was last requested to redraw for orientation change. */
private long mOrientationChangeRedrawRequestTime;
/**
- * Sometimes in addition to the mOrientationChanging
- * flag we report that the orientation is changing
- * due to a mismatch in current and reported configuration.
- *
- * In the case of timeout we still need to make sure we
- * leave the orientation changing state though, so we
- * use this as a special time out escape hatch.
- */
- private boolean mOrientationChangeTimedOut;
-
- /**
* The orientation during the last visible call to relayout. If our
* current orientation is different, the window can't be ready
* to be shown.
@@ -1497,8 +1479,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
// Reset the drawn state if the window need to redraw for the change, so the transition
// can wait until it has finished drawing to start.
- if ((configChanged || getOrientationChanging() || dragResizingChanged)
- && isVisibleRequested()) {
+ if ((configChanged || dragResizingChanged) && isVisibleRequested()) {
winAnimator.mDrawState = DRAW_PENDING;
if (mActivityRecord != null) {
mActivityRecord.clearAllDrawn();
@@ -1512,15 +1493,6 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
ProtoLog.v(WM_DEBUG_RESIZE, "Resizing window %s", this);
mWmService.mResizingWindows.add(this);
}
- } else if (getOrientationChanging()) {
- if (isDrawn()) {
- ProtoLog.v(WM_DEBUG_ORIENTATION,
- "Orientation not waiting for draw in %s, surfaceController %s", this,
- winAnimator.mSurfaceControl);
- setOrientationChanging(false);
- mLastFreezeDuration = (int)(SystemClock.elapsedRealtime()
- - mWmService.mDisplayFreezeTime);
- }
}
}
@@ -1528,46 +1500,6 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
return !mWindowFrames.mFrame.equals(mWindowFrames.mLastFrame);
}
- boolean getOrientationChanging() {
- if (mTransitionController.isShellTransitionsEnabled()) {
- // Shell transition doesn't use the methods for display frozen state.
- return false;
- }
- // In addition to the local state flag, we must also consider the difference in the last
- // reported configuration vs. the current state. If the client code has not been informed of
- // the change, logic dependent on having finished processing the orientation, such as
- // unfreezing, could be improperly triggered.
- // TODO(b/62846907): Checking against {@link mLastReportedConfiguration} could be flaky as
- // this is not necessarily what the client has processed yet. Find a
- // better indicator consistent with the client.
- return (mOrientationChanging || (isVisible()
- && getConfiguration().orientation != getLastReportedConfiguration().orientation))
- && !mSeamlesslyRotated
- && !mOrientationChangeTimedOut;
- }
-
- void setOrientationChanging(boolean changing) {
- mOrientationChangeTimedOut = false;
- if (mOrientationChanging == changing) {
- return;
- }
- mOrientationChanging = changing;
- if (changing) {
- mLastFreezeDuration = 0;
- if (mWmService.mRoot.mOrientationChangeComplete
- && mDisplayContent.shouldSyncRotationChange(this)) {
- mWmService.mRoot.mOrientationChangeComplete = false;
- }
- } else {
- // The orientation change is completed. If it was hidden by the animation, reshow it.
- mDisplayContent.finishAsyncRotation(mToken);
- }
- }
-
- void orientationChangeTimedOut() {
- mOrientationChangeTimedOut = true;
- }
-
@Override
void onDisplayChanged(DisplayContent dc) {
if (dc != null && mDisplayContent != null && dc != mDisplayContent
@@ -3355,12 +3287,6 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
mAppFreezing = false;
- if (mHasSurface && !getOrientationChanging()
- && mWmService.mWindowsFreezingScreen != WINDOWS_FREEZING_SCREENS_TIMEOUT) {
- ProtoLog.v(WM_DEBUG_ORIENTATION,
- "set mOrientationChanging of %s", this);
- setOrientationChanging(true);
- }
mLastFreezeDuration = 0;
setDisplayLayoutNeeded();
return true;
@@ -4266,9 +4192,8 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
+ " mDestroying=" + mDestroying
+ " mRemoved=" + mRemoved);
}
- if (getOrientationChanging() || mAppFreezing) {
- pw.println(prefix + "mOrientationChanging=" + mOrientationChanging
- + " configOrientationChanging="
+ if (mAppFreezing) {
+ pw.println(prefix + " configOrientationChanging="
+ (getLastReportedConfiguration().orientation != getConfiguration().orientation)
+ " mAppFreezing=" + mAppFreezing);
}
@@ -5356,7 +5281,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
// Send information to SurfaceFlinger about the priority of the current window.
updateFrameRateSelectionPriorityIfNeeded();
updateScaleIfNeeded();
- mWinAnimator.prepareSurfaceLocked(getSyncTransaction());
+ mWinAnimator.prepareSurfaceLocked(getPendingTransaction());
applyDims();
}
super.prepareSurfaces();
diff --git a/services/core/java/com/android/server/wm/WindowStateAnimator.java b/services/core/java/com/android/server/wm/WindowStateAnimator.java
index 0154d95d888d..298580e4bb81 100644
--- a/services/core/java/com/android/server/wm/WindowStateAnimator.java
+++ b/services/core/java/com/android/server/wm/WindowStateAnimator.java
@@ -29,7 +29,6 @@ import static android.view.WindowManager.TRANSIT_OLD_NONE;
import static com.android.internal.protolog.WmProtoLogGroups.WM_DEBUG_ANIM;
import static com.android.internal.protolog.WmProtoLogGroups.WM_DEBUG_DRAW;
-import static com.android.internal.protolog.WmProtoLogGroups.WM_DEBUG_ORIENTATION;
import static com.android.internal.protolog.WmProtoLogGroups.WM_DEBUG_STARTING_WINDOW;
import static com.android.internal.protolog.WmProtoLogGroups.WM_SHOW_SURFACE_ALLOC;
import static com.android.internal.protolog.WmProtoLogGroups.WM_SHOW_TRANSACTIONS;
@@ -417,56 +416,19 @@ class WindowStateAnimator {
}
}
- void computeShownFrameLocked() {
- if (mWin.mIsWallpaper && mService.mRoot.mWallpaperActionPending) {
- return;
- } else if (mWin.isDragResizeChanged()) {
- // This window is awaiting a relayout because user just started (or ended)
- // drag-resizing. The shown frame (which affects surface size and pos)
- // should not be updated until we get next finished draw with the new surface.
- // Otherwise one or two frames rendered with old settings would be displayed
- // with new geometry.
- return;
- }
-
- if (DEBUG) {
- Slog.v(TAG, "computeShownFrameLocked: " + this
- + " not attached, mAlpha=" + mAlpha);
- }
-
- mShownAlpha = mAlpha;
- }
-
void prepareSurfaceLocked(SurfaceControl.Transaction t) {
final WindowState w = mWin;
if (!hasSurface()) {
-
- // There is no need to wait for an animation change if our window is gone for layout
- // already as we'll never be visible.
- if (w.getOrientationChanging() && w.isGoneForLayout()) {
- ProtoLog.v(WM_DEBUG_ORIENTATION, "Orientation change skips hidden %s", w);
- w.setOrientationChanging(false);
- }
return;
}
- computeShownFrameLocked();
+ mShownAlpha = mAlpha;
if (!w.isOnScreen()) {
hide(t, "prepareSurfaceLocked");
if (!w.mIsWallpaper || !mService.mFlags.mEnsureWallpaperInTransitions) {
mWallpaperControllerLocked.hideWallpapers(w);
}
-
- // If we are waiting for this window to handle an orientation change. If this window is
- // really hidden (gone for layout), there is no point in still waiting for it.
- // Note that this does introduce a potential glitch if the window becomes unhidden
- // before it has drawn for the new orientation.
- if (w.getOrientationChanging() && w.isGoneForLayout()) {
- w.setOrientationChanging(false);
- ProtoLog.v(WM_DEBUG_ORIENTATION,
- "Orientation change skips hidden %s", w);
- }
} else if (mLastAlpha != mShownAlpha
|| mLastHidden) {
mLastAlpha = mShownAlpha;
@@ -480,35 +442,9 @@ class WindowStateAnimator {
if (mLastHidden) {
showRobustly(t);
mLastHidden = false;
- final DisplayContent displayContent = w.getDisplayContent();
- if (!displayContent.getLastHasContent()) {
- // This draw means the difference between unique content and mirroring.
- // Run another pass through performLayout to set mHasContent in the
- // LogicalDisplay.
- displayContent.pendingLayoutChanges |= FINISH_LAYOUT_REDO_ANIM;
- if (DEBUG_LAYOUT_REPEATS) {
- mService.mWindowPlacerLocked.debugLayoutRepeats(
- "showSurfaceRobustlyLocked " + w,
- displayContent.pendingLayoutChanges);
- }
- }
}
}
}
-
- if (w.getOrientationChanging()) {
- if (!w.isDrawn()) {
- if (w.mDisplayContent.shouldSyncRotationChange(w)) {
- w.mWmService.mRoot.mOrientationChangeComplete = false;
- mAnimator.mLastWindowFreezeSource = w;
- }
- ProtoLog.v(WM_DEBUG_ORIENTATION,
- "Orientation continue waiting for draw in %s", w);
- } else {
- w.setOrientationChanging(false);
- ProtoLog.v(WM_DEBUG_ORIENTATION, "Orientation change complete in %s", w);
- }
- }
}
private void showRobustly(SurfaceControl.Transaction t) {
diff --git a/services/core/java/com/android/server/wm/WindowSurfacePlacer.java b/services/core/java/com/android/server/wm/WindowSurfacePlacer.java
index dff718a4b7d5..a34b5115faf9 100644
--- a/services/core/java/com/android/server/wm/WindowSurfacePlacer.java
+++ b/services/core/java/com/android/server/wm/WindowSurfacePlacer.java
@@ -127,7 +127,6 @@ class WindowSurfacePlacer {
mService.mAnimationHandler.removeCallbacks(mPerformSurfacePlacement);
loopCount--;
} while (mTraversalScheduled && loopCount > 0);
- mService.mRoot.mWallpaperActionPending = false;
}
private void performSurfacePlacementLoop() {
diff --git a/services/core/xsd/device-state-config/device-state-config.xsd b/services/core/xsd/device-state-config/device-state-config.xsd
index 4a947327070a..324c9b422ecb 100644
--- a/services/core/xsd/device-state-config/device-state-config.xsd
+++ b/services/core/xsd/device-state-config/device-state-config.xsd
@@ -41,6 +41,7 @@
<xs:annotation name="nullable" />
</xs:element>
<xs:element name="properties" type="properties" />
+ <xs:element name="flags" type="flags" />
<xs:element name="conditions" type="conditions" />
</xs:sequence>
</xs:complexType>
@@ -53,6 +54,14 @@
</xs:sequence>
</xs:complexType>
+ <xs:complexType name="flags">
+ <xs:sequence>
+ <xs:element name="flag" type="xs:string" minOccurs="0" maxOccurs="unbounded">
+ <xs:annotation name="nullable" />
+ </xs:element>
+ </xs:sequence>
+ </xs:complexType>
+
<xs:complexType name="conditions">
<xs:sequence>
<xs:element name="lid-switch" type="lidSwitchCondition" minOccurs="0">
diff --git a/services/core/xsd/device-state-config/schema/current.txt b/services/core/xsd/device-state-config/schema/current.txt
index 5bb216e69e4d..c94f3a80b898 100644
--- a/services/core/xsd/device-state-config/schema/current.txt
+++ b/services/core/xsd/device-state-config/schema/current.txt
@@ -11,10 +11,12 @@ package com.android.server.policy.devicestate.config {
public class DeviceState {
ctor public DeviceState();
method public com.android.server.policy.devicestate.config.Conditions getConditions();
+ method public com.android.server.policy.devicestate.config.Flags getFlags();
method public java.math.BigInteger getIdentifier();
method @Nullable public String getName();
method public com.android.server.policy.devicestate.config.Properties getProperties();
method public void setConditions(com.android.server.policy.devicestate.config.Conditions);
+ method public void setFlags(com.android.server.policy.devicestate.config.Flags);
method public void setIdentifier(java.math.BigInteger);
method public void setName(@Nullable String);
method public void setProperties(com.android.server.policy.devicestate.config.Properties);
@@ -25,6 +27,11 @@ package com.android.server.policy.devicestate.config {
method public java.util.List<com.android.server.policy.devicestate.config.DeviceState> getDeviceState();
}
+ public class Flags {
+ ctor public Flags();
+ method @Nullable public java.util.List<java.lang.String> getFlag();
+ }
+
public class LidSwitchCondition {
ctor public LidSwitchCondition();
method public boolean getOpen();
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index 0ce25db6ea55..e960abd3b313 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -16412,7 +16412,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
@Override
public android.app.admin.EnforcingAdmin getEnforcingAdmin(int userId, String identifier) {
- Preconditions.checkCallAuthorization(isSystemUid(getCallerIdentity()));
+ Preconditions.checkCallAuthorization(canQueryAdminPolicy(getCallerIdentity()));
return getEnforcingAdminInternal(userId, identifier);
}
diff --git a/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/ImeVisibilityStateComputerTest.java b/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/ImeVisibilityStateComputerTest.java
index 8ce2422563a3..70eeae648dd0 100644
--- a/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/ImeVisibilityStateComputerTest.java
+++ b/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/ImeVisibilityStateComputerTest.java
@@ -50,6 +50,7 @@ import android.view.inputmethod.InputMethodManager;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import com.android.internal.annotations.GuardedBy;
+import com.android.server.pm.UserManagerInternal;
import com.android.server.wm.WindowManagerInternal;
import org.junit.Before;
@@ -74,6 +75,11 @@ public class ImeVisibilityStateComputerTest extends InputMethodManagerServiceTes
super.setUp();
ImeVisibilityStateComputer.Injector injector = new ImeVisibilityStateComputer.Injector() {
@Override
+ public UserManagerInternal getUserManagerService() {
+ return mMockUserManagerInternal;
+ }
+
+ @Override
public WindowManagerInternal getWmService() {
return mMockWindowManagerInternal;
}
diff --git a/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/UserDataRepositoryTest.java b/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/UserDataRepositoryTest.java
index b984624b3e9b..6adb01ccf11f 100644
--- a/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/UserDataRepositoryTest.java
+++ b/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/UserDataRepositoryTest.java
@@ -23,6 +23,7 @@ import android.view.WindowManager;
import androidx.annotation.NonNull;
+import com.android.server.pm.UserManagerInternal;
import com.android.server.wm.WindowManagerInternal;
import org.junit.After;
@@ -49,6 +50,9 @@ public final class UserDataRepositoryTest {
private InputMethodManagerService mMockInputMethodManagerService;
@Mock
+ private UserManagerInternal mMockUserManagerInternal;
+
+ @Mock
private WindowManagerInternal mMockWindowManagerInternal;
@NonNull
@@ -70,6 +74,12 @@ public final class UserDataRepositoryTest {
new ImeVisibilityStateComputer.Injector() {
@NonNull
@Override
+ public UserManagerInternal getUserManagerService() {
+ return mMockUserManagerInternal;
+ }
+
+ @NonNull
+ @Override
public WindowManagerInternal getWmService() {
return mMockWindowManagerInternal;
}
diff --git a/services/tests/mockingservicestests/src/com/android/server/location/fudger/LocationFudgerTest.java b/services/tests/mockingservicestests/src/com/android/server/location/fudger/LocationFudgerTest.java
index f442eb69594e..ebaa2e84c044 100644
--- a/services/tests/mockingservicestests/src/com/android/server/location/fudger/LocationFudgerTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/location/fudger/LocationFudgerTest.java
@@ -271,4 +271,31 @@ public class LocationFudgerTest {
assertThat(center[0]).isEqualTo(expected[0]);
assertThat(center[1]).isEqualTo(expected[1]);
}
+
+ @Test
+ public void getS2CellApproximateEdge_returnsCorrectRadius() {
+ int level = 10;
+
+ float radius = mFudger.getS2CellApproximateEdge(level);
+
+ assertThat(radius).isEqualTo(9000); // in meters
+ }
+
+ @Test
+ public void getS2CellApproximateEdge_doesNotThrow() {
+ int level = -1;
+
+ mFudger.getS2CellApproximateEdge(level);
+
+ // No exception thrown.
+ }
+
+ @Test
+ public void getS2CellApproximateEdge_doesNotThrow2() {
+ int level = 14;
+
+ mFudger.getS2CellApproximateEdge(level);
+
+ // No exception thrown.
+ }
}
diff --git a/services/tests/selinux/src/com/android/server/selinux/SelinuxAuditLogsCollectorTest.java b/services/tests/selinux/src/com/android/server/selinux/SelinuxAuditLogsCollectorTest.java
index db58c74e8431..29f55ff53e6e 100644
--- a/services/tests/selinux/src/com/android/server/selinux/SelinuxAuditLogsCollectorTest.java
+++ b/services/tests/selinux/src/com/android/server/selinux/SelinuxAuditLogsCollectorTest.java
@@ -20,6 +20,7 @@ import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
import static com.google.common.truth.Truth.assertThat;
+import static org.junit.Assert.assertThrows;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyInt;
@@ -81,7 +82,7 @@ public class SelinuxAuditLogsCollectorTest {
}
@Test
- public void testWriteAuditLogs() {
+ public void testWriteAuditLogs() throws Exception {
writeTestLog("granted", "perm", TEST_DOMAIN, "ttype", "tclass");
writeTestLog("denied", "perm1", TEST_DOMAIN, "ttype1", "tclass1");
@@ -117,7 +118,7 @@ public class SelinuxAuditLogsCollectorTest {
}
@Test
- public void testWriteAuditLogs_multiplePerms() {
+ public void testWriteAuditLogs_multiplePerms() throws Exception {
writeTestLog("denied", "perm1 perm2", TEST_DOMAIN, "ttype", "tclass");
writeTestLog("denied", "perm3 perm4", TEST_DOMAIN, "ttype", "tclass");
@@ -153,7 +154,7 @@ public class SelinuxAuditLogsCollectorTest {
}
@Test
- public void testWriteAuditLogs_withPaths() {
+ public void testWriteAuditLogs_withPaths() throws Exception {
writeTestLog("denied", "perm", TEST_DOMAIN, "ttype", "tclass", "/good/path");
writeTestLog("denied", "perm", TEST_DOMAIN, "ttype", "tclass", "/very/long/path");
writeTestLog("denied", "perm", TEST_DOMAIN, "ttype", "tclass", "/short_path");
@@ -217,7 +218,7 @@ public class SelinuxAuditLogsCollectorTest {
}
@Test
- public void testWriteAuditLogs_withCategories() {
+ public void testWriteAuditLogs_withCategories() throws Exception {
writeTestLog("denied", "perm", TEST_DOMAIN, new int[] {123}, "ttype", null, "tclass");
writeTestLog("denied", "perm", TEST_DOMAIN, new int[] {123, 456}, "ttype", null, "tclass");
writeTestLog("denied", "perm", TEST_DOMAIN, null, "ttype", new int[] {666}, "tclass");
@@ -288,7 +289,7 @@ public class SelinuxAuditLogsCollectorTest {
}
@Test
- public void testWriteAuditLogs_withPathAndCategories() {
+ public void testWriteAuditLogs_withPathAndCategories() throws Exception {
writeTestLog(
"denied",
"perm",
@@ -318,7 +319,7 @@ public class SelinuxAuditLogsCollectorTest {
}
@Test
- public void testWriteAuditLogs_permissive() {
+ public void testWriteAuditLogs_permissive() throws Exception {
writeTestLog("denied", "perm", TEST_DOMAIN, "ttype", "tclass");
writeTestLog("denied", "perm", TEST_DOMAIN, "ttype", "tclass", true);
writeTestLog("denied", "perm", TEST_DOMAIN, "ttype", "tclass", false);
@@ -356,7 +357,7 @@ public class SelinuxAuditLogsCollectorTest {
}
@Test
- public void testNotWriteAuditLogs_notTestDomain() {
+ public void testNotWriteAuditLogs_notTestDomain() throws Exception {
writeTestLog("denied", "perm", "stype", "ttype", "tclass");
boolean done = mSelinuxAutidLogsCollector.collect(ANSWER_TAG);
@@ -379,7 +380,7 @@ public class SelinuxAuditLogsCollectorTest {
}
@Test
- public void testWriteAuditLogs_upToQuota() {
+ public void testWriteAuditLogs_upToQuota() throws Exception {
writeTestLog("denied", "perm", TEST_DOMAIN, "ttype", "tclass");
writeTestLog("denied", "perm", TEST_DOMAIN, "ttype", "tclass");
writeTestLog("denied", "perm", TEST_DOMAIN, "ttype", "tclass");
@@ -389,9 +390,9 @@ public class SelinuxAuditLogsCollectorTest {
writeTestLog("denied", "perm", TEST_DOMAIN, "ttype", "tclass");
writeTestLog("denied", "perm", TEST_DOMAIN, "ttype", "tclass");
- boolean done = mSelinuxAutidLogsCollector.collect(ANSWER_TAG);
+ assertThrows(QuotaExceededException.class, () ->
+ mSelinuxAutidLogsCollector.collect(ANSWER_TAG));
- assertThat(done).isTrue();
verify(
() ->
FrameworkStatsLog.write(
@@ -409,7 +410,7 @@ public class SelinuxAuditLogsCollectorTest {
}
@Test
- public void testWriteAuditLogs_resetQuota() {
+ public void testWriteAuditLogs_resetQuota() throws Exception {
writeTestLog("denied", "perm", TEST_DOMAIN, "ttype", "tclass");
writeTestLog("denied", "perm", TEST_DOMAIN, "ttype", "tclass");
writeTestLog("denied", "perm", TEST_DOMAIN, "ttype", "tclass");
@@ -418,8 +419,8 @@ public class SelinuxAuditLogsCollectorTest {
writeTestLog("denied", "perm", TEST_DOMAIN, "ttype", "tclass");
writeTestLog("denied", "perm", TEST_DOMAIN, "ttype", "tclass");
- boolean done = mSelinuxAutidLogsCollector.collect(ANSWER_TAG);
- assertThat(done).isTrue();
+ assertThrows(QuotaExceededException.class, () ->
+ mSelinuxAutidLogsCollector.collect(ANSWER_TAG));
verify(
() ->
FrameworkStatsLog.write(
@@ -442,8 +443,8 @@ public class SelinuxAuditLogsCollectorTest {
writeTestLog("denied", "perm", TEST_DOMAIN, "ttype", "tclass");
// move the clock forward to reset the quota limiter.
mClock.currentTimeMillis += Duration.ofHours(1).toMillis();
- done = mSelinuxAutidLogsCollector.collect(ANSWER_TAG);
- assertThat(done).isTrue();
+ assertThrows(QuotaExceededException.class, () ->
+ mSelinuxAutidLogsCollector.collect(ANSWER_TAG));
verify(
() ->
FrameworkStatsLog.write(
@@ -461,14 +462,11 @@ public class SelinuxAuditLogsCollectorTest {
}
@Test
- public void testNotWriteAuditLogs_stopRequested() {
- writeTestLog("denied", "perm", TEST_DOMAIN, "ttype", "tclass");
+ public void testNotWriteAuditLogs_stopRequested() throws Exception {
writeTestLog("denied", "perm", TEST_DOMAIN, "ttype", "tclass");
writeTestLog("denied", "perm", TEST_DOMAIN, "ttype", "tclass");
writeTestLog("denied", "perm", TEST_DOMAIN, "ttype", "tclass");
writeTestLog("denied", "perm", TEST_DOMAIN, "ttype", "tclass");
- // These are not pushed.
- writeTestLog("denied", "perm", TEST_DOMAIN, "ttype", "tclass");
writeTestLog("denied", "perm", TEST_DOMAIN, "ttype", "tclass");
mSelinuxAutidLogsCollector.setStopRequested(true);
@@ -509,7 +507,7 @@ public class SelinuxAuditLogsCollectorTest {
}
@Test
- public void testAuditLogs_resumeJobDoesNotExceedLimit() {
+ public void testAuditLogs_resumeJobDoesNotExceedLimit() throws Exception {
writeTestLog("denied", "perm", TEST_DOMAIN, "ttype", "tclass");
mSelinuxAutidLogsCollector.setStopRequested(true);
diff --git a/services/tests/selinux/src/com/android/server/selinux/SelinuxAuditLogsJobTest.java b/services/tests/selinux/src/com/android/server/selinux/SelinuxAuditLogsJobTest.java
index 2aea8a033f87..344f3295f682 100644
--- a/services/tests/selinux/src/com/android/server/selinux/SelinuxAuditLogsJobTest.java
+++ b/services/tests/selinux/src/com/android/server/selinux/SelinuxAuditLogsJobTest.java
@@ -53,7 +53,7 @@ public class SelinuxAuditLogsJobTest {
}
@Test
- public void testFinishSuccessfully() {
+ public void testFinishSuccessfully() throws Exception {
when(mAuditLogsCollector.collect(anyInt())).thenReturn(true);
mAuditLogsJob.start(mJobService, mParams);
@@ -63,7 +63,7 @@ public class SelinuxAuditLogsJobTest {
}
@Test
- public void testInterrupt() {
+ public void testInterrupt() throws Exception {
when(mAuditLogsCollector.collect(anyInt())).thenReturn(false);
mAuditLogsJob.start(mJobService, mParams);
@@ -73,7 +73,7 @@ public class SelinuxAuditLogsJobTest {
}
@Test
- public void testInterruptAndResume() {
+ public void testInterruptAndResume() throws Exception {
when(mAuditLogsCollector.collect(anyInt())).thenReturn(false);
mAuditLogsJob.start(mJobService, mParams);
verify(mJobService, never()).jobFinished(any(), anyBoolean());
@@ -85,7 +85,7 @@ public class SelinuxAuditLogsJobTest {
}
@Test
- public void testRequestStop() throws InterruptedException {
+ public void testRequestStop() throws Exception {
Semaphore isRunning = new Semaphore(0);
Semaphore stopRequested = new Semaphore(0);
AtomicReference<Throwable> uncaughtException = new AtomicReference<>();
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityManagerServiceTest.java
index dafe4827b2fe..5602fb76e6f5 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityManagerServiceTest.java
@@ -829,26 +829,6 @@ public class AccessibilityManagerServiceTest {
@SmallTest
@Test
- @DisableFlags(com.android.systemui.Flags.FLAG_HEARING_AIDS_QS_TILE_DIALOG)
- public void testPerformAccessibilityShortcut_hearingAids_startActivityWithExpectedComponent() {
- final AccessibilityUserState userState = mA11yms.mUserStates.get(
- mA11yms.getCurrentUserIdLocked());
- mFakePermissionEnforcer.grant(Manifest.permission.MANAGE_ACCESSIBILITY);
- userState.updateShortcutTargetsLocked(
- Set.of(ACCESSIBILITY_HEARING_AIDS_COMPONENT_NAME.flattenToString()), HARDWARE);
-
- mA11yms.performAccessibilityShortcut(
- Display.DEFAULT_DISPLAY, HARDWARE,
- ACCESSIBILITY_HEARING_AIDS_COMPONENT_NAME.flattenToString());
- mTestableLooper.processAllMessages();
-
- assertStartActivityWithExpectedComponentName(mTestableContext.getMockContext(),
- ACCESSIBILITY_HEARING_AIDS_COMPONENT_NAME.flattenToString());
- }
-
- @SmallTest
- @Test
- @EnableFlags(com.android.systemui.Flags.FLAG_HEARING_AIDS_QS_TILE_DIALOG)
public void testPerformAccessibilityShortcut_hearingAids_sendExpectedBroadcast() {
final AccessibilityUserState userState = mA11yms.mUserStates.get(
mA11yms.getCurrentUserIdLocked());
@@ -1510,7 +1490,6 @@ public class AccessibilityManagerServiceTest {
}
@Test
- @EnableFlags(android.view.accessibility.Flags.FLAG_A11Y_QS_SHORTCUT)
public void notifyQuickSettingsTilesChanged_statusBarServiceNotGranted_throwsException() {
mFakePermissionEnforcer.revoke(Manifest.permission.STATUS_BAR_SERVICE);
mFakePermissionEnforcer.grant(Manifest.permission.MANAGE_ACCESSIBILITY);
@@ -1523,7 +1502,6 @@ public class AccessibilityManagerServiceTest {
}
@Test
- @EnableFlags(android.view.accessibility.Flags.FLAG_A11Y_QS_SHORTCUT)
public void notifyQuickSettingsTilesChanged_manageAccessibilityNotGranted_throwsException() {
mFakePermissionEnforcer.grant(Manifest.permission.STATUS_BAR_SERVICE);
mTestableContext.getTestablePermissions().setPermission(
@@ -1537,7 +1515,6 @@ public class AccessibilityManagerServiceTest {
}
@Test
- @EnableFlags(android.view.accessibility.Flags.FLAG_A11Y_QS_SHORTCUT)
public void notifyQuickSettingsTilesChanged_qsTileChanges_updateA11yTilesInQsPanel() {
mFakePermissionEnforcer.grant(Manifest.permission.STATUS_BAR_SERVICE);
mFakePermissionEnforcer.grant(Manifest.permission.MANAGE_ACCESSIBILITY);
@@ -1557,7 +1534,6 @@ public class AccessibilityManagerServiceTest {
}
@Test
- @EnableFlags(android.view.accessibility.Flags.FLAG_A11Y_QS_SHORTCUT)
public void notifyQuickSettingsTilesChanged_sameQsTiles_noUpdateToA11yTilesInQsPanel() {
notifyQuickSettingsTilesChanged_qsTileChanges_updateA11yTilesInQsPanel();
List<ComponentName> tiles =
@@ -1574,7 +1550,6 @@ public class AccessibilityManagerServiceTest {
}
@Test
- @EnableFlags(android.view.accessibility.Flags.FLAG_A11Y_QS_SHORTCUT)
public void notifyQuickSettingsTilesChanged_serviceWarningRequired_qsShortcutRemainDisabled() {
mFakePermissionEnforcer.grant(Manifest.permission.STATUS_BAR_SERVICE);
mFakePermissionEnforcer.grant(Manifest.permission.MANAGE_ACCESSIBILITY);
@@ -1593,7 +1568,6 @@ public class AccessibilityManagerServiceTest {
}
@Test
- @EnableFlags(android.view.accessibility.Flags.FLAG_A11Y_QS_SHORTCUT)
public void notifyQuickSettingsTilesChanged_serviceWarningNotRequired_qsShortcutEnabled() {
mFakePermissionEnforcer.grant(Manifest.permission.STATUS_BAR_SERVICE);
mFakePermissionEnforcer.grant(Manifest.permission.MANAGE_ACCESSIBILITY);
@@ -1615,7 +1589,6 @@ public class AccessibilityManagerServiceTest {
}
@Test
- @EnableFlags(android.view.accessibility.Flags.FLAG_A11Y_QS_SHORTCUT)
public void notifyQuickSettingsTilesChanged_addFrameworkTile_qsShortcutEnabled() {
mFakePermissionEnforcer.grant(Manifest.permission.STATUS_BAR_SERVICE);
mFakePermissionEnforcer.grant(Manifest.permission.MANAGE_ACCESSIBILITY);
@@ -1638,7 +1611,6 @@ public class AccessibilityManagerServiceTest {
}
@Test
- @EnableFlags(android.view.accessibility.Flags.FLAG_A11Y_QS_SHORTCUT)
public void notifyQuickSettingsTilesChanged_removeFrameworkTile_qsShortcutDisabled() {
notifyQuickSettingsTilesChanged_addFrameworkTile_qsShortcutEnabled();
Set<ComponentName> qsTiles = mA11yms.getCurrentUserState().getA11yQsTilesInQsPanel();
@@ -1656,7 +1628,6 @@ public class AccessibilityManagerServiceTest {
}
@Test
- @EnableFlags(android.view.accessibility.Flags.FLAG_A11Y_QS_SHORTCUT)
@DisableFlags(android.view.accessibility.Flags.FLAG_RESTORE_A11Y_SECURE_SETTINGS_ON_HSUM_DEVICE)
public void restoreShortcutTargetsAssumeUser0_qs_a11yQsTargetsRestored() {
assumeTrue("The test is setup to run as a user 0",
@@ -1665,8 +1636,7 @@ public class AccessibilityManagerServiceTest {
}
@Test
- @EnableFlags({android.view.accessibility.Flags.FLAG_A11Y_QS_SHORTCUT,
- android.view.accessibility.Flags.FLAG_RESTORE_A11Y_SECURE_SETTINGS_ON_HSUM_DEVICE})
+ @EnableFlags(android.view.accessibility.Flags.FLAG_RESTORE_A11Y_SECURE_SETTINGS_ON_HSUM_DEVICE)
public void restoreShortcutTargets_qs_a11yQsTargetsRestored() {
String daltonizerTile =
AccessibilityShortcutController.DALTONIZER_COMPONENT_NAME.flattenToString();
@@ -1689,40 +1659,6 @@ public class AccessibilityManagerServiceTest {
}
@Test
- @DisableFlags({android.view.accessibility.Flags.FLAG_A11Y_QS_SHORTCUT,
- android.view.accessibility.Flags.FLAG_RESTORE_A11Y_SECURE_SETTINGS_ON_HSUM_DEVICE})
- public void restoreShortcutTargetsAssumeUser0_qs_a11yQsTargetsNotRestored() {
- assumeTrue("The test is setup to run as a user 0",
- mTestableContext.getUserId() == UserHandle.USER_SYSTEM);
- restoreShortcutTargets_qs_a11yQsTargetsNotRestored();
- }
-
- @Test
- @DisableFlags(android.view.accessibility.Flags.FLAG_A11Y_QS_SHORTCUT)
- @EnableFlags(android.view.accessibility.Flags.FLAG_RESTORE_A11Y_SECURE_SETTINGS_ON_HSUM_DEVICE)
- public void restoreShortcutTargets_qs_a11yQsTargetsNotRestored() {
- String daltonizerTile =
- AccessibilityShortcutController.DALTONIZER_COMPONENT_NAME.flattenToString();
- String colorInversionTile =
- AccessibilityShortcutController.COLOR_INVERSION_COMPONENT_NAME.flattenToString();
- final AccessibilityUserState userState = new AccessibilityUserState(
- mA11yms.getCurrentUserIdLocked(), mTestableContext, mA11yms);
- userState.updateShortcutTargetsLocked(Set.of(daltonizerTile), QUICK_SETTINGS);
- putShortcutSettingForUser(QUICK_SETTINGS, daltonizerTile, userState.mUserId);
- mA11yms.mUserStates.put(userState.mUserId, userState);
-
- broadcastSettingRestored(
- ShortcutUtils.convertToKey(QUICK_SETTINGS),
- /*newValue=*/colorInversionTile, userState.mUserId);
-
- Set<String> expected = Set.of(daltonizerTile);
- assertThat(readStringsFromSetting(ShortcutUtils.convertToKey(QUICK_SETTINGS)))
- .containsExactlyElementsIn(expected);
- assertThat(userState.getShortcutTargetsLocked(QUICK_SETTINGS))
- .containsExactlyElementsIn(expected);
- }
-
- @Test
public void onHandleForceStop_dontDoIt_packageEnabled_returnsTrue() {
setupShortcutTargetServices();
AccessibilityUserState userState = mA11yms.getCurrentUserState();
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDevicePlaybackTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDevicePlaybackTest.java
index 861e72d4ac79..cfdf17668229 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDevicePlaybackTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDevicePlaybackTest.java
@@ -2868,6 +2868,9 @@ public class HdmiCecLocalDevicePlaybackTest {
assertThat(mPowerManager.isInteractive()).isTrue();
mNativeWrapper.clearResultMessages();
+ mTestLooper.moveTimeForward(TIMEOUT_MS);
+ mTestLooper.dispatchAll();
+
mTestLooper.moveTimeForward(MONITORING_INTERVAL_MS);
mTestLooper.dispatchAll();
diff --git a/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest1.java b/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest1.java
index 776f05dfb6ea..f536cae53e3a 100644
--- a/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest1.java
+++ b/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest1.java
@@ -167,7 +167,7 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest {
* Test for {@link ShortcutService#getLastResetTimeLocked()} and
* {@link ShortcutService#getNextResetTimeLocked()}.
*/
- public void testUpdateAndGetNextResetTimeLocked() {
+ public void disabled_testUpdateAndGetNextResetTimeLocked() {
assertResetTimes(START_TIME, START_TIME + INTERVAL);
// Advance clock.
@@ -284,7 +284,7 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest {
assertEquals(START_TIME + 5 * INTERVAL, mManager.getRateLimitResetTime());
}
- public void testSetDynamicShortcuts() {
+ public void disabled_testSetDynamicShortcuts() {
setCaller(CALLING_PACKAGE_1, USER_10);
final Icon icon1 = Icon.createWithResource(getTestContext(), R.drawable.icon1);
@@ -596,7 +596,7 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest {
eq(CALLING_PACKAGE_2), any(), eq(USER_10));
}
- public void testUnlimitedCalls() {
+ public void disabled_testUnlimitedCalls() {
setCaller(CALLING_PACKAGE_1, USER_10);
final ShortcutInfo si1 = makeShortcut("shortcut1");
@@ -1205,7 +1205,7 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest {
maxSize));
}
- public void testShrinkBitmap() {
+ public void disabled_testShrinkBitmap() {
checkShrinkBitmap(32, 32, R.drawable.black_512x512, 32);
checkShrinkBitmap(511, 511, R.drawable.black_512x512, 511);
checkShrinkBitmap(512, 512, R.drawable.black_512x512, 512);
@@ -1302,7 +1302,7 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest {
assertFalse(p11_1_3.getName().contains("_"));
}
- public void testUpdateShortcuts() {
+ public void disabled_testUpdateShortcuts() {
runWithCaller(CALLING_PACKAGE_1, USER_10, () -> {
assertTrue(mManager.setDynamicShortcuts(list(
makeShortcut("s1"),
@@ -1433,7 +1433,7 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest {
});
}
- public void testUpdateShortcuts_icons() {
+ public void disabled_testUpdateShortcuts_icons() {
runWithCaller(CALLING_PACKAGE_1, USER_10, () -> {
assertTrue(mManager.setDynamicShortcuts(list(
makeShortcut("s1")
@@ -1527,7 +1527,7 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest {
});
}
- public void testShortcutManagerGetShortcuts_shortcutTypes() {
+ public void disabled_testShortcutManagerGetShortcuts_shortcutTypes() {
// Create 3 manifest and 3 dynamic shortcuts
addManifestShortcutResource(
@@ -2281,7 +2281,7 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest {
assertEquals("ABC", findById(list, "s1").getTitle());
}
- public void testPinShortcutAndGetPinnedShortcuts() {
+ public void disabled_testPinShortcutAndGetPinnedShortcuts() {
runWithCaller(CALLING_PACKAGE_1, USER_10, () -> {
final ShortcutInfo s1_1 = makeShortcutWithTimestamp("s1", 1000);
final ShortcutInfo s1_2 = makeShortcutWithTimestamp("s2", 2000);
@@ -3478,7 +3478,7 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest {
});
}
- public void testStartShortcut() {
+ public void disabled_testStartShortcut() {
// Create some shortcuts.
runWithCaller(CALLING_PACKAGE_1, USER_10, () -> {
final ShortcutInfo s1_1 = makeShortcut(
@@ -3902,7 +3902,7 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest {
// === Test for persisting ===
- public void testSaveAndLoadUser_empty() {
+ public void disabled_testSaveAndLoadUser_empty() {
assertTrue(mManager.setDynamicShortcuts(list()));
Log.i(TAG, "Saved state");
@@ -4074,7 +4074,7 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest {
assertNull(ShortcutPackage.loadFromFile(mService, user, corruptedShortcutPackage, false));
}
- public void testSaveCorruptAndLoadUser() throws Exception {
+ public void disabled_testSaveCorruptAndLoadUser() throws Exception {
// First, create some shortcuts and save.
runWithCaller(CALLING_PACKAGE_1, USER_10, () -> {
final Icon icon1 = Icon.createWithResource(getTestContext(), R.drawable.black_64x16);
@@ -6658,7 +6658,7 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest {
}
- public void testSaveAndLoad_crossProfile() {
+ public void disabled_testSaveAndLoad_crossProfile() {
prepareCrossProfileDataSet();
dumpsysOnLogcat("Before save & load");
@@ -8471,7 +8471,7 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest {
});
}
- public void testShortcutsPushedOutByManifest() {
+ public void disabled_testShortcutsPushedOutByManifest() {
// Change the max number of shortcuts.
mService.updateConfigurationLocked(ConfigConstants.KEY_MAX_SHORTCUTS + "=3");
@@ -8708,7 +8708,7 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest {
}
}
- public void testShareTargetInfo_saveToXml() throws IOException, XmlPullParserException {
+ public void disabled_testShareTargetInfo_saveToXml() throws IOException, XmlPullParserException {
List<ShareTargetInfo> expectedValues = new ArrayList<>();
expectedValues.add(new ShareTargetInfo(
new ShareTargetInfo.TargetData[]{new ShareTargetInfo.TargetData(
@@ -8902,7 +8902,7 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest {
});
}
- public void testUpdateShortcuts_ExcludesHiddenFromLauncherShortcuts() {
+ public void disabled_testUpdateShortcuts_ExcludesHiddenFromLauncherShortcuts() {
final ShortcutInfo s1 = makeShortcut("s1");
final ShortcutInfo s2 = makeShortcut("s2");
final ShortcutInfo s3 = makeShortcut("s3");
diff --git a/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest2.java b/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest2.java
index 9528467f7ad1..39206dcf21ef 100644
--- a/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest2.java
+++ b/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest2.java
@@ -952,7 +952,7 @@ public class ShortcutManagerTest2 extends BaseShortcutManagerTest {
assertEquals(99, si.getExtras().getInt("x"));
}
- public void testShortcutInfoSaveAndLoad() throws InterruptedException {
+ public void disabled_testShortcutInfoSaveAndLoad() throws InterruptedException {
mRunningUsers.put(USER_11, true);
setCaller(CALLING_PACKAGE_1, USER_11);
@@ -1065,7 +1065,7 @@ public class ShortcutManagerTest2 extends BaseShortcutManagerTest {
dumpUserFile(USER_11);
}
- public void testShortcutInfoSaveAndLoad_maskableBitmap() throws InterruptedException {
+ public void disabled_testShortcutInfoSaveAndLoad_maskableBitmap() throws InterruptedException {
mRunningUsers.put(USER_11, true);
setCaller(CALLING_PACKAGE_1, USER_11);
@@ -1134,7 +1134,7 @@ public class ShortcutManagerTest2 extends BaseShortcutManagerTest {
dumpUserFile(USER_11);
}
- public void testShortcutInfoSaveAndLoad_resId() throws InterruptedException {
+ public void disabled_testShortcutInfoSaveAndLoad_resId() throws InterruptedException {
mRunningUsers.put(USER_11, true);
setCaller(CALLING_PACKAGE_1, USER_11);
@@ -1211,7 +1211,7 @@ public class ShortcutManagerTest2 extends BaseShortcutManagerTest {
assertEquals(1, si.getRank());
}
- public void testShortcutInfoSaveAndLoad_uri() throws InterruptedException {
+ public void disabled_testShortcutInfoSaveAndLoad_uri() throws InterruptedException {
mRunningUsers.put(USER_11, true);
setCaller(CALLING_PACKAGE_1, USER_11);
@@ -1299,7 +1299,7 @@ public class ShortcutManagerTest2 extends BaseShortcutManagerTest {
assertEquals("uri_maskable", si.getIconUri());
}
- public void testShortcutInfoSaveAndLoad_forBackup() {
+ public void disabled_testShortcutInfoSaveAndLoad_forBackup() {
setCaller(CALLING_PACKAGE_1, USER_10);
final Icon bmp32x32 = Icon.createWithBitmap(BitmapFactory.decodeResource(
@@ -1368,7 +1368,7 @@ public class ShortcutManagerTest2 extends BaseShortcutManagerTest {
assertEquals(0, si.getRank());
}
- public void testShortcutInfoSaveAndLoad_forBackup_resId() {
+ public void disabled_testShortcutInfoSaveAndLoad_forBackup_resId() {
setCaller(CALLING_PACKAGE_1, USER_10);
final Icon res32x32 = Icon.createWithResource(mClientContext, R.drawable.black_32x32);
@@ -1438,7 +1438,7 @@ public class ShortcutManagerTest2 extends BaseShortcutManagerTest {
assertEquals(0, si.getRank());
}
- public void testShortcutInfoSaveAndLoad_forBackup_uri() {
+ public void disabled_testShortcutInfoSaveAndLoad_forBackup_uri() {
setCaller(CALLING_PACKAGE_1, USER_10);
final Icon uriIcon = Icon.createWithContentUri("test_uri");
@@ -1547,7 +1547,7 @@ public class ShortcutManagerTest2 extends BaseShortcutManagerTest {
});
}
- public void testShortcutInfoSaveAndLoad_intents() {
+ public void disabled_testShortcutInfoSaveAndLoad_intents() {
checkShortcutInfoSaveAndLoad_intents(new Intent(Intent.ACTION_VIEW));
mInjectedCurrentTimeMillis += INTERVAL; // reset throttling.
@@ -1789,7 +1789,7 @@ public class ShortcutManagerTest2 extends BaseShortcutManagerTest {
assertFalse(mManager.setDynamicShortcuts(list(si2)));
}
- public void testThrottling_localeChanges() {
+ public void disabled_testThrottling_localeChanges() {
prepareCrossProfileDataSet();
dumpsysOnLogcat("Before save & load");
@@ -2078,7 +2078,7 @@ public class ShortcutManagerTest2 extends BaseShortcutManagerTest {
}
- public void testThrottling_resetByInternalCall() throws Exception {
+ public void disabled_testThrottling_resetByInternalCall() throws Exception {
prepareCrossProfileDataSet();
dumpsysOnLogcat("Before save & load");
@@ -2173,7 +2173,7 @@ public class ShortcutManagerTest2 extends BaseShortcutManagerTest {
});
}
- public void testReportShortcutUsed() {
+ public void disabled_testReportShortcutUsed() {
mRunningUsers.put(USER_11, true);
runWithCaller(CALLING_PACKAGE_1, USER_11, () -> {
@@ -2322,7 +2322,7 @@ public class ShortcutManagerTest2 extends BaseShortcutManagerTest {
getTestContext().getPackageName()));
}
- public void testDumpCheckin() throws IOException {
+ public void disabled_testDumpCheckin() throws IOException {
prepareCrossProfileDataSet();
// prepareCrossProfileDataSet() doesn't set any icons, so do set here.
diff --git a/services/tests/servicestests/src/com/android/server/policy/DeviceStateProviderImplTest.java b/services/tests/servicestests/src/com/android/server/policy/DeviceStateProviderImplTest.java
index 551808243640..7f2935e0e497 100644
--- a/services/tests/servicestests/src/com/android/server/policy/DeviceStateProviderImplTest.java
+++ b/services/tests/servicestests/src/com/android/server/policy/DeviceStateProviderImplTest.java
@@ -18,6 +18,7 @@ package com.android.server.policy;
import static android.content.Context.SENSOR_SERVICE;
+import static android.hardware.devicestate.feature.flags.Flags.FLAG_DEVICE_STATE_CONFIGURATION_FLAG;
import static com.android.server.devicestate.DeviceStateProvider.SUPPORTED_DEVICE_STATES_CHANGED_INITIALIZED;
import static com.android.server.devicestate.DeviceStateProvider.SUPPORTED_DEVICE_STATES_CHANGED_POWER_SAVE_DISABLED;
@@ -42,6 +43,9 @@ import android.hardware.SensorEvent;
import android.hardware.SensorManager;
import android.hardware.devicestate.DeviceState;
import android.os.PowerManager;
+import android.platform.test.annotations.RequiresFlagsEnabled;
+import android.platform.test.flag.junit.CheckFlagsRule;
+import android.platform.test.flag.junit.DeviceFlagsValueProvider;
import androidx.annotation.NonNull;
@@ -51,6 +55,7 @@ import com.android.server.input.InputManagerInternal;
import org.junit.After;
import org.junit.Before;
+import org.junit.Rule;
import org.junit.Test;
import org.mockito.ArgumentCaptor;
import org.mockito.Mockito;
@@ -84,6 +89,10 @@ public final class DeviceStateProviderImplTest {
private Context mContext;
private SensorManager mSensorManager;
+ @Rule
+ public final CheckFlagsRule mCheckFlagsRule =
+ DeviceFlagsValueProvider.createCheckFlagsRule();
+
@Before
public void setup() {
LocalServices.addService(InputManagerInternal.class, mock(InputManagerInternal.class));
@@ -605,6 +614,37 @@ public final class DeviceStateProviderImplTest {
verify(listener, never()).onStateChanged(mIntegerCaptor.capture());
}
+ @RequiresFlagsEnabled(FLAG_DEVICE_STATE_CONFIGURATION_FLAG)
+ @Test
+ public void test_createConfigWithFlags() {
+ String configString = "<device-state-config>\n"
+ + " <device-state>\n"
+ + " <identifier>1</identifier>\n"
+ + " <flags>\n"
+ + " <flag>FLAG_CANCEL_OVERRIDE_REQUESTS</flag>\n"
+ + " </flags>\n"
+ + " </device-state>\n"
+ + " <device-state>\n"
+ + " <identifier>2</identifier>\n"
+ + " <name>CLOSED</name>\n"
+ + " </device-state>\n"
+ + "</device-state-config>\n";
+ DeviceStateProviderImpl.ReadableConfig config = new TestReadableConfig(configString);
+ DeviceStateProviderImpl provider = DeviceStateProviderImpl.createFromConfig(mContext,
+ config);
+
+ DeviceStateProvider.Listener listener = mock(DeviceStateProvider.Listener.class);
+ provider.setListener(listener);
+
+ verify(listener).onSupportedDeviceStatesChanged(mDeviceStateArrayCaptor.capture(),
+ eq(SUPPORTED_DEVICE_STATES_CHANGED_INITIALIZED));
+ final DeviceState[] expectedStates = new DeviceState[]{
+ createDeviceState(1, "",
+ Set.of(DeviceState.PROPERTY_POLICY_CANCEL_OVERRIDE_REQUESTS)),
+ createDeviceState(2, "CLOSED", EMPTY_PROPERTY_SET)};
+ assertArrayEquals(expectedStates, mDeviceStateArrayCaptor.getValue());
+ }
+
private DeviceState createDeviceState(int identifier, @NonNull String name,
@NonNull Set<@DeviceState.DeviceStateProperties Integer> systemProperties) {
DeviceState.Configuration configuration = new DeviceState.Configuration.Builder(identifier,
diff --git a/services/tests/servicestests/src/com/android/server/security/authenticationpolicy/AuthenticationPolicyServiceTest.java b/services/tests/servicestests/src/com/android/server/security/authenticationpolicy/AuthenticationPolicyServiceTest.java
index ee8eb9b35088..b76e0bc8cd14 100644
--- a/services/tests/servicestests/src/com/android/server/security/authenticationpolicy/AuthenticationPolicyServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/security/authenticationpolicy/AuthenticationPolicyServiceTest.java
@@ -42,8 +42,10 @@ import android.hardware.biometrics.BiometricSourceType;
import android.hardware.biometrics.events.AuthenticationFailedInfo;
import android.hardware.biometrics.events.AuthenticationSucceededInfo;
import android.os.RemoteException;
+import android.platform.test.annotations.EnableFlags;
import android.platform.test.annotations.Presubmit;
import android.platform.test.flag.junit.SetFlagsRule;
+import android.provider.Settings;
import androidx.test.InstrumentationRegistry;
import androidx.test.core.app.ApplicationProvider;
@@ -151,6 +153,8 @@ public class AuthenticationPolicyServiceTest {
when(mSecureLockDeviceService.disableSecureLockDevice(any()))
.thenReturn(ERROR_UNSUPPORTED);
}
+
+ toggleAdaptiveAuthSettingsOverride(PRIMARY_USER_ID, false /* disable */);
}
@After
@@ -252,8 +256,24 @@ public class AuthenticationPolicyServiceTest {
}
@Test
- public void testReportAuthAttempt_biometricAuthFailed_multiple_deviceCurrentlyNotLocked()
+ @EnableFlags({android.security.Flags.FLAG_DISABLE_ADAPTIVE_AUTH_COUNTER_LOCK})
+ public void testReportAuthAttempt_biometricAuthFailed_multiple_deviceCurrentlyNotLocked_deviceLockEnabled()
+ throws RemoteException {
+ testReportAuthAttempt_biometricAuthFailed_multiple_deviceCurrentlyNotLocked(
+ true /* enabled */);
+ }
+
+ @Test
+ @EnableFlags({android.security.Flags.FLAG_DISABLE_ADAPTIVE_AUTH_COUNTER_LOCK})
+ public void testReportAuthAttempt_biometricAuthFailed_multiple_deviceCurrentlyNotLocked_deviceLockDisabled()
throws RemoteException {
+ toggleAdaptiveAuthSettingsOverride(PRIMARY_USER_ID, true /* disabled */);
+ testReportAuthAttempt_biometricAuthFailed_multiple_deviceCurrentlyNotLocked(
+ false /* enabled */);
+ }
+
+ private void testReportAuthAttempt_biometricAuthFailed_multiple_deviceCurrentlyNotLocked(
+ boolean enabled) throws RemoteException {
// Device is currently not locked and Keyguard is not showing
when(mKeyguardManager.isDeviceLocked(PRIMARY_USER_ID)).thenReturn(false);
when(mKeyguardManager.isKeyguardLocked()).thenReturn(false);
@@ -264,7 +284,11 @@ public class AuthenticationPolicyServiceTest {
}
waitForAuthCompletion();
- verifyLockDevice(PRIMARY_USER_ID);
+ if (enabled) {
+ verifyLockDevice(PRIMARY_USER_ID);
+ } else {
+ verifyNotLockDevice(MAX_ALLOWED_FAILED_AUTH_ATTEMPTS, PRIMARY_USER_ID);
+ }
}
@Test
@@ -300,8 +324,24 @@ public class AuthenticationPolicyServiceTest {
}
@Test
- public void testReportAuthAttempt_primaryAuthAndBiometricAuthFailed_primaryUser()
+ @EnableFlags({android.security.Flags.FLAG_DISABLE_ADAPTIVE_AUTH_COUNTER_LOCK})
+ public void testReportAuthAttempt_primaryAuthAndBiometricAuthFailed_primaryUser_deviceLockEnabled()
throws RemoteException {
+ testReportAuthAttempt_primaryAuthAndBiometricAuthFailed_primaryUser(
+ true /* enabled */);
+ }
+
+ @Test
+ @EnableFlags({android.security.Flags.FLAG_DISABLE_ADAPTIVE_AUTH_COUNTER_LOCK})
+ public void testReportAuthAttempt_primaryAuthAndBiometricAuthFailed_primaryUser_deviceLockDisabled()
+ throws RemoteException {
+ toggleAdaptiveAuthSettingsOverride(PRIMARY_USER_ID, true /* disabled */);
+ testReportAuthAttempt_primaryAuthAndBiometricAuthFailed_primaryUser(
+ false /* enabled */);
+ }
+
+ private void testReportAuthAttempt_primaryAuthAndBiometricAuthFailed_primaryUser(
+ boolean enabled) throws RemoteException {
// Three failed primary auth attempts
for (int i = 0; i < 3; i++) {
mLockSettingsStateListenerCaptor.getValue().onAuthenticationFailed(PRIMARY_USER_ID);
@@ -313,7 +353,11 @@ public class AuthenticationPolicyServiceTest {
}
waitForAuthCompletion();
- verifyLockDevice(PRIMARY_USER_ID);
+ if (enabled) {
+ verifyLockDevice(PRIMARY_USER_ID);
+ } else {
+ verifyNotLockDevice(MAX_ALLOWED_FAILED_AUTH_ATTEMPTS, PRIMARY_USER_ID);
+ }
}
@Test
@@ -366,10 +410,13 @@ public class AuthenticationPolicyServiceTest {
REASON_UNKNOWN, true, userId).build();
}
-
private AuthenticationFailedInfo authFailedInfo(int userId) {
return new AuthenticationFailedInfo.Builder(BiometricSourceType.FINGERPRINT, REASON_UNKNOWN,
userId).build();
}
+ private void toggleAdaptiveAuthSettingsOverride(int userId, boolean disable) {
+ Settings.Secure.putIntForUser(mContext.getContentResolver(),
+ Settings.Secure.DISABLE_ADAPTIVE_AUTH_LIMIT_LOCK, disable ? 1 : 0, userId);
+ }
}
diff --git a/services/tests/timetests/src/com/android/server/timezonedetector/FakeEnvironment.java b/services/tests/timetests/src/com/android/server/timezonedetector/FakeEnvironment.java
new file mode 100644
index 000000000000..5d181b81eb4c
--- /dev/null
+++ b/services/tests/timetests/src/com/android/server/timezonedetector/FakeEnvironment.java
@@ -0,0 +1,141 @@
+/*
+ * Copyright 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.timezonedetector;
+
+import static com.android.server.SystemTimeZone.TIME_ZONE_CONFIDENCE_LOW;
+
+import android.annotation.CurrentTimeMillisLong;
+import android.annotation.ElapsedRealtimeLong;
+
+import com.android.server.SystemTimeZone;
+
+import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * A partially implemented, fake implementation of Environment for tests.
+ */
+public class FakeEnvironment implements Environment {
+
+ private final TestState<String> mTimeZoneId = new TestState<>();
+ private final TestState<Integer> mTimeZoneConfidence = new TestState<>();
+ private final List<Runnable> mAsyncRunnables = new ArrayList<>();
+ private @ElapsedRealtimeLong long mElapsedRealtimeMillis;
+ private @CurrentTimeMillisLong long mInitializationTimeMillis;
+
+ FakeEnvironment() {
+ // Ensure the fake environment starts with the defaults a fresh device would.
+ initializeTimeZoneSetting("", TIME_ZONE_CONFIDENCE_LOW);
+ }
+
+ void initializeClock(@CurrentTimeMillisLong long currentTimeMillis,
+ @ElapsedRealtimeLong long elapsedRealtimeMillis) {
+ mInitializationTimeMillis = currentTimeMillis - elapsedRealtimeMillis;
+ mElapsedRealtimeMillis = elapsedRealtimeMillis;
+ }
+
+ void initializeTimeZoneSetting(String zoneId,
+ @SystemTimeZone.TimeZoneConfidence int timeZoneConfidence) {
+ mTimeZoneId.init(zoneId);
+ mTimeZoneConfidence.init(timeZoneConfidence);
+ }
+
+ void incrementClock() {
+ mElapsedRealtimeMillis++;
+ }
+
+ @Override
+ public String getDeviceTimeZone() {
+ return mTimeZoneId.getLatest();
+ }
+
+ @Override
+ public int getDeviceTimeZoneConfidence() {
+ return mTimeZoneConfidence.getLatest();
+ }
+
+ @Override
+ public void setDeviceTimeZoneAndConfidence(
+ String zoneId, @SystemTimeZone.TimeZoneConfidence int confidence, String logInfo) {
+ mTimeZoneId.set(zoneId);
+ mTimeZoneConfidence.set(confidence);
+ }
+
+ void assertTimeZoneNotChanged() {
+ mTimeZoneId.assertHasNotBeenSet();
+ mTimeZoneConfidence.assertHasNotBeenSet();
+ }
+
+ void assertTimeZoneChangedTo(String timeZoneId,
+ @SystemTimeZone.TimeZoneConfidence int confidence) {
+ mTimeZoneId.assertHasBeenSet();
+ mTimeZoneId.assertChangeCount(1);
+ mTimeZoneId.assertLatestEquals(timeZoneId);
+
+ mTimeZoneConfidence.assertHasBeenSet();
+ mTimeZoneConfidence.assertChangeCount(1);
+ mTimeZoneConfidence.assertLatestEquals(confidence);
+ }
+
+ void commitAllChanges() {
+ mTimeZoneId.commitLatest();
+ mTimeZoneConfidence.commitLatest();
+ }
+
+ @Override
+ @ElapsedRealtimeLong
+ public long elapsedRealtimeMillis() {
+ return mElapsedRealtimeMillis;
+ }
+
+ @Override
+ @CurrentTimeMillisLong
+ public long currentTimeMillis() {
+ return mInitializationTimeMillis + mElapsedRealtimeMillis;
+ }
+
+ @Override
+ public void addDebugLogEntry(String logMsg) {
+ // No-op for tests
+ }
+
+ @Override
+ public void dumpDebugLog(PrintWriter printWriter) {
+ // No-op for tests
+ }
+
+ /**
+ * Adds the supplied runnable to a list but does not run them. To run all the runnables that
+ * have been supplied, call {@code #runAsyncRunnables}.
+ */
+ @Override
+ public void runAsync(Runnable runnable) {
+ mAsyncRunnables.add(runnable);
+ }
+
+ /**
+ * Requests that the runnable that have been supplied to {@code #runAsync} are invoked
+ * asynchronously and cleared.
+ */
+ public void runAsyncRunnables() {
+ for (Runnable runnable : mAsyncRunnables) {
+ runnable.run();
+ }
+ mAsyncRunnables.clear();
+ }
+}
diff --git a/services/tests/timetests/src/com/android/server/timezonedetector/NotifyingTimeZoneChangeListenerTest.java b/services/tests/timetests/src/com/android/server/timezonedetector/NotifyingTimeZoneChangeListenerTest.java
index 23c13bd04368..45cc891bff55 100644
--- a/services/tests/timetests/src/com/android/server/timezonedetector/NotifyingTimeZoneChangeListenerTest.java
+++ b/services/tests/timetests/src/com/android/server/timezonedetector/NotifyingTimeZoneChangeListenerTest.java
@@ -85,9 +85,6 @@ public class NotifyingTimeZoneChangeListenerTest {
return List.of(ORIGIN_LOCATION, ORIGIN_TELEPHONY);
}
- private static final long ARBITRARY_ELAPSED_REALTIME_MILLIS = 1234;
- /** A time zone used for initialization that does not occur elsewhere in tests. */
- private static final String ARBITRARY_TIME_ZONE_ID = "Etc/UTC";
private static final String INTERACT_ACROSS_USERS_FULL_PERMISSION =
"android.permission.INTERACT_ACROSS_USERS_FULL";
@@ -99,6 +96,7 @@ public class NotifyingTimeZoneChangeListenerTest {
private HandlerThread mHandlerThread;
private TestHandler mHandler;
private FakeServiceConfigAccessor mServiceConfigAccessor;
+ private FakeEnvironment mFakeEnvironment;
private int mUid;
private NotifyingTimeZoneChangeListener mTimeZoneChangeTracker;
@@ -106,6 +104,9 @@ public class NotifyingTimeZoneChangeListenerTest {
@Before
public void setUp() {
mUid = Process.myUid();
+ mFakeEnvironment = new FakeEnvironment();
+ mFakeEnvironment.initializeClock(1735689600L, 1234L);
+
// Create a thread + handler for processing the work that the service posts.
mHandlerThread = new HandlerThread("TimeZoneDetectorInternalTest");
mHandlerThread.start();
@@ -138,7 +139,7 @@ public class NotifyingTimeZoneChangeListenerTest {
mNotificationManager = new FakeNotificationManager(mContext, InstantSource.system());
mTimeZoneChangeTracker = new NotifyingTimeZoneChangeListener(mHandler, mContext,
- mServiceConfigAccessor, mNotificationManager);
+ mServiceConfigAccessor, mNotificationManager, mFakeEnvironment);
}
@After
@@ -151,7 +152,7 @@ public class NotifyingTimeZoneChangeListenerTest {
public void process_autoDetectionOff_noManualTracking_shouldTrackWithoutNotifying() {
enableTimeZoneNotifications();
- TimeZoneChangeRecord expectedChangeEvent = new TimeZoneChangeRecord(
+ TimeZoneChangeRecord expectedTimeZoneChangeRecord = new TimeZoneChangeRecord(
/* id= */ 1,
new TimeZoneChangeEvent(
/* elapsedRealtimeMillis= */ 0,
@@ -162,13 +163,14 @@ public class NotifyingTimeZoneChangeListenerTest {
/* newZoneId= */ "Europe/London",
/* newConfidence= */ 1,
/* cause= */ "NO_REASON"));
- expectedChangeEvent.setStatus(STATUS_UNTRACKED, SIGNAL_TYPE_NONE);
+ expectedTimeZoneChangeRecord.setStatus(STATUS_UNTRACKED, SIGNAL_TYPE_NONE);
assertNull(mTimeZoneChangeTracker.getLastTimeZoneChangeRecord());
- mTimeZoneChangeTracker.process(expectedChangeEvent.getEvent());
+ mTimeZoneChangeTracker.process(expectedTimeZoneChangeRecord.getEvent());
- assertEquals(expectedChangeEvent, mTimeZoneChangeTracker.getLastTimeZoneChangeRecord());
+ assertEquals(expectedTimeZoneChangeRecord,
+ mTimeZoneChangeTracker.getLastTimeZoneChangeRecord());
assertEquals(0, mNotificationManager.getNotifications().size());
mHandler.assertTotalMessagesEnqueued(0);
}
@@ -177,7 +179,7 @@ public class NotifyingTimeZoneChangeListenerTest {
public void process_autoDetectionOff_shouldTrackWithoutNotifying() {
enableNotificationsWithManualChangeTracking();
- TimeZoneChangeRecord expectedChangeEvent = new TimeZoneChangeRecord(
+ TimeZoneChangeRecord expectedTimeZoneChangeRecord = new TimeZoneChangeRecord(
/* id= */ 1,
new TimeZoneChangeEvent(
/* elapsedRealtimeMillis= */ 0,
@@ -188,13 +190,14 @@ public class NotifyingTimeZoneChangeListenerTest {
/* newZoneId= */ "Europe/London",
/* newConfidence= */ 1,
/* cause= */ "NO_REASON"));
- expectedChangeEvent.setStatus(STATUS_UNTRACKED, SIGNAL_TYPE_NONE);
+ expectedTimeZoneChangeRecord.setStatus(STATUS_UNTRACKED, SIGNAL_TYPE_NONE);
assertNull(mTimeZoneChangeTracker.getLastTimeZoneChangeRecord());
- mTimeZoneChangeTracker.process(expectedChangeEvent.getEvent());
+ mTimeZoneChangeTracker.process(expectedTimeZoneChangeRecord.getEvent());
- assertEquals(expectedChangeEvent, mTimeZoneChangeTracker.getLastTimeZoneChangeRecord());
+ assertEquals(expectedTimeZoneChangeRecord,
+ mTimeZoneChangeTracker.getLastTimeZoneChangeRecord());
assertEquals(0, mNotificationManager.getNotifications().size());
mHandler.assertTotalMessagesEnqueued(1);
}
@@ -214,7 +217,7 @@ public class NotifyingTimeZoneChangeListenerTest {
enableNotificationsWithManualChangeTracking();
- TimeZoneChangeRecord expectedChangeEvent = new TimeZoneChangeRecord(
+ TimeZoneChangeRecord expectedTimeZoneChangeRecord = new TimeZoneChangeRecord(
/* id= */ 1,
new TimeZoneChangeEvent(
/* elapsedRealtimeMillis= */ 0,
@@ -225,19 +228,20 @@ public class NotifyingTimeZoneChangeListenerTest {
/* newZoneId= */ "Europe/London",
/* newConfidence= */ 1,
/* cause= */ "NO_REASON"));
- expectedChangeEvent.setStatus(STATUS_UNKNOWN, SIGNAL_TYPE_UNKNOWN);
+ expectedTimeZoneChangeRecord.setStatus(STATUS_UNKNOWN, SIGNAL_TYPE_UNKNOWN);
assertNull(mTimeZoneChangeTracker.getLastTimeZoneChangeRecord());
// lastTrackedChangeEvent == null
- mTimeZoneChangeTracker.process(expectedChangeEvent.getEvent());
- TimeZoneChangeRecord trackedEvent1 = mTimeZoneChangeTracker.getLastTimeZoneChangeRecord();
+ mTimeZoneChangeTracker.process(expectedTimeZoneChangeRecord.getEvent());
+ TimeZoneChangeRecord timeZoneChangeRecord1 =
+ mTimeZoneChangeTracker.getLastTimeZoneChangeRecord();
- assertEquals(expectedChangeEvent, trackedEvent1);
+ assertEquals(expectedTimeZoneChangeRecord, timeZoneChangeRecord1);
assertEquals(1, mNotificationManager.getNotifications().size());
mHandler.assertTotalMessagesEnqueued(1);
- expectedChangeEvent = new TimeZoneChangeRecord(
+ expectedTimeZoneChangeRecord = new TimeZoneChangeRecord(
/* id= */ 2,
new TimeZoneChangeEvent(
/* elapsedRealtimeMillis= */ 1000L,
@@ -248,14 +252,15 @@ public class NotifyingTimeZoneChangeListenerTest {
/* newZoneId= */ "Europe/Paris",
/* newConfidence= */ 1,
/* cause= */ "NO_REASON"));
- expectedChangeEvent.setStatus(STATUS_UNKNOWN, SIGNAL_TYPE_UNKNOWN);
+ expectedTimeZoneChangeRecord.setStatus(STATUS_UNKNOWN, SIGNAL_TYPE_UNKNOWN);
// lastTrackedChangeEvent != null
- mTimeZoneChangeTracker.process(expectedChangeEvent.getEvent());
- TimeZoneChangeRecord trackedEvent2 = mTimeZoneChangeTracker.getLastTimeZoneChangeRecord();
+ mTimeZoneChangeTracker.process(expectedTimeZoneChangeRecord.getEvent());
+ TimeZoneChangeRecord timeZoneChangeRecord2 =
+ mTimeZoneChangeTracker.getLastTimeZoneChangeRecord();
- assertEquals(STATUS_SUPERSEDED, trackedEvent1.getStatus());
- assertEquals(expectedChangeEvent, trackedEvent2);
+ assertEquals(STATUS_SUPERSEDED, timeZoneChangeRecord1.getStatus());
+ assertEquals(expectedTimeZoneChangeRecord, timeZoneChangeRecord2);
assertEquals(2, mNotificationManager.getNotifications().size());
mHandler.assertTotalMessagesEnqueued(2);
@@ -263,7 +268,7 @@ public class NotifyingTimeZoneChangeListenerTest {
// Test manual change within revert threshold
{
- expectedChangeEvent = new TimeZoneChangeRecord(
+ expectedTimeZoneChangeRecord = new TimeZoneChangeRecord(
/* id= */ 3,
new TimeZoneChangeEvent(
/* elapsedRealtimeMillis= */ 999L + AUTO_REVERT_THRESHOLD,
@@ -275,16 +280,16 @@ public class NotifyingTimeZoneChangeListenerTest {
/* newZoneId= */ "Europe/London",
/* newConfidence= */ 1,
/* cause= */ "NO_REASON"));
- expectedChangeEvent.setStatus(STATUS_UNTRACKED, SIGNAL_TYPE_NONE);
+ expectedTimeZoneChangeRecord.setStatus(STATUS_UNTRACKED, SIGNAL_TYPE_NONE);
- mTimeZoneChangeTracker.process(expectedChangeEvent.getEvent());
- TimeZoneChangeRecord trackedEvent3 =
+ mTimeZoneChangeTracker.process(expectedTimeZoneChangeRecord.getEvent());
+ TimeZoneChangeRecord timeZoneChangeRecord3 =
mTimeZoneChangeTracker.getLastTimeZoneChangeRecord();
// The user manually changed the time zone within a short period of receiving the
// notification, indicating that they rejected the automatic change of time zone
- assertEquals(STATUS_REJECTED, trackedEvent2.getStatus());
- assertEquals(expectedChangeEvent, trackedEvent3);
+ assertEquals(STATUS_REJECTED, timeZoneChangeRecord2.getStatus());
+ assertEquals(expectedTimeZoneChangeRecord, timeZoneChangeRecord3);
assertEquals(2, mNotificationManager.getNotifications().size());
mHandler.assertTotalMessagesEnqueued(3);
}
@@ -293,12 +298,12 @@ public class NotifyingTimeZoneChangeListenerTest {
{
// [START] Reset previous event
enableNotificationsWithManualChangeTracking();
- mTimeZoneChangeTracker.process(trackedEvent2.getEvent());
- trackedEvent2 = mTimeZoneChangeTracker.getLastTimeZoneChangeRecord();
+ mTimeZoneChangeTracker.process(timeZoneChangeRecord2.getEvent());
+ timeZoneChangeRecord2 = mTimeZoneChangeTracker.getLastTimeZoneChangeRecord();
disableTimeZoneAutoDetection();
// [END] Reset previous event
- expectedChangeEvent = new TimeZoneChangeRecord(
+ expectedTimeZoneChangeRecord = new TimeZoneChangeRecord(
/* id= */ 5,
new TimeZoneChangeEvent(
/* elapsedRealtimeMillis= */ 1001L + AUTO_REVERT_THRESHOLD,
@@ -310,15 +315,15 @@ public class NotifyingTimeZoneChangeListenerTest {
/* newZoneId= */ "Europe/London",
/* newConfidence= */ 1,
/* cause= */ "NO_REASON"));
- expectedChangeEvent.setStatus(STATUS_UNTRACKED, SIGNAL_TYPE_NONE);
+ expectedTimeZoneChangeRecord.setStatus(STATUS_UNTRACKED, SIGNAL_TYPE_NONE);
- mTimeZoneChangeTracker.process(expectedChangeEvent.getEvent());
- TimeZoneChangeRecord trackedEvent3 =
+ mTimeZoneChangeTracker.process(expectedTimeZoneChangeRecord.getEvent());
+ TimeZoneChangeRecord timeZoneChangeRecord3 =
mTimeZoneChangeTracker.getLastTimeZoneChangeRecord();
// The user manually changed the time zone outside of the period we consider as a revert
- assertEquals(STATUS_SUPERSEDED, trackedEvent2.getStatus());
- assertEquals(expectedChangeEvent, trackedEvent3);
+ assertEquals(STATUS_SUPERSEDED, timeZoneChangeRecord2.getStatus());
+ assertEquals(expectedTimeZoneChangeRecord, timeZoneChangeRecord3);
assertEquals(3, mNotificationManager.getNotifications().size());
mHandler.assertTotalMessagesEnqueued(5);
}
@@ -339,7 +344,7 @@ public class NotifyingTimeZoneChangeListenerTest {
enableNotificationsWithManualChangeTracking();
- TimeZoneChangeRecord expectedChangeEvent = new TimeZoneChangeRecord(
+ TimeZoneChangeRecord expectedTimeZoneChangeRecord = new TimeZoneChangeRecord(
/* id= */ 1,
new TimeZoneChangeEvent(
/* elapsedRealtimeMillis= */ 0,
@@ -350,19 +355,20 @@ public class NotifyingTimeZoneChangeListenerTest {
/* newZoneId= */ "Europe/London",
/* newConfidence= */ 1,
/* cause= */ "NO_REASON"));
- expectedChangeEvent.setStatus(STATUS_UNKNOWN, SIGNAL_TYPE_UNKNOWN);
+ expectedTimeZoneChangeRecord.setStatus(STATUS_UNKNOWN, SIGNAL_TYPE_UNKNOWN);
assertNull(mTimeZoneChangeTracker.getLastTimeZoneChangeRecord());
// lastTrackedChangeEvent == null
- mTimeZoneChangeTracker.process(expectedChangeEvent.getEvent());
- TimeZoneChangeRecord trackedEvent1 = mTimeZoneChangeTracker.getLastTimeZoneChangeRecord();
+ mTimeZoneChangeTracker.process(expectedTimeZoneChangeRecord.getEvent());
+ TimeZoneChangeRecord timeZoneChangeRecord1 =
+ mTimeZoneChangeTracker.getLastTimeZoneChangeRecord();
- assertEquals(expectedChangeEvent, trackedEvent1);
+ assertEquals(expectedTimeZoneChangeRecord, timeZoneChangeRecord1);
assertEquals(1, mNotificationManager.getNotifications().size());
mHandler.assertTotalMessagesEnqueued(1);
- expectedChangeEvent = new TimeZoneChangeRecord(
+ expectedTimeZoneChangeRecord = new TimeZoneChangeRecord(
/* id= */ 3,
new TimeZoneChangeEvent(
/* elapsedRealtimeMillis= */ 1000L,
@@ -373,14 +379,15 @@ public class NotifyingTimeZoneChangeListenerTest {
/* newZoneId= */ "Europe/Paris",
/* newConfidence= */ 1,
/* cause= */ "NO_REASON"));
- expectedChangeEvent.setStatus(STATUS_UNKNOWN, SIGNAL_TYPE_UNKNOWN);
+ expectedTimeZoneChangeRecord.setStatus(STATUS_UNKNOWN, SIGNAL_TYPE_UNKNOWN);
// lastTrackedChangeEvent != null
- mTimeZoneChangeTracker.process(expectedChangeEvent.getEvent());
- TimeZoneChangeRecord trackedEvent2 = mTimeZoneChangeTracker.getLastTimeZoneChangeRecord();
+ mTimeZoneChangeTracker.process(expectedTimeZoneChangeRecord.getEvent());
+ TimeZoneChangeRecord timeZoneChangeRecord2 =
+ mTimeZoneChangeTracker.getLastTimeZoneChangeRecord();
- assertEquals(STATUS_SUPERSEDED, trackedEvent1.getStatus());
- assertEquals(expectedChangeEvent, trackedEvent2);
+ assertEquals(STATUS_SUPERSEDED, timeZoneChangeRecord1.getStatus());
+ assertEquals(expectedTimeZoneChangeRecord, timeZoneChangeRecord2);
assertEquals(2, mNotificationManager.getNotifications().size());
mHandler.assertTotalMessagesEnqueued(2);
}
@@ -400,7 +407,7 @@ public class NotifyingTimeZoneChangeListenerTest {
enableNotificationsWithManualChangeTracking();
- TimeZoneChangeRecord expectedChangeEvent = new TimeZoneChangeRecord(
+ TimeZoneChangeRecord expectedTimeZoneChangeRecord = new TimeZoneChangeRecord(
/* id= */ 1,
new TimeZoneChangeEvent(
/* elapsedRealtimeMillis= */ 0,
@@ -411,15 +418,16 @@ public class NotifyingTimeZoneChangeListenerTest {
/* newZoneId= */ "Europe/Rome",
/* newConfidence= */ 1,
/* cause= */ "NO_REASON"));
- expectedChangeEvent.setStatus(STATUS_UNKNOWN, SIGNAL_TYPE_UNKNOWN);
+ expectedTimeZoneChangeRecord.setStatus(STATUS_UNKNOWN, SIGNAL_TYPE_UNKNOWN);
assertNull(mTimeZoneChangeTracker.getLastTimeZoneChangeRecord());
// lastTrackedChangeEvent == null
- mTimeZoneChangeTracker.process(expectedChangeEvent.getEvent());
- TimeZoneChangeRecord trackedEvent1 = mTimeZoneChangeTracker.getLastTimeZoneChangeRecord();
+ mTimeZoneChangeTracker.process(expectedTimeZoneChangeRecord.getEvent());
+ TimeZoneChangeRecord timeZoneChangeRecord1 =
+ mTimeZoneChangeTracker.getLastTimeZoneChangeRecord();
- assertEquals(expectedChangeEvent, trackedEvent1);
+ assertEquals(expectedTimeZoneChangeRecord, timeZoneChangeRecord1);
// No notification sent for the same UTC offset
assertEquals(0, mNotificationManager.getNotifications().size());
mHandler.assertTotalMessagesEnqueued(1);
diff --git a/services/tests/timetests/src/com/android/server/timezonedetector/TimeZoneDetectorStrategyImplTest.java b/services/tests/timetests/src/com/android/server/timezonedetector/TimeZoneDetectorStrategyImplTest.java
index 9a01fa2eb966..44232e7ddfa2 100644
--- a/services/tests/timetests/src/com/android/server/timezonedetector/TimeZoneDetectorStrategyImplTest.java
+++ b/services/tests/timetests/src/com/android/server/timezonedetector/TimeZoneDetectorStrategyImplTest.java
@@ -63,7 +63,6 @@ import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
-import android.annotation.ElapsedRealtimeLong;
import android.annotation.NonNull;
import android.annotation.UserIdInt;
import android.app.time.LocationTimeZoneAlgorithmStatus;
@@ -94,7 +93,6 @@ import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
-import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
@@ -114,6 +112,7 @@ public class TimeZoneDetectorStrategyImplTest {
@Rule
public final SetFlagsRule mSetFlagsRule = mClassRule.createSetFlagsRule();
+ private static final long ARBITRARY_CURRENT_TIME_MILLIS = 1735689600; // 2025-01-01 00:00:00
private static final long ARBITRARY_ELAPSED_REALTIME_MILLIS = 1234;
/** A time zone used for initialization that does not occur elsewhere in tests. */
private static final String ARBITRARY_TIME_ZONE_ID = "Etc/UTC";
@@ -1220,7 +1219,7 @@ public class TimeZoneDetectorStrategyImplTest {
.build();
Script script = new Script()
- .initializeClock(ARBITRARY_ELAPSED_REALTIME_MILLIS)
+ .initializeClock(ARBITRARY_CURRENT_TIME_MILLIS, ARBITRARY_ELAPSED_REALTIME_MILLIS)
.initializeTimeZoneSetting(ARBITRARY_TIME_ZONE_ID, TIME_ZONE_CONFIDENCE_LOW)
.simulateConfigurationInternalChange(config)
.resetConfigurationTracking();
@@ -1420,7 +1419,7 @@ public class TimeZoneDetectorStrategyImplTest {
.build();
Script script = new Script()
- .initializeClock(ARBITRARY_ELAPSED_REALTIME_MILLIS)
+ .initializeClock(ARBITRARY_CURRENT_TIME_MILLIS, ARBITRARY_ELAPSED_REALTIME_MILLIS)
.initializeTimeZoneSetting(ARBITRARY_TIME_ZONE_ID, TIME_ZONE_CONFIDENCE_LOW)
.simulateConfigurationInternalChange(config)
.resetConfigurationTracking();
@@ -1591,7 +1590,7 @@ public class TimeZoneDetectorStrategyImplTest {
.build();
Script script = new Script()
- .initializeClock(ARBITRARY_ELAPSED_REALTIME_MILLIS)
+ .initializeClock(ARBITRARY_CURRENT_TIME_MILLIS, ARBITRARY_ELAPSED_REALTIME_MILLIS)
.initializeTimeZoneSetting(ARBITRARY_TIME_ZONE_ID, TIME_ZONE_CONFIDENCE_LOW)
.simulateConfigurationInternalChange(config)
.resetConfigurationTracking();
@@ -1666,7 +1665,7 @@ public class TimeZoneDetectorStrategyImplTest {
@Test
public void testGetTimeZoneState() {
Script script = new Script()
- .initializeClock(ARBITRARY_ELAPSED_REALTIME_MILLIS)
+ .initializeClock(ARBITRARY_CURRENT_TIME_MILLIS, ARBITRARY_ELAPSED_REALTIME_MILLIS)
.initializeTimeZoneSetting(ARBITRARY_TIME_ZONE_ID, TIME_ZONE_CONFIDENCE_LOW)
.simulateConfigurationInternalChange(CONFIG_AUTO_DISABLED_GEO_DISABLED)
.resetConfigurationTracking();
@@ -1688,7 +1687,7 @@ public class TimeZoneDetectorStrategyImplTest {
@Test
public void testSetTimeZoneState() {
Script script = new Script()
- .initializeClock(ARBITRARY_ELAPSED_REALTIME_MILLIS)
+ .initializeClock(ARBITRARY_CURRENT_TIME_MILLIS, ARBITRARY_ELAPSED_REALTIME_MILLIS)
.initializeTimeZoneSetting(ARBITRARY_TIME_ZONE_ID, TIME_ZONE_CONFIDENCE_LOW)
.simulateConfigurationInternalChange(CONFIG_AUTO_DISABLED_GEO_DISABLED)
.resetConfigurationTracking();
@@ -1715,7 +1714,7 @@ public class TimeZoneDetectorStrategyImplTest {
private void testConfirmTimeZone(ConfigurationInternal config) {
String timeZoneId = "Europe/London";
Script script = new Script()
- .initializeClock(ARBITRARY_ELAPSED_REALTIME_MILLIS)
+ .initializeClock(ARBITRARY_CURRENT_TIME_MILLIS, ARBITRARY_ELAPSED_REALTIME_MILLIS)
.initializeTimeZoneSetting(timeZoneId, TIME_ZONE_CONFIDENCE_LOW)
.simulateConfigurationInternalChange(config)
.resetConfigurationTracking();
@@ -1902,97 +1901,6 @@ public class TimeZoneDetectorStrategyImplTest {
mFakeEnvironment.elapsedRealtimeMillis(), Arrays.asList(zoneIds));
}
- static class FakeEnvironment implements TimeZoneDetectorStrategyImpl.Environment {
-
- private final TestState<String> mTimeZoneId = new TestState<>();
- private final TestState<Integer> mTimeZoneConfidence = new TestState<>();
- private final List<Runnable> mAsyncRunnables = new ArrayList<>();
- private @ElapsedRealtimeLong long mElapsedRealtimeMillis;
-
- FakeEnvironment() {
- // Ensure the fake environment starts with the defaults a fresh device would.
- initializeTimeZoneSetting("", TIME_ZONE_CONFIDENCE_LOW);
- }
-
- void initializeClock(@ElapsedRealtimeLong long elapsedRealtimeMillis) {
- mElapsedRealtimeMillis = elapsedRealtimeMillis;
- }
-
- void initializeTimeZoneSetting(String zoneId, @TimeZoneConfidence int timeZoneConfidence) {
- mTimeZoneId.init(zoneId);
- mTimeZoneConfidence.init(timeZoneConfidence);
- }
-
- void incrementClock() {
- mElapsedRealtimeMillis++;
- }
-
- @Override
- public String getDeviceTimeZone() {
- return mTimeZoneId.getLatest();
- }
-
- @Override
- public int getDeviceTimeZoneConfidence() {
- return mTimeZoneConfidence.getLatest();
- }
-
- @Override
- public void setDeviceTimeZoneAndConfidence(
- String zoneId, @TimeZoneConfidence int confidence, String logInfo) {
- mTimeZoneId.set(zoneId);
- mTimeZoneConfidence.set(confidence);
- }
-
- void assertTimeZoneNotChanged() {
- mTimeZoneId.assertHasNotBeenSet();
- mTimeZoneConfidence.assertHasNotBeenSet();
- }
-
- void assertTimeZoneChangedTo(String timeZoneId, @TimeZoneConfidence int confidence) {
- mTimeZoneId.assertHasBeenSet();
- mTimeZoneId.assertChangeCount(1);
- mTimeZoneId.assertLatestEquals(timeZoneId);
-
- mTimeZoneConfidence.assertHasBeenSet();
- mTimeZoneConfidence.assertChangeCount(1);
- mTimeZoneConfidence.assertLatestEquals(confidence);
- }
-
- void commitAllChanges() {
- mTimeZoneId.commitLatest();
- mTimeZoneConfidence.commitLatest();
- }
-
- @Override
- @ElapsedRealtimeLong
- public long elapsedRealtimeMillis() {
- return mElapsedRealtimeMillis;
- }
-
- @Override
- public void addDebugLogEntry(String logMsg) {
- // No-op for tests
- }
-
- @Override
- public void dumpDebugLog(PrintWriter printWriter) {
- // No-op for tests
- }
-
- @Override
- public void runAsync(Runnable runnable) {
- mAsyncRunnables.add(runnable);
- }
-
- public void runAsyncRunnables() {
- for (Runnable runnable : mAsyncRunnables) {
- runnable.run();
- }
- mAsyncRunnables.clear();
- }
- }
-
private void assertStateChangeNotificationsSent(
TestStateChangeListener stateChangeListener, int expectedCount) {
// The fake environment needs to be told to run posted work.
@@ -2013,8 +1921,8 @@ public class TimeZoneDetectorStrategyImplTest {
return this;
}
- Script initializeClock(long elapsedRealtimeMillis) {
- mFakeEnvironment.initializeClock(elapsedRealtimeMillis);
+ Script initializeClock(long currentTimeMillis, long elapsedRealtimeMillis) {
+ mFakeEnvironment.initializeClock(currentTimeMillis, elapsedRealtimeMillis);
return this;
}
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationAssistantsTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationAssistantsTest.java
index 4f5cdb73edd2..1df8e3deb84b 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationAssistantsTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationAssistantsTest.java
@@ -64,6 +64,8 @@ import android.testing.TestableContext;
import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.IntArray;
+import android.util.StatsEvent;
+import android.util.StatsEventTestUtils;
import android.util.Xml;
import androidx.test.runner.AndroidJUnit4;
@@ -71,9 +73,17 @@ import androidx.test.runner.AndroidJUnit4;
import com.android.internal.util.CollectionUtils;
import com.android.modules.utils.TypedXmlPullParser;
import com.android.modules.utils.TypedXmlSerializer;
+import com.android.os.AtomsProto;
+import com.android.os.notification.NotificationBundlePreferences;
+import com.android.os.notification.NotificationExtensionAtoms;
+import com.android.os.notification.NotificationProtoEnums;
import com.android.server.UiServiceTestCase;
import com.android.server.notification.NotificationManagerService.NotificationAssistants;
+import com.google.protobuf.CodedInputStream;
+import com.google.protobuf.CodedOutputStream;
+import com.google.protobuf.ExtensionRegistryLite;
+
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
@@ -120,6 +130,8 @@ public class NotificationAssistantsTest extends UiServiceTestCase {
ComponentName mCn = new ComponentName("a", "b");
+ private ExtensionRegistryLite mRegistry;
+
// Helper function to hold mApproved lock, avoid GuardedBy lint errors
private boolean isUserSetServicesEmpty(NotificationAssistants assistant, int userId) {
@@ -204,6 +216,8 @@ public class NotificationAssistantsTest extends UiServiceTestCase {
when(mUserProfiles.getCurrentProfileIds()).thenReturn(profileIds);
when(mNm.isNASMigrationDone(anyInt())).thenReturn(true);
when(mNm.canUseManagedServices(any(), anyInt(), any())).thenReturn(true);
+ mRegistry = ExtensionRegistryLite.newInstance();
+ NotificationExtensionAtoms.registerAllExtensions(mRegistry);
}
@Test
@@ -749,6 +763,28 @@ public class NotificationAssistantsTest extends UiServiceTestCase {
}
@Test
+ @EnableFlags({android.service.notification.Flags.FLAG_NOTIFICATION_CLASSIFICATION,
+ android.app.Flags.FLAG_NOTIFICATION_CLASSIFICATION_UI})
+ public void testGetPackagesWithKeyTypeAdjustmentSettings() throws Exception {
+ String pkg = "my.package";
+ String pkg2 = "my.package.2";
+ setDefaultAllowedAdjustmentKeyTypes(mAssistants);
+ assertThat(mAssistants.isTypeAdjustmentAllowedForPackage(pkg, TYPE_PROMOTION)).isTrue();
+ assertThat(mAssistants.getPackagesWithKeyTypeAdjustmentSettings()).isEmpty();
+
+ mAssistants.setAssistantAdjustmentKeyTypeStateForPackage(pkg, TYPE_PROMOTION, true);
+ assertThat(mAssistants.getPackagesWithKeyTypeAdjustmentSettings())
+ .containsExactly(pkg);
+ mAssistants.setAssistantAdjustmentKeyTypeStateForPackage(pkg, TYPE_PROMOTION, false);
+ assertThat(mAssistants.getPackagesWithKeyTypeAdjustmentSettings())
+ .containsExactly(pkg);
+ mAssistants.setAssistantAdjustmentKeyTypeStateForPackage(pkg2, TYPE_NEWS, true);
+ mAssistants.setAssistantAdjustmentKeyTypeStateForPackage(pkg2, TYPE_PROMOTION, false);
+ assertThat(mAssistants.getPackagesWithKeyTypeAdjustmentSettings())
+ .containsExactly(pkg, pkg2);
+ }
+
+ @Test
@EnableFlags(android.app.Flags.FLAG_NOTIFICATION_CLASSIFICATION_UI)
public void testSetAssistantAdjustmentKeyTypeStateForPackage_usesGlobalDefault() {
String pkg = "my.package";
@@ -892,4 +928,88 @@ public class NotificationAssistantsTest extends UiServiceTestCase {
assertThat(mAssistants.getAllowedAdjustmentKeyTypesForPackage(pkg)).asList()
.containsExactly(TYPE_NEWS, TYPE_PROMOTION, TYPE_SOCIAL_MEDIA);
}
+
+ @Test
+ @SuppressWarnings("GuardedBy")
+ @EnableFlags({android.service.notification.Flags.FLAG_NOTIFICATION_CLASSIFICATION,
+ android.app.Flags.FLAG_NOTIFICATION_CLASSIFICATION_UI})
+ public void testPullBundlePreferencesStats_fillsOutStatsEvent()
+ throws Exception {
+ // Create the current user and enable the package
+ int userId = ActivityManager.getCurrentUser();
+ mAssistants.loadDefaultsFromConfig(true);
+ mAssistants.setPackageOrComponentEnabled(mCn.flattenToString(), userId, true,
+ true, true);
+ ManagedServices.ManagedServiceInfo info =
+ mAssistants.new ManagedServiceInfo(null, mCn, userId, false, null, 35, 2345256);
+
+ // Ensure bundling is enabled
+ mAssistants.setAdjustmentTypeSupportedState(info, Adjustment.KEY_TYPE, true);
+ // Enable these specific bundle types
+ mAssistants.setAssistantAdjustmentKeyTypeState(TYPE_PROMOTION, false);
+ mAssistants.setAssistantAdjustmentKeyTypeState(TYPE_NEWS, true);
+ mAssistants.setAssistantAdjustmentKeyTypeState(TYPE_CONTENT_RECOMMENDATION, true);
+
+ // When pullBundlePreferencesStats is run with the given preferences
+ ArrayList<StatsEvent> events = new ArrayList<>();
+ mAssistants.pullBundlePreferencesStats(events);
+
+ // The StatsEvent is filled out with the expected NotificationBundlePreferences values.
+ assertThat(events.size()).isEqualTo(1);
+ AtomsProto.Atom atom = StatsEventTestUtils.convertToAtom(events.get(0));
+
+ // The returned atom does not have external extensions registered.
+ // So we serialize and then deserialize with extensions registered.
+ ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
+ CodedOutputStream codedos = CodedOutputStream.newInstance(outputStream);
+ atom.writeTo(codedos);
+ codedos.flush();
+
+ ByteArrayInputStream inputStream = new ByteArrayInputStream(outputStream.toByteArray());
+ CodedInputStream codedis = CodedInputStream.newInstance(inputStream);
+ atom = AtomsProto.Atom.parseFrom(codedis, mRegistry);
+ assertTrue(atom.hasExtension(NotificationExtensionAtoms.notificationBundlePreferences));
+ NotificationBundlePreferences p =
+ atom.getExtension(NotificationExtensionAtoms.notificationBundlePreferences);
+ assertThat(p.getBundlesAllowed()).isTrue();
+ assertThat(p.getAllowedBundleTypes(0).getNumber())
+ .isEqualTo(NotificationProtoEnums.TYPE_NEWS);
+ assertThat(p.getAllowedBundleTypes(1).getNumber())
+ .isEqualTo(NotificationProtoEnums.TYPE_CONTENT_RECOMMENDATION);
+
+ // Disable the top-level bundling setting
+ mAssistants.setAdjustmentTypeSupportedState(info, Adjustment.KEY_TYPE, false);
+ // Enable these specific bundle types
+ mAssistants.setAssistantAdjustmentKeyTypeState(TYPE_PROMOTION, true);
+ mAssistants.setAssistantAdjustmentKeyTypeState(TYPE_NEWS, false);
+ mAssistants.setAssistantAdjustmentKeyTypeState(TYPE_CONTENT_RECOMMENDATION, true);
+
+ ArrayList<StatsEvent> eventsDisabled = new ArrayList<>();
+ mAssistants.pullBundlePreferencesStats(eventsDisabled);
+
+ // The StatsEvent is filled out with the expected NotificationBundlePreferences values.
+ assertThat(eventsDisabled.size()).isEqualTo(1);
+ AtomsProto.Atom atomDisabled = StatsEventTestUtils.convertToAtom(eventsDisabled.get(0));
+
+ // The returned atom does not have external extensions registered.
+ // So we serialize and then deserialize with extensions registered.
+ outputStream = new ByteArrayOutputStream();
+ codedos = CodedOutputStream.newInstance(outputStream);
+ atomDisabled.writeTo(codedos);
+ codedos.flush();
+
+ inputStream = new ByteArrayInputStream(outputStream.toByteArray());
+ codedis = CodedInputStream.newInstance(inputStream);
+ atomDisabled = AtomsProto.Atom.parseFrom(codedis, mRegistry);
+ assertTrue(atomDisabled.hasExtension(NotificationExtensionAtoms
+ .notificationBundlePreferences));
+
+ NotificationBundlePreferences p2 =
+ atomDisabled.getExtension(NotificationExtensionAtoms.notificationBundlePreferences);
+ assertThat(p2.getBundlesAllowed()).isFalse();
+ assertThat(p2.getAllowedBundleTypes(0).getNumber())
+ .isEqualTo(NotificationProtoEnums.TYPE_PROMOTION);
+ assertThat(p2.getAllowedBundleTypes(1).getNumber())
+ .isEqualTo(NotificationProtoEnums.TYPE_CONTENT_RECOMMENDATION);
+ }
} \ No newline at end of file
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java
index f41805d40b0d..704b580a80b0 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java
@@ -19,6 +19,7 @@ import static android.app.AppOpsManager.MODE_ALLOWED;
import static android.app.AppOpsManager.MODE_DEFAULT;
import static android.app.AppOpsManager.OP_SYSTEM_ALERT_WINDOW;
import static android.app.Flags.FLAG_MODES_UI;
+import static android.app.Flags.FLAG_NOTIFICATION_CLASSIFICATION_UI;
import static android.app.Notification.VISIBILITY_PRIVATE;
import static android.app.Notification.VISIBILITY_SECRET;
import static android.app.NotificationChannel.ALLOW_BUBBLE_ON;
@@ -115,6 +116,7 @@ import android.content.IContentProvider;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
+import android.content.pm.ParceledListSlice;
import android.content.pm.Signature;
import android.content.pm.UserInfo;
import android.content.res.Resources;
@@ -161,6 +163,7 @@ import com.android.modules.utils.TypedXmlSerializer;
import com.android.os.AtomsProto;
import com.android.os.AtomsProto.PackageNotificationChannelPreferences;
import com.android.os.AtomsProto.PackageNotificationPreferences;
+import com.android.os.notification.NotificationProtoEnums;
import com.android.server.UiServiceTestCase;
import com.android.server.notification.PermissionHelper.PackagePermission;
import com.android.server.uri.UriGrantsManagerInternal;
@@ -256,7 +259,8 @@ public class PreferencesHelperTest extends UiServiceTestCase {
public static List<FlagsParameterization> getParams() {
return FlagsParameterization.allCombinationsOf(
android.app.Flags.FLAG_API_RICH_ONGOING,
- FLAG_NOTIFICATION_CLASSIFICATION, FLAG_MODES_UI);
+ FLAG_NOTIFICATION_CLASSIFICATION, FLAG_NOTIFICATION_CLASSIFICATION_UI,
+ FLAG_MODES_UI);
}
public PreferencesHelperTest(FlagsParameterization flags) {
@@ -6135,6 +6139,7 @@ public class PreferencesHelperTest extends UiServiceTestCase {
}
@Test
+ @DisableFlags({FLAG_NOTIFICATION_CLASSIFICATION_UI})
public void testPullPackagePreferencesStats_postPermissionMigration()
throws InvalidProtocolBufferException {
// make sure there's at least one channel for each package we want to test
@@ -6155,6 +6160,11 @@ public class PreferencesHelperTest extends UiServiceTestCase {
mHelper.canShowBadge(PKG_O, UID_O);
mHelper.canShowBadge(PKG_P, UID_P);
+ ArrayList<StatsEvent> events = new ArrayList<>();
+
+ mHelper.pullPackagePreferencesStats(events, appPermissions,
+ new ArrayMap<String, Set<Integer>>());
+
// expected output. format: uid -> importance, as only uid (and not package name)
// is in PackageNotificationPreferences
ArrayMap<Integer, Pair<Integer, Boolean>> expected = new ArrayMap<>();
@@ -6162,9 +6172,6 @@ public class PreferencesHelperTest extends UiServiceTestCase {
expected.put(UID_O, new Pair<>(IMPORTANCE_NONE, true)); // banned by permissions
expected.put(UID_P, new Pair<>(IMPORTANCE_UNSPECIFIED, false)); // default: unspecified
- ArrayList<StatsEvent> events = new ArrayList<>();
- mHelper.pullPackagePreferencesStats(events, appPermissions);
-
assertEquals("total number of packages", 3, events.size());
for (StatsEvent ev : events) {
AtomsProto.Atom atom = StatsEventTestUtils.convertToAtom(ev);
@@ -6180,6 +6187,74 @@ public class PreferencesHelperTest extends UiServiceTestCase {
}
@Test
+ @EnableFlags({FLAG_NOTIFICATION_CLASSIFICATION, FLAG_NOTIFICATION_CLASSIFICATION_UI})
+ public void testPullPackagePreferencesStats_createsExpectedStatsEvents()
+ throws InvalidProtocolBufferException {
+ // make sure there's at least one channel for each package we want to test
+ NotificationChannel channelA = new NotificationChannel("a", "a", IMPORTANCE_DEFAULT);
+ mHelper.createNotificationChannel(PKG_N_MR1, UID_N_MR1, channelA, true, false,
+ UID_N_MR1, false);
+ NotificationChannel channelB = new NotificationChannel("b", "b", IMPORTANCE_DEFAULT);
+ mHelper.createNotificationChannel(PKG_O, UID_O, channelB, true, false, UID_O, false);
+ NotificationChannel channelC = new NotificationChannel("c", "c", IMPORTANCE_DEFAULT);
+ mHelper.createNotificationChannel(PKG_P, UID_P, channelC, true, false, UID_P, false);
+
+ // build a collection of app permissions that should be passed in and used
+ ArrayMap<Pair<Integer, String>, Pair<Boolean, Boolean>> pkgPermissions = new ArrayMap<>();
+ pkgPermissions.put(new Pair<>(UID_N_MR1, PKG_N_MR1), new Pair<>(true, false));
+ pkgPermissions.put(new Pair<>(UID_O, PKG_O), new Pair<>(false, true)); // in local prefs
+
+ // local preferences
+ mHelper.canShowBadge(PKG_O, UID_O);
+ mHelper.canShowBadge(PKG_P, UID_P);
+
+ // Sets bundles_allowed to true for these packages.
+ ArrayMap<String, Set<Integer>> packageSpecificAdjustmentKeyTypes = new ArrayMap<>();
+ Set<Integer> nMr1BundlesSet = new ArraySet<Integer>();
+ nMr1BundlesSet.add(TYPE_NEWS);
+ nMr1BundlesSet.add(TYPE_SOCIAL_MEDIA);
+ packageSpecificAdjustmentKeyTypes.put(PKG_N_MR1, nMr1BundlesSet);
+ Set<Integer> pBundlesSet = new ArraySet<Integer>();
+ packageSpecificAdjustmentKeyTypes.put(PKG_P, pBundlesSet);
+
+ ArrayList<StatsEvent> events = new ArrayList<>();
+
+ mHelper.pullPackagePreferencesStats(events, pkgPermissions,
+ packageSpecificAdjustmentKeyTypes);
+
+ assertEquals("total number of packages", 3, events.size());
+
+ AtomsProto.Atom atom0 = StatsEventTestUtils.convertToAtom(events.get(0));
+ assertTrue(atom0.hasPackageNotificationPreferences());
+ PackageNotificationPreferences p0 = atom0.getPackageNotificationPreferences();
+ assertThat(p0.getUid()).isEqualTo(UID_O);
+ assertThat(p0.getImportance()).isEqualTo(IMPORTANCE_NONE); // banned by permissions
+ assertThat(p0.getUserSetImportance()).isTrue();
+ assertThat(p0.getAllowedBundleTypesList()).hasSize(0);
+
+ AtomsProto.Atom atom1 = StatsEventTestUtils.convertToAtom(events.get(1));
+ assertTrue(atom1.hasPackageNotificationPreferences());
+ PackageNotificationPreferences p1 = atom1.getPackageNotificationPreferences();
+ assertThat(p1.getUid()).isEqualTo(UID_N_MR1);
+ assertThat(p1.getImportance()).isEqualTo(IMPORTANCE_DEFAULT);
+ assertThat(p1.getUserSetImportance()).isFalse();
+ assertThat(p1.getAllowedBundleTypesList()).hasSize(2);
+
+ assertThat(p1.getAllowedBundleTypes(0).getNumber())
+ .isEqualTo(NotificationProtoEnums.TYPE_SOCIAL_MEDIA);
+ assertThat(p1.getAllowedBundleTypes(1).getNumber())
+ .isEqualTo(NotificationProtoEnums.TYPE_NEWS);
+
+ AtomsProto.Atom atom2 = StatsEventTestUtils.convertToAtom(events.get(2));
+ assertTrue(atom2.hasPackageNotificationPreferences());
+ PackageNotificationPreferences p2 = atom2.getPackageNotificationPreferences();
+ assertThat(p2.getUid()).isEqualTo(UID_P);
+ assertThat(p2.getImportance()).isEqualTo(IMPORTANCE_UNSPECIFIED); // default: unspecified
+ assertThat(p2.getUserSetImportance()).isFalse();
+ assertThat(p2.getAllowedBundleTypesList()).hasSize(0);
+ }
+
+ @Test
public void testUnlockNotificationChannelImportance() {
NotificationChannel channel = new NotificationChannel("a", "a", IMPORTANCE_LOW);
mHelper.createNotificationChannel(PKG_O, UID_O, channel, true, false, UID_O, false);
@@ -6760,6 +6835,41 @@ public class PreferencesHelperTest extends UiServiceTestCase {
assertThat(mHelper.hasCacheBeenInvalidated()).isFalse();
}
+ @Test
+ @EnableFlags(android.app.Flags.FLAG_NM_BINDER_PERF_CACHE_CHANNELS)
+ public void testGetNotificationChannels_createIfNeeded() {
+ // Test setup hasn't created any channels or read package preferences yet.
+ // If we ask for notification channels _without_ creating, we should get no result.
+ ParceledListSlice<NotificationChannel> channels = mHelper.getNotificationChannels(PKG_N_MR1,
+ UID_N_MR1, false, false, /* createPrefsIfNeeded= */ false);
+ assertThat(channels.getList().size()).isEqualTo(0);
+
+ // If we ask it to create package preferences, we expect the default channel to be created
+ // for N_MR1.
+ channels = mHelper.getNotificationChannels(PKG_N_MR1, UID_N_MR1, false,
+ false, /* createPrefsIfNeeded= */ true);
+ assertThat(channels.getList().size()).isEqualTo(1);
+ assertThat(channels.getList().getFirst().getId()).isEqualTo(
+ NotificationChannel.DEFAULT_CHANNEL_ID);
+ }
+
+ @Test
+ @DisableFlags(android.app.Flags.FLAG_NM_BINDER_PERF_CACHE_CHANNELS)
+ public void testGetNotificationChannels_neverCreatesWhenFlagOff() {
+ ParceledListSlice<NotificationChannel> channels;
+ try {
+ channels = mHelper.getNotificationChannels(PKG_N_MR1, UID_N_MR1, false,
+ false, /* createPrefsIfNeeded= */ true);
+ } catch (Exception e) {
+ // Slog.wtf kicks in, presumably
+ } finally {
+ channels = mHelper.getNotificationChannels(PKG_N_MR1, UID_N_MR1, false,
+ false, /* createPrefsIfNeeded= */ false);
+ assertThat(channels.getList().size()).isEqualTo(0);
+ }
+
+ }
+
// Test version of PreferencesHelper whose only functional difference is that it does not
// interact with the real IpcDataCache, and instead tracks whether or not the cache has been
// invalidated since creation or the last reset.
diff --git a/services/tests/wmtests/Android.bp b/services/tests/wmtests/Android.bp
index 132f95bea360..b328fc2d5868 100644
--- a/services/tests/wmtests/Android.bp
+++ b/services/tests/wmtests/Android.bp
@@ -11,19 +11,46 @@ package {
default_applicable_licenses: ["frameworks_base_license"],
}
-// Include all test java files.
filegroup {
- name: "wmtests-sources",
+ name: "wmtests-support-sources",
srcs: [
- "src/**/*.java",
+ "src/com/android/server/wm/WindowManagerServiceTestSupport.kt",
],
+ path: "src",
+ visibility: ["//visibility:private"],
+}
+
+java_library {
+ name: "wmtests-support",
+ srcs: [":wmtests-support-sources"],
+ static_libs: [
+ "com.android.window.flags.window-aconfig-java",
+ "kotlin-stdlib",
+ "services.core",
+ ],
+ lint: {
+ test: true,
+ },
+ visibility: [
+ "//frameworks/base/services/tests/wmtests",
+ "//frameworks/opt/car/services/updatableServices/tests",
+ ],
+}
+
+// Include all test files, but exclude test support files.
+filegroup {
+ name: "wmtests-sources",
+ srcs: ["src/**/*.java"],
+ exclude_srcs: [":wmtests-support-sources"],
+ path: "src",
+ visibility: ["//visibility:private"],
}
java_genrule {
name: "wmtests.protologsrc",
srcs: [
- ":protolog-impl",
":protolog-groups",
+ ":protolog-impl",
":wmtests-sources",
],
tools: ["protologtool"],
@@ -52,33 +79,34 @@ android_test {
],
static_libs: [
- "frameworks-base-testutils",
- "services.core",
- "service-permission.stubs.system_server",
- "androidx.test.runner",
+ "CtsSurfaceValidatorLib",
+ "android.view.inputmethod.flags-aconfig-java",
"androidx.test.rules",
+ "androidx.test.runner",
+ "com.android.window.flags.window-aconfig-java",
+ "flag-junit",
"flickerlib",
+ "frameworks-base-testutils",
+ "hamcrest-library",
"junit-params",
+ "mockito-kotlin2",
"mockito-target-extended-minus-junit4",
+ "platform-compat-test-rules",
"platform-test-annotations",
+ "service-permission.stubs.system_server",
+ "service-sdksandbox.impl",
+ "services.core",
"servicestests-utils",
+ "testables",
"testng",
"truth",
- "testables",
- "hamcrest-library",
- "flag-junit",
- "platform-compat-test-rules",
- "CtsSurfaceValidatorLib",
- "service-sdksandbox.impl",
- "com.android.window.flags.window-aconfig-java",
- "android.view.inputmethod.flags-aconfig-java",
- "flag-junit",
+ "wmtests-support",
],
libs: [
"android.hardware.power-V1-java",
- "android.test.mock.stubs.system",
"android.test.base.stubs.system",
+ "android.test.mock.stubs.system",
"android.test.runner.stubs.system",
],
@@ -94,8 +122,8 @@ android_test {
platform_apis: true,
test_suites: [
- "device-tests",
"automotive-tests",
+ "device-tests",
],
certificate: "platform",
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
index 6fad82b26808..c88d5153ed66 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
@@ -692,7 +692,7 @@ public class ActivityRecordTests extends WindowTestsBase {
// Asserts fixed orientation request is not ignored, and the orientation is changed.
assertNotEquals(activityCurOrientation, activity.getConfiguration().orientation);
- assertTrue(activity.mAppCompatController.getAppCompatAspectRatioPolicy()
+ assertTrue(activity.mAppCompatController.getAspectRatioPolicy()
.isLetterboxedForFixedOrientationAndAspectRatio());
}
@@ -721,13 +721,13 @@ public class ActivityRecordTests extends WindowTestsBase {
assertEquals(ORIENTATION_PORTRAIT, activity.getConfiguration().orientation);
// Clear size compat.
- activity.mAppCompatController.getAppCompatSizeCompatModePolicy().clearSizeCompatMode();
+ activity.mAppCompatController.getSizeCompatModePolicy().clearSizeCompatMode();
activity.ensureActivityConfiguration();
mDisplayContent.sendNewConfiguration();
// Relaunching the app should still respect the orientation request.
assertEquals(ORIENTATION_PORTRAIT, activity.getConfiguration().orientation);
- assertTrue(activity.mAppCompatController.getAppCompatAspectRatioPolicy()
+ assertTrue(activity.mAppCompatController.getAspectRatioPolicy()
.isLetterboxedForFixedOrientationAndAspectRatio());
}
@@ -742,7 +742,7 @@ public class ActivityRecordTests extends WindowTestsBase {
assertEquals(ORIENTATION_LANDSCAPE, activity.getTask().getConfiguration().orientation);
// The app should be letterboxed.
assertEquals(ORIENTATION_PORTRAIT, activity.getConfiguration().orientation);
- assertTrue(activity.mAppCompatController.getAppCompatAspectRatioPolicy()
+ assertTrue(activity.mAppCompatController.getAspectRatioPolicy()
.isLetterboxedForFixedOrientationAndAspectRatio());
}
@@ -755,7 +755,7 @@ public class ActivityRecordTests extends WindowTestsBase {
assertEquals(ORIENTATION_LANDSCAPE, activity.getTask().getConfiguration().orientation);
// Activity is not letterboxed.
assertEquals(ORIENTATION_LANDSCAPE, activity.getConfiguration().orientation);
- assertFalse(activity.mAppCompatController.getAppCompatAspectRatioPolicy()
+ assertFalse(activity.mAppCompatController.getAspectRatioPolicy()
.isLetterboxedForFixedOrientationAndAspectRatio());
}
@@ -770,7 +770,7 @@ public class ActivityRecordTests extends WindowTestsBase {
assertEquals(ORIENTATION_LANDSCAPE, activity.getTask().getConfiguration().orientation);
// Activity is not letterboxed.
assertEquals(ORIENTATION_LANDSCAPE, activity.getConfiguration().orientation);
- assertFalse(activity.mAppCompatController.getAppCompatAspectRatioPolicy()
+ assertFalse(activity.mAppCompatController.getAspectRatioPolicy()
.isLetterboxedForFixedOrientationAndAspectRatio());
}
@@ -785,7 +785,7 @@ public class ActivityRecordTests extends WindowTestsBase {
assertEquals(ORIENTATION_LANDSCAPE, activity.getTask().getConfiguration().orientation);
// Activity is not letterboxed.
assertEquals(ORIENTATION_LANDSCAPE, activity.getConfiguration().orientation);
- assertFalse(activity.mAppCompatController.getAppCompatAspectRatioPolicy()
+ assertFalse(activity.mAppCompatController.getAspectRatioPolicy()
.isLetterboxedForFixedOrientationAndAspectRatio());
}
@@ -3800,7 +3800,7 @@ public class ActivityRecordTests extends WindowTestsBase {
.setResizeMode(RESIZE_MODE_RESIZEABLE)
.setScreenOrientation(SCREEN_ORIENTATION_PORTRAIT)
.build();
- activity.mAppCompatController.getAppCompatSizeCompatModePolicy().clearSizeCompatMode();
+ activity.mAppCompatController.getSizeCompatModePolicy().clearSizeCompatMode();
return activity;
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/AppCompatActivityRobot.java b/services/tests/wmtests/src/com/android/server/wm/AppCompatActivityRobot.java
index 00c9691835db..018ea58e7120 100644
--- a/services/tests/wmtests/src/com/android/server/wm/AppCompatActivityRobot.java
+++ b/services/tests/wmtests/src/com/android/server/wm/AppCompatActivityRobot.java
@@ -180,7 +180,7 @@ class AppCompatActivityRobot {
void setLetterboxedForFixedOrientationAndAspectRatio(boolean enabled) {
doReturn(enabled).when(mActivityStack.top().mAppCompatController
- .getAppCompatAspectRatioPolicy()).isLetterboxedForFixedOrientationAndAspectRatio();
+ .getAspectRatioPolicy()).isLetterboxedForFixedOrientationAndAspectRatio();
}
void enableFullscreenCameraCompatTreatmentForTopActivity(boolean enabled) {
@@ -213,7 +213,7 @@ class AppCompatActivityRobot {
void setShouldApplyUserMinAspectRatioOverride(boolean enabled) {
doReturn(enabled).when(mActivityStack.top().mAppCompatController
- .getAppCompatAspectRatioOverrides()).shouldApplyUserMinAspectRatioOverride();
+ .getAspectRatioOverrides()).shouldApplyUserMinAspectRatioOverride();
}
void setShouldCreateCompatDisplayInsets(boolean enabled) {
@@ -226,17 +226,17 @@ class AppCompatActivityRobot {
void setShouldApplyUserFullscreenOverride(boolean enabled) {
doReturn(enabled).when(mActivityStack.top().mAppCompatController
- .getAppCompatAspectRatioOverrides()).shouldApplyUserFullscreenOverride();
+ .getAspectRatioOverrides()).shouldApplyUserFullscreenOverride();
}
void setGetUserMinAspectRatioOverrideCode(@UserMinAspectRatio int overrideCode) {
doReturn(overrideCode).when(mActivityStack.top().mAppCompatController
- .getAppCompatAspectRatioOverrides()).getUserMinAspectRatioOverrideCode();
+ .getAspectRatioOverrides()).getUserMinAspectRatioOverrideCode();
}
void setGetUserMinAspectRatioOverrideValue(float overrideValue) {
doReturn(overrideValue).when(mActivityStack.top().mAppCompatController
- .getAppCompatAspectRatioOverrides()).getUserMinAspectRatio();
+ .getAspectRatioOverrides()).getUserMinAspectRatio();
}
void setIgnoreOrientationRequest(boolean enabled) {
@@ -525,7 +525,7 @@ class AppCompatActivityRobot {
activity.setRequestedOrientation(screenOrientation);
}
// Make sure to use the provided configuration to construct the size compat fields.
- activity.mAppCompatController.getAppCompatSizeCompatModePolicy().clearSizeCompatMode();
+ activity.mAppCompatController.getSizeCompatModePolicy().clearSizeCompatMode();
activity.ensureActivityConfiguration();
// Make sure the display configuration reflects the change of activity.
if (activity.mDisplayContent.updateOrientation()) {
diff --git a/services/tests/wmtests/src/com/android/server/wm/AppCompatAspectRatioOverridesTest.java b/services/tests/wmtests/src/com/android/server/wm/AppCompatAspectRatioOverridesTest.java
index 14ef913e28db..f29cbc6b3360 100644
--- a/services/tests/wmtests/src/com/android/server/wm/AppCompatAspectRatioOverridesTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/AppCompatAspectRatioOverridesTest.java
@@ -413,7 +413,7 @@ public class AppCompatAspectRatioOverridesTest extends WindowTestsBase {
@Override
void onPostActivityCreation(@NonNull ActivityRecord activity) {
super.onPostActivityCreation(activity);
- spyOn(activity.mAppCompatController.getAppCompatAspectRatioOverrides());
+ spyOn(activity.mAppCompatController.getAspectRatioOverrides());
}
void checkShouldApplyUserFullscreenOverride(boolean expected) {
@@ -465,7 +465,7 @@ public class AppCompatAspectRatioOverridesTest extends WindowTestsBase {
}
private AppCompatAspectRatioOverrides getTopActivityAppCompatAspectRatioOverrides() {
- return activity().top().mAppCompatController.getAppCompatAspectRatioOverrides();
+ return activity().top().mAppCompatController.getAspectRatioOverrides();
}
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/AppCompatLetterboxPolicyTest.java b/services/tests/wmtests/src/com/android/server/wm/AppCompatLetterboxPolicyTest.java
index e046f7cc5845..b5ba111e4e72 100644
--- a/services/tests/wmtests/src/com/android/server/wm/AppCompatLetterboxPolicyTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/AppCompatLetterboxPolicyTest.java
@@ -364,7 +364,7 @@ public class AppCompatLetterboxPolicyTest extends WindowTestsBase {
@NonNull
private AppCompatAspectRatioPolicy getAspectRatioPolicy() {
- return activity().top().mAppCompatController.getAppCompatAspectRatioPolicy();
+ return activity().top().mAppCompatController.getAspectRatioPolicy();
}
@NonNull
diff --git a/services/tests/wmtests/src/com/android/server/wm/AppCompatOrientationOverridesTest.java b/services/tests/wmtests/src/com/android/server/wm/AppCompatOrientationOverridesTest.java
index a0727a7af87b..9e46c09f3ff6 100644
--- a/services/tests/wmtests/src/com/android/server/wm/AppCompatOrientationOverridesTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/AppCompatOrientationOverridesTest.java
@@ -275,7 +275,7 @@ public class AppCompatOrientationOverridesTest extends WindowTestsBase {
@Override
void onPostActivityCreation(@NonNull ActivityRecord activity) {
super.onPostActivityCreation(activity);
- spyOn(activity.mAppCompatController.getAppCompatAspectRatioPolicy());
+ spyOn(activity.mAppCompatController.getAspectRatioPolicy());
}
// Useful to reduce timeout during tests
diff --git a/services/tests/wmtests/src/com/android/server/wm/AppCompatOrientationPolicyTest.java b/services/tests/wmtests/src/com/android/server/wm/AppCompatOrientationPolicyTest.java
index 4faa71451a4d..f577c3a51e75 100644
--- a/services/tests/wmtests/src/com/android/server/wm/AppCompatOrientationPolicyTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/AppCompatOrientationPolicyTest.java
@@ -555,8 +555,8 @@ public class AppCompatOrientationPolicyTest extends WindowTestsBase {
@Override
void onPostActivityCreation(@NonNull ActivityRecord activity) {
super.onPostActivityCreation(activity);
- spyOn(activity.mAppCompatController.getAppCompatAspectRatioOverrides());
- spyOn(activity.mAppCompatController.getAppCompatAspectRatioPolicy());
+ spyOn(activity.mAppCompatController.getAspectRatioOverrides());
+ spyOn(activity.mAppCompatController.getAspectRatioPolicy());
}
@Override
diff --git a/services/tests/wmtests/src/com/android/server/wm/AppCompatUtilsTest.java b/services/tests/wmtests/src/com/android/server/wm/AppCompatUtilsTest.java
index a5b2cb39cfff..bfd533aa8f79 100644
--- a/services/tests/wmtests/src/com/android/server/wm/AppCompatUtilsTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/AppCompatUtilsTest.java
@@ -252,7 +252,7 @@ public class AppCompatUtilsTest extends WindowTestsBase {
@Override
void onPostActivityCreation(@NonNull ActivityRecord activity) {
super.onPostActivityCreation(activity);
- spyOn(activity.mAppCompatController.getAppCompatAspectRatioPolicy());
+ spyOn(activity.mAppCompatController.getAspectRatioPolicy());
}
@Override
@@ -272,13 +272,13 @@ public class AppCompatUtilsTest extends WindowTestsBase {
void setIsLetterboxedForFixedOrientationAndAspectRatio(
boolean forFixedOrientationAndAspectRatio) {
- when(activity().top().mAppCompatController.getAppCompatAspectRatioPolicy()
+ when(activity().top().mAppCompatController.getAspectRatioPolicy()
.isLetterboxedForFixedOrientationAndAspectRatio())
.thenReturn(forFixedOrientationAndAspectRatio);
}
void setIsLetterboxedForAspectRatioOnly(boolean forAspectRatio) {
- when(activity().top().mAppCompatController.getAppCompatAspectRatioPolicy()
+ when(activity().top().mAppCompatController.getAspectRatioPolicy()
.isLetterboxedForAspectRatioOnly()).thenReturn(forAspectRatio);
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/CameraCompatFreeformPolicyTests.java b/services/tests/wmtests/src/com/android/server/wm/CameraCompatFreeformPolicyTests.java
index b6f442460ec8..576d17af2e79 100644
--- a/services/tests/wmtests/src/com/android/server/wm/CameraCompatFreeformPolicyTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/CameraCompatFreeformPolicyTests.java
@@ -606,7 +606,7 @@ public class CameraCompatFreeformPolicyTests extends WindowTestsBase {
.setOnTop(true)
.setTask(task)
.build();
- mActivity.mAppCompatController.getAppCompatSizeCompatModePolicy().clearSizeCompatMode();
+ mActivity.mAppCompatController.getSizeCompatModePolicy().clearSizeCompatMode();
spyOn(mActivity.mAppCompatController.getAppCompatCameraOverrides());
spyOn(mActivity.info);
diff --git a/services/tests/wmtests/src/com/android/server/wm/DesktopAppCompatAspectRatioPolicyTests.java b/services/tests/wmtests/src/com/android/server/wm/DesktopAppCompatAspectRatioPolicyTests.java
index f4e1d4967ff5..fa7dcc8ebbc7 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DesktopAppCompatAspectRatioPolicyTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DesktopAppCompatAspectRatioPolicyTests.java
@@ -407,8 +407,8 @@ public class DesktopAppCompatAspectRatioPolicyTests extends WindowTestsBase {
@Override
void onPostActivityCreation(@NonNull ActivityRecord activity) {
super.onPostActivityCreation(activity);
- spyOn(activity.mAppCompatController.getAppCompatAspectRatioOverrides());
- spyOn(activity.mAppCompatController.getDesktopAppCompatAspectRatioPolicy());
+ spyOn(activity.mAppCompatController.getAspectRatioOverrides());
+ spyOn(activity.mAppCompatController.getDesktopAspectRatioPolicy());
}
void setDesiredAspectRatio(float aspectRatio) {
@@ -417,7 +417,7 @@ public class DesktopAppCompatAspectRatioPolicyTests extends WindowTestsBase {
}
DesktopAppCompatAspectRatioPolicy getDesktopAppCompatAspectRatioPolicy() {
- return getTopActivity().mAppCompatController.getDesktopAppCompatAspectRatioPolicy();
+ return getTopActivity().mAppCompatController.getDesktopAspectRatioPolicy();
}
float calculateAspectRatio() {
@@ -430,7 +430,7 @@ public class DesktopAppCompatAspectRatioPolicyTests extends WindowTestsBase {
}
float getSplitScreenAspectRatio() {
- return getTopActivity().mAppCompatController.getAppCompatAspectRatioOverrides()
+ return getTopActivity().mAppCompatController.getAspectRatioOverrides()
.getSplitScreenAspectRatio();
}
@@ -442,7 +442,7 @@ public class DesktopAppCompatAspectRatioPolicyTests extends WindowTestsBase {
void checkHasMinAspectRatioOverride(boolean expected) {
assertEquals(expected, this.activity().top().mAppCompatController
- .getDesktopAppCompatAspectRatioPolicy().hasMinAspectRatioOverride(
+ .getDesktopAspectRatioPolicy().hasMinAspectRatioOverride(
this.activity().top().getTask()));
}
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 6d508eabcd52..cdb51fc1c645 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DesktopModeLaunchParamsModifierTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DesktopModeLaunchParamsModifierTests.java
@@ -265,9 +265,9 @@ public class DesktopModeLaunchParamsModifierTests extends
final ActivityRecord activity = createActivity(display, SCREEN_ORIENTATION_LANDSCAPE,
task, /* ignoreOrientationRequest */ true);
- spyOn(mActivity.mAppCompatController.getAppCompatAspectRatioOverrides());
+ spyOn(mActivity.mAppCompatController.getAspectRatioOverrides());
doReturn(true).when(
- mActivity.mAppCompatController.getAppCompatAspectRatioOverrides())
+ mActivity.mAppCompatController.getAspectRatioOverrides())
.isUserFullscreenOverrideEnabled();
final int desiredWidth =
@@ -293,9 +293,9 @@ public class DesktopModeLaunchParamsModifierTests extends
final ActivityRecord activity = createActivity(display, SCREEN_ORIENTATION_LANDSCAPE,
task, /* ignoreOrientationRequest */ true);
- spyOn(mActivity.mAppCompatController.getAppCompatAspectRatioOverrides());
+ spyOn(mActivity.mAppCompatController.getAspectRatioOverrides());
doReturn(true).when(
- mActivity.mAppCompatController.getAppCompatAspectRatioOverrides())
+ mActivity.mAppCompatController.getAspectRatioOverrides())
.isSystemOverrideToFullscreenEnabled();
final int desiredWidth =
@@ -320,9 +320,9 @@ public class DesktopModeLaunchParamsModifierTests extends
final ActivityRecord activity = createActivity(display, SCREEN_ORIENTATION_PORTRAIT,
task, /* ignoreOrientationRequest */ true);
- spyOn(activity.mAppCompatController.getDesktopAppCompatAspectRatioPolicy());
+ spyOn(activity.mAppCompatController.getDesktopAspectRatioPolicy());
doReturn(LETTERBOX_ASPECT_RATIO).when(activity.mAppCompatController
- .getDesktopAppCompatAspectRatioPolicy()).calculateAspectRatio(any());
+ .getDesktopAspectRatioPolicy()).calculateAspectRatio(any());
final int desiredWidth =
(int) ((LANDSCAPE_DISPLAY_BOUNDS.height() / LETTERBOX_ASPECT_RATIO) + 0.5f);
@@ -424,7 +424,7 @@ public class DesktopModeLaunchParamsModifierTests extends
(int) (LANDSCAPE_DISPLAY_BOUNDS.height() * DESKTOP_MODE_INITIAL_BOUNDS_SCALE);
final int desiredWidth =
(int) (desiredHeight / activity.mAppCompatController
- .getAppCompatAspectRatioOverrides().getSplitScreenAspectRatio());
+ .getAspectRatioOverrides().getSplitScreenAspectRatio());
assertEquals(RESULT_CONTINUE, new CalculateRequestBuilder().setTask(task)
.setActivity(activity).calculate());
@@ -525,7 +525,7 @@ public class DesktopModeLaunchParamsModifierTests extends
final int desiredWidth =
(int) (PORTRAIT_DISPLAY_BOUNDS.width() * DESKTOP_MODE_INITIAL_BOUNDS_SCALE);
final int desiredHeight = (int) (desiredWidth * activity.mAppCompatController
- .getAppCompatAspectRatioOverrides().getSplitScreenAspectRatio());
+ .getAspectRatioOverrides().getSplitScreenAspectRatio());
assertEquals(RESULT_CONTINUE, new CalculateRequestBuilder().setTask(task)
.setActivity(activity).calculate());
@@ -616,7 +616,7 @@ public class DesktopModeLaunchParamsModifierTests extends
final ActivityRecord activity = createActivity(display, SCREEN_ORIENTATION_UNSPECIFIED,
task, /* ignoreOrientationRequest */ true);
final float userAspectRatioOverrideValueSplitScreen = activity.mAppCompatController
- .getAppCompatAspectRatioOverrides().getSplitScreenAspectRatio();
+ .getAspectRatioOverrides().getSplitScreenAspectRatio();
applyUserMinAspectRatioOverride(activity, USER_MIN_ASPECT_RATIO_SPLIT_SCREEN,
userAspectRatioOverrideValueSplitScreen);
@@ -641,7 +641,7 @@ public class DesktopModeLaunchParamsModifierTests extends
final ActivityRecord activity = createActivity(display, SCREEN_ORIENTATION_UNSPECIFIED,
task, /* ignoreOrientationRequest */ true);
final float userAspectRatioOverrideValueDisplaySize = activity.mAppCompatController
- .getAppCompatAspectRatioOverrides().getDisplaySizeMinAspectRatio();
+ .getAspectRatioOverrides().getDisplaySizeMinAspectRatio();
applyUserMinAspectRatioOverride(activity, USER_MIN_ASPECT_RATIO_DISPLAY_SIZE,
userAspectRatioOverrideValueDisplaySize);
@@ -738,7 +738,7 @@ public class DesktopModeLaunchParamsModifierTests extends
final ActivityRecord activity = createActivity(display, SCREEN_ORIENTATION_UNSPECIFIED,
task, /* ignoreOrientationRequest */ true);
final float userAspectRatioOverrideValueSplitScreen = activity.mAppCompatController
- .getAppCompatAspectRatioOverrides().getSplitScreenAspectRatio();
+ .getAspectRatioOverrides().getSplitScreenAspectRatio();
applyUserMinAspectRatioOverride(activity, USER_MIN_ASPECT_RATIO_SPLIT_SCREEN,
userAspectRatioOverrideValueSplitScreen);
@@ -763,7 +763,7 @@ public class DesktopModeLaunchParamsModifierTests extends
final ActivityRecord activity = createActivity(display, SCREEN_ORIENTATION_UNSPECIFIED,
task, /* ignoreOrientationRequest */ true);
final float userAspectRatioOverrideValueDisplaySize = activity.mAppCompatController
- .getAppCompatAspectRatioOverrides().getDisplaySizeMinAspectRatio();
+ .getAspectRatioOverrides().getDisplaySizeMinAspectRatio();
applyUserMinAspectRatioOverride(activity, USER_MIN_ASPECT_RATIO_DISPLAY_SIZE,
userAspectRatioOverrideValueDisplaySize);
@@ -812,9 +812,9 @@ public class DesktopModeLaunchParamsModifierTests extends
final ActivityRecord activity = createActivity(display, SCREEN_ORIENTATION_PORTRAIT,
task, /* ignoreOrientationRequest */ true);
- spyOn(activity.mAppCompatController.getDesktopAppCompatAspectRatioPolicy());
+ spyOn(activity.mAppCompatController.getDesktopAspectRatioPolicy());
doReturn(LETTERBOX_ASPECT_RATIO).when(activity.mAppCompatController
- .getDesktopAppCompatAspectRatioPolicy()).calculateAspectRatio(any());
+ .getDesktopAspectRatioPolicy()).calculateAspectRatio(any());
final int desiredHeight =
(int) (LANDSCAPE_DISPLAY_BOUNDS.height() * DESKTOP_MODE_INITIAL_BOUNDS_SCALE);
@@ -884,9 +884,9 @@ public class DesktopModeLaunchParamsModifierTests extends
final ActivityRecord activity = createActivity(display, SCREEN_ORIENTATION_PORTRAIT,
task, /* ignoreOrientationRequest */ true);
- spyOn(activity.mAppCompatController.getAppCompatAspectRatioOverrides());
+ spyOn(activity.mAppCompatController.getAspectRatioOverrides());
doReturn(true).when(
- activity.mAppCompatController.getAppCompatAspectRatioOverrides())
+ activity.mAppCompatController.getAspectRatioOverrides())
.isUserFullscreenOverrideEnabled();
final int desiredWidth =
@@ -912,9 +912,9 @@ public class DesktopModeLaunchParamsModifierTests extends
final ActivityRecord activity = createActivity(display, SCREEN_ORIENTATION_PORTRAIT,
task, /* ignoreOrientationRequest */ true);
- spyOn(activity.mAppCompatController.getAppCompatAspectRatioOverrides());
+ spyOn(activity.mAppCompatController.getAspectRatioOverrides());
doReturn(true).when(
- activity.mAppCompatController.getAppCompatAspectRatioOverrides())
+ activity.mAppCompatController.getAspectRatioOverrides())
.isSystemOverrideToFullscreenEnabled();
final int desiredWidth =
@@ -939,9 +939,9 @@ public class DesktopModeLaunchParamsModifierTests extends
final ActivityRecord activity = createActivity(display, SCREEN_ORIENTATION_LANDSCAPE,
task, /* ignoreOrientationRequest */ true);
- spyOn(activity.mAppCompatController.getDesktopAppCompatAspectRatioPolicy());
+ spyOn(activity.mAppCompatController.getDesktopAspectRatioPolicy());
doReturn(LETTERBOX_ASPECT_RATIO).when(activity.mAppCompatController
- .getDesktopAppCompatAspectRatioPolicy()).calculateAspectRatio(any());
+ .getDesktopAspectRatioPolicy()).calculateAspectRatio(any());
final int desiredWidth = PORTRAIT_DISPLAY_BOUNDS.width()
- (DESKTOP_MODE_LANDSCAPE_APP_PADDING * 2);
@@ -989,9 +989,9 @@ public class DesktopModeLaunchParamsModifierTests extends
final ActivityRecord activity = createActivity(display, SCREEN_ORIENTATION_LANDSCAPE,
task, /* ignoreOrientationRequest */ true);
- spyOn(activity.mAppCompatController.getDesktopAppCompatAspectRatioPolicy());
+ spyOn(activity.mAppCompatController.getDesktopAspectRatioPolicy());
doReturn(LETTERBOX_ASPECT_RATIO).when(activity.mAppCompatController
- .getDesktopAppCompatAspectRatioPolicy()).calculateAspectRatio(any());
+ .getDesktopAspectRatioPolicy()).calculateAspectRatio(any());
final int desiredWidth = PORTRAIT_DISPLAY_BOUNDS.width()
- (DESKTOP_MODE_LANDSCAPE_APP_PADDING * 2);
@@ -1294,7 +1294,7 @@ public class DesktopModeLaunchParamsModifierTests extends
private void setDesiredAspectRatio(ActivityRecord activity, float aspectRatio) {
final DesktopAppCompatAspectRatioPolicy desktopAppCompatAspectRatioPolicy =
- activity.mAppCompatController.getDesktopAppCompatAspectRatioPolicy();
+ activity.mAppCompatController.getDesktopAspectRatioPolicy();
spyOn(desktopAppCompatAspectRatioPolicy);
doReturn(aspectRatio).when(desktopAppCompatAspectRatioPolicy)
.getDesiredAspectRatio(any());
@@ -1304,7 +1304,7 @@ public class DesktopModeLaunchParamsModifierTests extends
float overrideValue) {
// Set desired aspect ratio to be below minimum so override can take effect.
final DesktopAppCompatAspectRatioPolicy desktopAppCompatAspectRatioPolicy =
- activity.mAppCompatController.getDesktopAppCompatAspectRatioPolicy();
+ activity.mAppCompatController.getDesktopAspectRatioPolicy();
spyOn(desktopAppCompatAspectRatioPolicy);
doReturn(1f).when(desktopAppCompatAspectRatioPolicy)
.getDesiredAspectRatio(any());
@@ -1318,7 +1318,7 @@ public class DesktopModeLaunchParamsModifierTests extends
// Simulate user min aspect ratio override being set.
final AppCompatAspectRatioOverrides appCompatAspectRatioOverrides =
- activity.mAppCompatController.getAppCompatAspectRatioOverrides();
+ activity.mAppCompatController.getAspectRatioOverrides();
spyOn(appCompatAspectRatioOverrides);
doReturn(overrideValue).when(appCompatAspectRatioOverrides).getUserMinAspectRatio();
doReturn(overrideCode).when(appCompatAspectRatioOverrides)
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 dfd10ec86a20..d76a907ba010 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
@@ -1583,42 +1583,6 @@ public class DisplayContentTests extends WindowTestsBase {
is(Configuration.ORIENTATION_PORTRAIT));
}
- @Test
- public void testHybridRotationAnimation() {
- final DisplayContent displayContent = mDefaultDisplay;
- final WindowState statusBar = newWindowBuilder("statusBar", TYPE_STATUS_BAR).build();
- final WindowState navBar = newWindowBuilder("navBar", TYPE_NAVIGATION_BAR).build();
- final WindowState app = newWindowBuilder("app", TYPE_BASE_APPLICATION).build();
- final WindowState[] windows = { statusBar, navBar, app };
- makeWindowVisible(windows);
- final DisplayPolicy displayPolicy = displayContent.getDisplayPolicy();
- displayPolicy.addWindowLw(statusBar, statusBar.mAttrs);
- displayPolicy.addWindowLw(navBar, navBar.mAttrs);
- final ScreenRotationAnimation rotationAnim = new ScreenRotationAnimation(displayContent,
- displayContent.getRotation());
- spyOn(rotationAnim);
- // Assume that the display rotation is changed so it is frozen in preparation for animation.
- doReturn(true).when(rotationAnim).hasScreenshot();
- displayContent.getDisplayRotation().setRotation((displayContent.getRotation() + 1) % 4);
- displayContent.setRotationAnimation(rotationAnim);
- // The fade rotation animation also starts to hide some non-app windows.
- assertNotNull(displayContent.getAsyncRotationController());
- assertTrue(statusBar.isAnimating(PARENTS, ANIMATION_TYPE_TOKEN_TRANSFORM));
-
- for (WindowState w : windows) {
- w.setOrientationChanging(true);
- }
- // The display only waits for the app window to unfreeze.
- assertFalse(displayContent.shouldSyncRotationChange(statusBar));
- assertFalse(displayContent.shouldSyncRotationChange(navBar));
- assertTrue(displayContent.shouldSyncRotationChange(app));
- // If all windows animated by fade rotation animation have done the orientation change,
- // the animation controller should be cleared.
- statusBar.setOrientationChanging(false);
- navBar.setOrientationChanging(false);
- assertNull(displayContent.getAsyncRotationController());
- }
-
@SetupWindows(addWindows = { W_ACTIVITY, W_WALLPAPER, W_STATUS_BAR, W_NAVIGATION_BAR,
W_INPUT_METHOD, W_NOTIFICATION_SHADE })
@Test
diff --git a/services/tests/wmtests/src/com/android/server/wm/DualDisplayAreaGroupPolicyTest.java b/services/tests/wmtests/src/com/android/server/wm/DualDisplayAreaGroupPolicyTest.java
index 347d1bc1becc..a7e8ce915f07 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DualDisplayAreaGroupPolicyTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DualDisplayAreaGroupPolicyTest.java
@@ -216,7 +216,7 @@ public class DualDisplayAreaGroupPolicyTest extends WindowTestsBase {
final Rect activityBounds = new Rect(mFirstActivity.getBounds());
// DAG is portrait (860x1200), so Task and Activity fill DAG.
- assertThat(mFirstActivity.mAppCompatController.getAppCompatAspectRatioPolicy()
+ assertThat(mFirstActivity.mAppCompatController.getAspectRatioPolicy()
.isLetterboxedForFixedOrientationAndAspectRatio()).isFalse();
assertThat(mFirstActivity.inSizeCompatMode()).isFalse();
assertThat(taskBounds).isEqualTo(dagBounds);
@@ -241,7 +241,7 @@ public class DualDisplayAreaGroupPolicyTest extends WindowTestsBase {
new Rect(mFirstActivity.getConfiguration().windowConfiguration.getBounds());
// DAG is landscape (1200x860), no fixed orientation letterbox
- assertThat(mFirstActivity.mAppCompatController.getAppCompatAspectRatioPolicy()
+ assertThat(mFirstActivity.mAppCompatController.getAspectRatioPolicy()
.isLetterboxedForFixedOrientationAndAspectRatio()).isFalse();
assertThat(mFirstActivity.inSizeCompatMode()).isTrue();
assertThat(newDagBounds.width()).isEqualTo(dagBounds.height());
@@ -266,12 +266,12 @@ public class DualDisplayAreaGroupPolicyTest extends WindowTestsBase {
mDisplay.onLastFocusedTaskDisplayAreaChanged(mFirstTda);
prepareUnresizable(mFirstActivity, SCREEN_ORIENTATION_NOSENSOR);
- assertThat(mFirstActivity.mAppCompatController.getAppCompatAspectRatioPolicy()
+ assertThat(mFirstActivity.mAppCompatController.getAspectRatioPolicy()
.isLetterboxedForFixedOrientationAndAspectRatio()).isFalse();
assertThat(mFirstActivity.inSizeCompatMode()).isFalse();
rotateDisplay(mDisplay, ROTATION_90);
- assertThat(mFirstActivity.mAppCompatController.getAppCompatAspectRatioPolicy()
+ assertThat(mFirstActivity.mAppCompatController.getAspectRatioPolicy()
.isLetterboxedForFixedOrientationAndAspectRatio()).isFalse();
assertThat(mFirstActivity.inSizeCompatMode()).isFalse();
}
@@ -289,7 +289,7 @@ public class DualDisplayAreaGroupPolicyTest extends WindowTestsBase {
// DAG is portrait (860x1200), and activity is letterboxed for fixed orientation
// (860x[860x860/1200=616]). Task fills DAG.
- assertThat(mFirstActivity.mAppCompatController.getAppCompatAspectRatioPolicy()
+ assertThat(mFirstActivity.mAppCompatController.getAspectRatioPolicy()
.isLetterboxedForFixedOrientationAndAspectRatio()).isTrue();
assertThat(mFirstActivity.inSizeCompatMode()).isFalse();
assertThat(taskBounds).isEqualTo(dagBounds);
@@ -307,7 +307,7 @@ public class DualDisplayAreaGroupPolicyTest extends WindowTestsBase {
mDisplay.onLastFocusedTaskDisplayAreaChanged(mFirstTda);
prepareUnresizable(mFirstActivity, SCREEN_ORIENTATION_NOSENSOR);
- assertThat(mFirstActivity.mAppCompatController.getAppCompatAspectRatioPolicy()
+ assertThat(mFirstActivity.mAppCompatController.getAspectRatioPolicy()
.isLetterboxedForFixedOrientationAndAspectRatio()).isFalse();
}
@@ -318,7 +318,7 @@ public class DualDisplayAreaGroupPolicyTest extends WindowTestsBase {
mDisplay.onLastFocusedTaskDisplayAreaChanged(mFirstTda);
prepareUnresizable(mFirstActivity, SCREEN_ORIENTATION_LOCKED);
- assertThat(mFirstActivity.mAppCompatController.getAppCompatAspectRatioPolicy()
+ assertThat(mFirstActivity.mAppCompatController.getAspectRatioPolicy()
.isLetterboxedForFixedOrientationAndAspectRatio()).isFalse();
}
@@ -338,7 +338,7 @@ public class DualDisplayAreaGroupPolicyTest extends WindowTestsBase {
final Rect newActivityBounds = new Rect(mFirstActivity.getBounds());
// DAG is landscape (1200x860), no fixed orientation letterbox
- assertThat(mFirstActivity.mAppCompatController.getAppCompatAspectRatioPolicy()
+ assertThat(mFirstActivity.mAppCompatController.getAspectRatioPolicy()
.isLetterboxedForFixedOrientationAndAspectRatio()).isFalse();
assertThat(mFirstActivity.inSizeCompatMode()).isTrue();
assertThat(newDagBounds.width()).isEqualTo(dagBounds.height());
@@ -364,7 +364,7 @@ public class DualDisplayAreaGroupPolicyTest extends WindowTestsBase {
rotateDisplay(mDisplay, ROTATION_90);
- assertThat(mFirstActivity.mAppCompatController.getAppCompatAspectRatioPolicy()
+ assertThat(mFirstActivity.mAppCompatController.getAspectRatioPolicy()
.isLetterboxedForFixedOrientationAndAspectRatio()).isFalse();
assertThat(mFirstActivity.inSizeCompatMode()).isFalse();
}
@@ -527,7 +527,7 @@ public class DualDisplayAreaGroupPolicyTest extends WindowTestsBase {
assertThat(mDisplay.getLastOrientation()).isEqualTo(SCREEN_ORIENTATION_LANDSCAPE);
assertThat(mFirstRoot.getConfiguration().orientation).isEqualTo(ORIENTATION_PORTRAIT);
assertThat(mFirstActivity.getConfiguration().orientation).isEqualTo(ORIENTATION_PORTRAIT);
- assertThat(mFirstActivity.mAppCompatController.getAppCompatAspectRatioPolicy()
+ assertThat(mFirstActivity.mAppCompatController.getAspectRatioPolicy()
.isLetterboxedForFixedOrientationAndAspectRatio()).isFalse();
assertThat(mFirstActivity.inSizeCompatMode()).isFalse();
@@ -540,14 +540,14 @@ public class DualDisplayAreaGroupPolicyTest extends WindowTestsBase {
assertThat(mDisplay.getLastOrientation()).isEqualTo(SCREEN_ORIENTATION_PORTRAIT);
assertThat(mSecondRoot.getConfiguration().orientation).isEqualTo(ORIENTATION_LANDSCAPE);
assertThat(mSecondActivity.getConfiguration().orientation).isEqualTo(ORIENTATION_LANDSCAPE);
- assertThat(mSecondActivity.mAppCompatController.getAppCompatAspectRatioPolicy()
+ assertThat(mSecondActivity.mAppCompatController.getAspectRatioPolicy()
.isLetterboxedForFixedOrientationAndAspectRatio()).isFalse();
assertThat(mSecondActivity.inSizeCompatMode()).isFalse();
// First activity is letterboxed in portrait as requested.
assertThat(mFirstRoot.getConfiguration().orientation).isEqualTo(ORIENTATION_LANDSCAPE);
assertThat(mFirstActivity.getConfiguration().orientation).isEqualTo(ORIENTATION_PORTRAIT);
- assertThat(mFirstActivity.mAppCompatController.getAppCompatAspectRatioPolicy()
+ assertThat(mFirstActivity.mAppCompatController.getAspectRatioPolicy()
.isLetterboxedForFixedOrientationAndAspectRatio()).isTrue();
assertThat(mFirstActivity.inSizeCompatMode()).isFalse();
diff --git a/services/tests/wmtests/src/com/android/server/wm/RootWindowContainerTests.java b/services/tests/wmtests/src/com/android/server/wm/RootWindowContainerTests.java
index 7e62b89d35bb..fc4f54a431d6 100644
--- a/services/tests/wmtests/src/com/android/server/wm/RootWindowContainerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/RootWindowContainerTests.java
@@ -535,7 +535,7 @@ public class RootWindowContainerTests extends WindowTestsBase {
final Rect bounds = new Rect(task.getBounds());
bounds.scale(0.5f);
task.setBounds(bounds);
- assertFalse(activity.mAppCompatController.getAppCompatAspectRatioPolicy()
+ assertFalse(activity.mAppCompatController.getAspectRatioPolicy()
.isLetterboxedForFixedOrientationAndAspectRatio());
assertThat(task.autoRemoveRecents).isFalse();
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java b/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java
index 07ee09a350d9..7683c66c18f3 100644
--- a/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java
@@ -673,7 +673,7 @@ public class SizeCompatTests extends WindowTestsBase {
assertFalse(mActivity.mDisplayContent.shouldImeAttachedToApp());
// Recompute the natural configuration without resolving size compat configuration.
- mActivity.mAppCompatController.getAppCompatSizeCompatModePolicy().clearSizeCompatMode();
+ mActivity.mAppCompatController.getSizeCompatModePolicy().clearSizeCompatMode();
mActivity.onConfigurationChanged(mTask.getConfiguration());
// It should keep non-attachable because the resolved bounds will be computed according to
// the aspect ratio that won't match its parent bounds.
@@ -765,7 +765,7 @@ public class SizeCompatTests extends WindowTestsBase {
/ originalBounds.width()));
// Recompute the natural configuration in the new display.
- mActivity.mAppCompatController.getAppCompatSizeCompatModePolicy().clearSizeCompatMode();
+ mActivity.mAppCompatController.getSizeCompatModePolicy().clearSizeCompatMode();
mActivity.ensureActivityConfiguration();
// Because the display cannot rotate, the portrait activity will fit the short side of
// display with keeping portrait bounds [200, 0 - 700, 1000] in center.
@@ -1143,11 +1143,11 @@ public class SizeCompatTests extends WindowTestsBase {
// Simulate the user selecting the fullscreen user aspect ratio override
spyOn(activity.mWmService.mAppCompatConfiguration);
- spyOn(activity.mAppCompatController.getAppCompatAspectRatioOverrides());
+ spyOn(activity.mAppCompatController.getAspectRatioOverrides());
doReturn(true).when(activity.mWmService.mAppCompatConfiguration)
.isUserAppAspectRatioFullscreenEnabled();
doReturn(USER_MIN_ASPECT_RATIO_FULLSCREEN)
- .when(activity.mAppCompatController.getAppCompatAspectRatioOverrides())
+ .when(activity.mAppCompatController.getAspectRatioOverrides())
.getUserMinAspectRatioOverrideCode();
assertFalse(activity.shouldCreateAppCompatDisplayInsets());
}
@@ -1165,9 +1165,9 @@ public class SizeCompatTests extends WindowTestsBase {
RESIZE_MODE_UNRESIZEABLE, ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
// Simulate the user selecting the fullscreen user aspect ratio override
- spyOn(activity.mAppCompatController.getAppCompatAspectRatioOverrides());
+ spyOn(activity.mAppCompatController.getAspectRatioOverrides());
doReturn(true).when(
- activity.mAppCompatController.getAppCompatAspectRatioOverrides())
+ activity.mAppCompatController.getAspectRatioOverrides())
.isSystemOverrideToFullscreenEnabled();
assertFalse(activity.shouldCreateAppCompatDisplayInsets());
}
@@ -1187,7 +1187,7 @@ public class SizeCompatTests extends WindowTestsBase {
prepareUnresizable(activity, /* maxAspect=*/ 1.5f, SCREEN_ORIENTATION_LANDSCAPE);
// Activity max bounds should not be sandboxed, even though it is letterboxed.
- assertTrue(activity.mAppCompatController.getAppCompatAspectRatioPolicy()
+ assertTrue(activity.mAppCompatController.getAspectRatioPolicy()
.isLetterboxedForFixedOrientationAndAspectRatio());
assertThat(activity.getConfiguration().windowConfiguration.getMaxBounds())
.isEqualTo(activity.getDisplayArea().getBounds());
@@ -1229,7 +1229,7 @@ public class SizeCompatTests extends WindowTestsBase {
prepareUnresizable(activity, /* maxAspect=*/ 1.5f, SCREEN_ORIENTATION_LANDSCAPE);
// Activity max bounds should not be sandboxed, even though it is letterboxed.
- assertTrue(activity.mAppCompatController.getAppCompatAspectRatioPolicy()
+ assertTrue(activity.mAppCompatController.getAspectRatioPolicy()
.isLetterboxedForFixedOrientationAndAspectRatio());
assertThat(activity.getConfiguration().windowConfiguration.getMaxBounds())
.isEqualTo(activity.getDisplayArea().getBounds());
@@ -1253,7 +1253,7 @@ public class SizeCompatTests extends WindowTestsBase {
prepareUnresizable(activity, /* maxAspect=*/ 1.5f, SCREEN_ORIENTATION_LANDSCAPE);
// Activity max bounds should not be sandboxed, even though it is letterboxed.
- assertTrue(activity.mAppCompatController.getAppCompatAspectRatioPolicy()
+ assertTrue(activity.mAppCompatController.getAspectRatioPolicy()
.isLetterboxedForFixedOrientationAndAspectRatio());
assertThat(activity.getConfiguration().windowConfiguration.getMaxBounds())
.isEqualTo(activity.getDisplayArea().getBounds());
@@ -1507,7 +1507,7 @@ public class SizeCompatTests extends WindowTestsBase {
// After changing the orientation to portrait the override should be applied.
activity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
- activity.mAppCompatController.getAppCompatSizeCompatModePolicy().clearSizeCompatMode();
+ activity.mAppCompatController.getSizeCompatModePolicy().clearSizeCompatMode();
// The per-package override forces the activity into a 3:2 aspect ratio
assertEquals(1200, activity.getBounds().height());
@@ -1531,7 +1531,7 @@ public class SizeCompatTests extends WindowTestsBase {
// After changing the orientation to portrait the override should be applied.
activity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
- activity.mAppCompatController.getAppCompatSizeCompatModePolicy().clearSizeCompatMode();
+ activity.mAppCompatController.getSizeCompatModePolicy().clearSizeCompatMode();
// The per-package override forces the activity into a 3:2 aspect ratio
assertEquals(1200, activity.getBounds().height());
@@ -1554,7 +1554,7 @@ public class SizeCompatTests extends WindowTestsBase {
// After changing the orientation to landscape the override shouldn't be applied.
activity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED);
- activity.mAppCompatController.getAppCompatSizeCompatModePolicy().clearSizeCompatMode();
+ activity.mAppCompatController.getSizeCompatModePolicy().clearSizeCompatMode();
// The per-package override should have no effect
assertEquals(1200, activity.getBounds().height());
@@ -1754,7 +1754,7 @@ public class SizeCompatTests extends WindowTestsBase {
addWindowToActivity(mActivity);
// App should launch in fullscreen.
- assertFalse(mActivity.mAppCompatController.getAppCompatAspectRatioPolicy()
+ assertFalse(mActivity.mAppCompatController.getAspectRatioPolicy()
.isLetterboxedForFixedOrientationAndAspectRatio());
assertFalse(mActivity.inSizeCompatMode());
@@ -1769,7 +1769,7 @@ public class SizeCompatTests extends WindowTestsBase {
assertTrue(rotatedDisplayBounds.width() < rotatedDisplayBounds.height());
// App should be in size compat.
- assertFalse(mActivity.mAppCompatController.getAppCompatAspectRatioPolicy()
+ assertFalse(mActivity.mAppCompatController.getAspectRatioPolicy()
.isLetterboxedForFixedOrientationAndAspectRatio());
assertDownScaled();
assertThat(mActivity.inSizeCompatMode()).isTrue();
@@ -1882,7 +1882,7 @@ public class SizeCompatTests extends WindowTestsBase {
assertTrue(displayBounds.width() > displayBounds.height());
// App should launch in fixed orientation letterbox.
- assertTrue(mActivity.mAppCompatController.getAppCompatAspectRatioPolicy()
+ assertTrue(mActivity.mAppCompatController.getAspectRatioPolicy()
.isLetterboxedForFixedOrientationAndAspectRatio());
assertFalse(mActivity.inSizeCompatMode());
assertActivityMaxBoundsSandboxed();
@@ -1913,7 +1913,7 @@ public class SizeCompatTests extends WindowTestsBase {
assertTrue(displayBounds.width() > displayBounds.height());
// App should launch in fixed orientation letterbox.
- assertTrue(mActivity.mAppCompatController.getAppCompatAspectRatioPolicy()
+ assertTrue(mActivity.mAppCompatController.getAspectRatioPolicy()
.isLetterboxedForFixedOrientationAndAspectRatio());
assertFalse(mActivity.inSizeCompatMode());
@@ -1943,7 +1943,7 @@ public class SizeCompatTests extends WindowTestsBase {
assertTrue(displayBounds.width() > displayBounds.height());
// App should launch in fixed orientation letterbox.
- assertTrue(mActivity.mAppCompatController.getAppCompatAspectRatioPolicy()
+ assertTrue(mActivity.mAppCompatController.getAspectRatioPolicy()
.isLetterboxedForFixedOrientationAndAspectRatio());
assertFalse(mActivity.inSizeCompatMode());
@@ -1973,7 +1973,7 @@ public class SizeCompatTests extends WindowTestsBase {
assertTrue(displayBounds.width() > displayBounds.height());
// App should launch in fixed orientation letterbox.
- assertTrue(mActivity.mAppCompatController.getAppCompatAspectRatioPolicy()
+ assertTrue(mActivity.mAppCompatController.getAspectRatioPolicy()
.isLetterboxedForFixedOrientationAndAspectRatio());
assertFalse(mActivity.inSizeCompatMode());
@@ -2070,7 +2070,7 @@ public class SizeCompatTests extends WindowTestsBase {
assertTrue(displayBounds.width() > displayBounds.height());
// App should launch in fixed orientation letterbox.
- assertTrue(mActivity.mAppCompatController.getAppCompatAspectRatioPolicy()
+ assertTrue(mActivity.mAppCompatController.getAspectRatioPolicy()
.isLetterboxedForFixedOrientationAndAspectRatio());
assertFalse(mActivity.inSizeCompatMode());
@@ -2094,7 +2094,7 @@ public class SizeCompatTests extends WindowTestsBase {
// App should launch in fixed orientation letterbox.
// Activity bounds should be 700x1400 with the ratio as the display.
- assertTrue(mActivity.mAppCompatController.getAppCompatAspectRatioPolicy()
+ assertTrue(mActivity.mAppCompatController.getAspectRatioPolicy()
.isLetterboxedForFixedOrientationAndAspectRatio());
assertFitted();
assertEquals(originalScreenWidthDp, mActivity.getConfiguration().smallestScreenWidthDp);
@@ -2134,7 +2134,7 @@ public class SizeCompatTests extends WindowTestsBase {
// App should launch in fixed orientation letterbox.
// Activity bounds should be 700x1400 with the ratio as the display.
- assertTrue(mActivity.mAppCompatController.getAppCompatAspectRatioPolicy()
+ assertTrue(mActivity.mAppCompatController.getAspectRatioPolicy()
.isLetterboxedForFixedOrientationAndAspectRatio());
assertFitted();
assertEquals(originalScreenWidthDp, mActivity.getConfiguration().smallestScreenWidthDp);
@@ -2169,7 +2169,7 @@ public class SizeCompatTests extends WindowTestsBase {
final Rect activityBounds = new Rect(mActivity.getBounds());
// App should launch in fixed orientation letterbox.
- assertTrue(mActivity.mAppCompatController.getAppCompatAspectRatioPolicy()
+ assertTrue(mActivity.mAppCompatController.getAspectRatioPolicy()
.isLetterboxedForFixedOrientationAndAspectRatio());
// Checking that there is no size compat mode.
assertFitted();
@@ -2199,9 +2199,9 @@ public class SizeCompatTests extends WindowTestsBase {
.isUserAppAspectRatioFullscreenEnabled();
// Set user aspect ratio override
- spyOn(mActivity.mAppCompatController.getAppCompatAspectRatioOverrides());
+ spyOn(mActivity.mAppCompatController.getAspectRatioOverrides());
doReturn(USER_MIN_ASPECT_RATIO_FULLSCREEN)
- .when(mActivity.mAppCompatController.getAppCompatAspectRatioOverrides())
+ .when(mActivity.mAppCompatController.getAspectRatioOverrides())
.getUserMinAspectRatioOverrideCode();
prepareMinAspectRatio(mActivity, 16 / 9f, SCREEN_ORIENTATION_PORTRAIT);
@@ -2224,9 +2224,9 @@ public class SizeCompatTests extends WindowTestsBase {
.isUserAppAspectRatioFullscreenEnabled();
// Set user aspect ratio override
- spyOn(mActivity.mAppCompatController.getAppCompatAspectRatioOverrides());
+ spyOn(mActivity.mAppCompatController.getAspectRatioOverrides());
doReturn(USER_MIN_ASPECT_RATIO_FULLSCREEN)
- .when(mActivity.mAppCompatController.getAppCompatAspectRatioOverrides())
+ .when(mActivity.mAppCompatController.getAspectRatioOverrides())
.getUserMinAspectRatioOverrideCode();
prepareMinAspectRatio(mActivity, 16 / 9f, SCREEN_ORIENTATION_LANDSCAPE);
@@ -2244,9 +2244,9 @@ public class SizeCompatTests extends WindowTestsBase {
final int displayWidth = mDisplayContent.mBaseDisplayWidth;
final int displayHeight = mDisplayContent.mBaseDisplayHeight;
- spyOn(mActivity.mAppCompatController.getAppCompatAspectRatioOverrides());
+ spyOn(mActivity.mAppCompatController.getAspectRatioOverrides());
doReturn(true).when(
- mActivity.mAppCompatController.getAppCompatAspectRatioOverrides())
+ mActivity.mAppCompatController.getAspectRatioOverrides())
.isSystemOverrideToFullscreenEnabled();
prepareMinAspectRatio(mActivity, 16 / 9f, SCREEN_ORIENTATION_PORTRAIT);
@@ -2264,9 +2264,9 @@ public class SizeCompatTests extends WindowTestsBase {
final int displayWidth = mDisplayContent.mBaseDisplayWidth;
final int displayHeight = mDisplayContent.mBaseDisplayHeight;
- spyOn(mActivity.mAppCompatController.getAppCompatAspectRatioOverrides());
+ spyOn(mActivity.mAppCompatController.getAspectRatioOverrides());
doReturn(true).when(
- mActivity.mAppCompatController.getAppCompatAspectRatioOverrides())
+ mActivity.mAppCompatController.getAspectRatioOverrides())
.isSystemOverrideToFullscreenEnabled();
prepareMinAspectRatio(mActivity, 16 / 9f, SCREEN_ORIENTATION_LANDSCAPE);
@@ -2416,7 +2416,7 @@ public class SizeCompatTests extends WindowTestsBase {
boolean enabled) {
final ActivityRecord activity = getActivityBuilderWithoutTask().build();
final DesktopAppCompatAspectRatioPolicy desktopAppCompatAspectRatioPolicy =
- activity.mAppCompatController.getDesktopAppCompatAspectRatioPolicy();
+ activity.mAppCompatController.getDesktopAspectRatioPolicy();
spyOn(desktopAppCompatAspectRatioPolicy);
doReturn(enabled).when(desktopAppCompatAspectRatioPolicy)
.hasMinAspectRatioOverride(any());
@@ -2426,7 +2426,7 @@ public class SizeCompatTests extends WindowTestsBase {
doReturn(enabled).when(activity.mWmService.mAppCompatConfiguration)
.isUserAppAspectRatioSettingsEnabled();
final AppCompatAspectRatioOverrides aspectRatioOverrides =
- activity.mAppCompatController.getAppCompatAspectRatioOverrides();
+ activity.mAppCompatController.getAspectRatioOverrides();
spyOn(aspectRatioOverrides);
// Set user aspect ratio override.
doReturn(aspectRatio).when(aspectRatioOverrides).getUserMinAspectRatioOverrideCode();
@@ -2703,7 +2703,7 @@ public class SizeCompatTests extends WindowTestsBase {
final Rect activityBounds = new Rect(mActivity.getBounds());
// App should launch in fixed orientation letterbox.
- assertTrue(mActivity.mAppCompatController.getAppCompatAspectRatioPolicy()
+ assertTrue(mActivity.mAppCompatController.getAspectRatioPolicy()
.isLetterboxedForFixedOrientationAndAspectRatio());
// Checking that there is no size compat mode.
assertFitted();
@@ -2747,7 +2747,7 @@ public class SizeCompatTests extends WindowTestsBase {
assertEquals(WINDOWING_MODE_MULTI_WINDOW, mActivity.getWindowingMode());
// App should launch in fixed orientation letterbox.
- assertTrue(mActivity.mAppCompatController.getAppCompatAspectRatioPolicy()
+ assertTrue(mActivity.mAppCompatController.getAspectRatioPolicy()
.isLetterboxedForFixedOrientationAndAspectRatio());
// Checking that there is no size compat mode.
assertFitted();
@@ -2782,7 +2782,7 @@ public class SizeCompatTests extends WindowTestsBase {
assertEquals(WINDOWING_MODE_MULTI_WINDOW, mActivity.getWindowingMode());
// App should launch in fixed orientation letterbox.
- assertTrue(mActivity.mAppCompatController.getAppCompatAspectRatioPolicy()
+ assertTrue(mActivity.mAppCompatController.getAspectRatioPolicy()
.isLetterboxedForFixedOrientationAndAspectRatio());
// Checking that there is no size compat mode.
assertFitted();
@@ -2808,7 +2808,7 @@ public class SizeCompatTests extends WindowTestsBase {
prepareUnresizable(mActivity, SCREEN_ORIENTATION_LANDSCAPE);
// App should launch in fixed orientation letterbox.
- assertTrue(mActivity.mAppCompatController.getAppCompatAspectRatioPolicy()
+ assertTrue(mActivity.mAppCompatController.getAspectRatioPolicy()
.isLetterboxedForFixedOrientationAndAspectRatio());
// Checking that there is no size compat mode.
assertFitted();
@@ -2834,7 +2834,7 @@ public class SizeCompatTests extends WindowTestsBase {
prepareUnresizable(mActivity, SCREEN_ORIENTATION_PORTRAIT);
// App should launch in fixed orientation letterbox.
- assertTrue(mActivity.mAppCompatController.getAppCompatAspectRatioPolicy()
+ assertTrue(mActivity.mAppCompatController.getAspectRatioPolicy()
.isLetterboxedForFixedOrientationAndAspectRatio());
// Checking that there is no size compat mode.
assertFitted();
@@ -2863,7 +2863,7 @@ public class SizeCompatTests extends WindowTestsBase {
assertTrue(displayBounds.width() < displayBounds.height());
// App should be in size compat.
- assertFalse(mActivity.mAppCompatController.getAppCompatAspectRatioPolicy()
+ assertFalse(mActivity.mAppCompatController.getAspectRatioPolicy()
.isLetterboxedForFixedOrientationAndAspectRatio());
assertThat(mActivity.inSizeCompatMode()).isTrue();
assertEquals(activityBounds.width(), newActivityBounds.width());
@@ -2880,7 +2880,7 @@ public class SizeCompatTests extends WindowTestsBase {
prepareUnresizable(mActivity, 0, SCREEN_ORIENTATION_PORTRAIT);
// App should launch in fullscreen.
- assertFalse(mActivity.mAppCompatController.getAppCompatAspectRatioPolicy()
+ assertFalse(mActivity.mAppCompatController.getAspectRatioPolicy()
.isLetterboxedForFixedOrientationAndAspectRatio());
assertFalse(mActivity.inSizeCompatMode());
// Activity inherits max bounds from TaskDisplayArea.
@@ -2894,7 +2894,7 @@ public class SizeCompatTests extends WindowTestsBase {
assertTrue(rotatedDisplayBounds.width() > rotatedDisplayBounds.height());
// App should be in size compat.
- assertFalse(mActivity.mAppCompatController.getAppCompatAspectRatioPolicy()
+ assertFalse(mActivity.mAppCompatController.getAspectRatioPolicy()
.isLetterboxedForFixedOrientationAndAspectRatio());
assertDownScaled();
assertThat(mActivity.inSizeCompatMode()).isTrue();
@@ -2915,7 +2915,7 @@ public class SizeCompatTests extends WindowTestsBase {
// Portrait fixed app without max aspect.
prepareUnresizable(mActivity, 0, SCREEN_ORIENTATION_PORTRAIT);
- assertTrue(mActivity.mAppCompatController.getAppCompatAspectRatioPolicy()
+ assertTrue(mActivity.mAppCompatController.getAspectRatioPolicy()
.isLetterboxedForFixedOrientationAndAspectRatio());
assertFalse(mActivity.inSizeCompatMode());
@@ -2938,7 +2938,7 @@ public class SizeCompatTests extends WindowTestsBase {
// Task and display bounds should be equal while activity should be letterboxed and
// has 700x1400 bounds with the ratio as the display.
- assertTrue(newActivity.mAppCompatController.getAppCompatAspectRatioPolicy()
+ assertTrue(newActivity.mAppCompatController.getAspectRatioPolicy()
.isLetterboxedForFixedOrientationAndAspectRatio());
assertFalse(newActivity.inSizeCompatMode());
// Activity max bounds are sandboxed due to size compat mode.
@@ -2959,7 +2959,7 @@ public class SizeCompatTests extends WindowTestsBase {
// Portrait fixed app without max aspect.
prepareUnresizable(mActivity, 0, SCREEN_ORIENTATION_PORTRAIT);
- assertTrue(mActivity.mAppCompatController.getAppCompatAspectRatioPolicy()
+ assertTrue(mActivity.mAppCompatController.getAspectRatioPolicy()
.isLetterboxedForFixedOrientationAndAspectRatio());
assertFalse(mActivity.inSizeCompatMode());
@@ -2980,7 +2980,7 @@ public class SizeCompatTests extends WindowTestsBase {
// Portrait fixed app without max aspect.
prepareUnresizable(mActivity, 0, SCREEN_ORIENTATION_PORTRAIT);
- assertTrue(mActivity.mAppCompatController.getAppCompatAspectRatioPolicy()
+ assertTrue(mActivity.mAppCompatController.getAspectRatioPolicy()
.isLetterboxedForFixedOrientationAndAspectRatio());
assertFalse(mActivity.inSizeCompatMode());
@@ -3011,7 +3011,7 @@ public class SizeCompatTests extends WindowTestsBase {
assertActivityMaxBoundsSandboxed();
// Activity bounds should be (1400 / 1.3 = 1076)x1400 with the app requested ratio.
- assertTrue(mActivity.mAppCompatController.getAppCompatAspectRatioPolicy()
+ assertTrue(mActivity.mAppCompatController.getAspectRatioPolicy()
.isLetterboxedForFixedOrientationAndAspectRatio());
assertFalse(newActivity.inSizeCompatMode());
assertEquals(displayBounds.height(), newActivityBounds.height());
@@ -3030,7 +3030,7 @@ public class SizeCompatTests extends WindowTestsBase {
prepareUnresizable(mActivity, 0, SCREEN_ORIENTATION_PORTRAIT);
clearInvocations(mActivity);
- assertTrue(mActivity.mAppCompatController.getAppCompatAspectRatioPolicy()
+ assertTrue(mActivity.mAppCompatController.getAspectRatioPolicy()
.isLetterboxedForFixedOrientationAndAspectRatio());
assertFalse(mActivity.inSizeCompatMode());
@@ -3038,7 +3038,7 @@ public class SizeCompatTests extends WindowTestsBase {
rotateDisplay(mActivity.mDisplayContent, ROTATION_90);
// App should be in size compat.
- assertFalse(mActivity.mAppCompatController.getAppCompatAspectRatioPolicy()
+ assertFalse(mActivity.mAppCompatController.getAspectRatioPolicy()
.isLetterboxedForFixedOrientationAndAspectRatio());
assertThat(mActivity.inSizeCompatMode()).isTrue();
// Activity max bounds are sandboxed due to size compat mode.
@@ -3050,10 +3050,10 @@ public class SizeCompatTests extends WindowTestsBase {
// App still in size compat, and the bounds don't change.
final AppCompatSizeCompatModePolicy scmPolicy = mActivity.mAppCompatController
- .getAppCompatSizeCompatModePolicy();
+ .getSizeCompatModePolicy();
spyOn(scmPolicy);
verify(scmPolicy, never()).clearSizeCompatMode();
- assertFalse(mActivity.mAppCompatController.getAppCompatAspectRatioPolicy()
+ assertFalse(mActivity.mAppCompatController.getAspectRatioPolicy()
.isLetterboxedForFixedOrientationAndAspectRatio());
assertThat(mActivity.inSizeCompatMode()).isTrue();
assertEquals(activityBounds, mActivity.getBounds());
@@ -3071,7 +3071,7 @@ public class SizeCompatTests extends WindowTestsBase {
prepareUnresizable(mActivity, 0, SCREEN_ORIENTATION_PORTRAIT);
// In fixed orientation letterbox
- assertTrue(mActivity.mAppCompatController.getAppCompatAspectRatioPolicy()
+ assertTrue(mActivity.mAppCompatController.getAspectRatioPolicy()
.isLetterboxedForFixedOrientationAndAspectRatio());
assertFalse(mActivity.inSizeCompatMode());
assertActivityMaxBoundsSandboxed();
@@ -3080,7 +3080,7 @@ public class SizeCompatTests extends WindowTestsBase {
rotateDisplay(display, ROTATION_90);
// App should be in size compat.
- assertFalse(mActivity.mAppCompatController.getAppCompatAspectRatioPolicy()
+ assertFalse(mActivity.mAppCompatController.getAspectRatioPolicy()
.isLetterboxedForFixedOrientationAndAspectRatio());
assertThat(mActivity.inSizeCompatMode()).isTrue();
assertActivityMaxBoundsSandboxed();
@@ -3089,7 +3089,7 @@ public class SizeCompatTests extends WindowTestsBase {
rotateDisplay(display, ROTATION_180);
// In activity letterbox
- assertTrue(mActivity.mAppCompatController.getAppCompatAspectRatioPolicy()
+ assertTrue(mActivity.mAppCompatController.getAspectRatioPolicy()
.isLetterboxedForFixedOrientationAndAspectRatio());
assertFalse(mActivity.inSizeCompatMode());
assertActivityMaxBoundsSandboxed();
@@ -3108,7 +3108,7 @@ public class SizeCompatTests extends WindowTestsBase {
prepareUnresizable(mActivity, 0, SCREEN_ORIENTATION_LANDSCAPE);
// In fixed orientation letterbox
- assertTrue(mActivity.mAppCompatController.getAppCompatAspectRatioPolicy()
+ assertTrue(mActivity.mAppCompatController.getAspectRatioPolicy()
.isLetterboxedForFixedOrientationAndAspectRatio());
assertFalse(mActivity.inSizeCompatMode());
assertActivityMaxBoundsSandboxed();
@@ -3117,7 +3117,7 @@ public class SizeCompatTests extends WindowTestsBase {
rotateDisplay(display, ROTATION_90);
// App should be in size compat.
- assertFalse(mActivity.mAppCompatController.getAppCompatAspectRatioPolicy()
+ assertFalse(mActivity.mAppCompatController.getAspectRatioPolicy()
.isLetterboxedForFixedOrientationAndAspectRatio());
assertDownScaled();
assertActivityMaxBoundsSandboxed();
@@ -3126,7 +3126,7 @@ public class SizeCompatTests extends WindowTestsBase {
rotateDisplay(display, ROTATION_180);
// In fixed orientation letterbox
- assertTrue(mActivity.mAppCompatController.getAppCompatAspectRatioPolicy()
+ assertTrue(mActivity.mAppCompatController.getAspectRatioPolicy()
.isLetterboxedForFixedOrientationAndAspectRatio());
assertFalse(mActivity.inSizeCompatMode());
assertActivityMaxBoundsSandboxed();
@@ -3325,7 +3325,7 @@ public class SizeCompatTests extends WindowTestsBase {
assertEquals(ORIENTATION_PORTRAIT, mTask.getConfiguration().orientation);
assertEquals(ORIENTATION_LANDSCAPE, mActivity.getConfiguration().orientation);
assertFitted();
- assertTrue(mActivity.mAppCompatController.getAppCompatAspectRatioPolicy()
+ assertTrue(mActivity.mAppCompatController.getAspectRatioPolicy()
.isLetterboxedForFixedOrientationAndAspectRatio());
assertActivityMaxBoundsSandboxed();
@@ -3352,7 +3352,7 @@ public class SizeCompatTests extends WindowTestsBase {
// Resizable activity is not in size compat mode but in the letterbox for fixed orientation.
assertFitted();
- assertTrue(mActivity.mAppCompatController.getAppCompatAspectRatioPolicy()
+ assertTrue(mActivity.mAppCompatController.getAspectRatioPolicy()
.isLetterboxedForFixedOrientationAndAspectRatio());
}
@@ -3389,7 +3389,7 @@ public class SizeCompatTests extends WindowTestsBase {
assertEquals(ORIENTATION_PORTRAIT, mTask.getConfiguration().orientation);
assertEquals(ORIENTATION_PORTRAIT, mActivity.getConfiguration().orientation);
assertFitted();
- assertFalse(mActivity.mAppCompatController.getAppCompatAspectRatioPolicy()
+ assertFalse(mActivity.mAppCompatController.getAspectRatioPolicy()
.isLetterboxedForFixedOrientationAndAspectRatio());
assertActivityMaxBoundsSandboxed();
@@ -3889,7 +3889,7 @@ public class SizeCompatTests extends WindowTestsBase {
private void recomputeNaturalConfigurationOfUnresizableActivity() {
// Recompute the natural configuration of the non-resizable activity and the split screen.
- mActivity.mAppCompatController.getAppCompatSizeCompatModePolicy().clearSizeCompatMode();
+ mActivity.mAppCompatController.getSizeCompatModePolicy().clearSizeCompatMode();
// Draw letterbox.
mActivity.setVisible(false);
@@ -4031,7 +4031,7 @@ public class SizeCompatTests extends WindowTestsBase {
// orientation is not respected with insets as insets have been decoupled.
final Rect appBounds = activity.getWindowConfiguration().getAppBounds();
final Rect displayBounds = display.getBounds();
- assertFalse(activity.mAppCompatController.getAppCompatAspectRatioPolicy()
+ assertFalse(activity.mAppCompatController.getAspectRatioPolicy()
.isLetterboxedForFixedOrientationAndAspectRatio());
assertNotNull(appBounds);
assertEquals(displayBounds.width(), appBounds.width());
@@ -4063,7 +4063,7 @@ public class SizeCompatTests extends WindowTestsBase {
final Rect bounds = activity.getBounds();
// Activity should be letterboxed and should have portrait app bounds
- assertTrue(activity.mAppCompatController.getAppCompatAspectRatioPolicy()
+ assertTrue(activity.mAppCompatController.getAspectRatioPolicy()
.isLetterboxedForFixedOrientationAndAspectRatio());
assertTrue(bounds.height() > bounds.width());
}
@@ -4098,7 +4098,7 @@ public class SizeCompatTests extends WindowTestsBase {
assertNotNull(activity.getAppCompatDisplayInsets());
// Activity is not letterboxed for fixed orientation because orientation is respected
// with insets, and should not be in size compat mode
- assertFalse(activity.mAppCompatController.getAppCompatAspectRatioPolicy()
+ assertFalse(activity.mAppCompatController.getAspectRatioPolicy()
.isLetterboxedForFixedOrientationAndAspectRatio());
assertFalse(activity.inSizeCompatMode());
}
@@ -4357,7 +4357,7 @@ public class SizeCompatTests extends WindowTestsBase {
Configuration parentConfig = mActivity.getParent().getConfiguration();
final AppCompatAspectRatioOverrides aspectRatioOverrides =
- mActivity.mAppCompatController.getAppCompatAspectRatioOverrides();
+ mActivity.mAppCompatController.getAspectRatioOverrides();
float actual = aspectRatioOverrides.getFixedOrientationLetterboxAspectRatio(parentConfig);
float expected = aspectRatioOverrides.getSplitScreenAspectRatio();
@@ -4497,14 +4497,14 @@ public class SizeCompatTests extends WindowTestsBase {
.isUserAppAspectRatioSettingsEnabled();
final AppCompatController appCompatController = mActivity.mAppCompatController;
final AppCompatAspectRatioOverrides aspectRatioOverrides =
- appCompatController.getAppCompatAspectRatioOverrides();
+ appCompatController.getAspectRatioOverrides();
spyOn(aspectRatioOverrides);
// Set user aspect ratio override.
doReturn(USER_MIN_ASPECT_RATIO_16_9).when(aspectRatioOverrides)
.getUserMinAspectRatioOverrideCode();
prepareLimitedBounds(mActivity, SCREEN_ORIENTATION_PORTRAIT, /* isUnresizable= */ false);
- assertFalse(appCompatController.getAppCompatAspectRatioPolicy().isAspectRatioApplied());
+ assertFalse(appCompatController.getAspectRatioPolicy().isAspectRatioApplied());
}
@Test
@@ -4520,14 +4520,14 @@ public class SizeCompatTests extends WindowTestsBase {
.isUserAppAspectRatioSettingsEnabled();
final AppCompatController appCompatController = mActivity.mAppCompatController;
final AppCompatAspectRatioOverrides aspectRatioOverrides =
- appCompatController.getAppCompatAspectRatioOverrides();
+ appCompatController.getAspectRatioOverrides();
spyOn(aspectRatioOverrides);
// Set user aspect ratio override.
doReturn(USER_MIN_ASPECT_RATIO_16_9).when(aspectRatioOverrides)
.getUserMinAspectRatioOverrideCode();
prepareLimitedBounds(mActivity, SCREEN_ORIENTATION_PORTRAIT, /* isUnresizable= */ true);
- assertTrue(appCompatController.getAppCompatAspectRatioPolicy().isAspectRatioApplied());
+ assertTrue(appCompatController.getAspectRatioPolicy().isAspectRatioApplied());
}
@Test
@@ -4540,8 +4540,7 @@ public class SizeCompatTests extends WindowTestsBase {
setUpDisplaySizeWithApp(2500, 1600, taskBuilder);
prepareLimitedBounds(mActivity, SCREEN_ORIENTATION_PORTRAIT, /* isUnresizable= */ false);
- assertFalse(mActivity.mAppCompatController.getAppCompatAspectRatioPolicy()
- .isAspectRatioApplied());
+ assertFalse(mActivity.mAppCompatController.getAspectRatioPolicy().isAspectRatioApplied());
}
@Test
@@ -4554,8 +4553,7 @@ public class SizeCompatTests extends WindowTestsBase {
setUpDisplaySizeWithApp(2500, 1600, taskBuilder);
prepareLimitedBounds(mActivity, SCREEN_ORIENTATION_PORTRAIT, /* isUnresizable= */ true);
- assertTrue(mActivity.mAppCompatController.getAppCompatAspectRatioPolicy()
- .isAspectRatioApplied());
+ assertTrue(mActivity.mAppCompatController.getAspectRatioPolicy().isAspectRatioApplied());
}
private void assertVerticalPositionForDifferentDisplayConfigsForLandscapeActivity(
@@ -4604,7 +4602,7 @@ public class SizeCompatTests extends WindowTestsBase {
verifyLogAppCompatState(mActivity, APP_COMPAT_STATE_CHANGED__STATE__NOT_LETTERBOXED);
prepareUnresizable(mActivity, /* maxAspect= */ 2, SCREEN_ORIENTATION_PORTRAIT);
- assertFalse(mActivity.mAppCompatController.getAppCompatAspectRatioPolicy()
+ assertFalse(mActivity.mAppCompatController.getAspectRatioPolicy()
.isLetterboxedForFixedOrientationAndAspectRatio());
assertFalse(mActivity.inSizeCompatMode());
assertTrue(mActivity.areBoundsLetterboxed());
@@ -4621,7 +4619,7 @@ public class SizeCompatTests extends WindowTestsBase {
// ActivityRecord#resolveSizeCompatModeConfiguration because mCompatDisplayInsets aren't
// null but activity doesn't enter size compat mode. Checking that areBoundsLetterboxed()
// still returns true because of the aspect ratio restrictions.
- assertFalse(mActivity.mAppCompatController.getAppCompatAspectRatioPolicy()
+ assertFalse(mActivity.mAppCompatController.getAspectRatioPolicy()
.isLetterboxedForFixedOrientationAndAspectRatio());
assertFalse(mActivity.inSizeCompatMode());
assertTrue(mActivity.areBoundsLetterboxed());
@@ -4649,7 +4647,7 @@ public class SizeCompatTests extends WindowTestsBase {
prepareUnresizable(mActivity, SCREEN_ORIENTATION_PORTRAIT);
- assertTrue(mActivity.mAppCompatController.getAppCompatAspectRatioPolicy()
+ assertTrue(mActivity.mAppCompatController.getAspectRatioPolicy()
.isLetterboxedForFixedOrientationAndAspectRatio());
assertFalse(mActivity.inSizeCompatMode());
assertTrue(mActivity.areBoundsLetterboxed());
@@ -4668,7 +4666,7 @@ public class SizeCompatTests extends WindowTestsBase {
rotateDisplay(mActivity.mDisplayContent, ROTATION_90);
- assertFalse(mActivity.mAppCompatController.getAppCompatAspectRatioPolicy()
+ assertFalse(mActivity.mAppCompatController.getAspectRatioPolicy()
.isLetterboxedForFixedOrientationAndAspectRatio());
assertTrue(mActivity.inSizeCompatMode());
assertTrue(mActivity.areBoundsLetterboxed());
@@ -4730,7 +4728,7 @@ public class SizeCompatTests extends WindowTestsBase {
prepareUnresizable(mActivity, SCREEN_ORIENTATION_LANDSCAPE);
assertFalse(mActivity.isEligibleForLetterboxEducation());
- assertTrue(mActivity.mAppCompatController.getAppCompatAspectRatioPolicy()
+ assertTrue(mActivity.mAppCompatController.getAspectRatioPolicy()
.isLetterboxedForFixedOrientationAndAspectRatio());
}
@@ -4789,7 +4787,7 @@ public class SizeCompatTests extends WindowTestsBase {
prepareUnresizable(mActivity, SCREEN_ORIENTATION_PORTRAIT);
assertTrue(mActivity.isEligibleForLetterboxEducation());
- assertTrue(mActivity.mAppCompatController.getAppCompatAspectRatioPolicy()
+ assertTrue(mActivity.mAppCompatController.getAspectRatioPolicy()
.isLetterboxedForFixedOrientationAndAspectRatio());
}
@@ -4804,7 +4802,7 @@ public class SizeCompatTests extends WindowTestsBase {
rotateDisplay(mActivity.mDisplayContent, ROTATION_90);
assertTrue(mActivity.isEligibleForLetterboxEducation());
- assertFalse(mActivity.mAppCompatController.getAppCompatAspectRatioPolicy()
+ assertFalse(mActivity.mAppCompatController.getAspectRatioPolicy()
.isLetterboxedForFixedOrientationAndAspectRatio());
assertTrue(mActivity.inSizeCompatMode());
}
@@ -5007,13 +5005,13 @@ public class SizeCompatTests extends WindowTestsBase {
assertEquals(SCREEN_ORIENTATION_UNSPECIFIED, mActivity.getOverrideOrientation());
assertEquals(mActivity.getTask().getBounds(), mActivity.getBounds());
final AppCompatAspectRatioPolicy aspectRatioPolicy = mActivity.mAppCompatController
- .getAppCompatAspectRatioPolicy();
+ .getAspectRatioPolicy();
assertEquals(0, aspectRatioPolicy.getMaxAspectRatio(), 0 /* delta */);
assertEquals(0, aspectRatioPolicy.getMinAspectRatio(), 0 /* delta */);
// Compat override can still take effect.
final AppCompatAspectRatioOverrides aspectRatioOverrides =
- mActivity.mAppCompatController.getAppCompatAspectRatioOverrides();
+ mActivity.mAppCompatController.getAspectRatioOverrides();
spyOn(aspectRatioOverrides);
doReturn(true).when(aspectRatioOverrides).shouldOverrideMinAspectRatio();
assertEquals(minAspect, aspectRatioPolicy.getMinAspectRatio(), 0 /* delta */);
@@ -5090,7 +5088,7 @@ public class SizeCompatTests extends WindowTestsBase {
assertEquals(origDensity, mActivity.getConfiguration().densityDpi);
// Activity should exit size compat with new density.
- mActivity.mAppCompatController.getAppCompatSizeCompatModePolicy().clearSizeCompatMode();
+ mActivity.mAppCompatController.getSizeCompatModePolicy().clearSizeCompatMode();
assertFitted();
assertEquals(newDensity, mActivity.getConfiguration().densityDpi);
@@ -5274,7 +5272,7 @@ public class SizeCompatTests extends WindowTestsBase {
activity.setRequestedOrientation(screenOrientation);
}
// Make sure to use the provided configuration to construct the size compat fields.
- activity.mAppCompatController.getAppCompatSizeCompatModePolicy().clearSizeCompatMode();
+ activity.mAppCompatController.getSizeCompatModePolicy().clearSizeCompatMode();
activity.ensureActivityConfiguration();
// Make sure the display configuration reflects the change of activity.
if (activity.mDisplayContent.updateOrientation()) {
diff --git a/services/tests/wmtests/src/com/android/server/wm/SystemServicesTestRule.java b/services/tests/wmtests/src/com/android/server/wm/SystemServicesTestRule.java
index a12831e1ccf1..a95093d7e113 100644
--- a/services/tests/wmtests/src/com/android/server/wm/SystemServicesTestRule.java
+++ b/services/tests/wmtests/src/com/android/server/wm/SystemServicesTestRule.java
@@ -43,6 +43,7 @@ import static org.mockito.Mockito.CALLS_REAL_METHODS;
import static org.mockito.Mockito.when;
import static org.mockito.Mockito.withSettings;
+import android.annotation.Nullable;
import android.app.ActivityManagerInternal;
import android.app.ActivityThread;
import android.app.AppOpsManager;
@@ -67,7 +68,6 @@ import android.os.Looper;
import android.os.PowerManager;
import android.os.PowerManagerInternal;
import android.os.PowerSaveState;
-import android.os.StrictMode;
import android.os.UserHandle;
import android.os.UserManager;
import android.provider.DeviceConfig;
@@ -75,7 +75,6 @@ import android.util.Log;
import android.view.DisplayInfo;
import android.view.InputChannel;
import android.view.SurfaceControl;
-import android.window.ConfigurationChangeSetting;
import com.android.dx.mockito.inline.extended.StaticMockitoSession;
import com.android.internal.os.BackgroundThread;
@@ -96,11 +95,9 @@ import com.android.server.input.InputManagerService;
import com.android.server.pm.UserManagerInternal;
import com.android.server.pm.UserManagerService;
import com.android.server.policy.PermissionPolicyInternal;
-import com.android.server.policy.WindowManagerPolicy;
import com.android.server.statusbar.StatusBarManagerInternal;
import com.android.server.testutils.StubTransaction;
import com.android.server.uri.UriGrantsManagerInternal;
-import com.android.window.flags.Flags;
import org.junit.rules.TestRule;
import org.junit.runner.Description;
@@ -144,13 +141,15 @@ public class SystemServicesTestRule implements TestRule {
private ActivityTaskManagerService mAtmService;
private WindowManagerService mWmService;
private InputManagerService mImService;
- private Runnable mOnBeforeServicesCreated;
+ @Nullable
+ private final Runnable mOnBeforeServicesCreated;
+
/**
* Spied {@link SurfaceControl.Transaction} class than can be used to verify calls.
*/
SurfaceControl.Transaction mTransaction;
- public SystemServicesTestRule(Runnable onBeforeServicesCreated) {
+ public SystemServicesTestRule(@Nullable Runnable onBeforeServicesCreated) {
mOnBeforeServicesCreated = onBeforeServicesCreated;
}
@@ -398,15 +397,13 @@ public class SystemServicesTestRule implements TestRule {
}
private void setUpWindowManagerService() {
- TestWindowManagerPolicy wmPolicy = new TestWindowManagerPolicy();
- TestDisplayWindowSettingsProvider testDisplayWindowSettingsProvider =
- new TestDisplayWindowSettingsProvider();
- // Suppress StrictMode violation (DisplayWindowSettings) to avoid log flood.
- DisplayThread.getHandler().post(StrictMode::allowThreadDiskWritesMask);
- mWmService = WindowManagerService.main(
- mContext, mImService, false, wmPolicy, mAtmService,
- testDisplayWindowSettingsProvider, StubTransaction::new,
- MockSurfaceControlBuilder::new, mAppCompat);
+ // Use a spied Transaction class to prevent native code calls and verify interactions.
+ mTransaction = spy(StubTransaction.class);
+
+ mWmService = WindowManagerServiceTestSupport.setUpService(mContext, mImService,
+ new TestWindowManagerPolicy(), mAtmService, new TestDisplayWindowSettingsProvider(),
+ mTransaction, new MockSurfaceControlBuilder(), mAppCompat);
+
spyOn(mWmService);
spyOn(mWmService.mRoot);
// Invoked during {@link ActivityStack} creation.
@@ -418,10 +415,6 @@ public class SystemServicesTestRule implements TestRule {
spyOn(mWmService.mDisplayWindowSettings);
spyOn(mWmService.mDisplayWindowSettingsProvider);
- // Setup factory classes to prevent calls to native code.
- mTransaction = spy(StubTransaction.class);
- // Return a spied Transaction class than can be used to verify calls.
- mWmService.mTransactionFactory = () -> mTransaction;
mWmService.mSurfaceAnimationRunner = new SurfaceAnimationRunner(
null, null, mTransaction, mWmService.mPowerManagerInternal);
@@ -488,12 +481,12 @@ public class SystemServicesTestRule implements TestRule {
}
private static void tearDownLocalServices() {
+ WindowManagerServiceTestSupport.tearDownService();
+
LocalServices.removeServiceForTest(DisplayManagerInternal.class);
LocalServices.removeServiceForTest(PowerManagerInternal.class);
LocalServices.removeServiceForTest(ActivityManagerInternal.class);
LocalServices.removeServiceForTest(ActivityTaskManagerInternal.class);
- LocalServices.removeServiceForTest(WindowManagerInternal.class);
- LocalServices.removeServiceForTest(WindowManagerPolicy.class);
LocalServices.removeServiceForTest(PackageManagerInternal.class);
LocalServices.removeServiceForTest(UriGrantsManagerInternal.class);
LocalServices.removeServiceForTest(PermissionPolicyInternal.class);
@@ -501,12 +494,7 @@ public class SystemServicesTestRule implements TestRule {
LocalServices.removeServiceForTest(UsageStatsManagerInternal.class);
LocalServices.removeServiceForTest(StatusBarManagerInternal.class);
LocalServices.removeServiceForTest(UserManagerInternal.class);
- LocalServices.removeServiceForTest(ImeTargetVisibilityPolicy.class);
LocalServices.removeServiceForTest(GrammaticalInflectionManagerInternal.class);
- if (Flags.condenseConfigurationChangeForSimpleMode()) {
- LocalServices.removeServiceForTest(
- ConfigurationChangeSetting.ConfigurationChangeSettingInternal.class);
- }
}
Description getDescription() {
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskFragmentTest.java b/services/tests/wmtests/src/com/android/server/wm/TaskFragmentTest.java
index 299717393028..0d9772492e59 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskFragmentTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskFragmentTest.java
@@ -759,13 +759,13 @@ public class TaskFragmentTest extends WindowTestsBase {
// Assert fixed orientation request is ignored for activity in ActivityEmbedding split.
activity0.setRequestedOrientation(SCREEN_ORIENTATION_LANDSCAPE);
- assertFalse(activity0.mAppCompatController.getAppCompatAspectRatioPolicy()
+ assertFalse(activity0.mAppCompatController.getAspectRatioPolicy()
.isLetterboxedForFixedOrientationAndAspectRatio());
assertEquals(SCREEN_ORIENTATION_UNSET, task.getOrientation());
activity1.setRequestedOrientation(SCREEN_ORIENTATION_PORTRAIT);
- assertFalse(activity1.mAppCompatController.getAppCompatAspectRatioPolicy()
+ assertFalse(activity1.mAppCompatController.getAspectRatioPolicy()
.isLetterboxedForFixedOrientationAndAspectRatio());
assertEquals(SCREEN_ORIENTATION_UNSET, task.getOrientation());
@@ -773,9 +773,9 @@ public class TaskFragmentTest extends WindowTestsBase {
mDisplayContent.setIgnoreOrientationRequest(true);
task.onConfigurationChanged(task.getParent().getConfiguration());
- assertFalse(activity0.mAppCompatController.getAppCompatAspectRatioPolicy()
+ assertFalse(activity0.mAppCompatController.getAspectRatioPolicy()
.isLetterboxedForFixedOrientationAndAspectRatio());
- assertFalse(activity1.mAppCompatController.getAppCompatAspectRatioPolicy()
+ assertFalse(activity1.mAppCompatController.getAspectRatioPolicy()
.isLetterboxedForFixedOrientationAndAspectRatio());
assertEquals(SCREEN_ORIENTATION_UNSET, task.getOrientation());
diff --git a/services/tests/wmtests/src/com/android/server/wm/TransparentPolicyTest.java b/services/tests/wmtests/src/com/android/server/wm/TransparentPolicyTest.java
index f1180ff93edb..9cd302e71d3b 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TransparentPolicyTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TransparentPolicyTest.java
@@ -354,7 +354,7 @@ public class TransparentPolicyTest extends WindowTestsBase {
ta.launchTransparentActivityInTask();
a.assertNotNullOnTopActivity(ActivityRecord::getAppCompatDisplayInsets);
a.applyToTopActivity((top) -> {
- top.mAppCompatController.getAppCompatSizeCompatModePolicy()
+ top.mAppCompatController.getSizeCompatModePolicy()
.clearSizeCompatMode();
});
a.assertNullOnTopActivity(ActivityRecord::getAppCompatDisplayInsets);
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceTestSupport.kt b/services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceTestSupport.kt
new file mode 100644
index 000000000000..a165d20eb5c1
--- /dev/null
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceTestSupport.kt
@@ -0,0 +1,112 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.wm
+
+import android.content.Context
+import android.os.StrictMode
+import android.view.SurfaceControl
+import android.window.ConfigurationChangeSetting
+import com.android.server.DisplayThread
+import com.android.server.LocalServices
+import com.android.server.input.InputManagerService
+import com.android.server.policy.WindowManagerPolicy
+import com.android.window.flags.Flags
+
+/**
+ * Provides support for tests that require a [WindowManagerService].
+ *
+ * It provides functionalities for setting up and tearing down the service with proper dependencies,
+ * which can be used across different test modules.
+ */
+object WindowManagerServiceTestSupport {
+
+ /**
+ * Sets up and initializes a [WindowManagerService] instance with the provided dependencies.
+ *
+ * This method constructs a [WindowManagerService] using the provided dependencies for testing.
+ * It's marked as `internal` due to the package-private classes [DisplayWindowSettingsProvider]
+ * and [AppCompatConfiguration]. The `@JvmName` annotation is used to bypass name mangling and
+ * allow access from Java via `WindowManagerServiceTestSupport.setUpService`.
+ *
+ * **Important:** Before calling this method, ensure that any previous [WindowManagerService]
+ * instance and its related services are properly torn down. In your test's setup, it is
+ * recommended to call [tearDownService] before calling [setUpService] to handle cases where a
+ * previous test might have crashed and left services in an inconsistent state. This is crucial
+ * for test reliability.
+ *
+ * Example usage in a test's `setUp()` method:
+ * ```
+ * @Before
+ * fun setUp() {
+ * WindowManagerServiceTestSupport.tearDownService() // Clean up before setup.
+ * mWindowManagerService = WindowManagerServiceTestSupport.setUpService(...)
+ * // ... rest of your setup logic ...
+ * }
+ * ```
+ *
+ * @param context the [Context] for the service.
+ * @param im the [InputManagerService] to use.
+ * @param policy the [WindowManagerPolicy] to use.
+ * @param atm the [ActivityTaskManagerService] to use.
+ * @param displayWindowSettingsProvider the [DisplayWindowSettingsProvider] to use.
+ * @param surfaceControlTransaction the [SurfaceControl.Transaction] instance to use.
+ * @param surfaceControlBuilder the [SurfaceControl.Builder] instance to use.
+ * @param appCompat the [AppCompatConfiguration] to use.
+ * @return the created [WindowManagerService] instance.
+ */
+ @JvmStatic
+ @JvmName("setUpService")
+ internal fun setUpService(
+ context: Context,
+ im: InputManagerService,
+ policy: WindowManagerPolicy,
+ atm: ActivityTaskManagerService,
+ displayWindowSettingsProvider: DisplayWindowSettingsProvider,
+ surfaceControlTransaction: SurfaceControl.Transaction,
+ surfaceControlBuilder: SurfaceControl.Builder,
+ appCompat: AppCompatConfiguration,
+ ): WindowManagerService {
+ // Suppress StrictMode violation (DisplayWindowSettings) to avoid log flood.
+ DisplayThread.getHandler().post { StrictMode.allowThreadDiskWritesMask() }
+
+ return WindowManagerService.main(
+ context,
+ im,
+ false, /* showBootMsgs */
+ policy,
+ atm,
+ displayWindowSettingsProvider,
+ { surfaceControlTransaction },
+ { surfaceControlBuilder },
+ appCompat,
+ )
+ }
+
+ /** Tears down the [WindowManagerService] and removes related local services. */
+ @JvmStatic
+ fun tearDownService() {
+ LocalServices.removeServiceForTest(WindowManagerPolicy::class.java)
+ LocalServices.removeServiceForTest(WindowManagerInternal::class.java)
+ LocalServices.removeServiceForTest(ImeTargetVisibilityPolicy::class.java)
+
+ if (Flags.condenseConfigurationChangeForSimpleMode()) {
+ LocalServices.removeServiceForTest(
+ ConfigurationChangeSetting.ConfigurationChangeSettingInternal::class.java,
+ )
+ }
+ }
+}
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 ab9abfc4a876..f6f473b4964d 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java
@@ -855,7 +855,6 @@ public class WindowStateTests extends WindowTestsBase {
startingApp.updateResizingWindowIfNeeded();
assertTrue(mWm.mResizingWindows.contains(startingApp));
assertTrue(startingApp.isDrawn());
- assertFalse(startingApp.getOrientationChanging());
// Even if the display is frozen, invisible requested window should not be affected.
mWm.startFreezingDisplay(0, 0, mDisplayContent);
@@ -873,7 +872,6 @@ public class WindowStateTests extends WindowTestsBase {
win.updateResizingWindowIfNeeded();
assertThat(mWm.mResizingWindows).contains(win);
- assertTrue(win.getOrientationChanging());
mWm.mResizingWindows.remove(win);
spyOn(win.mClient);
@@ -892,7 +890,6 @@ public class WindowStateTests extends WindowTestsBase {
// Even "resized" throws remote exception, it is still considered as reported. So the window
// shouldn't be resized again (which may block unfreeze in real case).
assertThat(mWm.mResizingWindows).doesNotContain(win);
- assertFalse(win.getOrientationChanging());
}
@Test